kis_paint_device.cc 65.9 KB
Newer Older
1 2
/*
 *  Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
Boudewijn Rempt's avatar
Boudewijn Rempt committed
3
 *  Copyright (c) 2004 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_paint_device.h"
Boudewijn Rempt's avatar
Boudewijn Rempt committed
21

22
#include <QRect>
23
#include <QTransform>
24
#include <QImage>
Casper Boemann's avatar
Casper Boemann committed
25
#include <QList>
26
#include <QHash>
27
#include <QIODevice>
28
#include <qmath.h>
29

30
#include <klocalizedstring.h>
31

32
#include <KoChannelInfo.h>
Boudewijn Rempt's avatar
Boudewijn Rempt committed
33 34 35 36
#include <KoColorProfile.h>
#include <KoColor.h>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
37
#include <KoColorModelStandardIds.h>
Boudewijn Rempt's avatar
Boudewijn Rempt committed
38
#include <KoIntegerMaths.h>
39
#include <KoMixColorsOp.h>
40
#include <KoUpdater.h>
41

42
#include "kis_image.h"
43
#include "kis_random_sub_accessor.h"
44
#include "kis_selection.h"
45
#include "kis_node.h"
46
#include "kis_datamanager.h"
47
#include "kis_paint_device_writer.h"
Sven Langkamp's avatar
Sven Langkamp committed
48 49
#include "kis_selection_component.h"
#include "kis_pixel_selection.h"
50
#include "kis_repeat_iterators_pixel.h"
51
#include "kis_fixed_paint_device.h"
52

53
#include "tiles3/kis_hline_iterator.h"
54
#include "tiles3/kis_vline_iterator.h"
55
#include "tiles3/kis_random_accessor.h"
56

57
#include "kis_default_bounds.h"
58

59
#include "kis_lod_transform.h"
60

61
#include "kis_raster_keyframe_channel.h"
62

63 64
#include "kis_paint_device_cache.h"
#include "kis_paint_device_data.h"
65
#include "kis_paint_device_frames_interface.h"
66

67 68
#include "kis_transform_worker.h"
#include "kis_filter_strategy.h"
69
#include "krita_utils.h"
70

71

72 73 74 75 76 77 78 79
struct KisPaintDeviceSPStaticRegistrar {
    KisPaintDeviceSPStaticRegistrar() {
        qRegisterMetaType<KisPaintDeviceSP>("KisPaintDeviceSP");
    }
};
static KisPaintDeviceSPStaticRegistrar __registrar;


80

81
struct KisPaintDevice::Private
82
{
83 84 85 86 87 88
    /**
     * Used when the paint device is loading to ensure no lod/animation
     * interferes the process.
     */
    static const KisDefaultBoundsSP transitionalDefaultBounds;

89
public:
90

91 92
    class KisPaintDeviceStrategy;
    class KisPaintDeviceWrappedStrategy;
93

94 95 96
    class DeviceChangeProfileCommand;
    class DeviceChangeColorSpaceCommand;

97
    Private(KisPaintDevice *paintDevice);
98
    ~Private();
99

100
    KisPaintDevice *q;
101
    KisNodeWSP parent;
102
    QScopedPointer<KisRasterKeyframeChannel> contentChannel;
103
    KisDefaultBoundsBaseSP defaultBounds;
104 105
    QScopedPointer<KisPaintDeviceStrategy> basicStrategy;
    QScopedPointer<KisPaintDeviceWrappedStrategy> wrappedStrategy;
106 107
    QMutex m_wrappedStrategyMutex;

108
    QScopedPointer<KisPaintDeviceFramesInterface> framesInterface;
109
    bool isProjectionDevice;
110

111
    KisPaintDeviceStrategy* currentStrategy();
112

113
    void init(const KoColorSpace *cs, const quint8 *defaultPixel);
114
    void convertColorSpace(const KoColorSpace * dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags, KUndo2Command *parentCommand);
115
    bool assignProfile(const KoColorProfile * profile, KUndo2Command *parentCommand);
116

117 118 119 120 121 122 123 124
    inline const KoColorSpace* colorSpace() const
    {
        return currentData()->colorSpace();
    }
    inline KisDataManagerSP dataManager() const
    {
        return currentData()->dataManager();
    }
125

126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
    inline qint32 x() const
    {
        return currentData()->x();
    }
    inline qint32 y() const
    {
        return currentData()->y();
    }
    inline void setX(qint32 x)
    {
        currentData()->setX(x);
    }
    inline void setY(qint32 y)
    {
        currentData()->setY(y);
    }
142

143 144 145 146
    inline KisPaintDeviceCache* cache()
    {
        return currentData()->cache();
    }
