1 /* Copyright (C) 2000-2012 by George Williams */
2 /*
3  * Redistribution and use in source and binary forms, with or without
4  * modification, are permitted provided that the following conditions are met:
5 
6  * Redistributions of source code must retain the above copyright notice, this
7  * list of conditions and the following disclaimer.
8 
9  * Redistributions in binary form must reproduce the above copyright notice,
10  * this list of conditions and the following disclaimer in the documentation
11  * and/or other materials provided with the distribution.
12 
13  * The name of the author may not be used to endorse or promote products
14  * derived from this software without specific prior written permission.
15 
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include <fontforge-config.h>
29 
30 #ifdef _NO_LIBPNG
31 
32 static int a_file_must_define_something=0;	/* ANSI says so */
33 
34 #else
35 
36 #include <png.h>
37 
38 #define int32 _int32
39 #define uint32 _uint32
40 #define int16 _int16
41 #define uint16 _uint16
42 #define int8 _int8
43 #define uint8 _uint8
44 
45 #include "gimage.h"
46 #include "ffglib.h"
47 
user_error_fn(png_structp png_ptr,png_const_charp error_msg)48 static void user_error_fn(png_structp png_ptr, png_const_charp error_msg) {
49     fprintf(stderr, "%s\n", error_msg );
50 #if (PNG_LIBPNG_VER < 10500)
51     longjmp(png_ptr->jmpbuf,1);
52 #else
53     png_longjmp (png_ptr, 1);
54 #endif
55 }
56 
user_warning_fn(png_structp UNUSED (png_ptr),png_const_charp warning_msg)57 static void user_warning_fn(png_structp UNUSED(png_ptr), png_const_charp warning_msg) {
58     fprintf(stderr,"%s\n", warning_msg);
59 }
60 
mem_write_fn(png_structp png_ptr,png_bytep data,png_size_t sz)61 static void mem_write_fn(png_structp png_ptr, png_bytep data, png_size_t sz) {
62     GByteArray *arr = (GByteArray*)(png_get_io_ptr(png_ptr));
63     g_byte_array_append(arr, data, sz);
64 }
65 
mem_flush_fn(png_structp UNUSED (png_ptr))66 static void mem_flush_fn(png_structp UNUSED(png_ptr)) {
67 }
68 
GImageWritePngFull(GImage * gi,void * io,bool in_memory,int compression_level,bool progressive)69 static int GImageWritePngFull(GImage *gi, void *io, bool in_memory, int compression_level, bool progressive) {
70     struct _GImage *base = gi->list_len==0?gi->u.image:gi->u.images[0];
71     png_structp png_ptr;
72     png_infop info_ptr;
73     png_byte **rows;
74     int i;
75     int bit_depth;
76     int color_type;
77     int num_palette;
78     png_bytep trans_alpha = NULL;
79     png_color_16p trans_color = NULL;
80     png_colorp palette = NULL;
81 
82    png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
83       (void *)NULL, user_error_fn, user_warning_fn);
84 
85    if (!png_ptr) {
86 return(false);
87    }
88 
89    info_ptr = png_create_info_struct(png_ptr);
90    if (!info_ptr) {
91       png_destroy_write_struct(&png_ptr,  (png_infopp)NULL);
92 return(false);
93    }
94 
95 #if (PNG_LIBPNG_VER < 10500)
96     if (setjmp(png_ptr->jmpbuf))
97 #else
98    if (setjmp(*png_set_longjmp_fn(png_ptr, longjmp, sizeof (jmp_buf))))
99 #endif
100    {
101       png_destroy_write_struct(&png_ptr,  (png_infopp)NULL);
102 return(false);
103    }
104 
105    if (in_memory) {
106         png_set_write_fn(png_ptr, io, mem_write_fn, mem_flush_fn);
107    } else {
108         png_init_io(png_ptr, (FILE*)io);
109    }
110 
111    if (compression_level >= 0 && compression_level <= 9) {
112         png_set_compression_level(png_ptr, compression_level);
113    }
114 
115    bit_depth = 8;
116    num_palette = base->clut==NULL?2:base->clut->clut_len;
117    if ( base->image_type==it_index || base->image_type==it_bitmap ) {
118        color_type = PNG_COLOR_TYPE_PALETTE;
119        if ( num_palette<=2 )
120 	   bit_depth=1;
121        else if ( num_palette<=4 )
122 	   bit_depth=2;
123        else if ( num_palette<=16 )
124 	   bit_depth=4;
125    } else {
126        color_type = PNG_COLOR_TYPE_RGB;
127        if ( base->image_type == it_rgba )
128 	   color_type = PNG_COLOR_TYPE_RGB_ALPHA;
129    }
130 
131    png_set_IHDR(png_ptr, info_ptr, base->width, base->height,
132 		bit_depth, color_type, progressive,
133 		PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
134    if ( base->image_type==it_index || base->image_type==it_bitmap ) {
135        palette = (png_color *) malloc(num_palette*sizeof(png_color));
136        if ( base->clut==NULL ) {
137 	    palette[0].red = palette[0].green = palette[0].blue = 0;
138 	    palette[1].red = palette[1].green = palette[1].blue = 0xff;
139        } else {
140 	   for ( i=0; i<num_palette; ++i ) {
141 		long col = base->clut->clut[i];
142 		palette[i].red = COLOR_RED(col);
143 		palette[i].green = COLOR_GREEN(col);
144 		palette[i].blue = COLOR_BLUE(col);
145 	   }
146        }
147        png_set_PLTE(png_ptr, info_ptr, palette, num_palette);
148        if ( num_palette<=16 )
149 	   png_set_packing(png_ptr);
150 
151        if ( base->trans!=(Color)-1 ) {
152 	   trans_alpha = (png_bytep) malloc(1);
153 	   trans_alpha[0] = base->trans;
154        }
155    } else {
156        if ( base->trans!=(Color)-1 ) {
157 	   trans_color = (png_color_16p) malloc(sizeof(png_color_16));
158 	   trans_color->red = COLOR_RED(base->trans);
159 	   trans_color->green = COLOR_GREEN(base->trans);
160 	   trans_color->blue = COLOR_BLUE(base->trans);
161        }
162    }
163    if ( base->trans!=(Color)-1 ) {
164        png_set_tRNS(png_ptr, info_ptr, trans_alpha, 1, trans_color);
165    }
166    png_write_info(png_ptr, info_ptr);
167 
168     if (color_type == PNG_COLOR_TYPE_RGB)
169         png_set_filler(png_ptr, '\0', PNG_FILLER_AFTER);
170 
171     if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_RGB_ALPHA)
172         png_set_bgr(png_ptr);
173 
174     rows = (png_byte **) malloc(base->height*sizeof(png_byte *));
175     for ( i=0; i<base->height; ++i )
176 	rows[i] = (png_byte *) (base->data + i*base->bytes_per_line);
177 
178     png_write_image(png_ptr,rows);
179 
180     png_write_end(png_ptr, info_ptr);
181 
182     free(trans_alpha);
183     free(trans_color);
184     free(palette);
185     png_destroy_write_struct(&png_ptr, &info_ptr);
186     free(rows);
187 return( 1 );
188 }
189 
GImageWritePngBuf(GImage * gi,char ** buf,size_t * sz,int compression_level,int progressive)190 int GImageWritePngBuf(GImage *gi, char** buf, size_t* sz, int compression_level, int progressive) {
191     GByteArray *arr;
192     *buf = NULL;
193     *sz = 0;
194 
195     arr = g_byte_array_new();
196     if (arr == NULL) {
197         return false;
198     }
199 
200     if (!GImageWritePngFull(gi, arr, true, compression_level, progressive)) {
201         g_byte_array_free(arr, true);
202         return false;
203     }
204 
205     // The only reason we do this step is because we don't want
206     // to pollute g_free across function calls...
207     *buf = malloc(arr->len);
208     if (*buf == NULL) {
209         return false;
210     }
211     *sz = arr->len;
212 
213     memcpy(*buf, arr->data, arr->len);
214     g_byte_array_free(arr, true);
215     return true;
216 }
217 
GImageWrite_Png(GImage * gi,FILE * fp,int progressive)218 int GImageWrite_Png(GImage *gi, FILE *fp, int progressive) {
219     return GImageWritePngFull(gi, fp, false, -1, progressive);
220 }
221 
GImageWritePng(GImage * gi,char * filename,int progressive)222 int GImageWritePng(GImage *gi, char *filename, int progressive) {
223     FILE *fp;
224     int ret;
225 
226    /* open the file */
227    fp = fopen(filename, "wb");
228    if (!fp)
229 return(false);
230     ret = GImageWrite_Png(gi,fp,progressive);
231     fclose(fp);
232 return( ret );
233 }
234 
235 #endif
236