Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_tiled_data_manager.cc
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2004 C. Boemann <cbo@boemann.dk>
3 * SPDX-FileCopyrightText: 2009 Dmitry Kazakov <dimula73@gmail.com>
4 * SPDX-FileCopyrightText: 2010 Cyrille Berger <cberger@cberger.net>
5 *
6 * SPDX-License-Identifier: GPL-2.0-or-later
7 */
8
9#include <QRect>
10#include <QVector>
11
12#include "kis_tile.h"
16#include "kis_memento_manager.h"
19
21
22#include "kis_global.h"
23
24
25/* The data area is divided into tiles each say 64x64 pixels (defined at compile time)
26 * The tiles are laid out in a matrix that can have negative indexes.
27 * The matrix grows automatically if needed (a call for writeaccess to a tile
28 * outside the current extent)
29 * Even though the matrix has grown it may still not contain tiles at specific positions.
30 * They are created on demand
31 */
32
34 const quint8 *defaultPixel)
35{
36 /* See comment in destructor for details */
39
41 m_defaultPixel = new quint8[m_pixelSize];
43}
44
46 : KisShared()
47{
48 /* See comment in destructor for details */
49
50 /* We do not clone the history of the device, there is no usecase for it */
52
53 KisTileData *defaultTileData = dm.m_hashTable->refAndFetchDefaultTileData();
54 m_mementoManager->setDefaultTileData(defaultTileData);
55 defaultTileData->deref();
56
58
60 m_defaultPixel = new quint8[m_pixelSize];
67}
68
84
85void KisTiledDataManager::setDefaultPixel(const quint8 *defaultPixel)
86{
87 QWriteLocker locker(&m_lock);
89}
90
99
101{
102 QReadLocker locker(&m_lock);
103
104 bool retval = true;
105
107 char str[80];
108 sprintf(str, "%d\n", m_hashTable->numTiles());
109 retval = store.write(str, strlen(str));
110 }
111 else {
112 retval = writeTilesHeader(store, m_hashTable->numTiles());
113 }
114
115
117 KisTileSP tile;
118
119 KisAbstractTileCompressorSP compressor =
121
122 while ((tile = iter.tile())) {
123 retval = compressor->writeTile(tile, store);
124 if (!retval) {
125 warnFile << "Failed to write tile";
126 break;
127 }
128 iter.next();
129 }
130
131 return retval;
132}
133bool KisTiledDataManager::read(QIODevice *stream)
134{
135 clear();
136
137 QWriteLocker locker(&m_lock);
139
140 if (!stream) {
142 return false;
143 }
144
145 const qint32 maxLineLength = 79; // Legacy magic
146 QByteArray line = stream->readLine(maxLineLength);
147 line = line.trimmed();
148
149 quint32 numTiles;
150 qint32 tilesVersion = LEGACY_VERSION;
151
152 if (line[0] == 'V') {
153 QList<QByteArray> lineItems = line.split(' ');
154
155 QString keyword = lineItems.takeFirst();
156 Q_ASSERT(keyword == "VERSION");
157
158 tilesVersion = lineItems.takeFirst().toInt();
159
160 if(!processTilesHeader(stream, numTiles))
161 return false;
162 }
163 else {
164 numTiles = line.toUInt();
165 }
166
167 KisAbstractTileCompressorSP compressor =
169
170 bool readSuccess = true;
171 for (quint32 i = 0; i < numTiles; i++) {
172 if (!compressor->readTile(stream, this)) {
173 readSuccess = false;
174 }
175 }
176
178 return readSuccess;
179}
180
182{
183 QString buffer;
184
185 buffer = QString("VERSION %1\n"
186 "TILEWIDTH %2\n"
187 "TILEHEIGHT %3\n"
188 "PIXELSIZE %4\n"
189 "DATA %5\n")
190 .arg(CURRENT_VERSION)
193 .arg(pixelSize())
194 .arg(numTiles);
195
196 return store.write(buffer.toLatin1());
197}
198
199#define takeOneLine(stream, maxLine, keyword, value) \
200 do { \
201 QByteArray line = stream->readLine(maxLine); \
202 line = line.trimmed(); \
203 QList<QByteArray> lineItems = line.split(' '); \
204 keyword = lineItems.takeFirst(); \
205 value = lineItems.takeFirst().toInt(); \
206 } while(0) \
207
208
209bool KisTiledDataManager::processTilesHeader(QIODevice *stream, quint32 &numTiles)
210{
217 const qint32 maxLineLength = 25;
218 const qint32 totalNumTests = 4;
219 bool foundDataMark = false;
220 qint32 testsPassed = 0;
221
222 QString keyword;
223 qint32 value;
224
225 while(!foundDataMark && stream->canReadLine()) {
226 takeOneLine(stream, maxLineLength, keyword, value);
227
228 if (keyword == "TILEWIDTH") {
230 goto wrongString;
231 }
232 else if (keyword == "TILEHEIGHT") {
234 goto wrongString;
235 }
236 else if (keyword == "PIXELSIZE") {
237 if((quint32)value != pixelSize())
238 goto wrongString;
239 }
240 else if (keyword == "DATA") {
241 numTiles = value;
242 foundDataMark = true;
243 }
244 else {
245 goto wrongString;
246 }
247
248 testsPassed++;
249 }
250
251 if(testsPassed != totalNumTests) {
252 warnTiles << "Not enough fields of tiles header present"
253 << testsPassed << "of" << totalNumTests;
254 }
255
256 return testsPassed == totalNumTests;
257
258wrongString:
259 warnTiles << "Wrong string in tiles header:" << keyword << value;
260 return false;
261}
262
263void KisTiledDataManager::purge(const QRect& area)
264{
265 QList<KisTileSP> tilesToDelete;
266 {
267 const qint32 tileDataSize = KisTileData::HEIGHT * KisTileData::WIDTH * pixelSize();
269 tileData->blockSwapping();
270 const quint8 *defaultData = tileData->data();
271
273 KisTileSP tile;
274
275 while ((tile = iter.tile())) {
276 if (tile->extent().intersects(area)) {
277 tile->lockForRead();
278 if(memcmp(defaultData, tile->data(), tileDataSize) == 0) {
279 tilesToDelete.push_back(tile);
280 }
281 tile->unlockForRead();
282 }
283 iter.next();
284 }
285
286 tileData->unblockSwapping();
287 tileData->deref();
288 }
289 Q_FOREACH (KisTileSP tile, tilesToDelete) {
290 if (m_hashTable->deleteTile(tile)) {
291 m_extentManager.notifyTileRemoved(tile->col(), tile->row());
292 }
293 }
294}
295
296quint8* KisTiledDataManager::duplicatePixel(qint32 num, const quint8 *pixel)
297{
298 const qint32 pixelSize = this->pixelSize();
299 /* FIXME: Make a fun filling here */
300 quint8 *dstBuf = new quint8[num * pixelSize];
301 quint8 *dstIt = dstBuf;
302 for (qint32 i = 0; i < num; i++) {
303 memcpy(dstIt, pixel, pixelSize);
304 dstIt += pixelSize;
305 }
306 return dstBuf;
307}
308
309void KisTiledDataManager::clear(QRect clearRect, const quint8 *clearPixel)
310{
311 if (clearPixel == 0)
312 clearPixel = m_defaultPixel;
313
314 if (clearRect.isEmpty())
315 return;
316
317 const qint32 pixelSize = this->pixelSize();
318
319 bool pixelBytesAreDefault = !memcmp(clearPixel, m_defaultPixel, pixelSize);
320
321 bool pixelBytesAreTheSame = true;
322 for (qint32 i = 0; i < pixelSize; ++i) {
323 if (clearPixel[i] != clearPixel[0]) {
324 pixelBytesAreTheSame = false;
325 break;
326 }
327 }
328
329 if (pixelBytesAreDefault) {
330 clearRect &= m_extentManager.extent();
331 }
332
333 qint32 firstColumn = xToCol(clearRect.left());
334 qint32 lastColumn = xToCol(clearRect.right());
335
336 qint32 firstRow = yToRow(clearRect.top());
337 qint32 lastRow = yToRow(clearRect.bottom());
338
339 const quint32 rowStride = KisTileData::WIDTH * pixelSize;
340
341 // Generate one row
342 quint8 *clearPixelData = 0;
343 quint32 maxRunLength = qMin(clearRect.width(), KisTileData::WIDTH);
344 clearPixelData = duplicatePixel(maxRunLength, clearPixel);
345
346 KisTileData *td = 0;
347 if (!pixelBytesAreDefault &&
348 clearRect.width() >= KisTileData::WIDTH &&
349 clearRect.height() >= KisTileData::HEIGHT) {
350
352 td->acquire();
353 }
354
355 for (qint32 row = firstRow; row <= lastRow; ++row) {
356 for (qint32 column = firstColumn; column <= lastColumn; ++column) {
357
358 QRect tileRect(column*KisTileData::WIDTH, row*KisTileData::HEIGHT,
360 QRect clearTileRect = clearRect & tileRect;
361
362 if (clearTileRect == tileRect) {
363 // Clear whole tile
364 const bool wasDeleted =
365 m_hashTable->deleteTile(column, row);
366
367 if (wasDeleted) {
369 }
370
371
372 if (!pixelBytesAreDefault) {
373 KisTileSP clearedTile = KisTileSP(new KisTile(column, row, td, m_mementoManager));
374 m_hashTable->addTile(clearedTile);
375 m_extentManager.notifyTileAdded(column, row);
376 }
377 } else {
378 const qint32 lineSize = clearTileRect.width() * pixelSize;
379 qint32 rowsRemaining = clearTileRect.height();
380
381 KisTileDataWrapper tw(this,
382 clearTileRect.left(),
383 clearTileRect.top(),
385 quint8* tileIt = tw.data();
386
387 if (pixelBytesAreTheSame) {
388 while (rowsRemaining > 0) {
389 memset(tileIt, *clearPixelData, lineSize);
390 tileIt += rowStride;
391 rowsRemaining--;
392 }
393 } else {
394 while (rowsRemaining > 0) {
395 memcpy(tileIt, clearPixelData, lineSize);
396 tileIt += rowStride;
397 rowsRemaining--;
398 }
399 }
400 }
401 }
402 }
403
404 if (td) td->release();
405 delete[] clearPixelData;
406}
407
408void KisTiledDataManager::clear(QRect clearRect, quint8 clearValue)
409{
410 quint8 *buf = new quint8[pixelSize()];
411 memset(buf, clearValue, pixelSize());
412 clear(clearRect, buf);
413 delete[] buf;
414}
415
416void KisTiledDataManager::clear(qint32 x, qint32 y, qint32 w, qint32 h, const quint8 *clearPixel)
417{
418 clear(QRect(x, y, w, h), clearPixel);
419}
420void KisTiledDataManager::clear(qint32 x, qint32 y, qint32 w, qint32 h, quint8 clearValue)
421{
422 clear(QRect(x, y, w, h), clearValue);
423}
424
430
431
432template<bool useOldSrcData>
434{
435 if (rect.isEmpty()) return;
436
437 const qint32 pixelSize = this->pixelSize();
438 const bool defaultPixelsCoincide =
439 !memcmp(srcDM->defaultPixel(), m_defaultPixel, pixelSize);
440
441 const quint32 rowStride = KisTileData::WIDTH * pixelSize;
442
443 qint32 firstColumn = xToCol(rect.left());
444 qint32 lastColumn = xToCol(rect.right());
445
446 qint32 firstRow = yToRow(rect.top());
447 qint32 lastRow = yToRow(rect.bottom());
448
449 for (qint32 row = firstRow; row <= lastRow; ++row) {
450 for (qint32 column = firstColumn; column <= lastColumn; ++column) {
451
452 bool srcTileExists = false;
453
454 // this is the only variation in the template
455 KisTileSP srcTile = useOldSrcData ?
456 srcDM->getOldTile(column, row, srcTileExists) :
457 srcDM->getReadOnlyTileLazy(column, row, srcTileExists);
458
459 QRect tileRect(column*KisTileData::WIDTH, row*KisTileData::HEIGHT,
461 QRect cloneTileRect = rect & tileRect;
462
463 if (cloneTileRect == tileRect) {
464 // Clone whole tile
465 const bool wasDeleted =
466 m_hashTable->deleteTile(column, row);
467
468 if (srcTileExists || !defaultPixelsCoincide) {
469 srcTile->lockForRead();
470 KisTileData *td = srcTile->tileData();
471 KisTileSP clonedTile = KisTileSP(new KisTile(column, row, td, m_mementoManager));
472 srcTile->unlockForRead();
473
474 m_hashTable->addTile(clonedTile);
475
476 if (!wasDeleted) {
477 m_extentManager.notifyTileAdded(column, row);
478 }
479 } else if (wasDeleted) {
481 }
482
483 } else {
484 const qint32 lineSize = cloneTileRect.width() * pixelSize;
485 qint32 rowsRemaining = cloneTileRect.height();
486
487 KisTileDataWrapper tw(this,
488 cloneTileRect.left(),
489 cloneTileRect.top(),
491 srcTile->lockForRead();
492 // We suppose that the shift in both tiles is the same
493 const quint8* srcTileIt = srcTile->data() + tw.offset();
494 quint8* dstTileIt = tw.data();
495
496 while (rowsRemaining > 0) {
497 memcpy(dstTileIt, srcTileIt, lineSize);
498 srcTileIt += rowStride;
499 dstTileIt += rowStride;
500 rowsRemaining--;
501 }
502
503 srcTile->unlockForRead();
504 }
505 }
506 }
507}
508
509template<bool useOldSrcData>
511{
512 if (rect.isEmpty()) return;
513
514 const qint32 pixelSize = this->pixelSize();
515 const bool defaultPixelsCoincide =
516 !memcmp(srcDM->defaultPixel(), m_defaultPixel, pixelSize);
517
518 qint32 firstColumn = xToCol(rect.left());
519 qint32 lastColumn = xToCol(rect.right());
520
521 qint32 firstRow = yToRow(rect.top());
522 qint32 lastRow = yToRow(rect.bottom());
523
524 for (qint32 row = firstRow; row <= lastRow; ++row) {
525 for (qint32 column = firstColumn; column <= lastColumn; ++column) {
526
532 bool srcTileExists = false;
533
534 // this is the only variation in the template
535 KisTileSP srcTile = useOldSrcData ?
536 srcDM->getOldTile(column, row, srcTileExists) :
537 srcDM->getReadOnlyTileLazy(column, row, srcTileExists);
538
539 const bool wasDeleted =
540 m_hashTable->deleteTile(column, row);
541
542 if (srcTileExists || !defaultPixelsCoincide) {
543 srcTile->lockForRead();
544 KisTileData *td = srcTile->tileData();
545 KisTileSP clonedTile = KisTileSP(new KisTile(column, row, td, m_mementoManager));
546 srcTile->unlockForRead();
547
548 m_hashTable->addTile(clonedTile);
549
550 if (!wasDeleted) {
551 m_extentManager.notifyTileAdded(column, row);
552 }
553 } else if (wasDeleted) {
555 }
556 }
557 }
558}
559
561{
562 bitBltImpl<false>(srcDM, rect);
563}
564
566{
567 bitBltImpl<true>(srcDM, rect);
568}
569
571{
572 bitBltRoughImpl<false>(srcDM, rect);
573}
574
576{
577 bitBltRoughImpl<true>(srcDM, rect);
578}
579
580void KisTiledDataManager::setExtent(qint32 x, qint32 y, qint32 w, qint32 h)
581{
582 setExtent(QRect(x, y, w, h));
583}
584
586{
587 QRect oldRect = extent();
588 newRect = newRect.normalized();
589
590 // Do nothing if the desired size is bigger than we currently are:
591 // that is handled by the autoextending automatically
592 if (newRect.contains(oldRect)) return;
593
594 KisTileSP tile;
595 QRect tileRect;
596 {
598
599 while (!iter.isDone()) {
600 tile = iter.tile();
601
602 tileRect = tile->extent();
603 if (newRect.contains(tileRect)) {
604 //do nothing
605 iter.next();
606 } else if (newRect.intersects(tileRect)) {
607 QRect intersection = newRect & tileRect;
608 intersection.translate(- tileRect.topLeft());
609
610 const qint32 pixelSize = this->pixelSize();
611
612 tile->lockForWrite();
613 quint8* data = tile->data();
614 quint8* ptr;
615
616 /* FIXME: make it faster */
617 for (int y = 0; y < KisTileData::HEIGHT; y++) {
618 for (int x = 0; x < KisTileData::WIDTH; x++) {
619 if (!intersection.contains(x, y)) {
620 ptr = data + pixelSize * (y * KisTileData::WIDTH + x);
621 memcpy(ptr, m_defaultPixel, pixelSize);
622 }
623 }
624 }
625 tile->unlockForWrite();
626 iter.next();
627 } else {
628 m_extentManager.notifyTileRemoved(tile->col(), tile->row());
629 iter.deleteCurrent();
630 }
631 }
632 }
633}
634
636{
637 QVector<QPoint> indexes;
638
639 {
641 KisTileSP tile;
642
643 while ((tile = iter.tile())) {
644 indexes << QPoint(tile->col(), tile->row());
645 iter.next();
646 }
647 }
648
650}
651
652void KisTiledDataManager::extent(qint32 &x, qint32 &y, qint32 &w, qint32 &h) const
653{
654 QRect rect = extent();
655 rect.getRect(&x, &y, &w, &h);
656}
657
659{
660 return m_extentManager.extent();
661}
662
664{
665 QVector<QRect> rects;
666
668 KisTileSP tile;
669
670 while ((tile = iter.tile())) {
671 rects << tile->extent();
672 iter.next();
673 }
674
675 return KisRegion(std::move(rects));
676}
677
678void KisTiledDataManager::setPixel(qint32 x, qint32 y, const quint8 * data)
679{
681 memcpy(tw.data(), data, pixelSize());
682}
683
684void KisTiledDataManager::writeBytes(const quint8 *data,
685 qint32 x, qint32 y,
686 qint32 width, qint32 height,
687 qint32 dataRowStride)
688{
689 QWriteLocker locker(&m_lock);
690 // Actual bytes reading/writing is done in private header
691 writeBytesBody(data, x, y, width, height, dataRowStride);
692}
693
695 qint32 x, qint32 y,
696 qint32 width, qint32 height,
697 qint32 dataRowStride) const
698{
699 QReadLocker locker(&m_lock);
700 // Actual bytes reading/writing is done in private header
701 readBytesBody(data, x, y, width, height, dataRowStride);
702}
703
706 qint32 x, qint32 y,
707 qint32 width, qint32 height) const
708{
709 QReadLocker locker(&m_lock);
710 // Actual bytes reading/writing is done in private header
711 return readPlanarBytesBody(channelSizes, x, y, width, height);
712}
713
714
716 QVector<qint32> channelSizes,
717 qint32 x, qint32 y,
718 qint32 width, qint32 height)
719{
720 QWriteLocker locker(&m_lock);
721 // Actual bytes reading/writing is done in private header
722
723 bool allChannelsPresent = true;
724
725 Q_FOREACH (const quint8* plane, planes) {
726 if (!plane) {
727 allChannelsPresent = false;
728 break;
729 }
730 }
731
732 if (allChannelsPresent) {
733 writePlanarBytesBody<true>(planes, channelSizes, x, y, width, height);
734 } else {
735 writePlanarBytesBody<false>(planes, channelSizes, x, y, width, height);
736 }
737}
738
739qint32 KisTiledDataManager::numContiguousColumns(qint32 x, qint32 minY, qint32 maxY) const
740{
741 qint32 numColumns;
742
743 Q_UNUSED(minY);
744 Q_UNUSED(maxY);
745
746 if (x >= 0) {
747 numColumns = KisTileData::WIDTH - (x % KisTileData::WIDTH);
748 } else {
749 numColumns = ((-x - 1) % KisTileData::WIDTH) + 1;
750 }
751
752 return numColumns;
753}
754
755qint32 KisTiledDataManager::numContiguousRows(qint32 y, qint32 minX, qint32 maxX) const
756{
757 qint32 numRows;
758
759 Q_UNUSED(minX);
760 Q_UNUSED(maxX);
761
762 if (y >= 0) {
764 } else {
765 numRows = ((-y - 1) % KisTileData::HEIGHT) + 1;
766 }
767
768 return numRows;
769}
770
771qint32 KisTiledDataManager::rowStride(qint32 x, qint32 y) const
772{
773 Q_UNUSED(x);
774 Q_UNUSED(y);
775
776 return KisTileData::WIDTH * pixelSize();
777}
778
float value(const T *src, size_t ch)
void setDefaultTileData(KisTileData *defaultTileData)
virtual bool write(const QByteArray &data)=0
static KisAbstractTileCompressorSP create(qint32 version)
KisTileData * createDefaultTileData(qint32 pixelSize, const quint8 *defPixel)
static KisTileDataStore * instance()
void unblockSwapping()
void blockSwapping()
quint8 * data() const
static void releaseInternalPools()
static const qint32 HEIGHT
static const qint32 WIDTH
std::enable_if< std::is_same< Helper, QWriteLocker >::value, void >::type deleteCurrent()
KisTileData * refAndFetchDefaultTileData() const
void addTile(TileTypeSP tile)
bool deleteTile(TileTypeSP tile)
void setDefaultTileData(KisTileData *defaultTileData)
qint32 row() const
Definition kis_tile.h:92
void lockForWrite()
Definition kis_tile.cc:221
qint32 col() const
Definition kis_tile.h:95
void lockForRead() const
Definition kis_tile.cc:208
QRect extent() const
Definition kis_tile.h:99
KisTileData * tileData() const
Definition kis_tile.h:118
void unlockForWrite()
Definition kis_tile.cc:264
void unlockForRead() const
Definition kis_tile.cc:275
quint8 * data() const
Definition kis_tile.h:85
void writePlanarBytes(QVector< quint8 * > planes, QVector< qint32 > channelsizes, qint32 x, qint32 y, qint32 w, qint32 h)
void readBytesBody(quint8 *data, qint32 x, qint32 y, qint32 width, qint32 height, qint32 dataRowStride=-1) const
QVector< quint8 * > readPlanarBytesBody(QVector< qint32 > channelsizes, qint32 x, qint32 y, qint32 w, qint32 h) const
void bitBltImpl(KisTiledDataManager *srcDM, const QRect &rect)
qint32 rowStride(qint32 x, qint32 y) const
QVector< quint8 * > readPlanarBytes(QVector< qint32 > channelsizes, qint32 x, qint32 y, qint32 w, qint32 h) const
void readBytes(quint8 *bytes, qint32 x, qint32 y, qint32 w, qint32 h, qint32 dataRowStride=-1) const
KisMementoManager * m_mementoManager
void bitBltRoughImpl(KisTiledDataManager *srcDM, const QRect &rect)
KisTiledExtentManager m_extentManager
qint32 yToRow(qint32 y) const
static const qint32 LEGACY_VERSION
bool writeTilesHeader(KisPaintDeviceWriter &store, quint32 numTiles)
const quint8 * defaultPixel() const
void writeBytes(const quint8 *bytes, qint32 x, qint32 y, qint32 w, qint32 h, qint32 dataRowStride=-1)
void bitBltOldData(KisTiledDataManager *srcDM, const QRect &rect)
qint32 numContiguousRows(qint32 y, qint32 minX, qint32 maxX) const
void setPixel(qint32 x, qint32 y, const quint8 *data)
void setDefaultPixel(const quint8 *defPixel)
bool processTilesHeader(QIODevice *stream, quint32 &numTiles)
qint32 numContiguousColumns(qint32 x, qint32 minY, qint32 maxY) const
static const qint32 CURRENT_VERSION
void writeBytesBody(const quint8 *data, qint32 x, qint32 y, qint32 width, qint32 height, qint32 dataRowStride=-1)
KisTiledDataManager(quint32 pixelSize, const quint8 *defPixel)
bool read(QIODevice *stream)
qint32 xToCol(qint32 x) const
quint8 * duplicatePixel(qint32 num, const quint8 *pixel)
void bitBltRoughOldData(KisTiledDataManager *srcDM, const QRect &rect)
KisTileSP getOldTile(qint32 col, qint32 row, bool &existingTile)
void bitBlt(KisTiledDataManager *srcDM, const QRect &rect)
KisTileHashTable * m_hashTable
void setDefaultPixelImpl(const quint8 *defPixel)
bool write(KisPaintDeviceWriter &store)
void bitBltRough(KisTiledDataManager *srcDM, const QRect &rect)
KisTileSP getReadOnlyTileLazy(qint32 col, qint32 row, bool &existingTile)
void setExtent(qint32 x, qint32 y, qint32 w, qint32 h)
void purge(const QRect &area)
void notifyTileAdded(qint32 col, qint32 row)
void replaceTileStats(const QVector< QPoint > &indexes)
void notifyTileRemoved(qint32 col, qint32 row)
#define warnFile
Definition kis_debug.h:95
#define warnTiles
Definition kis_debug.h:91
KisSharedPtr< KisTile > KisTileSP
Definition kis_tile.h:27
KisTileHashTableTraits< KisTile > KisTileHashTable
#define takeOneLine(stream, maxLine, keyword, value)