File indexing completed on 2024-12-22 04:10:33
0001 /* 0002 * SPDX-FileCopyrightText: 2004 C. Boemann <cbo@boemann.dk> 0003 * SPDX-FileCopyrightText: 2009 Dmitry Kazakov <dimula73@gmail.com> 0004 * SPDX-FileCopyrightText: 2010 Cyrille Berger <cberger@cberger.net> 0005 * 0006 * SPDX-License-Identifier: GPL-2.0-or-later 0007 */ 0008 0009 #include <QRect> 0010 #include <QVector> 0011 0012 #include "kis_tile.h" 0013 #include "kis_tiled_data_manager.h" 0014 #include "kis_tile_data_wrapper.h" 0015 #include "kis_tiled_data_manager_p.h" 0016 #include "kis_memento_manager.h" 0017 #include "swap/kis_legacy_tile_compressor.h" 0018 #include "swap/kis_tile_compressor_factory.h" 0019 0020 #include "kis_paint_device_writer.h" 0021 0022 #include "kis_global.h" 0023 0024 0025 /* The data area is divided into tiles each say 64x64 pixels (defined at compiletime) 0026 * The tiles are laid out in a matrix that can have negative indexes. 0027 * The matrix grows automatically if needed (a call for writeacces to a tile 0028 * outside the current extent) 0029 * Even though the matrix has grown it may still not contain tiles at specific positions. 0030 * They are created on demand 0031 */ 0032 0033 KisTiledDataManager::KisTiledDataManager(quint32 pixelSize, 0034 const quint8 *defaultPixel) 0035 { 0036 /* See comment in destructor for details */ 0037 m_mementoManager = new KisMementoManager(); 0038 m_hashTable = new KisTileHashTable(m_mementoManager); 0039 0040 m_pixelSize = pixelSize; 0041 m_defaultPixel = new quint8[m_pixelSize]; 0042 setDefaultPixel(defaultPixel); 0043 } 0044 0045 KisTiledDataManager::KisTiledDataManager(const KisTiledDataManager &dm) 0046 : KisShared() 0047 { 0048 /* See comment in destructor for details */ 0049 0050 /* We do not clone the history of the device, there is no usecase for it */ 0051 m_mementoManager = new KisMementoManager(); 0052 0053 KisTileData *defaultTileData = dm.m_hashTable->refAndFetchDefaultTileData(); 0054 m_mementoManager->setDefaultTileData(defaultTileData); 0055 defaultTileData->deref(); 0056 0057 m_hashTable = new KisTileHashTable(*dm.m_hashTable, m_mementoManager); 0058 0059 m_pixelSize = dm.m_pixelSize; 0060 m_defaultPixel = new quint8[m_pixelSize]; 0061 /** 0062 * We won't call setDefaultTileData here, as defaultTileDatas 0063 * has already been made shared in m_hashTable(dm->m_hashTable) 0064 */ 0065 memcpy(m_defaultPixel, dm.m_defaultPixel, m_pixelSize); 0066 recalculateExtent(); 0067 } 0068 0069 KisTiledDataManager::~KisTiledDataManager() 0070 { 0071 /** 0072 * Here is an explanation why we use hash table and The Memento Manager 0073 * dynamically allocated We need to destroy them in that very order. The 0074 * reason is that when hash table destroying all her child tiles they all 0075 * cry about it to The Memento Manager using a pointer. So The Memento 0076 * Manager should be alive during that destruction. We could use shared 0077 * pointers instead, but they create too much overhead. 0078 */ 0079 delete m_hashTable; 0080 delete m_mementoManager; 0081 0082 delete[] m_defaultPixel; 0083 } 0084 0085 void KisTiledDataManager::setDefaultPixel(const quint8 *defaultPixel) 0086 { 0087 QWriteLocker locker(&m_lock); 0088 setDefaultPixelImpl(defaultPixel); 0089 } 0090 0091 void KisTiledDataManager::setDefaultPixelImpl(const quint8 *defaultPixel) 0092 { 0093 KisTileData *td = KisTileDataStore::instance()->createDefaultTileData(pixelSize(), defaultPixel); 0094 m_hashTable->setDefaultTileData(td); 0095 m_mementoManager->setDefaultTileData(td); 0096 0097 memcpy(m_defaultPixel, defaultPixel, pixelSize()); 0098 } 0099 0100 bool KisTiledDataManager::write(KisPaintDeviceWriter &store) 0101 { 0102 QReadLocker locker(&m_lock); 0103 0104 bool retval = true; 0105 0106 if(CURRENT_VERSION == LEGACY_VERSION) { 0107 char str[80]; 0108 sprintf(str, "%d\n", m_hashTable->numTiles()); 0109 retval = store.write(str, strlen(str)); 0110 } 0111 else { 0112 retval = writeTilesHeader(store, m_hashTable->numTiles()); 0113 } 0114 0115 0116 KisTileHashTableConstIterator iter(m_hashTable); 0117 KisTileSP tile; 0118 0119 KisAbstractTileCompressorSP compressor = 0120 KisTileCompressorFactory::create(CURRENT_VERSION); 0121 0122 while ((tile = iter.tile())) { 0123 retval = compressor->writeTile(tile, store); 0124 if (!retval) { 0125 warnFile << "Failed to write tile"; 0126 break; 0127 } 0128 iter.next(); 0129 } 0130 0131 return retval; 0132 } 0133 bool KisTiledDataManager::read(QIODevice *stream) 0134 { 0135 clear(); 0136 0137 QWriteLocker locker(&m_lock); 0138 KisMementoSP nothing = m_mementoManager->getMemento(); 0139 0140 if (!stream) { 0141 m_mementoManager->commit(); 0142 return false; 0143 } 0144 0145 const qint32 maxLineLength = 79; // Legacy magic 0146 QByteArray line = stream->readLine(maxLineLength); 0147 line = line.trimmed(); 0148 0149 quint32 numTiles; 0150 qint32 tilesVersion = LEGACY_VERSION; 0151 0152 if (line[0] == 'V') { 0153 QList<QByteArray> lineItems = line.split(' '); 0154 0155 QString keyword = lineItems.takeFirst(); 0156 Q_ASSERT(keyword == "VERSION"); 0157 0158 tilesVersion = lineItems.takeFirst().toInt(); 0159 0160 if(!processTilesHeader(stream, numTiles)) 0161 return false; 0162 } 0163 else { 0164 numTiles = line.toUInt(); 0165 } 0166 0167 KisAbstractTileCompressorSP compressor = 0168 KisTileCompressorFactory::create(tilesVersion); 0169 0170 bool readSuccess = true; 0171 for (quint32 i = 0; i < numTiles; i++) { 0172 if (!compressor->readTile(stream, this)) { 0173 readSuccess = false; 0174 } 0175 } 0176 0177 m_mementoManager->commit(); 0178 return readSuccess; 0179 } 0180 0181 bool KisTiledDataManager::writeTilesHeader(KisPaintDeviceWriter &store, quint32 numTiles) 0182 { 0183 QString buffer; 0184 0185 buffer = QString("VERSION %1\n" 0186 "TILEWIDTH %2\n" 0187 "TILEHEIGHT %3\n" 0188 "PIXELSIZE %4\n" 0189 "DATA %5\n") 0190 .arg(CURRENT_VERSION) 0191 .arg(KisTileData::WIDTH) 0192 .arg(KisTileData::HEIGHT) 0193 .arg(pixelSize()) 0194 .arg(numTiles); 0195 0196 return store.write(buffer.toLatin1()); 0197 } 0198 0199 #define takeOneLine(stream, maxLine, keyword, value) \ 0200 do { \ 0201 QByteArray line = stream->readLine(maxLine); \ 0202 line = line.trimmed(); \ 0203 QList<QByteArray> lineItems = line.split(' '); \ 0204 keyword = lineItems.takeFirst(); \ 0205 value = lineItems.takeFirst().toInt(); \ 0206 } while(0) \ 0207 0208 0209 bool KisTiledDataManager::processTilesHeader(QIODevice *stream, quint32 &numTiles) 0210 { 0211 /** 0212 * We assume that there is only one version of this header 0213 * possible. In case we invent something new, it'll be quite easy 0214 * to modify the behavior 0215 */ 0216 0217 const qint32 maxLineLength = 25; 0218 const qint32 totalNumTests = 4; 0219 bool foundDataMark = false; 0220 qint32 testsPassed = 0; 0221 0222 QString keyword; 0223 qint32 value; 0224 0225 while(!foundDataMark && stream->canReadLine()) { 0226 takeOneLine(stream, maxLineLength, keyword, value); 0227 0228 if (keyword == "TILEWIDTH") { 0229 if(value != KisTileData::WIDTH) 0230 goto wrongString; 0231 } 0232 else if (keyword == "TILEHEIGHT") { 0233 if(value != KisTileData::HEIGHT) 0234 goto wrongString; 0235 } 0236 else if (keyword == "PIXELSIZE") { 0237 if((quint32)value != pixelSize()) 0238 goto wrongString; 0239 } 0240 else if (keyword == "DATA") { 0241 numTiles = value; 0242 foundDataMark = true; 0243 } 0244 else { 0245 goto wrongString; 0246 } 0247 0248 testsPassed++; 0249 } 0250 0251 if(testsPassed != totalNumTests) { 0252 warnTiles << "Not enough fields of tiles header present" 0253 << testsPassed << "of" << totalNumTests; 0254 } 0255 0256 return testsPassed == totalNumTests; 0257 0258 wrongString: 0259 warnTiles << "Wrong string in tiles header:" << keyword << value; 0260 return false; 0261 } 0262 0263 void KisTiledDataManager::purge(const QRect& area) 0264 { 0265 QList<KisTileSP> tilesToDelete; 0266 { 0267 const qint32 tileDataSize = KisTileData::HEIGHT * KisTileData::WIDTH * pixelSize(); 0268 KisTileData *tileData = m_hashTable->refAndFetchDefaultTileData(); 0269 tileData->blockSwapping(); 0270 const quint8 *defaultData = tileData->data(); 0271 0272 KisTileHashTableConstIterator iter(m_hashTable); 0273 KisTileSP tile; 0274 0275 while ((tile = iter.tile())) { 0276 if (tile->extent().intersects(area)) { 0277 tile->lockForRead(); 0278 if(memcmp(defaultData, tile->data(), tileDataSize) == 0) { 0279 tilesToDelete.push_back(tile); 0280 } 0281 tile->unlockForRead(); 0282 } 0283 iter.next(); 0284 } 0285 0286 tileData->unblockSwapping(); 0287 tileData->deref(); 0288 } 0289 Q_FOREACH (KisTileSP tile, tilesToDelete) { 0290 if (m_hashTable->deleteTile(tile)) { 0291 m_extentManager.notifyTileRemoved(tile->col(), tile->row()); 0292 } 0293 } 0294 } 0295 0296 quint8* KisTiledDataManager::duplicatePixel(qint32 num, const quint8 *pixel) 0297 { 0298 const qint32 pixelSize = this->pixelSize(); 0299 /* FIXME: Make a fun filling here */ 0300 quint8 *dstBuf = new quint8[num * pixelSize]; 0301 quint8 *dstIt = dstBuf; 0302 for (qint32 i = 0; i < num; i++) { 0303 memcpy(dstIt, pixel, pixelSize); 0304 dstIt += pixelSize; 0305 } 0306 return dstBuf; 0307 } 0308 0309 void KisTiledDataManager::clear(QRect clearRect, const quint8 *clearPixel) 0310 { 0311 if (clearPixel == 0) 0312 clearPixel = m_defaultPixel; 0313 0314 if (clearRect.isEmpty()) 0315 return; 0316 0317 const qint32 pixelSize = this->pixelSize(); 0318 0319 bool pixelBytesAreDefault = !memcmp(clearPixel, m_defaultPixel, pixelSize); 0320 0321 bool pixelBytesAreTheSame = true; 0322 for (qint32 i = 0; i < pixelSize; ++i) { 0323 if (clearPixel[i] != clearPixel[0]) { 0324 pixelBytesAreTheSame = false; 0325 break; 0326 } 0327 } 0328 0329 if (pixelBytesAreDefault) { 0330 clearRect &= m_extentManager.extent(); 0331 } 0332 0333 qint32 firstColumn = xToCol(clearRect.left()); 0334 qint32 lastColumn = xToCol(clearRect.right()); 0335 0336 qint32 firstRow = yToRow(clearRect.top()); 0337 qint32 lastRow = yToRow(clearRect.bottom()); 0338 0339 const quint32 rowStride = KisTileData::WIDTH * pixelSize; 0340 0341 // Generate one row 0342 quint8 *clearPixelData = 0; 0343 quint32 maxRunLength = qMin(clearRect.width(), KisTileData::WIDTH); 0344 clearPixelData = duplicatePixel(maxRunLength, clearPixel); 0345 0346 KisTileData *td = 0; 0347 if (!pixelBytesAreDefault && 0348 clearRect.width() >= KisTileData::WIDTH && 0349 clearRect.height() >= KisTileData::HEIGHT) { 0350 0351 td = KisTileDataStore::instance()->createDefaultTileData(pixelSize, clearPixel); 0352 td->acquire(); 0353 } 0354 0355 for (qint32 row = firstRow; row <= lastRow; ++row) { 0356 for (qint32 column = firstColumn; column <= lastColumn; ++column) { 0357 0358 QRect tileRect(column*KisTileData::WIDTH, row*KisTileData::HEIGHT, 0359 KisTileData::WIDTH, KisTileData::HEIGHT); 0360 QRect clearTileRect = clearRect & tileRect; 0361 0362 if (clearTileRect == tileRect) { 0363 // Clear whole tile 0364 const bool wasDeleted = 0365 m_hashTable->deleteTile(column, row); 0366 0367 if (wasDeleted) { 0368 m_extentManager.notifyTileRemoved(column, row); 0369 } 0370 0371 0372 if (!pixelBytesAreDefault) { 0373 KisTileSP clearedTile = KisTileSP(new KisTile(column, row, td, m_mementoManager)); 0374 m_hashTable->addTile(clearedTile); 0375 m_extentManager.notifyTileAdded(column, row); 0376 } 0377 } else { 0378 const qint32 lineSize = clearTileRect.width() * pixelSize; 0379 qint32 rowsRemaining = clearTileRect.height(); 0380 0381 KisTileDataWrapper tw(this, 0382 clearTileRect.left(), 0383 clearTileRect.top(), 0384 KisTileDataWrapper::WRITE); 0385 quint8* tileIt = tw.data(); 0386 0387 if (pixelBytesAreTheSame) { 0388 while (rowsRemaining > 0) { 0389 memset(tileIt, *clearPixelData, lineSize); 0390 tileIt += rowStride; 0391 rowsRemaining--; 0392 } 0393 } else { 0394 while (rowsRemaining > 0) { 0395 memcpy(tileIt, clearPixelData, lineSize); 0396 tileIt += rowStride; 0397 rowsRemaining--; 0398 } 0399 } 0400 } 0401 } 0402 } 0403 0404 if (td) td->release(); 0405 delete[] clearPixelData; 0406 } 0407 0408 void KisTiledDataManager::clear(QRect clearRect, quint8 clearValue) 0409 { 0410 quint8 *buf = new quint8[pixelSize()]; 0411 memset(buf, clearValue, pixelSize()); 0412 clear(clearRect, buf); 0413 delete[] buf; 0414 } 0415 0416 void KisTiledDataManager::clear(qint32 x, qint32 y, qint32 w, qint32 h, const quint8 *clearPixel) 0417 { 0418 clear(QRect(x, y, w, h), clearPixel); 0419 } 0420 void KisTiledDataManager::clear(qint32 x, qint32 y, qint32 w, qint32 h, quint8 clearValue) 0421 { 0422 clear(QRect(x, y, w, h), clearValue); 0423 } 0424 0425 void KisTiledDataManager::clear() 0426 { 0427 m_hashTable->clear(); 0428 m_extentManager.clear(); 0429 } 0430 0431 0432 template<bool useOldSrcData> 0433 void KisTiledDataManager::bitBltImpl(KisTiledDataManager *srcDM, const QRect &rect) 0434 { 0435 if (rect.isEmpty()) return; 0436 0437 const qint32 pixelSize = this->pixelSize(); 0438 const bool defaultPixelsCoincide = 0439 !memcmp(srcDM->defaultPixel(), m_defaultPixel, pixelSize); 0440 0441 const quint32 rowStride = KisTileData::WIDTH * pixelSize; 0442 0443 qint32 firstColumn = xToCol(rect.left()); 0444 qint32 lastColumn = xToCol(rect.right()); 0445 0446 qint32 firstRow = yToRow(rect.top()); 0447 qint32 lastRow = yToRow(rect.bottom()); 0448 0449 for (qint32 row = firstRow; row <= lastRow; ++row) { 0450 for (qint32 column = firstColumn; column <= lastColumn; ++column) { 0451 0452 bool srcTileExists = false; 0453 0454 // this is the only variation in the template 0455 KisTileSP srcTile = useOldSrcData ? 0456 srcDM->getOldTile(column, row, srcTileExists) : 0457 srcDM->getReadOnlyTileLazy(column, row, srcTileExists); 0458 0459 QRect tileRect(column*KisTileData::WIDTH, row*KisTileData::HEIGHT, 0460 KisTileData::WIDTH, KisTileData::HEIGHT); 0461 QRect cloneTileRect = rect & tileRect; 0462 0463 if (cloneTileRect == tileRect) { 0464 // Clone whole tile 0465 const bool wasDeleted = 0466 m_hashTable->deleteTile(column, row); 0467 0468 if (srcTileExists || !defaultPixelsCoincide) { 0469 srcTile->lockForRead(); 0470 KisTileData *td = srcTile->tileData(); 0471 KisTileSP clonedTile = KisTileSP(new KisTile(column, row, td, m_mementoManager)); 0472 srcTile->unlockForRead(); 0473 0474 m_hashTable->addTile(clonedTile); 0475 0476 if (!wasDeleted) { 0477 m_extentManager.notifyTileAdded(column, row); 0478 } 0479 } else if (wasDeleted) { 0480 m_extentManager.notifyTileRemoved(column, row); 0481 } 0482 0483 } else { 0484 const qint32 lineSize = cloneTileRect.width() * pixelSize; 0485 qint32 rowsRemaining = cloneTileRect.height(); 0486 0487 KisTileDataWrapper tw(this, 0488 cloneTileRect.left(), 0489 cloneTileRect.top(), 0490 KisTileDataWrapper::WRITE); 0491 srcTile->lockForRead(); 0492 // We suppose that the shift in both tiles is the same 0493 const quint8* srcTileIt = srcTile->data() + tw.offset(); 0494 quint8* dstTileIt = tw.data(); 0495 0496 while (rowsRemaining > 0) { 0497 memcpy(dstTileIt, srcTileIt, lineSize); 0498 srcTileIt += rowStride; 0499 dstTileIt += rowStride; 0500 rowsRemaining--; 0501 } 0502 0503 srcTile->unlockForRead(); 0504 } 0505 } 0506 } 0507 } 0508 0509 template<bool useOldSrcData> 0510 void KisTiledDataManager::bitBltRoughImpl(KisTiledDataManager *srcDM, const QRect &rect) 0511 { 0512 if (rect.isEmpty()) return; 0513 0514 const qint32 pixelSize = this->pixelSize(); 0515 const bool defaultPixelsCoincide = 0516 !memcmp(srcDM->defaultPixel(), m_defaultPixel, pixelSize); 0517 0518 qint32 firstColumn = xToCol(rect.left()); 0519 qint32 lastColumn = xToCol(rect.right()); 0520 0521 qint32 firstRow = yToRow(rect.top()); 0522 qint32 lastRow = yToRow(rect.bottom()); 0523 0524 for (qint32 row = firstRow; row <= lastRow; ++row) { 0525 for (qint32 column = firstColumn; column <= lastColumn; ++column) { 0526 0527 /** 0528 * We are cloning whole tiles here so let's not be so boring 0529 * to check any borders :) 0530 */ 0531 0532 bool srcTileExists = false; 0533 0534 // this is the only variation in the template 0535 KisTileSP srcTile = useOldSrcData ? 0536 srcDM->getOldTile(column, row, srcTileExists) : 0537 srcDM->getReadOnlyTileLazy(column, row, srcTileExists); 0538 0539 const bool wasDeleted = 0540 m_hashTable->deleteTile(column, row); 0541 0542 if (srcTileExists || !defaultPixelsCoincide) { 0543 srcTile->lockForRead(); 0544 KisTileData *td = srcTile->tileData(); 0545 KisTileSP clonedTile = KisTileSP(new KisTile(column, row, td, m_mementoManager)); 0546 srcTile->unlockForRead(); 0547 0548 m_hashTable->addTile(clonedTile); 0549 0550 if (!wasDeleted) { 0551 m_extentManager.notifyTileAdded(column, row); 0552 } 0553 } else if (wasDeleted) { 0554 m_extentManager.notifyTileRemoved(column, row); 0555 } 0556 } 0557 } 0558 } 0559 0560 void KisTiledDataManager::bitBlt(KisTiledDataManager *srcDM, const QRect &rect) 0561 { 0562 bitBltImpl<false>(srcDM, rect); 0563 } 0564 0565 void KisTiledDataManager::bitBltOldData(KisTiledDataManager *srcDM, const QRect &rect) 0566 { 0567 bitBltImpl<true>(srcDM, rect); 0568 } 0569 0570 void KisTiledDataManager::bitBltRough(KisTiledDataManager *srcDM, const QRect &rect) 0571 { 0572 bitBltRoughImpl<false>(srcDM, rect); 0573 } 0574 0575 void KisTiledDataManager::bitBltRoughOldData(KisTiledDataManager *srcDM, const QRect &rect) 0576 { 0577 bitBltRoughImpl<true>(srcDM, rect); 0578 } 0579 0580 void KisTiledDataManager::setExtent(qint32 x, qint32 y, qint32 w, qint32 h) 0581 { 0582 setExtent(QRect(x, y, w, h)); 0583 } 0584 0585 void KisTiledDataManager::setExtent(QRect newRect) 0586 { 0587 QRect oldRect = extent(); 0588 newRect = newRect.normalized(); 0589 0590 // Do nothing if the desired size is bigger than we currently are: 0591 // that is handled by the autoextending automatically 0592 if (newRect.contains(oldRect)) return; 0593 0594 KisTileSP tile; 0595 QRect tileRect; 0596 { 0597 KisTileHashTableIterator iter(m_hashTable); 0598 0599 while (!iter.isDone()) { 0600 tile = iter.tile(); 0601 0602 tileRect = tile->extent(); 0603 if (newRect.contains(tileRect)) { 0604 //do nothing 0605 iter.next(); 0606 } else if (newRect.intersects(tileRect)) { 0607 QRect intersection = newRect & tileRect; 0608 intersection.translate(- tileRect.topLeft()); 0609 0610 const qint32 pixelSize = this->pixelSize(); 0611 0612 tile->lockForWrite(); 0613 quint8* data = tile->data(); 0614 quint8* ptr; 0615 0616 /* FIXME: make it faster */ 0617 for (int y = 0; y < KisTileData::HEIGHT; y++) { 0618 for (int x = 0; x < KisTileData::WIDTH; x++) { 0619 if (!intersection.contains(x, y)) { 0620 ptr = data + pixelSize * (y * KisTileData::WIDTH + x); 0621 memcpy(ptr, m_defaultPixel, pixelSize); 0622 } 0623 } 0624 } 0625 tile->unlockForWrite(); 0626 iter.next(); 0627 } else { 0628 m_extentManager.notifyTileRemoved(tile->col(), tile->row()); 0629 iter.deleteCurrent(); 0630 } 0631 } 0632 } 0633 } 0634 0635 void KisTiledDataManager::recalculateExtent() 0636 { 0637 QVector<QPoint> indexes; 0638 0639 { 0640 KisTileHashTableConstIterator iter(m_hashTable); 0641 KisTileSP tile; 0642 0643 while ((tile = iter.tile())) { 0644 indexes << QPoint(tile->col(), tile->row()); 0645 iter.next(); 0646 } 0647 } 0648 0649 m_extentManager.replaceTileStats(indexes); 0650 } 0651 0652 void KisTiledDataManager::extent(qint32 &x, qint32 &y, qint32 &w, qint32 &h) const 0653 { 0654 QRect rect = extent(); 0655 rect.getRect(&x, &y, &w, &h); 0656 } 0657 0658 QRect KisTiledDataManager::extent() const 0659 { 0660 return m_extentManager.extent(); 0661 } 0662 0663 KisRegion KisTiledDataManager::region() const 0664 { 0665 QVector<QRect> rects; 0666 0667 KisTileHashTableConstIterator iter(m_hashTable); 0668 KisTileSP tile; 0669 0670 while ((tile = iter.tile())) { 0671 rects << tile->extent(); 0672 iter.next(); 0673 } 0674 0675 return KisRegion(std::move(rects)); 0676 } 0677 0678 void KisTiledDataManager::setPixel(qint32 x, qint32 y, const quint8 * data) 0679 { 0680 KisTileDataWrapper tw(this, x, y, KisTileDataWrapper::WRITE); 0681 memcpy(tw.data(), data, pixelSize()); 0682 } 0683 0684 void KisTiledDataManager::writeBytes(const quint8 *data, 0685 qint32 x, qint32 y, 0686 qint32 width, qint32 height, 0687 qint32 dataRowStride) 0688 { 0689 QWriteLocker locker(&m_lock); 0690 // Actual bytes reading/writing is done in private header 0691 writeBytesBody(data, x, y, width, height, dataRowStride); 0692 } 0693 0694 void KisTiledDataManager::readBytes(quint8 *data, 0695 qint32 x, qint32 y, 0696 qint32 width, qint32 height, 0697 qint32 dataRowStride) const 0698 { 0699 QReadLocker locker(&m_lock); 0700 // Actual bytes reading/writing is done in private header 0701 readBytesBody(data, x, y, width, height, dataRowStride); 0702 } 0703 0704 QVector<quint8*> 0705 KisTiledDataManager::readPlanarBytes(QVector<qint32> channelSizes, 0706 qint32 x, qint32 y, 0707 qint32 width, qint32 height) const 0708 { 0709 QReadLocker locker(&m_lock); 0710 // Actual bytes reading/writing is done in private header 0711 return readPlanarBytesBody(channelSizes, x, y, width, height); 0712 } 0713 0714 0715 void KisTiledDataManager::writePlanarBytes(QVector<quint8*> planes, 0716 QVector<qint32> channelSizes, 0717 qint32 x, qint32 y, 0718 qint32 width, qint32 height) 0719 { 0720 QWriteLocker locker(&m_lock); 0721 // Actual bytes reading/writing is done in private header 0722 0723 bool allChannelsPresent = true; 0724 0725 Q_FOREACH (const quint8* plane, planes) { 0726 if (!plane) { 0727 allChannelsPresent = false; 0728 break; 0729 } 0730 } 0731 0732 if (allChannelsPresent) { 0733 writePlanarBytesBody<true>(planes, channelSizes, x, y, width, height); 0734 } else { 0735 writePlanarBytesBody<false>(planes, channelSizes, x, y, width, height); 0736 } 0737 } 0738 0739 qint32 KisTiledDataManager::numContiguousColumns(qint32 x, qint32 minY, qint32 maxY) const 0740 { 0741 qint32 numColumns; 0742 0743 Q_UNUSED(minY); 0744 Q_UNUSED(maxY); 0745 0746 if (x >= 0) { 0747 numColumns = KisTileData::WIDTH - (x % KisTileData::WIDTH); 0748 } else { 0749 numColumns = ((-x - 1) % KisTileData::WIDTH) + 1; 0750 } 0751 0752 return numColumns; 0753 } 0754 0755 qint32 KisTiledDataManager::numContiguousRows(qint32 y, qint32 minX, qint32 maxX) const 0756 { 0757 qint32 numRows; 0758 0759 Q_UNUSED(minX); 0760 Q_UNUSED(maxX); 0761 0762 if (y >= 0) { 0763 numRows = KisTileData::HEIGHT - (y % KisTileData::HEIGHT); 0764 } else { 0765 numRows = ((-y - 1) % KisTileData::HEIGHT) + 1; 0766 } 0767 0768 return numRows; 0769 } 0770 0771 qint32 KisTiledDataManager::rowStride(qint32 x, qint32 y) const 0772 { 0773 Q_UNUSED(x); 0774 Q_UNUSED(y); 0775 0776 return KisTileData::WIDTH * pixelSize(); 0777 } 0778 0779 void KisTiledDataManager::releaseInternalPools() 0780 { 0781 KisTileData::releaseInternalPools(); 0782 }