summaryrefslogtreecommitdiffstats
path: root/chalk/core/tiles
diff options
context:
space:
mode:
authortpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2011-06-26 00:41:16 +0000
committertpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2011-06-26 00:41:16 +0000
commit698569f8428ca088f764d704034a1330517b98c0 (patch)
treebf45be6946ebbbee9cce5a5bcf838f4c952d87e6 /chalk/core/tiles
parent2785103a6bd4de55bd26d79e34d0fdd4b329a73a (diff)
downloadkoffice-698569f8428ca088f764d704034a1330517b98c0.tar.gz
koffice-698569f8428ca088f764d704034a1330517b98c0.zip
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
Diffstat (limited to 'chalk/core/tiles')
-rw-r--r--chalk/core/tiles/Makefile.am23
-rw-r--r--chalk/core/tiles/kis_memento.cc154
-rw-r--r--chalk/core/tiles/kis_memento.h147
-rw-r--r--chalk/core/tiles/kis_tile.cc152
-rw-r--r--chalk/core/tiles/kis_tile.h87
-rw-r--r--chalk/core/tiles/kis_tile_global.h23
-rw-r--r--chalk/core/tiles/kis_tiled_random_accessor.cc115
-rw-r--r--chalk/core/tiles/kis_tiled_random_accessor.h66
-rw-r--r--chalk/core/tiles/kis_tileddatamanager.cc1044
-rw-r--r--chalk/core/tiles/kis_tileddatamanager.h233
-rw-r--r--chalk/core/tiles/kis_tiledhlineiterator.cc213
-rw-r--r--chalk/core/tiles/kis_tilediterator.cc131
-rw-r--r--chalk/core/tiles/kis_tilediterator.h213
-rw-r--r--chalk/core/tiles/kis_tiledrectiterator.cc242
-rw-r--r--chalk/core/tiles/kis_tiledvlineiterator.cc154
-rw-r--r--chalk/core/tiles/kis_tilemanager.cc578
-rw-r--r--chalk/core/tiles/kis_tilemanager.h139
-rw-r--r--chalk/core/tiles/tests/Makefile.am15
-rw-r--r--chalk/core/tiles/tests/kis_tiled_data_tester.cpp74
-rw-r--r--chalk/core/tiles/tests/kis_tiled_data_tester.h32
20 files changed, 3835 insertions, 0 deletions
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 <cbr@boemann.dk>
+ *
+ * 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 <cbr@boemann.dk>
+ *
+ * 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 <tqglobal.h>
+#include <tqrect.h>
+
+#include <ksharedptr.h>
+
+class KisTile;
+class KisTiledDataManager;
+
+class KisMemento;
+typedef KSharedPtr<KisMemento> 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 <freak@codepimps.org>
+ *
+ * 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 <assert.h>
+#include <kdebug.h>
+
+#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 <WIDTH * HEIGHT;i++)
+ {
+ memcpy(dst, pixel, m_pixelSize);
+ dst+=m_pixelSize;
+ }
+ removeReader();
+}
+
+void KisTile::addReader() const
+{
+ if (m_nReadlock++ == 0)
+ KisTileManager::instance()->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 <cbr@boemann.dk>
+ *
+ * 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 <tqglobal.h>
+#include <tqrect.h>
+
+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 <boud@valdyas.org>
+ *
+ * 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 <cberger@cberger.net>
+ *
+ * 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 <cberger@cberger.net>
+ *
+ * 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 <tqrect.h>
+#include <tqvaluelist.h>
+
+#include <ksharedptr.h>
+
+#include <kis_tileddatamanager.h>
+
+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 <cbr@boemann.dk>
+ *
+ * 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 <tqvaluevector.h>
+
+#include <kdebug.h>
+
+#include <KoStore.h>
+
+#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 <boud@valdyas.org>
+ *
+ * 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 <tqglobal.h>
+#include <tqvaluevector.h>
+
+#include <ksharedptr.h>
+
+#include "kis_tile_global.h"
+#include "kis_tile.h"
+#include "kis_memento.h"
+
+class KisTiledDataManager;
+typedef KSharedPtr<KisTiledDataManager> KisTiledDataManagerSP;
+
+class KisDataManager;
+typedef KSharedPtr<KisDataManager> 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<KisTileDataWrapper> 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 <cbr@boemann.dk>
+ *
+ * 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 <kdebug.h>
+
+#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 <cbr@boemann.dk>
+ *
+ * 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 <kdebug.h>
+
+#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 <cbr@boemann.dkt>
+ * 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 <tqglobal.h>
+
+#include <ksharedptr.h>
+
+#include <kis_tile.h>
+#include <kis_tileddatamanager.h>
+#include <koffice_export.h>
+/**
+ * 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 <cbr@boemann.dk>
+ *
+ * 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 <kdebug.h>
+
+#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 <cbr@boemann.dk>
+ *
+ * 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 <kdebug.h>
+
+#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 <kde@bartcoppens.be>
+ *
+ * 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 <kdebug.h>
+
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+
+#include <tqmutex.h>
+#include <tqthread.h>
+#include <tqfile.h>
+
+#include <kstaticdeleter.h>
+#include <kglobal.h>
+#include <kconfig.h>
+
+#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<KisTileManager> 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 <kde@bartcoppens.be>
+ *
+ * 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 <sys/types.h>
+
+#include <tqglobal.h>
+#include <tqmap.h>
+#include <tqvaluelist.h>
+#include <tqmutex.h>
+
+#include <ktempfile.h>
+
+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<TileInfo*>::iterator node;
+ bool inMem; bool onFile; bool mmapped; bool validNode; };
+ typedef struct { KTempFile* file; off_t filePos; int size; } FreeInfo;
+ typedef TQMap<const KisTile*, TileInfo*> TileMap;
+ typedef TQValueList<TileInfo*> TileList;
+ typedef TQValueList<FreeInfo*> FreeList;
+ typedef TQValueVector<FreeList> FreeListList;
+ typedef TQValueList<TQ_UINT8*> PoolFreeList;
+ typedef TQValueList<TempFile> 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 <adrian@pagenet.plus.com>
+ *
+ * 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 <kunittest/runner.h>
+#include <kunittest/module.h>
+
+#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 <adrian@pagenet.plus.com>
+ *
+ * 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 <kunittest/tester.h>
+
+class KisTiledDataTester : public KUnitTest::Tester
+{
+public:
+ void allTests();
+};
+
+#endif
+