kis_image.cc 38.7 KB
Newer Older
Boudewijn Rempt's avatar
Boudewijn Rempt committed
1
/*
2
 *  Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
3
 *  Copyright (c) 2007 Boudewijn Rempt <boud@valdyas.org>
4 5 6 7 8 9 10 11 12 13 14 15 16
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
17
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18
 */
Boudewijn Rempt's avatar
Boudewijn Rempt committed
19

20
#include "kis_image.h"
Boudewijn Rempt's avatar
Boudewijn Rempt committed
21

22
#include <KoConfig.h> // WORDS_BIGENDIAN
23

24
#include <stdlib.h>
25
#include <math.h>
26

27 28 29 30
#include <QImage>
#include <QPainter>
#include <QSize>
#include <QDateTime>
31 32
#include <QRect>
#include <QRegion>
33

Patrick Julien's avatar
Patrick Julien committed
34
#include <klocale.h>
35

36
#include "KoUnit.h"
37
#include "KoColorSpaceRegistry.h"
38
#include "KoColor.h"
Boudewijn Rempt's avatar
Boudewijn Rempt committed
39
#include "KoColorConversionTransformation.h"
Boudewijn Rempt's avatar
Boudewijn Rempt committed
40
#include "KoColorProfile.h"
41

42
#include "recorder/kis_action_recorder.h"
43
#include "kis_adjustment_layer.h"
44
#include "kis_annotation.h"
Boudewijn Rempt's avatar
Boudewijn Rempt committed
45
#include "kis_background.h"
46
#include "kis_change_profile_visitor.h"
47
#include "kis_colorspace_convert_visitor.h"
Boudewijn Rempt's avatar
Boudewijn Rempt committed
48
#include "kis_count_visitor.h"
Boudewijn Rempt's avatar
Boudewijn Rempt committed
49
#include "kis_filter_strategy.h"
Boudewijn Rempt's avatar
Boudewijn Rempt committed
50
#include "kis_group_layer.h"
51
#include "commands/kis_image_commands.h"
52
#include "kis_iterators_pixel.h"
53
#include "kis_layer.h"
54
#include "kis_meta_data_merge_strategy_registry.h"
55
#include "kis_name_server.h"
56 57 58
#include "kis_paint_device.h"
#include "kis_paint_layer.h"
#include "kis_painter.h"
59
#include "kis_perspective_grid.h"
60 61 62 63
#include "kis_selection.h"
#include "kis_transaction.h"
#include "kis_transform_visitor.h"
#include "kis_types.h"
64 65
#include "kis_meta_data_merge_strategy.h"

66
#include "kis_image_config.h"
67
#include "kis_update_scheduler.h"
68
#include "kis_image_signal_router.h"
69

Dmitry Kazakov's avatar
Dmitry Kazakov committed
70 71 72 73 74 75
#include "kis_undo_stores.h"
#include "kis_legacy_undo_adapter.h"
#include "kis_post_execution_undo_adapter.h"

#include "kis_processing_applicator.h"
#include "processing/kis_crop_processing_visitor.h"
76
#include "processing/kis_transform_processing_visitor.h"
77
#include "commands_new/kis_image_resize_command.h"
78
#include "commands_new/kis_image_set_resolution_command.h"
79
#include "kis_composite_progress_proxy.h"
80

81

82 83 84 85
// #define SANITY_CHECKS

#ifdef SANITY_CHECKS
#define SANITY_CHECK_LOCKED(name)                                       \
86 87 88
    if (!locked()) warnKrita() << "Locking policy failed:" << name          \
                               << "has been called without the image"       \
                                  "being locked";
89 90 91 92 93
#else
#define SANITY_CHECK_LOCKED(name)
#endif


Boudewijn Rempt's avatar
Boudewijn Rempt committed
94 95
class KisImage::KisImagePrivate
{
96
public:
Boudewijn Rempt's avatar
Boudewijn Rempt committed
97
    KisBackgroundSP  backgroundPattern;
98 99 100 101 102 103 104 105 106 107
    quint32 lockCount;
    bool sizeChangedWhileLocked;
    KisPerspectiveGrid* perspectiveGrid;

    qint32 width;
    qint32 height;

    double xres;
    double yres;

108
    KoUnit unit;
109

110
    const KoColorSpace * colorSpace;
111 112 113 114 115

    KisGroupLayerSP rootLayer; // The layers are contained in here
    QList<KisLayer*> dirtyLayers; // for thumbnails

    KisNameServer *nserver;
Dmitry Kazakov's avatar
Dmitry Kazakov committed
116 117 118 119

