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