mirror of
https://invent.kde.org/frameworks/kimageformats.git
synced 2025-06-03 17:08:08 -04:00
ras: rle decode
Added support for RLE compressed images. This patch makes the player almost complete allowing to read all type 1,2 and 3 RAS files. Only types 4 and 5 are missing which are images converted from TIFF and IFF.
This commit is contained in:
parent
4badb3088e
commit
9173f02ea3
@ -21,7 +21,7 @@ The following image formats have read-only support:
|
|||||||
- OpenRaster (ora)
|
- OpenRaster (ora)
|
||||||
- Photoshop documents (psd, psb, pdd, psdt)
|
- Photoshop documents (psd, psb, pdd, psdt)
|
||||||
- Radiance HDR (hdr)
|
- Radiance HDR (hdr)
|
||||||
- Sun Raster (ras)
|
- Sun Raster (im1, im8, im24, im32, ras, sun)
|
||||||
|
|
||||||
The following image formats have read and write support:
|
The following image formats have read and write support:
|
||||||
|
|
||||||
|
BIN
autotests/read/ras/rle_1bit.png
Normal file
BIN
autotests/read/ras/rle_1bit.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 34 KiB |
BIN
autotests/read/ras/rle_1bit.ras
Normal file
BIN
autotests/read/ras/rle_1bit.ras
Normal file
Binary file not shown.
BIN
autotests/read/ras/rle_24bit.png
Normal file
BIN
autotests/read/ras/rle_24bit.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 21 KiB |
BIN
autotests/read/ras/rle_24bit.ras
Normal file
BIN
autotests/read/ras/rle_24bit.ras
Normal file
Binary file not shown.
BIN
autotests/read/ras/rle_pal8bit.png
Normal file
BIN
autotests/read/ras/rle_pal8bit.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
BIN
autotests/read/ras/rle_pal8bit.ras
Normal file
BIN
autotests/read/ras/rle_pal8bit.ras
Normal file
Binary file not shown.
@ -40,14 +40,14 @@ enum RASColorMapType {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct RasHeader {
|
struct RasHeader {
|
||||||
quint32 MagicNumber;
|
quint32 MagicNumber = 0;
|
||||||
quint32 Width;
|
quint32 Width = 0;
|
||||||
quint32 Height;
|
quint32 Height = 0;
|
||||||
quint32 Depth;
|
quint32 Depth = 0;
|
||||||
quint32 Length;
|
quint32 Length = 0;
|
||||||
quint32 Type;
|
quint32 Type = 0;
|
||||||
quint32 ColorMapType;
|
quint32 ColorMapType = 0;
|
||||||
quint32 ColorMapLength;
|
quint32 ColorMapLength = 0;
|
||||||
enum {
|
enum {
|
||||||
SIZE = 32,
|
SIZE = 32,
|
||||||
}; // 8 fields of four bytes each
|
}; // 8 fields of four bytes each
|
||||||
@ -88,9 +88,9 @@ static bool IsSupported(const RasHeader &head)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// the Type field adds support for RLE(BGR), RGB and other encodings
|
// the Type field adds support for RLE(BGR), RGB and other encodings
|
||||||
// we support Type 1: Normal(BGR) and Type 3: Normal(RGB) ONLY!
|
// we support Type 1: Normal(BGR), Type 2: RLE(BGR) and Type 3: Normal(RGB) ONLY!
|
||||||
// TODO: add support for Type 2: RLE(BGR) & Type 4,5: TIFF/IFF
|
// TODO: add support for Type 4,5: TIFF/IFF
|
||||||
if (!(head.Type == RAS_TYPE_STANDARD || head.Type == RAS_TYPE_RGB_FORMAT)) {
|
if (!(head.Type == RAS_TYPE_STANDARD || head.Type == RAS_TYPE_RGB_FORMAT || head.Type == RAS_TYPE_BYTE_ENCODED)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -110,6 +110,96 @@ static QImage::Format imageFormat(const RasHeader &header)
|
|||||||
return QImage::Format_RGB32;
|
return QImage::Format_RGB32;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class LineDecoder
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LineDecoder(QIODevice *d, const RasHeader &ras)
|
||||||
|
: device(d)
|
||||||
|
, header(ras)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray readLine(qint64 size)
|
||||||
|
{
|
||||||
|
/* *** uncompressed
|
||||||
|
*/
|
||||||
|
if (header.Type != RAS_TYPE_BYTE_ENCODED) {
|
||||||
|
return device->read(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* *** rle compressed
|
||||||
|
* The Run-length encoding (RLE) scheme optionally used in Sun Raster
|
||||||
|
* files (Type = 0002h) is used to encode bytes of image data
|
||||||
|
* separately. RLE encoding may be found in any Sun Raster file
|
||||||
|
* regardless of the type of image data it contains.
|
||||||
|
*
|
||||||
|
* The RLE packets are typically three bytes in size:
|
||||||
|
* - The first byte is a Flag Value indicating the type of RLE packet.
|
||||||
|
* - The second byte is the Run Count.
|
||||||
|
* - The third byte is the Run Value.
|
||||||
|
*
|
||||||
|
* A Flag Value of 80h is followed by a Run Count in the range of 01h
|
||||||
|
* to FFh. The Run Value follows the Run count and is in the range of
|
||||||
|
* 00h to FFh. The pixel run is the Run Value repeated Run Count times.
|
||||||
|
* There are two exceptions to this algorithm. First, if the Run Count
|
||||||
|
* following the Flag Value is 00h, this is an indication that the run
|
||||||
|
* is a single byte in length and has a value of 80h. And second, if
|
||||||
|
* the Flag Value is not 80h, then it is assumed that the data is
|
||||||
|
* unencoded pixel data and is written directly to the output stream.
|
||||||
|
*
|
||||||
|
* source: http://www.fileformat.info/format/sunraster/egff.htm
|
||||||
|
*/
|
||||||
|
for (qsizetype psz = 0, ptr = 0; uncBuffer.size() < size;) {
|
||||||
|
rleBuffer.append(device->read(std::min(qint64(32768), size)));
|
||||||
|
qsizetype sz = rleBuffer.size();
|
||||||
|
if (psz == sz) {
|
||||||
|
break; // avoid infinite loop (data corrupted?!)
|
||||||
|
}
|
||||||
|
auto data = reinterpret_cast<uchar *>(rleBuffer.data());
|
||||||
|
for (; ptr < sz;) {
|
||||||
|
auto flag = data[ptr++];
|
||||||
|
if (flag == 0x80) {
|
||||||
|
if (ptr >= sz) {
|
||||||
|
ptr -= 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
auto cnt = data[ptr++];
|
||||||
|
if (cnt == 0) {
|
||||||
|
uncBuffer.append(char(0x80));
|
||||||
|
continue;
|
||||||
|
} else if (ptr >= sz) {
|
||||||
|
ptr -= 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
auto val = data[ptr++];
|
||||||
|
uncBuffer.append(QByteArray(1 + cnt, char(val)));
|
||||||
|
} else {
|
||||||
|
uncBuffer.append(char(flag));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ptr) { // remove consumed data
|
||||||
|
rleBuffer.remove(0, ptr);
|
||||||
|
ptr = 0;
|
||||||
|
}
|
||||||
|
psz = rleBuffer.size();
|
||||||
|
}
|
||||||
|
if (uncBuffer.size() < size) {
|
||||||
|
return QByteArray(); // something wrong
|
||||||
|
}
|
||||||
|
auto line = uncBuffer.mid(0, size);
|
||||||
|
uncBuffer.remove(0, line.size()); // remove consumed data
|
||||||
|
return line;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
QIODevice *device;
|
||||||
|
RasHeader header;
|
||||||
|
|
||||||
|
// RLE decoding buffers
|
||||||
|
QByteArray rleBuffer;
|
||||||
|
QByteArray uncBuffer;
|
||||||
|
};
|
||||||
|
|
||||||
static bool LoadRAS(QDataStream &s, const RasHeader &ras, QImage &img)
|
static bool LoadRAS(QDataStream &s, const RasHeader &ras, QImage &img)
|
||||||
{
|
{
|
||||||
s.device()->seek(RasHeader::SIZE);
|
s.device()->seek(RasHeader::SIZE);
|
||||||
@ -148,11 +238,11 @@ static bool LoadRAS(QDataStream &s, const RasHeader &ras, QImage &img)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray rasLine(rasLineSize, 0);
|
LineDecoder dec(s.device(), ras);
|
||||||
auto bytesPerLine = std::min(img.bytesPerLine(), rasLine.size());
|
auto bytesPerLine = std::min(img.bytesPerLine(), qsizetype(rasLineSize));
|
||||||
for (quint32 y = 0; y < ras.Height; ++y) {
|
for (quint32 y = 0; y < ras.Height; ++y) {
|
||||||
auto read = s.readRawData(rasLine.data(), rasLine.size());
|
auto rasLine = dec.readLine(rasLineSize);
|
||||||
if (read != rasLine.size()) {
|
if (rasLine.size() != rasLineSize) {
|
||||||
qWarning() << "LoadRAS() unable to read line" << y << ": the seems corrupted!";
|
qWarning() << "LoadRAS() unable to read line" << y << ": the seems corrupted!";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user