147

148 149 150 151
    inline KisIteratorCompleteListener* cacheInvalidator() {
        return currentData()->cacheInvalidator();
    }

152

153 154
    void cloneAllDataObjects(Private *rhs, bool copyFrames)
    {
155 156 157 158 159 160 161 162 163 164

        m_lodData.reset();
        m_externalFrameData.reset();

        if (!m_frames.isEmpty()) {
            m_frames.clear();
        }

        if (!copyFrames) {
            if (m_data) {
165
                m_data->prepareClone(rhs->currentNonLodData(), true);
166
            } else {
167
                m_data = toQShared(new KisPaintDeviceData(q, rhs->currentNonLodData(), true));
168 169 170
            }
        } else {
            if (m_data && !rhs->m_data) {
171
                m_data.clear();
172
            } else if (!m_data && rhs->m_data) {
173
                m_data = toQShared(new KisPaintDeviceData(q, rhs->m_data.data(), true));
174
            } else if (m_data && rhs->m_data) {
175
                m_data->prepareClone(rhs->m_data.data(), true);
176 177 178 179 180 181 182
            }

            if (!rhs->m_frames.isEmpty()) {
                FramesHash::const_iterator it = rhs->m_frames.constBegin();
                FramesHash::const_iterator end = rhs->m_frames.constEnd();

                for (; it != end; ++it) {
183
                    DataSP data = toQShared(new KisPaintDeviceData(q, it.value().data(), true));
184
                    m_frames.insert(it.key(), data);
185 186
                }
            }
187
            m_nextFreeFrameId = rhs->m_nextFreeFrameId;
188
        }
189 190

        if (rhs->m_lodData) {
191
            m_lodData.reset(new KisPaintDeviceData(q, rhs->m_lodData.data(), true));
192
        }
193 194
    }

195 196 197
    void prepareClone(KisPaintDeviceSP src)
    {
        prepareCloneImpl(src, src->m_d->currentData());
198
        KIS_SAFE_ASSERT_RECOVER_NOOP(fastBitBltPossible(src));
199
    }
Boudewijn Rempt's avatar
Boudewijn Rempt committed
200

201 202 203
    bool fastBitBltPossible(KisPaintDeviceSP src)
    {
        return fastBitBltPossibleImpl(src->m_d->currentData());
204
    }
Boudewijn Rempt's avatar
Boudewijn Rempt committed
205

206 207 208 209 210
    int currentFrameId() const
    {
        KIS_ASSERT_RECOVER(contentChannel) {
            return -1;
        }
211
        return !defaultBounds->currentLevelOfDetail() ?
212 213
               contentChannel->frameIdAt(defaultBounds->currentTime()) :
               -1;
214 215
    }

216 217
    KisDataManagerSP frameDataManager(int frameId) const
    {
218
        DataSP data = m_frames[frameId];
219
        return data->dataManager();
220 221
    }

222 223
    void invalidateFrameCache(int frameId)
    {
224
        DataSP data = m_frames[frameId];
225
        return data->cache()->invalidate();
226 227
    }

228 229 230 231 232
private:
    typedef KisPaintDeviceData Data;
    typedef QSharedPointer<Data> DataSP;
    typedef QHash<int, DataSP> FramesHash;

233 234
    class FrameInsertionCommand : public KUndo2Command
    {
235 236 237 238 239 240 241 242 243 244 245
    public:

        FrameInsertionCommand(FramesHash *hash, DataSP data, int frameId, bool insert, KUndo2Command *parentCommand)
            : KUndo2Command(parentCommand),
              m_hash(hash),
              m_data(data),
              m_frameId(frameId),
              m_insert(insert)
        {
        }

246
        void redo() override
247
        {
248 249
            doSwap(m_insert);
        }
250

251
        void undo() override
252
        {
253
            doSwap(!m_insert);
254
        }
255 256

    private:
257 258
        void doSwap(bool insert)
        {
259 260 261 262 263
            if (insert) {
                m_hash->insert(m_frameId, m_data);
            } else {
                DataSP deletedData = m_hash->take(m_frameId);
            }
264
        }
265

266 267 268 269 270 271 272 273 274
    private:
        FramesHash *m_hash;
        DataSP m_data;
        int m_frameId;
        bool m_insert;
    };

public:

275 276 277 278 279 280 281 282
    int getNextFrameId() {
        int frameId = 0;
        while (m_frames.contains(frameId = m_nextFreeFrameId++));
        KIS_SAFE_ASSERT_RECOVER_NOOP(!m_frames.contains(frameId));

        return frameId;
    }