    KisUndoStore *undoStore;
    KisUndoAdapter *legacyUndoAdapter;
    KisPostExecutionUndoAdapter *postExecutionUndoAdapter;
120

121
    KisActionRecorder *recorder;
122 123 124

    vKisAnnotationSP annotations;

125
    QAtomicInt disableUIUpdateSignals;
126 127
    KisImageSignalRouter *signalRouter;
    KisUpdateScheduler *scheduler;
128

129 130
    KisCompositeProgressProxy *compositeProgressProxy;

131
    bool startProjection;
132
};
133

Dmitry Kazakov's avatar
Dmitry Kazakov committed
134
KisImage::KisImage(KisUndoStore *undoStore, qint32 width, qint32 height, const KoColorSpace * colorSpace, const QString& name, bool startProjection)
Boudewijn Rempt's avatar
Boudewijn Rempt committed
135 136 137
        : QObject(0)
        , KisShared()
        , m_d(new KisImagePrivate())
Patrick Julien's avatar
Patrick Julien committed
138
{
Adrian Page's avatar
Adrian Page committed
139
    setObjectName(name);
140
    dbgImage << "creating" << name;
141
    m_d->startProjection = startProjection;
Dmitry Kazakov's avatar
Dmitry Kazakov committed
142
    init(undoStore, width, height, colorSpace);
Patrick Julien's avatar
Patrick Julien committed
143
}
Patrick Julien's avatar
Patrick Julien committed
144

Boudewijn Rempt's avatar
Boudewijn Rempt committed
145
KisImage::~KisImage()
Patrick Julien's avatar
Patrick Julien committed
146
{
147
    dbgImage << "deleting kisimage" << objectName();
148

Dmitry Kazakov's avatar
Dmitry Kazakov committed
149 150 151 152 153 154
    /**
     * First delete the nodes, while strokes
     * and undo are still alive
     */
    m_d->rootLayer = 0;

155 156 157 158 159

    KisUpdateScheduler *scheduler = m_d->scheduler;
    m_d->scheduler = 0;
    delete scheduler;

Dmitry Kazakov's avatar
Dmitry Kazakov committed
160 161 162
    delete m_d->postExecutionUndoAdapter;
    delete m_d->legacyUndoAdapter;
    delete m_d->undoStore;
163
    delete m_d->compositeProgressProxy;
164

165
    delete m_d->signalRouter;
166 167 168
    delete m_d->perspectiveGrid;
    delete m_d->nserver;
    delete m_d;
169 170

    disconnect(); // in case Qt gets confused
Patrick Julien's avatar
Patrick Julien committed
171
}
Patrick Julien's avatar
Patrick Julien committed
172

Boudewijn Rempt's avatar
Boudewijn Rempt committed
173
void KisImage::nodeHasBeenAdded(KisNode *parent, int index)
Boudewijn Rempt's avatar
Boudewijn Rempt committed
174
{
175 176
    KisNodeGraphListener::nodeHasBeenAdded(parent, index);

177
    SANITY_CHECK_LOCKED("nodeHasBeenAdded");
178
    m_d->signalRouter->emitNodeHasBeenAdded(parent, index);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
179 180
}

Boudewijn Rempt's avatar
Boudewijn Rempt committed
181
void KisImage::aboutToRemoveANode(KisNode *parent, int index)
Boudewijn Rempt's avatar
Boudewijn Rempt committed
182
{
183 184
    KisNodeGraphListener::aboutToRemoveANode(parent, index);

185
    SANITY_CHECK_LOCKED("aboutToRemoveANode");
186
    m_d->signalRouter->emitAboutToRemoveANode(parent, index);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
187 188
}

189 190
void KisImage::nodeChanged(KisNode* node)
{
191 192
    KisNodeGraphListener::nodeChanged(node);

193
    m_d->signalRouter->emitNodeChanged(node);
194
}
Boudewijn Rempt's avatar
Boudewijn Rempt committed
195

196 197
KisSelectionSP KisImage::globalSelection() const
{
198 199 200 201 202 203 204
    KisSelectionMaskSP selectionMask = m_d->rootLayer->selectionMask();
    if (selectionMask) {
        return selectionMask->selection();
    }
    else {
        return 0;
    }
205 206
}

