kis_image.cc 37.9 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
    KisSelectionMaskSP selectionMask = m_d->rootLayer->selectionMask();
199 200 201 202 203
    if (selectionMask) {
        return selectionMask->selection();
    } else {
        return 0;
    }
204 205
}

Boudewijn Rempt's avatar
Boudewijn Rempt committed
206
void KisImage::setGlobalSelection(KisSelectionSP globalSelection)
207
{
208
    KisSelectionMaskSP selectionMask = m_d->rootLayer->selectionMask();
Dmitry Kazakov's avatar
Dmitry Kazakov committed
209 210 211 212

    if(!globalSelection) {
        if(selectionMask) {
            removeNode(selectionMask);
213
        }
214 215
    }
    else {
Dmitry Kazakov's avatar
Dmitry Kazakov committed
216 217 218 219 220 221 222 223 224
        if(!selectionMask) {
            selectionMask = new KisSelectionMask(this);
            addNode(selectionMask);
            selectionMask->setActive(true);
        }
        selectionMask->setSelection(globalSelection);

        Q_ASSERT(m_d->rootLayer->childCount() > 0);
        Q_ASSERT(m_d->rootLayer->selectionMask());
225
    }
Dmitry Kazakov's avatar
Dmitry Kazakov committed
226 227

    m_d->legacyUndoAdapter->emitSelectionChanged();
228 229
}

Dmitry Kazakov's avatar
Dmitry Kazakov committed
230
void KisImage::deselectGlobalSelection()
Boudewijn Rempt's avatar
Boudewijn Rempt committed
231
{
232
    KisSelectionMaskSP selectionMask = m_d->rootLayer->selectionMask();
Dmitry Kazakov's avatar
Dmitry Kazakov committed
233 234
    if(selectionMask) {
        selectionMask->setActive(false);
235
    }
Dmitry Kazakov's avatar
Dmitry Kazakov committed
236 237

    m_d->legacyUndoAdapter->emitSelectionChanged();
238
}
239

Dmitry Kazakov's avatar
Dmitry Kazakov committed
240
bool KisImage::canReselectGlobalSelection()
241
{
Dmitry Kazakov's avatar
Dmitry Kazakov committed
242
    return deselectedMask();
243 244
}

Dmitry Kazakov's avatar
Dmitry Kazakov committed
245
void KisImage::reselectGlobalSelection()
246
{
Dmitry Kazakov's avatar
Dmitry Kazakov committed
247 248 249
    KisSelectionMaskSP mask = deselectedMask();
    if(mask) {
        mask->setActive(true);
250 251
    }

Dmitry Kazakov's avatar
Dmitry Kazakov committed
252 253 254 255 256 257 258 259 260 261 262 263
    m_d->legacyUndoAdapter->emitSelectionChanged();
}

KisSelectionMaskSP KisImage::deselectedMask()
{
    // Get a list of non-active masks
    KoProperties properties;
    properties.setProperty("active", false);
    QList<KisNodeSP> masks = root()->childNodes(QStringList("KisSelectionMask"), properties);

    return masks.size() > 0 ?
        static_cast<KisSelectionMask*>(masks.last().data()) : 0;
264 265
}

Boudewijn Rempt's avatar
Boudewijn Rempt committed
266
KisBackgroundSP KisImage::backgroundPattern() const
267 268 269 270
{
    return m_d->backgroundPattern;
}

Boudewijn Rempt's avatar
Boudewijn Rempt committed
271
void KisImage::setBackgroundPattern(KisBackgroundSP background)
272
{
Boudewijn Rempt's avatar
Boudewijn Rempt committed
273 274 275
    if (background != m_d->backgroundPattern) {
        m_d->backgroundPattern = background;
        emit sigImageUpdated(bounds());
276 277
    }
}
278

