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