Merge branch 'bug_599_picload_fd_leak' into experimental
[vuplus_dvbapp] / lib / gdi / epng.cpp
1 #define PNG_SKIP_SETJMP_CHECK
2 #include <png.h>
3 #include <stdio.h>
4 #include <lib/gdi/epng.h>
5 #include <unistd.h>
6
7 extern "C" {
8 #include <jpeglib.h>
9 }
10
11 int loadPNG(ePtr<gPixmap> &result, const char *filename)
12 {
13         __u8 header[8];
14         FILE *fp=fopen(filename, "rb");
15         
16         if (!fp)
17         {
18 //              eDebug("couldn't open %s", filename );
19                 return 0;
20         }
21         if (!fread(header, 8, 1, fp))
22         {
23                 eDebug("couldn't read");
24                 fclose(fp);
25                 return 0;
26         }
27         if (png_sig_cmp(header, 0, 8))
28         {
29                 fclose(fp);
30                 return 0;
31         }
32         png_structp png_ptr=png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
33         if (!png_ptr)
34         {
35                 eDebug("no pngptr");
36                 fclose(fp);
37                 return 0;
38         }
39         png_infop info_ptr=png_create_info_struct(png_ptr);
40         if (!info_ptr)
41         {
42                 eDebug("no info ptr");
43                 png_destroy_read_struct(&png_ptr, (png_infopp)0, (png_infopp)0);
44                 fclose(fp);
45                 return 0;
46         }
47         png_infop end_info = png_create_info_struct(png_ptr);
48         if (!end_info)
49         {
50                 eDebug("no end");
51                 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
52                 fclose(fp);
53                 return 0;
54          }
55         if (setjmp(png_ptr->jmpbuf))
56         {
57                 eDebug("das war wohl nix");
58                 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
59                 fclose(fp);
60                 result = 0;
61                 return 0;
62         }
63         png_init_io(png_ptr, fp);
64         png_set_sig_bytes(png_ptr, 8);
65         png_set_invert_alpha(png_ptr);
66         png_read_info(png_ptr, info_ptr);
67         
68         png_uint_32 width, height;
69         int bit_depth;
70         int color_type;
71         
72         png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, 0, 0, 0);
73         
74         if (color_type == PNG_COLOR_TYPE_GRAY || color_type & PNG_COLOR_MASK_PALETTE)
75         {
76                 result=new gPixmap(eSize(width, height), bit_depth);
77                 gSurface *surface = result->surface;
78         
79                 png_bytep *rowptr=new png_bytep[height];
80         
81                 for (unsigned int i=0; i<height; i++)
82                         rowptr[i]=((png_byte*)(surface->data))+i*surface->stride;
83                 png_read_rows(png_ptr, rowptr, 0, height);
84         
85                 delete [] rowptr;
86         
87                 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_PLTE))
88                 {
89                         png_color *palette;
90                         int num_palette;
91                         png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette);
92                         if (num_palette)
93                                 surface->clut.data=new gRGB[num_palette];
94                         else
95                                 surface->clut.data=0;
96                         surface->clut.colors=num_palette;
97                         
98                         for (int i=0; i<num_palette; i++)
99                         {
100                                 surface->clut.data[i].a=0;
101                                 surface->clut.data[i].r=palette[i].red;
102                                 surface->clut.data[i].g=palette[i].green;
103                                 surface->clut.data[i].b=palette[i].blue;
104                         }
105                         if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
106                         {
107                                 png_byte *trans;
108                                 png_get_tRNS(png_ptr, info_ptr, &trans, &num_palette, 0);
109                                 for (int i=0; i<num_palette; i++)
110                                         surface->clut.data[i].a=255-trans[i];
111                         }
112                 } else
113                 {
114                         surface->clut.data=0;
115                         surface->clut.colors=0;
116                 }
117                 surface->clut.start=0;
118                 png_read_end(png_ptr, end_info);
119         } else {
120                 result=0;
121                 eDebug("%s: %dx%dx%d png, %d", filename, (int)width, (int)height, (int)bit_depth, color_type);
122         }
123
124         png_destroy_read_struct(&png_ptr, &info_ptr,&end_info);
125         fclose(fp);
126         return 0;
127 }
128
129 struct my_error_mgr {
130         struct jpeg_error_mgr pub;
131         jmp_buf setjmp_buffer;
132 };
133
134 typedef struct my_error_mgr * my_error_ptr;
135
136 static void
137 my_error_exit (j_common_ptr cinfo)
138 {
139         my_error_ptr myerr = (my_error_ptr) cinfo->err;
140         (*cinfo->err->output_message) (cinfo);
141         longjmp(myerr->setjmp_buffer, 1);
142 }
143
144 int loadJPG(ePtr<gPixmap> &result, const char *filename, ePtr<gPixmap> alpha)
145 {
146         struct jpeg_decompress_struct cinfo;
147         struct my_error_mgr jerr;
148         FILE *infile;
149         JSAMPARRAY buffer;
150         int row_stride;
151         infile = fopen(filename, "rb");
152         result = 0;
153
154         if (alpha)
155         {
156                 if (alpha->surface->bpp != 8)
157                 {
158                         eWarning("alpha channel for jpg must be 8bit");
159                         alpha = 0;
160                 }
161         }
162
163         if (!infile)
164                 return -1;
165         cinfo.err = jpeg_std_error(&jerr.pub);
166         jerr.pub.error_exit = my_error_exit;
167         if (setjmp(jerr.setjmp_buffer)) {
168                 result = 0;
169                 jpeg_destroy_decompress(&cinfo);
170                 fclose(infile);
171                 return -1;
172         }
173         jpeg_create_decompress(&cinfo);
174         jpeg_stdio_src(&cinfo, infile);
175         (void) jpeg_read_header(&cinfo, TRUE);
176         cinfo.out_color_space = JCS_RGB;
177         cinfo.scale_denom = 1;
178
179         (void) jpeg_start_decompress(&cinfo);
180
181         int grayscale = cinfo.output_components == 1;
182
183         if (alpha)
184         {
185                 if (((int)cinfo.output_width != alpha->surface->x) || ((int)cinfo.output_height != alpha->surface->y))
186                 {
187                         eWarning("alpha channel size (%dx%d) must match jpeg size (%dx%d)", alpha->surface->x, alpha->surface->y, cinfo.output_width, cinfo.output_height);
188                         alpha = 0;
189                 }
190                 if (grayscale)
191                 {
192                         eWarning("we don't support grayscale + alpha at the moment");
193                         alpha = 0;
194                 }
195         }
196
197         result = new gPixmap(eSize(cinfo.output_width, cinfo.output_height), grayscale ? 8 : 32);
198
199         row_stride = cinfo.output_width * cinfo.output_components;
200         buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
201         while (cinfo.output_scanline < cinfo.output_height) {
202                 int y = cinfo.output_scanline;
203                 (void) jpeg_read_scanlines(&cinfo, buffer, 1);
204                 unsigned char *dst = ((unsigned char*)result->surface->data) + result->surface->stride * y;
205                 unsigned char *src = (unsigned char*)buffer[0];
206                 unsigned char *palpha = alpha ? ((unsigned char*)alpha->surface->data + alpha->surface->stride * y) : 0;
207                 if (grayscale)
208                         memcpy(dst, src, cinfo.output_width);
209                 else
210                 {
211                         int x;
212                         for (x = 0; x < (int)cinfo.output_width; ++x)
213                         {
214                                 *dst++ = src[2];
215                                 *dst++ = src[1];
216                                 *dst++ = src[0];
217                                 src += 3;
218                                 if (palpha)
219                                         *dst++ = *palpha++;
220                                 else 
221                                         *dst++ = 0xFF;
222                         }
223                 }
224         }
225         (void) jpeg_finish_decompress(&cinfo);
226         jpeg_destroy_decompress(&cinfo);
227         fclose(infile);
228         return 0;
229 }
230
231 int savePNG(const char *filename, gPixmap *pixmap)
232 {
233
234         eDebug("\33[33m %s \33[0m",filename);
235         FILE *fp=fopen(filename, "wb");
236         if (!fp)
237                 return -1;
238         
239         gSurface *surface = pixmap->surface;
240         if (!surface)
241                 return -2;
242         
243         png_structp png_ptr=png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
244         if (!png_ptr)
245         {
246                 eDebug("write png, couldnt allocate write struct");
247                 fclose(fp);
248                 unlink(filename);
249                 return -2;
250         }
251         png_infop info_ptr=png_create_info_struct(png_ptr);
252         if (!info_ptr)
253         {
254                 eDebug("info");
255                 png_destroy_write_struct(&png_ptr, 0);
256                 fclose(fp);
257                 unlink(filename);
258                 return -3;
259         }
260
261         png_set_IHDR(png_ptr, info_ptr, surface->x, surface->y, surface->bpp/surface->bypp, 
262                 PNG_COLOR_TYPE_RGB_ALPHA, 
263                 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
264
265         if (setjmp(png_ptr->jmpbuf))
266         {
267                 eDebug("error :/");
268                 png_destroy_write_struct(&png_ptr, &info_ptr);
269                 fclose(fp);
270                 unlink(filename);
271                 return -4;
272         }
273         png_init_io(png_ptr, fp);
274         png_set_filter(png_ptr, 0, PNG_FILTER_NONE|PNG_FILTER_SUB|PNG_FILTER_PAETH);
275         png_set_compression_level(png_ptr, Z_BEST_COMPRESSION);
276
277         png_write_info(png_ptr, info_ptr);
278         png_set_packing(png_ptr);
279
280         png_byte *row_pointer;
281         png_byte *cr = new png_byte[surface->y * surface->stride];
282         if (cr == NULL)
283         {
284                 printf("Error: malloc\n");
285                 return -5;
286         }
287         for (int i=0; i<surface->y; ++i)
288         {
289                 row_pointer=((png_byte*)surface->data)+i*surface->stride;
290                 if (surface->bypp == 4)
291                 {
292                         memcpy(cr, row_pointer, surface->stride);
293                         for (int j=0; j<surface->stride; j+=4)
294                         {
295                                 unsigned char tmp = cr[j];
296                                 cr[j] = cr[j+2];
297                                 cr[j+2]= tmp;
298                         }
299                         png_write_row(png_ptr, cr);
300                 }
301                 else
302                         png_write_row(png_ptr, row_pointer);
303         }
304         delete [] cr;
305
306         png_write_end(png_ptr, info_ptr);
307         png_destroy_write_struct(&png_ptr, &info_ptr);
308         fclose(fp);
309         eDebug("wrote png ! fine !");
310         return 0;
311 }