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 ®ion)
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