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