1 /*
2    Copyright (C) 2009 Red Hat, Inc.
3 
4    This library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public
6    License as published by the Free Software Foundation; either
7    version 2.1 of the License, or (at your option) any later version.
8 
9    This library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Lesser General Public License for more details.
13 
14    You should have received a copy of the GNU Lesser General Public
15    License along with this library; if not, see <http://www.gnu.org/licenses/>.
16 */
17 
18 // External defines: PLT, RGBX/PLTXX/ALPHA, TO_RGB32.
19 // If PLT4/1 and TO_RGB32 are defined, we need CAST_PLT_DISTANCE (
20 // because then the number of pixels differ from the units used in the compression)
21 
22 /*
23     For each output pixel type the following macros are defined:
24     OUT_PIXEL                      - the output pixel type
25     COPY_PIXEL(p, out)              - assigns the pixel to the place pointed by out and
26                                       increases out. Used in RLE.
27                                       Need special handling because in alpha we copy only
28                                       the pad byte.
29     COPY_REF_PIXEL(ref, out)      - copies the pixel pointed by ref to the pixel pointed by out.
30                                     Increases ref and out.
31     COPY_COMP_PIXEL(encoder, out) - copies pixel from the compressed buffer to the decompressed
32                                     buffer. Increases out.
33 */
34 
35 #if !defined(LZ_RGB_ALPHA)
36 #define COPY_PIXEL(p, out) (*(out++) = p)
37 #define COPY_REF_PIXEL(ref, out) (*(out++) = *(ref++))
38 #endif
39 
40 // decompressing plt to plt
41 #ifdef LZ_PLT
42 #ifndef TO_RGB32
43 #define OUT_PIXEL one_byte_pixel_t
44 #define FNAME(name) glz_plt_##name
45 #define COPY_COMP_PIXEL(in, out) {(out)->a = *(in++); out++;}
46 #else // TO_RGB32
47 #define OUT_PIXEL rgb32_pixel_t
48 #define COPY_PLT_ENTRY(ent, out) {\
49     (out)->b = ent; (out)->g = (ent >> 8); (out)->r = (ent >> 16); (out)->pad = 0;}
50 #ifdef PLT8
51 #define FNAME(name) glz_plt8_to_rgb32_##name
52 #define COPY_COMP_PIXEL(in, out, palette) {         \
53     uint32_t rgb = palette->ents[*(in++)];          \
54     COPY_PLT_ENTRY(rgb, out);                       \
55     out++;                                          \
56 }
57 #elif defined(PLT4_BE)
58 #define FNAME(name) glz_plt4_be_to_rgb32_##name
59 #define COPY_COMP_PIXEL(in, out, palette){                                    \
60     uint8_t byte = *(in++);                                                   \
61     uint32_t rgb = palette->ents[((byte >> 4) & 0x0f) % (palette->num_ents)]; \
62     COPY_PLT_ENTRY(rgb, out);                                                 \
63     out++;                                                                    \
64     rgb = palette->ents[(byte & 0x0f) % (palette->num_ents)];                 \
65     COPY_PLT_ENTRY(rgb, out);                                                 \
66     out++;                                                                    \
67 }
68 #define CAST_PLT_DISTANCE(dist) (dist*2)
69 #elif  defined(PLT4_LE)
70 #define FNAME(name) glz_plt4_le_to_rgb32_##name
71 #define COPY_COMP_PIXEL(in, out, palette){                                \
72     uint8_t byte = *(in++);                                               \
73     uint32_t rgb = palette->ents[(byte & 0x0f) % (palette->num_ents)];    \
74     COPY_PLT_ENTRY(rgb, out);                                             \
75     out++;                                                                \
76     rgb = palette->ents[((byte >> 4) & 0x0f) % (palette->num_ents)];      \
77     COPY_PLT_ENTRY(rgb, out);                                             \
78     out++;                                                                \
79 }
80 #define CAST_PLT_DISTANCE(dist) (dist*2)
81 #elif defined(PLT1_BE) // TODO store palette entries for direct access
82 #define FNAME(name) glz_plt1_be_to_rgb32_##name
83 #define COPY_COMP_PIXEL(in, out, palette){                                \
84     uint8_t byte = *(in++);                                               \
85     int i;                                                                \
86     uint32_t fore = palette->ents[1];                                     \
87     uint32_t back = palette->ents[0];                                     \
88     for (i = 7; i >= 0; i--)                                              \
89     {                                                                     \
90         if ((byte >> i) & 1) {                                            \
91             COPY_PLT_ENTRY(fore, out);                                    \
92         } else {                                                          \
93             COPY_PLT_ENTRY(back, out);                                    \
94         }                                                                 \
95         out++;                                                            \
96     }                                                                     \
97 }
98 #define CAST_PLT_DISTANCE(dist) (dist*8)
99 #elif defined(PLT1_LE)
100 #define FNAME(name) glz_plt1_le_to_rgb32_##name
101 #define COPY_COMP_PIXEL(in, out, palette){                                \
102     uint8_t byte = *(in++);                                               \
103     int i;                                                                \
104     uint32_t fore = palette->ents[1];                                     \
105     uint32_t back = palette->ents[0];                                     \
106     for (i = 0; i < 8; i++)                                               \
107     {                                                                     \
108         if ((byte >> i) & 1) {                                            \
109             COPY_PLT_ENTRY(fore, out);                                    \
110         } else {                                                          \
111             COPY_PLT_ENTRY(back, out);                                    \
112         }                                                                 \
113         out++;                                                            \
114     }                                                                     \
115 }
116 #define CAST_PLT_DISTANCE(dist) (dist*8)
117 #endif // PLT Type
118 #endif // TO_RGB32
119 #endif
120 
121 #ifdef LZ_RGB16
122 #ifndef TO_RGB32
123 #define OUT_PIXEL rgb16_pixel_t
124 #define FNAME(name) glz_rgb16_##name
125 #define COPY_COMP_PIXEL(in, out) {*out = (*(in++)) << 8; *out |= *(in++); out++;}
126 #else
127 #define OUT_PIXEL rgb32_pixel_t
128 #define FNAME(name) glz_rgb16_to_rgb32_##name
129 #define COPY_COMP_PIXEL(in, out) {out->r = *(in++); out->b= *(in++);       \
130     out->g = (((out->r) << 6) | ((out->b) >> 2)) & ~0x07;                  \
131     out->g |= (out->g >> 5);                                               \
132     out->r = ((out->r << 1) & ~0x07) | ((out->r >> 4) & 0x07) ;            \
133     out->b = (out->b << 3) | ((out->b >> 2) & 0x07);                       \
134     out->pad = 0;                                                          \
135     out++;                                                                 \
136 }
137 #endif
138 #endif
139 
140 #ifdef LZ_RGB24
141 #define OUT_PIXEL rgb24_pixel_t
142 #define FNAME(name) glz_rgb24_##name
143 #define COPY_COMP_PIXEL(in, out) {  \
144     out->b = *(in++);               \
145     out->g = *(in++);               \
146     out->r = *(in++);               \
147     out++;                          \
148 }
149 #endif
150 
151 #ifdef LZ_RGB32
152 #define OUT_PIXEL rgb32_pixel_t
153 #define FNAME(name) glz_rgb32_##name
154 #define COPY_COMP_PIXEL(in, out) {  \
155     out->b = *(in++);               \
156     out->g = *(in++);               \
157     out->r = *(in++);               \
158     out->pad = 0;                   \
159     out++;                          \
160 }
161 #endif
162 
163 #ifdef LZ_RGB_ALPHA
164 #define OUT_PIXEL rgb32_pixel_t
165 #define FNAME(name) glz_rgb_alpha_##name
166 #define COPY_PIXEL(p, out) {out->pad = p.pad; out++;}
167 #define COPY_REF_PIXEL(ref, out) {out->pad = ref->pad; out++; ref++;}
168 #define COPY_COMP_PIXEL(in, out) {out->pad = *(in++); out++;}
169 #endif
170 
171 // TODO: separate into routines that decode to dist,len. and to a routine that
172 // actually copies the data.
173 
174 /* returns num of bytes read from in buf.
175    size should be in PIXEL */
FNAME(decode)176 static size_t FNAME(decode)(SpiceGlzDecoderWindow *window,
177                             uint8_t* in_buf, uint8_t *out_buf, int size,
178                             uint64_t image_id, SpicePalette *plt)
179 {
180     uint8_t      *ip = in_buf;
181     OUT_PIXEL    *out_pix_buf = SPICE_ALIGNED_CAST(OUT_PIXEL *, out_buf);
182     OUT_PIXEL    *op = out_pix_buf;
183     OUT_PIXEL    *op_limit = out_pix_buf + size;
184 
185     uint32_t ctrl = *(ip++);
186     int loop = true;
187 
188     do {
189         if (ctrl >= MAX_COPY) { // reference (dictionary/RLE)
190             OUT_PIXEL *ref = op;
191             uint32_t len = ctrl >> 5;
192             uint8_t pixel_flag = (ctrl >> 4) & 0x01;
193             uint32_t pixel_ofs = (ctrl & 0x0f);
194             uint8_t image_flag;
195             uint32_t image_dist;
196 
197             /* retrieving the referenced images, the offset of the first pixel,
198                and the match length */
199 
200             uint8_t code;
201             //len--; // TODO: why do we do this?
202 
203             if (len == 7) { // match length is bigger than 7
204                 do {
205                     code = *(ip++);
206                     len += code;
207                 } while (code == 255); // remaining of len
208             }
209             code = *(ip++);
210             pixel_ofs += (code << 4);
211 
212             code = *(ip++);
213             image_flag = (code >> 6) & 0x03;
214             if (!pixel_flag) { // short pixel offset
215                 int i;
216                 image_dist = code & 0x3f;
217                 for (i = 0; i < image_flag; i++) {
218                     code = *(ip++);
219                     image_dist += (code << (6 + (8 * i)));
220                 }
221             } else {
222                 int i;
223                 pixel_flag = (code >> 5) & 0x01;
224                 pixel_ofs += (code & 0x1f) << 12;
225                 image_dist = 0;
226                 for (i = 0; i < image_flag; i++) {
227                     code = *(ip++);
228                     image_dist += (code << 8 * i);
229                 }
230 
231 
232                 if (pixel_flag) { // very long pixel offset
233                     code = *(ip++);
234                     pixel_ofs += code << 17;
235                 }
236             }
237 
238 #if defined(LZ_PLT) || defined(LZ_RGB_ALPHA)
239             len += 2; // length is biased by 2 (fixing bias)
240 #elif defined(LZ_RGB16)
241             len += 1; // length is biased by 1  (fixing bias)
242 #endif
243             if (!image_dist) {
244                 pixel_ofs += 1; // offset is biased by 1 (fixing bias)
245             }
246 
247 #if defined(TO_RGB32)
248 #if defined(PLT4_BE) || defined(PLT4_LE) || defined(PLT1_BE) || defined(PLT1_LE)
249             pixel_ofs = CAST_PLT_DISTANCE(pixel_ofs);
250             len = CAST_PLT_DISTANCE(len);
251 #endif
252 #endif
253 
254             if (!image_dist) { // reference is inside the same image
255                 ref -= pixel_ofs;
256                 g_return_val_if_fail(ref + len <= op_limit, 0);
257                 g_return_val_if_fail(ref >= out_pix_buf, 0);
258             } else {
259                 ref = glz_decoder_window_bits(window, image_id,
260                                               image_dist, pixel_ofs);
261             }
262 
263             g_return_val_if_fail(ref != NULL, 0);
264             g_return_val_if_fail(op + len <= op_limit, 0);
265 
266             /* copying the match*/
267 
268             if (ref == (op - 1)) { // run (this will never be called in PLT4/1_TO_RGB because the
269                                   // number of pixel copied is larger then one...
270                 /* optimize copy for a run */
271                 OUT_PIXEL b = *ref;
272                 for (; len; --len) {
273                     COPY_PIXEL(b, op);
274                     g_return_val_if_fail(op <= op_limit, 0);
275                 }
276             } else {
277                 for (; len; --len) {
278                     COPY_REF_PIXEL(ref, op);
279                     g_return_val_if_fail(op <= op_limit, 0);
280                 }
281             }
282         } else { // copy
283             ctrl++; // copy count is biased by 1
284 #if defined(TO_RGB32) && (defined(PLT4_BE) || defined(PLT4_LE) || defined(PLT1_BE) || \
285                                                                                    defined(PLT1_LE))
286             g_return_val_if_fail(op + CAST_PLT_DISTANCE(ctrl) <= op_limit, 0);
287 #else
288             g_return_val_if_fail(op + ctrl <= op_limit, 0);
289 #endif
290 
291 #if defined(TO_RGB32) && defined(LZ_PLT)
292             g_return_val_if_fail(plt, 0);
293             COPY_COMP_PIXEL(ip, op, plt);
294 #else
295             COPY_COMP_PIXEL(ip, op);
296 #endif
297             g_return_val_if_fail(op <= op_limit, 0);
298 
299             for (--ctrl; ctrl; ctrl--) {
300 #if defined(TO_RGB32) && defined(LZ_PLT)
301                 g_return_val_if_fail(plt, 0);
302                 COPY_COMP_PIXEL(ip, op, plt);
303 #else
304                 COPY_COMP_PIXEL(ip, op);
305 #endif
306                 g_return_val_if_fail(op <= op_limit, 0);
307             }
308         } // END REF/COPY
309 
310         if (LZ_EXPECT_CONDITIONAL(op < op_limit)) {
311             ctrl = *(ip++);
312         } else {
313             loop = false;
314         }
315     } while (LZ_EXPECT_CONDITIONAL(loop));
316 
317     return (ip - in_buf);
318 }
319 #undef LZ_PLT
320 #undef PLT8
321 #undef PLT4_BE
322 #undef PLT4_LE
323 #undef PLT1_BE
324 #undef PLT1_LE
325 #undef LZ_RGB16
326 #undef LZ_RGB24
327 #undef LZ_RGB32
328 #undef LZ_RGB_ALPHA
329 #undef TO_RGB32
330 #undef OUT_PIXEL
331 #undef FNAME
332 #undef COPY_PIXEL
333 #undef COPY_REF_PIXEL
334 #undef COPY_COMP_PIXEL
335 #undef COPY_PLT_ENTRY
336 #undef CAST_PLT_DISTANCE
337