Boudewijn Rempt's avatar
Boudewijn Rempt committed
207
void KisImage::setGlobalSelection(KisSelectionSP globalSelection)
208
{
209 210 211 212
    KisSelectionMaskSP selectionMask = m_d->rootLayer->selectionMask();
    if (!selectionMask) {
        selectionMask = new KisSelectionMask(this);
        selectionMask->setActive(true);
213 214 215 216 217
        bool success = addNode(selectionMask, m_d->rootLayer, 0);
        Q_ASSERT(success);
        if (!success) {
            warnKrita << "Could not creaste global selection mask!";
        }
218 219 220 221 222 223 224 225 226
    }
    if (globalSelection) {
        selectionMask->setSelection(globalSelection);
    }
    else {
        selectionMask->setSelection(new KisSelection(new KisDefaultBounds(this)));
    }
    Q_ASSERT(m_d->rootLayer->childCount() > 0);
    Q_ASSERT(m_d->rootLayer->selectionMask());
227 228
}

Boudewijn Rempt's avatar
Boudewijn Rempt committed
229 230
void KisImage::removeGlobalSelection()
{
231 232 233 234
    KisSelectionMaskSP selectionMask = m_d->rootLayer->selectionMask();
    if (selectionMask) {
        removeNode(selectionMask);
    }
235
}
236

Boudewijn Rempt's avatar
Boudewijn Rempt committed
237
KisSelectionSP KisImage::deselectedGlobalSelection()
238
{
239 240 241 242 243 244 245
    KisSelectionMaskSP selectionMask = m_d->rootLayer->selectionMask();
    if (selectionMask) {
        return selectionMask->deselectedSelection();
    }
    else {
        return 0;
    }
246 247
}

248
void KisImage::setDeselectedGlobalSelection(KisSelectionSP selection)
249
{
250 251 252 253 254 255 256 257
    KisSelectionMaskSP selectionMask = m_d->rootLayer->selectionMask();
    if (!selectionMask) {
        setGlobalSelection();
        selectionMask = m_d->rootLayer->selectionMask();
    }
    Q_ASSERT(selectionMask);
    selectionMask->setDeselectedSelection(selection);

258 259
}

Boudewijn Rempt's avatar
Boudewijn Rempt committed
260
KisBackgroundSP KisImage::backgroundPattern() const
261 262 263 264
{
    return m_d->backgroundPattern;
}

Boudewijn Rempt's avatar
Boudewijn Rempt committed
265
void KisImage::setBackgroundPattern(KisBackgroundSP background)
266
{
Boudewijn Rempt's avatar
Boudewijn Rempt committed
267 268 269
    if (background != m_d->backgroundPattern) {
        m_d->backgroundPattern = background;
        emit sigImageUpdated(bounds());
270 271
    }
}
272

Patrick Julien's avatar
Patrick Julien committed
273 274
QString KisImage::nextLayerName() const
{
275 276
    if (m_d->nserver->currentSeed() == 0) {
        m_d->nserver->number();
277 278
        return i18n("background");
    }
Patrick Julien's avatar
Patrick Julien committed
279

Sven Langkamp's avatar
Sven Langkamp committed
280
    return i18n("Layer %1", m_d->nserver->number());
Patrick Julien's avatar
Patrick Julien committed
281 282
}

283 284
void KisImage::rollBackLayerName()
{
285
    m_d->nserver->rollback();
286 287
}

Dmitry Kazakov's avatar
Dmitry Kazakov committed
288
void KisImage::init(KisUndoStore *undoStore, qint32 width, qint32 height, const KoColorSpace *colorSpace)
Patrick Julien's avatar
Patrick Julien committed
289
{
Boudewijn Rempt's avatar
Boudewijn Rempt committed
290
    if (colorSpace == 0) {
291
        colorSpace = KoColorSpaceRegistry::instance()->rgb8();
Boudewijn Rempt's avatar
Boudewijn Rempt committed
292 293
    }

294 295
    m_d->lockCount = 0;
    m_d->sizeChangedWhileLocked = false;
Boudewijn Rempt's avatar
Boudewijn Rempt committed
296
    m_d->perspectiveGrid = 0;
297

298 299
    m_d->signalRouter = new KisImageSignalRouter(this);

Dmitry Kazakov's avatar
Dmitry Kazakov committed
300 301
    if(!undoStore) {
        undoStore = new KisDumbUndoStore();
302 303
    }

Dmitry Kazakov's avatar
Dmitry Kazakov committed
304 305 306
    m_d->undoStore = undoStore;
    m_d->legacyUndoAdapter = new KisLegacyUndoAdapter(m_d->undoStore, this);
    m_d->postExecutionUndoAdapter = new KisPostExecutionUndoAdapter(m_d->undoStore, this);
307

Sven Langkamp's avatar
Sven Langkamp committed
308
    m_d->nserver = new KisNameServer(1);
309

310
    m_d->colorSpace = colorSpace;
311

312
    setRootLayer(new KisGroupLayer(this, "root", OPACITY_OPAQUE_U8));
313

314 315
    m_d->xres = 1.0;
    m_d->yres = 1.0;
316
    m_d->unit = KoUnit::Point;
317 318
    m_d->width = width;
    m_d->height = height;
319

320
    m_d->recorder = new KisActionRecorder(this);
321

322 323
    m_d->compositeProgressProxy = new KisCompositeProgressProxy();

324
    m_d->scheduler = 0;
325
    if (m_d->startProjection) {
326
        m_d->scheduler = new KisUpdateScheduler(this);
327
        m_d->scheduler->setProgressProxy(m_d->compositeProgressProxy);
328
    }
Patrick Julien's avatar
Patrick Julien committed
329 330
}

