File indexing completed on 2024-05-12 15:58:31

0001 /*
0002  *  SPDX-FileCopyrightText: 2002 Patrick Julien <freak@codepimps.org>
0003  *  SPDX-FileCopyrightText: 2004 Boudewijn Rempt <boud@valdyas.org>
0004  *  SPDX-FileCopyrightText: 2021 L. E. Segovia <amy@amyspark.me>
0005  *
0006  *  SPDX-License-Identifier: GPL-2.0-or-later
0007  */
0008 
0009 #include "kis_paint_device.h"
0010 
0011 #include <QRect>
0012 #include <QTransform>
0013 #include <QImage>
0014 #include <QList>
0015 #include <QHash>
0016 #include <QIODevice>
0017 #include <qmath.h>
0018 #include <KisRegion.h>
0019 
0020 #include <klocalizedstring.h>
0021 
0022 #include <KoChannelInfo.h>
0023 #include <KoColorProfile.h>
0024 #include <KoColor.h>
0025 #include <KoColorSpace.h>
0026 #include <KoColorSpaceRegistry.h>
0027 #include <KoColorModelStandardIds.h>
0028 #include <KoIntegerMaths.h>
0029 #include <KoMixColorsOp.h>
0030 #include <KoUpdater.h>
0031 
0032 #include "kis_image.h"
0033 #include "kis_random_sub_accessor.h"
0034 #include "kis_selection.h"
0035 #include "kis_node.h"
0036 #include "kis_datamanager.h"
0037 #include "kis_paint_device_writer.h"
0038 #include "kis_selection_component.h"
0039 #include "kis_pixel_selection.h"
0040 #include "kis_repeat_iterators_pixel.h"
0041 #include "kis_fixed_paint_device.h"
0042 
0043 #include "tiles3/kis_hline_iterator.h"
0044 #include "tiles3/kis_vline_iterator.h"
0045 #include "tiles3/kis_random_accessor.h"
0046 
0047 #include "kis_default_bounds.h"
0048 
0049 #include "kis_lod_transform.h"
0050 
0051 #include "kis_raster_keyframe_channel.h"
0052 
0053 #include "kis_paint_device_cache.h"
0054 #include "kis_paint_device_data.h"
0055 #include "kis_paint_device_frames_interface.h"
0056 
0057 #include "kis_transform_worker.h"
0058 #include "kis_filter_strategy.h"
0059 #include "krita_utils.h"
0060 
0061 
0062 struct KisPaintDeviceSPStaticRegistrar {
0063     KisPaintDeviceSPStaticRegistrar() {
0064         qRegisterMetaType<KisPaintDeviceSP>("KisPaintDeviceSP");
0065     }
0066 };
0067 static KisPaintDeviceSPStaticRegistrar __registrar;
0068 
0069 
0070 
0071 struct KisPaintDevice::Private
0072 {
0073     /**
0074      * Used when the paint device is loading to ensure no lod/animation
0075      * interferes the process.
0076      */
0077     static const KisDefaultBoundsSP transitionalDefaultBounds;
0078 
0079 public:
0080 
0081     class KisPaintDeviceStrategy;
0082     class KisPaintDeviceWrappedStrategy;
0083 
0084     class DeviceChangeProfileCommand;
0085     class DeviceChangeColorSpaceCommand;
0086 
0087     Private(KisPaintDevice *paintDevice);
0088     ~Private();
0089 
0090     KisPaintDevice *q;
0091     KisNodeWSP parent;
0092     QScopedPointer<KisRasterKeyframeChannel> contentChannel;
0093     KisDefaultBoundsBaseSP defaultBounds;
0094     QScopedPointer<KisPaintDeviceStrategy> basicStrategy;
0095     QScopedPointer<KisPaintDeviceWrappedStrategy> wrappedStrategy;
0096     QMutex m_wrappedStrategyMutex;
0097 
0098     QScopedPointer<KisPaintDeviceFramesInterface> framesInterface;
0099     bool isProjectionDevice;
0100 
0101     KisPaintDeviceStrategy* currentStrategy();
0102 
0103     void init(const KoColorSpace *cs, const quint8 *defaultPixel);
0104     void convertColorSpace(const KoColorSpace *dstColorSpace,
0105                            KoColorConversionTransformation::Intent renderingIntent,
0106                            KoColorConversionTransformation::ConversionFlags conversionFlags,
0107                            KUndo2Command *parentCommand,
0108                            KoUpdater *progressUpdater);
0109     bool assignProfile(const KoColorProfile * profile, KUndo2Command *parentCommand);
0110 
0111     KUndo2Command* reincarnateWithDetachedHistory(bool copyContent);
0112 
0113 
0114     inline const KoColorSpace* colorSpace() const
0115     {
0116         return currentData()->colorSpace();
0117     }
0118     inline KisDataManagerSP dataManager() const
0119     {
0120         return currentData()->dataManager();
0121     }
0122 
0123     inline qint32 x() const
0124     {
0125         return currentData()->x();
0126     }
0127     inline qint32 y() const
0128     {
0129         return currentData()->y();
0130     }
0131     inline void setX(qint32 x)
0132     {
0133         currentData()->setX(x);
0134     }
0135     inline void setY(qint32 y)
0136     {
0137         currentData()->setY(y);
0138     }
0139 
0140     inline KisPaintDeviceCache* cache()
0141     {
0142         return currentData()->cache();
0143     }
0144 
0145     inline KisIteratorCompleteListener* cacheInvalidator() {
0146         return currentData()->cacheInvalidator();
0147     }
0148 
0149     inline KisInterstrokeDataSP interstrokeData() const {
0150         return currentData()->interstrokeData();
0151     }
0152 
0153     inline KUndo2Command* createChangeInterstrokeDataCommand(KisInterstrokeDataSP value) {
0154         return currentData()->createChangeInterstrokeDataCommand(value);
0155     }
0156 
0157     void cloneAllDataObjects(Private *rhs, bool copyFrames)
0158     {
0159 
0160         m_lodData.reset();
0161         m_externalFrameData.reset();
0162 
0163         if (!m_frames.isEmpty()) {
0164             m_frames.clear();
0165         }
0166 
0167         if (!copyFrames) {
0168             if (m_data) {
0169                 m_data->prepareClone(rhs->currentNonLodData(), true);
0170             } else {
0171                 m_data = toQShared(new KisPaintDeviceData(q, rhs->currentNonLodData(), true));
0172             }
0173         } else {
0174             if (m_data && !rhs->m_data) {
0175                 m_data.clear();
0176             } else if (!m_data && rhs->m_data) {
0177                 m_data = toQShared(new KisPaintDeviceData(q, rhs->m_data.data(), true));
0178             } else if (m_data && rhs->m_data) {
0179                 m_data->prepareClone(rhs->m_data.data(), true);
0180             }
0181 
0182             if (!rhs->m_frames.isEmpty()) {
0183                 FramesHash::const_iterator it = rhs->m_frames.constBegin();
0184                 FramesHash::const_iterator end = rhs->m_frames.constEnd();
0185 
0186                 for (; it != end; ++it) {
0187                     DataSP data = toQShared(new KisPaintDeviceData(q, it.value().data(), true));
0188                     m_frames.insert(it.key(), data);
0189                 }
0190             }
0191             m_nextFreeFrameId = rhs->m_nextFreeFrameId;
0192         }
0193 
0194         if (rhs->m_lodData) {
0195             m_lodData.reset(new KisPaintDeviceData(q, rhs->m_lodData.data(), true));
0196         }
0197     }
0198 
0199     void prepareClone(KisPaintDeviceSP src)
0200     {
0201         prepareCloneImpl(src, src->m_d->currentData());
0202         KIS_SAFE_ASSERT_RECOVER_NOOP(fastBitBltPossible(src));
0203     }
0204 
0205     bool fastBitBltPossible(KisPaintDeviceSP src)
0206     {
0207         return fastBitBltPossibleImpl(src->m_d->currentData());
0208     }
0209 
0210     int currentFrameId() const
0211     {
0212         KIS_ASSERT_RECOVER(contentChannel) {
0213             return -1;
0214         }
0215         return !defaultBounds->currentLevelOfDetail() ?
0216                contentChannel->activeKeyframeAt<KisRasterKeyframe>(defaultBounds->currentTime())->frameID() :
0217                -1;
0218     }
0219 
0220     KisDataManagerSP frameDataManager(int frameId) const
0221     {
0222         DataSP data = m_frames[frameId];
0223         return data->dataManager();
0224     }
0225 
0226     void invalidateFrameCache(int frameId)
0227     {
0228         DataSP data = m_frames[frameId];
0229         return data->cache()->invalidate();
0230     }
0231 
0232 private:
0233     typedef KisPaintDeviceData Data;
0234     typedef QSharedPointer<Data> DataSP;
0235     typedef QHash<int, DataSP> FramesHash;
0236 
0237     class FrameInsertionCommand : public KUndo2Command
0238     {
0239     public:
0240 
0241         FrameInsertionCommand(FramesHash *hash, DataSP data, int frameId, bool insert, KUndo2Command *parentCommand)
0242             : KUndo2Command(parentCommand),
0243               m_hash(hash),
0244               m_data(data),
0245               m_frameId(frameId),
0246               m_insert(insert)
0247         {
0248         }
0249 
0250         void redo() override
0251         {
0252             doSwap(m_insert);
0253         }
0254 
0255         void undo() override
0256         {
0257             doSwap(!m_insert);
0258         }
0259 
0260     private:
0261         void doSwap(bool insert)
0262         {
0263             if (insert) {
0264                 m_hash->insert(m_frameId, m_data);
0265             } else {
0266                 DataSP deletedData = m_hash->take(m_frameId);
0267             }
0268         }
0269 
0270     private:
0271         FramesHash *m_hash;
0272         DataSP m_data;
0273         int m_frameId;
0274         bool m_insert;
0275     };
0276 
0277 public:
0278 
0279     int getNextFrameId() {
0280         int frameId = 0;
0281         while (m_frames.contains(frameId = m_nextFreeFrameId++));
0282         KIS_SAFE_ASSERT_RECOVER_NOOP(!m_frames.contains(frameId));
0283 
0284         return frameId;
0285     }
0286 
0287     int createFrame(bool copy, int copySrc, const QPoint &offset, KUndo2Command *parentCommand)
0288     {
0289         DataSP data;
0290         bool initialFrame = false;
0291 
0292         if (m_frames.isEmpty()) {
0293             /**
0294              * Here we move the contents of the paint device to the
0295              * new frame and clear m_data to make the "background" for
0296              * the areas where there is no frame at all.
0297              */
0298             data = toQShared(new Data(q, m_data.data(), true));
0299             m_data->dataManager()->clear();
0300             m_data->cache()->invalidate();
0301             initialFrame = true;
0302 
0303         } else if (copy) {
0304             DataSP srcData = m_frames[copySrc];
0305             data = toQShared(new Data(q, srcData.data(), true));
0306         } else {
0307             DataSP srcData = m_frames.begin().value();
0308             data = toQShared(new Data(q, srcData.data(), false));
0309         }
0310 
0311         if (!initialFrame && !copy) {
0312             data->setX(offset.x());
0313             data->setY(offset.y());
0314         }
0315 
0316         int frameId = getNextFrameId();
0317 
0318         if (parentCommand) {
0319             KUndo2Command *cmd =
0320                 new FrameInsertionCommand(&m_frames,
0321                                           data,
0322                                           frameId, true,
0323                                           parentCommand);
0324 
0325             cmd->redo();
0326         } else {
0327             m_frames.insert(frameId, data);
0328         }
0329 
0330         return frameId;
0331     }
0332 
0333     void deleteFrame(int frameID, KUndo2Command *parentCommand)
0334     {
0335         KIS_SAFE_ASSERT_RECOVER_RETURN(m_frames.contains(frameID));
0336         DataSP deletedData = m_frames[frameID];
0337 
0338         if(parentCommand) {
0339             KUndo2Command *cmd =
0340                 new FrameInsertionCommand(&m_frames,
0341                                           deletedData,
0342                                           frameID, false,
0343                                           parentCommand);
0344             cmd->redo();
0345         } else {
0346             m_frames.take(frameID);
0347         }
0348     }
0349 
0350     QRect frameBounds(int frameId)
0351     {
0352         DataSP data = m_frames[frameId];
0353 
0354         QRect extent = data->dataManager()->extent();
0355         extent.translate(data->x(), data->y());
0356 
0357         quint8 defaultOpacity = data->colorSpace()->opacityU8(data->dataManager()->defaultPixel());
0358 
0359         if (defaultOpacity != OPACITY_TRANSPARENT_U8) {
0360             extent |= defaultBounds->bounds();
0361         }
0362 
0363         return extent;
0364     }
0365 
0366     QPoint frameOffset(int frameId) const
0367     {
0368         DataSP data = m_frames[frameId];
0369         return QPoint(data->x(), data->y());
0370     }
0371 
0372     void setFrameOffset(int frameId, const QPoint &offset)
0373     {
0374         DataSP data = m_frames[frameId];
0375         data->setX(offset.x());
0376         data->setY(offset.y());
0377     }
0378 
0379     const QList<int> frameIds() const
0380     {
0381         return m_frames.keys();
0382     }
0383 
0384     bool readFrame(QIODevice *stream, int frameId)
0385     {
0386         bool retval = false;
0387         DataSP data = m_frames[frameId];
0388         retval = data->dataManager()->read(stream);
0389         data->cache()->invalidate();
0390         return retval;
0391     }
0392 
0393     bool writeFrame(KisPaintDeviceWriter &store, int frameId)
0394     {
0395         DataSP data = m_frames[frameId];
0396         return data->dataManager()->write(store);
0397     }
0398 
0399     void setFrameDefaultPixel(const KoColor &defPixel, int frameId)
0400     {
0401         DataSP data = m_frames[frameId];
0402         KoColor color(defPixel);
0403         color.convertTo(data->colorSpace());
0404         data->dataManager()->setDefaultPixel(color.data());
0405     }
0406 
0407     KoColor frameDefaultPixel(int frameId) const
0408     {
0409         DataSP data = m_frames[frameId];
0410         return KoColor(data->dataManager()->defaultPixel(),
0411                        data->colorSpace());
0412     }
0413 
0414     void writeFrameToDevice(int frameId, KisPaintDeviceSP targetDevice);
0415     void uploadFrame(int srcFrameId, int dstFrameId, KisPaintDeviceSP srcDevice);
0416     void uploadFrame(int dstFrameId, KisPaintDeviceSP srcDevice);
0417     void uploadFrameData(DataSP srcData, DataSP dstData);
0418 
0419     struct LodDataStructImpl;
0420     LodDataStruct* createLodDataStruct(int lod);
0421     void updateLodDataStruct(LodDataStruct *dst, const QRect &srcRect);
0422     void uploadLodDataStruct(LodDataStruct *dst);
0423     KisRegion regionForLodSyncing() const;
0424 
0425     void updateLodDataManager(KisDataManager *srcDataManager,
0426                               KisDataManager *dstDataManager, const QPoint &srcOffset, const QPoint &dstOffset,
0427                               const QRect &originalRect, int lod);
0428 
0429     void generateLodCloneDevice(KisPaintDeviceSP dst, const QRect &originalRect, int lod);
0430 
0431     void tesingFetchLodDevice(KisPaintDeviceSP targetDevice);
0432 
0433 
0434 private:
0435     qint64 estimateDataSize(Data *data) const {
0436         const QRect &rc = data->dataManager()->extent();
0437         return qint64(rc.width()) * rc.height() * data->colorSpace()->pixelSize();
0438     }
0439 
0440 public:
0441 
0442     void estimateMemoryStats(qint64 &imageData, qint64 &temporaryData, qint64 &lodData) const {
0443         imageData = 0;
0444         temporaryData = 0;
0445         lodData = 0;
0446 
0447         if (m_data) {
0448             imageData += estimateDataSize(m_data.data());
0449         }
0450 
0451         if (m_lodData) {
0452             lodData += estimateDataSize(m_lodData.data());
0453         }
0454 
0455         if (m_externalFrameData) {
0456             temporaryData += estimateDataSize(m_externalFrameData.data());
0457         }
0458 
0459         Q_FOREACH (DataSP value, m_frames.values()) {
0460             imageData += estimateDataSize(value.data());
0461         }
0462     }
0463 
0464 
0465 private:
0466 
0467     inline DataSP currentFrameData() const
0468     {
0469         DataSP data;
0470 
0471         const int vFramesCount = contentChannel->keyframeCount();
0472 
0473         if (vFramesCount >= 1) {
0474             KisRasterKeyframeSP keyframe =  contentChannel->activeKeyframeAt<KisRasterKeyframe>(defaultBounds->currentTime());
0475             if (!keyframe || keyframe->frameID() < 0) {
0476                 return m_data;
0477             }
0478 
0479             const int frameID = keyframe->frameID();
0480             KIS_ASSERT_RECOVER(m_frames.contains(frameID)) {
0481                 return m_data;
0482             }
0483 
0484             data = m_frames[frameID];
0485         } else {
0486             data = m_data;
0487         }
0488 
0489         return data;
0490     }
0491 
0492     inline Data* currentNonLodData() const
0493     {
0494         Data *data = m_data.data();
0495 
0496         if (contentChannel) {
0497             data = currentFrameData().data();
0498         } else if (isProjectionDevice && defaultBounds->externalFrameActive()) {
0499             if (!m_externalFrameData) {
0500                 QMutexLocker l(&m_dataSwitchLock);
0501                 if (!m_externalFrameData) {
0502                     m_externalFrameData.reset(new Data(q, m_data.data(), false));
0503                 }
0504             }
0505             data = m_externalFrameData.data();
0506         }
0507 
0508         return data;
0509     }
0510 
0511     inline void ensureLodDataPresent() const
0512     {
0513         if (!m_lodData) {
0514             Data *srcData = currentNonLodData();
0515 
0516             QMutexLocker l(&m_dataSwitchLock);
0517             if (!m_lodData) {
0518                 m_lodData.reset(new Data(q, srcData, false));
0519             }
0520         }
0521     }
0522 
0523     inline Data* currentData() const
0524     {
0525         Data *data;
0526 
0527         if (defaultBounds->currentLevelOfDetail()) {
0528             ensureLodDataPresent();
0529             data = m_lodData.data();
0530         } else {
0531             data = currentNonLodData();
0532         }
0533 
0534         return data;
0535     }
0536 
0537     void prepareCloneImpl(KisPaintDeviceSP src, Data *srcData)
0538     {
0539         /**
0540          * The result of currentData() depends on the current
0541          * level of detail and animation frame index. So we
0542          * should first connect the device to the new
0543          * default bounds object, and only after that ask
0544          * currentData() to start cloning.
0545          */
0546         q->setDefaultBounds(src->defaultBounds());
0547 
0548         currentData()->prepareClone(srcData);
0549 
0550 
0551         /**
0552          * Default pixel must be updated **after** the color space
0553          * of the device has been adjusted in prpareClone(). Otherwise,
0554          * colorSpace() of the resulting KoColor object will be
0555          * incorrect.
0556          */
0557         KIS_SAFE_ASSERT_RECOVER_RETURN(*colorSpace() == *src->colorSpace());
0558         q->setDefaultPixel(KoColor(srcData->dataManager()->defaultPixel(), colorSpace()));
0559 
0560     }
0561 
0562     bool fastBitBltPossibleImpl(Data *srcData)
0563     {
0564         return x() == srcData->x() && y() == srcData->y() &&
0565                *colorSpace() == *srcData->colorSpace();
0566     }
0567 
0568     QList<Data*> allDataObjects() const
0569     {
0570         QList<Data*> dataObjects;
0571 
0572         if (m_frames.isEmpty()) {
0573             dataObjects << m_data.data();
0574         }
0575         dataObjects << m_lodData.data();
0576         dataObjects << m_externalFrameData.data();
0577 
0578         Q_FOREACH (DataSP value, m_frames.values()) {
0579             dataObjects << value.data();
0580         }
0581 
0582         return dataObjects;
0583     }
0584 
0585     void transferFromData(Data *data, KisPaintDeviceSP targetDevice);
0586 
0587     struct Q_DECL_HIDDEN StrategyPolicy;
0588     typedef KisSequentialIteratorBase<ReadOnlyIteratorPolicy<StrategyPolicy>, StrategyPolicy> InternalSequentialConstIterator;
0589     typedef KisSequentialIteratorBase<WritableIteratorPolicy<StrategyPolicy>, StrategyPolicy> InternalSequentialIterator;
0590 
0591 private:
0592     friend class KisPaintDeviceFramesInterface;
0593 
0594 private:
0595     DataSP m_data;
0596     mutable QScopedPointer<Data> m_lodData;
0597     mutable QScopedPointer<Data> m_externalFrameData;
0598     mutable QMutex m_dataSwitchLock;
0599 
0600     FramesHash m_frames;
0601     int m_nextFreeFrameId;
0602 };
0603 
0604 const KisDefaultBoundsSP KisPaintDevice::Private::transitionalDefaultBounds = new KisDefaultBounds();
0605 
0606 #include "kis_paint_device_strategies.h"
0607 
0608 KisPaintDevice::Private::Private(KisPaintDevice *paintDevice)
0609     : q(paintDevice),
0610       basicStrategy(new KisPaintDeviceStrategy(paintDevice, this)),
0611       isProjectionDevice(false),
0612       m_data(new Data(paintDevice)),
0613       m_nextFreeFrameId(0)
0614 {
0615 }
0616 
0617 KisPaintDevice::Private::~Private()
0618 {
0619     contentChannel.reset();
0620     m_frames.clear();
0621 }
0622 
0623 KisPaintDevice::Private::KisPaintDeviceStrategy* KisPaintDevice::Private::currentStrategy()
0624 {
0625     if (!defaultBounds->wrapAroundMode()) {
0626         return basicStrategy.data();
0627     }
0628 
0629     const QRect wrapRect = defaultBounds->imageBorderRect();
0630 
0631     if (!wrappedStrategy || wrappedStrategy->wrapRect() != wrapRect) {
0632         QMutexLocker locker(&m_wrappedStrategyMutex);
0633 
0634         if (!wrappedStrategy) {
0635             wrappedStrategy.reset(new KisPaintDeviceWrappedStrategy(wrapRect, q, this));
0636         }  else if (wrappedStrategy->wrapRect() != wrapRect) {
0637             wrappedStrategy->setWrapRect(wrapRect);
0638         }
0639     }
0640 
0641     return wrappedStrategy.data();
0642 }
0643 
0644 struct KisPaintDevice::Private::StrategyPolicy {
0645     StrategyPolicy(KisPaintDevice::Private::KisPaintDeviceStrategy *strategy,
0646                    KisDataManager *dataManager, qint32 offsetX, qint32 offsetY)
0647         : m_strategy(strategy),
0648           m_dataManager(dataManager),
0649           m_offsetX(offsetX),
0650           m_offsetY(offsetY)
0651     {
0652     }
0653 
0654     KisHLineConstIteratorSP createConstIterator(const QRect &rect)
0655     {
0656         return m_strategy->createHLineConstIteratorNG(m_dataManager, rect.x(), rect.y(), rect.width(), m_offsetX, m_offsetY);
0657     }
0658 
0659     KisHLineIteratorSP createIterator(const QRect &rect)
0660     {
0661         return m_strategy->createHLineIteratorNG(m_dataManager, rect.x(), rect.y(), rect.width(), m_offsetX, m_offsetY);
0662     }
0663 
0664     int pixelSize() const
0665     {
0666         return m_dataManager->pixelSize();
0667     }
0668 
0669 
0670     KisPaintDeviceStrategy *m_strategy;
0671     KisDataManager *m_dataManager;
0672     int m_offsetX;
0673     int m_offsetY;
0674 };
0675 
0676 struct KisPaintDevice::Private::LodDataStructImpl : public KisPaintDevice::LodDataStruct {
0677     LodDataStructImpl(Data *_lodData) : lodData(_lodData) {}
0678     QScopedPointer<Data> lodData;
0679 };
0680 
0681 KisRegion KisPaintDevice::Private::regionForLodSyncing() const
0682 {
0683     Data *srcData = currentNonLodData();
0684     return srcData->dataManager()->region().translated(srcData->x(), srcData->y());
0685 }
0686 
0687 KisPaintDevice::LodDataStruct* KisPaintDevice::Private::createLodDataStruct(int newLod)
0688 {
0689     KIS_SAFE_ASSERT_RECOVER_NOOP(newLod > 0);
0690 
0691     Data *srcData = currentNonLodData();
0692 
0693     Data *lodData = new Data(q, srcData, false);
0694     LodDataStruct *lodStruct = new LodDataStructImpl(lodData);
0695 
0696     int expectedX = KisLodTransform::coordToLodCoord(srcData->x(), newLod);
0697     int expectedY = KisLodTransform::coordToLodCoord(srcData->y(), newLod);
0698 
0699     /**
0700      * We compare color spaces as pure pointers, because they must be
0701      * exactly the same, since they come from the common source.
0702      */
0703     if (lodData->levelOfDetail() != newLod ||
0704         lodData->colorSpace() != srcData->colorSpace() ||
0705         lodData->x() != expectedX ||
0706         lodData->y() != expectedY) {
0707 
0708 
0709         lodData->prepareClone(srcData);
0710 
0711         lodData->setLevelOfDetail(newLod);
0712         lodData->setX(expectedX);
0713         lodData->setY(expectedY);
0714 
0715         // FIXME: different kind of synchronization
0716     }
0717 
0718     lodData->cache()->invalidate();
0719 
0720     return lodStruct;
0721 }
0722 
0723 void KisPaintDevice::Private::updateLodDataManager(KisDataManager *srcDataManager,
0724                                                    KisDataManager *dstDataManager,
0725                                                    const QPoint &srcOffset,
0726                                                    const QPoint &dstOffset,
0727                                                    const QRect &originalRect,
0728                                                    int lod)
0729 {
0730     if (originalRect.isEmpty()) return;
0731 
0732     const int srcStepSize = 1 << lod;
0733 
0734     KIS_ASSERT_RECOVER_RETURN(lod > 0);
0735 
0736     const QRect srcRect = KisLodTransform::alignedRect(originalRect, lod);
0737     const QRect dstRect = KisLodTransform::scaledRect(srcRect, lod);
0738     if (!srcRect.isValid() || !dstRect.isValid()) return;
0739 
0740     KIS_ASSERT_RECOVER_NOOP(srcRect.width() / srcStepSize == dstRect.width());
0741 
0742     const int pixelSize = srcDataManager->pixelSize();
0743 
0744     int rowsAccumulated = 0;
0745     int columnsAccumulated = 0;
0746 
0747     KoMixColorsOp *mixOp = colorSpace()->mixColorsOp();
0748 
0749     QScopedArrayPointer<quint8> blendData(new quint8[srcStepSize * srcRect.width() * pixelSize]);
0750     quint8 *blendDataPtr = blendData.data();
0751     int blendDataOffset = 0;
0752 
0753     const int srcCellSize = srcStepSize * srcStepSize;
0754     const int srcCellStride = srcCellSize * pixelSize;
0755     const int srcStepStride = srcStepSize * pixelSize;
0756     const int srcColumnStride = (srcStepSize - 1) * srcStepStride;
0757 
0758     QScopedArrayPointer<qint16> weights(new qint16[srcCellSize]);
0759 
0760     {
0761         const qint16 averageWeight = qCeil(255.0 / srcCellSize);
0762         const qint16 extraWeight = averageWeight * srcCellSize - 255;
0763         KIS_ASSERT_RECOVER_NOOP(extraWeight == 1);
0764 
0765         for (int i = 0; i < srcCellSize - 1; i++) {
0766             weights[i] = averageWeight;
0767         }
0768         weights[srcCellSize - 1] = averageWeight - extraWeight;
0769     }
0770 
0771     InternalSequentialConstIterator srcIntIt(StrategyPolicy(currentStrategy(), srcDataManager, srcOffset.x(), srcOffset.y()), srcRect);
0772     InternalSequentialIterator dstIntIt(StrategyPolicy(currentStrategy(), dstDataManager, dstOffset.x(), dstOffset.y()), dstRect);
0773 
0774     int rowsRemaining = srcRect.height();
0775     while (rowsRemaining > 0) {
0776 
0777         int colsRemaining = srcRect.width();
0778         while (colsRemaining > 0 && srcIntIt.nextPixel()) {
0779 
0780             memcpy(blendDataPtr, srcIntIt.rawDataConst(), pixelSize);
0781             blendDataPtr += pixelSize;
0782             columnsAccumulated++;
0783 
0784             if (columnsAccumulated >= srcStepSize) {
0785                 blendDataPtr += srcColumnStride;
0786                 columnsAccumulated = 0;
0787             }
0788 
0789             colsRemaining--;
0790         }
0791 
0792         rowsAccumulated++;
0793 
0794         if (rowsAccumulated >= srcStepSize) {
0795 
0796             // blend and write the final data
0797             blendDataPtr = blendData.data();
0798 
0799             int colsRemaining = dstRect.width();
0800             while (colsRemaining > 0 && dstIntIt.nextPixel()) {
0801                 mixOp->mixColors(blendDataPtr, weights.data(), srcCellSize, dstIntIt.rawData());
0802                 blendDataPtr += srcCellStride;
0803 
0804                 colsRemaining--;
0805             }
0806 
0807             // reset counters
0808             rowsAccumulated = 0;
0809             blendDataPtr = blendData.data();
0810             blendDataOffset = 0;
0811         } else {
0812             blendDataOffset += srcStepStride;
0813             blendDataPtr = blendData.data() + blendDataOffset;
0814         }
0815 
0816         rowsRemaining--;
0817     }
0818 }
0819 
0820 void KisPaintDevice::Private::updateLodDataStruct(LodDataStruct *_dst, const QRect &originalRect)
0821 {
0822     LodDataStructImpl *dst = dynamic_cast<LodDataStructImpl*>(_dst);
0823     KIS_SAFE_ASSERT_RECOVER_RETURN(dst);
0824 
0825     Data *lodData = dst->lodData.data();
0826     Data *srcData = currentNonLodData();
0827 
0828     const int lod = lodData->levelOfDetail();
0829 
0830     updateLodDataManager(srcData->dataManager().data(), lodData->dataManager().data(),
0831                          QPoint(srcData->x(), srcData->y()),
0832                          QPoint(lodData->x(), lodData->y()),
0833                          originalRect, lod);
0834 }
0835 
0836 void KisPaintDevice::Private::generateLodCloneDevice(KisPaintDeviceSP dst, const QRect &originalRect, int lod)
0837 {
0838     KIS_SAFE_ASSERT_RECOVER_RETURN(fastBitBltPossible(dst));
0839 
0840     Data *srcData = currentNonLodData();
0841     updateLodDataManager(srcData->dataManager().data(), dst->dataManager().data(),
0842                          QPoint(srcData->x(), srcData->y()),
0843                          QPoint(dst->x(), dst->y()),
0844                          originalRect, lod);
0845 }
0846 
0847 void KisPaintDevice::Private::uploadLodDataStruct(LodDataStruct *_dst)
0848 {
0849     LodDataStructImpl *dst = dynamic_cast<LodDataStructImpl*>(_dst);
0850     KIS_SAFE_ASSERT_RECOVER_RETURN(dst);
0851 
0852     KIS_SAFE_ASSERT_RECOVER_RETURN(
0853         dst->lodData->levelOfDetail() == defaultBounds->currentLevelOfDetail());
0854 
0855     ensureLodDataPresent();
0856 
0857     m_lodData->prepareClone(dst->lodData.data());
0858     m_lodData->dataManager()->bitBltRough(dst->lodData->dataManager(), dst->lodData->dataManager()->extent());
0859 }
0860 
0861 void KisPaintDevice::Private::transferFromData(Data *data, KisPaintDeviceSP targetDevice)
0862 {
0863     QRect extent = data->dataManager()->extent();
0864     extent.translate(data->x(), data->y());
0865 
0866     targetDevice->m_d->prepareCloneImpl(q, data);
0867     targetDevice->m_d->currentStrategy()->fastBitBltRough(data->dataManager(), extent);
0868 }
0869 
0870 void KisPaintDevice::Private::writeFrameToDevice(int frameId, KisPaintDeviceSP targetDevice)
0871 {
0872     DataSP data = m_frames[frameId];
0873     transferFromData(data.data(), targetDevice);
0874 }
0875 
0876 void KisPaintDevice::Private::uploadFrame(int srcFrameId, int dstFrameId, KisPaintDeviceSP srcDevice)
0877 {
0878     DataSP dstData = m_frames[dstFrameId];
0879     KIS_ASSERT_RECOVER_RETURN(dstData);
0880 
0881     DataSP srcData = srcDevice->m_d->m_frames[srcFrameId];
0882     KIS_ASSERT_RECOVER_RETURN(srcData);
0883 
0884     uploadFrameData(srcData, dstData);
0885 }
0886 
0887 void KisPaintDevice::Private::uploadFrame(int dstFrameId, KisPaintDeviceSP srcDevice)
0888 {
0889     DataSP dstData = m_frames[dstFrameId];
0890     KIS_ASSERT_RECOVER_RETURN(dstData);
0891 
0892     DataSP srcData = srcDevice->m_d->m_data;
0893     KIS_ASSERT_RECOVER_RETURN(srcData);
0894 
0895     uploadFrameData(srcData, dstData);
0896 }
0897 
0898 void KisPaintDevice::Private::uploadFrameData(DataSP srcData, DataSP dstData)
0899 {
0900     if (srcData->colorSpace() != dstData->colorSpace() &&
0901         *srcData->colorSpace() != *dstData->colorSpace()) {
0902 
0903         KUndo2Command tempCommand;
0904 
0905         srcData = toQShared(new Data(q, srcData.data(), true));
0906         srcData->convertDataColorSpace(dstData->colorSpace(),
0907                                        KoColorConversionTransformation::internalRenderingIntent(),
0908                                        KoColorConversionTransformation::internalConversionFlags(),
0909                                        &tempCommand);
0910     }
0911 
0912     /* If the destination data doesn't share a default pixel value
0913      * with src, we should make sure that the default pixel is set
0914      * properly before clearing and writing contents.
0915      */
0916     const int defaultPixelcmp =
0917             memcmp(srcData->dataManager()->defaultPixel(),
0918                    dstData->dataManager()->defaultPixel(),
0919                    dstData->dataManager()->pixelSize());
0920     if (defaultPixelcmp != 0) {
0921         dstData->dataManager()->setDefaultPixel(srcData->dataManager()->defaultPixel());
0922     }
0923 
0924     dstData->dataManager()->clear();
0925     dstData->cache()->invalidate();
0926 
0927     const QRect rect = srcData->dataManager()->extent();
0928     dstData->dataManager()->bitBltRough(srcData->dataManager(), rect);
0929     dstData->setX(srcData->x());
0930     dstData->setY(srcData->y());
0931 }
0932 
0933 void KisPaintDevice::Private::tesingFetchLodDevice(KisPaintDeviceSP targetDevice)
0934 {
0935     Data *data = m_lodData.data();
0936     Q_ASSERT(data);
0937 
0938     transferFromData(data, targetDevice);
0939 }
0940 
0941 class KisPaintDevice::Private::DeviceChangeProfileCommand : public KUndo2Command
0942 {
0943 public:
0944     DeviceChangeProfileCommand(KisPaintDeviceSP device, KUndo2Command *parent = 0)
0945         : KUndo2Command(parent),
0946           m_device(device)
0947     {
0948     }
0949 
0950     virtual void emitNotifications()
0951     {
0952         m_device->emitProfileChanged();
0953     }
0954 
0955     void redo() override
0956     {
0957         if (m_firstRun) {
0958             m_firstRun = false;
0959             return;
0960         }
0961 
0962         KUndo2Command::redo();
0963         emitNotifications();
0964     }
0965 
0966     void undo() override
0967     {
0968         KUndo2Command::undo();
0969         emitNotifications();
0970     }
0971 
0972 protected:
0973     KisPaintDeviceSP m_device;
0974 
0975 private:
0976     bool m_firstRun {true};
0977 };
0978 
0979 class KisPaintDevice::Private::DeviceChangeColorSpaceCommand : public DeviceChangeProfileCommand
0980 {
0981 public:
0982     DeviceChangeColorSpaceCommand(KisPaintDeviceSP device, KUndo2Command *parent = 0)
0983         : DeviceChangeProfileCommand(device, parent)
0984     {
0985     }
0986 
0987     void emitNotifications() override
0988     {
0989         m_device->emitColorSpaceChanged();
0990     }
0991 };
0992 
0993 void KisPaintDevice::Private::convertColorSpace(const KoColorSpace *dstColorSpace,
0994                                                 KoColorConversionTransformation::Intent renderingIntent,
0995                                                 KoColorConversionTransformation::ConversionFlags conversionFlags,
0996                                                 KUndo2Command *parentCommand,
0997                                                 KoUpdater *progressUpdater)
0998 {
0999     QList<Data*> dataObjects = allDataObjects();
1000     if (dataObjects.isEmpty()) return;
1001 
1002     KUndo2Command *mainCommand =
1003         parentCommand ? new DeviceChangeColorSpaceCommand(q, parentCommand) : 0;
1004 
1005     Q_FOREACH (Data *data, dataObjects) {
1006         if (!data) continue;
1007 
1008         data->convertDataColorSpace(dstColorSpace, renderingIntent, conversionFlags, mainCommand, progressUpdater);
1009     }
1010 
1011     q->emitColorSpaceChanged();
1012 }
1013 
1014 bool KisPaintDevice::Private::assignProfile(const KoColorProfile * profile, KUndo2Command *parentCommand)
1015 {
1016     if (!profile) return false;
1017 
1018     const KoColorSpace *dstColorSpace =
1019         KoColorSpaceRegistry::instance()->colorSpace(colorSpace()->colorModelId().id(), colorSpace()->colorDepthId().id(), profile);
1020     if (!dstColorSpace) return false;
1021 
1022     KUndo2Command *mainCommand =
1023         parentCommand ? new DeviceChangeColorSpaceCommand(q, parentCommand) : 0;
1024 
1025 
1026     QList<Data*> dataObjects = allDataObjects();
1027     Q_FOREACH (Data *data, dataObjects) {
1028         if (!data) continue;
1029         data->assignColorSpace(dstColorSpace, mainCommand);
1030     }
1031     q->emitProfileChanged();
1032 
1033     // no undo information is provided here
1034     return true;
1035 }
1036 
1037 KUndo2Command *KisPaintDevice::Private::reincarnateWithDetachedHistory(bool copyContent)
1038 {
1039     KUndo2Command *mainCommand = new KUndo2Command();
1040     currentData()->reincarnateWithDetachedHistory(copyContent, mainCommand);
1041     return mainCommand;
1042 }
1043 
1044 void KisPaintDevice::Private::init(const KoColorSpace *cs, const quint8 *defaultPixel)
1045 {
1046     QList<Data*> dataObjects = allDataObjects();
1047     Q_FOREACH (Data *data, dataObjects) {
1048         if (!data) continue;
1049 
1050         KisDataManagerSP dataManager = new KisDataManager(cs->pixelSize(), defaultPixel);
1051         data->init(cs, dataManager);
1052     }
1053 }
1054 
1055 KisPaintDevice::KisPaintDevice(const KoColorSpace * colorSpace, const QString& name)
1056     : QObject(0)
1057     , m_d(new Private(this))
1058 {
1059     init(colorSpace, new KisDefaultBounds(), 0, name);
1060 }
1061 
1062 KisPaintDevice::KisPaintDevice(KisNodeWSP parent, const KoColorSpace * colorSpace, KisDefaultBoundsBaseSP defaultBounds, const QString& name)
1063     : QObject(0)
1064     , m_d(new Private(this))
1065 {
1066     init(colorSpace, defaultBounds, parent, name);
1067 }
1068 
1069 void KisPaintDevice::init(const KoColorSpace *colorSpace,
1070                           KisDefaultBoundsBaseSP defaultBounds,
1071                           KisNodeWSP parent, const QString& name)
1072 {
1073     Q_ASSERT(colorSpace);
1074     setObjectName(name);
1075 
1076     // temporary def. bounds object for the initialization phase only
1077     m_d->defaultBounds = m_d->transitionalDefaultBounds;
1078 
1079     if (!defaultBounds) {
1080         // Reuse transitionalDefaultBounds here. Change if you change
1081         // semantics of transitionalDefaultBounds
1082         defaultBounds = m_d->transitionalDefaultBounds;
1083     }
1084 
1085     QScopedArrayPointer<quint8> defaultPixel(new quint8[colorSpace->pixelSize()]);
1086     colorSpace->fromQColor(Qt::transparent, defaultPixel.data());
1087     m_d->init(colorSpace, defaultPixel.data());
1088 
1089     Q_ASSERT(m_d->colorSpace());
1090 
1091     setDefaultBounds(defaultBounds);
1092     setParentNode(parent);
1093 }
1094 
1095 KisPaintDevice::KisPaintDevice(const KisPaintDevice& rhs, KritaUtils::DeviceCopyMode copyMode, KisNode *newParentNode)
1096     : QObject()
1097     , KisShared()
1098     , m_d(new Private(this))
1099 {
1100     if (this != &rhs) {
1101         makeFullCopyFrom(rhs, copyMode, newParentNode);
1102     }
1103 }
1104 
1105 void KisPaintDevice::makeFullCopyFrom(const KisPaintDevice &rhs, KritaUtils::DeviceCopyMode copyMode, KisNode *newParentNode)
1106 {
1107     // temporary def. bounds object for the initialization phase only
1108     m_d->defaultBounds = m_d->transitionalDefaultBounds;
1109 
1110     // copy data objects with or without frames
1111     m_d->cloneAllDataObjects(rhs.m_d, copyMode == KritaUtils::CopyAllFrames);
1112 
1113     if (copyMode == KritaUtils::CopyAllFrames && rhs.m_d->framesInterface) {
1114         KIS_ASSERT_RECOVER_RETURN(rhs.m_d->framesInterface);
1115         KIS_ASSERT_RECOVER_RETURN(rhs.m_d->contentChannel);
1116         m_d->framesInterface.reset(new KisPaintDeviceFramesInterface(this));
1117         m_d->contentChannel.reset(new KisRasterKeyframeChannel(*rhs.m_d->contentChannel.data(), this));
1118     }
1119 
1120     setDefaultBounds(rhs.m_d->defaultBounds);
1121     setParentNode(newParentNode);
1122 }
1123 
1124 KisPaintDevice::~KisPaintDevice()
1125 {
1126     delete m_d;
1127 }
1128 
1129 void KisPaintDevice::setProjectionDevice(bool value)
1130 {
1131     m_d->isProjectionDevice = value;
1132 }
1133 
1134 void KisPaintDevice::prepareClone(KisPaintDeviceSP src)
1135 {
1136     m_d->prepareClone(src);
1137 }
1138 
1139 void KisPaintDevice::makeCloneFrom(KisPaintDeviceSP src, const QRect &rect)
1140 {
1141     prepareClone(src);
1142 
1143     // we guarantee that *this is totally empty, so copy pixels that
1144     // are areally present on the source image only
1145     const QRect optimizedRect = rect & src->extent();
1146 
1147     fastBitBlt(src, optimizedRect);
1148 }
1149 
1150 void KisPaintDevice::makeCloneFromRough(KisPaintDeviceSP src, const QRect &minimalRect)
1151 {
1152     prepareClone(src);
1153 
1154     // we guarantee that *this is totally empty, so copy pixels that
1155     // are areally present on the source image only
1156     const QRect optimizedRect = minimalRect & src->extent();
1157 
1158     fastBitBltRough(src, optimizedRect);
1159 }
1160 
1161 void KisPaintDevice::setDirty()
1162 {
1163     m_d->cache()->invalidate();
1164     if (m_d->parent.isValid())
1165         m_d->parent->setDirty();
1166 }
1167 
1168 void KisPaintDevice::setDirty(const QRect & rc)
1169 {
1170     m_d->cache()->invalidate();
1171     if (m_d->parent.isValid())
1172         m_d->parent->setDirty(rc);
1173 }
1174 
1175 void KisPaintDevice::setDirty(const KisRegion &region)
1176 {
1177     m_d->cache()->invalidate();
1178     if (m_d->parent.isValid())
1179         m_d->parent->setDirty(region);
1180 }
1181 
1182 void KisPaintDevice::setDirty(const QVector<QRect> &rects)
1183 {
1184     m_d->cache()->invalidate();
1185     if (m_d->parent.isValid())
1186         m_d->parent->setDirty(rects);
1187 }
1188 
1189 void KisPaintDevice::requestTimeSwitch(int time)
1190 {
1191     if (m_d->parent.isValid()) {
1192         m_d->parent->requestTimeSwitch(time);
1193     }
1194 }
1195 
1196 int KisPaintDevice::sequenceNumber() const
1197 {
1198     return m_d->cache()->sequenceNumber();
1199 }
1200 
1201 void KisPaintDevice::estimateMemoryStats(qint64 &imageData, qint64 &temporaryData, qint64 &lodData) const
1202 {
1203     m_d->estimateMemoryStats(imageData, temporaryData, lodData);
1204 }
1205 
1206 void KisPaintDevice::setParentNode(KisNodeWSP parent)
1207 {
1208     m_d->parent = parent;
1209 }
1210 
1211 // for testing purposes only
1212 KisNodeWSP KisPaintDevice::parentNode() const
1213 {
1214     return m_d->parent;
1215 }
1216 
1217 void KisPaintDevice::setDefaultBounds(KisDefaultBoundsBaseSP defaultBounds)
1218 {
1219     m_d->defaultBounds = defaultBounds;
1220     m_d->cache()->invalidate();
1221 }
1222 
1223 KisDefaultBoundsBaseSP KisPaintDevice::defaultBounds() const
1224 {
1225     return m_d->defaultBounds;
1226 }
1227 
1228 void KisPaintDevice::moveTo(const QPoint &pt)
1229 {
1230     m_d->currentStrategy()->move(pt);
1231     m_d->cache()->invalidate();
1232 }
1233 
1234 QPoint KisPaintDevice::offset() const
1235 {
1236     return QPoint(x(), y());
1237 }
1238 
1239 void KisPaintDevice::moveTo(qint32 x, qint32 y)
1240 {
1241     moveTo(QPoint(x, y));
1242 }
1243 
1244 void KisPaintDevice::setX(qint32 x)
1245 {
1246     moveTo(QPoint(x, m_d->y()));
1247 }
1248 
1249 void KisPaintDevice::setY(qint32 y)
1250 {
1251     moveTo(QPoint(m_d->x(), y));
1252 }
1253 
1254 qint32 KisPaintDevice::x() const
1255 {
1256     return m_d->x();
1257 }
1258 
1259 qint32 KisPaintDevice::y() const
1260 {
1261     return m_d->y();
1262 }
1263 
1264 void KisPaintDevice::extent(qint32 &x, qint32 &y, qint32 &w, qint32 &h) const
1265 {
1266     QRect rc = extent();
1267     x = rc.x();
1268     y = rc.y();
1269     w = rc.width();
1270     h = rc.height();
1271 }
1272 
1273 QRect KisPaintDevice::extent() const
1274 {
1275     return m_d->currentStrategy()->extent();
1276 }
1277 
1278 KisRegion KisPaintDevice::region() const
1279 {
1280     return m_d->currentStrategy()->region();
1281 }
1282 
1283 QRect KisPaintDevice::nonDefaultPixelArea() const
1284 {
1285     return m_d->cache()->nonDefaultPixelArea();
1286 }
1287 
1288 QRect KisPaintDevice::exactBounds() const
1289 {
1290     return m_d->cache()->exactBounds();
1291 }
1292 
1293 QRect KisPaintDevice::exactBoundsAmortized() const
1294 {
1295     return m_d->cache()->exactBoundsAmortized();
1296 }
1297 
1298 namespace Impl
1299 {
1300 
1301 struct CheckFullyTransparent {
1302     CheckFullyTransparent(const KoColorSpace *colorSpace)
1303         : m_colorSpace(colorSpace)
1304     {
1305     }
1306 
1307     bool isPixelEmpty(const quint8 *pixelData)
1308     {
1309         return m_colorSpace->opacityU8(pixelData) == OPACITY_TRANSPARENT_U8;
1310     }
1311 
1312 private:
1313     const KoColorSpace *m_colorSpace;
1314 };
1315 
1316 struct CheckNonDefault {
1317     CheckNonDefault(int pixelSize, const quint8 *defaultPixel)
1318         : m_pixelSize(pixelSize),
1319           m_defaultPixel(defaultPixel)
1320     {
1321     }
1322 
1323     bool isPixelEmpty(const quint8 *pixelData)
1324     {
1325         return memcmp(m_defaultPixel, pixelData, m_pixelSize) == 0;
1326     }
1327 
1328 private:
1329     int m_pixelSize;
1330     const quint8 *m_defaultPixel;
1331 };
1332 
1333 template <class ComparePixelOp>
1334 QRect calculateExactBoundsImpl(const KisPaintDevice *device, const QRect &startRect, const QRect &endRect, ComparePixelOp compareOp)
1335 {
1336     if (startRect == endRect) return startRect;
1337 
1338     // the passed extent might have weird invalid structure that
1339     // can overflow integer precision when calling startRect.right()
1340     if (!startRect.isValid()) return QRect();
1341 
1342     // Solution n°2
1343     int  x, y, w, h;
1344     int boundLeft, boundTop, boundRight, boundBottom;
1345     int endDirN, endDirE, endDirS, endDirW;
1346 
1347     startRect.getRect(&x, &y, &w, &h);
1348 
1349     if (endRect.isEmpty()) {
1350         endDirS = startRect.bottom();
1351         endDirN = startRect.top();
1352         endDirE = startRect.right();
1353         endDirW = startRect.left();
1354         startRect.getCoords(&boundLeft, &boundTop, &boundRight, &boundBottom);
1355     } else {
1356         endDirS = endRect.top() - 1;
1357         endDirN = endRect.bottom() + 1;
1358         endDirE = endRect.left() - 1;
1359         endDirW = endRect.right() + 1;
1360         endRect.getCoords(&boundLeft, &boundTop, &boundRight, &boundBottom);
1361     }
1362 
1363     // XXX: a small optimization is possible by using H/V line iterators in the first
1364     //      and third cases, at the cost of making the code a bit more complex
1365 
1366     KisRandomConstAccessorSP accessor = device->createRandomConstAccessorNG();
1367 
1368     bool found = false;
1369     {
1370         for (qint32 y2 = y; y2 <= endDirS; ++y2) {
1371             for (qint32 x2 = x; x2 < x + w || found; ++ x2) {
1372                 accessor->moveTo(x2, y2);
1373                 if (!compareOp.isPixelEmpty(accessor->rawDataConst())) {
1374                     boundTop = y2;
1375                     found = true;
1376                     break;
1377                 }
1378             }
1379             if (found) break;
1380         }
1381     }
1382 
1383     /**
1384      * If the first pass hasn't found any opaque pixel, there is no
1385      * reason to check that 3 more times. They will not appear in the
1386      * meantime. Just return an empty bounding rect.
1387      */
1388     if (!found && endRect.isEmpty()) {
1389         return QRect();
1390     }
1391 
1392     found = false;
1393 
1394     for (qint32 y2 = y + h - 1; y2 >= endDirN ; --y2) {
1395         for (qint32 x2 = x + w - 1; x2 >= x || found; --x2) {
1396             accessor->moveTo(x2, y2);
1397             if (!compareOp.isPixelEmpty(accessor->rawDataConst())) {
1398                 boundBottom = y2;
1399                 found = true;
1400                 break;
1401             }
1402         }
1403         if (found) break;
1404     }
1405     found = false;
1406 
1407     {
1408         for (qint32 x2 = x; x2 <= endDirE ; ++x2) {
1409             for (qint32 y2 = y; y2 < y + h || found; ++y2) {
1410                 accessor->moveTo(x2, y2);
1411                 if (!compareOp.isPixelEmpty(accessor->rawDataConst())) {
1412                     boundLeft = x2;
1413                     found = true;
1414                     break;
1415                 }
1416             }
1417             if (found) break;
1418         }
1419     }
1420 
1421     found = false;
1422 
1423     // Look for right edge )
1424     {
1425 
1426         for (qint32 x2 = x + w - 1; x2 >= endDirW; --x2) {
1427             for (qint32 y2 = y + h - 1; y2 >= y || found; --y2) {
1428                 accessor->moveTo(x2, y2);
1429                 if (!compareOp.isPixelEmpty(accessor->rawDataConst())) {
1430                     boundRight = x2;
1431                     found = true;
1432                     break;
1433                 }
1434             }
1435             if (found) break;
1436         }
1437     }
1438 
1439     return QRect(boundLeft, boundTop,
1440                  boundRight - boundLeft + 1,
1441                  boundBottom - boundTop + 1);
1442 }
1443 
1444 }
1445 
1446 QRect KisPaintDevice::calculateExactBounds(bool nonDefaultOnly) const
1447 {
1448     QRect startRect = extent();
1449     QRect endRect;
1450 
1451     quint8 defaultOpacity = defaultPixel().opacityU8();
1452     if (defaultOpacity != OPACITY_TRANSPARENT_U8) {
1453         if (!nonDefaultOnly) {
1454             /**
1455              * We will calculate exact bounds only outside of the
1456              * image bounds, and that'll be nondefault area only.
1457              */
1458 
1459             endRect = defaultBounds()->bounds();
1460             nonDefaultOnly = true;
1461 
1462         } else {
1463             startRect = region().boundingRect();
1464         }
1465     }
1466 
1467     if (nonDefaultOnly) {
1468         const KoColor defaultPixel = this->defaultPixel();
1469         Impl::CheckNonDefault compareOp(pixelSize(), defaultPixel.data());
1470         endRect = Impl::calculateExactBoundsImpl(this, startRect, endRect, compareOp);
1471     } else {
1472         Impl::CheckFullyTransparent compareOp(m_d->colorSpace());
1473         endRect = Impl::calculateExactBoundsImpl(this, startRect, endRect, compareOp);
1474     }
1475 
1476     return endRect;
1477 }
1478 
1479 KisRegion KisPaintDevice::regionExact() const
1480 {
1481     QVector<QRect> sourceRects = region().rects();
1482     QVector<QRect> resultRects;
1483 
1484     const KoColor defaultPixel = this->defaultPixel();
1485     Impl::CheckNonDefault compareOp(pixelSize(), defaultPixel.data());
1486 
1487     Q_FOREACH (const QRect &rc1, sourceRects) {
1488         const int patchSize = 64;
1489         QVector<QRect> smallerRects = KritaUtils::splitRectIntoPatches(rc1, QSize(patchSize, patchSize));
1490         Q_FOREACH (const QRect &rc2, smallerRects) {
1491 
1492             const QRect result =
1493                 Impl::calculateExactBoundsImpl(this, rc2, QRect(), compareOp);
1494 
1495             if (!result.isEmpty()) {
1496                 resultRects << result;
1497             }
1498         }
1499     }
1500     return KisRegion(std::move(resultRects));
1501 }
1502 
1503 void KisPaintDevice::crop(qint32 x, qint32 y, qint32 w, qint32 h)
1504 {
1505     crop(QRect(x, y, w, h));
1506 }
1507 
1508 
1509 void KisPaintDevice::crop(const QRect &rect)
1510 {
1511     m_d->currentStrategy()->crop(rect);
1512 }
1513 
1514 void KisPaintDevice::purgeDefaultPixels()
1515 {
1516     KisDataManagerSP dm = m_d->dataManager();
1517     dm->purge(dm->extent());
1518 }
1519 
1520 void KisPaintDevice::setDefaultPixel(const KoColor &defPixel)
1521 {
1522     KoColor color(defPixel);
1523     color.convertTo(colorSpace());
1524 
1525     m_d->dataManager()->setDefaultPixel(color.data());
1526     m_d->cache()->invalidate();
1527 }
1528 
1529 KoColor KisPaintDevice::defaultPixel() const
1530 {
1531     return KoColor(m_d->dataManager()->defaultPixel(), colorSpace());
1532 }
1533 
1534 void KisPaintDevice::clear()
1535 {
1536     m_d->dataManager()->clear();
1537     m_d->cache()->invalidate();
1538 }
1539 
1540 void KisPaintDevice::clear(const QRect & rc)
1541 {
1542     m_d->currentStrategy()->clear(rc);
1543 }
1544 
1545 void KisPaintDevice::fill(const QRect & rc, const KoColor &color)
1546 {
1547     KIS_ASSERT_RECOVER_RETURN(*color.colorSpace() == *colorSpace());
1548     m_d->currentStrategy()->fill(rc, color.data());
1549 }
1550 
1551 void KisPaintDevice::fill(qint32 x, qint32 y, qint32 w, qint32 h, const quint8 *fillPixel)
1552 {
1553     m_d->currentStrategy()->fill(QRect(x, y, w, h), fillPixel);
1554 }
1555 
1556 
1557 bool KisPaintDevice::write(KisPaintDeviceWriter &store)
1558 {
1559     return m_d->dataManager()->write(store);
1560 }
1561 
1562 bool KisPaintDevice::read(QIODevice *stream)
1563 {
1564     bool retval;
1565 
1566     retval = m_d->dataManager()->read(stream);
1567     m_d->cache()->invalidate();
1568 
1569     return retval;
1570 }
1571 
1572 void KisPaintDevice::emitColorSpaceChanged()
1573 {
1574     emit colorSpaceChanged(m_d->colorSpace());
1575 }
1576 
1577 void KisPaintDevice::emitProfileChanged()
1578 {
1579     emit profileChanged(m_d->colorSpace()->profile());
1580 }
1581 
1582 void KisPaintDevice::convertTo(const KoColorSpace *dstColorSpace,
1583                                KoColorConversionTransformation::Intent renderingIntent,
1584                                KoColorConversionTransformation::ConversionFlags conversionFlags,
1585                                KUndo2Command *parentCommand,
1586                                KoUpdater *progressUpdater)
1587 {
1588     m_d->convertColorSpace(dstColorSpace, renderingIntent, conversionFlags, parentCommand, progressUpdater);
1589 }
1590 
1591 bool KisPaintDevice::setProfile(const KoColorProfile * profile, KUndo2Command *parentCommand)
1592 {
1593     return m_d->assignProfile(profile, parentCommand);
1594 }
1595 
1596 KUndo2Command *KisPaintDevice::reincarnateWithDetachedHistory(bool copyContent)
1597 {
1598     return m_d->reincarnateWithDetachedHistory(copyContent);
1599 }
1600 
1601 KisDataManagerSP KisPaintDevice::dataManager() const
1602 {
1603     return m_d->dataManager();
1604 }
1605 
1606 void KisPaintDevice::convertFromQImage(const QImage& _image, const KoColorProfile *profile,
1607                                        qint32 offsetX, qint32 offsetY)
1608 {
1609     QImage image = _image;
1610 
1611     if (image.format() != QImage::Format_ARGB32) {
1612         image = image.convertToFormat(QImage::Format_ARGB32);
1613     }
1614     // Don't convert if not no profile is given and both paint dev and qimage are rgba.
1615     if (!profile && colorSpace()->id() == "RGBA") {
1616         writeBytes(image.constBits(), offsetX, offsetY, image.width(), image.height());
1617     } else {
1618         try {
1619             quint8 * dstData = new quint8[image.width() * image.height() * pixelSize()];
1620             KoColorSpaceRegistry::instance()
1621             ->colorSpace(RGBAColorModelID.id(), Integer8BitsColorDepthID.id(), profile)
1622             ->convertPixelsTo(image.constBits(), dstData, colorSpace(), image.width() * image.height(),
1623                               KoColorConversionTransformation::internalRenderingIntent(),
1624                               KoColorConversionTransformation::internalConversionFlags());
1625 
1626             writeBytes(dstData, offsetX, offsetY, image.width(), image.height());
1627             delete[] dstData;
1628         } catch (const std::bad_alloc&) {
1629             warnKrita << "KisPaintDevice::convertFromQImage: Could not allocate" << image.width() * image.height() * pixelSize() << "bytes";
1630             return;
1631         }
1632     }
1633     m_d->cache()->invalidate();
1634 }
1635 
1636 QImage KisPaintDevice::convertToQImage(const KoColorProfile *dstProfile, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const
1637 {
1638     qint32 x1;
1639     qint32 y1;
1640     qint32 w;
1641     qint32 h;
1642 
1643     QRect rc = exactBounds();
1644     x1 = rc.x();
1645     y1 = rc.y();
1646     w = rc.width();
1647     h = rc.height();
1648 
1649     return convertToQImage(dstProfile, x1, y1, w, h, renderingIntent, conversionFlags);
1650 }
1651 
1652 QImage KisPaintDevice::convertToQImage(const KoColorProfile *dstProfile,
1653                                        const QRect &rc,
1654                                        KoColorConversionTransformation::Intent renderingIntent,
1655                                        KoColorConversionTransformation::ConversionFlags conversionFlags) const
1656 {
1657     return convertToQImage(dstProfile,
1658                            rc.x(), rc.y(), rc.width(), rc.height(),
1659                            renderingIntent, conversionFlags);
1660 }
1661 
1662 QImage KisPaintDevice::convertToQImage(const KoColorProfile *dstProfile, qint32 x1, qint32 y1, qint32 w, qint32 h, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const
1663 {
1664 
1665     if (w < 0)
1666         return QImage();
1667 
1668     if (h < 0)
1669         return QImage();
1670 
1671     quint8 *data = 0;
1672     try {
1673         data = new quint8 [w * h * pixelSize()];
1674     } catch (const std::bad_alloc&) {
1675         warnKrita << "KisPaintDevice::convertToQImage std::bad_alloc for " << w << " * " << h << " * " << pixelSize();
1676         //delete[] data; // data is not allocated, so don't free it
1677         return QImage();
1678     }
1679     Q_CHECK_PTR(data);
1680 
1681     // XXX: Is this really faster than converting line by line and building the QImage directly?
1682     //      This copies potentially a lot of data.
1683     readBytes(data, x1, y1, w, h);
1684     QImage image = colorSpace()->convertToQImage(data, w, h, dstProfile, renderingIntent, conversionFlags);
1685     delete[] data;
1686 
1687     return image;
1688 }
1689 
1690 inline bool moveBy(KisSequentialConstIterator& iter, int numPixels)
1691 {
1692     int pos = 0;
1693     while (pos < numPixels) {
1694         int step = std::min(iter.nConseqPixels(), numPixels - pos);
1695         if (!iter.nextPixels(step))
1696             return false;
1697         pos += step;
1698     }
1699     return true;
1700 }
1701 
1702 static KisPaintDeviceSP createThumbnailDeviceInternal(const KisPaintDevice* srcDev, qint32 srcX0, qint32 srcY0, qint32 srcWidth, qint32 srcHeight, qint32 w, qint32 h, QRect outputRect)
1703 {
1704     KisPaintDeviceSP thumbnail = new KisPaintDevice(srcDev->colorSpace());
1705     qint32 pixelSize = srcDev->pixelSize();
1706 
1707     KisRandomConstAccessorSP srcIter = srcDev->createRandomConstAccessorNG();
1708     KisRandomAccessorSP dstIter = thumbnail->createRandomAccessorNG();
1709 
1710     for (qint32 y = outputRect.y(); y < outputRect.y() + outputRect.height(); ++y) {
1711         qint32 iY = srcY0 + (y * srcHeight) / h;
1712         for (qint32 x = outputRect.x(); x < outputRect.x() + outputRect.width(); ++x) {
1713             qint32 iX = srcX0 + (x * srcWidth) / w;
1714             srcIter->moveTo(iX, iY);
1715             dstIter->moveTo(x,  y);
1716             memcpy(dstIter->rawData(), srcIter->rawDataConst(), pixelSize);
1717         }
1718     }
1719     return thumbnail;
1720 }
1721 
1722 QSize fixThumbnailSize(QSize size)
1723 {
1724     if (!size.width() && size.height()) {
1725         size.setWidth(1);
1726     }
1727 
1728     if (size.width() && !size.height()) {
1729         size.setHeight(1);
1730     }
1731 
1732     return size;
1733 }
1734 
1735 KisPaintDeviceSP KisPaintDevice::createThumbnailDevice(qint32 w, qint32 h, QRect rect, QRect outputRect) const
1736 {
1737     QSize thumbnailSize(w, h);
1738 
1739     QRect imageRect = rect.isValid() ? rect : extent();
1740 
1741     if ((thumbnailSize.width() > imageRect.width()) || (thumbnailSize.height() > imageRect.height())) {
1742         thumbnailSize.scale(imageRect.size(), Qt::KeepAspectRatio);
1743     }
1744 
1745     thumbnailSize = fixThumbnailSize(thumbnailSize);
1746 
1747     //can't create thumbnail for an empty device, e.g. layer thumbnail for empty image
1748     if (imageRect.isEmpty() || thumbnailSize.isEmpty()) {
1749         return new KisPaintDevice(colorSpace());
1750     }
1751 
1752     int srcWidth, srcHeight;
1753     int srcX0, srcY0;
1754     imageRect.getRect(&srcX0, &srcY0, &srcWidth, &srcHeight);
1755 
1756     if (!outputRect.isValid()) {
1757         outputRect = QRect(0, 0, w, h);
1758     }
1759 
1760     KisPaintDeviceSP thumbnail = createThumbnailDeviceInternal(this, imageRect.x(), imageRect.y(), imageRect.width(), imageRect.height(),
1761                                  thumbnailSize.width(), thumbnailSize.height(), outputRect);
1762 
1763     return thumbnail;
1764 }
1765 
1766 KisPaintDeviceSP KisPaintDevice::createThumbnailDeviceOversampled(qint32 w, qint32 h, qreal oversample, QRect rect,  QRect outputTileRect) const
1767 {
1768     QSize thumbnailSize(w, h);
1769     qreal oversampleAdjusted = qMax(oversample, 1.);
1770     QSize thumbnailOversampledSize = oversampleAdjusted * thumbnailSize;
1771 
1772     QRect outputRect;
1773     QRect imageRect = rect.isValid() ? rect : extent();
1774 
1775     qint32 hstart = thumbnailOversampledSize.height();
1776 
1777     if ((thumbnailOversampledSize.width() > imageRect.width()) || (thumbnailOversampledSize.height() > imageRect.height())) {
1778         thumbnailOversampledSize.scale(imageRect.size(), Qt::KeepAspectRatio);
1779     }
1780 
1781     thumbnailOversampledSize = fixThumbnailSize(thumbnailOversampledSize);
1782 
1783     //can't create thumbnail for an empty device, e.g. layer thumbnail for empty image
1784     if (imageRect.isEmpty() || thumbnailSize.isEmpty() || thumbnailOversampledSize.isEmpty()) {
1785         return new KisPaintDevice(colorSpace());
1786     }
1787 
1788     oversampleAdjusted *= (hstart > 0) ? ((qreal)thumbnailOversampledSize.height() / hstart) : 1.; //readjusting oversample ratio, given that we had to adjust thumbnail size
1789 
1790     outputRect = QRect(0, 0, thumbnailOversampledSize.width(), thumbnailOversampledSize.height());
1791 
1792     if (outputTileRect.isValid()) {
1793         //compensating output rectangle for oversampling
1794         outputTileRect = QRect(oversampleAdjusted * outputTileRect.topLeft(), oversampleAdjusted * outputTileRect.bottomRight());
1795         outputRect = outputRect.intersected(outputTileRect);
1796     }
1797 
1798     KisPaintDeviceSP thumbnail = createThumbnailDeviceInternal(this, imageRect.x(), imageRect.y(), imageRect.width(), imageRect.height(),
1799                                  thumbnailOversampledSize.width(), thumbnailOversampledSize.height(), outputRect);
1800 
1801     if (oversample != 1. && oversampleAdjusted != 1.) {
1802         KoDummyUpdater updater;
1803         KisTransformWorker worker(thumbnail, 1 / oversampleAdjusted, 1 / oversampleAdjusted, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
1804                                   &updater, KisFilterStrategyRegistry::instance()->value("Bilinear"));
1805         worker.run();
1806     }
1807     return thumbnail;
1808 }
1809 
1810 QImage KisPaintDevice::createThumbnail(qint32 w, qint32 h, QRect rect, qreal oversample, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags)
1811 {
1812     QSize size = fixThumbnailSize(QSize(w, h));
1813 
1814     KisPaintDeviceSP dev = createThumbnailDeviceOversampled(size.width(), size.height(), oversample, rect);
1815     QImage thumbnail = dev->convertToQImage(KoColorSpaceRegistry::instance()->rgb8()->profile(), 0, 0, w, h, renderingIntent, conversionFlags);
1816     return thumbnail;
1817 }
1818 
1819 QImage KisPaintDevice::createThumbnail(qint32 w, qint32 h, qreal oversample, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags)
1820 {
1821     QSize size = fixThumbnailSize(QSize(w, h));
1822 
1823     return m_d->cache()->createThumbnail(size.width(), size.height(), oversample, renderingIntent, conversionFlags);
1824 }
1825 
1826 QImage KisPaintDevice::createThumbnail(qint32 maxw, qint32 maxh,
1827                                        Qt::AspectRatioMode aspectRatioMode,
1828                                        qreal oversample, KoColorConversionTransformation::Intent renderingIntent,
1829                                        KoColorConversionTransformation::ConversionFlags conversionFlags)
1830 {
1831     const QRect deviceExtent = extent();
1832     const QSize thumbnailSize = deviceExtent.size().scaled(maxw, maxh, aspectRatioMode);
1833     return createThumbnail(thumbnailSize.width(), thumbnailSize.height(),
1834                            oversample, renderingIntent, conversionFlags);
1835 }
1836 
1837 KisHLineIteratorSP KisPaintDevice::createHLineIteratorNG(qint32 x, qint32 y, qint32 w)
1838 {
1839     m_d->cache()->invalidate();
1840     return m_d->currentStrategy()->createHLineIteratorNG(m_d->dataManager().data(), x, y, w, m_d->x(), m_d->y());
1841 }
1842 
1843 KisHLineConstIteratorSP KisPaintDevice::createHLineConstIteratorNG(qint32 x, qint32 y, qint32 w) const
1844 {
1845     return m_d->currentStrategy()->createHLineConstIteratorNG(m_d->dataManager().data(), x, y, w, m_d->x(), m_d->y());
1846 }
1847 
1848 KisVLineIteratorSP KisPaintDevice::createVLineIteratorNG(qint32 x, qint32 y, qint32 w)
1849 {
1850     m_d->cache()->invalidate();
1851     return m_d->currentStrategy()->createVLineIteratorNG(x, y, w);
1852 }
1853 
1854 KisVLineConstIteratorSP KisPaintDevice::createVLineConstIteratorNG(qint32 x, qint32 y, qint32 w) const
1855 {
1856     return m_d->currentStrategy()->createVLineConstIteratorNG(x, y, w);
1857 }
1858 
1859 KisRepeatHLineConstIteratorSP KisPaintDevice::createRepeatHLineConstIterator(qint32 x, qint32 y, qint32 w, const QRect& _dataWidth) const
1860 {
1861     return new KisRepeatHLineConstIteratorNG(m_d->dataManager().data(), x, y, w, m_d->x(), m_d->y(), _dataWidth, m_d->cacheInvalidator());
1862 }
1863 
1864 KisRepeatVLineConstIteratorSP KisPaintDevice::createRepeatVLineConstIterator(qint32 x, qint32 y, qint32 h, const QRect& _dataWidth) const
1865 {
1866     return new KisRepeatVLineConstIteratorNG(m_d->dataManager().data(), x, y, h, m_d->x(), m_d->y(), _dataWidth, m_d->cacheInvalidator());
1867 }
1868 
1869 KisRandomAccessorSP KisPaintDevice::createRandomAccessorNG()
1870 {
1871     m_d->cache()->invalidate();
1872     return m_d->currentStrategy()->createRandomAccessorNG();
1873 }
1874 
1875 KisRandomConstAccessorSP KisPaintDevice::createRandomConstAccessorNG() const
1876 {
1877     return m_d->currentStrategy()->createRandomConstAccessorNG();
1878 }
1879 
1880 KisRandomSubAccessorSP KisPaintDevice::createRandomSubAccessor() const
1881 {
1882     KisPaintDevice* pd = const_cast<KisPaintDevice*>(this);
1883     return new KisRandomSubAccessor(pd);
1884 }
1885 
1886 void KisPaintDevice::clearSelection(KisSelectionSP selection)
1887 {
1888     const KoColorSpace *colorSpace = m_d->colorSpace();
1889     const QRect r = selection->selectedExactRect();
1890 
1891     if (r.isValid()) {
1892 
1893         {
1894             KisHLineIteratorSP devIt = createHLineIteratorNG(r.x(), r.y(), r.width());
1895             KisHLineConstIteratorSP selectionIt = selection->projection()->createHLineConstIteratorNG(r.x(), r.y(), r.width());
1896 
1897             const KoColor defaultPixel = this->defaultPixel();
1898             bool transparentDefault = (defaultPixel.opacityU8() == OPACITY_TRANSPARENT_U8);
1899             for (qint32 y = 0; y < r.height(); y++) {
1900 
1901                 do {
1902                     // XXX: Optimize by using stretches
1903                     colorSpace->applyInverseAlphaU8Mask(devIt->rawData(), selectionIt->rawDataConst(), 1);
1904                     if (transparentDefault && colorSpace->opacityU8(devIt->rawData()) == OPACITY_TRANSPARENT_U8) {
1905                         memcpy(devIt->rawData(), defaultPixel.data(), colorSpace->pixelSize());
1906                     }
1907                 } while (devIt->nextPixel() && selectionIt->nextPixel());
1908                 devIt->nextRow();
1909                 selectionIt->nextRow();
1910             }
1911         }
1912 
1913         // purge() must be executed **after** all iterators have been destroyed!
1914         m_d->dataManager()->purge(r.translated(-m_d->x(), -m_d->y()));
1915 
1916         setDirty(r);
1917     }
1918 }
1919 
1920 bool KisPaintDevice::pixel(qint32 x, qint32 y, QColor *c) const
1921 {
1922     KisHLineConstIteratorSP iter = createHLineConstIteratorNG(x, y, 1);
1923 
1924     const quint8 *pix = iter->rawDataConst();
1925 
1926     if (!pix) return false;
1927 
1928     colorSpace()->toQColor(pix, c);
1929 
1930     return true;
1931 }
1932 
1933 
1934 bool KisPaintDevice::pixel(qint32 x, qint32 y, KoColor * kc) const
1935 {
1936     KisHLineConstIteratorSP iter = createHLineConstIteratorNG(x, y, 1);
1937 
1938     const quint8 *pix = iter->rawDataConst();
1939 
1940     if (!pix) return false;
1941 
1942     kc->setColor(pix, m_d->colorSpace());
1943 
1944     return true;
1945 }
1946 
1947 KoColor KisPaintDevice::pixel(const QPoint &pos) const
1948 {
1949     KisHLineConstIteratorSP iter = createHLineConstIteratorNG(pos.x(), pos.y(), 1);
1950     return KoColor(iter->rawDataConst(), m_d->colorSpace());
1951 }
1952 
1953 bool KisPaintDevice::setPixel(qint32 x, qint32 y, const QColor& c)
1954 {
1955     KisHLineIteratorSP iter = createHLineIteratorNG(x, y, 1);
1956 
1957     colorSpace()->fromQColor(c, iter->rawData());
1958     m_d->cache()->invalidate();
1959     return true;
1960 }
1961 
1962 bool KisPaintDevice::setPixel(qint32 x, qint32 y, const KoColor& kc)
1963 {
1964     const quint8 * pix;
1965     KisHLineIteratorSP iter = createHLineIteratorNG(x, y, 1);
1966     if (kc.colorSpace() != m_d->colorSpace()) {
1967         KoColor kc2(kc, m_d->colorSpace());
1968         pix = kc2.data();
1969         memcpy(iter->rawData(), pix, m_d->colorSpace()->pixelSize());
1970     } else {
1971         pix = kc.data();
1972         memcpy(iter->rawData(), pix, m_d->colorSpace()->pixelSize());
1973     }
1974     m_d->cache()->invalidate();
1975     return true;
1976 }
1977 
1978 bool KisPaintDevice::fastBitBltPossible(KisPaintDeviceSP src)
1979 {
1980     return m_d->fastBitBltPossible(src);
1981 }
1982 
1983 void KisPaintDevice::fastBitBlt(KisPaintDeviceSP src, const QRect &rect)
1984 {
1985     m_d->currentStrategy()->fastBitBlt(src, rect);
1986 }
1987 
1988 void KisPaintDevice::fastBitBltOldData(KisPaintDeviceSP src, const QRect &rect)
1989 {
1990     m_d->currentStrategy()->fastBitBltOldData(src, rect);
1991 }
1992 
1993 void KisPaintDevice::fastBitBltRough(KisPaintDeviceSP src, const QRect &rect)
1994 {
1995     m_d->currentStrategy()->fastBitBltRough(src, rect);
1996 }
1997 
1998 void KisPaintDevice::fastBitBltRoughOldData(KisPaintDeviceSP src, const QRect &rect)
1999 {
2000     m_d->currentStrategy()->fastBitBltRoughOldData(src, rect);
2001 }
2002 
2003 void KisPaintDevice::readBytes(quint8 * data, qint32 x, qint32 y, qint32 w, qint32 h) const
2004 {
2005     readBytes(data, QRect(x, y, w, h));
2006 }
2007 
2008 void KisPaintDevice::readBytes(quint8 *data, const QRect &rect) const
2009 {
2010     m_d->currentStrategy()->readBytes(data, rect);
2011 }
2012 
2013 void KisPaintDevice::writeBytes(const quint8 *data, qint32 x, qint32 y, qint32 w, qint32 h)
2014 {
2015     writeBytes(data, QRect(x, y, w, h));
2016 }
2017 
2018 void KisPaintDevice::writeBytes(const quint8 *data, const QRect &rect)
2019 {
2020     m_d->currentStrategy()->writeBytes(data, rect);
2021 }
2022 
2023 QVector<quint8*> KisPaintDevice::readPlanarBytes(qint32 x, qint32 y, qint32 w, qint32 h) const
2024 {
2025     return m_d->currentStrategy()->readPlanarBytes(x, y, w, h);
2026 }
2027 
2028 void KisPaintDevice::writePlanarBytes(QVector<quint8*> planes, qint32 x, qint32 y, qint32 w, qint32 h)
2029 {
2030     m_d->currentStrategy()->writePlanarBytes(planes, x, y, w, h);
2031 }
2032 
2033 
2034 quint32 KisPaintDevice::pixelSize() const
2035 {
2036     quint32 _pixelSize = m_d->colorSpace()->pixelSize();
2037     Q_ASSERT(_pixelSize > 0);
2038     return _pixelSize;
2039 }
2040 
2041 quint32 KisPaintDevice::channelCount() const
2042 {
2043     quint32 _channelCount = m_d->colorSpace()->channelCount();
2044     Q_ASSERT(_channelCount > 0);
2045     return _channelCount;
2046 }
2047 
2048 KisInterstrokeDataSP KisPaintDevice::interstrokeData() const
2049 {
2050     return m_d->interstrokeData();
2051 }
2052 
2053 KUndo2Command* KisPaintDevice::createChangeInterstrokeDataCommand(KisInterstrokeDataSP data)
2054 {
2055     return m_d->createChangeInterstrokeDataCommand(data);
2056 }
2057 
2058 KisRasterKeyframeChannel *KisPaintDevice::createKeyframeChannel(const KoID &id)
2059 {
2060     Q_ASSERT(!m_d->framesInterface);
2061     m_d->framesInterface.reset(new KisPaintDeviceFramesInterface(this));
2062 
2063     Q_ASSERT(!m_d->contentChannel);
2064     if (m_d->parent.isValid()) {
2065         m_d->contentChannel.reset(new KisRasterKeyframeChannel(id, this, new KisDefaultBoundsNodeWrapper(m_d->parent)));
2066     } else {
2067         //fallback when paint device is isolated / does not belong to a node.
2068         m_d->contentChannel.reset(new KisRasterKeyframeChannel(id, this, m_d->defaultBounds));
2069     }
2070 
2071     // Raster channels always have at least one frame (representing a static image)
2072     KUndo2Command tempParentCommand;
2073     m_d->contentChannel->addKeyframe(0);
2074 
2075     return m_d->contentChannel.data();
2076 }
2077 
2078 KisRasterKeyframeChannel* KisPaintDevice::keyframeChannel() const
2079 {
2080     if (m_d->contentChannel) {
2081         return m_d->contentChannel.data();
2082     }
2083     return 0;
2084 }
2085 
2086 const KoColorSpace* KisPaintDevice::colorSpace() const
2087 {
2088     Q_ASSERT(m_d->colorSpace() != 0);
2089     return m_d->colorSpace();
2090 }
2091 
2092 KisPaintDeviceSP KisPaintDevice::createCompositionSourceDevice() const
2093 {
2094     KisPaintDeviceSP device = new KisPaintDevice(compositionSourceColorSpace());
2095     device->setDefaultBounds(defaultBounds());
2096     return device;
2097 }
2098 
2099 KisPaintDeviceSP KisPaintDevice::createCompositionSourceDevice(KisPaintDeviceSP cloneSource) const
2100 {
2101     KisPaintDeviceSP clone = new KisPaintDevice(*cloneSource);
2102     clone->setDefaultBounds(defaultBounds());
2103     clone->convertTo(compositionSourceColorSpace(),
2104                      KoColorConversionTransformation::internalRenderingIntent(),
2105                      KoColorConversionTransformation::internalConversionFlags());
2106     return clone;
2107 }
2108 
2109 KisPaintDeviceSP KisPaintDevice::createCompositionSourceDevice(KisPaintDeviceSP cloneSource, const QRect roughRect) const
2110 {
2111     KisPaintDeviceSP clone = new KisPaintDevice(colorSpace());
2112     clone->setDefaultBounds(defaultBounds());
2113     clone->makeCloneFromRough(cloneSource, roughRect);
2114     clone->convertTo(compositionSourceColorSpace(),
2115                      KoColorConversionTransformation::internalRenderingIntent(),
2116                      KoColorConversionTransformation::internalConversionFlags());
2117     return clone;
2118 }
2119 
2120 KisFixedPaintDeviceSP KisPaintDevice::createCompositionSourceDeviceFixed() const
2121 {
2122     return new KisFixedPaintDevice(compositionSourceColorSpace());
2123 }
2124 
2125 const KoColorSpace* KisPaintDevice::compositionSourceColorSpace() const
2126 {
2127     return colorSpace();
2128 }
2129 
2130 QVector<qint32> KisPaintDevice::channelSizes() const
2131 {
2132     QVector<qint32> sizes;
2133     QList<KoChannelInfo*> channels = colorSpace()->channels();
2134     std::sort(channels.begin(), channels.end());
2135 
2136     Q_FOREACH (KoChannelInfo * channelInfo, channels) {
2137         sizes.append(channelInfo->size());
2138     }
2139     return sizes;
2140 }
2141 
2142 KisPaintDevice::MemoryReleaseObject::~MemoryReleaseObject()
2143 {
2144     KisDataManager::releaseInternalPools();
2145 }
2146 
2147 KisPaintDevice::MemoryReleaseObject* KisPaintDevice::createMemoryReleaseObject()
2148 {
2149     return new MemoryReleaseObject();
2150 }
2151 
2152 KisPaintDevice::LodDataStruct::~LodDataStruct()
2153 {
2154 }
2155 
2156 KisRegion KisPaintDevice::regionForLodSyncing() const
2157 {
2158     return m_d->regionForLodSyncing();
2159 }
2160 
2161 KisPaintDevice::LodDataStruct* KisPaintDevice::createLodDataStruct(int lod)
2162 {
2163     return m_d->createLodDataStruct(lod);
2164 }
2165 
2166 void KisPaintDevice::updateLodDataStruct(LodDataStruct *dst, const QRect &srcRect)
2167 {
2168     m_d->updateLodDataStruct(dst, srcRect);
2169 }
2170 
2171 void KisPaintDevice::uploadLodDataStruct(LodDataStruct *dst)
2172 {
2173     m_d->uploadLodDataStruct(dst);
2174 }
2175 
2176 void KisPaintDevice::generateLodCloneDevice(KisPaintDeviceSP dst, const QRect &originalRect, int lod)
2177 {
2178     m_d->generateLodCloneDevice(dst, originalRect, lod);
2179 }
2180 
2181 
2182 KisPaintDeviceFramesInterface* KisPaintDevice::framesInterface()
2183 {
2184     return m_d->framesInterface.data();
2185 }
2186 
2187 bool KisPaintDevice::burnKeyframe(int frameID)
2188 {
2189     KIS_ASSERT_RECOVER_RETURN_VALUE(m_d->framesInterface.data()->frames().contains(frameID), false);
2190 
2191     // Preserve keyframe data from frameID...
2192     KisPaintDeviceSP holder = new KisPaintDevice(m_d->colorSpace());
2193     m_d->framesInterface->writeFrameToDevice(frameID, holder);
2194 
2195     // Remove all keyframes..
2196     QSet<int> times = m_d->contentChannel->allKeyframeTimes();
2197     Q_FOREACH( const int& time, times ) {
2198         m_d->contentChannel->removeKeyframe(time);
2199     }
2200 
2201     // TODO: Eventually rewrite this to completely remove contentChannel.
2202     //          For now, importing it as frame 0 will be close enough.
2203     m_d->contentChannel->importFrame(0, holder, nullptr);
2204 
2205     return true;
2206 }
2207 
2208 bool KisPaintDevice::burnKeyframe()
2209 {
2210     if (m_d->framesInterface) {
2211         return burnKeyframe(m_d->framesInterface->currentFrameId());
2212     }
2213     return true;
2214 }
2215 
2216 /******************************************************************/
2217 /*               KisPaintDeviceFramesInterface                    */
2218 /******************************************************************/
2219 
2220 KisPaintDeviceFramesInterface::KisPaintDeviceFramesInterface(KisPaintDevice *parentDevice)
2221     : q(parentDevice)
2222 {
2223 }
2224 
2225 QList<int> KisPaintDeviceFramesInterface::frames()
2226 {
2227     return q->m_d->frameIds();
2228 }
2229 
2230 int KisPaintDeviceFramesInterface::createFrame(bool copy, int copySrc, const QPoint &offset, KUndo2Command *parentCommand)
2231 {
2232     return q->m_d->createFrame(copy, copySrc, offset, parentCommand);
2233 }
2234 
2235 void KisPaintDeviceFramesInterface::deleteFrame(int frame, KUndo2Command *parentCommand)
2236 {
2237     return q->m_d->deleteFrame(frame, parentCommand);
2238 }
2239 
2240 void KisPaintDeviceFramesInterface::writeFrameToDevice(int frameId, KisPaintDeviceSP targetDevice)
2241 {
2242     q->m_d->writeFrameToDevice(frameId, targetDevice);
2243 }
2244 
2245 void KisPaintDeviceFramesInterface::uploadFrame(int srcFrameId, int dstFrameId, KisPaintDeviceSP srcDevice)
2246 {
2247     q->m_d->uploadFrame(srcFrameId, dstFrameId, srcDevice);
2248 }
2249 
2250 void KisPaintDeviceFramesInterface::uploadFrame(int dstFrameId, KisPaintDeviceSP srcDevice)
2251 {
2252     q->m_d->uploadFrame(dstFrameId, srcDevice);
2253 }
2254 
2255 QRect KisPaintDeviceFramesInterface::frameBounds(int frameId)
2256 {
2257     return q->m_d->frameBounds(frameId);
2258 }
2259 
2260 QPoint KisPaintDeviceFramesInterface::frameOffset(int frameId) const
2261 {
2262     return q->m_d->frameOffset(frameId);
2263 }
2264 
2265 void KisPaintDeviceFramesInterface::setFrameDefaultPixel(const KoColor &defPixel, int frameId)
2266 {
2267     KIS_ASSERT_RECOVER_RETURN(frameId >= 0);
2268     q->m_d->setFrameDefaultPixel(defPixel, frameId);
2269 }
2270 
2271 KoColor KisPaintDeviceFramesInterface::frameDefaultPixel(int frameId) const
2272 {
2273     KIS_ASSERT_RECOVER(frameId >= 0) {
2274         return KoColor(Qt::red, q->m_d->colorSpace());
2275     }
2276     return q->m_d->frameDefaultPixel(frameId);
2277 }
2278 
2279 bool KisPaintDeviceFramesInterface::writeFrame(KisPaintDeviceWriter &store, int frameId)
2280 {
2281     KIS_ASSERT_RECOVER(frameId >= 0) {
2282         return false;
2283     }
2284     return q->m_d->writeFrame(store, frameId);
2285 }
2286 
2287 bool KisPaintDeviceFramesInterface::readFrame(QIODevice *stream, int frameId)
2288 {
2289     KIS_ASSERT_RECOVER(frameId >= 0) {
2290         return false;
2291     }
2292     return q->m_d->readFrame(stream, frameId);
2293 }
2294 
2295 int KisPaintDeviceFramesInterface::currentFrameId() const
2296 {
2297     return q->m_d->currentFrameId();
2298 }
2299 
2300 KisDataManagerSP KisPaintDeviceFramesInterface::frameDataManager(int frameId) const
2301 {
2302     KIS_ASSERT_RECOVER(frameId >= 0) {
2303         return q->m_d->dataManager();
2304     }
2305     return q->m_d->frameDataManager(frameId);
2306 }
2307 
2308 void KisPaintDeviceFramesInterface::invalidateFrameCache(int frameId)
2309 {
2310     KIS_ASSERT_RECOVER_RETURN(frameId >= 0);
2311 
2312     return q->m_d->invalidateFrameCache(frameId);
2313 }
2314 
2315 void KisPaintDeviceFramesInterface::setFrameOffset(int frameId, const QPoint &offset)
2316 {
2317     KIS_ASSERT_RECOVER_RETURN(frameId >= 0);
2318     return q->m_d->setFrameOffset(frameId, offset);
2319 }
2320 
2321 KisPaintDeviceFramesInterface::TestingDataObjects
2322 KisPaintDeviceFramesInterface::testingGetDataObjects() const
2323 {
2324     TestingDataObjects objects;
2325 
2326     objects.m_data = q->m_d->m_data.data();
2327     objects.m_lodData = q->m_d->m_lodData.data();
2328     objects.m_externalFrameData = q->m_d->m_externalFrameData.data();
2329 
2330     typedef KisPaintDevice::Private::FramesHash FramesHash;
2331 
2332     FramesHash::const_iterator it = q->m_d->m_frames.constBegin();
2333     FramesHash::const_iterator end = q->m_d->m_frames.constEnd();
2334 
2335     for (; it != end; ++it) {
2336         objects.m_frames.insert(it.key(), it.value().data());
2337     }
2338 
2339     objects.m_currentData = q->m_d->currentData();
2340 
2341     return objects;
2342 }
2343 
2344 QList<KisPaintDeviceData*> KisPaintDeviceFramesInterface::testingGetDataObjectsList() const
2345 {
2346     return q->m_d->allDataObjects();
2347 }
2348 
2349 void KisPaintDevice::tesingFetchLodDevice(KisPaintDeviceSP targetDevice)
2350 {
2351     m_d->tesingFetchLodDevice(targetDevice);
2352 }