From 698569f8428ca088f764d704034a1330517b98c0 Mon Sep 17 00:00:00 2001 From: tpearson Date: Sun, 26 Jun 2011 00:41:16 +0000 Subject: Finish rebranding of Krita as Chalk git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/koffice@1238363 283d02a7-25f6-0310-bc7c-ecb5cbfe19da --- chalk/core/tiles/Makefile.am | 23 + chalk/core/tiles/kis_memento.cc | 154 ++++ chalk/core/tiles/kis_memento.h | 147 +++ chalk/core/tiles/kis_tile.cc | 152 ++++ chalk/core/tiles/kis_tile.h | 87 ++ chalk/core/tiles/kis_tile_global.h | 23 + chalk/core/tiles/kis_tiled_random_accessor.cc | 115 +++ chalk/core/tiles/kis_tiled_random_accessor.h | 66 ++ chalk/core/tiles/kis_tileddatamanager.cc | 1044 ++++++++++++++++++++++ chalk/core/tiles/kis_tileddatamanager.h | 233 +++++ chalk/core/tiles/kis_tiledhlineiterator.cc | 213 +++++ chalk/core/tiles/kis_tilediterator.cc | 131 +++ chalk/core/tiles/kis_tilediterator.h | 213 +++++ chalk/core/tiles/kis_tiledrectiterator.cc | 242 +++++ chalk/core/tiles/kis_tiledvlineiterator.cc | 154 ++++ chalk/core/tiles/kis_tilemanager.cc | 578 ++++++++++++ chalk/core/tiles/kis_tilemanager.h | 139 +++ chalk/core/tiles/tests/Makefile.am | 15 + chalk/core/tiles/tests/kis_tiled_data_tester.cpp | 74 ++ chalk/core/tiles/tests/kis_tiled_data_tester.h | 32 + 20 files changed, 3835 insertions(+) create mode 100644 chalk/core/tiles/Makefile.am create mode 100644 chalk/core/tiles/kis_memento.cc create mode 100644 chalk/core/tiles/kis_memento.h create mode 100644 chalk/core/tiles/kis_tile.cc create mode 100644 chalk/core/tiles/kis_tile.h create mode 100644 chalk/core/tiles/kis_tile_global.h create mode 100644 chalk/core/tiles/kis_tiled_random_accessor.cc create mode 100644 chalk/core/tiles/kis_tiled_random_accessor.h create mode 100644 chalk/core/tiles/kis_tileddatamanager.cc create mode 100644 chalk/core/tiles/kis_tileddatamanager.h create mode 100644 chalk/core/tiles/kis_tiledhlineiterator.cc create mode 100644 chalk/core/tiles/kis_tilediterator.cc create mode 100644 chalk/core/tiles/kis_tilediterator.h create mode 100644 chalk/core/tiles/kis_tiledrectiterator.cc create mode 100644 chalk/core/tiles/kis_tiledvlineiterator.cc create mode 100644 chalk/core/tiles/kis_tilemanager.cc create mode 100644 chalk/core/tiles/kis_tilemanager.h create mode 100644 chalk/core/tiles/tests/Makefile.am create mode 100644 chalk/core/tiles/tests/kis_tiled_data_tester.cpp create mode 100644 chalk/core/tiles/tests/kis_tiled_data_tester.h (limited to 'chalk/core/tiles') diff --git a/chalk/core/tiles/Makefile.am b/chalk/core/tiles/Makefile.am new file mode 100644 index 00000000..1e334ab4 --- /dev/null +++ b/chalk/core/tiles/Makefile.am @@ -0,0 +1,23 @@ +if include_kunittest_tests +TESTSDIR = tests +endif + +SUBDIRS = . $(TESTSDIR) + +INCLUDES = -I$(srcdir)/../ \ + -I$(srcdir)/../../sdk \ + $(KOFFICE_INCLUDES) \ + -I$(interfacedir) \ + $(all_includes) + +noinst_LTLIBRARIES = libchalktile.la + +libchalktile_la_SOURCES = kis_tiledvlineiterator.cc kis_tiledhlineiterator.cc \ + kis_tileddatamanager.cc kis_tile.cc kis_tilediterator.cc kis_tiledrectiterator.cc \ + kis_memento.cc kis_tilemanager.cc kis_tiled_random_accessor.cc + +libchalktile_la_METASOURCES = AUTO + +include_HEADERS = \ + kis_tileddatamanager.h +noinst_HEADERS = kis_tiled_random_accessor.h diff --git a/chalk/core/tiles/kis_memento.cc b/chalk/core/tiles/kis_memento.cc new file mode 100644 index 00000000..aaec3724 --- /dev/null +++ b/chalk/core/tiles/kis_memento.cc @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2005 Casper Boemann + * + * 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 "kis_global.h" +#include "kis_memento.h" +#include "kis_tile.h" +#include "kis_tile_global.h" + +KisMemento::KisMemento(TQ_UINT32 pixelSize) : KShared() +{ + m_hashTable = new KisTile * [1024]; + Q_CHECK_PTR(m_hashTable); + + m_redoHashTable = new KisTile * [1024]; + Q_CHECK_PTR(m_redoHashTable); + + for(int i = 0; i < 1024; i++) + { + m_hashTable [i] = 0; + m_redoHashTable [i] = 0; + } + m_numTiles = 0; + m_defPixel = new TQ_UINT8[pixelSize]; + m_redoDefPixel = new TQ_UINT8[pixelSize]; + m_valid = true; +} + +KisMemento::~KisMemento() +{ + // Deep delete every tile + for(int i = 0; i < 1024; i++) + { + deleteAll(m_hashTable[i]); + deleteAll(m_redoHashTable[i]); + } + delete [] m_hashTable; + delete [] m_redoHashTable; + + // Delete defPixel arrays; + delete [] m_defPixel; + delete [] m_redoDefPixel; +} + +KisMemento::DeletedTileList::~DeletedTileList() +{ + clear(); +} + +void KisMemento::DeletedTileList::clear() +{ + // They are not tiles just references. The actual tiles have already been deleted, + // so just delete the references. + + const DeletedTile *deletedTile = m_firstDeletedTile; + + while (deletedTile) + { + const DeletedTile *d = deletedTile; + deletedTile = deletedTile->next(); + delete d; + } + + m_firstDeletedTile = 0; +} + +void KisMemento::deleteAll(KisTile *tile) +{ + while(tile) + { + KisTile *deltile = tile; + tile = tile->getNext(); + delete deltile; + } +} + +void KisMemento::extent(TQ_INT32 &x, TQ_INT32 &y, TQ_INT32 &w, TQ_INT32 &h) const +{ + TQ_INT32 maxX = TQ_INT32_MIN; + TQ_INT32 maxY = TQ_INT32_MIN; + x = TQ_INT32_MAX; + y = TQ_INT32_MAX; + + for(int i = 0; i < 1024; i++) + { + KisTile *tile = m_hashTable[i]; + + while(tile) + { + if(x > tile->getCol() * KisTile::WIDTH) + x = tile->getCol() * KisTile::WIDTH; + if(maxX < (tile->getCol() + 1) * KisTile::WIDTH - 1) + maxX = (tile->getCol() + 1) * KisTile::WIDTH - 1; + if(y > tile->getRow() * KisTile::HEIGHT) + y = tile->getRow() * KisTile::HEIGHT; + if(maxY < (tile->getRow() +1) * KisTile::HEIGHT - 1) + maxY = (tile->getRow() +1) * KisTile::HEIGHT - 1; + + tile = tile->getNext(); + } + } + + if(maxX < x) + w = 0; + else + w = maxX - x +1; + + if(maxY < y) + h = 0; + else + h = maxY - y +1; +} + +TQRect KisMemento::extent() const +{ + TQ_INT32 x; + TQ_INT32 y; + TQ_INT32 w; + TQ_INT32 h; + + extent(x, y, w, h); + + return TQRect(x, y, w, h); +} + +bool KisMemento::containsTile(TQ_INT32 col, TQ_INT32 row, TQ_UINT32 tileHash) const +{ + const KisTile *tile = m_hashTable[tileHash]; + + while (tile != 0) + { + if (tile->getRow() == row && tile->getCol() == col) { + return true; + } + + tile = tile->getNext(); + } + + return false; +} + diff --git a/chalk/core/tiles/kis_memento.h b/chalk/core/tiles/kis_memento.h new file mode 100644 index 00000000..5942dc44 --- /dev/null +++ b/chalk/core/tiles/kis_memento.h @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2005 Casper Boemann + * + * 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. + */ + +#ifndef KIS_MEMENTO_H_ +#define KIS_MEMENTO_H_ + +#include +#include + +#include + +class KisTile; +class KisTiledDataManager; + +class KisMemento; +typedef KSharedPtr KisMementoSP; + +class KisMemento : public KShared +{ +public: + KisMemento(TQ_UINT32 pixelSize); + ~KisMemento(); +/* + // For consolidating transactions + virtual KisTransaction &operator+=(const KisTransaction &) = 0; + // For consolidating transactions + virtual KisTransaction &operator+(const KisTransaction &, + const KisTransaction &) = 0; +*/ + void extent(TQ_INT32 &x, TQ_INT32 &y, TQ_INT32 &w, TQ_INT32 &h) const; + TQRect extent() const; + + bool containsTile(TQ_INT32 col, TQ_INT32 row, TQ_UINT32 tileHash) const; + + // For debugging use + bool valid() const { return m_valid; } + void setInvalid() { m_valid = false; } + +private: + + class DeletedTile { + public: + DeletedTile(TQ_INT32 col, TQ_INT32 row, const DeletedTile *next) + : m_col(col), + m_row(row), + m_next(next) + { + } + + TQ_INT32 col() const { return m_col; } + TQ_INT32 row() const { return m_row; } + const DeletedTile *next() const { return m_next; } + + private: + TQ_INT32 m_col; + TQ_INT32 m_row; + const DeletedTile *m_next; + }; + + class DeletedTileList { + public: + DeletedTileList() + : m_firstDeletedTile(0) + { + } + + ~DeletedTileList(); + + void addTile(TQ_INT32 col, TQ_INT32 row) + { + DeletedTile *d = new DeletedTile(col, row, m_firstDeletedTile); + Q_CHECK_PTR(d); + + m_firstDeletedTile = d; + } + + DeletedTile *firstTile() const + { + return m_firstDeletedTile; + } + + void clear(); + + private: + DeletedTile *m_firstDeletedTile; + }; + + void addTileToDeleteOnRedo(TQ_INT32 col, TQ_INT32 row) + { + m_redoDelTilesList.addTile(col, row); + } + + DeletedTile *tileListToDeleteOnRedo() + { + return m_redoDelTilesList.firstTile(); + } + + void clearTilesToDeleteOnRedo() + { + m_redoDelTilesList.clear(); + } + + void addTileToDeleteOnUndo(TQ_INT32 col, TQ_INT32 row) + { + m_undoDelTilesList.addTile(col, row); + } + + DeletedTile *tileListToDeleteOnUndo() + { + return m_undoDelTilesList.firstTile(); + } + + void clearTilesToDeleteOnUndo() + { + m_undoDelTilesList.clear(); + } + + friend class KisTiledDataManager; + KisTiledDataManager *originator; + KisTile **m_hashTable; + TQ_UINT32 m_numTiles; + KisTile **m_redoHashTable; + DeletedTileList m_redoDelTilesList; + DeletedTileList m_undoDelTilesList; + TQ_UINT8 *m_defPixel; + TQ_UINT8 *m_redoDefPixel; + void deleteAll(KisTile *tile); + + bool m_valid; +}; + +#endif // KIS_MEMENTO_H_ diff --git a/chalk/core/tiles/kis_tile.cc b/chalk/core/tiles/kis_tile.cc new file mode 100644 index 00000000..86d55128 --- /dev/null +++ b/chalk/core/tiles/kis_tile.cc @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2002 Patrick Julien + * + * 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_global.h" +#include "kis_tile.h" +#include "kis_tileddatamanager.h" +#include "kis_tilemanager.h" + +const TQ_INT32 KisTile::WIDTH = 64; +const TQ_INT32 KisTile::HEIGHT = 64; + + +KisTile::KisTile(TQ_INT32 pixelSize, TQ_INT32 col, TQ_INT32 row, const TQ_UINT8 *defPixel) +{ + m_pixelSize = pixelSize; + m_data = 0; + m_nextTile = 0; + m_col = col; + m_row = row; + m_nReadlock = 0; + + allocate(); + + KisTileManager::instance()->registerTile(this); + + setData(defPixel); +} + +KisTile::KisTile(const KisTile& rhs, TQ_INT32 col, TQ_INT32 row) +{ + if (this != &rhs) { + m_pixelSize = rhs.m_pixelSize; + m_data = 0; + m_nextTile = 0; + m_nReadlock = 0; + + allocate(); + + // Assure we have data to copy + rhs.addReader(); + memcpy(m_data, rhs.m_data, WIDTH * HEIGHT * m_pixelSize * sizeof(TQ_UINT8)); + rhs.removeReader(); + + m_col = col; + m_row = row; + + KisTileManager::instance()->registerTile(this); + } +} + +KisTile::KisTile(const KisTile& rhs) +{ + if (this != &rhs) { + m_pixelSize = rhs.m_pixelSize; + m_col = rhs.m_col; + m_row = rhs.m_row; + m_data = 0; + m_nextTile = 0; + m_nReadlock = 0; + + allocate(); + + rhs.addReader(); + memcpy(m_data, rhs.m_data, WIDTH * HEIGHT * m_pixelSize * sizeof(TQ_UINT8)); + rhs.removeReader(); + + KisTileManager::instance()->registerTile(this); + } +} + +KisTile::~KisTile() +{ + KisTileManager::instance()->deregisterTile(this); // goes before the deleting of m_data! + + if (m_data) { +// delete[] m_data; + KisTileManager::instance()->dontNeedTileData(m_data, m_pixelSize); + m_data = 0; + } + assert( !readers() ); +} + +void KisTile::allocate() +{ + if (m_data == 0) { + assert (!readers()); + m_data = KisTileManager::instance()->requestTileData(m_pixelSize); + Q_CHECK_PTR(m_data); + } +} + +void KisTile::setNext(KisTile *n) +{ + m_nextTile = n; +} + +TQ_UINT8 *KisTile::data(TQ_INT32 x, TQ_INT32 y ) const +{ + addReader(); + removeReader(); + + Q_ASSERT(m_data != 0); + if (m_data == 0) return 0; + + return m_data + m_pixelSize * ( y * WIDTH + x ); +} + +void KisTile::setData(const TQ_UINT8 *pixel) +{ + addReader(); + TQ_UINT8 *dst = m_data; + for(int i=0; i ensureTileLoaded(this); + else if (m_nReadlock < 0) { + kdDebug(41000) << m_nReadlock << endl; + assert(0); + } + assert(m_data); +} + +void KisTile::removeReader() const +{ + if (--m_nReadlock == 0) + KisTileManager::instance()->maySwapTile(this); +} diff --git a/chalk/core/tiles/kis_tile.h b/chalk/core/tiles/kis_tile.h new file mode 100644 index 00000000..f2d6f1e9 --- /dev/null +++ b/chalk/core/tiles/kis_tile.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2004 Casper Boemann + * + * 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. + */ +#ifndef KIS_TILE_H_ +#define KIS_TILE_H_ + +#include +#include + +class KisTiledDataManager; +class KisTiledIterator; + +/** + * Provides abstraction to a tile. A tile tqcontains + * a part of a PaintDevice, but only the individual pixels + * are accesable and that only via iterators. + */ +class KisTile { +public: + KisTile(TQ_INT32 pixelSize, TQ_INT32 col, TQ_INT32 row, const TQ_UINT8 *defPixel); + KisTile(const KisTile& rhs, TQ_INT32 col, TQ_INT32 row); + KisTile(const KisTile& rhs); + ~KisTile(); + +public: + void release(); + void allocate(); + + TQ_UINT8 *data(TQ_INT32 xoff, TQ_INT32 yoff) const; + TQ_UINT8 *data() const { return m_data; } + + void setData(const TQ_UINT8 *pixel); + + TQ_INT32 refCount() const; + void ref(); + + TQ_INT32 getRow() const { return m_row; } + TQ_INT32 getCol() const { return m_col; } + + TQRect extent() const { return TQRect(m_col * WIDTH, m_row * HEIGHT, WIDTH, HEIGHT); } + + void setNext(KisTile *); + KisTile *getNext() const { return m_nextTile; } + + // These are const because they don't change the external data the tile represents, + // although they do change internal representations. We need to be able to request + // access to a tile in a const enviroment (like copyconstructor and so)! + void addReader() const; + void removeReader() const; + TQ_INT32 readers() { return m_nReadlock; } + + friend class KisTiledIterator; + friend class KisTiledDataManager; + friend class KisMemento; + friend class KisTileManager; +private: + KisTile& operator=(const KisTile&); + +private: + TQ_UINT8 *m_data; + mutable TQ_INT32 m_nReadlock; + TQ_INT32 m_row; + TQ_INT32 m_col; + TQ_INT32 m_pixelSize; + KisTile *m_nextTile; + +public: + static const TQ_INT32 WIDTH; + static const TQ_INT32 HEIGHT; +}; + +#endif // KIS_TILE_H_ + diff --git a/chalk/core/tiles/kis_tile_global.h b/chalk/core/tiles/kis_tile_global.h new file mode 100644 index 00000000..93052a4f --- /dev/null +++ b/chalk/core/tiles/kis_tile_global.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2005 Boudewijn Rempt + * + * 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. + */ +#ifndef KIS_TILE_GLOBAL_H_ +#define KIS_TILE_GLOBAL_H_ + +#define DBG_AREA_TILES 41000 + +#endif diff --git a/chalk/core/tiles/kis_tiled_random_accessor.cc b/chalk/core/tiles/kis_tiled_random_accessor.cc new file mode 100644 index 00000000..159faf18 --- /dev/null +++ b/chalk/core/tiles/kis_tiled_random_accessor.cc @@ -0,0 +1,115 @@ +/* + * copyright (c) 2006 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., 675 mass ave, cambridge, ma 02139, usa. + */ +#include "kis_tiled_random_accessor.h" + + +const TQ_UINT32 KisTiledRandomAccessor::CACHESIZE = 4; // Define the number of tiles we keep in cache + +KisTiledRandomAccessor::KisTiledRandomAccessor(KisTiledDataManager *ktm, TQ_INT32 x, TQ_INT32 y, bool writable) : m_ktm(ktm), m_tilesCache(new KisTileInfo*[4]), m_tilesCacheSize(0), m_pixelSize (m_ktm->pixelSize()), m_writable(writable) +{ + Q_ASSERT(ktm != 0); + moveTo(x, y); +} + +KisTiledRandomAccessor::~KisTiledRandomAccessor() +{ + for( uint i = 0; i < m_tilesCacheSize; i++) + { + m_tilesCache[i]->tile->removeReader(); + m_tilesCache[i]->oldtile->removeReader(); + delete m_tilesCache[i]; + } + delete m_tilesCache; +} + +void KisTiledRandomAccessor::moveTo(TQ_INT32 x, TQ_INT32 y) +{ + // Look in the cache if the tile if the data is available + for( uint i = 0; i < m_tilesCacheSize; i++) + { + if( x >= m_tilesCache[i]->area_x1 && x <= m_tilesCache[i]->area_x2 && + y >= m_tilesCache[i]->area_y1 && y <= m_tilesCache[i]->area_y2 ) + { + KisTileInfo* kti = m_tilesCache[i]; + TQ_UINT32 offset = x - kti->area_x1 + (y -kti->area_y1) * KisTile::WIDTH; + offset *= m_pixelSize; + m_data = kti->data + offset; + m_oldData = kti->oldData + offset; + if(i > 0) + { + memmove(m_tilesCache+1,m_tilesCache, i * sizeof(KisTileInfo*)); + m_tilesCache[0] = kti; + } + return; + } + } + // The tile wasn't in cache + if(m_tilesCacheSize == KisTiledRandomAccessor::CACHESIZE ) + { // Remove last element of cache + m_tilesCache[CACHESIZE-1]->tile->removeReader(); + m_tilesCache[CACHESIZE-1]->oldtile->removeReader(); + delete m_tilesCache[CACHESIZE-1]; + } else { + m_tilesCacheSize++; + } + TQ_UINT32 col = xToCol( x ); + TQ_UINT32 row = yToRow( y ); + KisTileInfo* kti = fetchTileData(col, row); + TQ_UINT32 offset = x - kti->area_x1 + (y - kti->area_y1) * KisTile::WIDTH; + offset *= m_pixelSize; + m_data = kti->data + offset; + m_oldData = kti->oldData + offset; + memmove(m_tilesCache+1,m_tilesCache, (KisTiledRandomAccessor::CACHESIZE-1) * sizeof(KisTileInfo*)); + m_tilesCache[0] = kti; +} + + +TQ_UINT8 * KisTiledRandomAccessor::rawData() const +{ + return m_data; +} + + +const TQ_UINT8 * KisTiledRandomAccessor::oldRawData() const +{ +#ifdef DEBUG + kdWarning(!m_ktm->hasCurrentMemento(), DBG_AREA_TILES) << "Accessing oldRawData() when no transaction is in progress.\n"; +#endif + return m_oldData; +} + +KisTiledRandomAccessor::KisTileInfo* KisTiledRandomAccessor::fetchTileData(TQ_INT32 col, TQ_INT32 row) +{ + KisTileInfo* kti = new KisTileInfo; + kti->tile = m_ktm->getTile(col, row, m_writable); + + kti->tile->addReader(); + + kti->data = kti->tile->data(); + + kti->area_x1 = col * KisTile::HEIGHT; + kti->area_y1 = row * KisTile::WIDTH; + kti->area_x2 = kti->area_x1 + KisTile::HEIGHT - 2; + kti->area_y2 = kti->area_y1 + KisTile::WIDTH - 2; + + // set old data + kti->oldtile = m_ktm->getOldTile(col, row, kti->tile); + kti->oldtile->addReader(); + kti->oldData = kti->oldtile->data(); + return kti; +} diff --git a/chalk/core/tiles/kis_tiled_random_accessor.h b/chalk/core/tiles/kis_tiled_random_accessor.h new file mode 100644 index 00000000..23768ee6 --- /dev/null +++ b/chalk/core/tiles/kis_tiled_random_accessor.h @@ -0,0 +1,66 @@ +/* + * copyright (c) 2006 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., 675 mass ave, cambridge, ma 02139, usa. + */ +#ifndef KIS_TILED_RANDOM_ACCESSOR_H +#define KIS_TILED_RANDOM_ACCESSOR_H + +#include +#include + +#include + +#include + +class KisTile; + +class KisTiledRandomAccessor : public KShared { + struct KisTileInfo { + KisTile* tile; + KisTile* oldtile; + TQ_UINT8* data; + const TQ_UINT8* oldData; + TQ_INT32 area_x1, area_y1, area_x2, area_y2; + }; + public: + KisTiledRandomAccessor(KisTiledDataManager *ktm, TQ_INT32 x, TQ_INT32 y, bool writable); + ~KisTiledRandomAccessor(); + + + private: + inline TQ_UINT32 xToCol(TQ_UINT32 x) const { if (m_ktm) return m_ktm->xToCol(x); else return 0; }; + inline TQ_UINT32 yToRow(TQ_UINT32 y) const { if (m_ktm) return m_ktm->yToRow(y); else return 0; }; + KisTileInfo* fetchTileData(TQ_INT32 col, TQ_INT32 row); + + public: + /// Move to a given x,y position, fetch tiles and data + void moveTo(TQ_INT32 x, TQ_INT32 y); + TQ_UINT8* rawData() const; + const TQ_UINT8* oldRawData() const; + + private: + KisTiledDataManager *m_ktm; + KisTileInfo** m_tilesCache; + TQ_UINT32 m_tilesCacheSize; + TQ_INT32 m_pixelSize; + TQ_UINT8* m_data; + const TQ_UINT8* m_oldData; + bool m_writable; + static const TQ_UINT32 CACHESIZE; // Define the number of tiles we keep in cache + +}; + +#endif diff --git a/chalk/core/tiles/kis_tileddatamanager.cc b/chalk/core/tiles/kis_tileddatamanager.cc new file mode 100644 index 00000000..629b1b38 --- /dev/null +++ b/chalk/core/tiles/kis_tileddatamanager.cc @@ -0,0 +1,1044 @@ +/* + * Copyright (c) 2004 Casper Boemann + * + * 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 + +#include "kis_global.h" +#include "kis_debug_areas.h" +#include "kis_tileddatamanager.h" +#include "kis_tilediterator.h" +#include "kis_tile.h" +#include "kis_memento.h" +#include "kis_tilemanager.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(TQ_UINT32 pixelSize, const TQ_UINT8 *defPixel) +{ + m_pixelSize = pixelSize; + + m_defPixel = new TQ_UINT8[m_pixelSize]; + Q_CHECK_PTR(m_defPixel); + memcpy(m_defPixel, defPixel, m_pixelSize); + + m_defaultTile = new KisTile(pixelSize,0,0, m_defPixel); + Q_CHECK_PTR(m_defaultTile); + + m_hashTable = new KisTile * [1024]; + Q_CHECK_PTR(m_hashTable); + + for(int i = 0; i < 1024; i++) + m_hashTable [i] = 0; + m_numTiles = 0; + m_currentMemento = 0; + m_extentMinX = TQ_INT32_MAX; + m_extentMinY = TQ_INT32_MAX; + m_extentMaxX = TQ_INT32_MIN; + m_extentMaxY = TQ_INT32_MIN; +} + +KisTiledDataManager::KisTiledDataManager(const KisTiledDataManager & dm) + : KShared() +{ + m_pixelSize = dm.m_pixelSize; + + m_defPixel = new TQ_UINT8[m_pixelSize]; + Q_CHECK_PTR(m_defPixel); + memcpy(m_defPixel, dm.m_defPixel, m_pixelSize); + + m_defaultTile = new KisTile(*dm.m_defaultTile, dm.m_defaultTile->getCol(), dm.m_defaultTile->getRow()); + Q_CHECK_PTR(m_defaultTile); + + m_hashTable = new KisTile * [1024]; + Q_CHECK_PTR(m_hashTable); + + m_numTiles = 0; + m_currentMemento = 0; + m_extentMinX = dm.m_extentMinX; + m_extentMinY = dm.m_extentMinY; + m_extentMaxX = dm.m_extentMaxX; + m_extentMaxY = dm.m_extentMaxY; + + // Deep copy every tile. XXX: Make this copy-on-write! + for(int i = 0; i < 1024; i++) + { + const KisTile *tile = dm.m_hashTable[i]; + + m_hashTable[i] = 0; + + while(tile) + { + KisTile *newtile = new KisTile(*tile, tile->getCol(), tile->getRow()); + Q_CHECK_PTR(newtile); + + newtile->setNext(m_hashTable[i]); + m_hashTable[i] = newtile; + tile = tile->getNext(); + + m_numTiles++; + } + } + +} + +KisTiledDataManager::~KisTiledDataManager() +{ + // Deep delete every tile + for(int i = 0; i < 1024; i++) + { + const KisTile *tile = m_hashTable[i]; + + while(tile) + { + const KisTile *deltile = tile; + tile = tile->getNext(); + delete deltile; + } + } + delete [] m_hashTable; + delete m_defaultTile; + delete [] m_defPixel; +} + +void KisTiledDataManager::setDefaultPixel(const TQ_UINT8 *defPixel) +{ + if (defPixel == 0) return; + + memcpy(m_defPixel, defPixel, m_pixelSize); + + m_defaultTile->setData(m_defPixel); +} + +bool KisTiledDataManager::write(KoStore *store) +{ + + if (store == 0) return false; + //Q_ASSERT(store != 0); + + char str[80]; + + sprintf(str, "%d\n", m_numTiles); + store->write(str,strlen(str)); + + for(int i = 0; i < 1024; i++) + { + const KisTile *tile = m_hashTable[i]; + + while(tile) + { + sprintf(str, "%d,%d,%d,%d\n", tile->getCol() * KisTile::WIDTH, + tile->getRow() * KisTile::HEIGHT, + KisTile::WIDTH, KisTile::HEIGHT); + store->write(str,strlen(str)); + + tile->addReader(); + store->write((char *)tile->m_data, KisTile::HEIGHT * KisTile::WIDTH * m_pixelSize); + tile->removeReader(); + + tile = tile->getNext(); + } + } + + return true; +} +bool KisTiledDataManager::read(KoStore *store) +{ + if (store == 0) return false; + //Q_ASSERT(store != 0); + + char str[80]; + TQ_INT32 x,y,w,h; + + TQIODevice *stream = store->device(); + if (stream == 0) return false; + //Q_ASSERT(stream != 0); + + stream->readLine(str, 79); + + sscanf(str,"%u",&m_numTiles); + + for(TQ_UINT32 i = 0; i < m_numTiles; i++) + { + stream->readLine(str, 79); + sscanf(str,"%d,%d,%d,%d",&x,&y,&w,&h); + + // the following is only correct as long as tile size is not changed + // The first time we change tilesize the dimensions just read needs to be respected + // but for now we just assume that tiles are the same size as ever. + TQ_INT32 row = yToRow(y); + TQ_INT32 col = xToCol(x); + TQ_UINT32 tileHash = calcTileHash(col, row); + + KisTile *tile = new KisTile(m_pixelSize, col, row, m_defPixel); + Q_CHECK_PTR(tile); + + updateExtent(col,row); + + tile->addReader(); + store->read((char *)tile->m_data, KisTile::HEIGHT * KisTile::WIDTH * m_pixelSize); + tile->removeReader(); + + tile->setNext(m_hashTable[tileHash]); + m_hashTable[tileHash] = tile; + } + return true; +} + +void KisTiledDataManager::extent(TQ_INT32 &x, TQ_INT32 &y, TQ_INT32 &w, TQ_INT32 &h) const +{ + x = m_extentMinX; + y = m_extentMinY; + + if (m_extentMaxX >= m_extentMinX) { + w = m_extentMaxX - m_extentMinX + 1; + } else { + w = 0; + } + + if (m_extentMaxY >= m_extentMinY) { + h = m_extentMaxY - m_extentMinY + 1; + } else { + h = 0; + } +} + +TQRect KisTiledDataManager::extent() const +{ + TQ_INT32 x; + TQ_INT32 y; + TQ_INT32 w; + TQ_INT32 h; + + extent(x, y, w, h); + + return TQRect(x, y, w, h); +} + +void KisTiledDataManager::setExtent(TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, TQ_INT32 h) +{ + TQRect newRect = TQRect(x, y, w, h).normalize(); + //printRect("newRect", newRect); + TQRect oldRect = TQRect(m_extentMinX, m_extentMinY, m_extentMaxX - m_extentMinX + 1, m_extentMaxY - m_extentMinY + 1).normalize(); + //printRect("oldRect", oldRect); + + // Do nothing if the desired size is bigger than we currently are: that is handled by the autoextending automatically + if (newRect.tqcontains(oldRect)) return; + + // Loop through all tiles, if a tile is wholly outside the extent, add to the memento, then delete it, + // if the tile is partially outside the extent, clear the outside pixels to the default pixel. + for(int tileHash = 0; tileHash < 1024; tileHash++) + { + KisTile *tile = m_hashTable[tileHash]; + KisTile *previousTile = 0; + + while(tile) + { + TQRect tileRect = TQRect(tile->getCol() * KisTile::WIDTH, tile->getRow() * KisTile::HEIGHT, KisTile::WIDTH, KisTile::HEIGHT); + //printRect("tileRect", tileRect); + + if (newRect.tqcontains(tileRect)) { + // Completely inside, do nothing + previousTile = tile; + tile = tile->getNext(); + } + else { + ensureTileMementoed(tile->getCol(), tile->getRow(), tileHash, tile); + + if (newRect.intersects(tileRect)) { + + // Create the intersection of the tile and new rect + TQRect intersection = newRect.intersect(tileRect); + //printRect("intersection", intersection); + intersection.setRect(intersection.x() - tileRect.x(), intersection.y() - tileRect.y(), intersection.width(), intersection.height()); + + // This can be done a lot more efficiently, no doubt, by clearing runs of pixels to the left and the right of + // the intersecting line. + tile->addReader(); + for (int y = 0; y < KisTile::HEIGHT; ++y) { + for (int x = 0; x < KisTile::WIDTH; ++x) { + if (!intersection.tqcontains(x,y)) { + TQ_UINT8 * ptr = tile->data(x, y); + memcpy(ptr, m_defPixel, m_pixelSize); + } + } + } + tile->removeReader(); + previousTile = tile; + tile = tile->getNext(); + } + else { + KisTile *deltile = tile; + tile = tile->getNext(); + + m_numTiles--; + + if (previousTile) + previousTile->setNext(tile); + else + m_hashTable[tileHash] = tile; + delete deltile; + } + } + } + } + + // Set the extent correctly + m_extentMinX = x; + m_extentMinY = y; + m_extentMaxX = x + w - 1; + m_extentMaxY = y + h - 1; +} + +void KisTiledDataManager::recalculateExtent() +{ + m_extentMinX = TQ_INT32_MAX; + m_extentMinY = TQ_INT32_MAX; + m_extentMaxX = TQ_INT32_MIN; + m_extentMaxY = TQ_INT32_MIN; + + // Loop through all tiles. + for (int tileHash = 0; tileHash < 1024; tileHash++) + { + const KisTile *tile = m_hashTable[tileHash]; + + while (tile) + { + updateExtent(tile->getCol(), tile->getRow()); + tile = tile->getNext(); + } + } +} + +void KisTiledDataManager::clear(TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, TQ_INT32 h, TQ_UINT8 clearValue) +{ + if (w < 1 || h < 1) { + return; + } + + TQ_INT32 firstColumn = xToCol(x); + TQ_INT32 lastColumn = xToCol(x + w - 1); + + TQ_INT32 firstRow = yToRow(y); + TQ_INT32 lastRow = yToRow(y + h - 1); + + TQRect clearRect(x, y, w, h); + + const TQ_UINT32 rowStride = KisTile::WIDTH * m_pixelSize; + + for (TQ_INT32 row = firstRow; row <= lastRow; ++row) { + for (TQ_INT32 column = firstColumn; column <= lastColumn; ++column) { + + KisTile *tile = getTile(column, row, true); + TQRect tileRect = tile->extent(); + + TQRect clearTileRect = clearRect & tileRect; + + tile->addReader(); + if (clearTileRect == tileRect) { + // Clear whole tile + memset(tile->data(), clearValue, KisTile::WIDTH * KisTile::HEIGHT * m_pixelSize); + } else { + + TQ_UINT32 rowsRemaining = clearTileRect.height(); + TQ_UINT8 *dst = tile->data(clearTileRect.x() - tileRect.x(), clearTileRect.y() - tileRect.y()); + + while (rowsRemaining > 0) { + memset(dst, clearValue, clearTileRect.width() * m_pixelSize); + dst += rowStride; + --rowsRemaining; + } + } + tile->removeReader(); + } + } +} + +void KisTiledDataManager::clear(TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, TQ_INT32 h, const TQ_UINT8 *clearPixel) +{ + Q_ASSERT(clearPixel != 0); + + if (clearPixel == 0 || w < 1 || h < 1) { + return; + } + + bool pixelBytesAreTheSame = true; + + for (TQ_UINT32 i = 0; i < m_pixelSize; ++i) { + if (clearPixel[i] != clearPixel[0]) { + pixelBytesAreTheSame = false; + break; + } + } + + if (pixelBytesAreTheSame) { + clear(x, y, w, h, clearPixel[0]); + } else { + + TQ_INT32 firstColumn = xToCol(x); + TQ_INT32 lastColumn = xToCol(x + w - 1); + + TQ_INT32 firstRow = yToRow(y); + TQ_INT32 lastRow = yToRow(y + h - 1); + + TQRect clearRect(x, y, w, h); + + const TQ_UINT32 rowStride = KisTile::WIDTH * m_pixelSize; + + TQ_UINT8 *clearPixelData = 0; + + if (w >= KisTile::WIDTH && h >= KisTile::HEIGHT) { + + // There might be a whole tile to be cleared so generate a cleared tile. + clearPixelData = new TQ_UINT8[KisTile::WIDTH * KisTile::HEIGHT * m_pixelSize]; + + TQ_UINT8 *dst = clearPixelData; + TQ_UINT32 pixelsRemaining = KisTile::WIDTH; + + // Generate one row + while (pixelsRemaining > 0) { + memcpy(dst, clearPixel, m_pixelSize); + dst += m_pixelSize; + --pixelsRemaining; + } + + TQ_UINT32 rowsRemaining = KisTile::HEIGHT - 1; + + // Copy to the rest of the rows. + while (rowsRemaining > 0) { + memcpy(dst, clearPixelData, rowStride); + dst += rowStride; + --rowsRemaining; + } + + } else { + + // Generate one row + TQ_UINT32 maxRunLength = TQMIN(w, KisTile::WIDTH); + + clearPixelData = new TQ_UINT8[maxRunLength * m_pixelSize]; + + TQ_UINT8 *dst = clearPixelData; + TQ_UINT32 pixelsRemaining = maxRunLength; + + while (pixelsRemaining > 0) { + memcpy(dst, clearPixel, m_pixelSize); + dst += m_pixelSize; + --pixelsRemaining; + } + } + + for (TQ_INT32 row = firstRow; row <= lastRow; ++row) { + for (TQ_INT32 column = firstColumn; column <= lastColumn; ++column) { + + KisTile *tile = getTile(column, row, true); + TQRect tileRect = tile->extent(); + + TQRect clearTileRect = clearRect & tileRect; + + if (clearTileRect == tileRect) { + + // Clear whole tile + tile->addReader(); + memcpy(tile->data(), clearPixelData, KisTile::WIDTH * KisTile::HEIGHT * m_pixelSize); + tile->removeReader(); + } else { + + TQ_UINT32 rowsRemaining = clearTileRect.height(); + tile->addReader(); + TQ_UINT8 *dst = tile->data(clearTileRect.x() - tileRect.x(), clearTileRect.y() - tileRect.y()); + + while (rowsRemaining > 0) { + memcpy(dst, clearPixelData, clearTileRect.width() * m_pixelSize); + dst += rowStride; + --rowsRemaining; + } + tile->removeReader(); + } + } + } + + delete [] clearPixelData; + } +} + +void KisTiledDataManager::clear() +{ + // Loop through all tiles, add to the memento, then delete it, + for(int tileHash = 0; tileHash < 1024; tileHash++) + { + const KisTile *tile = m_hashTable[tileHash]; + + while(tile) + { + ensureTileMementoed(tile->getCol(), tile->getRow(), tileHash, tile); + + const KisTile *deltile = tile; + tile = tile->getNext(); + + delete deltile; + } + m_hashTable[tileHash] = 0; + } + + m_numTiles = 0; + + // Set the extent correctly + m_extentMinX = TQ_INT32_MAX; + m_extentMinY = TQ_INT32_MAX; + m_extentMaxX = TQ_INT32_MIN; + m_extentMaxY = TQ_INT32_MIN; +} + +void KisTiledDataManager::paste(KisDataManagerSP data, TQ_INT32 sx, TQ_INT32 sy, TQ_INT32 dx, TQ_INT32 dy, + TQ_INT32 w, TQ_INT32 h) +{ + //CBR_MISSING + sx=sy=dx=dy=w=h;data=0; +} + + +TQ_UINT32 KisTiledDataManager::calcTileHash(TQ_INT32 col, TQ_INT32 row) +{ + return ((row << 5) + (col & 0x1F)) & 0x3FF; +} + +KisMementoSP KisTiledDataManager::getMemento() +{ + m_currentMemento = new KisMemento(m_pixelSize); + Q_CHECK_PTR(m_currentMemento); + + memcpy(m_currentMemento->m_defPixel, m_defPixel, m_pixelSize); + + return m_currentMemento; +} + +void KisTiledDataManager::rollback(KisMementoSP memento) +{ + if (memento == 0) return; + //Q_ASSERT(memento != 0); + + if (m_currentMemento != 0) { + // Undo means our current memento is no longer valid so remove it. + m_currentMemento = 0; + } + + // Rollback means restoring all of the tiles in the memento to our hashtable. + + // But first clear the memento redo hashtable. + // This is nessesary as new changes might have been done since last rollback (automatic filters) + for(int i = 0; i < 1024; i++) + { + memento->deleteAll(memento->m_redoHashTable[i]); + memento->m_redoHashTable[i]=0; + } + + // Also clear the table of deleted tiles + memento->clearTilesToDeleteOnRedo(); + + // Now on to the real rollback + + memcpy(memento->m_redoDefPixel, m_defPixel, m_pixelSize); + setDefaultPixel(memento->m_defPixel); + + for(int i = 0; i < 1024; i++) + { + KisTile *tile = memento->m_hashTable[i]; + + while(tile) + { + // The memento has a tile stored that we need to roll back + // Now find the corresponding one in our hashtable + KisTile *curTile = m_hashTable[i]; + KisTile *preTile = 0; + while(curTile) + { + if(curTile->getRow() == tile->getRow() && curTile->getCol() == tile->getCol()) + { + break; + } + preTile = curTile; + curTile = curTile->getNext(); + } + + if(curTile) + { + // Remove it from our hashtable + if(preTile) + preTile->setNext(curTile->getNext()); + else + m_hashTable[i]= curTile->getNext(); + + m_numTiles--; + + // And put it in the redo hashtable of the memento + curTile->setNext(memento->m_redoHashTable[i]); + memento->m_redoHashTable[i] = curTile; + } + else + { + memento->addTileToDeleteOnRedo(tile->getCol(), tile->getRow()); + // As we are pratically adding a new tile we need to update the extent + updateExtent(tile->getCol(), tile->getRow()); + } + + // Put a copy of the memento tile into our hashtable + curTile = new KisTile(*tile); + Q_CHECK_PTR(curTile); + m_numTiles++; + + curTile->setNext(m_hashTable[i]); + m_hashTable[i] = curTile; + + tile = tile->getNext(); + } + } + + if (memento->tileListToDeleteOnUndo() != 0) { + // XXX: We currently add these tiles above, only to delete them again here. + deleteTiles(memento->tileListToDeleteOnUndo()); + } +} + +void KisTiledDataManager::rollforward(KisMementoSP memento) +{ + if (memento == 0) return; + //Q_ASSERT(memento != 0); + + if (m_currentMemento != 0) { + // Redo means our current memento is no longer valid so remove it. + m_currentMemento = 0; + } + + // Rollforward means restoring all of the tiles in the memento's redo to our hashtable. + + setDefaultPixel(memento->m_redoDefPixel); + + for(int i = 0; i < 1024; i++) + { + KisTile *tile = memento->m_redoHashTable[i]; + + while(tile) + { + // The memento has a tile stored that we need to roll forward + // Now find the corresponding one in our hashtable + KisTile *curTile = m_hashTable[i]; + KisTile *preTile = 0; + while(curTile) + { + if(curTile->getRow() == tile->getRow() && curTile->getCol() == tile->getCol()) + { + break; + } + preTile = curTile; + curTile = curTile->getNext(); + } + + if (curTile) + { + // Remove it from our hashtable + if(preTile) + preTile->setNext(curTile->getNext()); + else + m_hashTable[i]= curTile->getNext(); + + // And delete it (it's equal to the one stored in the memento's undo) + m_numTiles--; + delete curTile; + } + + // Put a copy of the memento tile into our hashtable + curTile = new KisTile(*tile); + Q_CHECK_PTR(curTile); + + curTile->setNext(m_hashTable[i]); + m_hashTable[i] = curTile; + m_numTiles++; + updateExtent(curTile->getCol(), curTile->getRow()); + + tile = tile->getNext(); + } + } + + // Roll forward also means re-deleting the tiles that was deleted but restored by the undo + if (memento->tileListToDeleteOnRedo() != 0) { + deleteTiles(memento->tileListToDeleteOnRedo()); + } +} + +void KisTiledDataManager::deleteTiles(const KisMemento::DeletedTile *d) +{ + while (d) + { + TQ_UINT32 tileHash = calcTileHash(d->col(), d->row()); + KisTile *curTile = m_hashTable[tileHash]; + KisTile *preTile = 0; + while(curTile) + { + if(curTile->getRow() == d->row() && curTile->getCol() == d->col()) + { + break; + } + preTile = curTile; + curTile = curTile->getNext(); + } + if (curTile) { + // Remove it from our hashtable + if(preTile) + preTile->setNext(curTile->getNext()); + else + m_hashTable[tileHash] = curTile->getNext(); + + // And delete it (it's equal to the one stored in the memento's undo) + m_numTiles--; + delete curTile; + } + d = d->next(); + } + + recalculateExtent(); +} + +void KisTiledDataManager::ensureTileMementoed(TQ_INT32 col, TQ_INT32 row, TQ_UINT32 tileHash, const KisTile *refTile) +{ + if (refTile == 0) return; + //Q_ASSERT(refTile != 0); + + // Basically we search for the tile in the current memento, and if it's already there we do nothing, otherwise + // we make a copy of the tile and put it in the current memento + + if(!m_currentMemento) + return; + + KisTile *tile = m_currentMemento->m_hashTable[tileHash]; + while(tile != 0) + { + if(tile->getRow() == row && tile->getCol() == col) + break; + + tile = tile->getNext(); + } + if(tile) + return; // it has allready been stored + + tile = new KisTile(*refTile); + Q_CHECK_PTR(tile); + + tile->setNext(m_currentMemento->m_hashTable[tileHash]); + m_currentMemento->m_hashTable[tileHash] = tile; + m_currentMemento->m_numTiles++; +} + +void KisTiledDataManager::updateExtent(TQ_INT32 col, TQ_INT32 row) +{ + if(m_extentMinX > col * KisTile::WIDTH) + m_extentMinX = col * KisTile::WIDTH; + if(m_extentMaxX < (col+1) * KisTile::WIDTH - 1) + m_extentMaxX = (col+1) * KisTile::WIDTH - 1; + if(m_extentMinY > row * KisTile::HEIGHT) + m_extentMinY = row * KisTile::HEIGHT; + if(m_extentMaxY < (row+1) * KisTile::HEIGHT - 1) + m_extentMaxY = (row+1) * KisTile::HEIGHT - 1; +} + +KisTile *KisTiledDataManager::getTile(TQ_INT32 col, TQ_INT32 row, bool writeAccess) +{ + TQ_UINT32 tileHash = calcTileHash(col, row); + + // Lookup tile in hash table + KisTile *tile = m_hashTable[tileHash]; + while(tile != 0) + { + if(tile->getRow() == row && tile->getCol() == col) + break; + + tile = tile->getNext(); + } + + // Might not have been created yet + if(!tile) + { + if(writeAccess) + { + // Create a new tile + tile = new KisTile(*m_defaultTile, col, row); + Q_CHECK_PTR(tile); + + tile->setNext(m_hashTable[tileHash]); + m_hashTable[tileHash] = tile; + m_numTiles++; + updateExtent(col, row); + + if (m_currentMemento && !m_currentMemento->containsTile(col, row, tileHash)) { + m_currentMemento->addTileToDeleteOnUndo(col, row); + } + } + else + // If only read access then it's enough to share a default tile + tile = m_defaultTile; + } + + if(writeAccess) + ensureTileMementoed(col, row, tileHash, tile); + + return tile; +} + +KisTile *KisTiledDataManager::getOldTile(TQ_INT32 col, TQ_INT32 row, KisTile *def) +{ + KisTile *tile = 0; + + // Lookup tile in hash table of current memento + if (m_currentMemento) + { + if (!m_currentMemento->valid()) return def; + //Q_ASSERT(m_currentMemento->valid()); + + TQ_UINT32 tileHash = calcTileHash(col, row); + tile = m_currentMemento->m_hashTable[tileHash]; + while (tile != 0) + { + if (tile->getRow() == row && tile->getCol() == col) + break; + + tile = tile->getNext(); + } + } + + if (!tile) + tile = def; + + return tile; +} + +TQ_UINT8* KisTiledDataManager::pixelPtr(TQ_INT32 x, TQ_INT32 y, bool writable) +{ + // Ahem, this is a bit not as good. The point is, this function needs the tile data, + // but it might be swapped out. This code swaps it in, but at function exit it might + // be swapped out again! THIS MAKES THE RETURNED POINTER TQUITE VOLATILE + return pixelPtrSafe(x, y, writable) -> data(); +} + +KisTileDataWrapperSP KisTiledDataManager::pixelPtrSafe(TQ_INT32 x, TQ_INT32 y, bool writable) { + TQ_INT32 row = yToRow(y); + TQ_INT32 col = xToCol(x); + + // calc limits within the tile + TQ_INT32 yInTile = y - row * KisTile::HEIGHT; + TQ_INT32 xInTile = x - col * KisTile::WIDTH; + TQ_INT32 offset = m_pixelSize * (yInTile * KisTile::WIDTH + xInTile); + + KisTile *tile = getTile(col, row, writable); + + return new KisTileDataWrapper(tile, offset); +} + +const TQ_UINT8* KisTiledDataManager::pixel(TQ_INT32 x, TQ_INT32 y) +{ + return pixelPtr(x, y, false); +} + +TQ_UINT8* KisTiledDataManager::writablePixel(TQ_INT32 x, TQ_INT32 y) +{ + return pixelPtr(x, y, true); +} + +void KisTiledDataManager::setPixel(TQ_INT32 x, TQ_INT32 y, const TQ_UINT8 * data) +{ + TQ_UINT8 *pixel = pixelPtr(x, y, true); + memcpy(pixel, data, m_pixelSize); +} + + +void KisTiledDataManager::readBytes(TQ_UINT8 * data, + TQ_INT32 x, TQ_INT32 y, + TQ_INT32 w, TQ_INT32 h) +{ + if (data == 0) return; + //Q_ASSERT(data != 0); + if (w < 0) + w = 0; + + if (h < 0) + h = 0; + + TQ_INT32 dstY = 0; + TQ_INT32 srcY = y; + TQ_INT32 rowsRemaining = h; + + while (rowsRemaining > 0) { + + TQ_INT32 dstX = 0; + TQ_INT32 srcX = x; + TQ_INT32 columnsRemaining = w; + TQ_INT32 numContiguousSrcRows = numContiguousRows(srcY, srcX, srcX + w - 1); + + TQ_INT32 rows = TQMIN(numContiguousSrcRows, rowsRemaining); + + while (columnsRemaining > 0) { + + TQ_INT32 numContiguousSrcColumns = numContiguousColumns(srcX, srcY, srcY + rows - 1); + + TQ_INT32 columns = TQMIN(numContiguousSrcColumns, columnsRemaining); + + KisTileDataWrapperSP tileData = pixelPtrSafe(srcX, srcY, false); + const TQ_UINT8 *srcData = tileData -> data(); + TQ_INT32 srcRowStride = rowStride(srcX, srcY); + + TQ_UINT8 *dstData = data + ((dstX + (dstY * w)) * m_pixelSize); + TQ_INT32 dstRowStride = w * m_pixelSize; + + for (TQ_INT32 row = 0; row < rows; row++) { + memcpy(dstData, srcData, columns * m_pixelSize); + dstData += dstRowStride; + srcData += srcRowStride; + } + + srcX += columns; + dstX += columns; + columnsRemaining -= columns; + } + + srcY += rows; + dstY += rows; + rowsRemaining -= rows; + } + +} + + +void KisTiledDataManager::writeBytes(const TQ_UINT8 * bytes, + TQ_INT32 x, TQ_INT32 y, + TQ_INT32 w, TQ_INT32 h) +{ + if (bytes == 0) return; + //Q_ASSERT(bytes != 0); + + // XXX: Is this correct? + if (w < 0) + w = 0; + + if (h < 0) + h = 0; + + TQ_INT32 srcY = 0; + TQ_INT32 dstY = y; + TQ_INT32 rowsRemaining = h; + + while (rowsRemaining > 0) { + + TQ_INT32 srcX = 0; + TQ_INT32 dstX = x; + TQ_INT32 columnsRemaining = w; + TQ_INT32 numContiguousdstRows = numContiguousRows(dstY, dstX, dstX + w - 1); + + TQ_INT32 rows = TQMIN(numContiguousdstRows, rowsRemaining); + + while (columnsRemaining > 0) { + + TQ_INT32 numContiguousdstColumns = numContiguousColumns(dstX, dstY, dstY + rows - 1); + + TQ_INT32 columns = TQMIN(numContiguousdstColumns, columnsRemaining); + + //TQ_UINT8 *dstData = writablePixel(dstX, dstY); + KisTileDataWrapperSP tileData = pixelPtrSafe(dstX, dstY, true); + TQ_UINT8 *dstData = tileData->data(); + TQ_INT32 dstRowStride = rowStride(dstX, dstY); + + const TQ_UINT8 *srcData = bytes + ((srcX + (srcY * w)) * m_pixelSize); + TQ_INT32 srcRowStride = w * m_pixelSize; + + for (TQ_INT32 row = 0; row < rows; row++) { + memcpy(dstData, srcData, columns * m_pixelSize); + srcData += srcRowStride; + dstData += dstRowStride; + } + + dstX += columns; + srcX += columns; + columnsRemaining -= columns; + } + + dstY += rows; + srcY += rows; + rowsRemaining -= rows; + } +} + +TQ_INT32 KisTiledDataManager::numContiguousColumns(TQ_INT32 x, TQ_INT32 minY, TQ_INT32 maxY) +{ + TQ_INT32 numColumns; + + Q_UNUSED(minY); + Q_UNUSED(maxY); + + if (x >= 0) { + numColumns = KisTile::WIDTH - (x % KisTile::WIDTH); + } else { + numColumns = ((-x - 1) % KisTile::WIDTH) + 1; + } + + return numColumns; +} + +TQ_INT32 KisTiledDataManager::numContiguousRows(TQ_INT32 y, TQ_INT32 minX, TQ_INT32 maxX) +{ + TQ_INT32 numRows; + + Q_UNUSED(minX); + Q_UNUSED(maxX); + + if (y >= 0) { + numRows = KisTile::HEIGHT - (y % KisTile::HEIGHT); + } else { + numRows = ((-y - 1) % KisTile::HEIGHT) + 1; + } + + return numRows; +} + +TQ_INT32 KisTiledDataManager::rowStride(TQ_INT32 x, TQ_INT32 y) +{ + Q_UNUSED(x); + Q_UNUSED(y); + + return KisTile::WIDTH * m_pixelSize; +} + +TQ_INT32 KisTiledDataManager::numTiles(void) const +{ + return m_numTiles; +} + +KisTileDataWrapper::KisTileDataWrapper(KisTile* tile, TQ_INT32 offset) + : m_tile(tile), m_offset(offset) +{ + m_tile->addReader(); +} + +KisTileDataWrapper::~KisTileDataWrapper() +{ + m_tile->removeReader(); +} diff --git a/chalk/core/tiles/kis_tileddatamanager.h b/chalk/core/tiles/kis_tileddatamanager.h new file mode 100644 index 00000000..20d78085 --- /dev/null +++ b/chalk/core/tiles/kis_tileddatamanager.h @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2004 Boudewijn Rempt + * + * 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. + */ +#ifndef KIS_TILEDDATAMANAGER_H_ +#define KIS_TILEDDATAMANAGER_H_ + +#include +#include + +#include + +#include "kis_tile_global.h" +#include "kis_tile.h" +#include "kis_memento.h" + +class KisTiledDataManager; +typedef KSharedPtr KisTiledDataManagerSP; + +class KisDataManager; +typedef KSharedPtr KisDataManagerSP; + +class KisTiledIterator; +class KisTiledRandomAccessor; +class KoStore; + +class KisTileDataWrapper : public KShared { + KisTile* m_tile; + TQ_INT32 m_offset; +public: + KisTileDataWrapper(KisTile* tile, TQ_INT32 offset); + virtual ~KisTileDataWrapper(); + TQ_UINT8* data() const { return m_tile->data() + m_offset; } +}; + +typedef KSharedPtr KisTileDataWrapperSP; + +/** + * KisTiledDataManager implements the interface that KisDataManager defines + * + * The interface definition is enforced by KisDataManager calling all the methods + * which must also be defined in KisTiledDataManager. It is not allowed to change the interface + * as other datamangers may also rely on the same interface. + * + * * Storing undo/redo data + * * Offering ordered and unordered iterators over rects of pixels + * * (eventually) efficiently loading and saving data in a format + * that may allow deferred loading. + * + * A datamanager knows nothing about the type of pixel data except + * how many TQ_UINT8's a single pixel takes. + */ + +class KisTiledDataManager : public KShared { + +protected: + KisTiledDataManager(TQ_UINT32 pixelSize, const TQ_UINT8 *defPixel); + ~KisTiledDataManager(); + KisTiledDataManager(const KisTiledDataManager &dm); + KisTiledDataManager & operator=(const KisTiledDataManager &dm); + + +protected: + // Allow the baseclass of iterators acces to the interior + // derived iterator classes must go through KisTiledIterator + friend class KisTiledIterator; + friend class KisTiledRandomAccessor; + +protected: + + void setDefaultPixel(const TQ_UINT8 *defPixel); + const TQ_UINT8 * defaultPixel() const { return m_defPixel;}; + + KisMementoSP getMemento(); + void rollback(KisMementoSP memento); + void rollforward(KisMementoSP memento); + + // For debugging use. + bool hasCurrentMemento() const { return m_currentMemento != 0; } + +protected: + /** + * Reads and writes the tiles from/onto a KoStore (which is simply a file within a zip file) + * + */ + bool write(KoStore *store); + bool read(KoStore *store); + +protected: + + TQ_UINT32 pixelSize(); + + void extent(TQ_INT32 &x, TQ_INT32 &y, TQ_INT32 &w, TQ_INT32 &h) const; + TQRect extent() const; + + void setExtent(TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, TQ_INT32 h); + +protected: + + void clear(TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, TQ_INT32 h, TQ_UINT8 clearValue); + void clear(TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, TQ_INT32 h, const TQ_UINT8 *clearPixel); + void clear(); + + +protected: + + void paste(KisDataManagerSP data, TQ_INT32 sx, TQ_INT32 sy, TQ_INT32 dx, TQ_INT32 dy, + TQ_INT32 w, TQ_INT32 h); + + +protected: + + + /** + * Get a read-only pointer to pixel (x, y). + */ + const TQ_UINT8* pixel(TQ_INT32 x, TQ_INT32 y); + + /** + * Get a read-write pointer to pixel (x, y). + */ + TQ_UINT8* writablePixel(TQ_INT32 x, TQ_INT32 y); + + /** + * write the specified data to x, y. There is no checking on pixelSize! + */ + void setPixel(TQ_INT32 x, TQ_INT32 y, const TQ_UINT8 * data); + + + /** + * Copy the bytes in the specified rect to a vector. The caller is responsible + * for managing the vector. + */ + void readBytes(TQ_UINT8 * bytes, + TQ_INT32 x, TQ_INT32 y, + TQ_INT32 w, TQ_INT32 h); + /** + * Copy the bytes in the vector to the specified rect. If there are bytes left + * in the vector after filling the rect, they will be ignored. If there are + * not enough bytes, the rest of the rect will be filled with the default value + * given (by default, 0); + */ + void writeBytes(const TQ_UINT8 * bytes, + TQ_INT32 x, TQ_INT32 y, + TQ_INT32 w, TQ_INT32 h); + + /// Get the number of contiguous columns starting at x, valid for all values + /// of y between minY and maxY. + TQ_INT32 numContiguousColumns(TQ_INT32 x, TQ_INT32 minY, TQ_INT32 maxY); + + /// Get the number of contiguous rows starting at y, valid for all values + /// of x between minX and maxX. + TQ_INT32 numContiguousRows(TQ_INT32 y, TQ_INT32 minX, TQ_INT32 maxX); + + /// Get the row stride at pixel (x, y). This is the number of bytes to add to a + /// pointer to pixel (x, y) to access (x, y + 1). + TQ_INT32 rowStride(TQ_INT32 x, TQ_INT32 y); + + // For debugging use + TQ_INT32 numTiles() const; + +private: + + TQ_UINT32 m_pixelSize; + TQ_UINT32 m_numTiles; + KisTile *m_defaultTile; + KisTile **m_hashTable; + KisMementoSP m_currentMemento; + TQ_INT32 m_extentMinX; + TQ_INT32 m_extentMinY; + TQ_INT32 m_extentMaxX; + TQ_INT32 m_extentMaxY; + TQ_UINT8 *m_defPixel; + +private: + + void ensureTileMementoed(TQ_INT32 col, TQ_INT32 row, TQ_UINT32 tileHash, const KisTile *refTile); + KisTile *getOldTile(TQ_INT32 col, TQ_INT32 row, KisTile *def); + KisTile *getTile(TQ_INT32 col, TQ_INT32 row, bool writeAccess); + TQ_UINT32 calcTileHash(TQ_INT32 col, TQ_INT32 row); + void updateExtent(TQ_INT32 col, TQ_INT32 row); + void recalculateExtent(); + void deleteTiles(const KisMemento::DeletedTile *deletedTileList); + TQ_INT32 xToCol(TQ_INT32 x) const; + TQ_INT32 yToRow(TQ_INT32 y) const; + void getContiguousColumnsAndRows(TQ_INT32 x, TQ_INT32 y, TQ_INT32 *columns, TQ_INT32 *rows); + TQ_UINT8* pixelPtr(TQ_INT32 x, TQ_INT32 y, bool writable); + KisTileDataWrapperSP pixelPtrSafe(TQ_INT32 x, TQ_INT32 y, bool writable); +}; + + +inline TQ_UINT32 KisTiledDataManager::pixelSize() +{ + return m_pixelSize; +} + +inline TQ_INT32 KisTiledDataManager::xToCol(TQ_INT32 x) const +{ + if (x >= 0) { + return x / KisTile::WIDTH; + } else { + return -(((-x - 1) / KisTile::WIDTH) + 1); + } +} + +inline TQ_INT32 KisTiledDataManager::yToRow(TQ_INT32 y) const +{ + if (y >= 0) { + return y / KisTile::HEIGHT; + } else { + return -(((-y - 1) / KisTile::HEIGHT) + 1); + } +} + +// during development the following line helps to check the interface is correct +// it should be safe to keep it here even during normal compilation +#include "kis_datamanager.h" + +#endif // KIS_TILEDDATAMANAGER_H_ + diff --git a/chalk/core/tiles/kis_tiledhlineiterator.cc b/chalk/core/tiles/kis_tiledhlineiterator.cc new file mode 100644 index 00000000..cf023c1e --- /dev/null +++ b/chalk/core/tiles/kis_tiledhlineiterator.cc @@ -0,0 +1,213 @@ +/* + * This file is part of the KDE project + * + * Copyright (c) 2004 Casper Boemann + * + * 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_tile_global.h" +#include "kis_tilediterator.h" + +KisTiledHLineIterator::KisTiledHLineIterator( KisTiledDataManager *ndevice, TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, bool writable) : + KisTiledIterator(ndevice), + m_right(x+w-1), m_left(x) +{ + Q_ASSERT(ndevice != 0); + + m_writable = writable; + m_x = x; + m_y = y; + + // Find tile row,col matching x,y + m_row = yToRow(m_y); + m_leftCol = xToCol(m_x); + m_rightCol = xToCol(m_right); + m_col = m_leftCol; + + // calc limits within the tile + m_yInTile = m_y - m_row * KisTile::HEIGHT; + m_leftInTile = m_x - m_leftCol * KisTile::WIDTH; + + if(m_col == m_rightCol) + m_rightInTile = m_right - m_rightCol * KisTile::WIDTH; + else + m_rightInTile = KisTile::WIDTH - 1; + + m_xInTile = m_leftInTile; + + fetchTileData(m_col, m_row); + m_offset = m_pixelSize * (m_yInTile * KisTile::WIDTH + m_xInTile); +} + +KisTiledHLineIterator::KisTiledHLineIterator(const KisTiledHLineIterator& rhs) + : KisTiledIterator(rhs) +{ + if (this != &rhs) { + m_right = rhs.m_right; + m_left = rhs.m_left; + m_leftCol = rhs.m_leftCol; + m_rightCol = rhs.m_rightCol; + m_xInTile = rhs.m_xInTile; + m_yInTile = rhs.m_yInTile; + m_leftInTile = rhs.m_leftInTile; + m_rightInTile = rhs.m_rightInTile; + } +} + +KisTiledHLineIterator& KisTiledHLineIterator::operator=(const KisTiledHLineIterator& rhs) +{ + if (this != &rhs) { + KisTiledIterator::operator=(rhs); + m_right = rhs.m_right; + m_left = rhs.m_left; + m_leftCol = rhs.m_leftCol; + m_rightCol = rhs.m_rightCol; + m_xInTile = rhs.m_xInTile; + m_yInTile = rhs.m_yInTile; + m_leftInTile = rhs.m_leftInTile; + m_rightInTile = rhs.m_rightInTile; + } + return *this; +} + +KisTiledHLineIterator::~KisTiledHLineIterator( ) +{ +} + +KisTiledHLineIterator & KisTiledHLineIterator::operator ++ () +{ + if(m_xInTile >= m_rightInTile) + { + nextTile(); + fetchTileData(m_col, m_row); + m_xInTile =m_leftInTile; + m_offset = m_pixelSize * (m_yInTile * KisTile::WIDTH + m_xInTile); + } + else + { + m_xInTile++; + m_offset += m_pixelSize; + } + m_x++; + + return *this; +} + +void KisTiledHLineIterator::nextTile() +{ + if(m_col < m_rightCol) + { + m_col++; + m_leftInTile = 0; + + if(m_col == m_rightCol) + m_rightInTile = m_right - m_rightCol * KisTile::WIDTH; + else + m_rightInTile = KisTile::WIDTH - 1; + } +} + +void KisTiledHLineIterator::prevTile() +{ + if(m_col > m_leftCol) + { + m_col--; + + if(m_col == m_leftCol) { + m_leftInTile = m_left - m_leftCol * KisTile::WIDTH; + } else { + m_leftInTile = 0; + } + // the only place this doesn't apply, is if we're in rightCol, and we can't go there + m_rightInTile = KisTile::WIDTH - 1; + } +} + +TQ_INT32 KisTiledHLineIterator::nConseqHPixels() const +{ + return m_rightInTile - m_xInTile + 1; +} + +KisTiledHLineIterator & KisTiledHLineIterator::operator+=(int n) +{ + // XXX what if outside the valid range of this iterator? + if(m_xInTile + n > m_rightInTile) + { + m_x += n; + m_col = xToCol(m_x); + m_xInTile = m_x - m_col * KisTile::WIDTH; + m_leftInTile = 0; + + if(m_col == m_rightCol) + m_rightInTile = m_right - m_rightCol * KisTile::WIDTH; + else + m_rightInTile = KisTile::WIDTH - 1; + + fetchTileData(m_col, m_row); + } + else + { + m_xInTile += n; + m_x += n; + } + m_offset = m_pixelSize * (m_yInTile * KisTile::WIDTH + m_xInTile); + + return *this; +} + +KisTiledHLineIterator & KisTiledHLineIterator::operator -- () +{ + if(m_xInTile <= 0) + { + prevTile(); + fetchTileData(m_col, m_row); + m_xInTile = KisTile::WIDTH - 1; + m_offset = m_pixelSize * (m_yInTile * KisTile::WIDTH + m_xInTile); + } + else + { + m_xInTile--; + m_offset -= m_pixelSize; + } + m_x--; + + return *this; +} + +void KisTiledHLineIterator::nextRow() +{ + m_y++; + m_yInTile++; + m_x = m_left; + m_leftInTile = m_x - m_leftCol * KisTile::WIDTH; + m_xInTile = m_leftInTile; + if( m_yInTile >= KisTile::HEIGHT ) + { // Need a new row + m_yInTile = 0; + m_row++; + m_col = m_leftCol; + fetchTileData(m_col, m_row); + } else if( m_leftCol != m_col ) { + m_col = m_leftCol; + fetchTileData(m_col, m_row); + } + if(m_col == m_rightCol) + m_rightInTile = m_right - m_rightCol * KisTile::WIDTH; + else + m_rightInTile = KisTile::WIDTH - 1; + m_offset = m_pixelSize * (m_yInTile * KisTile::WIDTH + m_xInTile); +} diff --git a/chalk/core/tiles/kis_tilediterator.cc b/chalk/core/tiles/kis_tilediterator.cc new file mode 100644 index 00000000..d6205f5a --- /dev/null +++ b/chalk/core/tiles/kis_tilediterator.cc @@ -0,0 +1,131 @@ +/* + * This file is part of the Chalk + * + * Copyright (c) 2004 Casper Boemann + * + * 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_tile_global.h" +#include "kis_tilediterator.h" + +KisTiledIterator::KisTiledIterator( KisTiledDataManager *ndevice) +{ + Q_ASSERT(ndevice != 0); + m_ktm = ndevice; + m_x = 0; + m_y = 0; + m_row = 0; + m_col = 0; + m_pixelSize = m_ktm->pixelSize(); + m_tile = 0; + m_oldTile = 0; +} + +KisTiledIterator::~KisTiledIterator( ) +{ + if (m_tile) + m_tile->removeReader(); + if (m_oldTile) + m_oldTile->removeReader(); +} + +KisTiledIterator::KisTiledIterator(const KisTiledIterator& rhs) + : KShared() +{ + if (this != &rhs) { + m_ktm = rhs.m_ktm; + m_pixelSize = rhs.m_pixelSize; + m_x = rhs.m_x; + m_y = rhs.m_y; + m_row = rhs.m_row; + m_col = rhs.m_col; + m_data = rhs.m_data; + m_oldData = rhs.m_oldData; + m_offset = rhs.m_offset; + m_tile = rhs.m_tile; + m_oldTile = rhs.m_oldTile; + m_writable = rhs.m_writable; + if (m_tile) + m_tile->addReader(); + } +} + +KisTiledIterator& KisTiledIterator::operator=(const KisTiledIterator& rhs) +{ + if (this != &rhs) { + if (m_tile) + m_tile->removeReader(); + if (m_oldTile) + m_oldTile->removeReader(); + m_ktm = rhs.m_ktm; + m_pixelSize = rhs.m_pixelSize; + m_x = rhs.m_x; + m_y = rhs.m_y; + m_row = rhs.m_row; + m_col = rhs.m_col; + m_data = rhs.m_data; + m_oldData = rhs.m_oldData; + m_offset = rhs.m_offset; + m_tile = rhs.m_tile; + m_oldTile = rhs.m_oldTile; + m_writable = rhs.m_writable; + if (m_tile) + m_tile->addReader(); + } + return *this; +} + +TQ_UINT8 * KisTiledIterator::rawData() const +{ + return m_data + m_offset; +} + + +const TQ_UINT8 * KisTiledIterator::oldRawData() const +{ +#ifdef DEBUG + // Warn if we're misusing oldRawData(). If there's no memento, oldRawData is the same + // as rawData(). + kdWarning(!m_ktm->hasCurrentMemento(), DBG_AREA_TILES) << "Accessing oldRawData() when no transaction is in progress.\n"; +#endif + return m_oldData + m_offset; +} + +void KisTiledIterator::fetchTileData(TQ_INT32 col, TQ_INT32 row) +{ + if (m_tile) + m_tile->removeReader(); + if (m_oldTile) + m_oldTile->removeReader(); + m_oldTile = 0; + + m_tile = m_ktm->getTile(col, row, m_writable); + + if (m_tile == 0) return; + //Q_ASSERT(m_tile != 0); + m_tile->addReader(); + + m_data = m_tile->data(); + if (m_data == 0) return; + + //Q_ASSERT(m_data != 0); + + // set old data but default to current value + m_oldTile = m_ktm->getOldTile(col, row, m_tile); + m_oldTile->addReader(); // Double locking in case m_oldTile==m_tile is no problem + m_oldData = m_oldTile->data(); +} diff --git a/chalk/core/tiles/kis_tilediterator.h b/chalk/core/tiles/kis_tilediterator.h new file mode 100644 index 00000000..958876cd --- /dev/null +++ b/chalk/core/tiles/kis_tilediterator.h @@ -0,0 +1,213 @@ +/* This file is part of the KDE project + * Copyright (c) 2004 Casper Boemann + * 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. + */ + +#ifndef KIS_TILED_ITERATOR_H_ +#define KIS_TILED_ITERATOR_H_ + +#include + +#include + +#include +#include +#include +/** + * The KisIterator class iterates through the pixels of a KisPaintDevice hiding the tile structure + */ +class KRITACORE_EXPORT KisTiledIterator : public KShared { + +protected: + KisTiledDataManager *m_ktm; + TQ_INT32 m_pixelSize; // bytes per pixel + TQ_INT32 m_x; // current x position + TQ_INT32 m_y; // cirrent y position + TQ_INT32 m_row; // row in tilemgr + TQ_INT32 m_col; // col in tilemgr + TQ_UINT8 *m_data; + TQ_UINT8 *m_oldData; + TQ_INT32 m_offset; + KisTile *m_tile; + KisTile* m_oldTile; + bool m_writable; + +protected: + inline TQ_UINT32 xToCol(TQ_UINT32 x) const { if (m_ktm) return m_ktm->xToCol(x); else return 0; }; + inline TQ_UINT32 yToRow(TQ_UINT32 y) const { if (m_ktm) return m_ktm->yToRow(y); else return 0; }; + void fetchTileData(TQ_INT32 col, TQ_INT32 row); + +public: + KisTiledIterator( KisTiledDataManager *ktm); + KisTiledIterator(const KisTiledIterator&); + KisTiledIterator& operator=(const KisTiledIterator&); + ~KisTiledIterator(); + +public: + // current x position + TQ_INT32 x() const { return m_x; }; + + // cirrent y position + TQ_INT32 y() const { return m_y; }; + + /// Returns a pointer to the pixel data. Do NOT interpret the data - leave that to a colorstrategy + TQ_UINT8 *rawData() const; + + /// Returns a pointer to the pixel data as it was at the moment tof he last memento creation. + const TQ_UINT8 * oldRawData() const; +}; + +/** + * The KisRectIterator class iterates through the pixels of a rect in a KisPaintDevice hiding the + * tile structure + */ +class KRITACORE_EXPORT KisTiledRectIterator : public KisTiledIterator +{ + +public: + /// do not call constructor directly use factory method in KisDataManager instead. + KisTiledRectIterator( KisTiledDataManager *dm, TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, TQ_INT32 h, bool writable); + KisTiledRectIterator(const KisTiledRectIterator&); + KisTiledRectIterator& operator=(const KisTiledRectIterator&); + ~KisTiledRectIterator(); + +public: + TQ_INT32 nConseqPixels() const; + + /// Advances a number of pixels until it reaches the end of the rect + KisTiledRectIterator & operator+=(int n); + + /// Advances one pixel. Going to the beginning of the next line when it reaches the end of a line + KisTiledRectIterator & operator++(); + + /// Goes back one pixel. Going to the end of the line above when it reaches the beginning of a line + //KisTiledRectIterator & operator--(); + + /// returns true when the iterator has reached the end + inline bool isDone() const { return m_beyondEnd; } + + +protected: + TQ_INT32 m_left; + TQ_INT32 m_top; + TQ_INT32 m_w; + TQ_INT32 m_h; + TQ_INT32 m_topRow; + TQ_INT32 m_bottomRow; + TQ_INT32 m_leftCol; + TQ_INT32 m_rightCol; + TQ_INT32 m_xInTile; + TQ_INT32 m_yInTile; + TQ_INT32 m_leftInTile; + TQ_INT32 m_rightInTile; + TQ_INT32 m_topInTile; + TQ_INT32 m_bottomInTile; + bool m_beyondEnd; + +private: + void nextTile(); +}; + +/** + * The KisHLineIterator class iterates through the pixels of a horizontal line in a KisPaintDevice hiding the + * tile structure + */ +class KRITACORE_EXPORT KisTiledHLineIterator : public KisTiledIterator +{ + +public: + /// do not call constructor directly use factory method in KisDataManager instead. + KisTiledHLineIterator( KisTiledDataManager *dm, TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, bool writable); + KisTiledHLineIterator(const KisTiledHLineIterator&); + KisTiledHLineIterator& operator=(const KisTiledHLineIterator&); + ~KisTiledHLineIterator(); + +public: + /// Advances one pixel. Going to the beginning of the next line when it reaches the end of a line + KisTiledHLineIterator & operator++(); + + /// Returns the number of consequtive horizontal pixels that we point at + /// This is useful for optimizing + TQ_INT32 nConseqHPixels() const; + + /// Advances a number of pixels until it reaches the end of the line + KisTiledHLineIterator & operator+=(int); + + /// Goes back one pixel. Going to the end of the line above when it reaches the beginning of a line + KisTiledHLineIterator & operator--(); + + /// returns true when the iterator has reached the end + bool isDone() const { return m_x > m_right; } + + /// increment to the next row and rewind to the begining + void nextRow(); + +protected: + TQ_INT32 m_right; + TQ_INT32 m_left; + TQ_INT32 m_leftCol; + TQ_INT32 m_rightCol; + TQ_INT32 m_xInTile; + TQ_INT32 m_yInTile; + TQ_INT32 m_leftInTile; + TQ_INT32 m_rightInTile; + +private: + void nextTile(); + void prevTile(); +}; + +/** + * The KisVLineIterator class iterates through the pixels of a vertical line in a KisPaintDevice hiding the + * tile structure + */ +class KRITACORE_EXPORT KisTiledVLineIterator : public KisTiledIterator +{ + +public: + /// do not call constructor directly use factory method in KisDataManager instead. + KisTiledVLineIterator( KisTiledDataManager *dm, TQ_INT32 x, TQ_INT32 y, TQ_INT32 h, bool writable); + KisTiledVLineIterator(const KisTiledVLineIterator&); + KisTiledVLineIterator& operator=(const KisTiledVLineIterator&); + ~KisTiledVLineIterator(); + +public: + /// Advances one pixel. Going to the beginning of the next line when it reaches the end of a line + KisTiledVLineIterator & operator++(); + + /// Goes back one pixel. Going to the end of the line above when it reaches the beginning of a line + //KisTiledVLineIterator & operator--(); + + /// returns true when the iterator has reached the end + bool isDone() const { return m_y > m_bottom; } + + /// increment to the next column and rewind to the begining + void nextCol(); + +protected: + TQ_INT32 m_top; + TQ_INT32 m_bottom; + TQ_INT32 m_topRow; + TQ_INT32 m_bottomRow; + TQ_INT32 m_xInTile; + TQ_INT32 m_yInTile; + TQ_INT32 m_topInTile; + TQ_INT32 m_bottomInTile; + +private: + void nextTile(); +}; + +#endif // KIS_TILED_ITERATOR_H_ diff --git a/chalk/core/tiles/kis_tiledrectiterator.cc b/chalk/core/tiles/kis_tiledrectiterator.cc new file mode 100644 index 00000000..8f0f7ed1 --- /dev/null +++ b/chalk/core/tiles/kis_tiledrectiterator.cc @@ -0,0 +1,242 @@ +/* + * This file is part of the KDE project + * + * Copyright (c) 2004 Casper Boemann + * + * 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_tile_global.h" +#include "kis_tilediterator.h" + +KisTiledRectIterator::KisTiledRectIterator( KisTiledDataManager *ndevice, TQ_INT32 nleft, + TQ_INT32 ntop, TQ_INT32 nw, TQ_INT32 nh, bool writable) : + KisTiledIterator(ndevice), + m_left(nleft), + m_top(ntop), + m_w(nw), + m_h(nh) +{ + + Q_ASSERT(ndevice != 0); + + m_writable = writable; + m_x = nleft; + m_y = ntop; + m_beyondEnd = (m_w == 0) || (m_h == 0); + + // Find tile row,col matching x,y + m_topRow = yToRow(m_y); + m_bottomRow = yToRow(m_y + m_h - 1); + m_leftCol = xToCol(m_x); + m_rightCol = xToCol(m_x + m_w - 1); + m_row = m_topRow; + m_col = m_leftCol; + + // calc limits within the tile + m_topInTile = m_top - m_topRow * KisTile::HEIGHT; + + if(m_row == m_bottomRow) + m_bottomInTile = m_top + m_h - 1 - m_bottomRow * KisTile::HEIGHT; + else + m_bottomInTile = KisTile::HEIGHT - 1; + + m_leftInTile = m_left - m_leftCol * KisTile::WIDTH; + + if(m_col == m_rightCol) + m_rightInTile = m_left + m_w - 1 - m_rightCol * KisTile::WIDTH; + else + m_rightInTile = KisTile::WIDTH - 1; + + m_xInTile = m_leftInTile; + m_yInTile = m_topInTile; + + if( ! m_beyondEnd) + fetchTileData(m_col, m_row); + m_offset = m_pixelSize * (m_yInTile * KisTile::WIDTH + m_xInTile); +} + +KisTiledRectIterator::KisTiledRectIterator(const KisTiledRectIterator& rhs) + : KisTiledIterator(rhs) +{ + if (this != &rhs) { + m_left = rhs.m_left; + m_top = rhs.m_top; + m_w = rhs.m_w; + m_h = rhs.m_h; + m_topRow = rhs.m_topRow; + m_bottomRow = rhs.m_bottomRow; + m_leftCol = rhs.m_leftCol; + m_rightCol = rhs.m_rightCol; + m_xInTile = rhs.m_xInTile; + m_yInTile = rhs.m_yInTile; + m_leftInTile = rhs.m_leftInTile; + m_rightInTile = rhs.m_rightInTile; + m_topInTile = rhs.m_topInTile; + m_bottomInTile = rhs.m_bottomInTile; + m_beyondEnd = rhs.m_beyondEnd; + } +} + +KisTiledRectIterator& KisTiledRectIterator::operator=(const KisTiledRectIterator& rhs) +{ + if (this != &rhs) { + KisTiledIterator::operator=(rhs); + m_left = rhs.m_left; + m_top = rhs.m_top; + m_w = rhs.m_w; + m_h = rhs.m_h; + m_topRow = rhs.m_topRow; + m_bottomRow = rhs.m_bottomRow; + m_leftCol = rhs.m_leftCol; + m_rightCol = rhs.m_rightCol; + m_xInTile = rhs.m_xInTile; + m_yInTile = rhs.m_yInTile; + m_leftInTile = rhs.m_leftInTile; + m_rightInTile = rhs.m_rightInTile; + m_topInTile = rhs.m_topInTile; + m_bottomInTile = rhs.m_bottomInTile; + m_beyondEnd = rhs.m_beyondEnd; + } + return *this; +} + +KisTiledRectIterator::~KisTiledRectIterator( ) +{ +} + +TQ_INT32 KisTiledRectIterator::nConseqPixels() const +{ + if(m_leftInTile || (m_rightInTile != KisTile::WIDTH - 1)) + return m_rightInTile - m_xInTile + 1; + else + return KisTile::WIDTH * (m_bottomInTile - m_yInTile + 1) - m_xInTile; +} + +KisTiledRectIterator & KisTiledRectIterator::operator+=(int n) +{ + int remainInTile; + + remainInTile= (m_bottomInTile - m_yInTile) * (m_rightInTile - m_leftInTile + 1); + remainInTile += m_rightInTile - m_xInTile + 1; + + // This while loop may not bet the fastest, but usually it's not entered more than once. + while(n >= remainInTile) + { + n -= remainInTile; + nextTile(); + if(m_beyondEnd) + return *this; + m_yInTile = m_topInTile; + m_xInTile = m_leftInTile; + remainInTile= (m_bottomInTile - m_yInTile) * (m_rightInTile - m_leftInTile + 1); + remainInTile += m_rightInTile - m_xInTile + 1; + } + + int lWidth = m_rightInTile - m_leftInTile + 1; + while(n >= lWidth) + { + n -= lWidth; + m_yInTile++; + } + m_xInTile += n; + m_x = m_col * KisTile::WIDTH + m_xInTile; + m_y = m_row * KisTile::HEIGHT + m_yInTile; + fetchTileData(m_col, m_row); + m_offset = m_pixelSize * (m_yInTile * KisTile::WIDTH + m_xInTile); + + return *this; +} + + +KisTiledRectIterator & KisTiledRectIterator::operator ++ () +{ + // advance through rect completing each tile before moving on + // as per excellent suggestion by Cyrille, avoiding excessive tile switching + if(m_xInTile >= m_rightInTile) + { + if (m_yInTile >= m_bottomInTile) + { + nextTile(); + if(m_beyondEnd) + return *this; + m_yInTile = m_topInTile; + m_x = m_col * KisTile::WIDTH + m_leftInTile; + m_y = m_row * KisTile::HEIGHT + m_topInTile; + fetchTileData(m_col, m_row); + } + else + { + m_x -= m_rightInTile - m_leftInTile; + m_y++; + m_yInTile++; + } + m_xInTile =m_leftInTile; + m_offset = m_pixelSize * (m_yInTile * KisTile::WIDTH + m_xInTile); + } + else + { + m_x++; + m_xInTile++; + m_offset += m_pixelSize; + } + return *this; +} + +void KisTiledRectIterator::nextTile() +{ + if(m_col >= m_rightCol) + { + // needs to switch row + if(m_row >= m_bottomRow) + m_beyondEnd = true; + else + { + m_col = m_leftCol; + m_row++; + // The row has now changed, so recalc vertical limits + if(m_row == m_topRow) + m_topInTile = m_top - m_topRow * KisTile::HEIGHT; + else + m_topInTile = 0; + + if(m_row == m_bottomRow) + m_bottomInTile = m_top + m_h - 1 - m_bottomRow * KisTile::HEIGHT; + else + m_bottomInTile = KisTile::HEIGHT - 1; + } + } + else + m_col++; + + // No matter what the column has now changed, so recalc horizontal limits + if(m_col == m_leftCol) + m_leftInTile = m_left - m_leftCol * KisTile::WIDTH; + else + m_leftInTile = 0; + + if(m_col == m_rightCol) + m_rightInTile = m_left + m_w - 1 - m_rightCol * KisTile::WIDTH; + else + m_rightInTile = KisTile::WIDTH - 1; +} + +/* +KisTiledRectIterator & KisTiledRectIterator::operator -- () +{ + return *this; +} +*/ diff --git a/chalk/core/tiles/kis_tiledvlineiterator.cc b/chalk/core/tiles/kis_tiledvlineiterator.cc new file mode 100644 index 00000000..0fe8514f --- /dev/null +++ b/chalk/core/tiles/kis_tiledvlineiterator.cc @@ -0,0 +1,154 @@ +/* + * This file is part of the Chalk + * + * Copyright (c) 2004 Casper Boemann + * + * 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_tile_global.h" +#include "kis_tilediterator.h" + +KisTiledVLineIterator::KisTiledVLineIterator( KisTiledDataManager *ndevice, TQ_INT32 x, TQ_INT32 y, TQ_INT32 h, bool writable) : + KisTiledIterator(ndevice), + m_bottom(y + h - 1) +{ + m_writable = writable; + m_top = y; + m_x = x; + m_y = y; + + // Find tile row,col matching x,y + m_col = xToCol(m_x); + m_topRow = yToRow(m_y); + m_bottomRow = yToRow(m_bottom); + m_row = m_topRow; + + // calc limits within the tile + m_xInTile = m_x - m_col * KisTile::WIDTH; + m_topInTile = m_y - m_topRow * KisTile::HEIGHT; + + if(m_row == m_bottomRow) + m_bottomInTile = m_bottom - m_bottomRow * KisTile::HEIGHT; + else + m_bottomInTile = KisTile::HEIGHT - 1; + + m_yInTile = m_topInTile; + + fetchTileData(m_col, m_row); + m_offset = m_pixelSize * (m_yInTile * KisTile::WIDTH + m_xInTile); +} + +KisTiledVLineIterator::KisTiledVLineIterator(const KisTiledVLineIterator& rhs) + : KisTiledIterator(rhs) +{ + if (this != &rhs) { + m_top = rhs.m_top; + m_bottom = rhs.m_bottom; + m_topRow = rhs.m_topRow; + m_bottomRow = rhs.m_bottomRow; + m_xInTile = rhs.m_xInTile; + m_yInTile = rhs.m_yInTile; + m_topInTile = rhs.m_topInTile; + m_bottomInTile = rhs.m_bottomInTile; + } +} + +KisTiledVLineIterator& KisTiledVLineIterator::operator=(const KisTiledVLineIterator& rhs) +{ + if (this != &rhs) { + KisTiledIterator::operator=(rhs); + + m_top = rhs.m_top; + m_bottom = rhs.m_bottom; + m_topRow = rhs.m_topRow; + m_bottomRow = rhs.m_bottomRow; + m_xInTile = rhs.m_xInTile; + m_yInTile = rhs.m_yInTile; + m_topInTile = rhs.m_topInTile; + m_bottomInTile = rhs.m_bottomInTile; + } + return *this; +} + +KisTiledVLineIterator::~KisTiledVLineIterator( ) +{ +} + +KisTiledVLineIterator & KisTiledVLineIterator::operator ++ () +{ + if(m_yInTile >= m_bottomInTile) + { + nextTile(); + fetchTileData(m_col, m_row); + m_yInTile =m_topInTile; + m_offset = m_pixelSize * (m_yInTile * KisTile::WIDTH + m_xInTile); + } + else + { + m_yInTile++; + m_offset += m_pixelSize * KisTile::WIDTH; + } + m_y++; + + return *this; +} + +void KisTiledVLineIterator::nextTile() +{ + if(m_row < m_bottomRow) + { + m_row++; + m_topInTile = 0; + + if(m_row == m_bottomRow) + m_bottomInTile = m_bottom - m_bottomRow * KisTile::HEIGHT; + else + m_bottomInTile = KisTile::HEIGHT - 1; + } +} + +void KisTiledVLineIterator::nextCol() +{ + m_x++; + m_xInTile++; + m_y = m_top; + m_topInTile = m_y - m_topRow * KisTile::HEIGHT; + m_yInTile = m_topInTile; + if( m_xInTile >= KisTile::WIDTH ) + { // Need a new row + m_xInTile = 0; + m_col++; + m_row = m_topRow; + fetchTileData(m_col, m_row); + } else if( m_topRow != m_row ) { + m_row = m_topRow; + fetchTileData(m_col, m_row); + } + if(m_row == m_bottomRow) + m_bottomInTile = m_bottom - m_bottomRow * KisTile::HEIGHT; + else + m_bottomInTile = KisTile::HEIGHT - 1; + + m_offset = m_pixelSize * (m_yInTile * KisTile::WIDTH + m_xInTile); +} + +/* +KisTiledVLineIterator & KisTiledVLineIterator::operator -- () +{ + return *this; +} +*/ diff --git a/chalk/core/tiles/kis_tilemanager.cc b/chalk/core/tiles/kis_tilemanager.cc new file mode 100644 index 00000000..dc9811e9 --- /dev/null +++ b/chalk/core/tiles/kis_tilemanager.cc @@ -0,0 +1,578 @@ +/* + * Copyright (c) 2005-2006 Bart Coppens + * + * 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 +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "kis_tileddatamanager.h" +#include "kis_tile.h" +#include "kis_tilemanager.h" + +// Note: the cache file doesn't get deleted when we crash and so :( + +KisTileManager* KisTileManager::m_singleton = 0; + +static KStaticDeleter staticDeleter; + +KisTileManager::KisTileManager() { + + Q_ASSERT(KisTileManager::m_singleton == 0); + KisTileManager::m_singleton = this; + m_bytesInMem = 0; + m_bytesTotal = 0; + m_swapForbidden = false; + + // Hardcoded (at the moment only?): 4 pools of 1000 tiles each + m_tilesPerPool = 1000; + + m_pools = new TQ_UINT8*[4]; + m_poolPixelSizes = new TQ_INT32[4]; + m_poolFreeList = new PoolFreeList[4]; + for (int i = 0; i < 4; i++) { + m_pools[i] = 0; + m_poolPixelSizes[i] = 0; + m_poolFreeList[i] = PoolFreeList(); + } + m_currentInMem = 0; + + KConfig * cfg = KGlobal::config(); + cfg->setGroup(""); + m_maxInMem = cfg->readNumEntry("maxtilesinmem", 4000); + m_swappiness = cfg->readNumEntry("swappiness", 100); + + m_tileSize = KisTile::WIDTH * KisTile::HEIGHT; + m_freeLists.resize(8); + + counter = 0; + + m_poolMutex = new TQMutex(true); + m_swapMutex = new TQMutex(true); +} + +KisTileManager::~KisTileManager() { + if (!m_freeLists.empty()) { // See if there are any nonempty freelists + FreeListList::iterator listsIt = m_freeLists.begin(); + FreeListList::iterator listsEnd = m_freeLists.end(); + + while(listsIt != listsEnd) { + if ( ! (*listsIt).empty() ) { + FreeList::iterator it = (*listsIt).begin(); + FreeList::iterator end = (*listsIt).end(); + + while (it != end) { + delete *it; + ++it; + } + (*listsIt).clear(); + } + ++listsIt; + } + m_freeLists.clear(); + } + + for (FileList::iterator it = m_files.begin(); it != m_files.end(); ++it) { + (*it).tempFile->close(); + (*it).tempFile->unlink(); + delete (*it).tempFile; + } + + delete [] m_poolPixelSizes; + delete [] m_pools; + + delete m_poolMutex; + delete m_swapMutex; +} + +KisTileManager* KisTileManager::instance() +{ + if(KisTileManager::m_singleton == 0) { + staticDeleter.setObject(KisTileManager::m_singleton, new KisTileManager()); + Q_CHECK_PTR(KisTileManager::m_singleton); + } + return KisTileManager::m_singleton; +} + +void KisTileManager::registerTile(KisTile* tile) +{ + + m_swapMutex->lock(); + + TileInfo* info = new TileInfo(); + info->tile = tile; + info->inMem = true; + info->mmapped = false; + info->onFile = false; + info->file = 0; + info->filePos = 0; + info->size = tile->WIDTH * tile->HEIGHT * tile->m_pixelSize; + info->fsize = 0; // the size in the file + info->validNode = true; + + m_tileMap[tile] = info; + m_swappableList.push_back(info); + info->node = -- m_swappableList.end(); + + m_currentInMem++; + m_bytesTotal += info->size; + m_bytesInMem += info->size; + + doSwapping(); + + if (++counter % 50 == 0) + printInfo(); + + m_swapMutex->unlock(); +} + +void KisTileManager::deregisterTile(KisTile* tile) { + + m_swapMutex->lock(); + + if (!m_tileMap.tqcontains(tile)) { + m_swapMutex->unlock(); + return; + } + // Q_ASSERT(m_tileMap.tqcontains(tile)); + + TileInfo* info = m_tileMap[tile]; + + if (info->onFile) { // It was once mmapped + // To freelist + FreeInfo* freeInfo = new FreeInfo(); + freeInfo->file = info->file; + freeInfo->filePos = info->filePos; + freeInfo->size = info->fsize; + uint pixelSize = (info->size / m_tileSize); + + // It is still mmapped? + if (info->mmapped) { + // munmap it + munmap(info->tile->m_data, info->size); + m_bytesInMem -= info->size; + m_currentInMem--; + } + + if (m_freeLists.capacity() <= pixelSize) + m_freeLists.resize(pixelSize + 1); + m_freeLists[pixelSize].push_back(freeInfo); + + // the KisTile will attempt to delete its data. This is of course silly when + // it was mmapped. So change the m_data to NULL, which is safe to delete + tile->m_data = 0; + } else { + m_bytesInMem -= info->size; + m_currentInMem--; + } + + if (info->validNode) { + m_swappableList.erase(info->node); + info->validNode = false; + } + + m_bytesTotal -= info->size; + + delete info; + m_tileMap.erase(tile); + + doSwapping(); + + m_swapMutex->unlock(); +} + +void KisTileManager::ensureTileLoaded(const KisTile* tile) +{ + + m_swapMutex->lock(); + + TileInfo* info = m_tileMap[tile]; + if (info->validNode) { + m_swappableList.erase(info->node); + info->validNode = false; + } + + if (!info->inMem) { + fromSwap(info); + } + + m_swapMutex->unlock(); +} + +void KisTileManager::maySwapTile(const KisTile* tile) +{ + + m_swapMutex->lock(); + + TileInfo* info = m_tileMap[tile]; + m_swappableList.push_back(info); + info->validNode = true; + info->node = -- m_swappableList.end(); + + doSwapping(); + + m_swapMutex->unlock(); +} + +void KisTileManager::fromSwap(TileInfo* info) +{ + m_swapMutex->lock(); + + if (info->inMem) { + m_swapMutex->unlock(); + return; + } + + doSwapping(); + + Q_ASSERT(info->onFile); + Q_ASSERT(info->file); + Q_ASSERT(!info->mmapped); + + if (!chalkMmap(info->tile->m_data, 0, info->size, PROT_READ | PROT_WRITE, MAP_SHARED, + info->file->handle(), info->filePos)) { + kdWarning() << "fromSwap failed!" << endl; + m_swapMutex->unlock(); + return; + } + + info->inMem = true; + info->mmapped = true; + + m_currentInMem++; + m_bytesInMem += info->size; + + m_swapMutex->unlock(); +} + +void KisTileManager::toSwap(TileInfo* info) { + m_swapMutex->lock(); + + //Q_ASSERT(info->inMem); + if (!info || !info->inMem) { + m_swapMutex->unlock(); + return; + } + + KisTile *tile = info->tile; + + if (!info->onFile) { + // This tile is not yet in the file. Save it there + uint pixelSize = (info->size / m_tileSize); + bool foundFree = false; + + if (m_freeLists.capacity() > pixelSize) { + if (!m_freeLists[pixelSize].empty()) { + // found one + FreeList::iterator it = m_freeLists[pixelSize].begin(); + + info->file = (*it)->file; + info->filePos = (*it)->filePos; + info->fsize = (*it)->size; + + delete *it; + m_freeLists[pixelSize].erase(it); + + foundFree = true; + } + } + + if (!foundFree) { // No position found or free, create a new + long pagesize = sysconf(_SC_PAGESIZE); + TempFile* tfile = 0; + if (m_files.empty() || m_files.back().fileSize >= MaxSwapFileSize) { + m_files.push_back(TempFile()); + tfile = &(m_files.back()); + tfile->tempFile = new KTempFile(); + tfile->fileSize = 0; + } else { + tfile = &(m_files.back()); + } + off_t newsize = tfile->fileSize + info->size; + newsize = newsize + newsize % pagesize; + + if (ftruncate(tfile->tempFile->handle(), newsize)) { + // XXX make these maybe i18n()able and in an error box, but then through + // some kind of proxy such that we don't pollute this with GUI code + kdWarning(DBG_AREA_TILES) << "Resizing the temporary swapfile failed!" << endl; + // Be somewhat pollite and try to figure out why it failed + switch (errno) { + case EIO: kdWarning(DBG_AREA_TILES) << "Error was E IO, " + << "possible reason is a disk error!" << endl; break; + case EINVAL: kdWarning(DBG_AREA_TILES) << "Error was E INVAL, " + << "possible reason is that you are using more memory than " + << "the filesystem or disk can handle" << endl; break; + default: kdWarning(DBG_AREA_TILES) << "Errno was: " << errno << endl; + } + kdWarning(DBG_AREA_TILES) << "The swapfile is: " << tfile->tempFile->name() << endl; + kdWarning(DBG_AREA_TILES) << "Will try to avoid using the swap any further" << endl; + + kdDebug(DBG_AREA_TILES) << "Failed ftruncate info: " + << "tried adding " << info->size << " bytes " + << "(rounded to pagesize: " << newsize << ") " + << "from a " << tfile->fileSize << " bytes file" << endl; + printInfo(); + + m_swapForbidden = true; + m_swapMutex->unlock(); + return; + } + + info->file = tfile->tempFile; + info->fsize = info->size; + info->filePos = tfile->fileSize; + tfile->fileSize = newsize; + } + + //memcpy(data, tile->m_data, info->size); + TQFile* file = info->file->file(); + if(!file) { + kdWarning() << "Opening the file as TQFile failed" << endl; + m_swapForbidden = true; + m_swapMutex->unlock(); + return; + } + + int fd = file->handle(); + TQ_UINT8* data = 0; + if (!chalkMmap(data, 0, info->size, PROT_READ | PROT_WRITE, MAP_SHARED, + fd, info->filePos)) { + kdWarning() << "Initial mmap failed" << endl; + m_swapForbidden = true; + m_swapMutex->unlock(); + return; + } + + memcpy(data, info->tile->m_data, info->size); + munmap(data, info->size); + + m_poolMutex->lock(); + if (isPoolTile(tile->m_data, tile->m_pixelSize)) + reclaimTileToPool(tile->m_data, tile->m_pixelSize); + else + delete[] tile->m_data; + m_poolMutex->unlock(); + + tile->m_data = 0; + } else { + //madvise(info->tile->m_data, info->fsize, MADV_DONTNEED); + Q_ASSERT(info->mmapped); + + // munmap it + munmap(tile->m_data, info->size); + tile->m_data = 0; + } + + info->inMem = false; + info->mmapped = false; + info->onFile = true; + + m_currentInMem--; + m_bytesInMem -= info->size; + + m_swapMutex->unlock(); +} + +void KisTileManager::doSwapping() +{ + m_swapMutex->lock(); + + if (m_swapForbidden || m_currentInMem <= m_maxInMem) { + m_swapMutex->unlock(); + return; + } + +#if 1 // enable this to enable swapping + + TQ_UINT32 count = TQMIN(m_swappableList.size(), m_swappiness); + + for (TQ_UINT32 i = 0; i < count && !m_swapForbidden; i++) { + toSwap(m_swappableList.front()); + m_swappableList.front()->validNode = false; + m_swappableList.pop_front(); + } + +#endif + + m_swapMutex->unlock(); +} + +void KisTileManager::printInfo() +{ + kdDebug(DBG_AREA_TILES) << m_bytesInMem << " out of " << m_bytesTotal << " bytes in memory\n"; + kdDebug(DBG_AREA_TILES) << m_currentInMem << " out of " << m_tileMap.size() << " tiles in memory\n"; + kdDebug(DBG_AREA_TILES) << m_files.size() << " swap files in use" << endl; + kdDebug(DBG_AREA_TILES) << m_swappableList.size() << " elements in the swapable list\n"; + kdDebug(DBG_AREA_TILES) << "Freelists information\n"; + for (uint i = 0; i < m_freeLists.capacity(); i++) { + if ( ! m_freeLists[i].empty() ) { + kdDebug(DBG_AREA_TILES) << m_freeLists[i].size() + << " elements in the freelist for pixelsize " << i << "\n"; + } + } + kdDebug(DBG_AREA_TILES) << "Pool stats (" << m_tilesPerPool << " tiles per pool)" << endl; + for (int i = 0; i < 4; i++) { + if (m_pools[i]) { + kdDebug(DBG_AREA_TILES) << "Pool " << i << ": Freelist count: " << m_poolFreeList[i].count() + << ", pixelSize: " << m_poolPixelSizes[i] << endl; + } + } + if (m_swapForbidden) + kdDebug(DBG_AREA_TILES) << "Something was wrong with the swap, see above for details" << endl; + kdDebug(DBG_AREA_TILES) << endl; +} + +TQ_UINT8* KisTileManager::requestTileData(TQ_INT32 pixelSize) +{ + m_swapMutex->lock(); + + TQ_UINT8* data = findTileFor(pixelSize); + if ( data ) { + m_swapMutex->unlock(); + return data; + } + m_swapMutex->unlock(); + return new TQ_UINT8[m_tileSize * pixelSize]; +} + +void KisTileManager::dontNeedTileData(TQ_UINT8* data, TQ_INT32 pixelSize) +{ + m_poolMutex->lock(); + if (isPoolTile(data, pixelSize)) { + reclaimTileToPool(data, pixelSize); + } else + delete[] data; + m_poolMutex->unlock(); +} + +TQ_UINT8* KisTileManager::findTileFor(TQ_INT32 pixelSize) +{ + m_poolMutex->lock(); + + for (int i = 0; i < 4; i++) { + if (m_poolPixelSizes[i] == pixelSize) { + if (!m_poolFreeList[i].isEmpty()) { + TQ_UINT8* data = m_poolFreeList[i].front(); + m_poolFreeList[i].pop_front(); + m_poolMutex->unlock(); + return data; + } + } + if (m_pools[i] == 0) { + // allocate new pool + m_poolPixelSizes[i] = pixelSize; + m_pools[i] = new TQ_UINT8[pixelSize * m_tileSize * m_tilesPerPool]; + // j = 1 because we return the first element, so no need to add it to the freelist + for (int j = 1; j < m_tilesPerPool; j++) + m_poolFreeList[i].append(&m_pools[i][j * pixelSize * m_tileSize]); + m_poolMutex->unlock(); + return m_pools[i]; + } + } + + m_poolMutex->unlock(); + return 0; +} + +bool KisTileManager::isPoolTile(TQ_UINT8* data, TQ_INT32 pixelSize) { + + if (data == 0) + return false; + + m_poolMutex->lock(); + for (int i = 0; i < 4; i++) { + if (m_poolPixelSizes[i] == pixelSize) { + bool b = data >= m_pools[i] + && data < m_pools[i] + pixelSize * m_tileSize * m_tilesPerPool; + if (b) { + m_poolMutex->unlock(); + return true; + } + } + } + m_poolMutex->unlock(); + return false; +} + +void KisTileManager::reclaimTileToPool(TQ_UINT8* data, TQ_INT32 pixelSize) { + m_poolMutex->lock(); + for (int i = 0; i < 4; i++) { + if (m_poolPixelSizes[i] == pixelSize) + if (data >= m_pools[i] && data < m_pools[i] + pixelSize * m_tileSize * m_tilesPerPool) { + m_poolFreeList[i].append(data); + } + } + m_poolMutex->unlock(); +} + +void KisTileManager::configChanged() { + KConfig * cfg = KGlobal::config(); + cfg->setGroup(""); + m_maxInMem = cfg->readNumEntry("maxtilesinmem", 4000); + m_swappiness = cfg->readNumEntry("swappiness", 100); + + m_swapMutex->lock(); + doSwapping(); + m_swapMutex->unlock(); +} + +bool KisTileManager::chalkMmap(TQ_UINT8*& result, void *start, size_t length, + int prot, int flags, int fd, off_t offset) { + result = (TQ_UINT8*) mmap(start, length, prot, flags, fd, offset); + + // Same here for warning and GUI + if (result == (TQ_UINT8*)-1) { + kdWarning(DBG_AREA_TILES) << "mmap failed: errno is " << errno << "; we're probably going to crash very soon now...\n"; + + // Try to ignore what happened and carry on, but unlikely that we'll get + // much further, since the file resizing went OK and this is memory-related... + if (errno == ENOMEM) { + kdWarning(DBG_AREA_TILES) << "mmap failed with E NOMEM! This means that " + << "either there are no more memory mappings available for Chalk, " + << "or that there is no more memory available!" << endl; + } + + kdWarning(DBG_AREA_TILES) << "Trying to continue anyway (no guarantees)" << endl; + kdWarning(DBG_AREA_TILES) << "Will try to avoid using the swap any further" << endl; + kdDebug(DBG_AREA_TILES) << "Failed mmap info: " + << "tried mapping " << length << " bytes" << endl; + if (!m_files.empty()) { + kdDebug(DBG_AREA_TILES) << "Probably to a " << m_files.back().fileSize << " bytes file" << endl; + } + printInfo(); + + // Be nice + result = 0; + + return false; + } + + return true; +} diff --git a/chalk/core/tiles/kis_tilemanager.h b/chalk/core/tiles/kis_tilemanager.h new file mode 100644 index 00000000..d6886abe --- /dev/null +++ b/chalk/core/tiles/kis_tilemanager.h @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2005 Bart Coppens + * + * 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. + */ +#ifndef KIS_TILEMANAGER_H_ +#define KIS_TILEMANAGER_H_ + +#include + +#include +#include +#include +#include + +#include + +class KisTile; +class KisTiledDataManager; + +/** + * This class keeps has the intention to make certain tile-related operations faster or more + * efficient. It does this by keeping lots of info on KisTiles, and manages the way they are + * created, used, etc. + * It mainly does the following more visible things + * * provide a way to store tiles on disk to a swap file, to reduce memory usage + * * keep a list of previously swapped (but now unused) tiles, to reuse these when we want + * to swap new tiles. + * * tries to preallocate and recycle some tiles to make future allocations faster + * (not done yet) + */ +class KisTileManager { +public: + ~KisTileManager(); + static KisTileManager* instance(); + +public: // Tile management + void registerTile(KisTile* tile); + void deregisterTile(KisTile* tile); + // these can change the tile indirectly, though, through the actual swapping! + void ensureTileLoaded(const KisTile* tile); + void maySwapTile(const KisTile* tile); + +public: // Pool management + TQ_UINT8* requestTileData(TQ_INT32 pixelSize); + void dontNeedTileData(TQ_UINT8* data, TQ_INT32 pixelSize); + +public: // Configuration + void configChanged(); + +private: + KisTileManager(); + KisTileManager(KisTileManager&) {} + KisTileManager operator=(const KisTileManager&); + +private: + static KisTileManager *m_singleton; + + // For use when any swap-allocating function failed; the risk of swap allocating failing + // again is too big, and we'd clutter the logs with kdWarnings otherwise + bool m_swapForbidden; + + // This keeps track of open swap files, and their associated filesizes + struct TempFile { + KTempFile* tempFile; + off_t fileSize; + }; + // validNode says if you can swap it (true) or not (false) mmapped, if this tile + // currently is memory mapped. If it is false, but onFile, it is on disk, + // but not mmapped, and should be mapped! + // filePos is the position inside the file; size is the actual size, fsize is the size + // being used in the swap for this tile (may be larger!) + // The file points to 0 if it is not swapped, and to the relevant TempFile otherwise + struct TileInfo { KisTile *tile; KTempFile* file; off_t filePos; int size; int fsize; + TQValueList::iterator node; + bool inMem; bool onFile; bool mmapped; bool validNode; }; + typedef struct { KTempFile* file; off_t filePos; int size; } FreeInfo; + typedef TQMap TileMap; + typedef TQValueList TileList; + typedef TQValueList FreeList; + typedef TQValueVector FreeListList; + typedef TQValueList PoolFreeList; + typedef TQValueList FileList; + + + TileMap m_tileMap; + TileList m_swappableList; + FreeListList m_freeLists; + FileList m_files; + TQ_INT32 m_maxInMem; + TQ_INT32 m_currentInMem; + TQ_UINT32 m_swappiness; + TQ_INT32 m_tileSize; // size of a tile if it used 1 byte per pixel + unsigned long m_bytesInMem; + unsigned long m_bytesTotal; + + TQ_UINT8 **m_pools; + TQ_INT32 *m_poolPixelSizes; + TQ_INT32 m_tilesPerPool; + PoolFreeList *m_poolFreeList; + TQMutex * m_poolMutex; + TQMutex * m_swapMutex; + + // This is the constant that we will use to see if we want to add a new tempfile + // We use 1<<30 (one gigabyte) because aptqparently 32bit systems don't really like very + // large files. + static const long MaxSwapFileSize = 1<<30; // For debugging purposes: 1<<20 is a megabyte + + // debug + int counter; + +private: + void fromSwap(TileInfo* info); + void toSwap(TileInfo* info); + void doSwapping(); + void printInfo(); + TQ_UINT8* findTileFor(TQ_INT32 pixelSize); + bool isPoolTile(TQ_UINT8* data, TQ_INT32 pixelSize); + void reclaimTileToPool(TQ_UINT8* data, TQ_INT32 pixelSize); + + // Mmap wrapper that prints warnings on error. The result is stored in the *& result + // the return value is true on succes, false on failure. Other args as in man mmap + bool chalkMmap(TQ_UINT8*& result, void *start, size_t length, + int prot, int flags, int fd, off_t offset); +}; + +#endif // KIS_TILEMANAGER_H_ diff --git a/chalk/core/tiles/tests/Makefile.am b/chalk/core/tiles/tests/Makefile.am new file mode 100644 index 00000000..c546d7f5 --- /dev/null +++ b/chalk/core/tiles/tests/Makefile.am @@ -0,0 +1,15 @@ +AM_CPPFLAGS = -I$(srcdir)/../ \ + -I$(srcdir)/../.. \ + -I$(srcdir)/../../../sdk \ + $(all_includes) + +# The check_ target makes sure we don't install the modules, +# $(KDE_CHECK_PLUGIN) assures a shared library is created. +check_LTLIBRARIES = kunittest_kis_tiled_data_tester.la +kunittest_kis_tiled_data_tester_la_SOURCES = kis_tiled_data_tester.cpp +kunittest_kis_tiled_data_tester_la_LIBADD = -lkunittest ../../../libchalkcommon.la +kunittest_kis_tiled_data_tester_la_LDFLAGS = -module $(KDE_CHECK_PLUGIN) $(all_libraries) + +check-local: kunittest_kis_tiled_data_tester.la + kunittestmodrunner + diff --git a/chalk/core/tiles/tests/kis_tiled_data_tester.cpp b/chalk/core/tiles/tests/kis_tiled_data_tester.cpp new file mode 100644 index 00000000..15d3e50b --- /dev/null +++ b/chalk/core/tiles/tests/kis_tiled_data_tester.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2005 Adrian Page + * + * 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_tiled_data_tester.h" +#include "kis_datamanager.h" +#include "kis_global.h" + +using namespace KUnitTest; + +KUNITTEST_MODULE( kunittest_kis_tiled_data_tester, "Tiled Data Tester" ); +KUNITTEST_MODULE_REGISTER_TESTER( KisTiledDataTester ); + +#define TEST_PIXEL_SIZE 4 + +static TQ_UINT8 defaultPixel[TEST_PIXEL_SIZE] = {0, 0, 0, OPACITY_TRANSPARENT}; + +void KisTiledDataTester::allTests() +{ + KisDataManager *dm = new KisDataManager(TEST_PIXEL_SIZE, defaultPixel); + + TQ_INT32 extentX; + TQ_INT32 extentY; + TQ_INT32 extentWidth; + TQ_INT32 extentHeight; + + dm->extent(extentX, extentY, extentWidth, extentHeight); + CHECK(extentWidth, 0); + CHECK(extentHeight, 0); + + const TQ_UINT8 *readOnlyPixel = dm->pixel(KisTile::WIDTH/2, KisTile::HEIGHT/2); + dm->extent(extentX, extentY, extentWidth, extentHeight); + CHECK(extentWidth, 0); + CHECK(extentHeight, 0); + + TQ_UINT8 *writablePixel = dm->writablePixel(KisTile::WIDTH/2, KisTile::HEIGHT/2); + dm->extent(extentX, extentY, extentWidth, extentHeight); + CHECK(extentX, 0); + CHECK(extentY, 0); + CHECK(extentWidth, KisTile::WIDTH); + CHECK(extentHeight, KisTile::HEIGHT); + + writablePixel = dm->writablePixel(-KisTile::WIDTH, -KisTile::HEIGHT); + dm->extent(extentX, extentY, extentWidth, extentHeight); + CHECK(extentX, -KisTile::WIDTH); + CHECK(extentY, -KisTile::HEIGHT); + CHECK(extentWidth, 2*KisTile::WIDTH); + CHECK(extentHeight, 2*KisTile::HEIGHT); + + dm->clear(); + dm->extent(extentX, extentY, extentWidth, extentHeight); + CHECK(extentWidth, 0); + CHECK(extentHeight, 0); + + delete dm; +} + diff --git a/chalk/core/tiles/tests/kis_tiled_data_tester.h b/chalk/core/tiles/tests/kis_tiled_data_tester.h new file mode 100644 index 00000000..8a569d23 --- /dev/null +++ b/chalk/core/tiles/tests/kis_tiled_data_tester.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2005 Adrian Page + * + * 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. + */ + + +#ifndef KIS_TILED_DATA_TESTER_H +#define KIS_TILED_DATA_TESTER_H + +#include + +class KisTiledDataTester : public KUnitTest::Tester +{ +public: + void allTests(); +}; + +#endif + -- cgit v1.2.1