File indexing completed on 2024-04-28 15:25:45

0001 /*
0002     Run-Length Encoding utilities.
0003     SPDX-FileCopyrightText: 2014-2015 Alex Merry <alex.merry@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #ifndef KIMAGEFORMATS_RLE_P_H
0009 #define KIMAGEFORMATS_RLE_P_H
0010 
0011 #include <QDataStream>
0012 #include <QDebug>
0013 
0014 /**
0015  * The RLEVariant to use.
0016  *
0017  * This mostly concerns what to do values >= 128.
0018  */
0019 enum class RLEVariant {
0020     /**
0021      * PackBits-style RLE
0022      *
0023      * Value 128 is ignored, 129 indicates a repetition
0024      * of size 2, 130 of size 3, up to 255 of size 128.
0025      */
0026     PackBits,
0027     /**
0028      * Same as PackBits, but treat unpacked data as
0029      * 16-bit integers.
0030      */
0031     PackBits16,
0032     /**
0033      * PIC-style RLE
0034      *
0035      * Value 128 indicates a 16-bit repetition count
0036      * follows, while 129 indicates a repetition
0037      * of size 128, 130 of size 127, down to 255 of
0038      * size 2.
0039      */
0040     PIC,
0041 };
0042 
0043 /**
0044  * Decodes data written in run-length encoding format.
0045  *
0046  * This is intended to be used with lambda functions.
0047  *
0048  * Note that this functions expects that, at the current location in @p stream,
0049  * exactly @p length items have been encoded as a unit (and so it will not be
0050  * partway through a run when it has decoded @p length items). If this is not
0051  * the case, it will return @c false.
0052  *
0053  * @param variant     The RLE variant to decode.
0054  * @param stream      The stream to read the data from.
0055  * @param buf         The location to write the decoded data.
0056  * @param length      The number of items to read.
0057  * @param readData    A function that takes a QDataStream reference and reads a
0058  *                    single value.
0059  * @param updateItem  A function that takes an item from @p buf and the result
0060  *                    of a readData call, and produces the item that should be
0061  *                    written to @p buf.
0062  *
0063  * @returns @c true if @p length items in mixed RLE were successfully read
0064  *          into @p buf, @c false otherwise.
0065  */
0066 template<typename Item, typename Func1, typename Func2>
0067 static inline bool decodeRLEData(RLEVariant variant, QDataStream &stream, Item *dest, quint32 length, Func1 readData, Func2 updateItem)
0068 {
0069     unsigned offset = 0; // in dest
0070     bool is_msb = true; // only used for 16-bit PackBits, data is big-endian
0071     quint16 temp_data = 0;
0072     while (offset < length) {
0073         unsigned remaining = length - offset;
0074         quint8 count1;
0075         stream >> count1;
0076 
0077         if (count1 >= 128u) {
0078             unsigned length = 0;
0079             if (variant == RLEVariant::PIC) {
0080                 if (count1 == 128u) {
0081                     // If the value is exactly 128, it means that it is more than
0082                     // 127 repetitions
0083                     quint16 count2;
0084                     stream >> count2;
0085                     length = count2;
0086                 } else {
0087                     // 2 to 128 repetitions
0088                     length = count1 - 127u;
0089                 }
0090             } else if (variant == RLEVariant::PackBits || variant == RLEVariant::PackBits16) {
0091                 if (count1 == 128u) {
0092                     // Ignore value 128
0093                     continue;
0094                 } else {
0095                     // 128 to 2 repetitions
0096                     length = 257u - count1;
0097                 }
0098             } else {
0099                 Q_ASSERT(false);
0100             }
0101             if (length > remaining) {
0102                 qDebug() << "Row overrun:" << length << ">" << remaining;
0103                 return false;
0104             }
0105             auto datum = readData(stream);
0106             for (unsigned i = offset; i < offset + length; ++i) {
0107                 if (variant == RLEVariant::PackBits16) {
0108                     if (is_msb) {
0109                         temp_data = datum << 8;
0110                         is_msb = false;
0111                     } else {
0112                         temp_data |= datum;
0113                         dest[i >> 1] = updateItem(dest[i >> 1], temp_data);
0114                         is_msb = true;
0115                     }
0116                 } else {
0117                     dest[i] = updateItem(dest[i], datum);
0118                 }
0119             }
0120             offset += length;
0121         } else {
0122             // No repetitions
0123             unsigned length = count1 + 1u;
0124             if (length > remaining) {
0125                 qDebug() << "Row overrun:" << length << ">" << remaining;
0126                 return false;
0127             }
0128             for (unsigned i = offset; i < offset + length; ++i) {
0129                 auto datum = readData(stream);
0130                 if (variant == RLEVariant::PackBits16) {
0131                     if (is_msb) {
0132                         temp_data = datum << 8;
0133                         is_msb = false;
0134                     } else {
0135                         temp_data |= datum;
0136                         dest[i >> 1] = updateItem(dest[i >> 1], temp_data);
0137                         is_msb = true;
0138                     }
0139                 } else {
0140                     dest[i] = updateItem(dest[i], datum);
0141                 }
0142             }
0143             offset += length;
0144         }
0145     }
0146     if (stream.status() != QDataStream::Ok) {
0147         qDebug() << "DataStream status was" << stream.status();
0148     }
0149     return stream.status() == QDataStream::Ok;
0150 }
0151 
0152 /**
0153  * Encodes data in run-length encoding format.
0154  *
0155  * This is intended to be used with lambda functions.
0156  *
0157  * @param variant     The RLE variant to encode in.
0158  * @param stream      The stream to write the data to.
0159  * @param data        The data to be written.
0160  * @param length      The number of items to write.
0161  * @param itemsEqual  A function that takes two items and returns whether
0162  *                    @p writeItem would write them identically.
0163  * @param writeItem   A function that takes a QDataStream reference and an item
0164  *                    and writes the item to the data stream.
0165  */
0166 template<typename Item, typename Func1, typename Func2>
0167 static inline void encodeRLEData(RLEVariant variant, QDataStream &stream, const Item *data, unsigned length, Func1 itemsEqual, Func2 writeItem)
0168 {
0169     unsigned offset = 0;
0170     const unsigned maxEncodableChunk = (variant == RLEVariant::PIC) ? 65535u : 128;
0171     while (offset < length) {
0172         const Item *chunkStart = data + offset;
0173         unsigned maxChunk = qMin(length - offset, maxEncodableChunk);
0174 
0175         const Item *chunkEnd = chunkStart + 1;
0176         quint16 chunkLength = 1;
0177         while (chunkLength < maxChunk && itemsEqual(*chunkStart, *chunkEnd)) {
0178             ++chunkEnd;
0179             ++chunkLength;
0180         }
0181 
0182         if (chunkLength > 128) {
0183             // Sequence of > 128 identical pixels
0184             Q_ASSERT(variant == RLEVariant::PIC);
0185             stream << quint8(128);
0186             stream << quint16(chunkLength);
0187             writeItem(stream, *chunkStart);
0188         } else if (chunkLength > 1) {
0189             // Sequence of <= 128 identical pixels
0190             quint8 encodedLength;
0191             if (variant == RLEVariant::PIC) {
0192                 encodedLength = quint8(chunkLength + 127);
0193             } else if (variant == RLEVariant::PackBits) {
0194                 encodedLength = quint8(257 - chunkLength);
0195             } else {
0196                 Q_ASSERT(false);
0197                 encodedLength = 0;
0198             }
0199             stream << encodedLength;
0200             writeItem(stream, *chunkStart);
0201         } else {
0202             // find a string of up to 128 values, each different from the one
0203             // that follows it
0204             if (maxChunk > 128) {
0205                 maxChunk = 128;
0206             }
0207             chunkLength = 1;
0208             chunkEnd = chunkStart + 1;
0209             while (chunkLength < maxChunk && (chunkLength + 1u == maxChunk || !itemsEqual(*chunkEnd, *(chunkEnd + 1)))) {
0210                 ++chunkEnd;
0211                 ++chunkLength;
0212             }
0213             stream << quint8(chunkLength - 1);
0214             for (unsigned i = 0; i < chunkLength; ++i) {
0215                 writeItem(stream, *(chunkStart + i));
0216             }
0217         }
0218         offset += chunkLength;
0219     }
0220 }
0221 
0222 #endif // KIMAGEFORMATS_RLE_P_H