Source code upload
[framework/connectivity/libgphoto2.git] / camlibs / ax203 / ax203_compress_jpeg.c
1 /* Appotech ax203 picframe JPEG-ish compression code
2  * For ax203 picture frames with firmware version 3.5.x
3  *
4  *   Copyright (c) 2010 Hans de Goede <hdegoede@redhat.com>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation; either version 2.1 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include "config.h"
22
23 #include <stdlib.h>
24 #include <string.h>
25 #ifdef HAVE_GD
26 #include <gd.h>
27 #endif
28
29 #include "ax203.h"
30 #ifdef HAVE_LIBJPEG
31 #include "jpeg_memsrcdest.h"
32 #endif
33
34 #if defined(HAVE_GD) && defined(HAVE_LIBJPEG)
35 static int
36 locate_tables_n_write(JOCTET *jpeg, int jpeg_size, JOCTET table_type,
37         uint8_t *outbuf, int *outc)
38 {
39         int i, size, found, orig_outc = *outc;
40
41         /* Reserve space for writing table size */
42         *outc += 2;
43
44         /* Locate tables and write them to outbuf */
45         for (i = 2; i < jpeg_size; i += size) {
46                 if (jpeg[i] != 0xff) {
47                         gp_log (GP_LOG_ERROR, "ax203",
48                                 "marker does not start with ff?");
49                         return GP_ERROR_CORRUPTED_DATA; 
50                 }
51                 if (jpeg[i + 1] == 0xda)
52                         break;
53
54                 found = jpeg[i + 1] == table_type;
55                 i += 2;
56                 size = ((jpeg[i] << 8) | jpeg[i + 1]) - 2;
57                 i += 2;
58
59                 if (found) {
60                         memcpy(outbuf + *outc, jpeg + i, size);
61                         *outc += size;
62                 }
63         }
64
65         size = *outc - orig_outc;
66         outbuf[orig_outc    ] = size >> 8;
67         outbuf[orig_outc + 1] = size;
68
69         return GP_OK;
70 }
71
72 static int
73 copy_huffman(uint8_t *dst, JOCTET *src, int n)
74 {
75         int i, copied = 0;
76
77         for (i = 0; i < n; i++) {
78                 /* Skip 0xff escaping 0x00's (ax203 special) */
79                 if (i && src[i - 1] == 0xff && src[i] == 0x00)
80                         continue;
81
82                 dst[copied++] = src[i];
83         }
84
85         return copied;
86 }
87
88 /* Store MCU info needed by the picture frame (probably so that it
89    can start decompression with any block to allow transition effects) */
90 static void
91 add_mcu_info(uint8_t *outbuf, int block_nr, int last_Y, int last_Cb,
92         int last_Cr, int huffman_addr)
93 {
94         int info_addr = block_nr * 8;
95
96         /* skip header */       
97         huffman_addr -= 16;
98         info_addr += 16;
99
100         /* This is crazy, but it is what the pictframe wants */
101         huffman_addr -= 8 * block_nr;
102
103         /* Store last DC vals + huffman data address */
104         outbuf[info_addr + 0] = last_Y;
105         outbuf[info_addr + 1] = last_Y >> 8;
106         outbuf[info_addr + 2] = last_Cb;
107         outbuf[info_addr + 3] = last_Cb >> 8;
108         outbuf[info_addr + 4] = last_Cr;
109         outbuf[info_addr + 5] = last_Cr >> 8;
110         outbuf[info_addr + 6] = huffman_addr;
111         outbuf[info_addr + 7] = huffman_addr >> 8;
112 }
113
114 int
115 ax206_compress_jpeg(Camera *camera, int **in, uint8_t *outbuf, int out_size,
116         int width, int height)
117 {
118         struct jpeg_compress_struct cinfo;
119         struct jpeg_decompress_struct dinfo;
120         struct jpeg_error_mgr jcerr, jderr;
121         JSAMPROW row_pointer[1];
122         JOCTET *buf = NULL, *regular_jpeg = NULL;
123         jvirt_barray_ptr *in_coefficients;
124         int i, x, y, stop, size, ret, outc;
125         unsigned long regular_jpeg_size = 0, buf_size = 0;
126         int last_dc_val[3] = { 0, 0, 0 };
127
128         /* We have a rgb24bit image in the desired dimensions, first we
129            compress it into a regular jpeg, which we use as a base for
130            creating the ax203 JPEG-ish format */
131         cinfo.err = jpeg_std_error (&jcerr);
132         jpeg_create_compress (&cinfo);
133         jpeg_mem_dest (&cinfo, &regular_jpeg, &regular_jpeg_size);
134         cinfo.image_width = width;
135         cinfo.image_height = height;
136         cinfo.input_components = 3;
137         cinfo.in_color_space = JCS_RGB;
138         jpeg_set_defaults (&cinfo);
139         cinfo.comp_info[0].h_samp_factor = 2;
140         cinfo.comp_info[0].v_samp_factor = 2;
141
142         jpeg_start_compress (&cinfo, TRUE);
143         while( cinfo.next_scanline < cinfo.image_height ) {
144                 JOCTET row[width * 3];
145                 for (i = 0; i < width; i++) {
146                         int p = in[cinfo.next_scanline][i];
147                         row[i * 3 + 0] = gdTrueColorGetRed(p);
148                         row[i * 3 + 1] = gdTrueColorGetGreen(p);
149                         row[i * 3 + 2] = gdTrueColorGetBlue(p);
150                 }
151                 row_pointer[0] = row;
152                 jpeg_write_scanlines (&cinfo, row_pointer, 1);
153         }
154         jpeg_finish_compress (&cinfo);
155         jpeg_destroy_compress (&cinfo);
156
157         /* Write image header to outbuf */
158         outbuf[0] = width >> 8;
159         outbuf[1] = width;
160         outbuf[2] = height >> 8;
161         outbuf[3] = height;
162         outbuf[4] = 3; /* 2x uv sub-sampling */
163
164         outbuf[5] = 0;
165         outbuf[6] = 1;
166         outbuf[7] = 1;
167
168         outbuf[8] = 0;
169         outbuf[9] = 1;
170         outbuf[10] = 1;
171
172         outbuf[11] = 0;
173         outbuf[12] = 1;
174         outbuf[13] = 1;
175
176         outbuf[14] = 0;
177         outbuf[15] = 0;
178
179         /* Make outc point to after the MCU info table, so to the start of
180            the quantisation tables */
181         outc = 16 + ((width + 15) / 16) * ((height + 15) / 16) * 8;
182
183         /* Locate quant tables and write them to outbuf */
184         ret = locate_tables_n_write (regular_jpeg, regular_jpeg_size, 0xdb,
185                                      outbuf, &outc);
186         if (ret < 0) return ret;
187         
188         /* Locate huffman tables and write them to outbuf */
189         ret = locate_tables_n_write (regular_jpeg, regular_jpeg_size, 0xc4,
190                                      outbuf, &outc);
191         if (ret < 0) return ret;
192
193         /* The ax203 has 2 perculiarities in the huffman data for the JPEG
194            DCT coefficients:
195            1) It wants the MCU huffman data for each new MCU to start on a byte
196               boundary
197            2) The component order in an MCU is Cb Cr Y, rather then Y Cb Cr 
198
199            We solve both these issues by decompressing the regular jpeg we've
200            just created into its raw coefficients, following by creating
201            mini JPEGs with the size of one MCU block, using
202            the raw-coefficients from the regular JPEG we first created.
203
204            This allows us to both shuffle the component order, and to byte
205            align the start of each MCU. */
206
207         /* Get the raw coefficients from the regular JPEG */
208         dinfo.err = jpeg_std_error (&jderr);
209         jpeg_create_decompress (&dinfo);
210         jpeg_mem_src (&dinfo, regular_jpeg, regular_jpeg_size);
211         jpeg_read_header (&dinfo, TRUE);
212         in_coefficients = jpeg_read_coefficients (&dinfo);
213
214         /* Create a JPEG compression object for our mini JPEGs */
215         cinfo.err = jpeg_std_error (&jcerr);
216         jpeg_create_compress (&cinfo);
217         cinfo.image_width = 16;
218         cinfo.image_height = 16;
219         cinfo.input_components = 3;
220         cinfo.in_color_space = JCS_RGB;
221
222 #if JPEG_LIB_VERSION >= 80
223         cinfo.min_DCT_h_scaled_size = dinfo.min_DCT_h_scaled_size;
224         cinfo.min_DCT_v_scaled_size = dinfo.min_DCT_h_scaled_size;
225         cinfo.jpeg_width = 16;
226         cinfo.jpeg_height = 16;
227 #endif
228         jpeg_set_defaults (&cinfo);
229         /* We will write Cb values as comp. 0, so give it chroma settings */
230         cinfo.comp_info[0].h_samp_factor = 1; 
231         cinfo.comp_info[0].v_samp_factor = 1;
232         cinfo.comp_info[0].quant_tbl_no = 1;
233         cinfo.comp_info[0].dc_tbl_no = 1;
234         cinfo.comp_info[0].ac_tbl_no = 1;
235         /* We will write Y values as comp. 2, so give it luma settings */
236         cinfo.comp_info[2].h_samp_factor = 2;
237         cinfo.comp_info[2].v_samp_factor = 2;
238         cinfo.comp_info[2].quant_tbl_no = 0;
239         cinfo.comp_info[2].dc_tbl_no = 0;
240         cinfo.comp_info[2].ac_tbl_no = 0;
241
242         /* And create a mini JPEG for each MCU with the shuffled component order
243            and extract its huffman data. This fixes our component order problem and
244            gives us the needed byte aligning for free. */
245         for (y = 0; y < (height + 15) / 16; y++) {
246                 JBLOCKARRAY in_row[3], out_row[3];
247                 jvirt_barray_ptr out_barray[3];
248
249                 /* Notice the shuffling of components happening here !! */
250                 in_row[0] = dinfo.mem->access_virt_barray((j_common_ptr)&dinfo,
251                                                           in_coefficients[1],
252                                                           y, 1, 0);
253                 in_row[1] = dinfo.mem->access_virt_barray((j_common_ptr)&dinfo,
254                                                           in_coefficients[2],
255                                                           y, 1, 0);
256                 in_row[2] = dinfo.mem->access_virt_barray((j_common_ptr)&dinfo,
257                                                            in_coefficients[0],
258                                                            y * 2, 2, 0);
259
260                 for (x = 0; x < (width + 15) / 16; x++) {
261                         /* (Re)init our destination buffer */
262                         jpeg_mem_dest (&cinfo, &buf, &buf_size);
263
264                         /* Add MCU info block to output */
265                         add_mcu_info (outbuf, y * ((width + 15) / 16) + x,
266                                       last_dc_val[2], last_dc_val[0],
267                                       last_dc_val[1], outc);
268
269                         /* Allocate virtual arrays to store the coefficients
270                            for the mini jpg we are making */
271                         out_barray[0] = cinfo.mem->request_virt_barray(
272                                                 (j_common_ptr)&cinfo,
273                                                 JPOOL_IMAGE, 0, 1, 1, 1);
274                         out_barray[1] = cinfo.mem->request_virt_barray(
275                                                 (j_common_ptr)&cinfo,
276                                                 JPOOL_IMAGE, 0, 1, 1, 1);
277                         out_barray[2] = cinfo.mem->request_virt_barray(
278                                                 (j_common_ptr)&cinfo,
279                                                 JPOOL_IMAGE, 0, 2, 2, 2);
280                         jpeg_write_coefficients (&cinfo, out_barray);
281
282                         /* Copy over our 3 (or 6) coefficient blocks, and
283                            apply last_dc_val */
284                         out_row[0] = cinfo.mem->access_virt_barray(
285                                                 (j_common_ptr)&cinfo,
286                                                 out_barray[0], 0, 1, 1);
287                         out_row[1] = cinfo.mem->access_virt_barray(
288                                                 (j_common_ptr)&cinfo,
289                                                 out_barray[1], 0, 1, 1);
290                         out_row[2] = cinfo.mem->access_virt_barray(
291                                                 (j_common_ptr)&cinfo,
292                                                 out_barray[2], 0, 2, 1);
293
294                         for (i = 0; i < 2; i++) {
295                                 memcpy (out_row[i][0][0],
296                                         in_row[i][0][x],
297                                         sizeof(JBLOCK));
298                                 out_row[i][0][0][0] -= last_dc_val[i];
299                                 last_dc_val[i] = in_row[i][0][x][0];
300                         }
301
302                         memcpy (out_row[2][0][0],
303                                 in_row[2][0][x * 2 + 0],
304                                 sizeof(JBLOCK));
305                         memcpy (out_row[2][0][1],
306                                 in_row[2][0][x * 2 + 1],
307                                 sizeof(JBLOCK));
308                         memcpy (out_row[2][1][0],
309                                 in_row[2][1][x * 2 + 0],
310                                 sizeof(JBLOCK));
311                         memcpy (out_row[2][1][1],
312                                 in_row[2][1][x * 2 + 1],
313                                 sizeof(JBLOCK));
314                         out_row[2][0][0][0] -= last_dc_val[2];
315                         out_row[2][0][1][0] -= last_dc_val[2];
316                         out_row[2][1][0][0] -= last_dc_val[2];
317                         out_row[2][1][1][0] -= last_dc_val[2];
318                         last_dc_val[2] = in_row[2][1][x * 2 + 1][0];
319
320                         jpeg_finish_compress (&cinfo);
321
322                         stop = 0;
323                         for (i = 2; i < buf_size && !stop; i += size) {
324                                 stop = buf[i] == 0xff && buf[i + 1] == 0xda;
325                                 i += 2;
326                                 size = (buf[i] << 8) | buf[i + 1];
327                         }
328                         if (i >= buf_size) {
329                                 gp_log (GP_LOG_ERROR, "ax203",
330                                         "missing in ff da marker?");
331                                 return GP_ERROR_CORRUPTED_DATA; 
332                         }
333
334                         size = buf_size - i - 2;
335                         if ((outc + size) > out_size) {
336                                 gp_log (GP_LOG_ERROR, "ax203",
337                                         "jpeg output buffer overflow");
338                                 return GP_ERROR_FIXED_LIMIT_EXCEEDED;   
339                         }
340                         outc += copy_huffman(outbuf + outc, buf + i, size);
341
342                         /* Cleanup our memory dest */
343                         free (buf);
344                         buf = NULL;
345                         buf_size = 0;
346                 }
347         }
348
349         /* We're done with the jpeg decompress (input) and
350            compress (output) objs */
351         jpeg_destroy_decompress (&dinfo);
352         jpeg_destroy_compress (&cinfo);
353         free(regular_jpeg);
354
355         return outc;
356 }
357 #endif