diff --git a/libs/image/tiles3/kis_tile_hash_table_p.h b/libs/image/tiles3/kis_tile_hash_table_p.h index 063673b73b..d37e5f33f1 100644 --- a/libs/image/tiles3/kis_tile_hash_table_p.h +++ b/libs/image/tiles3/kis_tile_hash_table_p.h @@ -1,398 +1,396 @@ /* * Copyright (c) 2004 C. Boemann * (c) 2009 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include "kis_debug.h" #include "kis_global.h" //#define SHARED_TILES_SANITY_CHECK template KisTileHashTableTraits::KisTileHashTableTraits(KisMementoManager *mm) : m_lock(QReadWriteLock::NonRecursive) { m_hashTable = new TileTypeSP [TABLE_SIZE]; Q_CHECK_PTR(m_hashTable); m_numTiles = 0; m_defaultTileData = 0; m_mementoManager = mm; } template KisTileHashTableTraits::KisTileHashTableTraits(const KisTileHashTableTraits &ht, KisMementoManager *mm) : m_lock(QReadWriteLock::NonRecursive) { QReadLocker locker(&ht.m_lock); m_mementoManager = mm; m_defaultTileData = 0; setDefaultTileDataImp(ht.m_defaultTileData); m_hashTable = new TileTypeSP [TABLE_SIZE]; Q_CHECK_PTR(m_hashTable); TileTypeSP foreignTile; TileType* nativeTile; TileType* nativeTileHead; for (qint32 i = 0; i < TABLE_SIZE; i++) { nativeTileHead = 0; foreignTile = ht.m_hashTable[i]; while (foreignTile) { nativeTile = new TileType(*foreignTile, m_mementoManager); nativeTile->setNext(nativeTileHead); nativeTileHead = nativeTile; foreignTile = foreignTile->next(); } m_hashTable[i] = nativeTileHead; } m_numTiles = ht.m_numTiles; } template KisTileHashTableTraits::~KisTileHashTableTraits() { clear(); delete[] m_hashTable; setDefaultTileDataImp(0); } template quint32 KisTileHashTableTraits::calculateHash(qint32 col, qint32 row) { return ((row << 5) + (col & 0x1F)) & 0x3FF; } template typename KisTileHashTableTraits::TileTypeSP KisTileHashTableTraits::getTile(qint32 col, qint32 row) { qint32 idx = calculateHash(col, row); TileTypeSP tile = m_hashTable[idx]; for (; tile; tile = tile->next()) { if (tile->col() == col && tile->row() == row) { return tile; } } return 0; } template void KisTileHashTableTraits::linkTile(TileTypeSP tile) { qint32 idx = calculateHash(tile->col(), tile->row()); TileTypeSP firstTile = m_hashTable[idx]; #ifdef SHARED_TILES_SANITY_CHECK Q_ASSERT_X(!tile->next(), "KisTileHashTableTraits::linkTile", "A tile can't be shared by several hash tables, sorry."); #endif tile->setNext(firstTile); m_hashTable[idx] = tile; m_numTiles++; } template typename KisTileHashTableTraits::TileTypeSP KisTileHashTableTraits::unlinkTile(qint32 col, qint32 row) { qint32 idx = calculateHash(col, row); TileTypeSP tile = m_hashTable[idx]; TileTypeSP prevTile = 0; for (; tile; tile = tile->next()) { if (tile->col() == col && tile->row() == row) { if (prevTile) prevTile->setNext(tile->next()); else /* optimize here*/ m_hashTable[idx] = tile->next(); /** * The shared pointer may still be accessed by someone, so * we need to disconnects the tile from memento manager * explicitly */ tile->setNext(0); tile->notifyDead(); tile = 0; m_numTiles--; return tile; } prevTile = tile; } return 0; } template inline void KisTileHashTableTraits::setDefaultTileDataImp(KisTileData *defaultTileData) { if (m_defaultTileData) { - m_defaultTileData->unblockSwapping(); m_defaultTileData->release(); m_defaultTileData = 0; } if (defaultTileData) { defaultTileData->acquire(); - defaultTileData->blockSwapping(); m_defaultTileData = defaultTileData; } } template inline KisTileData* KisTileHashTableTraits::defaultTileDataImp() const { return m_defaultTileData; } template bool KisTileHashTableTraits::tileExists(qint32 col, qint32 row) { QReadLocker locker(&m_lock); return getTile(col, row); } template typename KisTileHashTableTraits::TileTypeSP KisTileHashTableTraits::getExistedTile(qint32 col, qint32 row) { QReadLocker locker(&m_lock); return getTile(col, row); } template typename KisTileHashTableTraits::TileTypeSP KisTileHashTableTraits::getTileLazy(qint32 col, qint32 row, bool& newTile) { /** * FIXME: Read access is better */ QWriteLocker locker(&m_lock); newTile = false; TileTypeSP tile = getTile(col, row); if (!tile) { tile = new TileType(col, row, m_defaultTileData, m_mementoManager); linkTile(tile); newTile = true; } return tile; } template typename KisTileHashTableTraits::TileTypeSP KisTileHashTableTraits::getReadOnlyTileLazy(qint32 col, qint32 row) { QReadLocker locker(&m_lock); TileTypeSP tile = getTile(col, row); if (!tile) tile = new TileType(col, row, m_defaultTileData, 0); return tile; } template void KisTileHashTableTraits::addTile(TileTypeSP tile) { QWriteLocker locker(&m_lock); linkTile(tile); } template void KisTileHashTableTraits::deleteTile(qint32 col, qint32 row) { QWriteLocker locker(&m_lock); TileTypeSP tile = unlinkTile(col, row); /* Done by KisSharedPtr */ //if(tile) // delete tile; } template void KisTileHashTableTraits::deleteTile(TileTypeSP tile) { deleteTile(tile->col(), tile->row()); } template void KisTileHashTableTraits::clear() { QWriteLocker locker(&m_lock); TileTypeSP tile = 0; qint32 i; for (i = 0; i < TABLE_SIZE; i++) { tile = m_hashTable[i]; while (tile) { TileTypeSP tmp = tile; tile = tile->next(); /** * About disconnection of tiles see a comment in unlinkTile() */ tmp->setNext(0); tmp->notifyDead(); tmp = 0; m_numTiles--; } m_hashTable[i] = 0; } Q_ASSERT(!m_numTiles); } template void KisTileHashTableTraits::setDefaultTileData(KisTileData *defaultTileData) { QWriteLocker locker(&m_lock); setDefaultTileDataImp(defaultTileData); } template KisTileData* KisTileHashTableTraits::defaultTileData() const { QWriteLocker locker(&m_lock); return defaultTileDataImp(); } /*************** Debugging stuff ***************/ template void KisTileHashTableTraits::debugPrintInfo() { dbgTiles << "==========================\n" << "TileHashTable:" << "\n def. data:\t\t" << m_defaultTileData << "\n numTiles:\t\t" << m_numTiles; debugListLengthDistibution(); dbgTiles << "==========================\n"; } template qint32 KisTileHashTableTraits::debugChainLen(qint32 idx) { qint32 len = 0; for (TileTypeSP it = m_hashTable[idx]; it; it = it->next(), len++) ; return len; } template void KisTileHashTableTraits::debugMaxListLength(qint32 &min, qint32 &max) { TileTypeSP tile; qint32 maxLen = 0; qint32 minLen = m_numTiles; qint32 tmp = 0; for (qint32 i = 0; i < TABLE_SIZE; i++) { tmp = debugChainLen(i); if (tmp > maxLen) maxLen = tmp; if (tmp < minLen) minLen = tmp; } min = minLen; max = maxLen; } template void KisTileHashTableTraits::debugListLengthDistibution() { qint32 min, max; qint32 arraySize; qint32 tmp; debugMaxListLength(min, max); arraySize = max - min + 1; qint32 *array = new qint32[arraySize]; memset(array, 0, sizeof(qint32)*arraySize); for (qint32 i = 0; i < TABLE_SIZE; i++) { tmp = debugChainLen(i); array[tmp-min]++; } dbgTiles << QString(" minChain:\t\t%d\n" " maxChain:\t\t%d").arg(min, max); dbgTiles << " Chain size distribution:"; for (qint32 i = 0; i < arraySize; i++) dbgTiles << QString(" %1:\t%2\n").arg(i + min, array[i]); delete[] array; } template void KisTileHashTableTraits::sanityChecksumCheck() { /** * We assume that the lock should have already been taken * by the code that was going to change the table */ Q_ASSERT(!m_lock.tryLockForWrite()); TileTypeSP tile = 0; qint32 exactNumTiles = 0; for (qint32 i = 0; i < TABLE_SIZE; i++) { tile = m_hashTable[i]; while (tile) { exactNumTiles++; tile = tile->next(); } } if (exactNumTiles != m_numTiles) { dbgKrita << "Sanity check failed!"; dbgKrita << ppVar(exactNumTiles); dbgKrita << ppVar(m_numTiles); dbgKrita << "Wrong tiles checksum!"; Q_ASSERT(0); // not fatalKrita for a backtrace support } } diff --git a/libs/image/tiles3/kis_tiled_data_manager.cc b/libs/image/tiles3/kis_tiled_data_manager.cc index cfb110d04c..5cf36da9f2 100644 --- a/libs/image/tiles3/kis_tiled_data_manager.cc +++ b/libs/image/tiles3/kis_tiled_data_manager.cc @@ -1,808 +1,812 @@ /* * Copyright (c) 2004 C. Boemann * (c) 2009 Dmitry Kazakov * (c) 2010 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include "kis_tile.h" #include "kis_tiled_data_manager.h" #include "kis_tile_data_wrapper.h" #include "kis_tiled_data_manager_p.h" #include "kis_memento_manager.h" #include "swap/kis_legacy_tile_compressor.h" #include "swap/kis_tile_compressor_factory.h" #include "kis_paint_device_writer.h" #include "kis_global.h" /* The data area is divided into tiles each say 64x64 pixels (defined at compiletime) * The tiles are laid out in a matrix that can have negative indexes. * The matrix grows automatically if needed (a call for writeacces to a tile * outside the current extent) * Even though the matrix has grown it may still not contain tiles at specific positions. * They are created on demand */ KisTiledDataManager::KisTiledDataManager(quint32 pixelSize, const quint8 *defaultPixel) { /* See comment in destructor for details */ m_mementoManager = new KisMementoManager(); m_hashTable = new KisTileHashTable(m_mementoManager); m_pixelSize = pixelSize; m_defaultPixel = new quint8[m_pixelSize]; setDefaultPixel(defaultPixel); m_extentMinX = qint32_MAX; m_extentMinY = qint32_MAX; m_extentMaxX = qint32_MIN; m_extentMaxY = qint32_MIN; } KisTiledDataManager::KisTiledDataManager(const KisTiledDataManager &dm) : KisShared() { /* See comment in destructor for details */ /* We do not clone the history of the device, there is no usecase for it */ m_mementoManager = new KisMementoManager(); m_mementoManager->setDefaultTileData(dm.m_hashTable->defaultTileData()); m_hashTable = new KisTileHashTable(*dm.m_hashTable, m_mementoManager); m_pixelSize = dm.m_pixelSize; m_defaultPixel = new quint8[m_pixelSize]; /** * We won't call setDefaultTileData here, as defaultTileDatas * has already been made shared in m_hashTable(dm->m_hashTable) */ memcpy(m_defaultPixel, dm.m_defaultPixel, m_pixelSize); m_extentMinX = dm.m_extentMinX; m_extentMinY = dm.m_extentMinY; m_extentMaxX = dm.m_extentMaxX; m_extentMaxY = dm.m_extentMaxY; } KisTiledDataManager::~KisTiledDataManager() { /** * Here is an explanation why we use hash table and The Memento Manager * dynamically allocated We need to destroy them in that very order. The * reason is that when hash table destroying all her child tiles they all * cry about it to The Memento Manager using a pointer. So The Memento * Manager sould be alive during that destruction. We could use shared * pointers instead, but they create too much overhead. */ delete m_hashTable; delete m_mementoManager; delete[] m_defaultPixel; } void KisTiledDataManager::setDefaultPixel(const quint8 *defaultPixel) { QWriteLocker locker(&m_lock); setDefaultPixelImpl(defaultPixel); } void KisTiledDataManager::setDefaultPixelImpl(const quint8 *defaultPixel) { KisTileData *td = KisTileDataStore::instance()->createDefaultTileData(pixelSize(), defaultPixel); m_hashTable->setDefaultTileData(td); m_mementoManager->setDefaultTileData(td); memcpy(m_defaultPixel, defaultPixel, pixelSize()); } bool KisTiledDataManager::write(KisPaintDeviceWriter &store) { QReadLocker locker(&m_lock); bool retval = true; if(CURRENT_VERSION == LEGACY_VERSION) { char str[80]; sprintf(str, "%d\n", m_hashTable->numTiles()); retval = store.write(str, strlen(str)); } else { retval = writeTilesHeader(store, m_hashTable->numTiles()); } KisTileHashTableIterator iter(m_hashTable); KisTileSP tile; KisAbstractTileCompressorSP compressor = KisTileCompressorFactory::create(CURRENT_VERSION); while ((tile = iter.tile())) { retval = compressor->writeTile(tile, store); if (!retval) { warnFile << "Failed to write tile"; break; } ++iter; } return retval; } bool KisTiledDataManager::read(QIODevice *stream) { if (!stream) return false; clear(); QWriteLocker locker(&m_lock); KisMementoSP nothing = m_mementoManager->getMemento(); if (!stream) { m_mementoManager->commit(); return false; } const qint32 maxLineLength = 79; // Legacy magic QByteArray line = stream->readLine(maxLineLength); line = line.trimmed(); quint32 numTiles; qint32 tilesVersion = LEGACY_VERSION; if (line[0] == 'V') { QList lineItems = line.split(' '); QString keyword = lineItems.takeFirst(); Q_ASSERT(keyword == "VERSION"); tilesVersion = lineItems.takeFirst().toInt(); if(!processTilesHeader(stream, numTiles)) return false; } else { numTiles = line.toUInt(); } KisAbstractTileCompressorSP compressor = KisTileCompressorFactory::create(tilesVersion); bool readSuccess = true; for (quint32 i = 0; i < numTiles; i++) { if (!compressor->readTile(stream, this)) { readSuccess = false; } } m_mementoManager->commit(); return readSuccess; } bool KisTiledDataManager::writeTilesHeader(KisPaintDeviceWriter &store, quint32 numTiles) { QString buffer; buffer = QString("VERSION %1\n" "TILEWIDTH %2\n" "TILEHEIGHT %3\n" "PIXELSIZE %4\n" "DATA %5\n") .arg(CURRENT_VERSION) .arg(KisTileData::WIDTH) .arg(KisTileData::HEIGHT) .arg(pixelSize()) .arg(numTiles); return store.write(buffer.toLatin1()); } #define takeOneLine(stream, maxLine, keyword, value) \ do { \ QByteArray line = stream->readLine(maxLine); \ line = line.trimmed(); \ QList lineItems = line.split(' '); \ keyword = lineItems.takeFirst(); \ value = lineItems.takeFirst().toInt(); \ } while(0) \ bool KisTiledDataManager::processTilesHeader(QIODevice *stream, quint32 &numTiles) { /** * We assume that there is only one version of this header * possible. In case we invent something new, it'll be quite easy * to modify the behavior */ const qint32 maxLineLength = 25; const qint32 totalNumTests = 4; bool foundDataMark = false; qint32 testsPassed = 0; QString keyword; qint32 value; while(!foundDataMark && stream->canReadLine()) { takeOneLine(stream, maxLineLength, keyword, value); if (keyword == "TILEWIDTH") { if(value != KisTileData::WIDTH) goto wrongString; } else if (keyword == "TILEHEIGHT") { if(value != KisTileData::HEIGHT) goto wrongString; } else if (keyword == "PIXELSIZE") { if((quint32)value != pixelSize()) goto wrongString; } else if (keyword == "DATA") { numTiles = value; foundDataMark = true; } else { goto wrongString; } testsPassed++; } if(testsPassed != totalNumTests) { warnTiles << "Not enough fields of tiles header present" << testsPassed << "of" << totalNumTests; } return testsPassed == totalNumTests; wrongString: warnTiles << "Wrong string in tiles header:" << keyword << value; return false; } void KisTiledDataManager::purge(const QRect& area) { QWriteLocker locker(&m_lock); QList tilesToDelete; { const qint32 tileDataSize = KisTileData::HEIGHT * KisTileData::WIDTH * pixelSize(); - const quint8 *defaultData = m_hashTable->defaultTileData()->data(); + KisTileData *tileData = m_hashTable->defaultTileData(); + tileData->blockSwapping(); + const quint8 *defaultData = tileData->data(); KisTileHashTableIterator iter(m_hashTable); KisTileSP tile; while ((tile = iter.tile())) { if (tile->extent().intersects(area)) { tile->lockForRead(); if(memcmp(defaultData, tile->data(), tileDataSize) == 0) { tilesToDelete.push_back(tile); } tile->unlock(); } ++iter; } + + tileData->unblockSwapping(); } Q_FOREACH (KisTileSP tile, tilesToDelete) { m_hashTable->deleteTile(tile); } recalculateExtent(); } quint8* KisTiledDataManager::duplicatePixel(qint32 num, const quint8 *pixel) { const qint32 pixelSize = this->pixelSize(); /* FIXME: Make a fun filling here */ quint8 *dstBuf = new quint8[num * pixelSize]; quint8 *dstIt = dstBuf; for (qint32 i = 0; i < num; i++) { memcpy(dstIt, pixel, pixelSize); dstIt += pixelSize; } return dstBuf; } void KisTiledDataManager::clear(QRect clearRect, const quint8 *clearPixel) { QWriteLocker locker(&m_lock); if (clearPixel == 0) clearPixel = m_defaultPixel; if (clearRect.isEmpty()) return; const qint32 pixelSize = this->pixelSize(); bool pixelBytesAreDefault = !memcmp(clearPixel, m_defaultPixel, pixelSize); bool pixelBytesAreTheSame = true; for (qint32 i = 0; i < pixelSize; ++i) { if (clearPixel[i] != clearPixel[0]) { pixelBytesAreTheSame = false; break; } } if (pixelBytesAreDefault) { clearRect &= extentImpl(); } qint32 firstColumn = xToCol(clearRect.left()); qint32 lastColumn = xToCol(clearRect.right()); qint32 firstRow = yToRow(clearRect.top()); qint32 lastRow = yToRow(clearRect.bottom()); const quint32 rowStride = KisTileData::WIDTH * pixelSize; // Generate one row quint8 *clearPixelData = 0; quint32 maxRunLength = qMin(clearRect.width(), KisTileData::WIDTH); clearPixelData = duplicatePixel(maxRunLength, clearPixel); KisTileData *td = 0; if (!pixelBytesAreDefault && clearRect.width() >= KisTileData::WIDTH && clearRect.height() >= KisTileData::HEIGHT) { td = KisTileDataStore::instance()->createDefaultTileData(pixelSize, clearPixel); td->acquire(); } bool needsRecalculateExtent = false; for (qint32 row = firstRow; row <= lastRow; ++row) { for (qint32 column = firstColumn; column <= lastColumn; ++column) { QRect tileRect(column*KisTileData::WIDTH, row*KisTileData::HEIGHT, KisTileData::WIDTH, KisTileData::HEIGHT); QRect clearTileRect = clearRect & tileRect; if (clearTileRect == tileRect) { // Clear whole tile m_hashTable->deleteTile(column, row); needsRecalculateExtent = true; if (!pixelBytesAreDefault) { KisTileSP clearedTile = new KisTile(column, row, td, m_mementoManager); m_hashTable->addTile(clearedTile); updateExtent(column, row); } } else { const qint32 lineSize = clearTileRect.width() * pixelSize; qint32 rowsRemaining = clearTileRect.height(); KisTileDataWrapper tw(this, clearTileRect.left(), clearTileRect.top(), KisTileDataWrapper::WRITE); quint8* tileIt = tw.data(); if (pixelBytesAreTheSame) { while (rowsRemaining > 0) { memset(tileIt, *clearPixelData, lineSize); tileIt += rowStride; rowsRemaining--; } } else { while (rowsRemaining > 0) { memcpy(tileIt, clearPixelData, lineSize); tileIt += rowStride; rowsRemaining--; } } } } } if (needsRecalculateExtent) { recalculateExtent(); } if (td) td->release(); delete[] clearPixelData; } void KisTiledDataManager::clear(QRect clearRect, quint8 clearValue) { quint8 *buf = new quint8[pixelSize()]; memset(buf, clearValue, pixelSize()); clear(clearRect, buf); delete[] buf; } void KisTiledDataManager::clear(qint32 x, qint32 y, qint32 w, qint32 h, const quint8 *clearPixel) { clear(QRect(x, y, w, h), clearPixel); } void KisTiledDataManager::clear(qint32 x, qint32 y, qint32 w, qint32 h, quint8 clearValue) { clear(QRect(x, y, w, h), clearValue); } void KisTiledDataManager::clear() { QWriteLocker locker(&m_lock); m_hashTable->clear(); m_extentMinX = qint32_MAX; m_extentMinY = qint32_MAX; m_extentMaxX = qint32_MIN; m_extentMaxY = qint32_MIN; } template void KisTiledDataManager::bitBltImpl(KisTiledDataManager *srcDM, const QRect &rect) { QWriteLocker locker(&m_lock); if (rect.isEmpty()) return; const qint32 pixelSize = this->pixelSize(); const quint32 rowStride = KisTileData::WIDTH * pixelSize; qint32 firstColumn = xToCol(rect.left()); qint32 lastColumn = xToCol(rect.right()); qint32 firstRow = yToRow(rect.top()); qint32 lastRow = yToRow(rect.bottom()); for (qint32 row = firstRow; row <= lastRow; ++row) { for (qint32 column = firstColumn; column <= lastColumn; ++column) { // this is the only variation in the template KisTileSP srcTile = useOldSrcData ? srcDM->getOldTile(column, row) : srcDM->getTile(column, row, false); QRect tileRect(column*KisTileData::WIDTH, row*KisTileData::HEIGHT, KisTileData::WIDTH, KisTileData::HEIGHT); QRect cloneTileRect = rect & tileRect; if (cloneTileRect == tileRect) { // Clone whole tile m_hashTable->deleteTile(column, row); srcTile->lockForRead(); KisTileData *td = srcTile->tileData(); KisTileSP clonedTile = new KisTile(column, row, td, m_mementoManager); srcTile->unlock(); m_hashTable->addTile(clonedTile); updateExtent(column, row); } else { const qint32 lineSize = cloneTileRect.width() * pixelSize; qint32 rowsRemaining = cloneTileRect.height(); KisTileDataWrapper tw(this, cloneTileRect.left(), cloneTileRect.top(), KisTileDataWrapper::WRITE); srcTile->lockForRead(); // We suppose that the shift in both tiles is the same const quint8* srcTileIt = srcTile->data() + tw.offset(); quint8* dstTileIt = tw.data(); while (rowsRemaining > 0) { memcpy(dstTileIt, srcTileIt, lineSize); srcTileIt += rowStride; dstTileIt += rowStride; rowsRemaining--; } srcTile->unlock(); } } } } template void KisTiledDataManager::bitBltRoughImpl(KisTiledDataManager *srcDM, const QRect &rect) { QWriteLocker locker(&m_lock); if (rect.isEmpty()) return; qint32 firstColumn = xToCol(rect.left()); qint32 lastColumn = xToCol(rect.right()); qint32 firstRow = yToRow(rect.top()); qint32 lastRow = yToRow(rect.bottom()); for (qint32 row = firstRow; row <= lastRow; ++row) { for (qint32 column = firstColumn; column <= lastColumn; ++column) { /** * We are cloning whole tiles here so let's not be so boring * to check any borders :) */ // this is the only variation in the template KisTileSP srcTile = useOldSrcData ? srcDM->getOldTile(column, row) : srcDM->getTile(column, row, false); m_hashTable->deleteTile(column, row); srcTile->lockForRead(); KisTileData *td = srcTile->tileData(); KisTileSP clonedTile = new KisTile(column, row, td, m_mementoManager); srcTile->unlock(); m_hashTable->addTile(clonedTile); updateExtent(column, row); } } } void KisTiledDataManager::bitBlt(KisTiledDataManager *srcDM, const QRect &rect) { bitBltImpl(srcDM, rect); } void KisTiledDataManager::bitBltOldData(KisTiledDataManager *srcDM, const QRect &rect) { bitBltImpl(srcDM, rect); } void KisTiledDataManager::bitBltRough(KisTiledDataManager *srcDM, const QRect &rect) { bitBltRoughImpl(srcDM, rect); } void KisTiledDataManager::bitBltRoughOldData(KisTiledDataManager *srcDM, const QRect &rect) { bitBltRoughImpl(srcDM, rect); } void KisTiledDataManager::setExtent(qint32 x, qint32 y, qint32 w, qint32 h) { setExtent(QRect(x, y, w, h)); } void KisTiledDataManager::setExtent(QRect newRect) { QRect oldRect = extent(); newRect = newRect.normalized(); // Do nothing if the desired size is bigger than we currently are: // that is handled by the autoextending automatically if (newRect.contains(oldRect)) return; QWriteLocker locker(&m_lock); KisTileSP tile; QRect tileRect; { KisTileHashTableIterator iter(m_hashTable); while (!iter.isDone()) { tile = iter.tile(); tileRect = tile->extent(); if (newRect.contains(tileRect)) { //do nothing ++iter; } else if (newRect.intersects(tileRect)) { QRect intersection = newRect & tileRect; intersection.translate(- tileRect.topLeft()); const qint32 pixelSize = this->pixelSize(); tile->lockForWrite(); quint8* data = tile->data(); quint8* ptr; /* FIXME: make it faster */ for (int y = 0; y < KisTileData::HEIGHT; y++) { for (int x = 0; x < KisTileData::WIDTH; x++) { if (!intersection.contains(x, y)) { ptr = data + pixelSize * (y * KisTileData::WIDTH + x); memcpy(ptr, m_defaultPixel, pixelSize); } } } tile->unlock(); ++iter; } else { iter.deleteCurrent(); } } } recalculateExtent(); } void KisTiledDataManager::recalculateExtent() { m_extentMinX = qint32_MAX; m_extentMinY = qint32_MAX; m_extentMaxX = qint32_MIN; m_extentMaxY = qint32_MIN; KisTileHashTableIterator iter(m_hashTable); KisTileSP tile; while ((tile = iter.tile())) { updateExtent(tile->col(), tile->row()); ++iter; } } void KisTiledDataManager::updateExtent(qint32 col, qint32 row) { const qint32 tileMinX = col * KisTileData::WIDTH; const qint32 tileMinY = row * KisTileData::HEIGHT; const qint32 tileMaxX = tileMinX + KisTileData::WIDTH - 1; const qint32 tileMaxY = tileMinY + KisTileData::HEIGHT - 1; m_extentMinX = qMin(m_extentMinX, tileMinX); m_extentMaxX = qMax(m_extentMaxX, tileMaxX); m_extentMinY = qMin(m_extentMinY, tileMinY); m_extentMaxY = qMax(m_extentMaxY, tileMaxY); } QRect KisTiledDataManager::extentImpl() const { qint32 x = m_extentMinX; qint32 y = m_extentMinY; qint32 w = (m_extentMaxX >= m_extentMinX) ? m_extentMaxX - m_extentMinX + 1 : 0; qint32 h = (m_extentMaxY >= m_extentMinY) ? m_extentMaxY - m_extentMinY + 1 : 0; return QRect(x, y, w, h); } void KisTiledDataManager::extent(qint32 &x, qint32 &y, qint32 &w, qint32 &h) const { QRect rect = extent(); rect.getRect(&x, &y, &w, &h); } QRect KisTiledDataManager::extent() const { QReadLocker locker(&m_lock); return extentImpl(); } QRegion KisTiledDataManager::region() const { QRegion region; KisTileHashTableIterator iter(m_hashTable); KisTileSP tile; while ((tile = iter.tile())) { region += tile->extent(); ++iter; } return region; } void KisTiledDataManager::setPixel(qint32 x, qint32 y, const quint8 * data) { QWriteLocker locker(&m_lock); KisTileDataWrapper tw(this, x, y, KisTileDataWrapper::WRITE); memcpy(tw.data(), data, pixelSize()); } void KisTiledDataManager::writeBytes(const quint8 *data, qint32 x, qint32 y, qint32 width, qint32 height, qint32 dataRowStride) { QWriteLocker locker(&m_lock); // Actial bytes reading/writing is done in private header writeBytesBody(data, x, y, width, height, dataRowStride); } void KisTiledDataManager::readBytes(quint8 *data, qint32 x, qint32 y, qint32 width, qint32 height, qint32 dataRowStride) const { QReadLocker locker(&m_lock); // Actual bytes reading/writing is done in private header readBytesBody(data, x, y, width, height, dataRowStride); } QVector KisTiledDataManager::readPlanarBytes(QVector channelSizes, qint32 x, qint32 y, qint32 width, qint32 height) const { QReadLocker locker(&m_lock); // Actial bytes reading/writing is done in private header return readPlanarBytesBody(channelSizes, x, y, width, height); } void KisTiledDataManager::writePlanarBytes(QVector planes, QVector channelSizes, qint32 x, qint32 y, qint32 width, qint32 height) { QWriteLocker locker(&m_lock); // Actial bytes reading/writing is done in private header bool allChannelsPresent = true; Q_FOREACH (const quint8* plane, planes) { if (!plane) { allChannelsPresent = false; break; } } if (allChannelsPresent) { writePlanarBytesBody(planes, channelSizes, x, y, width, height); } else { writePlanarBytesBody(planes, channelSizes, x, y, width, height); } } qint32 KisTiledDataManager::numContiguousColumns(qint32 x, qint32 minY, qint32 maxY) const { qint32 numColumns; Q_UNUSED(minY); Q_UNUSED(maxY); if (x >= 0) { numColumns = KisTileData::WIDTH - (x % KisTileData::WIDTH); } else { numColumns = ((-x - 1) % KisTileData::WIDTH) + 1; } return numColumns; } qint32 KisTiledDataManager::numContiguousRows(qint32 y, qint32 minX, qint32 maxX) const { qint32 numRows; Q_UNUSED(minX); Q_UNUSED(maxX); if (y >= 0) { numRows = KisTileData::HEIGHT - (y % KisTileData::HEIGHT); } else { numRows = ((-y - 1) % KisTileData::HEIGHT) + 1; } return numRows; } qint32 KisTiledDataManager::rowStride(qint32 x, qint32 y) const { Q_UNUSED(x); Q_UNUSED(y); return KisTileData::WIDTH * pixelSize(); } void KisTiledDataManager::releaseInternalPools() { KisTileData::releaseInternalPools(); }