1 /* Sitronix st2205 picframe decompression and compression code
3 * Copyright (c) 2010 Hans de Goede <hdegoede@redhat.com>
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.
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.
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
30 #define CLAMP256(x) (((x) > 255) ? 255 : (((x) < 0) ? 0 : (x)))
31 #define CLAMP64S(x) (((x) > 63) ? 63 : (((x) < -64) ? -64 : (x)))
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
38 st2205_decode_block(CameraPrivateLibrary *pl, unsigned char *src,
39 int src_length, int **dest, int dest_x, int dest_y)
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;
46 gp_log (GP_LOG_ERROR, "st2205", "short image block");
47 return GP_ERROR_CORRUPTED_DATA;
51 gp_log (GP_LOG_ERROR, "st2205",
52 "2 luma bits per pixel pictures are not supported");
53 return GP_ERROR_CORRUPTED_DATA;
55 y_base = src[1] & 0x7f;
56 luma_table = pl->lookup[src[1] >> 7];
57 chroma_table = pl->lookup[2];
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;
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;
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];
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];
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];
95 y_base + st2205_corr_table[corr >> 4];
97 y_base + st2205_corr_table[corr & 0x0f];
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),
118 st2205_decode_image(CameraPrivateLibrary *pl, unsigned char *src, int **dest)
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);
126 /* Skip the header */
127 src += sizeof(*header);
129 if (shuffle_pattern >= pl->no_shuffles) {
130 gp_log (GP_LOG_ERROR, "st2205", "invalid shuffle pattern");
131 return GP_ERROR_CORRUPTED_DATA;
133 shuffle_table = pl->shuffle[shuffle_pattern];
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;
142 ret = st2205_decode_block(pl, src, block_length, dest,
143 shuffle_table[block].x,
144 shuffle_table[block].y);
148 src_length -= block_length;
153 gp_log (GP_LOG_ERROR, "st2205", "data remaining after decoding %d blocks", block);
154 return GP_ERROR_CORRUPTED_DATA;
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;
167 static uint8_t st2205_find_closest_match(st2205_lookup_row *table,
168 int16_t *row, int *smallest_diff_ret)
171 uint8_t closest_match = 0;
172 unsigned int diff, smallest_diff = -1;
174 for (i = 0; i < 256; i++) {
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;
184 if (smallest_diff_ret)
185 *smallest_diff_ret = smallest_diff;
187 return closest_match;
190 static uint8_t st2205_closest_correction(int16_t corr)
192 int i, diff, smallest_diff;
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;
208 st2205_code_block(CameraPrivateLibrary *pl, int **src,
209 int src_x, int src_y, unsigned char *dest, int allow_uv_corr)
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;
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;
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];
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);
247 /* Step 2a calculate base values */
249 for (x = 0; x < 64; x++)
251 y_base = y_base / 64;
253 for (uv = 0; uv < 2; uv++) {
255 for (x = 0; x < 16; x++)
256 uv_base[uv] += UV[uv][x];
257 uv_base[uv] = uv_base[uv] / 16;
261 dest[2] = uv_base[0] + 64;
262 dest[3] = uv_base[1] + 64;
265 /* Step 2b adjust values for base values and normalize */
266 for (x = 0; x < 64; x++)
269 for (uv = 0; uv < 2; uv++)
270 for (x = 0; x < 16; x++)
271 UV[uv][x] -= uv_base[uv];
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],
278 dest[used++] = st2205_find_closest_match (pl->lookup[2],
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;
292 /* Step 4a encode luma values, choose luma table */
295 for (y = 0; y < 8; y++) {
296 st2205_find_closest_match(pl->lookup[0], &Y[y * 8], &x);
298 st2205_find_closest_match(pl->lookup[1], &Y[y * 8], &x);
302 if (diff1 <= diff2) {
303 luma_table = pl->lookup[0];
306 luma_table = pl->lookup[1];
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,
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;
333 st2205_code_image(CameraPrivateLibrary *pl, int **src,
334 unsigned char *dest, uint8_t shuffle_pattern, int allow_uv_corr)
336 int block = 0, used = 0, ret;
337 struct st2205_coord *shuffle_table;
338 struct st2205_image_header *header = (struct st2205_image_header*)dest;
340 /* Make room for the header */
341 dest += sizeof(*header);
343 if (shuffle_pattern >= pl->no_shuffles) {
344 gp_log (GP_LOG_ERROR, "st2205", "invalid shuffle pattern");
345 return GP_ERROR_BAD_PARAMETERS;
348 shuffle_table = pl->shuffle[shuffle_pattern];
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);
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);
371 return used + sizeof(*header);
375 st2205_rgb565_to_rgb24(CameraPrivateLibrary *pl, unsigned char *src,
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];
384 dest[y][x] = gdTrueColor ((w >> 8) & 0xf8,
395 st2205_rgb24_to_rgb565(CameraPrivateLibrary *pl, int **src,
400 for (y = 0; y < pl->height; y++) {
401 for (x = 0; x < pl->width; x++) {
404 int r = gdTrueColorGetRed(src[y][x]);
405 int g = gdTrueColorGetGreen(src[y][x]);
406 int b = gdTrueColorGetBlue(src[y][x]);
408 w = ((r << 8) & 0xf100) |
409 ((g << 3) & 0x07e0) |
417 return pl->height * pl->width * 2;
422 st2205_decode_image(CameraPrivateLibrary *pl, unsigned char *src, int **dest)
424 return GP_ERROR_NOT_SUPPORTED;
428 st2205_code_image(CameraPrivateLibrary *pl, int **src,
429 unsigned char *dest, uint8_t shuffle_pattern, int allow_uv_corr)
431 return GP_ERROR_NOT_SUPPORTED;
435 st2205_rgb565_to_rgb24(CameraPrivateLibrary *pl, unsigned char *src,
438 return GP_ERROR_NOT_SUPPORTED;
442 st2205_rgb24_to_rgb565(CameraPrivateLibrary *pl, int **src,
445 return GP_ERROR_NOT_SUPPORTED;