Patrick Julien's avatar
Patrick Julien committed
279 280
QString KisImage::nextLayerName() const
{
281 282
    if (m_d->nserver->currentSeed() == 0) {
        m_d->nserver->number();
283 284
        return i18n("background");
    }
Patrick Julien's avatar
Patrick Julien committed
285

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

289 290
void KisImage::rollBackLayerName()
{
291
    m_d->nserver->rollback();
292 293
}

Dmitry Kazakov's avatar
Dmitry Kazakov committed
294
void KisImage::init(KisUndoStore *undoStore, qint32 width, qint32 height, const KoColorSpace *colorSpace)
Patrick Julien's avatar
Patrick Julien committed
295
{
Boudewijn Rempt's avatar
Boudewijn Rempt committed
296
    if (colorSpace == 0) {
297
        colorSpace = KoColorSpaceRegistry::instance()->rgb8();
Boudewijn Rempt's avatar
Boudewijn Rempt committed
298 299
    }

300 301
    m_d->lockCount = 0;
    m_d->sizeChangedWhileLocked = false;
Boudewijn Rempt's avatar
Boudewijn Rempt committed
302
    m_d->perspectiveGrid = 0;
303

304 305
    m_d->signalRouter = new KisImageSignalRouter(this);

Dmitry Kazakov's avatar
Dmitry Kazakov committed
306 307
    if(!undoStore) {
        undoStore = new KisDumbUndoStore();
308 309
    }

Dmitry Kazakov's avatar
Dmitry Kazakov committed
310 311 312
    m_d->undoStore = undoStore;
    m_d->legacyUndoAdapter = new KisLegacyUndoAdapter(m_d->undoStore, this);
    m_d->postExecutionUndoAdapter = new KisPostExecutionUndoAdapter(m_d->undoStore, this);
313

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

316
    m_d->colorSpace = colorSpace;
317

318
    setRootLayer(new KisGroupLayer(this, "root", OPACITY_OPAQUE_U8));
319

320 321
    m_d->xres = 1.0;
    m_d->yres = 1.0;
322
    m_d->unit = KoUnit::Point;
323 324
    m_d->width = width;
    m_d->height = height;
325

326
    m_d->recorder = new KisActionRecorder(this);
327

328 329
    m_d->compositeProgressProxy = new KisCompositeProgressProxy();

330
    m_d->scheduler = 0;
331
    if (m_d->startProjection) {
332
        m_d->scheduler = new KisUpdateScheduler(this);
333
        m_d->scheduler->setProgressProxy(m_d->compositeProgressProxy);
334
    }
Patrick Julien's avatar
Patrick Julien committed
335 336
}

337 338 339 340 341
KisCompositeProgressProxy* KisImage::compositeProgressProxy()
{
    return m_d->compositeProgressProxy;
}

342 343
bool KisImage::locked() const
{
344
    return m_d->lockCount != 0;
345 346
}

347 348 349 350 351 352 353 354 355 356 357
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
358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378
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
379 380
void KisImage::lock()
{
381
    if (!locked()) {
382 383
        if (m_d->scheduler) {
            m_d->scheduler->lock();
Boudewijn Rempt's avatar
Boudewijn Rempt committed
384
        }
385
        m_d->sizeChangedWhileLocked = false;
386
    }
387
    m_d->lockCount++;
Boudewijn Rempt's avatar
Boudewijn Rempt committed
388 389 390 391
}

void KisImage::unlock()
{
392 393
    Q_ASSERT(locked());

394
    if (locked()) {
395
        m_d->lockCount--;
396

397 398
        if (m_d->lockCount == 0) {
            if (m_d->sizeChangedWhileLocked) {
399
                m_d->signalRouter->emitNotification(SizeChangedSignal);
400 401
            }

402 403
            if (m_d->scheduler) {
                m_d->scheduler->unlock();
404
            }
405
        }
406 407 408
    }
}

409 410 411 412 413 414 415 416 417 418
void KisImage::blockUpdates()
{
    m_d->scheduler->blockUpdates();
}

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

419
void KisImage::notifyLayerUpdated(KisLayerSP layer)
420
{
421 422
    // 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
423
    KisLayer *l = layer.data();
Boudewijn Rempt's avatar
Boudewijn Rempt committed
424 425 426 427
    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
428
    }
429 430
}

431
void KisImage::setSize(const QSize& size)
432
{
433 434 435
    m_d->width = size.width();
    m_d->height = size.height();
    emitSizeChanged();
436 437
}

