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