add (untested) loadJPG with optional alpha channel
[vuplus_dvbapp] / 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, 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, "r");
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
176         int grayscale = cinfo.output_components == 1;
177
178         if (alpha)
179         {
180                 if (((int)cinfo.output_width != alpha->surface->x) || ((int)cinfo.output_height != alpha->surface->y))
181                 {
182                         eWarning("alpha channel size (%dx%d) must match jpeg size (%dx%d)", alpha->surface->x, alpha->surface->y, cinfo.output_width, cinfo.output_height);
183                         alpha = 0;
184                 }
185                 if (grayscale)
186                 {
187                         eWarning("we don't support grayscale + alpha at the moment");
188                         alpha = 0;
189                 }
190         }
191
192         result = new gPixmap(eSize(cinfo.output_width, cinfo.output_height), grayscale ? 8 : 32);
193
194         (void) jpeg_start_decompress(&cinfo);
195         row_stride = cinfo.output_width * cinfo.output_components;
196         buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
197         while (cinfo.output_scanline < cinfo.output_height) {
198                 (void) jpeg_read_scanlines(&cinfo, buffer, 1);
199                 unsigned char *dst = ((unsigned char*)result->surface->data) + result->surface->stride * cinfo.output_scanline;
200                 unsigned char *src = (unsigned char*)buffer[0];
201                 unsigned char *palpha = alpha ? ((unsigned char*)alpha->surface->data + alpha->surface->stride * cinfo.output_scanline) : 0;
202                 if (grayscale)
203                         memcpy(dst, src, row_stride);
204                 else
205                 {
206                         int x;
207                         for (x = 0; x < (int)cinfo.output_width; ++x)
208                         {
209                                 *dst++ = *src++;
210                                 *dst++ = *src++;
211                                 *dst++ = *src++;
212                                 if (alpha)
213                                         *dst++ = *palpha++;
214                                 else
215                                         *dst++ = 0xFF;
216                         }
217                 }
218         }
219         (void) jpeg_finish_decompress(&cinfo);
220         jpeg_destroy_decompress(&cinfo);
221         fclose(infile);
222         return 0;
223 }
224
225 int savePNG(const char *filename, gPixmap *pixmap)
226 {
227         FILE *fp=fopen(filename, "wb");
228         if (!fp)
229                 return -1;
230         
231         gSurface *surface = pixmap->surface;
232         if (!surface)
233                 return -2;
234         
235         png_structp png_ptr=png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
236         if (!png_ptr)
237         {
238                 eDebug("write png, couldnt allocate write struct");
239                 fclose(fp);
240                 unlink(filename);
241                 return -2;
242         }
243         png_infop info_ptr=png_create_info_struct(png_ptr);
244         if (!info_ptr)
245         {
246                 eDebug("info");
247                 png_destroy_write_struct(&png_ptr, 0);
248                 fclose(fp);
249                 unlink(filename);
250                 return -3;
251         }
252         if (setjmp(png_ptr->jmpbuf))
253         {
254                 eDebug("error :/");
255                 png_destroy_write_struct(&png_ptr, &info_ptr);
256                 fclose(fp);
257                 unlink(filename);
258                 return -4;
259         }
260         png_init_io(png_ptr, fp);
261         png_set_filter(png_ptr, 0, PNG_FILTER_NONE|PNG_FILTER_SUB|PNG_FILTER_PAETH);
262         png_set_compression_level(png_ptr, Z_BEST_COMPRESSION);
263
264         png_set_IHDR(png_ptr, info_ptr, surface->x, surface->y, surface->bpp, 
265                 surface->clut.data ? PNG_COLOR_TYPE_PALETTE : PNG_COLOR_TYPE_GRAY, 
266                 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
267         if (surface->clut.data)
268         {
269                 png_color palette[surface->clut.colors];
270                 png_byte trans[surface->clut.colors];
271                 for (int i=0; i<surface->clut.colors; ++i)
272                 {
273                         palette[i].red=surface->clut.data[i].r;
274                         palette[i].green=surface->clut.data[i].g;
275                         palette[i].blue=surface->clut.data[i].b;
276                         trans[i]=255-surface->clut.data[i].a;
277                 }
278                 png_set_PLTE(png_ptr, info_ptr, palette, surface->clut.colors);
279                 png_set_tRNS(png_ptr, info_ptr, trans, surface->clut.colors, 0);
280         }
281         png_write_info(png_ptr, info_ptr);
282         png_set_packing(png_ptr);
283         png_byte *row_pointers[surface->y];
284         for (int i=0; i<surface->y; ++i)
285                 row_pointers[i]=((png_byte*)surface->data)+i*surface->stride;
286         png_write_image(png_ptr, row_pointers);
287         png_write_end(png_ptr, info_ptr);
288         png_destroy_write_struct(&png_ptr, &info_ptr);
289         fclose(fp);
290         eDebug("wrote png ! fine !");
291         return 0;
292 }