File indexing completed on 2024-12-22 04:10:30
0001 /* 0002 * SPDX-FileCopyrightText: 2010 Dmitry Kazakov <dimula73@gmail.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "kis_tile_compressor_2.h" 0008 #include "kis_lzf_compression.h" 0009 #include <QIODevice> 0010 #include "kis_paint_device_writer.h" 0011 #define TILE_DATA_SIZE(pixelSize) ((pixelSize) * KisTileData::WIDTH * KisTileData::HEIGHT) 0012 0013 const QString KisTileCompressor2::m_compressionName = "LZF"; 0014 0015 0016 KisTileCompressor2::KisTileCompressor2() 0017 { 0018 m_compression = new KisLzfCompression(); 0019 } 0020 0021 KisTileCompressor2::~KisTileCompressor2() 0022 { 0023 delete m_compression; 0024 } 0025 0026 bool KisTileCompressor2::writeTile(KisTileSP tile, KisPaintDeviceWriter &store) 0027 { 0028 const qint32 tileDataSize = TILE_DATA_SIZE(tile->pixelSize()); 0029 prepareStreamingBuffer(tileDataSize); 0030 0031 qint32 bytesWritten; 0032 0033 tile->lockForRead(); 0034 compressTileData(tile->tileData(), (quint8*)m_streamingBuffer.data(), 0035 m_streamingBuffer.size(), bytesWritten); 0036 tile->unlockForRead(); 0037 0038 QString header = getHeader(tile, bytesWritten); 0039 bool retval = true; 0040 retval = store.write(header.toLatin1()); 0041 if (!retval) { 0042 warnFile << "Failed to write the tile header"; 0043 } 0044 retval = store.write(m_streamingBuffer.data(), bytesWritten); 0045 if (!retval) { 0046 warnFile << "Failed to write the tile data"; 0047 } 0048 return retval; 0049 } 0050 0051 bool KisTileCompressor2::readTile(QIODevice *stream, KisTiledDataManager *dm) 0052 { 0053 const qint32 tileDataSize = TILE_DATA_SIZE(pixelSize(dm)); 0054 prepareStreamingBuffer(tileDataSize); 0055 0056 QByteArray header = stream->readLine(maxHeaderLength()); 0057 0058 QList<QByteArray> headerItems = header.trimmed().split(','); 0059 if (headerItems.size() == 4) { 0060 qint32 x = headerItems.takeFirst().toInt(); 0061 qint32 y = headerItems.takeFirst().toInt(); 0062 QString compressionName = headerItems.takeFirst(); 0063 qint32 dataSize = headerItems.takeFirst().toInt(); 0064 0065 Q_ASSERT(headerItems.isEmpty()); 0066 Q_ASSERT(compressionName == m_compressionName); 0067 0068 qint32 row = yToRow(dm, y); 0069 qint32 col = xToCol(dm, x); 0070 0071 KisTileSP tile = dm->getTile(col, row, true); 0072 0073 stream->read(m_streamingBuffer.data(), dataSize); 0074 0075 tile->lockForWrite(); 0076 bool res = decompressTileData((quint8*)m_streamingBuffer.data(), dataSize, tile->tileData()); 0077 tile->unlockForWrite(); 0078 return res; 0079 } 0080 return false; 0081 } 0082 0083 void KisTileCompressor2::prepareStreamingBuffer(qint32 tileDataSize) 0084 { 0085 /** 0086 * TODO: delete this buffer! 0087 * It is better to use one of other two buffers to store streams 0088 */ 0089 m_streamingBuffer.resize(tileDataSize + 1); 0090 } 0091 0092 void KisTileCompressor2::prepareWorkBuffers(qint32 tileDataSize) 0093 { 0094 const qint32 bufferSize = m_compression->outputBufferSize(tileDataSize); 0095 0096 if (m_linearizationBuffer.size() < tileDataSize) { 0097 m_linearizationBuffer.resize(tileDataSize); 0098 } 0099 0100 if (m_compressionBuffer.size() < bufferSize) { 0101 m_compressionBuffer.resize(bufferSize); 0102 } 0103 } 0104 0105 void KisTileCompressor2::compressTileData(KisTileData *tileData, 0106 quint8 *buffer, 0107 qint32 bufferSize, 0108 qint32 &bytesWritten) 0109 { 0110 const qint32 pixelSize = tileData->pixelSize(); 0111 const qint32 tileDataSize = TILE_DATA_SIZE(pixelSize); 0112 qint32 compressedBytes; 0113 0114 Q_UNUSED(bufferSize); 0115 Q_ASSERT(bufferSize >= tileDataSize + 1); 0116 0117 prepareWorkBuffers(tileDataSize); 0118 0119 KisAbstractCompression::linearizeColors(tileData->data(), (quint8*)m_linearizationBuffer.data(), 0120 tileDataSize, pixelSize); 0121 0122 compressedBytes = m_compression->compress((quint8*)m_linearizationBuffer.data(), tileDataSize, 0123 (quint8*)m_compressionBuffer.data(), m_compressionBuffer.size()); 0124 0125 if(compressedBytes < tileDataSize) { 0126 buffer[0] = COMPRESSED_DATA_FLAG; 0127 memcpy(buffer + 1, m_compressionBuffer.data(), compressedBytes); 0128 bytesWritten = compressedBytes + 1; 0129 } 0130 else { 0131 buffer[0] = RAW_DATA_FLAG; 0132 memcpy(buffer + 1, tileData->data(), tileDataSize); 0133 bytesWritten = tileDataSize + 1; 0134 } 0135 } 0136 0137 bool KisTileCompressor2::decompressTileData(quint8 *buffer, 0138 qint32 bufferSize, 0139 KisTileData *tileData) 0140 { 0141 const qint32 pixelSize = tileData->pixelSize(); 0142 const qint32 tileDataSize = TILE_DATA_SIZE(pixelSize); 0143 0144 if(buffer[0] == COMPRESSED_DATA_FLAG) { 0145 prepareWorkBuffers(tileDataSize); 0146 0147 qint32 bytesWritten; 0148 bytesWritten = m_compression->decompress(buffer + 1, bufferSize - 1, 0149 (quint8*)m_linearizationBuffer.data(), tileDataSize); 0150 if (bytesWritten == tileDataSize) { 0151 KisAbstractCompression::delinearizeColors((quint8*)m_linearizationBuffer.data(), 0152 tileData->data(), 0153 tileDataSize, pixelSize); 0154 return true; 0155 } 0156 return false; 0157 } 0158 else { 0159 memcpy(tileData->data(), buffer + 1, tileDataSize); 0160 return true; 0161 } 0162 return false; 0163 0164 } 0165 0166 qint32 KisTileCompressor2::tileDataBufferSize(KisTileData *tileData) 0167 { 0168 return TILE_DATA_SIZE(tileData->pixelSize()) + 1; 0169 } 0170 0171 inline qint32 KisTileCompressor2::maxHeaderLength() 0172 { 0173 static const qint32 QINT32_LENGTH = 11; 0174 static const qint32 COMPRESSION_NAME_LENGTH = 5; 0175 static const qint32 SEPARATORS_LENGTH = 4; 0176 0177 return 3 * QINT32_LENGTH + COMPRESSION_NAME_LENGTH + SEPARATORS_LENGTH; 0178 } 0179 0180 inline QString KisTileCompressor2::getHeader(KisTileSP tile, 0181 qint32 compressedSize) 0182 { 0183 qint32 x, y; 0184 qint32 width, height; 0185 tile->extent().getRect(&x, &y, &width, &height); 0186 0187 return QString("%1,%2,%3,%4\n").arg(x).arg(y).arg(m_compressionName).arg(compressedSize); 0188 }