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 void *a_file_must_define_something=(void *) &a_file_must_define_something;
33 /* ANSI says so */
34
35 #else
36
37 # include <png.h>
38
39 # define int32 _int32
40 # define uint32 _uint32
41 # define int16 _int16
42 # define uint16 _uint16
43 # define int8 _int8
44 # define uint8 _uint8
45
46 # include "basics.h"
47 # include "gimage.h"
48
49 struct mem_buffer {
50 char *buffer;
51 size_t size;
52 size_t read;
53 };
54
user_error_fn(png_structp png_ptr,png_const_charp error_msg)55 static void user_error_fn(png_structp png_ptr, png_const_charp error_msg) {
56 fprintf(stderr,"%s\n", error_msg);
57 #if (PNG_LIBPNG_VER < 10500)
58 longjmp(png_ptr->jmpbuf,1);
59 #else
60 png_longjmp (png_ptr, 1);
61 #endif
62 }
63
user_warning_fn(png_structp UNUSED (png_ptr),png_const_charp warning_msg)64 static void user_warning_fn(png_structp UNUSED(png_ptr), png_const_charp warning_msg) {
65 fprintf(stderr,"%s\n", warning_msg);
66 }
67
mem_read_fn(png_structp png_ptr,png_bytep data,png_size_t sz)68 static void mem_read_fn(png_structp png_ptr, png_bytep data, png_size_t sz) {
69 struct mem_buffer* buf = (struct mem_buffer*)png_get_io_ptr(png_ptr);
70
71 if (buf->read + sz > buf->size) {
72 png_error(png_ptr, "memory buffer is too small");
73 return;
74 }
75
76 memcpy(data, buf->buffer+buf->read, sz);
77 buf->read += sz;
78 }
79
GImageReadPngFull(void * io,int in_memory)80 static GImage *GImageReadPngFull(void *io, int in_memory) {
81 GImage *ret=NULL;
82 struct _GImage *base;
83 png_structp png_ptr;
84 png_infop info_ptr;
85 png_bytep *row_pointers=NULL;
86 png_bytep trans_alpha;
87 int num_trans;
88 png_color_16p trans_color;
89 unsigned i;
90 int test;
91
92 png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
93 (void *)NULL, user_error_fn, user_warning_fn);
94
95 if (!png_ptr)
96 return( NULL );
97
98 info_ptr = png_create_info_struct(png_ptr);
99 if (!info_ptr) {
100 png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
101 return( NULL );
102 }
103
104 #if (PNG_LIBPNG_VER < 10500)
105 test = setjmp(png_ptr->jmpbuf);
106 #else
107 test = setjmp(*png_set_longjmp_fn(png_ptr, longjmp, sizeof (jmp_buf)));
108 #endif
109 if (test) {
110 /* Free all of the memory associated with the png_ptr and info_ptr */
111 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
112 if ( ret!=NULL ) {
113 GImageDestroy(ret);
114 free(row_pointers);
115 }
116 /* If we get here, we had a problem reading the file */
117 return( NULL );
118 }
119
120 if (in_memory) {
121 png_set_read_fn(png_ptr, io, mem_read_fn);
122 } else {
123 png_init_io(png_ptr, (FILE*)io);
124 }
125 png_read_info(png_ptr, info_ptr);
126 png_set_strip_16(png_ptr);
127 if ( (png_get_color_type(png_ptr, info_ptr)==PNG_COLOR_TYPE_GRAY || png_get_color_type(png_ptr, info_ptr)==PNG_COLOR_TYPE_PALETTE ) &&
128 png_get_bit_depth(png_ptr,info_ptr) == 1 )
129 /* Leave bitmaps packed */;
130 else
131 png_set_packing(png_ptr);
132 if ( png_get_color_type(png_ptr, info_ptr)==PNG_COLOR_TYPE_GRAY_ALPHA )
133 png_set_strip_alpha(png_ptr);
134 if (png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_RGB)
135 png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
136
137 if ( png_get_color_type(png_ptr, info_ptr)==PNG_COLOR_TYPE_GRAY && png_get_bit_depth(png_ptr,info_ptr) == 1 ) {
138 ret = GImageCreate(it_mono,png_get_image_width(png_ptr,info_ptr),png_get_image_height(png_ptr,info_ptr));
139 } else if ( png_get_color_type(png_ptr, info_ptr)==PNG_COLOR_TYPE_GRAY || png_get_color_type(png_ptr, info_ptr)==PNG_COLOR_TYPE_GRAY_ALPHA ) {
140 GClut *clut;
141 ret = GImageCreate(it_index,png_get_image_width(png_ptr,info_ptr),png_get_image_height(png_ptr,info_ptr));
142 clut = ret->u.image->clut;
143 clut->is_grey = true;
144 clut->clut_len = 256;
145 for ( i=0; i<256; ++i )
146 clut->clut[i] = COLOR_CREATE(i,i,i);
147 } else if ( png_get_color_type(png_ptr, info_ptr)==PNG_COLOR_TYPE_RGB_ALPHA ) {
148 ret = GImageCreate(it_rgba,png_get_image_width(png_ptr,info_ptr),png_get_image_height(png_ptr,info_ptr));
149 } else if ( png_get_color_type(png_ptr, info_ptr)==PNG_COLOR_TYPE_RGB || png_get_color_type(png_ptr, info_ptr)==PNG_COLOR_TYPE_RGB_ALPHA )
150 ret = GImageCreate(it_true,png_get_image_width(png_ptr,info_ptr),png_get_image_height(png_ptr,info_ptr));
151 else {
152 png_colorp palette;
153 int num_palette;
154 GClut *clut;
155 ret = GImageCreate(png_get_bit_depth(png_ptr,info_ptr) != 1? it_index : it_mono,
156 png_get_image_width(png_ptr,info_ptr),png_get_image_height(png_ptr,info_ptr));
157 clut = ret->u.image->clut;
158 if ( clut==NULL )
159 clut = ret->u.image->clut = (GClut *) calloc(1,sizeof(GClut));
160 clut->is_grey = true;
161 png_get_PLTE(png_ptr,info_ptr,&palette,&num_palette);
162 clut->clut_len = num_palette;
163 for ( i=0; i<(unsigned)num_palette; ++i )
164 clut->clut[i] = COLOR_CREATE(palette[i].red,
165 palette[i].green,
166 palette[i].blue);
167 }
168 png_get_tRNS(png_ptr,info_ptr,&trans_alpha,&num_trans,&trans_color);
169 base = ret->u.image;
170 if ( (png_get_valid(png_ptr,info_ptr,PNG_INFO_tRNS)) && num_trans>0 ) {
171 if ( png_get_color_type(png_ptr, info_ptr)==PNG_COLOR_TYPE_RGB || png_get_color_type(png_ptr, info_ptr)==PNG_COLOR_TYPE_RGB_ALPHA )
172 base->trans = COLOR_CREATE(
173 (trans_color->red>>8),
174 (trans_color->green>>8),
175 (trans_color->blue>>8));
176 else if ( base->image_type == it_mono )
177 base->trans = trans_alpha ? trans_alpha[0] : 0;
178 else
179 base->clut->trans_index = base->trans = trans_alpha ? trans_alpha[0] : 0;
180 }
181
182 row_pointers = (png_byte **) malloc(png_get_image_height(png_ptr,info_ptr)*sizeof(png_bytep));
183 for ( i=0; i<png_get_image_height(png_ptr,info_ptr); ++i )
184 row_pointers[i] = (png_bytep) (base->data + i*base->bytes_per_line);
185
186 /* Ignore progressive loads for now */
187 /* libpng wants me to do it with callbacks, but that doesn't sit well */
188 /* with my wish to be in control... */
189 png_read_image(png_ptr,row_pointers);
190 png_read_end(png_ptr, NULL);
191
192 if ( png_get_color_type(png_ptr, info_ptr)==PNG_COLOR_TYPE_RGB || png_get_color_type(png_ptr, info_ptr)==PNG_COLOR_TYPE_RGB_ALPHA ) {
193 /* PNG orders its bytes as AABBGGRR instead of 00RRGGBB */
194 uint32 *ipt, *iend;
195 for ( ipt = (uint32 *) (base->data), iend=ipt+base->width*base->height; ipt<iend; ++ipt ) {
196 uint32 r, g, b, a = *ipt&0xff000000;
197 r = (*ipt )&0xff;
198 g = (*ipt>>8 )&0xff;
199 b = (*ipt>>16)&0xff;
200 *ipt = COLOR_CREATE( r,g,b ) | a;
201 }
202 }
203
204 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
205 free(row_pointers);
206 /* Note png b&w images come out as indexed */
207 return( ret );
208 }
209
GImageReadPngBuf(char * buf,size_t sz)210 GImage *GImageReadPngBuf(char* buf, size_t sz) {
211 struct mem_buffer membuf = {buf, sz, 0};
212 return GImageReadPngFull(&membuf, true);
213 }
214
GImageRead_Png(FILE * fp)215 GImage *GImageRead_Png(FILE *fp) {
216 return GImageReadPngFull(fp, false);
217 }
218
GImageReadPng(char * filename)219 GImage *GImageReadPng(char *filename) {
220 GImage *ret=NULL;
221 FILE *fp;
222
223 fp = fopen(filename, "rb");
224 if (!fp)
225 return( NULL );
226
227 ret = GImageRead_Png(fp);
228 fclose(fp);
229 return( ret );
230 }
231 #endif
232