438
void KisImage::resizeImageImpl(const QRect& newRect, bool cropLayers)
Patrick Julien's avatar
Patrick Julien committed
439
{
440
    if(newRect == bounds()) return;
441

442 443 444 445
    QString actionName = cropLayers ? i18n("Crop Image") : i18n("Resize Image");

    KisImageSignalVector emitSignals;
    emitSignals << SizeChangedSignal << ModifiedSignal;
446

447
    KisProcessingApplicator applicator(this, m_d->rootLayer,
448 449
                                       KisProcessingApplicator::RECURSIVE |
                                       KisProcessingApplicator::NO_UI_UPDATES,
450 451
                                       emitSignals, actionName);

452
    if(cropLayers || !newRect.topLeft().isNull()) {
453
        KisProcessingVisitorSP visitor =
454
            new KisCropProcessingVisitor(newRect, cropLayers, true);
455
        applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT);
456
    }
457 458
    applicator.applyCommand(new KisImageResizeCommand(this, newRect.size()));
    applicator.end();
459
}
460

461
void KisImage::resizeImage(const QRect& newRect)
462
{
463
    resizeImageImpl(newRect, false);
Patrick Julien's avatar
Patrick Julien committed
464 465
}

466
void KisImage::cropImage(const QRect& newRect)
467
{
468 469 470 471 472 473 474 475 476 477 478
    resizeImageImpl(newRect, true);
}


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

    KisImageSignalVector emitSignals;
    emitSignals << ModifiedSignal;

479
    KisProcessingApplicator applicator(this, node,
480
                                       KisProcessingApplicator::RECURSIVE,
481 482
                                       emitSignals, actionName);

483 484 485 486
    KisProcessingVisitorSP visitor =
        new KisCropProcessingVisitor(newRect, true, false);
    applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT);
    applicator.end();
487
}
488 489

void KisImage::emitSizeChanged()
Patrick Julien's avatar
Patrick Julien committed
490
{
491
    if (!locked()) {
492
        m_d->signalRouter->emitNotification(SizeChangedSignal);
493 494 495
    } else {
        m_d->sizeChangedWhileLocked = true;
    }
Patrick Julien's avatar
Patrick Julien committed
496 497
}

498
void KisImage::scaleImage(const QSize &size, qreal xres, qreal yres, KisFilterStrategy *filterStrategy)
499
{
500 501
    bool resolutionChanged = xres != xRes() && yres != yRes();
    bool sizeChanged = size != this->size();
Boudewijn Rempt's avatar
Boudewijn Rempt committed
502

503
    if(!resolutionChanged && !sizeChanged) return;
504

505
    KisImageSignalVector emitSignals;
506 507
    if (resolutionChanged) emitSignals << ResolutionChangedSignal;
    if (sizeChanged) emitSignals << SizeChangedSignal;
508 509
    emitSignals << ModifiedSignal;

510
    // XXX: Translate after 2.4 is released
511 512
    QString actionName = sizeChanged ? "Scale Image" : "Change Image Resolution";

513 514
    KisProcessingApplicator::ProcessingFlags signalFlags =
        (resolutionChanged || sizeChanged) ?
515 516
                KisProcessingApplicator::NO_UI_UPDATES :
                KisProcessingApplicator::NONE;
517

518
    KisProcessingApplicator applicator(this, m_d->rootLayer,
519
                                       KisProcessingApplicator::RECURSIVE | signalFlags,
520
                                       emitSignals, actionName);
521

522 523 524
    qreal sx = qreal(size.width()) / this->size().width();
    qreal sy = qreal(size.height()) / this->size().height();

525 526 527 528 529
    QTransform shapesCorrection;

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

531 532 533 534 535 536 537
    KisProcessingVisitorSP visitor =
        new KisTransformProcessingVisitor(sx, sy,
                                          0, 0,
                                          QPointF(),
                                          0,
                                          0, 0,
                                          filterStrategy,
538
                                          shapesCorrection);
539

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

542
    if (resolutionChanged) {
543 544 545 546
        KUndo2Command *parent =
            new KisResetShapesCommand(m_d->rootLayer);
        new KisImageSetResolutionCommand(this, xres, yres, parent);
        applicator.applyCommand(parent);
547 548
    }

549
    if (sizeChanged) {
550 551 552
        applicator.applyCommand(new KisImageResizeCommand(this, size));
    }

553
    applicator.end();
Boudewijn Rempt's avatar
Boudewijn Rempt committed
554
}
555