283
    int createFrame(bool copy, int copySrc, const QPoint &offset, KUndo2Command *parentCommand)
284
    {
285 286 287
        KIS_ASSERT_RECOVER(parentCommand) {
            return -1;
        }
288

289
        DataSP data;
290
        bool initialFrame = false;
291

292
        if (m_frames.isEmpty()) {
293 294 295 296 297
            /**
             * Here we move the contents of the paint device to the
             * new frame and clear m_data to make the "background" for
             * the areas where there is no frame at all.
             */
298
            data = toQShared(new Data(q, m_data.data(), true));
299 300
            m_data->dataManager()->clear();
            m_data->cache()->invalidate();
301
            initialFrame = true;
302

303
        } else if (copy) {
304
            DataSP srcData = m_frames[copySrc];
305
            data = toQShared(new Data(q, srcData.data(), true));
306
        } else {
307
            DataSP srcData = m_frames.begin().value();
308
            data = toQShared(new Data(q, srcData.data(), false));
309
        }
310

311
        if (!initialFrame && !copy) {
312 313 314
            data->setX(offset.x());
            data->setY(offset.y());
        }
315

316
        int frameId = getNextFrameId();
317

318 319
        KUndo2Command *cmd =
            new FrameInsertionCommand(&m_frames,
320
                                      data,
321 322 323 324
                                      frameId, true,
                                      parentCommand);

        cmd->redo();
325

326
        return frameId;
327
    }
328

329
    void deleteFrame(int frame, KUndo2Command *parentCommand)
330
    {
331
        KIS_ASSERT_RECOVER_RETURN(m_frames.contains(frame));
332
        KIS_ASSERT_RECOVER_RETURN(parentCommand);
333

334 335
        DataSP deletedData = m_frames[frame];

336 337
        KUndo2Command *cmd =
            new FrameInsertionCommand(&m_frames,
338
                                      deletedData,
339 340 341
                                      frame, false,
                                      parentCommand);
        cmd->redo();
342 343
    }

344 345
    QRect frameBounds(int frameId)
    {
346
        DataSP data = m_frames[frameId];
347

348 349
        QRect extent = data->dataManager()->extent();
        extent.translate(data->x(), data->y());
350 351 352 353

        return extent;
    }

354 355
    QPoint frameOffset(int frameId) const
    {
356
        DataSP data = m_frames[frameId];
357 358 359
        return QPoint(data->x(), data->y());
    }

360 361
    void setFrameOffset(int frameId, const QPoint &offset)
    {
362
        DataSP data = m_frames[frameId];
363 364 365 366
        data->setX(offset.x());
        data->setY(offset.y());
    }

367 368
    const QList<int> frameIds() const
    {
369 370 371
        return m_frames.keys();
    }

372 373
    bool readFrame(QIODevice *stream, int frameId)
    {
374
        bool retval = false;
375
        DataSP data = m_frames[frameId];
376 377 378 379 380
        retval = data->dataManager()->read(stream);
        data->cache()->invalidate();
        return retval;
    }

381 382
    bool writeFrame(KisPaintDeviceWriter &store, int frameId)
    {
383
        DataSP data = m_frames[frameId];
384
        return data->dataManager()->write(store);
385 386
    }

387
    void setFrameDefaultPixel(const KoColor &defPixel, int frameId)
388
    {
389
        DataSP data = m_frames[frameId];
390 391 392
        KoColor color(defPixel);
        color.convertTo(data->colorSpace());
        data->dataManager()->setDefaultPixel(color.data());
393 394
    }

395
    KoColor frameDefaultPixel(int frameId) const
396
    {
Boudewijn Rempt's avatar
Boudewijn Rempt committed
397
        DataSP data = m_frames[frameId];
398 399
        return KoColor(data->dataManager()->defaultPixel(),
                       data->colorSpace());
400
    }
401

402
    void fetchFrame(int frameId, KisPaintDeviceSP targetDevice);
403
    void uploadFrame(int srcFrameId, int dstFrameId, KisPaintDeviceSP srcDevice);
404 405
    void uploadFrame(int dstFrameId, KisPaintDeviceSP srcDevice);
    void uploadFrameData(DataSP srcData, DataSP dstData);
406

407
    struct LodDataStructImpl;
408 409 410 411
    LodDataStruct* createLodDataStruct(int lod);
    void updateLodDataStruct(LodDataStruct *dst, const QRect &srcRect);
    void uploadLodDataStruct(LodDataStruct *dst);
    QRegion regionForLodSyncing() const;
412

