OSDN Git Service

test for replaceBitmapData Lossless with alpha0
[swfed/swfed.git] / src / swf_png.c
1 /*
2   +----------------------------------------------------------------------+
3   | Author: yoya@awm.jp                                                  |
4   +----------------------------------------------------------------------+
5 */
6
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include "swf_define.h"
10
11 #ifdef HAVE_PNG
12
13 #include <png.h>
14 #include "bitstream.h"
15 #include "swf_rgb.h"  // Lossless  format=3
16 #include "swf_rgba.h" // Lossless2 format=3
17 #include "swf_xrgb.h" // Lossless  format=5
18 #include "swf_argb.h" // Lossless2 format=5
19 #include "swf_png.h"
20
21 typedef struct my_png_buffer_ {
22     unsigned char *data;
23     unsigned long data_len;
24     unsigned long data_offset;
25 } my_png_buffer;
26
27 /*
28  * png read
29  */
30
31 static void
32 png_data_read_func(png_structp png_ptr, png_bytep buf, png_size_t size) {
33     my_png_buffer *png_buff = (my_png_buffer *)png_get_io_ptr(png_ptr);
34     if (png_buff->data_offset + size <= png_buff->data_len) {
35         memcpy(buf, png_buff->data + png_buff->data_offset, size);
36         png_buff->data_offset += size;
37     } else {
38         fprintf(stderr, "png_data_read_func: ! png_buff->data_offset(%lu) + size(%zd) <= png_buff->data_len(%lu)\n",
39                 png_buff->data_offset, size, png_buff->data_len);
40         png_error(png_ptr,"png_read_read_func failed");
41     }
42 }
43
44 static void png_data_read(png_structp png_ptr, my_png_buffer *png_buff) {
45     png_set_read_fn(png_ptr, (png_voidp) png_buff,
46                     (png_rw_ptr)png_data_read_func);
47 }
48
49 /*
50  * png write
51  */
52
53 void
54 png_data_write_func(png_structp png_ptr, png_bytep buf, png_size_t size) {
55     my_png_buffer *png_buff = (my_png_buffer *)png_get_io_ptr(png_ptr);
56     unsigned long new_data_len;
57     unsigned char *tmp;
58     if (png_buff->data_offset + size > png_buff->data_len) {
59         new_data_len = 2 * png_buff->data_len;
60         if (png_buff->data_offset + size > new_data_len) {
61             new_data_len = png_buff->data_offset + size;
62         }
63         tmp = realloc(png_buff->data, new_data_len);
64         if (tmp == NULL) {
65             fprintf(stderr, "png_data_write_func: can't realloc: new_data_len(%lu), data_len(%lu)\n",
66                     new_data_len, png_buff->data_len);
67             png_error(png_ptr,"png_data_write_func failed");
68         }
69         png_buff->data = tmp;
70         png_buff->data_len = new_data_len;
71     }
72     memcpy(png_buff->data + png_buff->data_offset, buf, size);
73     png_buff->data_offset += size;
74 }
75
76 void
77 png_data_write(png_structp png_ptr, my_png_buffer *png_buff) {
78     png_set_write_fn(png_ptr, (png_voidp) png_buff,
79                      (png_rw_ptr)png_data_write_func, NULL);
80 }
81
82 /*
83  *
84  */
85
86 void *
87 pngconv_png2lossless(unsigned char *png_data, unsigned long png_data_len,
88                      int *tag_no, int *format,
89                      unsigned short *width, unsigned short *height,
90                      void **colormap, int *colormap_count, int rgb15) {
91     volatile png_structp png_ptr = NULL;
92     volatile png_infop png_info_ptr = NULL;
93     my_png_buffer png_buff;
94     int bpp, color_type;
95     png_uint_32 png_width = 0, png_height = 0;
96     volatile png_bytepp png_image_data = NULL;
97     register png_uint_32 x, y;
98     void *image_data = NULL;
99     png_color *palette = NULL;
100     int palette_num = 0;
101     png_bytep trans = NULL;
102     int num_trans = 0;
103     png_color_16p trans_values = NULL;
104
105     if (png_data == NULL) {
106         fprintf(stderr, "pngconv_png2lossless: png_data == NULL\n");
107         return NULL;
108     }
109     if (png_sig_cmp((png_bytep)png_data, 0, 8)) { // bad signature
110         fprintf(stderr, "pngconv_png2lossless: is not PNG!\n");
111         return NULL;
112     }
113     png_ptr = png_create_read_struct
114         (PNG_LIBPNG_VER_STRING, NULL,NULL,NULL);
115     if (! png_ptr) {
116         fprintf(stderr, "pngconv_png2lossless: can't create read_struct\n");
117         return NULL;
118     }
119     if (setjmp(png_jmpbuf(png_ptr))) {
120         fprintf(stderr, "pngconv_png2lossless: libpng error jump occured\n");
121         png_destroy_read_struct((png_structpp) &png_ptr,
122                                 (png_infopp) &png_info_ptr, NULL);
123         if (png_image_data) {
124             for (y=0 ; y < png_height ; y++) {
125                 free(png_image_data[y]);
126             }
127             free(png_image_data);
128         }
129         return NULL;
130     }
131     png_info_ptr = png_create_info_struct(png_ptr);
132     if (! png_info_ptr) {
133         fprintf(stderr, "pngconv_png2lossless: can't create info_struct\n");
134         png_destroy_read_struct ((png_structpp)&png_ptr, NULL, NULL);
135         return NULL;
136     }
137     png_buff.data = png_data;
138     png_buff.data_len = png_data_len;
139     png_buff.data_offset = 0;
140
141     png_data_read(png_ptr, &png_buff);
142     png_read_info(png_ptr, png_info_ptr);
143     png_get_IHDR(png_ptr, png_info_ptr,
144                  &png_width, &png_height, &bpp, &color_type,
145                  NULL, NULL, NULL);
146     *width = (unsigned short) png_width;
147     *height = (unsigned short) png_height;
148
149     if ((rgb15 > 0) && (color_type !=  PNG_COLOR_TYPE_RGB)) { // warning
150         fprintf(stderr, "rgb15 is %d but color_type is not PNG24\n", rgb15);
151     }
152
153     switch(color_type) {
154     case PNG_COLOR_TYPE_PALETTE:
155         *format = 3;
156         png_get_PLTE(png_ptr, png_info_ptr, &palette, &palette_num);
157         if (png_get_tRNS(png_ptr, png_info_ptr, &trans, &num_trans,
158                          &trans_values) && (num_trans > 0)) {
159             *tag_no = 36; // DefineBitsLossless2
160         } else {
161             *tag_no = 20; // DefineBitsLossless
162         }
163         break;
164     case PNG_COLOR_TYPE_RGB:
165         if (rgb15 > 0) {
166             *format = 4;
167         } else {
168             *format = 5;
169         }
170         *tag_no = 20; /* DefineBitsLossless */
171         break;
172     case PNG_COLOR_TYPE_RGB_ALPHA:
173         *format = 5;
174         *tag_no = 36; /* DefineBitsLossless2 */
175         if (png_get_valid(png_ptr, png_info_ptr, PNG_INFO_tRNS))
176             png_set_tRNS_to_alpha(png_ptr);
177         break;
178     default:
179         fprintf(stderr, "pngconv_png2lossless: color_type=%d not implemented yet.\n", color_type);
180         png_destroy_read_struct((png_structpp)&png_ptr,
181                                 (png_infopp)&png_info_ptr, NULL);
182         return NULL;
183     }
184     if (bpp > 8) {
185         fprintf(stderr, "pngconv_png2lossless: bpp=%d not implemented yet. accept only bpp <= 8\n", bpp);
186         png_destroy_read_struct((png_structpp)&png_ptr,
187                                 (png_infopp)&png_info_ptr, NULL);
188         return NULL;
189     }
190     
191     png_image_data = (png_bytepp) malloc(png_height * sizeof(png_bytep));
192     for (y=0 ; y < png_height ; y++) {
193         png_image_data[y] = (png_bytep) malloc(png_get_rowbytes(png_ptr, png_info_ptr));
194     }
195     png_read_image(png_ptr, png_image_data);
196     /*
197      * image copy
198      */
199     if (color_type == PNG_COLOR_TYPE_PALETTE) {
200         register int i;
201         unsigned char *indices_data;
202         *colormap_count = palette_num;
203         if (num_trans == 0) {
204             swf_rgb_t *result_colormap = malloc(sizeof(swf_rgb_t) * palette_num);   // Lossless
205             for (i=0 ; i < palette_num ; i++) {
206                 result_colormap[i].red   = palette[i].red;
207                 result_colormap[i].green = palette[i].green;
208                 result_colormap[i].blue  = palette[i].blue;
209             }
210             *colormap = result_colormap;
211         } else {
212               swf_rgba_t *result_colormap = malloc(sizeof(swf_rgba_t) * palette_num);   // Lossless2
213             for (i=0 ; i < palette_num ; i++) {
214                 if (i < num_trans) {
215                     int alpha_value = trans[i];
216                     result_colormap[i].red   = palette[i].red   * alpha_value / 0xff;
217                     result_colormap[i].green = palette[i].green * alpha_value / 0xff;
218                     result_colormap[i].blue  = palette[i].blue  * alpha_value / 0xff;
219                     result_colormap[i].alpha = alpha_value;
220                 } else {
221                     result_colormap[i].red   = palette[i].red;
222                     result_colormap[i].green = palette[i].green;
223                     result_colormap[i].blue  = palette[i].blue;
224                     result_colormap[i].alpha = 0xff; // opaque
225                 }
226             }
227             *colormap = result_colormap;
228         }
229         indices_data = malloc(((png_width+ 3) & -4) * png_height);
230         i = 0;
231         for (y=0 ; y < png_height ; y++) {
232             bitstream_t *bs = bitstream_open();
233             if (bs == NULL) {
234                 free(*colormap);
235                 *colormap = NULL;
236                 free(indices_data);
237                 // free png image datas
238                 return NULL;
239             }
240             bitstream_input(bs, png_image_data[y], png_width);
241             for (x=0 ; x < png_width ; x++) {
242                 indices_data[i] = bitstream_getbits(bs, bpp);
243                 i++;
244             }
245             while (i % 4) { i++; } // 4byte alignment
246             bitstream_close(bs);
247         }
248         image_data = indices_data;
249     } else if (color_type == PNG_COLOR_TYPE_RGB) {
250         swf_xrgb_t *xrgb_list;
251         xrgb_list = malloc(sizeof(swf_xrgb_t) * png_width * png_height);
252         for (y=0 ; y < png_height ; y++) {
253             for (x=0 ; x < png_width ; x++) {
254                 xrgb_list[x+y*png_width].red   = png_image_data[y][3*x + 0];
255                 xrgb_list[x+y*png_width].green = png_image_data[y][3*x + 1];
256                 xrgb_list[x+y*png_width].blue  = png_image_data[y][3*x + 2];
257             }
258         }
259         image_data = xrgb_list;
260     } else { // PNG_COLOR_TYPE_RGB_ALPHA
261         swf_argb_t *argb_list;
262         argb_list = malloc(sizeof(swf_argb_t) * png_width * png_height);
263         for (y=0 ; y < png_height ; y++) {
264             for (x=0 ; x < png_width ; x++) {
265                 int alpha_value = png_image_data[y][4*x + 3];
266                 argb_list[x+y*png_width].red   = png_image_data[y][4*x + 0] * alpha_value / 0xff;
267                 argb_list[x+y*png_width].green = png_image_data[y][4*x + 1] * alpha_value / 0xff;
268                 argb_list[x+y*png_width].blue  = png_image_data[y][4*x + 2] * alpha_value / 0xff;
269                 argb_list[x+y*png_width].alpha = alpha_value;
270             }
271         }
272         image_data = argb_list;
273     }
274     for (y=0 ; y < png_height ; y++) {
275         free(png_image_data[y]);
276     }
277     free(png_image_data);
278     /*
279      * destruct
280      */
281     png_destroy_read_struct((png_structpp) &png_ptr,
282                             (png_infopp) &png_info_ptr, NULL);
283     return image_data;
284 }
285
286 unsigned char *
287 pngconv_lossless2png(void *image_data,
288                      unsigned short width, unsigned short height,
289                      void *index_data,
290                      unsigned short index_data_count,
291                      int tag_no, int format,
292                      unsigned long *length) {
293     volatile png_structp png_ptr = NULL;
294     volatile png_infop png_info_ptr = NULL;
295     volatile my_png_buffer png_buff;
296     png_uint_32 png_width = 0, png_height = 0;
297     int bpp, color_type;
298     volatile png_bytepp png_image_data = NULL;
299     png_uint_32 x, y;
300     volatile png_colorp png_palette = NULL;
301     if (image_data == NULL) {
302         fprintf(stderr, "pngconv_lossless2png: image_data == NULL\n");
303         return NULL;
304     }
305     if ((format != 3) && (format != 5)) {
306         fprintf(stderr, "jpegconv_lossless2png: format=%d not implemented yes.\n", format);
307         return NULL;
308     }
309     png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL,NULL,NULL);
310     if (! png_ptr) {
311         fprintf(stderr, "jpegconv_lossless2png: can't create write_struct\n");
312         return NULL;
313     }
314     if (setjmp(png_jmpbuf(png_ptr))) {
315         fprintf(stderr, "pngconv_lossless2png: libpng error jump occured\n");
316         free(png_palette);
317         if (png_image_data) {
318             for (y=0 ; y < png_height ; y++) {
319                 free(png_image_data[y]);
320             }
321             free(png_image_data);
322         }
323         free(png_buff.data);
324         png_destroy_write_struct((png_structpp) &png_ptr,
325                                  (png_infopp) &png_info_ptr);
326         return NULL;
327     }
328     png_info_ptr = png_create_info_struct(png_ptr);
329     if (! png_info_ptr) {
330         fprintf(stderr, "jpegconv_lossless2png: can't create info_struct\n");
331         png_destroy_write_struct((png_structpp) &png_ptr, NULL);
332         return NULL;
333     }
334     //
335     png_width = width;
336     png_height = height;
337     bpp = 8;
338     if (format == 3) {
339         color_type =  PNG_COLOR_TYPE_PALETTE;
340     } else if (tag_no == 20) { /* DefineBitsLossless */
341         color_type = PNG_COLOR_TYPE_RGB;
342     } else if (tag_no == 36) { /* DefineBitsLossless2 */
343         color_type = PNG_COLOR_TYPE_RGB_ALPHA;
344     } else {
345         fprintf(stderr, "jpegconv_lossless2png: format!=3 and tag_no=%d not implemented.\n",
346                 tag_no);
347         png_destroy_write_struct((png_structpp) &png_ptr,
348                                  (png_infopp) &png_info_ptr);
349         return NULL;
350     }
351     png_set_filter(png_ptr, 0, PNG_ALL_FILTERS);
352     png_set_IHDR(png_ptr, png_info_ptr,
353                  png_width, png_height, bpp, color_type,
354                  PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
355                  PNG_FILTER_TYPE_DEFAULT);
356     if (format == 3) {
357         register int i;
358         if (index_data_count == 0) {
359             fprintf(stderr, "jpegconv_lossless2png: index_data_count == 0 at line(%d)\n", __LINE__);
360             png_destroy_write_struct((png_structpp) &png_ptr,
361                                      (png_infopp) &png_info_ptr);
362             return NULL;
363         }
364         png_palette = (png_colorp) malloc(sizeof(png_color)*index_data_count);
365         png_set_packing(png_ptr);
366         if (tag_no == 20) {
367             swf_rgb_t *rgb_list  = index_data;
368             for (i=0 ; i < index_data_count ; i++) {
369                 png_palette[i].red   = rgb_list[i].red;
370                 png_palette[i].green = rgb_list[i].green;
371                 png_palette[i].blue  = rgb_list[i].blue;
372             }
373         } else {
374             swf_rgba_t *rgba_list  = index_data;
375             png_bytep trans = NULL;
376             int num_trans = 0;
377             png_color_16p trans_values = NULL;
378             for (i=0 ; i < index_data_count ; i++) {
379                 png_palette[i].red   = rgba_list[i].red;
380                 png_palette[i].green = rgba_list[i].green;
381                 png_palette[i].blue  = rgba_list[i].blue;
382             }
383             // scanning to end of transparent pixel
384             for (i = index_data_count - 1 ; 0 <= i ; i--) {
385                 if (rgba_list[i].alpha < 254) { // 254 XXX
386                     break;
387                 }
388             }
389             num_trans = i + 1;
390             if (num_trans > 0) {
391                 trans = malloc(num_trans);
392                 for (i = 0 ; i < num_trans ; i++) {
393                     trans[i] = rgba_list[i].alpha;
394                 }
395                 png_set_tRNS(png_ptr, png_info_ptr, trans, num_trans,
396                              trans_values);
397                 free(trans);
398             }
399
400         }
401         png_set_PLTE( png_ptr, png_info_ptr, png_palette, index_data_count);
402         free(png_palette);
403     }
404     png_image_data = (png_bytepp) malloc(png_height * sizeof(png_bytep));
405     if (color_type == PNG_COLOR_TYPE_PALETTE) {
406         for (y=0 ; y < png_height ; y++) {
407             png_image_data[y] = (png_bytep) malloc(png_get_rowbytes(png_ptr, png_info_ptr));
408             for (x=0 ; x < png_width ; x++) {
409                 unsigned char *data = image_data;
410                 png_image_data[y][x] = data[x + y*((png_width +3) & -4)];
411             }
412         }
413         
414     } else if (color_type == PNG_COLOR_TYPE_RGB) {
415         swf_xrgb_t *xrgb_list = image_data;
416         for (y=0 ; y < png_height ; y++) {
417             png_image_data[y] = (png_bytep) malloc(png_get_rowbytes(png_ptr, png_info_ptr));
418             for (x=0 ; x < png_width ; x++) {
419                 png_image_data[y][3*x]   =  xrgb_list[x+y*png_width].red;
420                 png_image_data[y][3*x+1] =  xrgb_list[x+y*png_width].green;
421                 png_image_data[y][3*x+2] =  xrgb_list[x+y*png_width].blue;
422             }
423         }
424     } else {
425         swf_argb_t *argb_list = image_data;
426         for (y=0 ; y < png_height ; y++) {
427             png_image_data[y] = (png_bytep) malloc(png_get_rowbytes(png_ptr, png_info_ptr));
428             for (x=0 ; x < png_width ; x++) {
429                 png_image_data[y][4*x]   = argb_list[x+y*png_width].red;
430                 png_image_data[y][4*x+1] = argb_list[x+y*png_width].green;
431                 png_image_data[y][4*x+2] = argb_list[x+y*png_width].blue;
432                 png_image_data[y][4*x+3] = argb_list[x+y*png_width].alpha;
433             }
434         }
435         
436     }
437     png_buff.data = NULL;
438     png_buff.data_len = 0;
439     png_buff.data_offset = 0;
440     png_data_write((png_structp) png_ptr, (my_png_buffer*) &png_buff);
441
442     png_write_info(png_ptr, png_info_ptr);
443     png_write_image(png_ptr, png_image_data);
444     png_write_end(png_ptr, png_info_ptr);
445     //
446     for (y=0 ; y < png_height ; y++) {
447         free(png_image_data[y]);
448     }
449     free(png_image_data);
450     png_destroy_write_struct((png_structpp) &png_ptr,
451                              (png_infopp) &png_info_ptr);
452     *length = png_buff.data_offset;
453     return png_buff.data;
454 }
455
456 #endif /* HAVE_PNG */