556
void KisImage::rotate(double radians)
557
{
Laurent Montel's avatar
Laurent Montel committed
558 559
    qint32 w = width();
    qint32 h = height();
Boudewijn Rempt's avatar
Boudewijn Rempt committed
560 561
    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
562 563
    w = (qint32)(width() * qAbs(cos(radians)) + height() * qAbs(sin(radians)) + 0.5);
    h = (qint32)(height() * qAbs(cos(radians)) + width() * qAbs(sin(radians)) + 0.5);
564

565 566
    tx -= (w - width()) / 2;
    ty -= (h - height()) / 2;
Boudewijn Rempt's avatar
Boudewijn Rempt committed
567

568 569
    bool sizeChanged = w != width() || h != height();

570 571
    // These signals will be emitted after processing is done
    KisImageSignalVector emitSignals;
572
    if (sizeChanged) emitSignals << SizeChangedSignal;
573 574 575 576 577 578 579 580 581 582 583 584 585
    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");
586

587
    KisFilterStrategy *filter = KisFilterStrategyRegistry::instance()->value("Triangle");
588

589 590 591 592 593
    KisProcessingVisitorSP visitor =
            new KisTransformProcessingVisitor(1.0, 1.0, 0.0, 0.0,
                                              QPointF(),
                                              radians,
                                              -tx, -ty, filter);
594

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

597
    if (sizeChanged) {
598 599 600
        applicator.applyCommand(new KisImageResizeCommand(this, QSize(w,h)));
    }
    applicator.end();
601 602
}

603 604 605 606 607
void KisImage::shearImpl(const QString &actionName,
                         KisNodeSP rootNode,
                         bool resizeImage,
                         double angleX, double angleY,
                         const QPointF &origin)
608
{
609
        //angleX, angleY are in degrees
610 611
    const qreal pi = 3.1415926535897932385;
    const qreal deg2rad = pi / 180.0;
612

613 614
    qreal tanX = tan(angleX * deg2rad);
    qreal tanY = tan(angleY * deg2rad);
615

616 617
    QPointF offset;
    QSize newSize;
618

619 620 621
    {
        KisTransformWorker worker(0,
                                  1.0, 1.0,
622
                                  tanX, tanY, origin.x(), origin.y(),
623 624 625 626 627
                                  0,
                                  0, 0, 0, 0);

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

631
    if(newSize == size()) return;
Boudewijn Rempt's avatar
Boudewijn Rempt committed
632

633
    KisImageSignalVector emitSignals;
634 635
    if(resizeImage) emitSignals << SizeChangedSignal;
    emitSignals << ModifiedSignal;
636

637 638
    KisProcessingApplicator::ProcessingFlags signalFlags =
        KisProcessingApplicator::RECURSIVE;
639
    if(resizeImage) signalFlags |= KisProcessingApplicator::NO_UI_UPDATES;
640

641
    KisProcessingApplicator applicator(this, rootNode,
642
                                       signalFlags,
643
                                       emitSignals, actionName);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
644

645 646 647 648
    KisFilterStrategy *filter = KisFilterStrategyRegistry::instance()->value("Triangle");

    KisProcessingVisitorSP visitor =
            new KisTransformProcessingVisitor(1.0, 1.0,
649
                                              tanX, tanY, origin,
650 651 652 653 654 655
                                              0,
                                              offset.x(), offset.y(),
                                              filter);

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

656 657 658 659
    if(resizeImage) {
        applicator.applyCommand(new KisImageResizeCommand(this, newSize));
    }

660
    applicator.end();
661 662
}

663 664 665 666 667 668 669 670 671 672 673 674 675 676
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());
}

677
void KisImage::convertImageColorSpace(const KoColorSpace *dstColorSpace, KoColorConversionTransformation::Intent renderingIntent)
678
{
679
    if (*m_d->colorSpace == *dstColorSpace) return;
680

681 682
    const KoColorSpace *srcColorSpace = m_d->colorSpace;

683 684 685
    undoAdapter()->beginMacro(i18n("Convert Image Color Space"));
    undoAdapter()->addCommand(new KisImageLockCommand(KisImageWSP(this), true));
    undoAdapter()->addCommand(new KisImageSetProjectionColorSpaceCommand(KisImageWSP(this), dstColorSpace));
686

687
    KisColorSpaceConvertVisitor visitor(this, srcColorSpace, dstColorSpace, renderingIntent);
688
    m_d->rootLayer->accept(visitor);
689

690 691
    undoAdapter()->addCommand(new KisImageLockCommand(KisImageWSP(this), false));
    undoAdapter()->endMacro();
692

693
    setModified();
694 695
}