413 414 415 416 417 418
    void updateLodDataManager(KisDataManager *srcDataManager,
                              KisDataManager *dstDataManager, const QPoint &srcOffset, const QPoint &dstOffset,
                              const QRect &originalRect, int lod);

    void generateLodCloneDevice(KisPaintDeviceSP dst, const QRect &originalRect, int lod);

419 420
    void tesingFetchLodDevice(KisPaintDeviceSP targetDevice);

421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452

private:
    qint64 estimateDataSize(Data *data) const {
        const QRect &rc = data->dataManager()->extent();
        return rc.width() * rc.height() * data->colorSpace()->pixelSize();
    }

public:

    void estimateMemoryStats(qint64 &imageData, qint64 &temporaryData, qint64 &lodData) const {
        imageData = 0;
        temporaryData = 0;
        lodData = 0;

        if (m_data) {
            imageData += estimateDataSize(m_data.data());
        }

        if (m_lodData) {
            lodData += estimateDataSize(m_lodData.data());
        }

        if (m_externalFrameData) {
            temporaryData += estimateDataSize(m_externalFrameData.data());
        }

        Q_FOREACH (DataSP value, m_frames.values()) {
            imageData += estimateDataSize(value.data());
        }
    }


453
private:
454

455
    QRegion syncWholeDevice(Data *srcData);
456

457 458
    inline DataSP currentFrameData() const
    {
459 460
        DataSP data;

461 462 463
        const int numberOfFrames = contentChannel->keyframeCount();

        if (numberOfFrames > 1) {
464
            int frameId = contentChannel->frameIdAt(defaultBounds->currentTime());
465 466 467 468 469 470 471 472

            if (frameId == -1) {
                data = m_data;
            } else {
                KIS_ASSERT_RECOVER(m_frames.contains(frameId)) {
                    return m_frames.begin().value();
                }
                data = m_frames[frameId];
473
            }
474
        } else if (numberOfFrames == 1) {
475
            data = m_frames.begin().value();
476 477
        } else {
            data = m_data;
478 479 480 481 482
        }

        return data;
    }

483 484
    inline Data* currentNonLodData() const
    {
485
        Data *data = m_data.data();
486

487
        if (contentChannel) {
488
            data = currentFrameData().data();
489
        } else if (isProjectionDevice && defaultBounds->externalFrameActive()) {
490 491
            if (!m_externalFrameData) {
                QMutexLocker l(&m_dataSwitchLock);
492
                if (!m_externalFrameData) {
493
                    m_externalFrameData.reset(new Data(q, m_data.data(), false));
494
                }
495
            }
496
            data = m_externalFrameData.data();
497 498
        }

499
        return data;
500 501
    }

502 503
    inline void ensureLodDataPresent() const
    {
504 505 506 507 508
        if (!m_lodData) {
            Data *srcData = currentNonLodData();

            QMutexLocker l(&m_dataSwitchLock);
            if (!m_lodData) {
509
                m_lodData.reset(new Data(q, srcData, false));
510 511 512 513
            }
        }
    }

514 515
    inline Data* currentData() const
    {
516
        Data *data;
517

518
        if (defaultBounds->currentLevelOfDetail()) {
519
            ensureLodDataPresent();
520 521 522
            data = m_lodData.data();
        } else {
            data = currentNonLodData();
523 524
        }

525
        return data;
526
    }
527

528 529
    void prepareCloneImpl(KisPaintDeviceSP src, Data *srcData)
    {
530 531 532 533 534 535 536
        /**
         * The result of currentData() depends on the current
         * level of detail and animation frame index. So we
         * should first connect the device to the new
         * default bounds object, and only after that ask
         * currentData() to start cloning.
         */
537
        q->setDefaultPixel(KoColor(srcData->dataManager()->defaultPixel(), colorSpace()));
538
        q->setDefaultBounds(src->defaultBounds());
539 540

        currentData()->prepareClone(srcData);
541
    }
542

543 544
    bool fastBitBltPossibleImpl(Data *srcData)
    {
545
        return x() == srcData->x() && y() == srcData->y() &&
546
               *colorSpace() == *srcData->colorSpace();
547
    }
548

549 550
    QList<Data*> allDataObjects() const
    {
551
        QList<Data*> dataObjects;
552

553
        if (m_frames.isEmpty()) {
554
            dataObjects << m_data.data();
555
        }
556 557
        dataObjects << m_lodData.data();
        dataObjects << m_externalFrameData.data();
558

559
        Q_FOREACH (DataSP value, m_frames.values()) {
560 561
            dataObjects << value.data();
        }
562 563 564 565

        return dataObjects;
    }

566 567
    void transferFromData(Data *data, KisPaintDeviceSP targetDevice);

568
    struct Q_DECL_HIDDEN StrategyPolicy;
