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