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 }