• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
multiply_alpha(unsigned int alpha,unsigned int color)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
premultiply_data(png_structp png,png_row_infop row_info,png_bytep data)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
convert_bytes_to_data(png_structp png,png_row_infop row_info,png_bytep data)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
error_callback(png_structp png,png_const_charp error_msg)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
warning_callback(png_structp png,png_const_charp error_msg)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
read_data_from_FT_Stream(png_structp png,png_bytep data,png_size_t length)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 )
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)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