1 /*
2  *  Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
3  *  Copyright (c) 2004 Boudewijn Rempt <boud@valdyas.org>
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  */
19 
20 #include "kis_paint_device.h"
21 
22 #include <QRect>
23 #include <QTransform>
24 #include <QImage>
25 #include <QList>
26 #include <QHash>
27 #include <QIODevice>
28 #include <qmath.h>
29 #include <KisRegion.h>
30 
31 #include <klocalizedstring.h>
32 
33 #include <KoChannelInfo.h>
34 #include <KoColorProfile.h>
35 #include <KoColor.h>
36 #include <KoColorSpace.h>
37 #include <KoColorSpaceRegistry.h>
38 #include <KoColorModelStandardIds.h>
39 #include <KoIntegerMaths.h>
40 #include <KoMixColorsOp.h>
41 #include <KoUpdater.h>
42 
43 #include "kis_image.h"
44 #include "kis_random_sub_accessor.h"
45 #include "kis_selection.h"
46 #include "kis_node.h"
47 #include "kis_datamanager.h"
48 #include "kis_paint_device_writer.h"
49 #include "kis_selection_component.h"
50 #include "kis_pixel_selection.h"
51 #include "kis_repeat_iterators_pixel.h"
52 #include "kis_fixed_paint_device.h"
53 
54 #include "tiles3/kis_hline_iterator.h"
55 #include "tiles3/kis_vline_iterator.h"
56 #include "tiles3/kis_random_accessor.h"
57 
58 #include "kis_default_bounds.h"
59 
60 #include "kis_lod_transform.h"
61 
62 #include "kis_raster_keyframe_channel.h"
63 
64 #include "kis_paint_device_cache.h"
65 #include "kis_paint_device_data.h"
66 #include "kis_paint_device_frames_interface.h"
67 
68 #include "kis_transform_worker.h"
69 #include "kis_filter_strategy.h"
70 #include "krita_utils.h"
71 
72 
73 struct KisPaintDeviceSPStaticRegistrar {
KisPaintDeviceSPStaticRegistrarKisPaintDeviceSPStaticRegistrar74     KisPaintDeviceSPStaticRegistrar() {
75         qRegisterMetaType<KisPaintDeviceSP>("KisPaintDeviceSP");
76     }
77 };
78 static KisPaintDeviceSPStaticRegistrar __registrar;
79 
80 
81 
82 struct KisPaintDevice::Private
83 {
84     /**
85      * Used when the paint device is loading to ensure no lod/animation
86      * interferes the process.
87      */
88     static const KisDefaultBoundsSP transitionalDefaultBounds;
89 
90 public:
91 
92     class KisPaintDeviceStrategy;
93     class KisPaintDeviceWrappedStrategy;
94 
95     class DeviceChangeProfileCommand;
96     class DeviceChangeColorSpaceCommand;
97 
98     Private(KisPaintDevice *paintDevice);
99     ~Private();
100 
101     KisPaintDevice *q;
102     KisNodeWSP parent;
103     QScopedPointer<KisRasterKeyframeChannel> contentChannel;
104     KisDefaultBoundsBaseSP defaultBounds;
105     QScopedPointer<KisPaintDeviceStrategy> basicStrategy;
106     QScopedPointer<KisPaintDeviceWrappedStrategy> wrappedStrategy;
107     QMutex m_wrappedStrategyMutex;
108 
109     QScopedPointer<KisPaintDeviceFramesInterface> framesInterface;
110     bool isProjectionDevice;
111 
112     KisPaintDeviceStrategy* currentStrategy();
113 
114     void init(const KoColorSpace *cs, const quint8 *defaultPixel);
115     void convertColorSpace(const KoColorSpace * dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags, KUndo2Command *parentCommand);
116     bool assignProfile(const KoColorProfile * profile, KUndo2Command *parentCommand);
117 
118     KUndo2Command* reincarnateWithDetachedHistory(bool copyContent);
119 
120 
colorSpaceKisPaintDevice::Private121     inline const KoColorSpace* colorSpace() const
122     {
123         return currentData()->colorSpace();
124     }
dataManagerKisPaintDevice::Private125     inline KisDataManagerSP dataManager() const
126     {
127         return currentData()->dataManager();
128     }
129 
xKisPaintDevice::Private130     inline qint32 x() const
131     {
132         return currentData()->x();
133     }
yKisPaintDevice::Private134     inline qint32 y() const
135     {
136         return currentData()->y();
137     }
setXKisPaintDevice::Private138     inline void setX(qint32 x)
139     {
140         currentData()->setX(x);
141     }
setYKisPaintDevice::Private142     inline void setY(qint32 y)
143     {
144         currentData()->setY(y);
145     }
146 
cacheKisPaintDevice::Private147     inline KisPaintDeviceCache* cache()
148     {
149         return currentData()->cache();
150     }
151 
cacheInvalidatorKisPaintDevice::Private152     inline KisIteratorCompleteListener* cacheInvalidator() {
153         return currentData()->cacheInvalidator();
154     }
155 
156 
cloneAllDataObjectsKisPaintDevice::Private157     void cloneAllDataObjects(Private *rhs, bool copyFrames)
158     {
159 
160         m_lodData.reset();
161         m_externalFrameData.reset();
162 
163         if (!m_frames.isEmpty()) {
164             m_frames.clear();
165         }
166 
167         if (!copyFrames) {
168             if (m_data) {
169                 m_data->prepareClone(rhs->currentNonLodData(), true);
170             } else {
171                 m_data = toQShared(new KisPaintDeviceData(q, rhs->currentNonLodData(), true));
172             }
173         } else {
174             if (m_data && !rhs->m_data) {
175                 m_data.clear();
176             } else if (!m_data && rhs->m_data) {
177                 m_data = toQShared(new KisPaintDeviceData(q, rhs->m_data.data(), true));
178             } else if (m_data && rhs->m_data) {
179                 m_data->prepareClone(rhs->m_data.data(), true);
180             }
181 
182             if (!rhs->m_frames.isEmpty()) {
183                 FramesHash::const_iterator it = rhs->m_frames.constBegin();
184                 FramesHash::const_iterator end = rhs->m_frames.constEnd();
185 
186                 for (; it != end; ++it) {
187                     DataSP data = toQShared(new KisPaintDeviceData(q, it.value().data(), true));
188                     m_frames.insert(it.key(), data);
189                 }
190             }
191             m_nextFreeFrameId = rhs->m_nextFreeFrameId;
192         }
193 
194         if (rhs->m_lodData) {
195             m_lodData.reset(new KisPaintDeviceData(q, rhs->m_lodData.data(), true));
196         }
197     }
198 
prepareCloneKisPaintDevice::Private199     void prepareClone(KisPaintDeviceSP src)
200     {
201         prepareCloneImpl(src, src->m_d->currentData());
202         KIS_SAFE_ASSERT_RECOVER_NOOP(fastBitBltPossible(src));
203     }
204 
fastBitBltPossibleKisPaintDevice::Private205     bool fastBitBltPossible(KisPaintDeviceSP src)
206     {
207         return fastBitBltPossibleImpl(src->m_d->currentData());
208     }
209 
currentFrameIdKisPaintDevice::Private210     int currentFrameId() const
211     {
212         KIS_ASSERT_RECOVER(contentChannel) {
213             return -1;
214         }
215         return !defaultBounds->currentLevelOfDetail() ?
216                contentChannel->frameIdAt(defaultBounds->currentTime()) :
217                -1;
218     }
219 
frameDataManagerKisPaintDevice::Private220     KisDataManagerSP frameDataManager(int frameId) const
221     {
222         DataSP data = m_frames[frameId];
223         return data->dataManager();
224     }
225 
invalidateFrameCacheKisPaintDevice::Private226     void invalidateFrameCache(int frameId)
227     {
228         DataSP data = m_frames[frameId];
229         return data->cache()->invalidate();
230     }
231 
232 private:
233     typedef KisPaintDeviceData Data;
234     typedef QSharedPointer<Data> DataSP;
235     typedef QHash<int, DataSP> FramesHash;
236 
237     class FrameInsertionCommand : public KUndo2Command
238     {
239     public:
240 
FrameInsertionCommand(FramesHash * hash,DataSP data,int frameId,bool insert,KUndo2Command * parentCommand)241         FrameInsertionCommand(FramesHash *hash, DataSP data, int frameId, bool insert, KUndo2Command *parentCommand)
242             : KUndo2Command(parentCommand),
243               m_hash(hash),
244               m_data(data),
245               m_frameId(frameId),
246               m_insert(insert)
247         {
248         }
249 
redo()250         void redo() override
251         {
252             doSwap(m_insert);
253         }
254 
undo()255         void undo() override
256         {
257             doSwap(!m_insert);
258         }
259 
260     private:
doSwap(bool insert)261         void doSwap(bool insert)
262         {
263             if (insert) {
264                 m_hash->insert(m_frameId, m_data);
265             } else {
266                 DataSP deletedData = m_hash->take(m_frameId);
267             }
268         }
269 
270     private:
271         FramesHash *m_hash;
272         DataSP m_data;
273         int m_frameId;
274         bool m_insert;
275     };
276 
277 public:
278 
getNextFrameIdKisPaintDevice::Private279     int getNextFrameId() {
280         int frameId = 0;
281         while (m_frames.contains(frameId = m_nextFreeFrameId++));
282         KIS_SAFE_ASSERT_RECOVER_NOOP(!m_frames.contains(frameId));
283 
284         return frameId;
285     }
286 
createFrameKisPaintDevice::Private287     int createFrame(bool copy, int copySrc, const QPoint &offset, KUndo2Command *parentCommand)
288     {
289         KIS_ASSERT_RECOVER(parentCommand) {
290             return -1;
291         }
292 
293         DataSP data;
294         bool initialFrame = false;
295 
296         if (m_frames.isEmpty()) {
297             /**
298              * Here we move the contents of the paint device to the
299              * new frame and clear m_data to make the "background" for
300              * the areas where there is no frame at all.
301              */
302             data = toQShared(new Data(q, m_data.data(), true));
303             m_data->dataManager()->clear();
304             m_data->cache()->invalidate();
305             initialFrame = true;
306 
307         } else if (copy) {
308             DataSP srcData = m_frames[copySrc];
309             data = toQShared(new Data(q, srcData.data(), true));
310         } else {
311             DataSP srcData = m_frames.begin().value();
312             data = toQShared(new Data(q, srcData.data(), false));
313         }
314 
315         if (!initialFrame && !copy) {
316             data->setX(offset.x());
317             data->setY(offset.y());
318         }
319 
320         int frameId = getNextFrameId();
321 
322         KUndo2Command *cmd =
323             new FrameInsertionCommand(&m_frames,
324                                       data,
325                                       frameId, true,
326                                       parentCommand);
327 
328         cmd->redo();
329 
330         return frameId;
331     }
332 
deleteFrameKisPaintDevice::Private333     void deleteFrame(int frame, KUndo2Command *parentCommand)
334     {
335         KIS_ASSERT_RECOVER_RETURN(m_frames.contains(frame));
336         KIS_ASSERT_RECOVER_RETURN(parentCommand);
337 
338         DataSP deletedData = m_frames[frame];
339 
340         KUndo2Command *cmd =
341             new FrameInsertionCommand(&m_frames,
342                                       deletedData,
343                                       frame, false,
344                                       parentCommand);
345         cmd->redo();
346     }
347 
frameBoundsKisPaintDevice::Private348     QRect frameBounds(int frameId)
349     {
350         DataSP data = m_frames[frameId];
351 
352         QRect extent = data->dataManager()->extent();
353         extent.translate(data->x(), data->y());
354 
355         return extent;
356     }
357 
frameOffsetKisPaintDevice::Private358     QPoint frameOffset(int frameId) const
359     {
360         DataSP data = m_frames[frameId];
361         return QPoint(data->x(), data->y());
362     }
363 
setFrameOffsetKisPaintDevice::Private364     void setFrameOffset(int frameId, const QPoint &offset)
365     {
366         DataSP data = m_frames[frameId];
367         data->setX(offset.x());
368         data->setY(offset.y());
369     }
370 
frameIdsKisPaintDevice::Private371     const QList<int> frameIds() const
372     {
373         return m_frames.keys();
374     }
375 
readFrameKisPaintDevice::Private376     bool readFrame(QIODevice *stream, int frameId)
377     {
378         bool retval = false;
379         DataSP data = m_frames[frameId];
380         retval = data->dataManager()->read(stream);
381         data->cache()->invalidate();
382         return retval;
383     }
384 
writeFrameKisPaintDevice::Private385     bool writeFrame(KisPaintDeviceWriter &store, int frameId)
386     {
387         DataSP data = m_frames[frameId];
388         return data->dataManager()->write(store);
389     }
390 
setFrameDefaultPixelKisPaintDevice::Private391     void setFrameDefaultPixel(const KoColor &defPixel, int frameId)
392     {
393         DataSP data = m_frames[frameId];
394         KoColor color(defPixel);
395         color.convertTo(data->colorSpace());
396         data->dataManager()->setDefaultPixel(color.data());
397     }
398 
frameDefaultPixelKisPaintDevice::Private399     KoColor frameDefaultPixel(int frameId) const
400     {
401         DataSP data = m_frames[frameId];
402         return KoColor(data->dataManager()->defaultPixel(),
403                        data->colorSpace());
404     }
405 
406     void fetchFrame(int frameId, KisPaintDeviceSP targetDevice);
407     void uploadFrame(int srcFrameId, int dstFrameId, KisPaintDeviceSP srcDevice);
408     void uploadFrame(int dstFrameId, KisPaintDeviceSP srcDevice);
409     void uploadFrameData(DataSP srcData, DataSP dstData);
410 
411     struct LodDataStructImpl;
412     LodDataStruct* createLodDataStruct(int lod);
413     void updateLodDataStruct(LodDataStruct *dst, const QRect &srcRect);
414     void uploadLodDataStruct(LodDataStruct *dst);
415     KisRegion regionForLodSyncing() const;
416 
417     void updateLodDataManager(KisDataManager *srcDataManager,
418                               KisDataManager *dstDataManager, const QPoint &srcOffset, const QPoint &dstOffset,
419                               const QRect &originalRect, int lod);
420 
421     void generateLodCloneDevice(KisPaintDeviceSP dst, const QRect &originalRect, int lod);
422 
423     void tesingFetchLodDevice(KisPaintDeviceSP targetDevice);
424 
425 
426 private:
estimateDataSizeKisPaintDevice::Private427     qint64 estimateDataSize(Data *data) const {
428         const QRect &rc = data->dataManager()->extent();
429         return qint64(rc.width()) * rc.height() * data->colorSpace()->pixelSize();
430     }
431 
432 public:
433 
estimateMemoryStatsKisPaintDevice::Private434     void estimateMemoryStats(qint64 &imageData, qint64 &temporaryData, qint64 &lodData) const {
435         imageData = 0;
436         temporaryData = 0;
437         lodData = 0;
438 
439         if (m_data) {
440             imageData += estimateDataSize(m_data.data());
441         }
442 
443         if (m_lodData) {
444             lodData += estimateDataSize(m_lodData.data());
445         }
446 
447         if (m_externalFrameData) {
448             temporaryData += estimateDataSize(m_externalFrameData.data());
449         }
450 
451         Q_FOREACH (DataSP value, m_frames.values()) {
452             imageData += estimateDataSize(value.data());
453         }
454     }
455 
456 
457 private:
458 
currentFrameDataKisPaintDevice::Private459     inline DataSP currentFrameData() const
460     {
461         DataSP data;
462 
463         const int numberOfFrames = contentChannel->keyframeCount();
464 
465         if (numberOfFrames > 1) {
466             int frameId = contentChannel->frameIdAt(defaultBounds->currentTime());
467 
468             if (frameId == -1) {
469                 data = m_data;
470             } else {
471                 KIS_ASSERT_RECOVER(m_frames.contains(frameId)) {
472                     return m_frames.begin().value();
473                 }
474                 data = m_frames[frameId];
475             }
476         } else if (numberOfFrames == 1) {
477             data = m_frames.begin().value();
478         } else {
479             data = m_data;
480         }
481 
482         return data;
483     }
484 
currentNonLodDataKisPaintDevice::Private485     inline Data* currentNonLodData() const
486     {
487         Data *data = m_data.data();
488 
489         if (contentChannel) {
490             data = currentFrameData().data();
491         } else if (isProjectionDevice && defaultBounds->externalFrameActive()) {
492             if (!m_externalFrameData) {
493                 QMutexLocker l(&m_dataSwitchLock);
494                 if (!m_externalFrameData) {
495                     m_externalFrameData.reset(new Data(q, m_data.data(), false));
496                 }
497             }
498             data = m_externalFrameData.data();
499         }
500 
501         return data;
502     }
503 
ensureLodDataPresentKisPaintDevice::Private504     inline void ensureLodDataPresent() const
505     {
506         if (!m_lodData) {
507             Data *srcData = currentNonLodData();
508 
509             QMutexLocker l(&m_dataSwitchLock);
510             if (!m_lodData) {
511                 m_lodData.reset(new Data(q, srcData, false));
512             }
513         }
514     }
515 
currentDataKisPaintDevice::Private516     inline Data* currentData() const
517     {
518         Data *data;
519 
520         if (defaultBounds->currentLevelOfDetail()) {
521             ensureLodDataPresent();
522             data = m_lodData.data();
523         } else {
524             data = currentNonLodData();
525         }
526 
527         return data;
528     }
529 
prepareCloneImplKisPaintDevice::Private530     void prepareCloneImpl(KisPaintDeviceSP src, Data *srcData)
531     {
532         /**
533          * The result of currentData() depends on the current
534          * level of detail and animation frame index. So we
535          * should first connect the device to the new
536          * default bounds object, and only after that ask
537          * currentData() to start cloning.
538          */
539         q->setDefaultBounds(src->defaultBounds());
540 
541         currentData()->prepareClone(srcData);
542 
543 
544         /**
545          * Default pixel must be updated **after** the color space
546          * of the device has been adjusted in prpareClone(). Otherwise,
547          * colorSpace() of the resulting KoColor object will be
548          * incorrect.
549          */
550         KIS_SAFE_ASSERT_RECOVER_RETURN(*colorSpace() == *src->colorSpace());
551         q->setDefaultPixel(KoColor(srcData->dataManager()->defaultPixel(), colorSpace()));
552 
553     }
554 
fastBitBltPossibleImplKisPaintDevice::Private555     bool fastBitBltPossibleImpl(Data *srcData)
556     {
557         return x() == srcData->x() && y() == srcData->y() &&
558                *colorSpace() == *srcData->colorSpace();
559     }
560 
allDataObjectsKisPaintDevice::Private561     QList<Data*> allDataObjects() const
562     {
563         QList<Data*> dataObjects;
564 
565         if (m_frames.isEmpty()) {
566             dataObjects << m_data.data();
567         }
568         dataObjects << m_lodData.data();
569         dataObjects << m_externalFrameData.data();
570 
571         Q_FOREACH (DataSP value, m_frames.values()) {
572             dataObjects << value.data();
573         }
574 
575         return dataObjects;
576     }
577 
578     void transferFromData(Data *data, KisPaintDeviceSP targetDevice);
579 
580     struct Q_DECL_HIDDEN StrategyPolicy;
581     typedef KisSequentialIteratorBase<ReadOnlyIteratorPolicy<StrategyPolicy>, StrategyPolicy> InternalSequentialConstIterator;
582     typedef KisSequentialIteratorBase<WritableIteratorPolicy<StrategyPolicy>, StrategyPolicy> InternalSequentialIterator;
583 
584 private:
585     friend class KisPaintDeviceFramesInterface;
586 
587 private:
588     DataSP m_data;
589     mutable QScopedPointer<Data> m_lodData;
590     mutable QScopedPointer<Data> m_externalFrameData;
591     mutable QMutex m_dataSwitchLock;
592 
593     FramesHash m_frames;
594     int m_nextFreeFrameId;
595 };
596 
597 const KisDefaultBoundsSP KisPaintDevice::Private::transitionalDefaultBounds = new KisDefaultBounds();
598 
599 #include "kis_paint_device_strategies.h"
600 
Private(KisPaintDevice * paintDevice)601 KisPaintDevice::Private::Private(KisPaintDevice *paintDevice)
602     : q(paintDevice),
603       basicStrategy(new KisPaintDeviceStrategy(paintDevice, this)),
604       isProjectionDevice(false),
605       m_data(new Data(paintDevice)),
606       m_nextFreeFrameId(0)
607 {
608 }
609 
~Private()610 KisPaintDevice::Private::~Private()
611 {
612     m_frames.clear();
613 }
614 
currentStrategy()615 KisPaintDevice::Private::KisPaintDeviceStrategy* KisPaintDevice::Private::currentStrategy()
616 {
617     if (!defaultBounds->wrapAroundMode()) {
618         return basicStrategy.data();
619     }
620 
621     const QRect wrapRect = defaultBounds->imageBorderRect();
622 
623     if (!wrappedStrategy || wrappedStrategy->wrapRect() != wrapRect) {
624         QMutexLocker locker(&m_wrappedStrategyMutex);
625 
626         if (!wrappedStrategy) {
627             wrappedStrategy.reset(new KisPaintDeviceWrappedStrategy(wrapRect, q, this));
628         }  else if (wrappedStrategy->wrapRect() != wrapRect) {
629             wrappedStrategy->setWrapRect(wrapRect);
630         }
631     }
632 
633     return wrappedStrategy.data();
634 }
635 
636 struct KisPaintDevice::Private::StrategyPolicy {
StrategyPolicyKisPaintDevice::Private::StrategyPolicy637     StrategyPolicy(KisPaintDevice::Private::KisPaintDeviceStrategy *strategy,
638                    KisDataManager *dataManager, qint32 offsetX, qint32 offsetY)
639         : m_strategy(strategy),
640           m_dataManager(dataManager),
641           m_offsetX(offsetX),
642           m_offsetY(offsetY)
643     {
644     }
645 
createConstIteratorKisPaintDevice::Private::StrategyPolicy646     KisHLineConstIteratorSP createConstIterator(const QRect &rect)
647     {
648         return m_strategy->createHLineConstIteratorNG(m_dataManager, rect.x(), rect.y(), rect.width(), m_offsetX, m_offsetY);
649     }
650 
createIteratorKisPaintDevice::Private::StrategyPolicy651     KisHLineIteratorSP createIterator(const QRect &rect)
652     {
653         return m_strategy->createHLineIteratorNG(m_dataManager, rect.x(), rect.y(), rect.width(), m_offsetX, m_offsetY);
654     }
655 
pixelSizeKisPaintDevice::Private::StrategyPolicy656     int pixelSize() const
657     {
658         return m_dataManager->pixelSize();
659     }
660 
661 
662     KisPaintDeviceStrategy *m_strategy;
663     KisDataManager *m_dataManager;
664     int m_offsetX;
665     int m_offsetY;
666 };
667 
668 struct KisPaintDevice::Private::LodDataStructImpl : public KisPaintDevice::LodDataStruct {
LodDataStructImplKisPaintDevice::Private::LodDataStructImpl669     LodDataStructImpl(Data *_lodData) : lodData(_lodData) {}
670     QScopedPointer<Data> lodData;
671 };
672 
regionForLodSyncing() const673 KisRegion KisPaintDevice::Private::regionForLodSyncing() const
674 {
675     Data *srcData = currentNonLodData();
676     return srcData->dataManager()->region().translated(srcData->x(), srcData->y());
677 }
678 
createLodDataStruct(int newLod)679 KisPaintDevice::LodDataStruct* KisPaintDevice::Private::createLodDataStruct(int newLod)
680 {
681     KIS_SAFE_ASSERT_RECOVER_NOOP(newLod > 0);
682 
683     Data *srcData = currentNonLodData();
684 
685     Data *lodData = new Data(q, srcData, false);
686     LodDataStruct *lodStruct = new LodDataStructImpl(lodData);
687 
688     int expectedX = KisLodTransform::coordToLodCoord(srcData->x(), newLod);
689     int expectedY = KisLodTransform::coordToLodCoord(srcData->y(), newLod);
690 
691     /**
692      * We compare color spaces as pure pointers, because they must be
693      * exactly the same, since they come from the common source.
694      */
695     if (lodData->levelOfDetail() != newLod ||
696         lodData->colorSpace() != srcData->colorSpace() ||
697         lodData->x() != expectedX ||
698         lodData->y() != expectedY) {
699 
700 
701         lodData->prepareClone(srcData);
702 
703         lodData->setLevelOfDetail(newLod);
704         lodData->setX(expectedX);
705         lodData->setY(expectedY);
706 
707         // FIXME: different kind of synchronization
708     }
709 
710     lodData->cache()->invalidate();
711 
712     return lodStruct;
713 }
714 
updateLodDataManager(KisDataManager * srcDataManager,KisDataManager * dstDataManager,const QPoint & srcOffset,const QPoint & dstOffset,const QRect & originalRect,int lod)715 void KisPaintDevice::Private::updateLodDataManager(KisDataManager *srcDataManager,
716                                                    KisDataManager *dstDataManager,
717                                                    const QPoint &srcOffset,
718                                                    const QPoint &dstOffset,
719                                                    const QRect &originalRect,
720                                                    int lod)
721 {
722     if (originalRect.isEmpty()) return;
723 
724     const int srcStepSize = 1 << lod;
725 
726     KIS_ASSERT_RECOVER_RETURN(lod > 0);
727 
728     const QRect srcRect = KisLodTransform::alignedRect(originalRect, lod);
729     const QRect dstRect = KisLodTransform::scaledRect(srcRect, lod);
730     if (!srcRect.isValid() || !dstRect.isValid()) return;
731 
732     KIS_ASSERT_RECOVER_NOOP(srcRect.width() / srcStepSize == dstRect.width());
733 
734     const int pixelSize = srcDataManager->pixelSize();
735 
736     int rowsAccumulated = 0;
737     int columnsAccumulated = 0;
738 
739     KoMixColorsOp *mixOp = colorSpace()->mixColorsOp();
740 
741     QScopedArrayPointer<quint8> blendData(new quint8[srcStepSize * srcRect.width() * pixelSize]);
742     quint8 *blendDataPtr = blendData.data();
743     int blendDataOffset = 0;
744 
745     const int srcCellSize = srcStepSize * srcStepSize;
746     const int srcCellStride = srcCellSize * pixelSize;
747     const int srcStepStride = srcStepSize * pixelSize;
748     const int srcColumnStride = (srcStepSize - 1) * srcStepStride;
749 
750     QScopedArrayPointer<qint16> weights(new qint16[srcCellSize]);
751 
752     {
753         const qint16 averageWeight = qCeil(255.0 / srcCellSize);
754         const qint16 extraWeight = averageWeight * srcCellSize - 255;
755         KIS_ASSERT_RECOVER_NOOP(extraWeight == 1);
756 
757         for (int i = 0; i < srcCellSize - 1; i++) {
758             weights[i] = averageWeight;
759         }
760         weights[srcCellSize - 1] = averageWeight - extraWeight;
761     }
762 
763     InternalSequentialConstIterator srcIntIt(StrategyPolicy(currentStrategy(), srcDataManager, srcOffset.x(), srcOffset.y()), srcRect);
764     InternalSequentialIterator dstIntIt(StrategyPolicy(currentStrategy(), dstDataManager, dstOffset.x(), dstOffset.y()), dstRect);
765 
766     int rowsRemaining = srcRect.height();
767     while (rowsRemaining > 0) {
768 
769         int colsRemaining = srcRect.width();
770         while (colsRemaining > 0 && srcIntIt.nextPixel()) {
771 
772             memcpy(blendDataPtr, srcIntIt.rawDataConst(), pixelSize);
773             blendDataPtr += pixelSize;
774             columnsAccumulated++;
775 
776             if (columnsAccumulated >= srcStepSize) {
777                 blendDataPtr += srcColumnStride;
778                 columnsAccumulated = 0;
779             }
780 
781             colsRemaining--;
782         }
783 
784         rowsAccumulated++;
785 
786         if (rowsAccumulated >= srcStepSize) {
787 
788             // blend and write the final data
789             blendDataPtr = blendData.data();
790 
791             int colsRemaining = dstRect.width();
792             while (colsRemaining > 0 && dstIntIt.nextPixel()) {
793                 mixOp->mixColors(blendDataPtr, weights.data(), srcCellSize, dstIntIt.rawData());
794                 blendDataPtr += srcCellStride;
795 
796                 colsRemaining--;
797             }
798 
799             // reset counters
800             rowsAccumulated = 0;
801             blendDataPtr = blendData.data();
802             blendDataOffset = 0;
803         } else {
804             blendDataOffset += srcStepStride;
805             blendDataPtr = blendData.data() + blendDataOffset;
806         }
807 
808         rowsRemaining--;
809     }
810 }
811 
updateLodDataStruct(LodDataStruct * _dst,const QRect & originalRect)812 void KisPaintDevice::Private::updateLodDataStruct(LodDataStruct *_dst, const QRect &originalRect)
813 {
814     LodDataStructImpl *dst = dynamic_cast<LodDataStructImpl*>(_dst);
815     KIS_SAFE_ASSERT_RECOVER_RETURN(dst);
816 
817     Data *lodData = dst->lodData.data();
818     Data *srcData = currentNonLodData();
819 
820     const int lod = lodData->levelOfDetail();
821 
822     updateLodDataManager(srcData->dataManager().data(), lodData->dataManager().data(),
823                          QPoint(srcData->x(), srcData->y()),
824                          QPoint(lodData->x(), lodData->y()),
825                          originalRect, lod);
826 }
827 
generateLodCloneDevice(KisPaintDeviceSP dst,const QRect & originalRect,int lod)828 void KisPaintDevice::Private::generateLodCloneDevice(KisPaintDeviceSP dst, const QRect &originalRect, int lod)
829 {
830     KIS_SAFE_ASSERT_RECOVER_RETURN(fastBitBltPossible(dst));
831 
832     Data *srcData = currentNonLodData();
833     updateLodDataManager(srcData->dataManager().data(), dst->dataManager().data(),
834                          QPoint(srcData->x(), srcData->y()),
835                          QPoint(dst->x(), dst->y()),
836                          originalRect, lod);
837 }
838 
uploadLodDataStruct(LodDataStruct * _dst)839 void KisPaintDevice::Private::uploadLodDataStruct(LodDataStruct *_dst)
840 {
841     LodDataStructImpl *dst = dynamic_cast<LodDataStructImpl*>(_dst);
842     KIS_SAFE_ASSERT_RECOVER_RETURN(dst);
843 
844     KIS_SAFE_ASSERT_RECOVER_RETURN(
845         dst->lodData->levelOfDetail() == defaultBounds->currentLevelOfDetail());
846 
847     ensureLodDataPresent();
848 
849     m_lodData->prepareClone(dst->lodData.data());
850     m_lodData->dataManager()->bitBltRough(dst->lodData->dataManager(), dst->lodData->dataManager()->extent());
851 }
852 
transferFromData(Data * data,KisPaintDeviceSP targetDevice)853 void KisPaintDevice::Private::transferFromData(Data *data, KisPaintDeviceSP targetDevice)
854 {
855     QRect extent = data->dataManager()->extent();
856     extent.translate(data->x(), data->y());
857 
858     targetDevice->m_d->prepareCloneImpl(q, data);
859     targetDevice->m_d->currentStrategy()->fastBitBltRough(data->dataManager(), extent);
860 }
861 
fetchFrame(int frameId,KisPaintDeviceSP targetDevice)862 void KisPaintDevice::Private::fetchFrame(int frameId, KisPaintDeviceSP targetDevice)
863 {
864     DataSP data = m_frames[frameId];
865     transferFromData(data.data(), targetDevice);
866 }
867 
uploadFrame(int srcFrameId,int dstFrameId,KisPaintDeviceSP srcDevice)868 void KisPaintDevice::Private::uploadFrame(int srcFrameId, int dstFrameId, KisPaintDeviceSP srcDevice)
869 {
870     DataSP dstData = m_frames[dstFrameId];
871     KIS_ASSERT_RECOVER_RETURN(dstData);
872 
873     DataSP srcData = srcDevice->m_d->m_frames[srcFrameId];
874     KIS_ASSERT_RECOVER_RETURN(srcData);
875 
876     uploadFrameData(srcData, dstData);
877 }
878 
uploadFrame(int dstFrameId,KisPaintDeviceSP srcDevice)879 void KisPaintDevice::Private::uploadFrame(int dstFrameId, KisPaintDeviceSP srcDevice)
880 {
881     DataSP dstData = m_frames[dstFrameId];
882     KIS_ASSERT_RECOVER_RETURN(dstData);
883 
884     DataSP srcData = srcDevice->m_d->m_data;
885     KIS_ASSERT_RECOVER_RETURN(srcData);
886 
887     uploadFrameData(srcData, dstData);
888 }
889 
uploadFrameData(DataSP srcData,DataSP dstData)890 void KisPaintDevice::Private::uploadFrameData(DataSP srcData, DataSP dstData)
891 {
892     if (srcData->colorSpace() != dstData->colorSpace() &&
893         *srcData->colorSpace() != *dstData->colorSpace()) {
894 
895         KUndo2Command tempCommand;
896 
897         srcData = toQShared(new Data(q, srcData.data(), true));
898         srcData->convertDataColorSpace(dstData->colorSpace(),
899                                        KoColorConversionTransformation::internalRenderingIntent(),
900                                        KoColorConversionTransformation::internalConversionFlags(),
901                                        &tempCommand);
902     }
903 
904     /* If the destination data doesn't share a default pixel value
905      * with src, we should make sure that the default pixel is set
906      * properly before clearing and writing contents.
907      */
908     const int defaultPixelcmp =
909             memcmp(srcData->dataManager()->defaultPixel(),
910                    dstData->dataManager()->defaultPixel(),
911                    dstData->dataManager()->pixelSize());
912     if (defaultPixelcmp != 0) {
913         dstData->dataManager()->setDefaultPixel(srcData->dataManager()->defaultPixel());
914     }
915 
916     dstData->dataManager()->clear();
917     dstData->cache()->invalidate();
918 
919     const QRect rect = srcData->dataManager()->extent();
920     dstData->dataManager()->bitBltRough(srcData->dataManager(), rect);
921     dstData->setX(srcData->x());
922     dstData->setY(srcData->y());
923 }
924 
tesingFetchLodDevice(KisPaintDeviceSP targetDevice)925 void KisPaintDevice::Private::tesingFetchLodDevice(KisPaintDeviceSP targetDevice)
926 {
927     Data *data = m_lodData.data();
928     Q_ASSERT(data);
929 
930     transferFromData(data, targetDevice);
931 }
932 
933 class KisPaintDevice::Private::DeviceChangeProfileCommand : public KUndo2Command
934 {
935 public:
DeviceChangeProfileCommand(KisPaintDeviceSP device,KUndo2Command * parent=0)936     DeviceChangeProfileCommand(KisPaintDeviceSP device, KUndo2Command *parent = 0)
937         : KUndo2Command(parent),
938           m_device(device)
939     {
940     }
941 
emitNotifications()942     virtual void emitNotifications()
943     {
944         m_device->emitProfileChanged();
945     }
946 
redo()947     void redo() override
948     {
949         if (m_firstRun) {
950             m_firstRun = false;
951             return;
952         }
953 
954         KUndo2Command::redo();
955         emitNotifications();
956     }
957 
undo()958     void undo() override
959     {
960         KUndo2Command::undo();
961         emitNotifications();
962     }
963 
964 protected:
965     KisPaintDeviceSP m_device;
966 
967 private:
968     bool m_firstRun {true};
969 };
970 
971 class KisPaintDevice::Private::DeviceChangeColorSpaceCommand : public DeviceChangeProfileCommand
972 {
973 public:
DeviceChangeColorSpaceCommand(KisPaintDeviceSP device,KUndo2Command * parent=0)974     DeviceChangeColorSpaceCommand(KisPaintDeviceSP device, KUndo2Command *parent = 0)
975         : DeviceChangeProfileCommand(device, parent)
976     {
977     }
978 
emitNotifications()979     void emitNotifications() override
980     {
981         m_device->emitColorSpaceChanged();
982     }
983 };
984 
convertColorSpace(const KoColorSpace * dstColorSpace,KoColorConversionTransformation::Intent renderingIntent,KoColorConversionTransformation::ConversionFlags conversionFlags,KUndo2Command * parentCommand)985 void KisPaintDevice::Private::convertColorSpace(const KoColorSpace * dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags, KUndo2Command *parentCommand)
986 {
987     QList<Data*> dataObjects = allDataObjects();
988     if (dataObjects.isEmpty()) return;
989 
990     KUndo2Command *mainCommand =
991         parentCommand ? new DeviceChangeColorSpaceCommand(q, parentCommand) : 0;
992 
993     Q_FOREACH (Data *data, dataObjects) {
994         if (!data) continue;
995 
996         data->convertDataColorSpace(dstColorSpace, renderingIntent, conversionFlags, mainCommand);
997     }
998 
999     q->emitColorSpaceChanged();
1000 }
1001 
assignProfile(const KoColorProfile * profile,KUndo2Command * parentCommand)1002 bool KisPaintDevice::Private::assignProfile(const KoColorProfile * profile, KUndo2Command *parentCommand)
1003 {
1004     if (!profile) return false;
1005 
1006     const KoColorSpace *dstColorSpace =
1007         KoColorSpaceRegistry::instance()->colorSpace(colorSpace()->colorModelId().id(), colorSpace()->colorDepthId().id(), profile);
1008     if (!dstColorSpace) return false;
1009 
1010     KUndo2Command *mainCommand =
1011         parentCommand ? new DeviceChangeColorSpaceCommand(q, parentCommand) : 0;
1012 
1013 
1014     QList<Data*> dataObjects = allDataObjects();
1015     Q_FOREACH (Data *data, dataObjects) {
1016         if (!data) continue;
1017         data->assignColorSpace(dstColorSpace, mainCommand);
1018     }
1019     q->emitProfileChanged();
1020 
1021     // no undo information is provided here
1022     return true;
1023 }
1024 
reincarnateWithDetachedHistory(bool copyContent)1025 KUndo2Command *KisPaintDevice::Private::reincarnateWithDetachedHistory(bool copyContent)
1026 {
1027     KUndo2Command *mainCommand = new KUndo2Command();
1028     currentData()->reincarnateWithDetachedHistory(copyContent, mainCommand);
1029     return mainCommand;
1030 }
1031 
init(const KoColorSpace * cs,const quint8 * defaultPixel)1032 void KisPaintDevice::Private::init(const KoColorSpace *cs, const quint8 *defaultPixel)
1033 {
1034     QList<Data*> dataObjects = allDataObjects();
1035     Q_FOREACH (Data *data, dataObjects) {
1036         if (!data) continue;
1037 
1038         KisDataManagerSP dataManager = new KisDataManager(cs->pixelSize(), defaultPixel);
1039         data->init(cs, dataManager);
1040     }
1041 }
1042 
KisPaintDevice(const KoColorSpace * colorSpace,const QString & name)1043 KisPaintDevice::KisPaintDevice(const KoColorSpace * colorSpace, const QString& name)
1044     : QObject(0)
1045     , m_d(new Private(this))
1046 {
1047     init(colorSpace, new KisDefaultBounds(), 0, name);
1048 }
1049 
KisPaintDevice(KisNodeWSP parent,const KoColorSpace * colorSpace,KisDefaultBoundsBaseSP defaultBounds,const QString & name)1050 KisPaintDevice::KisPaintDevice(KisNodeWSP parent, const KoColorSpace * colorSpace, KisDefaultBoundsBaseSP defaultBounds, const QString& name)
1051     : QObject(0)
1052     , m_d(new Private(this))
1053 {
1054     init(colorSpace, defaultBounds, parent, name);
1055 }
1056 
init(const KoColorSpace * colorSpace,KisDefaultBoundsBaseSP defaultBounds,KisNodeWSP parent,const QString & name)1057 void KisPaintDevice::init(const KoColorSpace *colorSpace,
1058                           KisDefaultBoundsBaseSP defaultBounds,
1059                           KisNodeWSP parent, const QString& name)
1060 {
1061     Q_ASSERT(colorSpace);
1062     setObjectName(name);
1063 
1064     // temporary def. bounds object for the initialization phase only
1065     m_d->defaultBounds = m_d->transitionalDefaultBounds;
1066 
1067     if (!defaultBounds) {
1068         // Reuse transitionalDefaultBounds here. Change if you change
1069         // semantics of transitionalDefaultBounds
1070         defaultBounds = m_d->transitionalDefaultBounds;
1071     }
1072 
1073     QScopedArrayPointer<quint8> defaultPixel(new quint8[colorSpace->pixelSize()]);
1074     colorSpace->fromQColor(Qt::transparent, defaultPixel.data());
1075     m_d->init(colorSpace, defaultPixel.data());
1076 
1077     Q_ASSERT(m_d->colorSpace());
1078 
1079     setDefaultBounds(defaultBounds);
1080     setParentNode(parent);
1081 }
1082 
KisPaintDevice(const KisPaintDevice & rhs,KritaUtils::DeviceCopyMode copyMode,KisNode * newParentNode)1083 KisPaintDevice::KisPaintDevice(const KisPaintDevice& rhs, KritaUtils::DeviceCopyMode copyMode, KisNode *newParentNode)
1084     : QObject()
1085     , KisShared()
1086     , m_d(new Private(this))
1087 {
1088     if (this != &rhs) {
1089         makeFullCopyFrom(rhs, copyMode, newParentNode);
1090     }
1091 }
1092 
makeFullCopyFrom(const KisPaintDevice & rhs,KritaUtils::DeviceCopyMode copyMode,KisNode * newParentNode)1093 void KisPaintDevice::makeFullCopyFrom(const KisPaintDevice &rhs, KritaUtils::DeviceCopyMode copyMode, KisNode *newParentNode)
1094 {
1095     // temporary def. bounds object for the initialization phase only
1096     m_d->defaultBounds = m_d->transitionalDefaultBounds;
1097 
1098     // copy data objects with or without frames
1099     m_d->cloneAllDataObjects(rhs.m_d, copyMode == KritaUtils::CopyAllFrames);
1100 
1101     if (copyMode == KritaUtils::CopyAllFrames && rhs.m_d->framesInterface) {
1102         KIS_ASSERT_RECOVER_RETURN(rhs.m_d->framesInterface);
1103         KIS_ASSERT_RECOVER_RETURN(rhs.m_d->contentChannel);
1104         m_d->framesInterface.reset(new KisPaintDeviceFramesInterface(this));
1105         m_d->contentChannel.reset(new KisRasterKeyframeChannel(*rhs.m_d->contentChannel.data(), newParentNode, this));
1106     }
1107 
1108     setDefaultBounds(rhs.m_d->defaultBounds);
1109     setParentNode(newParentNode);
1110 }
1111 
~KisPaintDevice()1112 KisPaintDevice::~KisPaintDevice()
1113 {
1114     delete m_d;
1115 }
1116 
setProjectionDevice(bool value)1117 void KisPaintDevice::setProjectionDevice(bool value)
1118 {
1119     m_d->isProjectionDevice = value;
1120 }
1121 
prepareClone(KisPaintDeviceSP src)1122 void KisPaintDevice::prepareClone(KisPaintDeviceSP src)
1123 {
1124     m_d->prepareClone(src);
1125 }
1126 
makeCloneFrom(KisPaintDeviceSP src,const QRect & rect)1127 void KisPaintDevice::makeCloneFrom(KisPaintDeviceSP src, const QRect &rect)
1128 {
1129     prepareClone(src);
1130 
1131     // we guarantee that *this is totally empty, so copy pixels that
1132     // are areally present on the source image only
1133     const QRect optimizedRect = rect & src->extent();
1134 
1135     fastBitBlt(src, optimizedRect);
1136 }
1137 
makeCloneFromRough(KisPaintDeviceSP src,const QRect & minimalRect)1138 void KisPaintDevice::makeCloneFromRough(KisPaintDeviceSP src, const QRect &minimalRect)
1139 {
1140     prepareClone(src);
1141 
1142     // we guarantee that *this is totally empty, so copy pixels that
1143     // are areally present on the source image only
1144     const QRect optimizedRect = minimalRect & src->extent();
1145 
1146     fastBitBltRough(src, optimizedRect);
1147 }
1148 
setDirty()1149 void KisPaintDevice::setDirty()
1150 {
1151     m_d->cache()->invalidate();
1152     if (m_d->parent.isValid())
1153         m_d->parent->setDirty();
1154 }
1155 
setDirty(const QRect & rc)1156 void KisPaintDevice::setDirty(const QRect & rc)
1157 {
1158     m_d->cache()->invalidate();
1159     if (m_d->parent.isValid())
1160         m_d->parent->setDirty(rc);
1161 }
1162 
setDirty(const KisRegion & region)1163 void KisPaintDevice::setDirty(const KisRegion &region)
1164 {
1165     m_d->cache()->invalidate();
1166     if (m_d->parent.isValid())
1167         m_d->parent->setDirty(region);
1168 }
1169 
setDirty(const QVector<QRect> & rects)1170 void KisPaintDevice::setDirty(const QVector<QRect> &rects)
1171 {
1172     m_d->cache()->invalidate();
1173     if (m_d->parent.isValid())
1174         m_d->parent->setDirty(rects);
1175 }
1176 
requestTimeSwitch(int time)1177 void KisPaintDevice::requestTimeSwitch(int time)
1178 {
1179     if (m_d->parent.isValid()) {
1180         m_d->parent->requestTimeSwitch(time);
1181     }
1182 }
1183 
sequenceNumber() const1184 int KisPaintDevice::sequenceNumber() const
1185 {
1186     return m_d->cache()->sequenceNumber();
1187 }
1188 
estimateMemoryStats(qint64 & imageData,qint64 & temporaryData,qint64 & lodData) const1189 void KisPaintDevice::estimateMemoryStats(qint64 &imageData, qint64 &temporaryData, qint64 &lodData) const
1190 {
1191     m_d->estimateMemoryStats(imageData, temporaryData, lodData);
1192 }
1193 
setParentNode(KisNodeWSP parent)1194 void KisPaintDevice::setParentNode(KisNodeWSP parent)
1195 {
1196     m_d->parent = parent;
1197 }
1198 
1199 // for testing purposes only
parentNode() const1200 KisNodeWSP KisPaintDevice::parentNode() const
1201 {
1202     return m_d->parent;
1203 }
1204 
setDefaultBounds(KisDefaultBoundsBaseSP defaultBounds)1205 void KisPaintDevice::setDefaultBounds(KisDefaultBoundsBaseSP defaultBounds)
1206 {
1207     m_d->defaultBounds = defaultBounds;
1208     m_d->cache()->invalidate();
1209 }
1210 
defaultBounds() const1211 KisDefaultBoundsBaseSP KisPaintDevice::defaultBounds() const
1212 {
1213     return m_d->defaultBounds;
1214 }
1215 
moveTo(const QPoint & pt)1216 void KisPaintDevice::moveTo(const QPoint &pt)
1217 {
1218     m_d->currentStrategy()->move(pt);
1219     m_d->cache()->invalidate();
1220 }
1221 
offset() const1222 QPoint KisPaintDevice::offset() const
1223 {
1224     return QPoint(x(), y());
1225 }
1226 
moveTo(qint32 x,qint32 y)1227 void KisPaintDevice::moveTo(qint32 x, qint32 y)
1228 {
1229     moveTo(QPoint(x, y));
1230 }
1231 
setX(qint32 x)1232 void KisPaintDevice::setX(qint32 x)
1233 {
1234     moveTo(QPoint(x, m_d->y()));
1235 }
1236 
setY(qint32 y)1237 void KisPaintDevice::setY(qint32 y)
1238 {
1239     moveTo(QPoint(m_d->x(), y));
1240 }
1241 
x() const1242 qint32 KisPaintDevice::x() const
1243 {
1244     return m_d->x();
1245 }
1246 
y() const1247 qint32 KisPaintDevice::y() const
1248 {
1249     return m_d->y();
1250 }
1251 
extent(qint32 & x,qint32 & y,qint32 & w,qint32 & h) const1252 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 
extent() const1261 QRect KisPaintDevice::extent() const
1262 {
1263     return m_d->currentStrategy()->extent();
1264 }
1265 
region() const1266 KisRegion KisPaintDevice::region() const
1267 {
1268     return m_d->currentStrategy()->region();
1269 }
1270 
nonDefaultPixelArea() const1271 QRect KisPaintDevice::nonDefaultPixelArea() const
1272 {
1273     return m_d->cache()->nonDefaultPixelArea();
1274 }
1275 
exactBounds() const1276 QRect KisPaintDevice::exactBounds() const
1277 {
1278     return m_d->cache()->exactBounds();
1279 }
1280 
exactBoundsAmortized() const1281 QRect KisPaintDevice::exactBoundsAmortized() const
1282 {
1283     return m_d->cache()->exactBoundsAmortized();
1284 }
1285 
1286 namespace Impl
1287 {
1288 
1289 struct CheckFullyTransparent {
CheckFullyTransparentImpl::CheckFullyTransparent1290     CheckFullyTransparent(const KoColorSpace *colorSpace)
1291         : m_colorSpace(colorSpace)
1292     {
1293     }
1294 
isPixelEmptyImpl::CheckFullyTransparent1295     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 {
CheckNonDefaultImpl::CheckNonDefault1305     CheckNonDefault(int pixelSize, const quint8 *defaultPixel)
1306         : m_pixelSize(pixelSize),
1307           m_defaultPixel(defaultPixel)
1308     {
1309     }
1310 
isPixelEmptyImpl::CheckNonDefault1311     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>
calculateExactBoundsImpl(const KisPaintDevice * device,const QRect & startRect,const QRect & endRect,ComparePixelOp compareOp)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 
calculateExactBounds(bool nonDefaultOnly) const1434 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 
regionExact() const1467 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 
crop(qint32 x,qint32 y,qint32 w,qint32 h)1491 void KisPaintDevice::crop(qint32 x, qint32 y, qint32 w, qint32 h)
1492 {
1493     crop(QRect(x, y, w, h));
1494 }
1495 
1496 
crop(const QRect & rect)1497 void KisPaintDevice::crop(const QRect &rect)
1498 {
1499     m_d->currentStrategy()->crop(rect);
1500 }
1501 
purgeDefaultPixels()1502 void KisPaintDevice::purgeDefaultPixels()
1503 {
1504     KisDataManagerSP dm = m_d->dataManager();
1505     dm->purge(dm->extent());
1506 }
1507 
setDefaultPixel(const KoColor & defPixel)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 
defaultPixel() const1517 KoColor KisPaintDevice::defaultPixel() const
1518 {
1519     return KoColor(m_d->dataManager()->defaultPixel(), colorSpace());
1520 }
1521 
clear()1522 void KisPaintDevice::clear()
1523 {
1524     m_d->dataManager()->clear();
1525     m_d->cache()->invalidate();
1526 }
1527 
clear(const QRect & rc)1528 void KisPaintDevice::clear(const QRect & rc)
1529 {
1530     m_d->currentStrategy()->clear(rc);
1531 }
1532 
fill(const QRect & rc,const KoColor & color)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 
fill(qint32 x,qint32 y,qint32 w,qint32 h,const quint8 * fillPixel)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 
write(KisPaintDeviceWriter & store)1545 bool KisPaintDevice::write(KisPaintDeviceWriter &store)
1546 {
1547     return m_d->dataManager()->write(store);
1548 }
1549 
read(QIODevice * stream)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 
emitColorSpaceChanged()1560 void KisPaintDevice::emitColorSpaceChanged()
1561 {
1562     emit colorSpaceChanged(m_d->colorSpace());
1563 }
1564 
emitProfileChanged()1565 void KisPaintDevice::emitProfileChanged()
1566 {
1567     emit profileChanged(m_d->colorSpace()->profile());
1568 }
1569 
convertTo(const KoColorSpace * dstColorSpace,KoColorConversionTransformation::Intent renderingIntent,KoColorConversionTransformation::ConversionFlags conversionFlags,KUndo2Command * parentCommand)1570 void KisPaintDevice::convertTo(const KoColorSpace * dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags, KUndo2Command *parentCommand)
1571 {
1572     m_d->convertColorSpace(dstColorSpace, renderingIntent, conversionFlags, parentCommand);
1573 }
1574 
setProfile(const KoColorProfile * profile,KUndo2Command * parentCommand)1575 bool KisPaintDevice::setProfile(const KoColorProfile * profile, KUndo2Command *parentCommand)
1576 {
1577     return m_d->assignProfile(profile, parentCommand);
1578 }
1579 
reincarnateWithDetachedHistory(bool copyContent)1580 KUndo2Command *KisPaintDevice::reincarnateWithDetachedHistory(bool copyContent)
1581 {
1582     return m_d->reincarnateWithDetachedHistory(copyContent);
1583 }
1584 
dataManager() const1585 KisDataManagerSP KisPaintDevice::dataManager() const
1586 {
1587     return m_d->dataManager();
1588 }
1589 
convertFromQImage(const QImage & _image,const KoColorProfile * profile,qint32 offsetX,qint32 offsetY)1590 void KisPaintDevice::convertFromQImage(const QImage& _image, const KoColorProfile *profile,
1591                                        qint32 offsetX, qint32 offsetY)
1592 {
1593     QImage image = _image;
1594 
1595     if (image.format() != QImage::Format_ARGB32) {
1596         image = image.convertToFormat(QImage::Format_ARGB32);
1597     }
1598     // Don't convert if not no profile is given and both paint dev and qimage are rgba.
1599     if (!profile && colorSpace()->id() == "RGBA") {
1600         writeBytes(image.constBits(), offsetX, offsetY, image.width(), image.height());
1601     } else {
1602         try {
1603             quint8 * dstData = new quint8[image.width() * image.height() * pixelSize()];
1604             KoColorSpaceRegistry::instance()
1605             ->colorSpace(RGBAColorModelID.id(), Integer8BitsColorDepthID.id(), profile)
1606             ->convertPixelsTo(image.constBits(), dstData, colorSpace(), image.width() * image.height(),
1607                               KoColorConversionTransformation::internalRenderingIntent(),
1608                               KoColorConversionTransformation::internalConversionFlags());
1609 
1610             writeBytes(dstData, offsetX, offsetY, image.width(), image.height());
1611             delete[] dstData;
1612         } catch (const std::bad_alloc&) {
1613             warnKrita << "KisPaintDevice::convertFromQImage: Could not allocate" << image.width() * image.height() * pixelSize() << "bytes";
1614             return;
1615         }
1616     }
1617     m_d->cache()->invalidate();
1618 }
1619 
convertToQImage(const KoColorProfile * dstProfile,KoColorConversionTransformation::Intent renderingIntent,KoColorConversionTransformation::ConversionFlags conversionFlags) const1620 QImage KisPaintDevice::convertToQImage(const KoColorProfile *dstProfile, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const
1621 {
1622     qint32 x1;
1623     qint32 y1;
1624     qint32 w;
1625     qint32 h;
1626 
1627     QRect rc = exactBounds();
1628     x1 = rc.x();
1629     y1 = rc.y();
1630     w = rc.width();
1631     h = rc.height();
1632 
1633     return convertToQImage(dstProfile, x1, y1, w, h, renderingIntent, conversionFlags);
1634 }
1635 
convertToQImage(const KoColorProfile * dstProfile,const QRect & rc,KoColorConversionTransformation::Intent renderingIntent,KoColorConversionTransformation::ConversionFlags conversionFlags) const1636 QImage KisPaintDevice::convertToQImage(const KoColorProfile *dstProfile,
1637                                        const QRect &rc,
1638                                        KoColorConversionTransformation::Intent renderingIntent,
1639                                        KoColorConversionTransformation::ConversionFlags conversionFlags) const
1640 {
1641     return convertToQImage(dstProfile,
1642                            rc.x(), rc.y(), rc.width(), rc.height(),
1643                            renderingIntent, conversionFlags);
1644 }
1645 
convertToQImage(const KoColorProfile * dstProfile,qint32 x1,qint32 y1,qint32 w,qint32 h,KoColorConversionTransformation::Intent renderingIntent,KoColorConversionTransformation::ConversionFlags conversionFlags) const1646 QImage KisPaintDevice::convertToQImage(const KoColorProfile *dstProfile, qint32 x1, qint32 y1, qint32 w, qint32 h, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const
1647 {
1648 
1649     if (w < 0)
1650         return QImage();
1651 
1652     if (h < 0)
1653         return QImage();
1654 
1655     quint8 *data = 0;
1656     try {
1657         data = new quint8 [w * h * pixelSize()];
1658     } catch (const std::bad_alloc&) {
1659         warnKrita << "KisPaintDevice::convertToQImage std::bad_alloc for " << w << " * " << h << " * " << pixelSize();
1660         //delete[] data; // data is not allocated, so don't free it
1661         return QImage();
1662     }
1663     Q_CHECK_PTR(data);
1664 
1665     // XXX: Is this really faster than converting line by line and building the QImage directly?
1666     //      This copies potentially a lot of data.
1667     readBytes(data, x1, y1, w, h);
1668     QImage image = colorSpace()->convertToQImage(data, w, h, dstProfile, renderingIntent, conversionFlags);
1669     delete[] data;
1670 
1671     return image;
1672 }
1673 
moveBy(KisSequentialConstIterator & iter,int numPixels)1674 inline bool moveBy(KisSequentialConstIterator& iter, int numPixels)
1675 {
1676     int pos = 0;
1677     while (pos < numPixels) {
1678         int step = std::min(iter.nConseqPixels(), numPixels - pos);
1679         if (!iter.nextPixels(step))
1680             return false;
1681         pos += step;
1682     }
1683     return true;
1684 }
1685 
createThumbnailDeviceInternal(const KisPaintDevice * srcDev,qint32 srcX0,qint32 srcY0,qint32 srcWidth,qint32 srcHeight,qint32 w,qint32 h,QRect outputRect)1686 static KisPaintDeviceSP createThumbnailDeviceInternal(const KisPaintDevice* srcDev, qint32 srcX0, qint32 srcY0, qint32 srcWidth, qint32 srcHeight, qint32 w, qint32 h, QRect outputRect)
1687 {
1688     KisPaintDeviceSP thumbnail = new KisPaintDevice(srcDev->colorSpace());
1689     qint32 pixelSize = srcDev->pixelSize();
1690 
1691     KisRandomConstAccessorSP srcIter = srcDev->createRandomConstAccessorNG();
1692     KisRandomAccessorSP dstIter = thumbnail->createRandomAccessorNG();
1693 
1694     for (qint32 y = outputRect.y(); y < outputRect.y() + outputRect.height(); ++y) {
1695         qint32 iY = srcY0 + (y * srcHeight) / h;
1696         for (qint32 x = outputRect.x(); x < outputRect.x() + outputRect.width(); ++x) {
1697             qint32 iX = srcX0 + (x * srcWidth) / w;
1698             srcIter->moveTo(iX, iY);
1699             dstIter->moveTo(x,  y);
1700             memcpy(dstIter->rawData(), srcIter->rawDataConst(), pixelSize);
1701         }
1702     }
1703     return thumbnail;
1704 }
1705 
fixThumbnailSize(QSize size)1706 QSize fixThumbnailSize(QSize size)
1707 {
1708     if (!size.width() && size.height()) {
1709         size.setWidth(1);
1710     }
1711 
1712     if (size.width() && !size.height()) {
1713         size.setHeight(1);
1714     }
1715 
1716     return size;
1717 }
1718 
createThumbnailDevice(qint32 w,qint32 h,QRect rect,QRect outputRect) const1719 KisPaintDeviceSP KisPaintDevice::createThumbnailDevice(qint32 w, qint32 h, QRect rect, QRect outputRect) const
1720 {
1721     QSize thumbnailSize(w, h);
1722 
1723     QRect imageRect = rect.isValid() ? rect : extent();
1724 
1725     if ((thumbnailSize.width() > imageRect.width()) || (thumbnailSize.height() > imageRect.height())) {
1726         thumbnailSize.scale(imageRect.size(), Qt::KeepAspectRatio);
1727     }
1728 
1729     thumbnailSize = fixThumbnailSize(thumbnailSize);
1730 
1731     //can't create thumbnail for an empty device, e.g. layer thumbnail for empty image
1732     if (imageRect.isEmpty() || thumbnailSize.isEmpty()) {
1733         return new KisPaintDevice(colorSpace());
1734     }
1735 
1736     int srcWidth, srcHeight;
1737     int srcX0, srcY0;
1738     imageRect.getRect(&srcX0, &srcY0, &srcWidth, &srcHeight);
1739 
1740     if (!outputRect.isValid()) {
1741         outputRect = QRect(0, 0, w, h);
1742     }
1743 
1744     KisPaintDeviceSP thumbnail = createThumbnailDeviceInternal(this, imageRect.x(), imageRect.y(), imageRect.width(), imageRect.height(),
1745                                  thumbnailSize.width(), thumbnailSize.height(), outputRect);
1746 
1747     return thumbnail;
1748 }
1749 
createThumbnailDeviceOversampled(qint32 w,qint32 h,qreal oversample,QRect rect,QRect outputTileRect) const1750 KisPaintDeviceSP KisPaintDevice::createThumbnailDeviceOversampled(qint32 w, qint32 h, qreal oversample, QRect rect,  QRect outputTileRect) const
1751 {
1752     QSize thumbnailSize(w, h);
1753     qreal oversampleAdjusted = qMax(oversample, 1.);
1754     QSize thumbnailOversampledSize = oversampleAdjusted * thumbnailSize;
1755 
1756     QRect outputRect;
1757     QRect imageRect = rect.isValid() ? rect : extent();
1758 
1759     qint32 hstart = thumbnailOversampledSize.height();
1760 
1761     if ((thumbnailOversampledSize.width() > imageRect.width()) || (thumbnailOversampledSize.height() > imageRect.height())) {
1762         thumbnailOversampledSize.scale(imageRect.size(), Qt::KeepAspectRatio);
1763     }
1764 
1765     thumbnailOversampledSize = fixThumbnailSize(thumbnailOversampledSize);
1766 
1767     //can't create thumbnail for an empty device, e.g. layer thumbnail for empty image
1768     if (imageRect.isEmpty() || thumbnailSize.isEmpty() || thumbnailOversampledSize.isEmpty()) {
1769         return new KisPaintDevice(colorSpace());
1770     }
1771 
1772     oversampleAdjusted *= (hstart > 0) ? ((qreal)thumbnailOversampledSize.height() / hstart) : 1.; //readjusting oversample ratio, given that we had to adjust thumbnail size
1773 
1774     outputRect = QRect(0, 0, thumbnailOversampledSize.width(), thumbnailOversampledSize.height());
1775 
1776     if (outputTileRect.isValid()) {
1777         //compensating output rectangle for oversampling
1778         outputTileRect = QRect(oversampleAdjusted * outputTileRect.topLeft(), oversampleAdjusted * outputTileRect.bottomRight());
1779         outputRect = outputRect.intersected(outputTileRect);
1780     }
1781 
1782     KisPaintDeviceSP thumbnail = createThumbnailDeviceInternal(this, imageRect.x(), imageRect.y(), imageRect.width(), imageRect.height(),
1783                                  thumbnailOversampledSize.width(), thumbnailOversampledSize.height(), outputRect);
1784 
1785     if (oversample != 1. && oversampleAdjusted != 1.) {
1786         KoDummyUpdater updater;
1787         KisTransformWorker worker(thumbnail, 1 / oversampleAdjusted, 1 / oversampleAdjusted, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
1788                                   &updater, KisFilterStrategyRegistry::instance()->value("Bilinear"));
1789         worker.run();
1790     }
1791     return thumbnail;
1792 }
1793 
createThumbnail(qint32 w,qint32 h,QRect rect,qreal oversample,KoColorConversionTransformation::Intent renderingIntent,KoColorConversionTransformation::ConversionFlags conversionFlags)1794 QImage KisPaintDevice::createThumbnail(qint32 w, qint32 h, QRect rect, qreal oversample, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags)
1795 {
1796     QSize size = fixThumbnailSize(QSize(w, h));
1797 
1798     KisPaintDeviceSP dev = createThumbnailDeviceOversampled(size.width(), size.height(), oversample, rect);
1799     QImage thumbnail = dev->convertToQImage(KoColorSpaceRegistry::instance()->rgb8()->profile(), 0, 0, w, h, renderingIntent, conversionFlags);
1800     return thumbnail;
1801 }
1802 
createThumbnail(qint32 w,qint32 h,qreal oversample,KoColorConversionTransformation::Intent renderingIntent,KoColorConversionTransformation::ConversionFlags conversionFlags)1803 QImage KisPaintDevice::createThumbnail(qint32 w, qint32 h, qreal oversample, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags)
1804 {
1805     QSize size = fixThumbnailSize(QSize(w, h));
1806 
1807     return m_d->cache()->createThumbnail(size.width(), size.height(), oversample, renderingIntent, conversionFlags);
1808 }
1809 
createThumbnail(qint32 maxw,qint32 maxh,Qt::AspectRatioMode aspectRatioMode,qreal oversample,KoColorConversionTransformation::Intent renderingIntent,KoColorConversionTransformation::ConversionFlags conversionFlags)1810 QImage KisPaintDevice::createThumbnail(qint32 maxw, qint32 maxh,
1811                                        Qt::AspectRatioMode aspectRatioMode,
1812                                        qreal oversample, KoColorConversionTransformation::Intent renderingIntent,
1813                                        KoColorConversionTransformation::ConversionFlags conversionFlags)
1814 {
1815     const QRect deviceExtent = extent();
1816     const QSize thumbnailSize = deviceExtent.size().scaled(maxw, maxh, aspectRatioMode);
1817     return createThumbnail(thumbnailSize.width(), thumbnailSize.height(),
1818                            oversample, renderingIntent, conversionFlags);
1819 }
1820 
createHLineIteratorNG(qint32 x,qint32 y,qint32 w)1821 KisHLineIteratorSP KisPaintDevice::createHLineIteratorNG(qint32 x, qint32 y, qint32 w)
1822 {
1823     m_d->cache()->invalidate();
1824     return m_d->currentStrategy()->createHLineIteratorNG(m_d->dataManager().data(), x, y, w, m_d->x(), m_d->y());
1825 }
1826 
createHLineConstIteratorNG(qint32 x,qint32 y,qint32 w) const1827 KisHLineConstIteratorSP KisPaintDevice::createHLineConstIteratorNG(qint32 x, qint32 y, qint32 w) const
1828 {
1829     return m_d->currentStrategy()->createHLineConstIteratorNG(m_d->dataManager().data(), x, y, w, m_d->x(), m_d->y());
1830 }
1831 
createVLineIteratorNG(qint32 x,qint32 y,qint32 w)1832 KisVLineIteratorSP KisPaintDevice::createVLineIteratorNG(qint32 x, qint32 y, qint32 w)
1833 {
1834     m_d->cache()->invalidate();
1835     return m_d->currentStrategy()->createVLineIteratorNG(x, y, w);
1836 }
1837 
createVLineConstIteratorNG(qint32 x,qint32 y,qint32 w) const1838 KisVLineConstIteratorSP KisPaintDevice::createVLineConstIteratorNG(qint32 x, qint32 y, qint32 w) const
1839 {
1840     return m_d->currentStrategy()->createVLineConstIteratorNG(x, y, w);
1841 }
1842 
createRepeatHLineConstIterator(qint32 x,qint32 y,qint32 w,const QRect & _dataWidth) const1843 KisRepeatHLineConstIteratorSP KisPaintDevice::createRepeatHLineConstIterator(qint32 x, qint32 y, qint32 w, const QRect& _dataWidth) const
1844 {
1845     return new KisRepeatHLineConstIteratorNG(m_d->dataManager().data(), x, y, w, m_d->x(), m_d->y(), _dataWidth, m_d->cacheInvalidator());
1846 }
1847 
createRepeatVLineConstIterator(qint32 x,qint32 y,qint32 h,const QRect & _dataWidth) const1848 KisRepeatVLineConstIteratorSP KisPaintDevice::createRepeatVLineConstIterator(qint32 x, qint32 y, qint32 h, const QRect& _dataWidth) const
1849 {
1850     return new KisRepeatVLineConstIteratorNG(m_d->dataManager().data(), x, y, h, m_d->x(), m_d->y(), _dataWidth, m_d->cacheInvalidator());
1851 }
1852 
createRandomAccessorNG()1853 KisRandomAccessorSP KisPaintDevice::createRandomAccessorNG()
1854 {
1855     m_d->cache()->invalidate();
1856     return m_d->currentStrategy()->createRandomAccessorNG();
1857 }
1858 
createRandomConstAccessorNG() const1859 KisRandomConstAccessorSP KisPaintDevice::createRandomConstAccessorNG() const
1860 {
1861     return m_d->currentStrategy()->createRandomConstAccessorNG();
1862 }
1863 
createRandomSubAccessor() const1864 KisRandomSubAccessorSP KisPaintDevice::createRandomSubAccessor() const
1865 {
1866     KisPaintDevice* pd = const_cast<KisPaintDevice*>(this);
1867     return new KisRandomSubAccessor(pd);
1868 }
1869 
clearSelection(KisSelectionSP selection)1870 void KisPaintDevice::clearSelection(KisSelectionSP selection)
1871 {
1872     const KoColorSpace *colorSpace = m_d->colorSpace();
1873     const QRect r = selection->selectedExactRect();
1874 
1875     if (r.isValid()) {
1876 
1877         {
1878             KisHLineIteratorSP devIt = createHLineIteratorNG(r.x(), r.y(), r.width());
1879             KisHLineConstIteratorSP selectionIt = selection->projection()->createHLineConstIteratorNG(r.x(), r.y(), r.width());
1880 
1881             const KoColor defaultPixel = this->defaultPixel();
1882             bool transparentDefault = (defaultPixel.opacityU8() == OPACITY_TRANSPARENT_U8);
1883             for (qint32 y = 0; y < r.height(); y++) {
1884 
1885                 do {
1886                     // XXX: Optimize by using stretches
1887                     colorSpace->applyInverseAlphaU8Mask(devIt->rawData(), selectionIt->rawDataConst(), 1);
1888                     if (transparentDefault && colorSpace->opacityU8(devIt->rawData()) == OPACITY_TRANSPARENT_U8) {
1889                         memcpy(devIt->rawData(), defaultPixel.data(), colorSpace->pixelSize());
1890                     }
1891                 } while (devIt->nextPixel() && selectionIt->nextPixel());
1892                 devIt->nextRow();
1893                 selectionIt->nextRow();
1894             }
1895         }
1896 
1897         // purge() must be executed **after** all iterators have been destroyed!
1898         m_d->dataManager()->purge(r.translated(-m_d->x(), -m_d->y()));
1899 
1900         setDirty(r);
1901     }
1902 }
1903 
pixel(qint32 x,qint32 y,QColor * c) const1904 bool KisPaintDevice::pixel(qint32 x, qint32 y, QColor *c) const
1905 {
1906     KisHLineConstIteratorSP iter = createHLineConstIteratorNG(x, y, 1);
1907 
1908     const quint8 *pix = iter->rawDataConst();
1909 
1910     if (!pix) return false;
1911 
1912     colorSpace()->toQColor(pix, c);
1913 
1914     return true;
1915 }
1916 
1917 
pixel(qint32 x,qint32 y,KoColor * kc) const1918 bool KisPaintDevice::pixel(qint32 x, qint32 y, KoColor * kc) const
1919 {
1920     KisHLineConstIteratorSP iter = createHLineConstIteratorNG(x, y, 1);
1921 
1922     const quint8 *pix = iter->rawDataConst();
1923 
1924     if (!pix) return false;
1925 
1926     kc->setColor(pix, m_d->colorSpace());
1927 
1928     return true;
1929 }
1930 
pixel(const QPoint & pos) const1931 KoColor KisPaintDevice::pixel(const QPoint &pos) const
1932 {
1933     KisHLineConstIteratorSP iter = createHLineConstIteratorNG(pos.x(), pos.y(), 1);
1934     return KoColor(iter->rawDataConst(), m_d->colorSpace());
1935 }
1936 
setPixel(qint32 x,qint32 y,const QColor & c)1937 bool KisPaintDevice::setPixel(qint32 x, qint32 y, const QColor& c)
1938 {
1939     KisHLineIteratorSP iter = createHLineIteratorNG(x, y, 1);
1940 
1941     colorSpace()->fromQColor(c, iter->rawData());
1942     m_d->cache()->invalidate();
1943     return true;
1944 }
1945 
setPixel(qint32 x,qint32 y,const KoColor & kc)1946 bool KisPaintDevice::setPixel(qint32 x, qint32 y, const KoColor& kc)
1947 {
1948     const quint8 * pix;
1949     KisHLineIteratorSP iter = createHLineIteratorNG(x, y, 1);
1950     if (kc.colorSpace() != m_d->colorSpace()) {
1951         KoColor kc2(kc, m_d->colorSpace());
1952         pix = kc2.data();
1953         memcpy(iter->rawData(), pix, m_d->colorSpace()->pixelSize());
1954     } else {
1955         pix = kc.data();
1956         memcpy(iter->rawData(), pix, m_d->colorSpace()->pixelSize());
1957     }
1958     m_d->cache()->invalidate();
1959     return true;
1960 }
1961 
fastBitBltPossible(KisPaintDeviceSP src)1962 bool KisPaintDevice::fastBitBltPossible(KisPaintDeviceSP src)
1963 {
1964     return m_d->fastBitBltPossible(src);
1965 }
1966 
fastBitBlt(KisPaintDeviceSP src,const QRect & rect)1967 void KisPaintDevice::fastBitBlt(KisPaintDeviceSP src, const QRect &rect)
1968 {
1969     m_d->currentStrategy()->fastBitBlt(src, rect);
1970 }
1971 
fastBitBltOldData(KisPaintDeviceSP src,const QRect & rect)1972 void KisPaintDevice::fastBitBltOldData(KisPaintDeviceSP src, const QRect &rect)
1973 {
1974     m_d->currentStrategy()->fastBitBltOldData(src, rect);
1975 }
1976 
fastBitBltRough(KisPaintDeviceSP src,const QRect & rect)1977 void KisPaintDevice::fastBitBltRough(KisPaintDeviceSP src, const QRect &rect)
1978 {
1979     m_d->currentStrategy()->fastBitBltRough(src, rect);
1980 }
1981 
fastBitBltRoughOldData(KisPaintDeviceSP src,const QRect & rect)1982 void KisPaintDevice::fastBitBltRoughOldData(KisPaintDeviceSP src, const QRect &rect)
1983 {
1984     m_d->currentStrategy()->fastBitBltRoughOldData(src, rect);
1985 }
1986 
readBytes(quint8 * data,qint32 x,qint32 y,qint32 w,qint32 h) const1987 void KisPaintDevice::readBytes(quint8 * data, qint32 x, qint32 y, qint32 w, qint32 h) const
1988 {
1989     readBytes(data, QRect(x, y, w, h));
1990 }
1991 
readBytes(quint8 * data,const QRect & rect) const1992 void KisPaintDevice::readBytes(quint8 *data, const QRect &rect) const
1993 {
1994     m_d->currentStrategy()->readBytes(data, rect);
1995 }
1996 
writeBytes(const quint8 * data,qint32 x,qint32 y,qint32 w,qint32 h)1997 void KisPaintDevice::writeBytes(const quint8 *data, qint32 x, qint32 y, qint32 w, qint32 h)
1998 {
1999     writeBytes(data, QRect(x, y, w, h));
2000 }
2001 
writeBytes(const quint8 * data,const QRect & rect)2002 void KisPaintDevice::writeBytes(const quint8 *data, const QRect &rect)
2003 {
2004     m_d->currentStrategy()->writeBytes(data, rect);
2005 }
2006 
readPlanarBytes(qint32 x,qint32 y,qint32 w,qint32 h) const2007 QVector<quint8*> KisPaintDevice::readPlanarBytes(qint32 x, qint32 y, qint32 w, qint32 h) const
2008 {
2009     return m_d->currentStrategy()->readPlanarBytes(x, y, w, h);
2010 }
2011 
writePlanarBytes(QVector<quint8 * > planes,qint32 x,qint32 y,qint32 w,qint32 h)2012 void KisPaintDevice::writePlanarBytes(QVector<quint8*> planes, qint32 x, qint32 y, qint32 w, qint32 h)
2013 {
2014     m_d->currentStrategy()->writePlanarBytes(planes, x, y, w, h);
2015 }
2016 
2017 
pixelSize() const2018 quint32 KisPaintDevice::pixelSize() const
2019 {
2020     quint32 _pixelSize = m_d->colorSpace()->pixelSize();
2021     Q_ASSERT(_pixelSize > 0);
2022     return _pixelSize;
2023 }
2024 
channelCount() const2025 quint32 KisPaintDevice::channelCount() const
2026 {
2027     quint32 _channelCount = m_d->colorSpace()->channelCount();
2028     Q_ASSERT(_channelCount > 0);
2029     return _channelCount;
2030 }
2031 
createKeyframeChannel(const KoID & id)2032 KisRasterKeyframeChannel *KisPaintDevice::createKeyframeChannel(const KoID &id)
2033 {
2034     Q_ASSERT(!m_d->framesInterface);
2035     m_d->framesInterface.reset(new KisPaintDeviceFramesInterface(this));
2036 
2037     Q_ASSERT(!m_d->contentChannel);
2038     if (m_d->parent.isValid()) {
2039         m_d->contentChannel.reset(new KisRasterKeyframeChannel(id, this, m_d->parent));
2040     } else {
2041         //fallback when paint device is isolated / does not belong to a node.
2042         ENTER_FUNCTION() << ppVar(this) << ppVar(m_d->defaultBounds);
2043         m_d->contentChannel.reset(new KisRasterKeyframeChannel(id, this, m_d->defaultBounds));
2044     }
2045 
2046     // Raster channels always have at least one frame (representing a static image)
2047     KUndo2Command tempParentCommand;
2048     m_d->contentChannel->addKeyframe(0, &tempParentCommand);
2049 
2050     return m_d->contentChannel.data();
2051 }
2052 
keyframeChannel() const2053 KisRasterKeyframeChannel* KisPaintDevice::keyframeChannel() const
2054 {
2055     if (m_d->contentChannel) {
2056         return m_d->contentChannel.data();
2057     }
2058     return 0;
2059 }
2060 
colorSpace() const2061 const KoColorSpace* KisPaintDevice::colorSpace() const
2062 {
2063     Q_ASSERT(m_d->colorSpace() != 0);
2064     return m_d->colorSpace();
2065 }
2066 
createCompositionSourceDevice() const2067 KisPaintDeviceSP KisPaintDevice::createCompositionSourceDevice() const
2068 {
2069     KisPaintDeviceSP device = new KisPaintDevice(compositionSourceColorSpace());
2070     device->setDefaultBounds(defaultBounds());
2071     return device;
2072 }
2073 
createCompositionSourceDevice(KisPaintDeviceSP cloneSource) const2074 KisPaintDeviceSP KisPaintDevice::createCompositionSourceDevice(KisPaintDeviceSP cloneSource) const
2075 {
2076     KisPaintDeviceSP clone = new KisPaintDevice(*cloneSource);
2077     clone->setDefaultBounds(defaultBounds());
2078     clone->convertTo(compositionSourceColorSpace(),
2079                      KoColorConversionTransformation::internalRenderingIntent(),
2080                      KoColorConversionTransformation::internalConversionFlags());
2081     return clone;
2082 }
2083 
createCompositionSourceDevice(KisPaintDeviceSP cloneSource,const QRect roughRect) const2084 KisPaintDeviceSP KisPaintDevice::createCompositionSourceDevice(KisPaintDeviceSP cloneSource, const QRect roughRect) const
2085 {
2086     KisPaintDeviceSP clone = new KisPaintDevice(colorSpace());
2087     clone->setDefaultBounds(defaultBounds());
2088     clone->makeCloneFromRough(cloneSource, roughRect);
2089     clone->convertTo(compositionSourceColorSpace(),
2090                      KoColorConversionTransformation::internalRenderingIntent(),
2091                      KoColorConversionTransformation::internalConversionFlags());
2092     return clone;
2093 }
2094 
createCompositionSourceDeviceFixed() const2095 KisFixedPaintDeviceSP KisPaintDevice::createCompositionSourceDeviceFixed() const
2096 {
2097     return new KisFixedPaintDevice(compositionSourceColorSpace());
2098 }
2099 
compositionSourceColorSpace() const2100 const KoColorSpace* KisPaintDevice::compositionSourceColorSpace() const
2101 {
2102     return colorSpace();
2103 }
2104 
channelSizes() const2105 QVector<qint32> KisPaintDevice::channelSizes() const
2106 {
2107     QVector<qint32> sizes;
2108     QList<KoChannelInfo*> channels = colorSpace()->channels();
2109     std::sort(channels.begin(), channels.end());
2110 
2111     Q_FOREACH (KoChannelInfo * channelInfo, channels) {
2112         sizes.append(channelInfo->size());
2113     }
2114     return sizes;
2115 }
2116 
~MemoryReleaseObject()2117 KisPaintDevice::MemoryReleaseObject::~MemoryReleaseObject()
2118 {
2119     KisDataManager::releaseInternalPools();
2120 }
2121 
createMemoryReleaseObject()2122 KisPaintDevice::MemoryReleaseObject* KisPaintDevice::createMemoryReleaseObject()
2123 {
2124     return new MemoryReleaseObject();
2125 }
2126 
~LodDataStruct()2127 KisPaintDevice::LodDataStruct::~LodDataStruct()
2128 {
2129 }
2130 
regionForLodSyncing() const2131 KisRegion KisPaintDevice::regionForLodSyncing() const
2132 {
2133     return m_d->regionForLodSyncing();
2134 }
2135 
createLodDataStruct(int lod)2136 KisPaintDevice::LodDataStruct* KisPaintDevice::createLodDataStruct(int lod)
2137 {
2138     return m_d->createLodDataStruct(lod);
2139 }
2140 
updateLodDataStruct(LodDataStruct * dst,const QRect & srcRect)2141 void KisPaintDevice::updateLodDataStruct(LodDataStruct *dst, const QRect &srcRect)
2142 {
2143     m_d->updateLodDataStruct(dst, srcRect);
2144 }
2145 
uploadLodDataStruct(LodDataStruct * dst)2146 void KisPaintDevice::uploadLodDataStruct(LodDataStruct *dst)
2147 {
2148     m_d->uploadLodDataStruct(dst);
2149 }
2150 
generateLodCloneDevice(KisPaintDeviceSP dst,const QRect & originalRect,int lod)2151 void KisPaintDevice::generateLodCloneDevice(KisPaintDeviceSP dst, const QRect &originalRect, int lod)
2152 {
2153     m_d->generateLodCloneDevice(dst, originalRect, lod);
2154 }
2155 
2156 
framesInterface()2157 KisPaintDeviceFramesInterface* KisPaintDevice::framesInterface()
2158 {
2159     return m_d->framesInterface.data();
2160 }
2161 
2162 /******************************************************************/
2163 /*               KisPaintDeviceFramesInterface                    */
2164 /******************************************************************/
2165 
KisPaintDeviceFramesInterface(KisPaintDevice * parentDevice)2166 KisPaintDeviceFramesInterface::KisPaintDeviceFramesInterface(KisPaintDevice *parentDevice)
2167     : q(parentDevice)
2168 {
2169 }
2170 
frames()2171 QList<int> KisPaintDeviceFramesInterface::frames()
2172 {
2173     return q->m_d->frameIds();
2174 }
2175 
createFrame(bool copy,int copySrc,const QPoint & offset,KUndo2Command * parentCommand)2176 int KisPaintDeviceFramesInterface::createFrame(bool copy, int copySrc, const QPoint &offset, KUndo2Command *parentCommand)
2177 {
2178     return q->m_d->createFrame(copy, copySrc, offset, parentCommand);
2179 }
2180 
deleteFrame(int frame,KUndo2Command * parentCommand)2181 void KisPaintDeviceFramesInterface::deleteFrame(int frame, KUndo2Command *parentCommand)
2182 {
2183     return q->m_d->deleteFrame(frame, parentCommand);
2184 }
2185 
fetchFrame(int frameId,KisPaintDeviceSP targetDevice)2186 void KisPaintDeviceFramesInterface::fetchFrame(int frameId, KisPaintDeviceSP targetDevice)
2187 {
2188     q->m_d->fetchFrame(frameId, targetDevice);
2189 }
2190 
uploadFrame(int srcFrameId,int dstFrameId,KisPaintDeviceSP srcDevice)2191 void KisPaintDeviceFramesInterface::uploadFrame(int srcFrameId, int dstFrameId, KisPaintDeviceSP srcDevice)
2192 {
2193     q->m_d->uploadFrame(srcFrameId, dstFrameId, srcDevice);
2194 }
2195 
uploadFrame(int dstFrameId,KisPaintDeviceSP srcDevice)2196 void KisPaintDeviceFramesInterface::uploadFrame(int dstFrameId, KisPaintDeviceSP srcDevice)
2197 {
2198     q->m_d->uploadFrame(dstFrameId, srcDevice);
2199 }
2200 
frameBounds(int frameId)2201 QRect KisPaintDeviceFramesInterface::frameBounds(int frameId)
2202 {
2203     return q->m_d->frameBounds(frameId);
2204 }
2205 
frameOffset(int frameId) const2206 QPoint KisPaintDeviceFramesInterface::frameOffset(int frameId) const
2207 {
2208     return q->m_d->frameOffset(frameId);
2209 }
2210 
setFrameDefaultPixel(const KoColor & defPixel,int frameId)2211 void KisPaintDeviceFramesInterface::setFrameDefaultPixel(const KoColor &defPixel, int frameId)
2212 {
2213     KIS_ASSERT_RECOVER_RETURN(frameId >= 0);
2214     q->m_d->setFrameDefaultPixel(defPixel, frameId);
2215 }
2216 
frameDefaultPixel(int frameId) const2217 KoColor KisPaintDeviceFramesInterface::frameDefaultPixel(int frameId) const
2218 {
2219     KIS_ASSERT_RECOVER(frameId >= 0) {
2220         return KoColor(Qt::red, q->m_d->colorSpace());
2221     }
2222     return q->m_d->frameDefaultPixel(frameId);
2223 }
2224 
writeFrame(KisPaintDeviceWriter & store,int frameId)2225 bool KisPaintDeviceFramesInterface::writeFrame(KisPaintDeviceWriter &store, int frameId)
2226 {
2227     KIS_ASSERT_RECOVER(frameId >= 0) {
2228         return false;
2229     }
2230     return q->m_d->writeFrame(store, frameId);
2231 }
2232 
readFrame(QIODevice * stream,int frameId)2233 bool KisPaintDeviceFramesInterface::readFrame(QIODevice *stream, int frameId)
2234 {
2235     KIS_ASSERT_RECOVER(frameId >= 0) {
2236         return false;
2237     }
2238     return q->m_d->readFrame(stream, frameId);
2239 }
2240 
currentFrameId() const2241 int KisPaintDeviceFramesInterface::currentFrameId() const
2242 {
2243     return q->m_d->currentFrameId();
2244 }
2245 
frameDataManager(int frameId) const2246 KisDataManagerSP KisPaintDeviceFramesInterface::frameDataManager(int frameId) const
2247 {
2248     KIS_ASSERT_RECOVER(frameId >= 0) {
2249         return q->m_d->dataManager();
2250     }
2251     return q->m_d->frameDataManager(frameId);
2252 }
2253 
invalidateFrameCache(int frameId)2254 void KisPaintDeviceFramesInterface::invalidateFrameCache(int frameId)
2255 {
2256     KIS_ASSERT_RECOVER_RETURN(frameId >= 0);
2257 
2258     return q->m_d->invalidateFrameCache(frameId);
2259 }
2260 
setFrameOffset(int frameId,const QPoint & offset)2261 void KisPaintDeviceFramesInterface::setFrameOffset(int frameId, const QPoint &offset)
2262 {
2263     KIS_ASSERT_RECOVER_RETURN(frameId >= 0);
2264     return q->m_d->setFrameOffset(frameId, offset);
2265 }
2266 
2267 KisPaintDeviceFramesInterface::TestingDataObjects
testingGetDataObjects() const2268 KisPaintDeviceFramesInterface::testingGetDataObjects() const
2269 {
2270     TestingDataObjects objects;
2271 
2272     objects.m_data = q->m_d->m_data.data();
2273     objects.m_lodData = q->m_d->m_lodData.data();
2274     objects.m_externalFrameData = q->m_d->m_externalFrameData.data();
2275 
2276     typedef KisPaintDevice::Private::FramesHash FramesHash;
2277 
2278     FramesHash::const_iterator it = q->m_d->m_frames.constBegin();
2279     FramesHash::const_iterator end = q->m_d->m_frames.constEnd();
2280 
2281     for (; it != end; ++it) {
2282         objects.m_frames.insert(it.key(), it.value().data());
2283     }
2284 
2285     objects.m_currentData = q->m_d->currentData();
2286 
2287     return objects;
2288 }
2289 
testingGetDataObjectsList() const2290 QList<KisPaintDeviceData*> KisPaintDeviceFramesInterface::testingGetDataObjectsList() const
2291 {
2292     return q->m_d->allDataObjects();
2293 }
2294 
tesingFetchLodDevice(KisPaintDeviceSP targetDevice)2295 void KisPaintDevice::tesingFetchLodDevice(KisPaintDeviceSP targetDevice)
2296 {
2297     m_d->tesingFetchLodDevice(targetDevice);
2298 }
2299