331 332 333 334 335
KisCompositeProgressProxy* KisImage::compositeProgressProxy()
{
    return m_d->compositeProgressProxy;
}

336 337
bool KisImage::locked() const
{
338
    return m_d->lockCount != 0;
339 340
}

341 342 343 344 345 346 347 348 349 350 351
void KisImage::barrierLock()
{
    if (!locked()) {
        if (m_d->scheduler) {
            m_d->scheduler->barrierLock();
        }
        m_d->sizeChangedWhileLocked = false;
    }
    m_d->lockCount++;
}

Dmitry Kazakov's avatar
Dmitry Kazakov committed
352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372
bool KisImage::tryBarrierLock()
{
    bool result = true;

    if (!locked()) {
        if (m_d->scheduler) {
            result = m_d->scheduler->tryBarrierLock();
        }

        if(result) {
            m_d->sizeChangedWhileLocked = false;
        }
    }

    if(result) {
        m_d->lockCount++;
    }

    return result;
}

Boudewijn Rempt's avatar
Boudewijn Rempt committed
373 374
void KisImage::lock()
{
375
    if (!locked()) {
376 377
        if (m_d->scheduler) {
            m_d->scheduler->lock();
Boudewijn Rempt's avatar
Boudewijn Rempt committed
378
        }
379
        m_d->sizeChangedWhileLocked = false;
380
    }
381
    m_d->lockCount++;
Boudewijn Rempt's avatar
Boudewijn Rempt committed
382 383 384 385
}

void KisImage::unlock()
{
386 387
    Q_ASSERT(locked());

388
    if (locked()) {
389
        m_d->lockCount--;
390

391 392
        if (m_d->lockCount == 0) {
            if (m_d->sizeChangedWhileLocked) {
393
                m_d->signalRouter->emitNotification(SizeChangedSignal);
394 395
            }

396 397
            if (m_d->scheduler) {
                m_d->scheduler->unlock();
398
            }
399
        }
400 401 402
    }
}

403 404 405 406 407 408 409 410 411 412
void KisImage::blockUpdates()
{
    m_d->scheduler->blockUpdates();
}

void KisImage::unblockUpdates()
{
    m_d->scheduler->unblockUpdates();
}

413
void KisImage::notifyLayerUpdated(KisLayerSP layer)
414
{
415 416
    // Add the layer to the list of layers that need to be
    // rescanned for the thumbnails in the layerbox
Gábor Lehel's avatar
Gábor Lehel committed
417
    KisLayer *l = layer.data();
Boudewijn Rempt's avatar
Boudewijn Rempt committed
418 419 420 421
    while (l) {
        if (!m_d->dirtyLayers.contains(l))
            m_d->dirtyLayers.append(l);
        l = dynamic_cast<KisLayer*>(l->parent().data());
Gábor Lehel's avatar
Gábor Lehel committed
422
    }
423 424
}

425
void KisImage::setSize(const QSize& size)
426
{
427 428 429
    m_d->width = size.width();
    m_d->height = size.height();
    emitSizeChanged();
430 431
}

432
void KisImage::resizeImageImpl(const QRect& newRect, bool cropLayers)
Patrick Julien's avatar
Patrick Julien committed
433
{
434
    if(newRect == bounds()) return;
435

436 437 438 439
    QString actionName = cropLayers ? i18n("Crop Image") : i18n("Resize Image");

    KisImageSignalVector emitSignals;
    emitSignals << SizeChangedSignal << ModifiedSignal;
440

441
    KisProcessingApplicator applicator(this, m_d->rootLayer,
442 443
                                       KisProcessingApplicator::RECURSIVE |
                                       KisProcessingApplicator::NO_UI_UPDATES,
444 445
                                       emitSignals, actionName);

446
    if(cropLayers || !newRect.topLeft().isNull()) {
447
        KisProcessingVisitorSP visitor =
448
            new KisCropProcessingVisitor(newRect, cropLayers, true);
449
        applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT);
450
    }
