• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***************************************************************************/
2 /*                                                                         */
3 /*  pngshim.c                                                              */
4 /*                                                                         */
5 /*    PNG Bitmap glyph support.                                            */
6 /*                                                                         */
7 /*  Copyright 2013-2017 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 <setjmp.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,FT_Bool metrics_only)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                  FT_Bool          metrics_only )
189   {
190     FT_Bitmap    *map   = &slot->bitmap;
191     FT_Error      error = FT_Err_Ok;
192     FT_StreamRec  stream;
193 
194     png_structp  png;
195     png_infop    info;
196     png_uint_32  imgWidth, imgHeight;
197 
198     int         bitdepth, color_type, interlace;
199     FT_Int      i;
200     png_byte*  *rows = NULL; /* pacify compiler */
201 
202 
203     if ( x_offset < 0 ||
204          y_offset < 0 )
205     {
206       error = FT_THROW( Invalid_Argument );
207       goto Exit;
208     }
209 
210     if ( !populate_map_and_metrics                            &&
211          ( (FT_UInt)x_offset + metrics->width  > map->width ||
212            (FT_UInt)y_offset + metrics->height > map->rows  ||
213            pix_bits != 32                                   ||
214            map->pixel_mode != FT_PIXEL_MODE_BGRA            ) )
215     {
216       error = FT_THROW( Invalid_Argument );
217       goto Exit;
218     }
219 
220     FT_Stream_OpenMemory( &stream, data, png_len );
221 
222     png = png_create_read_struct( PNG_LIBPNG_VER_STRING,
223                                   &error,
224                                   error_callback,
225                                   warning_callback );
226     if ( !png )
227     {
228       error = FT_THROW( Out_Of_Memory );
229       goto Exit;
230     }
231 
232     info = png_create_info_struct( png );
233     if ( !info )
234     {
235       error = FT_THROW( Out_Of_Memory );
236       png_destroy_read_struct( &png, NULL, NULL );
237       goto Exit;
238     }
239 
240     if ( ft_setjmp( png_jmpbuf( png ) ) )
241     {
242       error = FT_THROW( Invalid_File_Format );
243       goto DestroyExit;
244     }
245 
246     png_set_read_fn( png, &stream, read_data_from_FT_Stream );
247 
248     png_read_info( png, info );
249     png_get_IHDR( png, info,
250                   &imgWidth, &imgHeight,
251                   &bitdepth, &color_type, &interlace,
252                   NULL, NULL );
253 
254     if ( error                                        ||
255          ( !populate_map_and_metrics                &&
256            ( (FT_Int)imgWidth  != metrics->width  ||
257              (FT_Int)imgHeight != metrics->height ) ) )
258       goto DestroyExit;
259 
260     if ( populate_map_and_metrics )
261     {
262       metrics->width  = (FT_UShort)imgWidth;
263       metrics->height = (FT_UShort)imgHeight;
264 
265       map->width      = metrics->width;
266       map->rows       = metrics->height;
267       map->pixel_mode = FT_PIXEL_MODE_BGRA;
268       map->pitch      = (int)( map->width * 4 );
269       map->num_grays  = 256;
270 
271       /* reject too large bitmaps similarly to the rasterizer */
272       if ( map->rows > 0x7FFF || map->width > 0x7FFF )
273       {
274         error = FT_THROW( Array_Too_Large );
275         goto DestroyExit;
276       }
277     }
278 
279     /* convert palette/gray image to rgb */
280     if ( color_type == PNG_COLOR_TYPE_PALETTE )
281       png_set_palette_to_rgb( png );
282 
283     /* expand gray bit depth if needed */
284     if ( color_type == PNG_COLOR_TYPE_GRAY )
285     {
286 #if PNG_LIBPNG_VER >= 10209
287       png_set_expand_gray_1_2_4_to_8( png );
288 #else
289       png_set_gray_1_2_4_to_8( png );
290 #endif
291     }
292 
293     /* transform transparency to alpha */
294     if ( png_get_valid(png, info, PNG_INFO_tRNS ) )
295       png_set_tRNS_to_alpha( png );
296 
297     if ( bitdepth == 16 )
298       png_set_strip_16( png );
299 
300     if ( bitdepth < 8 )
301       png_set_packing( png );
302 
303     /* convert grayscale to RGB */
304     if ( color_type == PNG_COLOR_TYPE_GRAY       ||
305          color_type == PNG_COLOR_TYPE_GRAY_ALPHA )
306       png_set_gray_to_rgb( png );
307 
308     if ( interlace != PNG_INTERLACE_NONE )
309       png_set_interlace_handling( png );
310 
311     png_set_filler( png, 0xFF, PNG_FILLER_AFTER );
312 
313     /* recheck header after setting EXPAND options */
314     png_read_update_info(png, info );
315     png_get_IHDR( png, info,
316                   &imgWidth, &imgHeight,
317                   &bitdepth, &color_type, &interlace,
318                   NULL, NULL );
319 
320     if ( bitdepth != 8                              ||
321         !( color_type == PNG_COLOR_TYPE_RGB       ||
322            color_type == PNG_COLOR_TYPE_RGB_ALPHA ) )
323     {
324       error = FT_THROW( Invalid_File_Format );
325       goto DestroyExit;
326     }
327 
328     if ( metrics_only )
329       goto DestroyExit;
330 
331     switch ( color_type )
332     {
333     default:
334       /* Shouldn't happen, but fall through. */
335 
336     case PNG_COLOR_TYPE_RGB_ALPHA:
337       png_set_read_user_transform_fn( png, premultiply_data );
338       break;
339 
340     case PNG_COLOR_TYPE_RGB:
341       /* Humm, this smells.  Carry on though. */
342       png_set_read_user_transform_fn( png, convert_bytes_to_data );
343       break;
344     }
345 
346     if ( populate_map_and_metrics )
347     {
348       /* this doesn't overflow: 0x7FFF * 0x7FFF * 4 < 2^32 */
349       FT_ULong  size = map->rows * (FT_ULong)map->pitch;
350 
351 
352       error = ft_glyphslot_alloc_bitmap( slot, size );
353       if ( error )
354         goto DestroyExit;
355     }
356 
357     if ( FT_NEW_ARRAY( rows, imgHeight ) )
358     {
359       error = FT_THROW( Out_Of_Memory );
360       goto DestroyExit;
361     }
362 
363     for ( i = 0; i < (FT_Int)imgHeight; i++ )
364       rows[i] = map->buffer + ( y_offset + i ) * map->pitch + x_offset * 4;
365 
366     png_read_image( png, rows );
367 
368     FT_FREE( rows );
369 
370     png_read_end( png, info );
371 
372   DestroyExit:
373     png_destroy_read_struct( &png, &info, NULL );
374     FT_Stream_Close( &stream );
375 
376   Exit:
377     return error;
378   }
379 
380 #endif /* FT_CONFIG_OPTION_USE_PNG */
381 
382 
383 /* END */
384