569 570 571
    typedef KisSequentialIteratorBase<ReadOnlyIteratorPolicy<StrategyPolicy>, StrategyPolicy> InternalSequentialConstIterator;
    typedef KisSequentialIteratorBase<WritableIteratorPolicy<StrategyPolicy>, StrategyPolicy> InternalSequentialIterator;

572 573 574
private:
    friend class KisPaintDeviceFramesInterface;

575
private:
576
    DataSP m_data;
577
    mutable QScopedPointer<Data> m_lodData;
578
    mutable QScopedPointer<Data> m_externalFrameData;
579
    mutable QMutex m_dataSwitchLock;
580 581

    FramesHash m_frames;
582
    int m_nextFreeFrameId;
583 584
};

585 586
const KisDefaultBoundsSP KisPaintDevice::Private::transitionalDefaultBounds = new KisDefaultBounds();

587 588 589 590
#include "kis_paint_device_strategies.h"

KisPaintDevice::Private::Private(KisPaintDevice *paintDevice)
    : q(paintDevice),
591
      basicStrategy(new KisPaintDeviceStrategy(paintDevice, this)),
Dmitry Kazakov's avatar
Dmitry Kazakov committed
592
      isProjectionDevice(false),
593
      m_data(new Data(paintDevice)),
594
      m_nextFreeFrameId(0)
595 596 597
{
}

598
KisPaintDevice::Private::~Private()
599
{
600
    m_frames.clear();
601 602
}

Boudewijn Rempt's avatar
Boudewijn Rempt committed
603
KisPaintDevice::Private::KisPaintDeviceStrategy* KisPaintDevice::Private::currentStrategy()
604 605 606 607 608
{
    if (!defaultBounds->wrapAroundMode()) {
        return basicStrategy.data();
    }

609 610
    const QRect wrapRect = defaultBounds->bounds();

611
    if (!wrappedStrategy || wrappedStrategy->wrapRect() != wrapRect) {
612 613 614 615 616 617 618
        QMutexLocker locker(&m_wrappedStrategyMutex);

        if (!wrappedStrategy) {
            wrappedStrategy.reset(new KisPaintDeviceWrappedStrategy(wrapRect, q, this));
        }  else if (wrappedStrategy->wrapRect() != wrapRect) {
            wrappedStrategy->setWrapRect(wrapRect);
        }
619 620 621 622 623
    }

    return wrappedStrategy.data();
}

624 625
struct KisPaintDevice::Private::StrategyPolicy {
    StrategyPolicy(KisPaintDevice::Private::KisPaintDeviceStrategy *strategy,
626 627 628 629 630 631 632
                   KisDataManager *dataManager, qint32 offsetX, qint32 offsetY)
        : m_strategy(strategy),
          m_dataManager(dataManager),
          m_offsetX(offsetX),
          m_offsetY(offsetY)
    {
    }
633

634 635
    KisHLineConstIteratorSP createConstIterator(const QRect &rect)
    {
636
        return m_strategy->createHLineConstIteratorNG(m_dataManager, rect.x(), rect.y(), rect.width(), m_offsetX, m_offsetY);
637 638
    }

639 640
    KisHLineIteratorSP createIterator(const QRect &rect)
    {
641
        return m_strategy->createHLineIteratorNG(m_dataManager, rect.x(), rect.y(), rect.width(), m_offsetX, m_offsetY);
642 643
    }

644 645
    int pixelSize() const
    {
646 647 648 649 650 651
        return m_dataManager->pixelSize();
    }


    KisPaintDeviceStrategy *m_strategy;
    KisDataManager *m_dataManager;
652 653
    int m_offsetX;
    int m_offsetY;
654 655
};

656
struct KisPaintDevice::Private::LodDataStructImpl : public KisPaintDevice::LodDataStruct {
657 658
    LodDataStructImpl(Data *_lodData) : lodData(_lodData) {}
    QScopedPointer<Data> lodData;
659
};
660

661 662 663 664 665
QRegion KisPaintDevice::Private::regionForLodSyncing() const
{
    Data *srcData = currentNonLodData();
    return srcData->dataManager()->region().translated(srcData->x(), srcData->y());
}
666