451 452
    applicator.applyCommand(new KisImageResizeCommand(this, newRect.size()));
    applicator.end();
453
}
454

455
void KisImage::resizeImage(const QRect& newRect)
456
{
457
    resizeImageImpl(newRect, false);
Patrick Julien's avatar
Patrick Julien committed
458 459
}

460
void KisImage::cropImage(const QRect& newRect)
461
{
462 463 464 465 466 467 468 469 470 471 472
    resizeImageImpl(newRect, true);
}


void KisImage::cropNode(KisNodeSP node, const QRect& newRect)
{
    QString actionName = i18n("Crop Node");

    KisImageSignalVector emitSignals;
    emitSignals << ModifiedSignal;

473
    KisProcessingApplicator applicator(this, node,
474
                                       KisProcessingApplicator::RECURSIVE,
475 476
                                       emitSignals, actionName);

477 478 479 480
    KisProcessingVisitorSP visitor =
        new KisCropProcessingVisitor(newRect, true, false);
    applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT);
    applicator.end();
481
}
482 483

void KisImage::emitSizeChanged()
Patrick Julien's avatar
Patrick Julien committed
484
{
485
    if (!locked()) {
486
        m_d->signalRouter->emitNotification(SizeChangedSignal);
487 488 489
    } else {
        m_d->sizeChangedWhileLocked = true;
    }
Patrick Julien's avatar
Patrick Julien committed
490 491
}

492
void KisImage::scaleImage(const QSize &size, qreal xres, qreal yres, KisFilterStrategy *filterStrategy)
493
{
494 495
    bool resolutionChanged = xres != xRes() && yres != yRes();
    bool sizeChanged = size != this->size();
Boudewijn Rempt's avatar
Boudewijn Rempt committed
496

497
    if(!resolutionChanged && !sizeChanged) return;
498

499
    KisImageSignalVector emitSignals;
500 501
    if (resolutionChanged) emitSignals << ResolutionChangedSignal;
    if (sizeChanged) emitSignals << SizeChangedSignal;
502 503
    emitSignals << ModifiedSignal;

504
    // XXX: Translate after 2.4 is released
505 506
    QString actionName = sizeChanged ? "Scale Image" : "Change Image Resolution";

507 508
    KisProcessingApplicator::ProcessingFlags signalFlags =
        (resolutionChanged || sizeChanged) ?
509 510
                KisProcessingApplicator::NO_UI_UPDATES :
                KisProcessingApplicator::NONE;
511

512
    KisProcessingApplicator applicator(this, m_d->rootLayer,
513
                                       KisProcessingApplicator::RECURSIVE | signalFlags,
514
                                       emitSignals, actionName);
515

516 517 518
    qreal sx = qreal(size.width()) / this->size().width();
    qreal sy = qreal(size.height()) / this->size().height();

519 520 521 522 523
    QTransform shapesCorrection;

    if(resolutionChanged) {
        shapesCorrection = QTransform::fromScale(xRes() / xres, yRes() / yres);
    }
524

525 526 527 528 529 530 531
    KisProcessingVisitorSP visitor =
        new KisTransformProcessingVisitor(sx, sy,
                                          0, 0,
                                          QPointF(),
                                          0,
                                          0, 0,
                                          filterStrategy,
532
                                          shapesCorrection);
533

534
    applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT);
535

536
    if (resolutionChanged) {
537 538 539 540
        KUndo2Command *parent =
            new KisResetShapesCommand(m_d->rootLayer);
        new KisImageSetResolutionCommand(this, xres, yres, parent);
        applicator.applyCommand(parent);
541 542
    }

543
    if (sizeChanged) {
544 545 546
        applicator.applyCommand(new KisImageResizeCommand(this, size));
    }

547
    applicator.end();
Boudewijn Rempt's avatar
Boudewijn Rempt committed
548
}
549

