(root)/
freetype-2.13.2/
src/
sfnt/
pngshim.c
       1  /****************************************************************************
       2   *
       3   * pngshim.c
       4   *
       5   *   PNG Bitmap glyph support.
       6   *
       7   * Copyright (C) 2013-2023 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
      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
      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
     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
     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
     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
     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; (never reached) */
     243      }
     244  
     245      ft_memcpy( data, stream->cursor, length );
     246  
     247      FT_FRAME_EXIT();
     248    }
     249  
     250  
     251    FT_LOCAL_DEF( FT_Error )
     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:  /* Shouldn't happen, but ... */
     410      case PNG_COLOR_TYPE_RGB_ALPHA:
     411        png_set_read_user_transform_fn( png, premultiply_data );
     412        break;
     413  
     414      case PNG_COLOR_TYPE_RGB:
     415        /* Humm, this smells.  Carry on though. */
     416        png_set_read_user_transform_fn( png, convert_bytes_to_data );
     417        break;
     418      }
     419  
     420      if ( populate_map_and_metrics )
     421      {
     422        /* this doesn't overflow: 0x7FFF * 0x7FFF * 4 < 2^32 */
     423        FT_ULong  size = map->rows * (FT_ULong)map->pitch;
     424  
     425  
     426        error = ft_glyphslot_alloc_bitmap( slot, size );
     427        if ( error )
     428          goto DestroyExit;
     429      }
     430  
     431      if ( FT_QNEW_ARRAY( rows, imgHeight ) )
     432      {
     433        error = FT_THROW( Out_Of_Memory );
     434        goto DestroyExit;
     435      }
     436  
     437      for ( i = 0; i < (FT_Int)imgHeight; i++ )
     438        rows[i] = map->buffer + ( y_offset + i ) * map->pitch + x_offset * 4;
     439  
     440      png_read_image( png, rows );
     441  
     442      png_read_end( png, info );
     443  
     444    DestroyExit:
     445      /* even if reading fails with longjmp, rows must be freed */
     446      FT_FREE( rows );
     447      png_destroy_read_struct( &png, &info, NULL );
     448      FT_Stream_Close( &stream );
     449  
     450    Exit:
     451      return error;
     452    }
     453  
     454  #else /* !(TT_CONFIG_OPTION_EMBEDDED_BITMAPS && FT_CONFIG_OPTION_USE_PNG) */
     455  
     456    /* ANSI C doesn't like empty source files */
     457    typedef int  pngshim_dummy_;
     458  
     459  #endif /* !(TT_CONFIG_OPTION_EMBEDDED_BITMAPS && FT_CONFIG_OPTION_USE_PNG) */
     460  
     461  
     462  /* END */