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