667
KisPaintDevice::LodDataStruct* KisPaintDevice::Private::createLodDataStruct(int newLod)
668
{
669 670
    KIS_SAFE_ASSERT_RECOVER_NOOP(newLod > 0);

671
    Data *srcData = currentNonLodData();
672

673
    Data *lodData = new Data(q, srcData, false);
674
    LodDataStruct *lodStruct = new LodDataStructImpl(lodData);
675

676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703
    int expectedX = KisLodTransform::coordToLodCoord(srcData->x(), newLod);
    int expectedY = KisLodTransform::coordToLodCoord(srcData->y(), newLod);

    /**
     * We compare color spaces as pure pointers, because they must be
     * exactly the same, since they come from the common source.
     */
    if (lodData->levelOfDetail() != newLod ||
        lodData->colorSpace() != srcData->colorSpace() ||
        lodData->x() != expectedX ||
        lodData->y() != expectedY) {


        lodData->prepareClone(srcData);

        lodData->setLevelOfDetail(newLod);
        lodData->setX(expectedX);
        lodData->setY(expectedY);

        // FIXME: different kind of synchronization
    }

    //QRegion dirtyRegion = syncWholeDevice(srcData);
    lodData->cache()->invalidate();

    return lodStruct;
}

704 705 706 707 708 709
void KisPaintDevice::Private::updateLodDataManager(KisDataManager *srcDataManager,
                                                   KisDataManager *dstDataManager,
                                                   const QPoint &srcOffset,
                                                   const QPoint &dstOffset,
                                                   const QRect &originalRect,
                                                   int lod)
710
{
711 712
    if (originalRect.isEmpty()) return;

713 714
    const int srcStepSize = 1 << lod;

715 716
    KIS_ASSERT_RECOVER_RETURN(lod > 0);

717 718 719
    const QRect srcRect = KisLodTransform::alignedRect(originalRect, lod);
    const QRect dstRect = KisLodTransform::scaledRect(srcRect, lod);
    if (!srcRect.isValid() || !dstRect.isValid()) return;
720

721
    KIS_ASSERT_RECOVER_NOOP(srcRect.width() / srcStepSize == dstRect.width());
722

723
    const int pixelSize = srcDataManager->pixelSize();
724

725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751
    int rowsAccumulated = 0;
    int columnsAccumulated = 0;

    KoMixColorsOp *mixOp = colorSpace()->mixColorsOp();

    QScopedArrayPointer<quint8> blendData(new quint8[srcStepSize * srcRect.width() * pixelSize]);
    quint8 *blendDataPtr = blendData.data();
    int blendDataOffset = 0;

    const int srcCellSize = srcStepSize * srcStepSize;
    const int srcCellStride = srcCellSize * pixelSize;
    const int srcStepStride = srcStepSize * pixelSize;
    const int srcColumnStride = (srcStepSize - 1) * srcStepStride;

    QScopedArrayPointer<qint16> weights(new qint16[srcCellSize]);

    {
        const qint16 averageWeight = qCeil(255.0 / srcCellSize);
        const qint16 extraWeight = averageWeight * srcCellSize - 255;
        KIS_ASSERT_RECOVER_NOOP(extraWeight == 1);

        for (int i = 0; i < srcCellSize - 1; i++) {
            weights[i] = averageWeight;
        }
        weights[srcCellSize - 1] = averageWeight - extraWeight;
    }

752 753
    InternalSequentialConstIterator srcIntIt(StrategyPolicy(currentStrategy(), srcDataManager, srcOffset.x(), srcOffset.y()), srcRect);
    InternalSequentialIterator dstIntIt(StrategyPolicy(currentStrategy(), dstDataManager, dstOffset.x(), dstOffset.y()), dstRect);
754

755 756 757 758
    int rowsRemaining = srcRect.height();
    while (rowsRemaining > 0) {

        int colsRemaining = srcRect.width();
759
        while (colsRemaining > 0 && srcIntIt.nextPixel()) {
760

761 762 763 764 765 766 767 768
            memcpy(blendDataPtr, srcIntIt.rawDataConst(), pixelSize);
            blendDataPtr += pixelSize;
            columnsAccumulated++;

            if (columnsAccumulated >= srcStepSize) {
                blendDataPtr += srcColumnStride;
                columnsAccumulated = 0;
            }
769

770
            colsRemaining--;
771 772
        }

773 774 775 776 777 778 779
        rowsAccumulated++;

        if (rowsAccumulated >= srcStepSize) {

            // blend and write the final data
            blendDataPtr = blendData.data();

780 781 782
            int colsRemaining = dstRect.width();
            while (colsRemaining > 0 && dstIntIt.nextPixel()) {
                mixOp->mixColors(blendDataPtr, weights.data(), srcCellSize, dstIntIt.rawData());
783
                blendDataPtr += srcCellStride;
784 785

                colsRemaining--;
786 787 788 789 790 791 792 793 794
            }

            // reset counters
            rowsAccumulated = 0;
            blendDataPtr = blendData.data();
            blendDataOffset = 0;
        } else {
            blendDataOffset += srcStepStride;
            blendDataPtr = blendData.data() + blendDataOffset;
795
        }
796 797

        rowsRemaining--;
798
    }
799 800
}

