• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***************************************************************************/
2 /*                                                                         */
3 /*  pngshim.c                                                              */
4 /*                                                                         */
5 /*    PNG Bitmap glyph support.                                            */
6 /*                                                                         */
7 /*  Copyright 2013-2015 by                                                 */
8 /*  Google, Inc.                                                           */
9 /*  Written by Stuart Gill and Behdad Esfahbod.                            */
10 /*                                                                         */
11 /*  This file is part of the FreeType project, and may only be used,       */
12 /*  modified, and distributed under the terms of the FreeType project      */
13 /*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
14 /*  this file you indicate that you have read the license and              */
15 /*  understand and accept it fully.                                        */
16 /*                                                                         */
17 /***************************************************************************/
18 
19 
20 #include <ft2build.h>
21 #include FT_INTERNAL_DEBUG_H
22 #include FT_INTERNAL_STREAM_H
23 #include FT_TRUETYPE_TAGS_H
24 #include FT_CONFIG_STANDARD_LIBRARY_H
25 
26 
27 #ifdef FT_CONFIG_OPTION_USE_PNG
28 
29   /* We always include <stjmp.h>, so make libpng shut up! */
30 #define PNG_SKIP_SETJMP_CHECK 1
31 #include <png.h>
32 #include "pngshim.h"
33 
34 #include "sferrors.h"
35 
36 
37   /* This code is freely based on cairo-png.c.  There's so many ways */
38   /* to call libpng, and the way cairo does it is defacto standard.  */
39 
40   static unsigned int
multiply_alpha(unsigned int alpha,unsigned int color)41   multiply_alpha( unsigned int  alpha,
42                   unsigned int  color )
43   {
44     unsigned int  temp = alpha * color + 0x80;
45 
46 
47     return ( temp + ( temp >> 8 ) ) >> 8;
48   }
49 
50 
51   /* Premultiplies data and converts RGBA bytes => native endian. */
52   static void
premultiply_data(png_structp png,png_row_infop row_info,png_bytep data)53   premultiply_data( png_structp    png,
54                     png_row_infop  row_info,
55                     png_bytep      data )
56   {
57     unsigned int  i;
58 
59     FT_UNUSED( png );
60 
61 
62     for ( i = 0; i < row_info->rowbytes; i += 4 )
63     {
64       unsigned char*  base  = &data[i];
65       unsigned int    alpha = base[3];
66 
67 
68       if ( alpha == 0 )
69         base[0] = base[1] = base[2] = base[3] = 0;
70 
71       else
72       {
73         unsigned int  red   = base[0];
74         unsigned int  green = base[1];
75         unsigned int  blue  = base[2];
76 
77 
78         if ( alpha != 0xFF )
79         {
80           red   = multiply_alpha( alpha, red   );
81           green = multiply_alpha( alpha, green );
82           blue  = multiply_alpha( alpha, blue  );
83         }
84 
85         base[0] = (unsigned char)blue;
86         base[1] = (unsigned char)green;
87         base[2] = (unsigned char)red;
88         base[3] = (unsigned char)alpha;
89       }
90     }
91   }
92 
93 
94   /* Converts RGBx bytes to BGRA. */
95   static void
convert_bytes_to_data(png_structp png,png_row_infop row_info,png_bytep data)96   convert_bytes_to_data( png_structp    png,
97                          png_row_infop  row_info,
98                          png_bytep      data )
99   {
100     unsigned int  i;
101 
102     FT_UNUSED( png );
103 
104 
105     for ( i = 0; i < row_info->rowbytes; i += 4 )
106     {
107       unsigned char*  base  = &data[i];
108       unsigned int    red   = base[0];
109       unsigned int    green = base[1];
110       unsigned int    blue  = base[2];
111 
112 
113       base[0] = (unsigned char)blue;
114       base[1] = (unsigned char)green;
115       base[2] = (unsigned char)red;
116       base[3] = 0xFF;
117     }
118   }
119 
120 
121   /* Use error callback to avoid png writing to stderr. */
122   static void
error_callback(png_structp png,png_const_charp error_msg)123   error_callback( png_structp      png,
124                   png_const_charp  error_msg )
125   {
126     FT_Error*  error = (FT_Error*)png_get_error_ptr( png );
127 
128     FT_UNUSED( error_msg );
129 
130 
131     *error = FT_THROW( Out_Of_Memory );
132 #ifdef PNG_SETJMP_SUPPORTED
133     ft_longjmp( png_jmpbuf( png ), 1 );
134 #endif
135     /* if we get here, then we have no choice but to abort ... */
136   }
137 
138 
139   /* Use warning callback to avoid png writing to stderr. */
140   static void
warning_callback(png_structp png,png_const_charp error_msg)141   warning_callback( png_structp      png,
142                     png_const_charp  error_msg )
143   {
144     FT_UNUSED( png );
145     FT_UNUSED( error_msg );
146 
147     /* Just ignore warnings. */
148   }
149 
150 
151   static void
read_data_from_FT_Stream(png_structp png,png_bytep data,png_size_t length)152   read_data_from_FT_Stream( png_structp  png,
153                             png_bytep    data,
154                             png_size_t   length )
155   {
156     FT_Error   error;
157     png_voidp  p      = png_get_io_ptr( png );
158     FT_Stream  stream = (FT_Stream)p;
159 
160 
161     if ( FT_FRAME_ENTER( length ) )
162     {
163       FT_Error*  e = (FT_Error*)png_get_error_ptr( png );
164 
165 
166       *e = FT_THROW( Invalid_Stream_Read );
167       png_error( png, NULL );
168 
169       return;
170     }
171 
172     memcpy( data, stream->cursor, length );
173 
174     FT_FRAME_EXIT();
175   }
176 
177 
178   FT_LOCAL_DEF( FT_Error )
Load_SBit_Png(FT_GlyphSlot slot,FT_Int x_offset,FT_Int y_offset,FT_Int pix_bits,TT_SBit_Metrics metrics,FT_Memory memory,FT_Byte * data,FT_UInt png_len,FT_Bool populate_map_and_metrics)179   Load_SBit_Png( FT_GlyphSlot     slot,
180                  FT_Int           x_offset,
181                  FT_Int           y_offset,
182                  FT_Int           pix_bits,
183                  TT_SBit_Metrics  metrics,
184                  FT_Memory        memory,
185                  FT_Byte*         data,
186                  FT_UInt          png_len,
187                  FT_Bool          populate_map_and_metrics )
188   {
189     FT_Bitmap    *map   = &slot->bitmap;
190     FT_Error      error = FT_Err_Ok;
191     FT_StreamRec  stream;
192 
193     png_structp  png;
194     png_infop    info;
195     png_uint_32  imgWidth, imgHeight;
196 
197     int         bitdepth, color_type, interlace;
198     FT_Int      i;
199     png_byte*  *rows = NULL; /* pacify compiler */
200 
201 
202     if ( x_offset < 0 ||
203          y_offset < 0 )
204     {
205       error = FT_THROW( Invalid_Argument );
206       goto Exit;
207     }
208 
209     if ( !populate_map_and_metrics                            &&
210          ( (FT_UInt)x_offset + metrics->width  > map->width ||
211            (FT_UInt)y_offset + metrics->height > map->rows  ||
212            pix_bits != 32                                   ||
213            map->pixel_mode != FT_PIXEL_MODE_BGRA            ) )
214     {
215       error = FT_THROW( Invalid_Argument );
216       goto Exit;
217     }
218 
219     FT_Stream_OpenMemory( &stream, data, png_len );
220 
221     png = png_create_read_struct( PNG_LIBPNG_VER_STRING,
222                                   &error,
223                                   error_callback,
224                                   warning_callback );
225     if ( !png )
226     {
227       error = FT_THROW( Out_Of_Memory );
228       goto Exit;
229     }
230 
231     info = png_create_info_struct( png );
232     if ( !info )
233     {
234       error = FT_THROW( Out_Of_Memory );
235       png_destroy_read_struct( &png, NULL, NULL );
236       goto Exit;
237     }
238 
239     if ( ft_setjmp( png_jmpbuf( png ) ) )
240     {
241       error = FT_THROW( Invalid_File_Format );
242       goto DestroyExit;
243     }
244 
245     png_set_read_fn( png, &stream, read_data_from_FT_Stream );
246 
247     png_read_info( png, info );
248     png_get_IHDR( png, info,
249                   &imgWidth, &imgHeight,
250                   &bitdepth, &color_type, &interlace,
251                   NULL, NULL );
252 
253     if ( error                                        ||
254          ( !populate_map_and_metrics                &&
255            ( (FT_Int)imgWidth  != metrics->width  ||
256              (FT_Int)imgHeight != metrics->height ) ) )
257       goto DestroyExit;
258 
259     if ( populate_map_and_metrics )
260     {
261       FT_ULong  size;
262 
263 
264       metrics->width  = (FT_UShort)imgWidth;
265       metrics->height = (FT_UShort)imgHeight;
266 
267       map->width      = metrics->width;
268       map->rows       = metrics->height;
269       map->pixel_mode = FT_PIXEL_MODE_BGRA;
270       map->pitch      = (int)( map->width * 4 );
271       map->num_grays  = 256;
272 
273       /* reject too large bitmaps similarly to the rasterizer */
274       if ( map->rows > 0x7FFF || map->width > 0x7FFF )
275       {
276         error = FT_THROW( Array_Too_Large );
277         goto DestroyExit;
278       }
279 
280       /* this doesn't overflow: 0x7FFF * 0x7FFF * 4 < 2^32 */
281       size = map->rows * (FT_ULong)map->pitch;
282 
283       error = ft_glyphslot_alloc_bitmap( slot, size );
284       if ( error )
285         goto DestroyExit;
286     }
287 
288     /* convert palette/gray image to rgb */
289     if ( color_type == PNG_COLOR_TYPE_PALETTE )
290       png_set_palette_to_rgb( png );
291 
292     /* expand gray bit depth if needed */
293     if ( color_type == PNG_COLOR_TYPE_GRAY )
294     {
295 #if PNG_LIBPNG_VER >= 10209
296       png_set_expand_gray_1_2_4_to_8( png );
297 #else
298       png_set_gray_1_2_4_to_8( png );
299 #endif
300     }
301 
302     /* transform transparency to alpha */
303     if ( png_get_valid(png, info, PNG_INFO_tRNS ) )
304       png_set_tRNS_to_alpha( png );
305 
306     if ( bitdepth == 16 )
307       png_set_strip_16( png );
308 
309     if ( bitdepth < 8 )
310       png_set_packing( png );
311 
312     /* convert grayscale to RGB */
313     if ( color_type == PNG_COLOR_TYPE_GRAY       ||
314          color_type == PNG_COLOR_TYPE_GRAY_ALPHA )
315       png_set_gray_to_rgb( png );
316 
317     if ( interlace != PNG_INTERLACE_NONE )
318       png_set_interlace_handling( png );
319 
320     png_set_filler( png, 0xFF, PNG_FILLER_AFTER );
321 
322     /* recheck header after setting EXPAND options */
323     png_read_update_info(png, info );
324     png_get_IHDR( png, info,
325                   &imgWidth, &imgHeight,
326                   &bitdepth, &color_type, &interlace,
327                   NULL, NULL );
328 
329     if ( bitdepth != 8                              ||
330         !( color_type == PNG_COLOR_TYPE_RGB       ||
331            color_type == PNG_COLOR_TYPE_RGB_ALPHA ) )
332     {
333       error = FT_THROW( Invalid_File_Format );
334       goto DestroyExit;
335     }
336 
337     switch ( color_type )
338     {
339     default:
340       /* Shouldn't happen, but fall through. */
341 
342     case PNG_COLOR_TYPE_RGB_ALPHA:
343       png_set_read_user_transform_fn( png, premultiply_data );
344       break;
345 
346     case PNG_COLOR_TYPE_RGB:
347       /* Humm, this smells.  Carry on though. */
348       png_set_read_user_transform_fn( png, convert_bytes_to_data );
349       break;
350     }
351 
352     if ( FT_NEW_ARRAY( rows, imgHeight ) )
353     {
354       error = FT_THROW( Out_Of_Memory );
355       goto DestroyExit;
356     }
357 
358     for ( i = 0; i < (FT_Int)imgHeight; i++ )
359       rows[i] = map->buffer + ( y_offset + i ) * map->pitch + x_offset * 4;
360 
361     png_read_image( png, rows );
362 
363     FT_FREE( rows );
364 
365     png_read_end( png, info );
366 
367   DestroyExit:
368     png_destroy_read_struct( &png, &info, NULL );
369     FT_Stream_Close( &stream );
370 
371   Exit:
372     return error;
373   }
374 
375 #endif /* FT_CONFIG_OPTION_USE_PNG */
376 
377 
378 /* END */
379