550
void KisImage::rotate(double radians)
551
{
Laurent Montel's avatar
Laurent Montel committed
552 553
    qint32 w = width();
    qint32 h = height();
Boudewijn Rempt's avatar
Boudewijn Rempt committed
554 555
    qint32 tx = qint32((w * cos(radians) - h * sin(radians) - w) / 2 + 0.5);
    qint32 ty = qint32((h * cos(radians) + w * sin(radians) - h) / 2 + 0.5);
Pino Toscano's avatar
Pino Toscano committed
556 557
    w = (qint32)(width() * qAbs(cos(radians)) + height() * qAbs(sin(radians)) + 0.5);
    h = (qint32)(height() * qAbs(cos(radians)) + width() * qAbs(sin(radians)) + 0.5);
558

559 560
    tx -= (w - width()) / 2;
    ty -= (h - height()) / 2;
Boudewijn Rempt's avatar
Boudewijn Rempt committed
561

562 563
    bool sizeChanged = w != width() || h != height();

564 565
    // These signals will be emitted after processing is done
    KisImageSignalVector emitSignals;
566
    if (sizeChanged) emitSignals << SizeChangedSignal;
567 568 569 570 571 572 573 574 575 576 577 578 579
    emitSignals << ModifiedSignal;

    // These flags determine whether updates are transferred to the UI during processing
    KisProcessingApplicator::ProcessingFlags signalFlags =
        (emitSignals.contains(SizeChangedSignal)) ?
                KisProcessingApplicator::NO_UI_UPDATES :
                KisProcessingApplicator::NONE;


    // XXX i18n("Rotate Image") after 2.4
    KisProcessingApplicator applicator(this, m_d->rootLayer,
                                       KisProcessingApplicator::RECURSIVE | signalFlags,
                                       emitSignals, "Rotate Image");
580

581
    KisFilterStrategy *filter = KisFilterStrategyRegistry::instance()->value("Triangle");
582

583 584 585 586 587
    KisProcessingVisitorSP visitor =
            new KisTransformProcessingVisitor(1.0, 1.0, 0.0, 0.0,
                                              QPointF(),
                                              radians,
                                              -tx, -ty, filter);
588

589
    applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT);
590

591
    if (sizeChanged) {
592 593 594
        applicator.applyCommand(new KisImageResizeCommand(this, QSize(w,h)));
    }
    applicator.end();
595 596
}

597 598 599 600 601
void KisImage::shearImpl(const QString &actionName,
                         KisNodeSP rootNode,
                         bool resizeImage,
                         double angleX, double angleY,
                         const QPointF &origin)
602
{
603
        //angleX, angleY are in degrees
604 605
    const qreal pi = 3.1415926535897932385;
    const qreal deg2rad = pi / 180.0;
606

607 608
    qreal tanX = tan(angleX * deg2rad);
    qreal tanY = tan(angleY * deg2rad);
609

610 611
    QPointF offset;
    QSize newSize;
612

613 614 615
    {
        KisTransformWorker worker(0,
                                  1.0, 1.0,
616
                                  tanX, tanY, origin.x(), origin.y(),
617 618 619 620 621
                                  0,
                                  0, 0, 0, 0);

        QRect newRect = worker.transform().mapRect(bounds());
        newSize = newRect.size();
622
        if(resizeImage) offset = -newRect.topLeft();
623
    }
Boudewijn Rempt's avatar
Boudewijn Rempt committed
624

625
    if(newSize == size()) return;
Boudewijn Rempt's avatar
Boudewijn Rempt committed
626

627
    KisImageSignalVector emitSignals;
628 629
    if(resizeImage) emitSignals << SizeChangedSignal;
    emitSignals << ModifiedSignal;
630

631 632
    KisProcessingApplicator::ProcessingFlags signalFlags =
        KisProcessingApplicator::RECURSIVE;
633
    if(resizeImage) signalFlags |= KisProcessingApplicator::NO_UI_UPDATES;
634

635
    KisProcessingApplicator applicator(this, rootNode,
636
                                       signalFlags,
637
                                       emitSignals, actionName);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
638

639 640 641 642
    KisFilterStrategy *filter = KisFilterStrategyRegistry::instance()->value("Triangle");

    KisProcessingVisitorSP visitor =
            new KisTransformProcessingVisitor(1.0, 1.0,
643
                                              tanX, tanY, origin,
644 645 646 647 648 649
                                              0,
                                              offset.x(), offset.y(),
                                              filter);

    applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT);

650 651 652 653
    if(resizeImage) {
        applicator.applyCommand(new KisImageResizeCommand(this, newSize));
    }

654
    applicator.end();
655 656
}

