File indexing completed on 2024-05-19 04:26:25

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