Remove generated files
[framework/connectivity/libgphoto2.git] / camlibs / st2205 / st2205_decode.c
1 /* Sitronix st2205 picframe decompression and compression code
2  *
3  *   Copyright (c) 2010 Hans de Goede <hdegoede@redhat.com>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU Lesser General Public License as published by
7  * the Free Software Foundation; either version 2.1 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  */
19 #include "config.h"
20
21 #include <string.h>
22 #include <stdlib.h>
23 #ifdef HAVE_GD
24 # include <gd.h>
25 #endif
26
27 #include "st2205.h"
28
29 #ifdef HAVE_GD
30 #define CLAMP256(x) (((x) > 255) ? 255 : (((x) < 0) ? 0 : (x)))
31 #define CLAMP64S(x) (((x) > 63) ? 63 : (((x) < -64) ? -64 : (x)))
32
33 static const int16_t st2205_corr_table[16] = {
34         -26,-22,-18,-14,-11,-7,-4,-1,1,4,7,11,14,18,22,26
35 };
36
37 static int
38 st2205_decode_block(CameraPrivateLibrary *pl, unsigned char *src,
39         int src_length, int **dest, int dest_x, int dest_y)
40 {
41         st2205_lookup_row *luma_table, *chroma_table;
42         int y_base, uv_base[2], uv_corr[2];
43         int16_t Y[64], UV[2][16];
44         int x, y, r, g, b, uv;
45         if (src_length < 4) {
46                 gp_log (GP_LOG_ERROR, "st2205", "short image block");
47                 return GP_ERROR_CORRUPTED_DATA;
48         }
49
50         if (src[0] & 0x80) {
51                 gp_log (GP_LOG_ERROR, "st2205",
52                         "2 luma bits per pixel pictures are not supported");
53                 return GP_ERROR_CORRUPTED_DATA;
54         }
55         y_base = src[1] & 0x7f;
56         luma_table = pl->lookup[src[1] >> 7];
57         chroma_table = pl->lookup[2];
58
59         uv_base[0] = src[2] & 0x7f;
60         uv_corr[0] = src[2] & 0x80;
61         uv_base[1] = src[3] & 0x7f;
62         uv_corr[1] = src[3] & 0x80;
63
64         if (src_length != (4 + (uv_corr[0]? 10:2) + (uv_corr[1]? 10:2) + 40)) {
65                 GP_DEBUG ("src_length: %d, u_corr: %x v_corr: %x\n",
66                           src_length, uv_corr[0], uv_corr[1]);
67                 gp_log (GP_LOG_ERROR, "st2205", "invalid block length");
68                 return GP_ERROR_CORRUPTED_DATA;
69         }
70
71         src += 4;
72
73         for (uv = 0; uv < 2; uv++) {
74                 for (y = 0; y < 4; y++)
75                         for (x = 0; x < 4; x++)
76                                 UV[uv][y * 4 + x] = uv_base[uv] - 64 +
77                                     chroma_table[src[y / 2]][(y & 1) * 4 + x];
78                 src += 2;
79
80                 if (uv_corr[uv]) {
81                         for (x = 0; x < 16; x+= 2) {
82                                 uint8_t corr = src[x / 2];
83                                 UV[uv][x    ] += st2205_corr_table[corr >> 4];
84                                 UV[uv][x + 1] += st2205_corr_table[corr & 0x0f];
85                         }
86                         src += 8;
87                 }
88         }
89
90         for (y = 0; y < 8; y++) {
91                 memcpy(Y + y * 8, luma_table[src[y]], 8 * sizeof(uint16_t));
92                 for (x = 0; x < 8; x+= 2) {
93                         uint8_t corr = src[8 + y * 4 + x / 2];
94                         Y[y * 8 + x    ] +=
95                                 y_base + st2205_corr_table[corr >> 4];
96                         Y[y * 8 + x + 1] +=
97                                 y_base + st2205_corr_table[corr & 0x0f];
98                 }
99         }
100
101         for (y = 0; y < 8; y++) {
102                 for (x = 0; x < 8; x++) {
103                         r = Y[y * 8 + x] + UV[1][(y / 2) * 4 + x / 2];
104                         g = Y[y * 8 + x] - UV[0][(y / 2) * 4 + x / 2] -
105                             UV[1][(y / 2) * 4 + x / 2];
106                         b = Y[y * 8 + x] + UV[0][(y / 2) * 4 + x / 2];
107                         dest[dest_y + y][dest_x + x] =
108                                 gdTrueColor (CLAMP256(2 * r),
109                                              CLAMP256(2 * g),
110                                              CLAMP256(2 * b));
111                 }
112         }
113
114         return 0;
115 }
116
117 int
118 st2205_decode_image(CameraPrivateLibrary *pl, unsigned char *src, int **dest)
119 {
120         int ret, block = 0, block_length;
121         struct st2205_coord *shuffle_table;
122         struct st2205_image_header *header = (struct st2205_image_header*)src;
123         uint8_t shuffle_pattern = header->shuffle_table;
124         int src_length = be16toh (header->length);
125
126         /* Skip the header */
127         src += sizeof(*header);
128
129         if (shuffle_pattern >= pl->no_shuffles) {
130                 gp_log (GP_LOG_ERROR, "st2205", "invalid shuffle pattern");
131                 return GP_ERROR_CORRUPTED_DATA;
132         }
133         shuffle_table = pl->shuffle[shuffle_pattern];
134
135         while (src_length && block < (pl->width * pl->height / 64)) {
136                 block_length = (src[0] & 0x7f) + 1;
137                 if (block_length > src_length) {
138                         gp_log (GP_LOG_ERROR, "st2205", "block %d goes outside of image buffer", block);
139                         return GP_ERROR_CORRUPTED_DATA;
140                 }
141
142                 ret = st2205_decode_block(pl, src, block_length, dest,
143                                           shuffle_table[block].x,
144                                           shuffle_table[block].y);
145                 if (ret < 0)
146                         return ret;
147                 src += block_length;
148                 src_length -= block_length;
149                 block++;
150         }
151
152         if (src_length) {
153                 gp_log (GP_LOG_ERROR, "st2205", "data remaining after decoding %d blocks", block);
154                 return GP_ERROR_CORRUPTED_DATA;
155         }
156
157         if (block != (pl->width * pl->height / 64)) {
158                 gp_log (GP_LOG_ERROR, "st2205",
159                         "image only contained %d of %d blocks",
160                         block, pl->width * pl->height / 64);
161                 return GP_ERROR_CORRUPTED_DATA;
162         }
163
164         return 0;
165 }
166
167 static uint8_t st2205_find_closest_match(st2205_lookup_row *table,
168           int16_t *row, int *smallest_diff_ret)
169 {
170         int i, j;
171         uint8_t closest_match = 0;
172         unsigned int diff, smallest_diff = -1;
173
174         for (i = 0; i < 256; i++) {
175                 diff = 0;
176                 for (j = 0; j < 8; j++)
177                         diff += (row[j] - table[i][j]) * (row[j] - table[i][j]);
178                 if (diff < smallest_diff) {
179                         smallest_diff = diff;
180                         closest_match = i;
181                 }
182         }
183
184         if (smallest_diff_ret)
185                 *smallest_diff_ret = smallest_diff;
186
187         return closest_match;
188 }
189
190 static uint8_t st2205_closest_correction(int16_t corr)
191 {
192         int i, diff, smallest_diff;
193         uint8_t closest = 0;
194
195         smallest_diff = abs(st2205_corr_table[0] - corr);
196         for (i = 1; i < 16; i++) {
197                 diff = abs(st2205_corr_table[i] - corr);
198                 if (diff < smallest_diff) {
199                         smallest_diff = diff;
200                         closest = i;
201                 }
202         }
203
204         return closest;
205 }
206
207 static int
208 st2205_code_block(CameraPrivateLibrary *pl, int **src,
209         int src_x, int src_y, unsigned char *dest, int allow_uv_corr)
210 {
211         st2205_lookup_row *luma_table;
212         int y_base, uv_base[2];
213         int16_t Y[64], UV[2][16];
214         uint8_t corr1, corr2, *pattern;
215         int x, y, r, g, b, uv, diff1, diff2, used = 0;
216
217         /* Step 1 convert to "YUV" */
218         for (y = 0; y < 8; y++) {
219                 for (x = 0; x < 8; x++) {
220                         int p = src[src_y + y][src_x + x];
221                         Y[y * 8 + x] = (gdTrueColorGetRed(p) +
222                                         gdTrueColorGetGreen(p) +
223                                         gdTrueColorGetBlue(p)) / 6;
224                 }
225         }
226
227         for (y = 0; y < 4; y++) {
228                 for (x = 0; x < 4; x++) {
229                         int p1 = src[src_y + y    ][src_x + x    ];
230                         int p2 = src[src_y + y    ][src_x + x + 1];
231                         int p3 = src[src_y + y + 1][src_x + x    ];
232                         int p4 = src[src_y + y + 1][src_x + x + 1];
233
234                         r = gdTrueColorGetRed(p1) + gdTrueColorGetRed(p2) +
235                             gdTrueColorGetRed(p3) + gdTrueColorGetRed(p4);
236                         g = gdTrueColorGetGreen(p1) + gdTrueColorGetGreen(p2) +
237                             gdTrueColorGetGreen(p3) + gdTrueColorGetGreen(p4);
238                         b = gdTrueColorGetBlue(p1) + gdTrueColorGetBlue(p2) +
239                             gdTrueColorGetBlue(p3) + gdTrueColorGetBlue(p4);
240                         uv = (b * 3 - (r + g + b)) / 24;
241                         UV[0][y * 4 + x] = CLAMP64S(uv);
242                         uv = (r * 3 - (r + g + b)) / 24;
243                         UV[1][y * 4 + x] = CLAMP64S(uv);
244                 }
245         }
246
247         /* Step 2a calculate base values */
248         y_base = 0;
249         for (x = 0; x < 64; x++)
250                 y_base += Y[x];
251         y_base = y_base / 64;
252
253         for (uv = 0; uv < 2; uv++) {
254                 uv_base[uv] = 0;
255                 for (x = 0; x < 16; x++)
256                         uv_base[uv] += UV[uv][x];
257                 uv_base[uv] = uv_base[uv] / 16;
258         }
259
260         dest[1] = y_base;
261         dest[2] = uv_base[0] + 64;
262         dest[3] = uv_base[1] + 64;
263         used = 4;
264
265         /* Step 2b adjust values for base values and normalize */
266         for (x = 0; x < 64; x++)
267                 Y[x] -= y_base;
268
269         for (uv = 0; uv < 2; uv++)
270                 for (x = 0; x < 16; x++)
271                         UV[uv][x] -= uv_base[uv];
272
273         /* Step 3 encode chroma values */
274         for (uv = 0; uv < 2; uv++) {
275                 pattern = dest + used;
276                 dest[used++] = st2205_find_closest_match (pl->lookup[2],
277                                                           &UV[uv][0], &diff1);
278                 dest[used++] = st2205_find_closest_match (pl->lookup[2],
279                                                           &UV[uv][8], &diff2);
280                 if ((diff1 > 64 || diff2 > 64) && allow_uv_corr) {
281                         dest[2 + uv] |= 0x80;
282                         for (x = 0; x < 16; x+= 2) {
283                                 corr1 = st2205_closest_correction(UV[uv][x] -
284                                         pl->lookup[2][pattern[x / 8]][x % 8]);
285                                 corr2 = st2205_closest_correction(UV[uv][x + 1]
286                                    - pl->lookup[2][pattern[x / 8]][x % 8 + 1]);
287                                 dest[used++] = (corr1 << 4) | corr2;
288                         }
289                 }
290         }
291
292         /* Step 4a encode luma values, choose luma table */
293         diff1 = 0;
294         diff2 = 0;
295         for (y = 0; y < 8; y++) {
296                 st2205_find_closest_match(pl->lookup[0], &Y[y * 8], &x);
297                 diff1 += x;
298                 st2205_find_closest_match(pl->lookup[1], &Y[y * 8], &x);
299                 diff2 += x;
300         }
301
302         if (diff1 <= diff2) {
303                 luma_table = pl->lookup[0];
304                 dest[1] |= 0x00;
305         } else {
306                 luma_table = pl->lookup[1];
307                 dest[1] |= 0x80;
308         }
309
310         /* Step 4b encode luma values, choose luma patterns */
311         pattern = dest + used;
312         for (y = 0; y < 8; y++)
313                 dest[used++] = st2205_find_closest_match (luma_table,
314                                                           &Y[y * 8], NULL);
315
316         /* Step 4c encode luma values, add luma correction values */
317         for (y = 0; y < 8; y++) {
318                 for (x = 0; x < 8; x += 2) {
319                         corr1 = st2205_closest_correction (Y[y * 8 + x] -
320                                                 luma_table[pattern[y]][x]);
321                         corr2 = st2205_closest_correction(Y[y * 8 + x + 1] -
322                                                 luma_table[pattern[y]][x + 1]);
323                         dest[used++] = (corr1 << 4) | corr2;
324                 }
325         }
326
327         dest[0] = used - 1;
328
329         return used;
330 }
331
332 int
333 st2205_code_image(CameraPrivateLibrary *pl, int **src,
334         unsigned char *dest, uint8_t shuffle_pattern, int allow_uv_corr)
335 {
336         int block = 0, used = 0, ret;
337         struct st2205_coord *shuffle_table;
338         struct st2205_image_header *header = (struct st2205_image_header*)dest;
339
340         /* Make room for the header */
341         dest += sizeof(*header);
342
343         if (shuffle_pattern >= pl->no_shuffles) {
344                 gp_log (GP_LOG_ERROR, "st2205", "invalid shuffle pattern");
345                 return GP_ERROR_BAD_PARAMETERS;
346         }
347
348         shuffle_table = pl->shuffle[shuffle_pattern];
349
350         while (block < (pl->width * pl->height / 64)) {
351                 ret = st2205_code_block (pl, src, shuffle_table[block].x,
352                                          shuffle_table[block].y,
353                                          dest + used, allow_uv_corr);
354                 if (ret < 0)
355                         return ret;
356                 used += ret;
357                 block++;
358         }
359
360         /* Write the header now that we know the size */
361         memset(header, 0, sizeof(*header));
362         header->marker = ST2205_HEADER_MARKER;
363         header->width  = htobe16(pl->width);
364         header->height = htobe16(pl->height);
365         header->blocks = htobe16((pl->width * pl->height) / 64);
366         header->shuffle_table = shuffle_pattern;
367         header->unknown2 = 0x04;
368         header->unknown3 = pl->unknown3[shuffle_pattern];
369         header->length = htobe16(used);
370
371         return used + sizeof(*header);
372 }
373
374 int
375 st2205_rgb565_to_rgb24(CameraPrivateLibrary *pl, unsigned char *src,
376         int **dest)
377 {
378         int x,y;
379
380         for (y = 0; y < pl->height; y++) {
381                 for (x = 0; x < pl->width; x++) {
382                         unsigned short w = src[0] << 8 | src[1];
383
384                         dest[y][x] = gdTrueColor ((w >> 8) & 0xf8,
385                                                   (w >> 3) & 0xfb,
386                                                   (w << 3) & 0xf8);
387                         src += 2;
388                 }
389         }
390
391         return GP_OK;
392 }
393
394 int
395 st2205_rgb24_to_rgb565(CameraPrivateLibrary *pl, int **src,
396         unsigned char *dest)
397 {
398         int x,y;
399
400         for (y = 0; y < pl->height; y++) {
401                 for (x = 0; x < pl->width; x++) {
402                         unsigned short w;
403
404                         int r = gdTrueColorGetRed(src[y][x]);
405                         int g = gdTrueColorGetGreen(src[y][x]);
406                         int b = gdTrueColorGetBlue(src[y][x]);
407
408                         w = ((r <<  8) & 0xf100) |
409                             ((g <<  3) & 0x07e0) |
410                             ((b >>  3) & 0x001f);
411
412                         *dest++ = w >> 8;
413                         *dest++ = w & 0xff;
414                 }
415         }
416
417         return pl->height * pl->width * 2;
418 }
419
420 #else
421 int
422 st2205_decode_image(CameraPrivateLibrary *pl, unsigned char *src, int **dest)
423 {
424         return GP_ERROR_NOT_SUPPORTED;
425 }
426
427 int
428 st2205_code_image(CameraPrivateLibrary *pl, int **src,
429         unsigned char *dest, uint8_t shuffle_pattern, int allow_uv_corr)
430 {
431         return GP_ERROR_NOT_SUPPORTED;
432 }
433
434 int
435 st2205_rgb565_to_rgb24(CameraPrivateLibrary *pl, unsigned char *src,
436         int **dest)
437 {
438         return GP_ERROR_NOT_SUPPORTED;
439 }
440
441 int
442 st2205_rgb24_to_rgb565(CameraPrivateLibrary *pl, int **src,
443         unsigned char *dest)
444 {
445         return GP_ERROR_NOT_SUPPORTED;
446 }
447
448 #endif