801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827
void KisPaintDevice::Private::updateLodDataStruct(LodDataStruct *_dst, const QRect &originalRect)
{
    LodDataStructImpl *dst = dynamic_cast<LodDataStructImpl*>(_dst);
    KIS_SAFE_ASSERT_RECOVER_RETURN(dst);

    Data *lodData = dst->lodData.data();
    Data *srcData = currentNonLodData();

    const int lod = lodData->levelOfDetail();

    updateLodDataManager(srcData->dataManager().data(), lodData->dataManager().data(),
                         QPoint(srcData->x(), srcData->y()),
                         QPoint(lodData->x(), lodData->y()),
                         originalRect, lod);
}

void KisPaintDevice::Private::generateLodCloneDevice(KisPaintDeviceSP dst, const QRect &originalRect, int lod)
{
    KIS_SAFE_ASSERT_RECOVER_RETURN(fastBitBltPossible(dst));

    Data *srcData = currentNonLodData();
    updateLodDataManager(srcData->dataManager().data(), dst->dataManager().data(),
                         QPoint(srcData->x(), srcData->y()),
                         QPoint(dst->x(), dst->y()),
                         originalRect, lod);
}

828
void KisPaintDevice::Private::uploadLodDataStruct(LodDataStruct *_dst)
829
{
830
    LodDataStructImpl *dst = dynamic_cast<LodDataStructImpl*>(_dst);
831
    KIS_SAFE_ASSERT_RECOVER_RETURN(dst);
832

833
    KIS_SAFE_ASSERT_RECOVER_RETURN(
834
        dst->lodData->levelOfDetail() == defaultBounds->currentLevelOfDetail());
835

836
    ensureLodDataPresent();
837

838
    m_lodData->prepareClone(dst->lodData.data());
839
    m_lodData->dataManager()->bitBltRough(dst->lodData->dataManager(), dst->lodData->dataManager()->extent());
840 841
}

842
void KisPaintDevice::Private::transferFromData(Data *data, KisPaintDeviceSP targetDevice)
843
{
844 845
    QRect extent = data->dataManager()->extent();
    extent.translate(data->x(), data->y());
846 847

    targetDevice->m_d->prepareCloneImpl(q, data);
848
    targetDevice->m_d->currentStrategy()->fastBitBltRough(data->dataManager(), extent);
849 850
}

851 852
void KisPaintDevice::Private::fetchFrame(int frameId, KisPaintDeviceSP targetDevice)
{
853 854
    DataSP data = m_frames[frameId];
    transferFromData(data.data(), targetDevice);
855 856
}

857 858 859 860 861 862 863 864
void KisPaintDevice::Private::uploadFrame(int srcFrameId, int dstFrameId, KisPaintDeviceSP srcDevice)
{
    DataSP dstData = m_frames[dstFrameId];
    KIS_ASSERT_RECOVER_RETURN(dstData);

    DataSP srcData = srcDevice->m_d->m_frames[srcFrameId];
    KIS_ASSERT_RECOVER_RETURN(srcData);

865 866 867
    uploadFrameData(srcData, dstData);
}

868 869
void KisPaintDevice::Private::uploadFrame(int dstFrameId, KisPaintDeviceSP srcDevice)
{
870 871 872 873 874 875 876 877 878
    DataSP dstData = m_frames[dstFrameId];
    KIS_ASSERT_RECOVER_RETURN(dstData);

    DataSP srcData = srcDevice->m_d->m_data;
    KIS_ASSERT_RECOVER_RETURN(srcData);

    uploadFrameData(srcData, dstData);
}

879 880
void KisPaintDevice::Private::uploadFrameData(DataSP srcData, DataSP dstData)
{
881
    if (srcData->colorSpace() != dstData->colorSpace() &&
882
        *srcData->colorSpace() != *dstData->colorSpace()) {
883 884 885

        KUndo2Command tempCommand;

886
        srcData = toQShared(new Data(q, srcData.data(), true));
887 888 889 890 891 892 893 894 895 896 897
        srcData->convertDataColorSpace(dstData->colorSpace(),
                                       KoColorConversionTransformation::internalRenderingIntent(),
                                       KoColorConversionTransformation::internalConversionFlags(),
                                       &tempCommand);
    }

    dstData->dataManager()->clear();
    dstData->cache()->invalidate();

    const QRect rect = srcData->dataManager()->extent();
    dstData->dataManager()->bitBltRough(srcData->dataManager(), rect);
898 899
    dstData->setX(srcData->x());
    dstData->setY(srcData->y());
900 901
}