657 658 659 660 661 662 663 664 665 666 667 668 669 670
void KisImage::shearNode(KisNodeSP node, double angleX, double angleY)
{
    QPointF shearOrigin = 0.5 * (QPointF(1.0,1.0) + bounds().bottomRight());

    shearImpl(i18n("Shear layer"), node, false,
              angleX, angleY, shearOrigin);
}

void KisImage::shear(double angleX, double angleY)
{
    shearImpl(i18n("Shear Image"), m_d->rootLayer, true,
              angleX, angleY, QPointF());
}

671
void KisImage::convertImageColorSpace(const KoColorSpace *dstColorSpace, KoColorConversionTransformation::Intent renderingIntent)
672
{
673
    if (*m_d->colorSpace == *dstColorSpace) return;
674

675 676
    const KoColorSpace *srcColorSpace = m_d->colorSpace;

677 678 679
    undoAdapter()->beginMacro(i18n("Convert Image Color Space"));
    undoAdapter()->addCommand(new KisImageLockCommand(KisImageWSP(this), true));
    undoAdapter()->addCommand(new KisImageSetProjectionColorSpaceCommand(KisImageWSP(this), dstColorSpace));
680

681
    KisColorSpaceConvertVisitor visitor(this, srcColorSpace, dstColorSpace, renderingIntent);
682
    m_d->rootLayer->accept(visitor);
683

684 685
    undoAdapter()->addCommand(new KisImageLockCommand(KisImageWSP(this), false));
    undoAdapter()->endMacro();
686

687
    setModified();
688 689
}

690
void KisImage::assignImageProfile(const KoColorProfile *profile)
691
{
692 693 694 695 696
    if(!profile) return;

    const KoColorSpace *dstCs = KoColorSpaceRegistry::instance()->colorSpace(colorSpace()->colorModelId().id(), colorSpace()->colorDepthId().id(), profile);
    const KoColorSpace *srcCs = colorSpace();

Dmitry Kazakov's avatar
Dmitry Kazakov committed
697
    m_d->colorSpace = dstCs;
698

699 700 701
    KisChangeProfileVisitor visitor(srcCs, dstCs);
    m_d->rootLayer->accept(visitor);

702 703
}

704
void KisImage::convertProjectionColorSpace(const KoColorSpace *dstColorSpace)
705
{
706
    if (*m_d->colorSpace == *dstColorSpace) return;
707

708 709 710 711 712
    undoAdapter()->beginMacro(i18n("Convert Projection Color Space"));
    undoAdapter()->addCommand(new KisImageLockCommand(KisImageWSP(this), true));
    undoAdapter()->addCommand(new KisImageSetProjectionColorSpaceCommand(KisImageWSP(this), dstColorSpace));
    undoAdapter()->addCommand(new KisImageLockCommand(KisImageWSP(this), false));
    undoAdapter()->endMacro();
Boudewijn Rempt's avatar
Boudewijn Rempt committed
713

714 715
    setModified();
}
716

717 718 719 720
void KisImage::setProjectionColorSpace(const KoColorSpace * colorSpace)
{
    m_d->colorSpace = colorSpace;
    m_d->rootLayer->resetCache();
721
    m_d->signalRouter->emitNotification(ColorSpaceChangedSignal);
722
}
723

724 725 726 727
const KoColorSpace * KisImage::colorSpace() const
{
    return m_d->colorSpace;
}
Boudewijn Rempt's avatar
Boudewijn Rempt committed
728

729 730 731
const KoColorProfile * KisImage::profile() const
{
    return colorSpace()->profile();
Patrick Julien's avatar
Patrick Julien committed
732 733
}

Thomas Zander's avatar
Thomas Zander committed
734
double KisImage::xRes() const
735
{
736
    return m_d->xres;
737 738
}

Thomas Zander's avatar
Thomas Zander committed
739
double KisImage::yRes() const
740
{
741
    return m_d->yres;
742 743 744 745
}

void KisImage::setResolution(double xres, double yres)
{
746 747
    m_d->xres = xres;
    m_d->yres = yres;
748
    m_d->signalRouter->emitNotification(ResolutionChangedSignal);
749 750
}

751
QPointF KisImage::documentToPixel(const QPointF &documentCoord) const
752
{
753 754 755 756 757 758 759 760 761
    return QPointF(documentCoord.x() * xRes(), documentCoord.y() * yRes());
}

QPoint KisImage::documentToIntPixel(const QPointF &documentCoord) const
{
    QPointF pixelCoord = documentToPixel(documentCoord);
    return QPoint((int)pixelCoord.x(), (int)pixelCoord.y());
}