696
void KisImage::assignImageProfile(const KoColorProfile *profile)
697
{
698 699 700 701 702
    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
703
    m_d->colorSpace = dstCs;
704

705 706 707
    KisChangeProfileVisitor visitor(srcCs, dstCs);
    m_d->rootLayer->accept(visitor);

708 709
}

710
void KisImage::convertProjectionColorSpace(const KoColorSpace *dstColorSpace)
711
{
712
    if (*m_d->colorSpace == *dstColorSpace) return;
713

714 715 716 717 718
    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
719

720 721
    setModified();
}
722

723 724 725 726
void KisImage::setProjectionColorSpace(const KoColorSpace * colorSpace)
{
    m_d->colorSpace = colorSpace;
    m_d->rootLayer->resetCache();
727
    m_d->signalRouter->emitNotification(ColorSpaceChangedSignal);
728
}
729

730 731 732 733
const KoColorSpace * KisImage::colorSpace() const
{
    return m_d->colorSpace;
}
Boudewijn Rempt's avatar
Boudewijn Rempt committed
734

735 736 737
const KoColorProfile * KisImage::profile() const
{
    return colorSpace()->profile();
Patrick Julien's avatar
Patrick Julien committed
738 739
}

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

Thomas Zander's avatar
Thomas Zander committed
745
double KisImage::yRes() const
746
{
747
    return m_d->yres;
748 749 750 751
}

void KisImage::setResolution(double xres, double yres)
{
752 753
    m_d->xres = xres;
    m_d->yres = yres;
754
    m_d->signalRouter->emitNotification(ResolutionChangedSignal);
755 756
}

757
QPointF KisImage::documentToPixel(const QPointF &documentCoord) const
758
{
759 760 761 762 763 764 765 766 767
    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());
}

768 769 770 771 772 773 774 775 776 777
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();
}

778 779 780 781 782 783 784 785
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());
786 787
}

788 789
QRectF KisImage::pixelToDocument(const QRectF &pixelCoord) const
{
Boudewijn Rempt's avatar
Boudewijn Rempt committed
790
    return QRectF(pixelToDocument(pixelCoord.topLeft()), pixelToDocument(pixelCoord.bottomRight()));
791 792
}

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

Laurent Montel's avatar
Laurent Montel committed
798
qint32 KisImage::height() const
799
{
800 801 802
    return m_d->height;
}

803
KisGroupLayerSP KisImage::rootLayer() const
804
{
Boudewijn Rempt's avatar
Boudewijn Rempt committed
805
    Q_ASSERT(m_d->rootLayer);
806
    return m_d->rootLayer;
807
}
808

809
KisPaintDeviceSP KisImage::projection()
810
{
Boudewijn Rempt's avatar
Boudewijn Rempt committed
811
    Q_ASSERT(m_d->rootLayer);
812
    KisPaintDeviceSP projection = m_d->rootLayer->projection();
Boudewijn Rempt's avatar
Boudewijn Rempt committed
813
    Q_ASSERT(projection);
814
    return projection;
815 816
}

Laurent Montel's avatar
Laurent Montel committed
817
qint32 KisImage::nlayers() const
Patrick Julien's avatar
Patrick Julien committed
818
{
Boudewijn Rempt's avatar
Boudewijn Rempt committed
819 820 821 822
    QStringList list;
    list << "KisLayer";

    KisCountVisitor visitor(list, KoProperties());
Boudewijn Rempt's avatar
Boudewijn Rempt committed
823
    m_d->rootLayer->accept(visitor);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
824
    return visitor.count();
Patrick Julien's avatar
Patrick Julien committed
825 826
}

Laurent Montel's avatar
Laurent Montel committed
827
qint32 KisImage::nHiddenLayers() const
828
{
Boudewijn Rempt's avatar
Boudewijn Rempt committed
829 830 831
    QStringList list;
    list << "KisLayer";
    KoProperties properties;
Boudewijn Rempt's avatar
Boudewijn Rempt committed
832
    properties.setProperty("visible", false);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
833
    KisCountVisitor visitor(list, properties);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
834
    m_d->rootLayer->accept(visitor);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
835 836

    return visitor.count();
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 863 864 865 866 867 868
QRect KisImage::realNodeExtent(KisNodeSP rootNode, QRect currentRect)
{
    KisNodeSP node = rootNode->firstChild();

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