/*
Copyright (C) 2009 Red Hat, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, see .
*/
#ifdef HAVE_CONFIG_H
#include
#endif
// External defines: PLT, RGBX/PLTXX/ALPHA, TO_RGB32.
// If PLT4/1 and TO_RGB32 are defined, we need CAST_PLT_DISTANCE (
// because then the number of pixels differ from the units used in the compression)
/*
For each output pixel type the following macros are defined:
OUT_PIXEL - the output pixel type
COPY_PIXEL(p, out) - assigns the pixel to the place pointed by out and
increases out. Used in RLE.
Need special handling because in alpha we copy only
the pad byte.
COPY_REF_PIXEL(ref, out) - copies the pixel pointed by ref to the pixel pointed by out.
Increases ref and out.
COPY_COMP_PIXEL(encoder, out) - copies pixel from the compressed buffer to the decompressed
buffer. Increases out.
*/
#if !defined(LZ_RGB_ALPHA)
#define COPY_PIXEL(p, out) (*(out++) = p)
#define COPY_REF_PIXEL(ref, out) (*(out++) = *(ref++))
#endif
// decompressing plt to plt
#ifdef LZ_PLT
#ifndef TO_RGB32
#define OUT_PIXEL one_byte_pixel_t
#define FNAME(name) glz_plt_##name
#define COPY_COMP_PIXEL(in, out) {(out)->a = *(in++); out++;}
#else // TO_RGB32
#define OUT_PIXEL rgb32_pixel_t
#define COPY_PLT_ENTRY(ent, out) {\
(out)->b = ent; (out)->g = (ent >> 8); (out)->r = (ent >> 16); (out)->pad = 0;}
#ifdef PLT8
#define FNAME(name) glz_plt8_to_rgb32_##name
#define COPY_COMP_PIXEL(in, out, palette) { \
uint32_t rgb = palette->ents[*(in++)]; \
COPY_PLT_ENTRY(rgb, out); \
out++; \
}
#elif defined(PLT4_BE)
#define FNAME(name) glz_plt4_be_to_rgb32_##name
#define COPY_COMP_PIXEL(in, out, palette){ \
uint8_t byte = *(in++); \
uint32_t rgb = palette->ents[((byte >> 4) & 0x0f) % (palette->num_ents)]; \
COPY_PLT_ENTRY(rgb, out); \
out++; \
rgb = palette->ents[(byte & 0x0f) % (palette->num_ents)]; \
COPY_PLT_ENTRY(rgb, out); \
out++; \
}
#define CAST_PLT_DISTANCE(dist) (dist*2)
#elif defined(PLT4_LE)
#define FNAME(name) glz_plt4_le_to_rgb32_##name
#define COPY_COMP_PIXEL(in, out, palette){ \
uint8_t byte = *(in++); \
uint32_t rgb = palette->ents[(byte & 0x0f) % (palette->num_ents)]; \
COPY_PLT_ENTRY(rgb, out); \
out++; \
rgb = palette->ents[((byte >> 4) & 0x0f) % (palette->num_ents)]; \
COPY_PLT_ENTRY(rgb, out); \
out++; \
}
#define CAST_PLT_DISTANCE(dist) (dist*2)
#elif defined(PLT1_BE) // TODO store palette entries for direct access
#define FNAME(name) glz_plt1_be_to_rgb32_##name
#define COPY_COMP_PIXEL(in, out, palette){ \
uint8_t byte = *(in++); \
int i; \
uint32_t fore = palette->ents[1]; \
uint32_t back = palette->ents[0]; \
for (i = 7; i >= 0; i--) \
{ \
if ((byte >> i) & 1) { \
COPY_PLT_ENTRY(fore, out); \
} else { \
COPY_PLT_ENTRY(back, out); \
} \
out++; \
} \
}
#define CAST_PLT_DISTANCE(dist) (dist*8)
#elif defined(PLT1_LE)
#define FNAME(name) glz_plt1_le_to_rgb32_##name
#define COPY_COMP_PIXEL(in, out, palette){ \
uint8_t byte = *(in++); \
int i; \
uint32_t fore = palette->ents[1]; \
uint32_t back = palette->ents[0]; \
for (i = 0; i < 8; i++) \
{ \
if ((byte >> i) & 1) { \
COPY_PLT_ENTRY(fore, out); \
} else { \
COPY_PLT_ENTRY(back, out); \
} \
out++; \
} \
}
#define CAST_PLT_DISTANCE(dist) (dist*8)
#endif // PLT Type
#endif // TO_RGB32
#endif
#ifdef LZ_RGB16
#ifndef TO_RGB32
#define OUT_PIXEL rgb16_pixel_t
#define FNAME(name) glz_rgb16_##name
#define COPY_COMP_PIXEL(in, out) {*out = (*(in++)) << 8; *out |= *(in++); out++;}
#else
#define OUT_PIXEL rgb32_pixel_t
#define FNAME(name) glz_rgb16_to_rgb32_##name
#define COPY_COMP_PIXEL(in, out) {out->r = *(in++); out->b= *(in++); \
out->g = (((out->r) << 6) | ((out->b) >> 2)) & ~0x07; \
out->g |= (out->g >> 5); \
out->r = ((out->r << 1) & ~0x07) | ((out->r >> 4) & 0x07) ; \
out->b = (out->b << 3) | ((out->b >> 2) & 0x07); \
out->pad = 0; \
out++; \
}
#endif
#endif
#ifdef LZ_RGB24
#define OUT_PIXEL rgb24_pixel_t
#define FNAME(name) glz_rgb24_##name
#define COPY_COMP_PIXEL(in, out) { \
out->b = *(in++); \
out->g = *(in++); \
out->r = *(in++); \
out++; \
}
#endif
#ifdef LZ_RGB32
#define OUT_PIXEL rgb32_pixel_t
#define FNAME(name) glz_rgb32_##name
#define COPY_COMP_PIXEL(in, out) { \
out->b = *(in++); \
out->g = *(in++); \
out->r = *(in++); \
out->pad = 0; \
out++; \
}
#endif
#ifdef LZ_RGB_ALPHA
#define OUT_PIXEL rgb32_pixel_t
#define FNAME(name) glz_rgb_alpha_##name
#define COPY_PIXEL(p, out) {out->pad = p.pad; out++;}
#define COPY_REF_PIXEL(ref, out) {out->pad = ref->pad; out++; ref++;}
#define COPY_COMP_PIXEL(in, out) {out->pad = *(in++); out++;}
#endif
// TODO: separate into routines that decode to dist,len. and to a routine that
// actually copies the data.
/* returns num of bytes read from in buf.
size should be in PIXEL */
static size_t FNAME(decode)(GlzDecoderWindow &window, uint8_t* in_buf,
uint8_t *out_buf, int size,
DecodedImageWinId image_win_id, SpicePalette *plt,
GlzDecoderDebug &debug_calls)
{
uint8_t *ip = in_buf;
OUT_PIXEL *out_pix_buf = (OUT_PIXEL *)out_buf;
OUT_PIXEL *op = out_pix_buf;
OUT_PIXEL *op_limit = out_pix_buf + size;
uint32_t ctrl = *(ip++);
int loop = true;
do {
if (ctrl >= MAX_COPY) { // reference (dictionary/RLE)
OUT_PIXEL *ref = op;
uint32_t len = ctrl >> 5;
uint8_t pixel_flag = (ctrl >> 4) & 0x01;
uint32_t pixel_ofs = (ctrl & 0x0f);
uint8_t image_flag;
uint32_t image_dist;
/* retrieving the referenced images, the offset of the first pixel,
and the match length */
uint8_t code;
//len--; // TODO: why do we do this?
if (len == 7) { // match length is bigger than 7
do {
code = *(ip++);
len += code;
} while (code == 255); // remaining of len
}
code = *(ip++);
pixel_ofs += (code << 4);
code = *(ip++);
image_flag = (code >> 6) & 0x03;
if (!pixel_flag) { // short pixel offset
image_dist = code & 0x3f;
for (int i = 0; i < image_flag; i++) {
code = *(ip++);
image_dist += (code << (6 + (8 * i)));
}
} else {
pixel_flag = (code >> 5) & 0x01;
pixel_ofs += (code & 0x1f) << 12;
image_dist = 0;
for (int i = 0; i < image_flag; i++) {
code = *(ip++);
image_dist += (code << 8 * i);
}
if (pixel_flag) { // very long pixel offset
code = *(ip++);
pixel_ofs += code << 17;
}
}
#if defined(LZ_PLT) || defined(LZ_RGB_ALPHA)
len += 2; // length is biased by 2 (fixing bias)
#elif defined(LZ_RGB16)
len += 1; // length is biased by 1 (fixing bias)
#endif
if (!image_dist) {
pixel_ofs += 1; // offset is biased by 1 (fixing bias)
}
#if defined(TO_RGB32)
#if defined(PLT4_BE) || defined(PLT4_LE) || defined(PLT1_BE) || defined(PLT1_LE)
pixel_ofs = CAST_PLT_DISTANCE(pixel_ofs);
len = CAST_PLT_DISTANCE(len);
#endif
#endif
if (!image_dist) { // reference is inside the same image
ref -= pixel_ofs;
GLZ_ASSERT(debug_calls, ref + len <= op_limit);
GLZ_ASSERT(debug_calls, ref >= out_pix_buf);
} else {
ref = (OUT_PIXEL *)window.get_ref_pixel(image_win_id, image_dist,
pixel_ofs);
}
GLZ_ASSERT(debug_calls, op + len <= op_limit);
/* copying the match*/
if (ref == (op - 1)) { // run (this will never be called in PLT4/1_TO_RGB because the
// number of pixel copied is larger then one...
/* optimize copy for a run */
OUT_PIXEL b = *ref;
for (; len; --len) {
COPY_PIXEL(b, op);
GLZ_ASSERT(debug_calls, op <= op_limit);
}
} else {
for (; len; --len) {
COPY_REF_PIXEL(ref, op);
GLZ_ASSERT(debug_calls, op <= op_limit);
}
}
} else { // copy
ctrl++; // copy count is biased by 1
#if defined(TO_RGB32) && (defined(PLT4_BE) || defined(PLT4_LE) || defined(PLT1_BE) || \
defined(PLT1_LE))
GLZ_ASSERT(debug_calls, op + CAST_PLT_DISTANCE(ctrl) <= op_limit);
#else
GLZ_ASSERT(debug_calls, op + ctrl <= op_limit);
#endif
#if defined(TO_RGB32) && defined(LZ_PLT)
GLZ_ASSERT(debug_calls, plt);
COPY_COMP_PIXEL(ip, op, plt);
#else
COPY_COMP_PIXEL(ip, op);
#endif
GLZ_ASSERT(debug_calls, op <= op_limit);
for (--ctrl; ctrl; ctrl--) {
#if defined(TO_RGB32) && defined(LZ_PLT)
GLZ_ASSERT(debug_calls, plt);
COPY_COMP_PIXEL(ip, op, plt);
#else
COPY_COMP_PIXEL(ip, op);
#endif
GLZ_ASSERT(debug_calls, op <= op_limit);
}
} // END REF/COPY
if (LZ_EXPECT_CONDITIONAL(op < op_limit)) {
ctrl = *(ip++);
} else {
loop = false;
}
} while (LZ_EXPECT_CONDITIONAL(loop));
return (ip - in_buf);
}
#undef LZ_PLT
#undef PLT8
#undef PLT4_BE
#undef PLT4_LE
#undef PLT1_BE
#undef PLT1_LE
#undef LZ_RGB16
#undef LZ_RGB24
#undef LZ_RGB32
#undef LZ_RGB_ALPHA
#undef TO_RGB32
#undef OUT_PIXEL
#undef FNAME
#undef COPY_PIXEL
#undef COPY_REF_PIXEL
#undef COPY_COMP_PIXEL
#undef COPY_PLT_ENTRY
#undef CAST_PLT_DISTANCE