762 763 764 765 766 767 768 769 770 771
QRectF KisImage::documentToPixel(const QRectF &documentRect) const
{
    return QRectF(documentToPixel(documentRect.topLeft()), documentToPixel(documentRect.bottomRight()));
}

QRect KisImage::documentToIntPixel(const QRectF &documentRect) const
{
    return documentToPixel(documentRect).toAlignedRect();
}

772 773 774 775 776 777 778 779
QPointF KisImage::pixelToDocument(const QPointF &pixelCoord) const
{
    return QPointF(pixelCoord.x() / xRes(), pixelCoord.y() / yRes());
}

QPointF KisImage::pixelToDocument(const QPoint &pixelCoord) const
{
    return QPointF((pixelCoord.x() + 0.5) / xRes(), (pixelCoord.y() + 0.5) / yRes());
780 781
}

782 783
QRectF KisImage::pixelToDocument(const QRectF &pixelCoord) const
{
Boudewijn Rempt's avatar
Boudewijn Rempt committed
784
    return QRectF(pixelToDocument(pixelCoord.topLeft()), pixelToDocument(pixelCoord.bottomRight()));
785 786
}

Laurent Montel's avatar
Laurent Montel committed
787
qint32 KisImage::width() const
788
{
789
    return m_d->width;
790 791
}

Laurent Montel's avatar
Laurent Montel committed
792
qint32 KisImage::height() const
793
{
794 795 796
    return m_d->height;
}

797
KisGroupLayerSP KisImage::rootLayer() const
798
{
Boudewijn Rempt's avatar
Boudewijn Rempt committed
799
    Q_ASSERT(m_d->rootLayer);
800
    return m_d->rootLayer;
801
}
802

803
KisPaintDeviceSP KisImage::projection()
804
{
Boudewijn Rempt's avatar
Boudewijn Rempt committed
805
    Q_ASSERT(m_d->rootLayer);
806
    KisPaintDeviceSP projection = m_d->rootLayer->projection();
Boudewijn Rempt's avatar
Boudewijn Rempt committed
807
    Q_ASSERT(projection);
808
    return projection;
809 810
}

Laurent Montel's avatar
Laurent Montel committed
811
qint32 KisImage::nlayers() const
Patrick Julien's avatar
Patrick Julien committed
812
{
Boudewijn Rempt's avatar
Boudewijn Rempt committed
813 814 815 816
    QStringList list;
    list << "KisLayer";

    KisCountVisitor visitor(list, KoProperties());
Boudewijn Rempt's avatar
Boudewijn Rempt committed
817
    m_d->rootLayer->accept(visitor);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
818
    return visitor.count();
Patrick Julien's avatar
Patrick Julien committed
819 820
}

Laurent Montel's avatar
Laurent Montel committed
821
qint32 KisImage::nHiddenLayers() const
822
{
Boudewijn Rempt's avatar
Boudewijn Rempt committed
823 824 825
    QStringList list;
    list << "KisLayer";
    KoProperties properties;
Boudewijn Rempt's avatar
Boudewijn Rempt committed
826
    properties.setProperty("visible", false);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
827
    KisCountVisitor visitor(list, properties);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
828
    m_d->rootLayer->accept(visitor);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
829 830

    return visitor.count();
831 832
}

833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862
QRect KisImage::realNodeExtent(KisNodeSP rootNode, QRect currentRect)
{
    KisNodeSP node = rootNode->firstChild();

    while(node) {
        currentRect |= realNodeExtent(node, currentRect);
        node = node->nextSibling();
    }

    // TODO: it would be better to count up changeRect inside
    // node's extent() method
    currentRect |= rootNode->changeRect(rootNode->extent());

    return currentRect;
}

void KisImage::refreshHiddenArea(KisNodeSP rootNode, const QRect &preparedArea)
{
    QRect realNodeRect = realNodeExtent(rootNode);
    if(!preparedArea.contains(realNodeRect)) {

        QRegion dirtyRegion = realNodeRect;
        dirtyRegion -= preparedArea;

        foreach(const QRect &rc, dirtyRegion.rects()) {
            refreshGraph(rootNode, rc, realNodeRect);
        }
    }
}

863 864
void KisImage::flatten()
{
865
    KisGroupLayerSP oldRootLayer = m_d->rootLayer;
Dmitry Kazakov's avatar
Dmitry Kazakov committed
866
    KisGroupLayerSP newRootLayer =
867
        new KisGroupLayer(this, "root", OPACITY_OPAQUE_U8);
868

869
    refreshHiddenArea(oldRootLayer, bounds());
870

Dmitry Kazakov's avatar