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
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
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
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
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
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
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 )
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