1/***************************************************************************/
2/*                                                                         */
3/*  pngshim.c                                                              */
4/*                                                                         */
5/*    PNG Bitmap glyph support.                                            */
6/*                                                                         */
7/*  Copyright 2013-2018 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#if defined( TT_CONFIG_OPTION_EMBEDDED_BITMAPS ) && \
28    defined( FT_CONFIG_OPTION_USE_PNG )
29
30  /* We always include <setjmp.h>, so make libpng shut up! */
31#define PNG_SKIP_SETJMP_CHECK 1
32#include <png.h>
33#include "pngshim.h"
34
35#include "sferrors.h"
36
37
38  /* This code is freely based on cairo-png.c.  There's so many ways */
39  /* to call libpng, and the way cairo does it is defacto standard.  */
40
41  static unsigned int
42  multiply_alpha( unsigned int  alpha,
43                  unsigned int  color )
44  {
45    unsigned int  temp = alpha * color + 0x80;
46
47
48    return ( temp + ( temp >> 8 ) ) >> 8;
49  }
50
51
52  /* Premultiplies data and converts RGBA bytes => BGRA. */
53  static void
54  premultiply_data( png_structp    png,
55                    png_row_infop  row_info,
56                    png_bytep      data )
57  {
58    unsigned int  i = 0, limit;
59
60    /* The `vector_size' attribute was introduced in gcc 3.1, which */
61    /* predates clang; the `__BYTE_ORDER__' preprocessor symbol was */
62    /* introduced in gcc 4.6 and clang 3.2, respectively.           */
63    /* `__builtin_shuffle' for gcc was introduced in gcc 4.7.0.     */
64#if ( ( defined( __GNUC__ )                                &&             \
65        ( ( __GNUC__ >= 5 )                              ||               \
66        ( ( __GNUC__ == 4 ) && ( __GNUC_MINOR__ >= 7 ) ) ) )         ||   \
67      ( defined( __clang__ )                                       &&     \
68        ( ( __clang_major__ >= 4 )                               ||       \
69        ( ( __clang_major__ == 3 ) && ( __clang_minor__ >= 2 ) ) ) ) ) && \
70    defined( __OPTIMIZE__ )                                            && \
71    __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
72
73#ifdef __clang__
74    /* the clang documentation doesn't cover the two-argument case of */
75    /* `__builtin_shufflevector'; however, it is is implemented since */
76    /* version 2.8                                                    */
77#define vector_shuffle  __builtin_shufflevector
78#else
79#define vector_shuffle  __builtin_shuffle
80#endif
81
82    typedef unsigned short  v82 __attribute__(( vector_size( 16 ) ));
83
84
85    if ( row_info->rowbytes > 15 )
86    {
87      /* process blocks of 16 bytes in one rush, which gives a nice speed-up */
88      limit = row_info->rowbytes - 16 + 1;
89      for ( ; i < limit; i += 16 )
90      {
91        unsigned char*  base = &data[i];
92
93        v82  s, s0, s1, a;
94
95        /* clang <= 3.9 can't apply scalar values to vectors */
96        /* (or rather, it needs a different syntax)          */
97        v82  n0x80 = { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 };
98        v82  n0xFF = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
99        v82  n8    = { 8, 8, 8, 8, 8, 8, 8, 8 };
100
101        v82  ma = { 1, 1, 3, 3, 5, 5, 7, 7 };
102        v82  o1 = { 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF };
103        v82  m0 = { 1, 0, 3, 2, 5, 4, 7, 6 };
104
105
106        ft_memcpy( &s, base, 16 );            /* RGBA RGBA RGBA RGBA */
107        s0 = s & n0xFF;                       /*  R B  R B  R B  R B */
108        s1 = s >> n8;                         /*  G A  G A  G A  G A */
109
110        a   = vector_shuffle( s1, ma );       /*  A A  A A  A A  A A */
111        s1 |= o1;                             /*  G 1  G 1  G 1  G 1 */
112        s0  = vector_shuffle( s0, m0 );       /*  B R  B R  B R  B R */
113
114        s0 *= a;
115        s1 *= a;
116        s0 += n0x80;
117        s1 += n0x80;
118        s0  = ( s0 + ( s0 >> n8 ) ) >> n8;
119        s1  = ( s1 + ( s1 >> n8 ) ) >> n8;
120
121        s = s0 | ( s1 << n8 );
122        ft_memcpy( base, &s, 16 );
123      }
124    }
125#endif /* use `vector_size' */
126
127    FT_UNUSED( png );
128
129    limit = row_info->rowbytes;
130    for ( ; i < limit; i += 4 )
131    {
132      unsigned char*  base  = &data[i];
133      unsigned int    alpha = base[3];
134
135
136      if ( alpha == 0 )
137        base[0] = base[1] = base[2] = base[3] = 0;
138
139      else
140      {
141        unsigned int  red   = base[0];
142        unsigned int  green = base[1];
143        unsigned int  blue  = base[2];
144
145
146        if ( alpha != 0xFF )
147        {
148          red   = multiply_alpha( alpha, red   );
149          green = multiply_alpha( alpha, green );
150          blue  = multiply_alpha( alpha, blue  );
151        }
152
153        base[0] = (unsigned char)blue;
154        base[1] = (unsigned char)green;
155        base[2] = (unsigned char)red;
156        base[3] = (unsigned char)alpha;
157      }
158    }
159  }
160
161
162  /* Converts RGBx bytes to BGRA. */
163  static void
164  convert_bytes_to_data( png_structp    png,
165                         png_row_infop  row_info,
166                         png_bytep      data )
167  {
168    unsigned int  i;
169
170    FT_UNUSED( png );
171
172
173    for ( i = 0; i < row_info->rowbytes; i += 4 )
174    {
175      unsigned char*  base  = &data[i];
176      unsigned int    red   = base[0];
177      unsigned int    green = base[1];
178      unsigned int    blue  = base[2];
179
180
181      base[0] = (unsigned char)blue;
182      base[1] = (unsigned char)green;
183      base[2] = (unsigned char)red;
184      base[3] = 0xFF;
185    }
186  }
187
188
189  /* Use error callback to avoid png writing to stderr. */
190  static void
191  error_callback( png_structp      png,
192                  png_const_charp  error_msg )
193  {
194    FT_Error*  error = (FT_Error*)png_get_error_ptr( png );
195
196    FT_UNUSED( error_msg );
197
198
199    *error = FT_THROW( Out_Of_Memory );
200#ifdef PNG_SETJMP_SUPPORTED
201    ft_longjmp( png_jmpbuf( png ), 1 );
202#endif
203    /* if we get here, then we have no choice but to abort ... */
204  }
205
206
207  /* Use warning callback to avoid png writing to stderr. */
208  static void
209  warning_callback( png_structp      png,
210                    png_const_charp  error_msg )
211  {
212    FT_UNUSED( png );
213    FT_UNUSED( error_msg );
214
215    /* Just ignore warnings. */
216  }
217
218
219  static void
220  read_data_from_FT_Stream( png_structp  png,
221                            png_bytep    data,
222                            png_size_t   length )
223  {
224    FT_Error   error;
225    png_voidp  p      = png_get_io_ptr( png );
226    FT_Stream  stream = (FT_Stream)p;
227
228
229    if ( FT_FRAME_ENTER( length ) )
230    {
231      FT_Error*  e = (FT_Error*)png_get_error_ptr( png );
232
233
234      *e = FT_THROW( Invalid_Stream_Read );
235      png_error( png, NULL );
236
237      return;
238    }
239
240    ft_memcpy( data, stream->cursor, length );
241
242    FT_FRAME_EXIT();
243  }
244
245
246  FT_LOCAL_DEF( FT_Error )
247  Load_SBit_Png( FT_GlyphSlot     slot,
248                 FT_Int           x_offset,
249                 FT_Int           y_offset,
250                 FT_Int           pix_bits,
251                 TT_SBit_Metrics  metrics,
252                 FT_Memory        memory,
253                 FT_Byte*         data,
254                 FT_UInt          png_len,
255                 FT_Bool          populate_map_and_metrics,
256                 FT_Bool          metrics_only )
257  {
258    FT_Bitmap    *map   = &slot->bitmap;
259    FT_Error      error = FT_Err_Ok;
260    FT_StreamRec  stream;
261
262    png_structp  png;
263    png_infop    info;
264    png_uint_32  imgWidth, imgHeight;
265
266    int         bitdepth, color_type, interlace;
267    FT_Int      i;
268    png_byte*  *rows = NULL; /* pacify compiler */
269
270
271    if ( x_offset < 0 ||
272         y_offset < 0 )
273    {
274      error = FT_THROW( Invalid_Argument );
275      goto Exit;
276    }
277
278    if ( !populate_map_and_metrics                            &&
279         ( (FT_UInt)x_offset + metrics->width  > map->width ||
280           (FT_UInt)y_offset + metrics->height > map->rows  ||
281           pix_bits != 32                                   ||
282           map->pixel_mode != FT_PIXEL_MODE_BGRA            ) )
283    {
284      error = FT_THROW( Invalid_Argument );
285      goto Exit;
286    }
287
288    FT_Stream_OpenMemory( &stream, data, png_len );
289
290    png = png_create_read_struct( PNG_LIBPNG_VER_STRING,
291                                  &error,
292                                  error_callback,
293                                  warning_callback );
294    if ( !png )
295    {
296      error = FT_THROW( Out_Of_Memory );
297      goto Exit;
298    }
299
300    info = png_create_info_struct( png );
301    if ( !info )
302    {
303      error = FT_THROW( Out_Of_Memory );
304      png_destroy_read_struct( &png, NULL, NULL );
305      goto Exit;
306    }
307
308    if ( ft_setjmp( png_jmpbuf( png ) ) )
309    {
310      error = FT_THROW( Invalid_File_Format );
311      goto DestroyExit;
312    }
313
314    png_set_read_fn( png, &stream, read_data_from_FT_Stream );
315
316    png_read_info( png, info );
317    png_get_IHDR( png, info,
318                  &imgWidth, &imgHeight,
319                  &bitdepth, &color_type, &interlace,
320                  NULL, NULL );
321
322    if ( error                                        ||
323         ( !populate_map_and_metrics                &&
324           ( (FT_Int)imgWidth  != metrics->width  ||
325             (FT_Int)imgHeight != metrics->height ) ) )
326      goto DestroyExit;
327
328    if ( populate_map_and_metrics )
329    {
330      metrics->width  = (FT_UShort)imgWidth;
331      metrics->height = (FT_UShort)imgHeight;
332
333      map->width      = metrics->width;
334      map->rows       = metrics->height;
335      map->pixel_mode = FT_PIXEL_MODE_BGRA;
336      map->pitch      = (int)( map->width * 4 );
337      map->num_grays  = 256;
338
339      /* reject too large bitmaps similarly to the rasterizer */
340      if ( map->rows > 0x7FFF || map->width > 0x7FFF )
341      {
342        error = FT_THROW( Array_Too_Large );
343        goto DestroyExit;
344      }
345    }
346
347    /* convert palette/gray image to rgb */
348    if ( color_type == PNG_COLOR_TYPE_PALETTE )
349      png_set_palette_to_rgb( png );
350
351    /* expand gray bit depth if needed */
352    if ( color_type == PNG_COLOR_TYPE_GRAY )
353    {
354#if PNG_LIBPNG_VER >= 10209
355      png_set_expand_gray_1_2_4_to_8( png );
356#else
357      png_set_gray_1_2_4_to_8( png );
358#endif
359    }
360
361    /* transform transparency to alpha */
362    if ( png_get_valid(png, info, PNG_INFO_tRNS ) )
363      png_set_tRNS_to_alpha( png );
364
365    if ( bitdepth == 16 )
366      png_set_strip_16( png );
367
368    if ( bitdepth < 8 )
369      png_set_packing( png );
370
371    /* convert grayscale to RGB */
372    if ( color_type == PNG_COLOR_TYPE_GRAY       ||
373         color_type == PNG_COLOR_TYPE_GRAY_ALPHA )
374      png_set_gray_to_rgb( png );
375
376    if ( interlace != PNG_INTERLACE_NONE )
377      png_set_interlace_handling( png );
378
379    png_set_filler( png, 0xFF, PNG_FILLER_AFTER );
380
381    /* recheck header after setting EXPAND options */
382    png_read_update_info(png, info );
383    png_get_IHDR( png, info,
384                  &imgWidth, &imgHeight,
385                  &bitdepth, &color_type, &interlace,
386                  NULL, NULL );
387
388    if ( bitdepth != 8                              ||
389        !( color_type == PNG_COLOR_TYPE_RGB       ||
390           color_type == PNG_COLOR_TYPE_RGB_ALPHA ) )
391    {
392      error = FT_THROW( Invalid_File_Format );
393      goto DestroyExit;
394    }
395
396    if ( metrics_only )
397      goto DestroyExit;
398
399    switch ( color_type )
400    {
401    default:
402      /* Shouldn't happen, but fall through. */
403
404    case PNG_COLOR_TYPE_RGB_ALPHA:
405      png_set_read_user_transform_fn( png, premultiply_data );
406      break;
407
408    case PNG_COLOR_TYPE_RGB:
409      /* Humm, this smells.  Carry on though. */
410      png_set_read_user_transform_fn( png, convert_bytes_to_data );
411      break;
412    }
413
414    if ( populate_map_and_metrics )
415    {
416      /* this doesn't overflow: 0x7FFF * 0x7FFF * 4 < 2^32 */
417      FT_ULong  size = map->rows * (FT_ULong)map->pitch;
418
419
420      error = ft_glyphslot_alloc_bitmap( slot, size );
421      if ( error )
422        goto DestroyExit;
423    }
424
425    if ( FT_NEW_ARRAY( rows, imgHeight ) )
426    {
427      error = FT_THROW( Out_Of_Memory );
428      goto DestroyExit;
429    }
430
431    for ( i = 0; i < (FT_Int)imgHeight; i++ )
432      rows[i] = map->buffer + ( y_offset + i ) * map->pitch + x_offset * 4;
433
434    png_read_image( png, rows );
435
436    FT_FREE( rows );
437
438    png_read_end( png, info );
439
440  DestroyExit:
441    png_destroy_read_struct( &png, &info, NULL );
442    FT_Stream_Close( &stream );
443
444  Exit:
445    return error;
446  }
447
448#else /* !(TT_CONFIG_OPTION_EMBEDDED_BITMAPS && FT_CONFIG_OPTION_USE_PNG) */
449
450  /* ANSI C doesn't like empty source files */
451  typedef int  _pngshim_dummy;
452
453#endif /* !(TT_CONFIG_OPTION_EMBEDDED_BITMAPS && FT_CONFIG_OPTION_USE_PNG) */
454
455
456/* END */
457