902 903 904 905 906 907 908 909
void KisPaintDevice::Private::tesingFetchLodDevice(KisPaintDeviceSP targetDevice)
{
    Data *data = m_lodData.data();
    Q_ASSERT(data);

    transferFromData(data, targetDevice);
}

910
class KisPaintDevice::Private::DeviceChangeProfileCommand : public KUndo2Command
911
{
912 913 914 915 916 917 918
public:
    DeviceChangeProfileCommand(KisPaintDeviceSP device, KUndo2Command *parent = 0)
        : KUndo2Command(parent),
          m_firstRun(true),
          m_device(device)
    {
    }
919

920
    virtual void emitNotifications()
921
    {
922 923
        m_device->emitProfileChanged();
    }
924

925 926
    void redo() override
    {
927
        if (m_firstRun) {
928 929
            m_firstRun = false;
            return;
930 931
        }

932
        KUndo2Command::redo();
933 934
        emitNotifications();
    }
935

936 937 938 939 940
    void undo() override
    {
        KUndo2Command::undo();
        emitNotifications();
    }
941

942 943
protected:
    KisPaintDeviceSP m_device;
944

945 946 947
private:
    bool m_firstRun;
};
948

949 950 951 952 953 954 955
class KisPaintDevice::Private::DeviceChangeColorSpaceCommand : public DeviceChangeProfileCommand
{
public:
    DeviceChangeColorSpaceCommand(KisPaintDeviceSP device, KUndo2Command *parent = 0)
        : DeviceChangeProfileCommand(device, parent)
    {
    }
956

957 958 959 960 961
    void emitNotifications() override
    {
        m_device->emitColorSpaceChanged();
    }
};
962

963 964
void KisPaintDevice::Private::convertColorSpace(const KoColorSpace * dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags, KUndo2Command *parentCommand)
{
965
    QList<Data*> dataObjects = allDataObjects();
966 967 968 969
    if (dataObjects.isEmpty()) return;

    KUndo2Command *mainCommand =
        parentCommand ? new DeviceChangeColorSpaceCommand(q, parentCommand) : 0;
970

971
    Q_FOREACH (Data *data, dataObjects) {
972 973
        if (!data) continue;

974
        data->convertDataColorSpace(dstColorSpace, renderingIntent, conversionFlags, mainCommand);
975 976
    }

977
    q->emitColorSpaceChanged();
978 979
}

980
bool KisPaintDevice::Private::assignProfile(const KoColorProfile * profile, KUndo2Command *parentCommand)
981
{
982 983 984 985 986 987
    if (!profile) return false;

    const KoColorSpace *dstColorSpace =
        KoColorSpaceRegistry::instance()->colorSpace(colorSpace()->colorModelId().id(), colorSpace()->colorDepthId().id(), profile);
    if (!dstColorSpace) return false;

988 989 990 991
    KUndo2Command *mainCommand =
        parentCommand ? new DeviceChangeColorSpaceCommand(q, parentCommand) : 0;


992
    QList<Data*> dataObjects = allDataObjects();
993
    Q_FOREACH (Data *data, dataObjects) {
994
        if (!data) continue;
995
        data->assignColorSpace(dstColorSpace, mainCommand);
996 997 998 999 1000
    }
    q->emitProfileChanged();

    // no undo information is provided here
    return true;
1001 1002
}

1003 1004
void KisPaintDevice::Private::init(const KoColorSpace *cs, const quint8 *defaultPixel)
{
1005
    QList<Data*> dataObjects = allDataObjects();
1006
    Q_FOREACH (Data *data, dataObjects) {
1007 1008 1009 1010 1011 1012 1013 1014
        if (!data) continue;

        KisDataManagerSP dataManager = new KisDataManager(cs->pixelSize(), defaultPixel);
        data->init(cs, dataManager);
    }
}

KisPaintDevice::KisPaintDevice(const KoColorSpace * colorSpace, const QString& name)
1015 1016
    : QObject(0)
    , m_d(new Private(this))
Patrick Julien's avatar
Patrick Julien committed
1017
{
1018
    init(colorSpace, new KisDefaultBounds(), 0, name);
1019
}
Boudewijn Rempt's avatar
Boudewijn Rempt committed
1020

1021
KisPaintDevice::KisPaintDevice(KisNodeWSP parent, const KoColorSpace * colorSpace, KisDefaultBoundsBaseSP defaultBounds, const QString& name)
1022 1023 1024
    : QObject(0)
    , m_d(new Private(this))
{
1025
    init(colorSpace, defaultBounds, parent, name);