1 /***************************************************************************/ 2 /* */ 3 /* pngshim.c */ 4 /* */ 5 /* PNG Bitmap glyph support. */ 6 /* */ 7 /* Copyright 2013-2015 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 #ifdef FT_CONFIG_OPTION_USE_PNG 28 29 /* We always include <stjmp.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 => native endian. */ 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; 58 59 FT_UNUSED( png ); 60 61 62 for ( i = 0; i < row_info->rowbytes; i += 4 ) 63 { 64 unsigned char* base = &data[i]; 65 unsigned int alpha = base[3]; 66 67 68 if ( alpha == 0 ) 69 base[0] = base[1] = base[2] = base[3] = 0; 70 71 else 72 { 73 unsigned int red = base[0]; 74 unsigned int green = base[1]; 75 unsigned int blue = base[2]; 76 77 78 if ( alpha != 0xFF ) 79 { 80 red = multiply_alpha( alpha, red ); 81 green = multiply_alpha( alpha, green ); 82 blue = multiply_alpha( alpha, blue ); 83 } 84 85 base[0] = (unsigned char)blue; 86 base[1] = (unsigned char)green; 87 base[2] = (unsigned char)red; 88 base[3] = (unsigned char)alpha; 89 } 90 } 91 } 92 93 94 /* Converts RGBx bytes to BGRA. */ 95 static void convert_bytes_to_data(png_structp png,png_row_infop row_info,png_bytep data)96 convert_bytes_to_data( png_structp png, 97 png_row_infop row_info, 98 png_bytep data ) 99 { 100 unsigned int i; 101 102 FT_UNUSED( png ); 103 104 105 for ( i = 0; i < row_info->rowbytes; i += 4 ) 106 { 107 unsigned char* base = &data[i]; 108 unsigned int red = base[0]; 109 unsigned int green = base[1]; 110 unsigned int blue = base[2]; 111 112 113 base[0] = (unsigned char)blue; 114 base[1] = (unsigned char)green; 115 base[2] = (unsigned char)red; 116 base[3] = 0xFF; 117 } 118 } 119 120 121 /* Use error callback to avoid png writing to stderr. */ 122 static void error_callback(png_structp png,png_const_charp error_msg)123 error_callback( png_structp png, 124 png_const_charp error_msg ) 125 { 126 FT_Error* error = (FT_Error*)png_get_error_ptr( png ); 127 128 FT_UNUSED( error_msg ); 129 130 131 *error = FT_THROW( Out_Of_Memory ); 132 #ifdef PNG_SETJMP_SUPPORTED 133 ft_longjmp( png_jmpbuf( png ), 1 ); 134 #endif 135 /* if we get here, then we have no choice but to abort ... */ 136 } 137 138 139 /* Use warning callback to avoid png writing to stderr. */ 140 static void warning_callback(png_structp png,png_const_charp error_msg)141 warning_callback( png_structp png, 142 png_const_charp error_msg ) 143 { 144 FT_UNUSED( png ); 145 FT_UNUSED( error_msg ); 146 147 /* Just ignore warnings. */ 148 } 149 150 151 static void read_data_from_FT_Stream(png_structp png,png_bytep data,png_size_t length)152 read_data_from_FT_Stream( png_structp png, 153 png_bytep data, 154 png_size_t length ) 155 { 156 FT_Error error; 157 png_voidp p = png_get_io_ptr( png ); 158 FT_Stream stream = (FT_Stream)p; 159 160 161 if ( FT_FRAME_ENTER( length ) ) 162 { 163 FT_Error* e = (FT_Error*)png_get_error_ptr( png ); 164 165 166 *e = FT_THROW( Invalid_Stream_Read ); 167 png_error( png, NULL ); 168 169 return; 170 } 171 172 memcpy( data, stream->cursor, length ); 173 174 FT_FRAME_EXIT(); 175 } 176 177 178 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)179 Load_SBit_Png( FT_GlyphSlot slot, 180 FT_Int x_offset, 181 FT_Int y_offset, 182 FT_Int pix_bits, 183 TT_SBit_Metrics metrics, 184 FT_Memory memory, 185 FT_Byte* data, 186 FT_UInt png_len, 187 FT_Bool populate_map_and_metrics ) 188 { 189 FT_Bitmap *map = &slot->bitmap; 190 FT_Error error = FT_Err_Ok; 191 FT_StreamRec stream; 192 193 png_structp png; 194 png_infop info; 195 png_uint_32 imgWidth, imgHeight; 196 197 int bitdepth, color_type, interlace; 198 FT_Int i; 199 png_byte* *rows = NULL; /* pacify compiler */ 200 201 202 if ( x_offset < 0 || 203 y_offset < 0 ) 204 { 205 error = FT_THROW( Invalid_Argument ); 206 goto Exit; 207 } 208 209 if ( !populate_map_and_metrics && 210 ( (FT_UInt)x_offset + metrics->width > map->width || 211 (FT_UInt)y_offset + metrics->height > map->rows || 212 pix_bits != 32 || 213 map->pixel_mode != FT_PIXEL_MODE_BGRA ) ) 214 { 215 error = FT_THROW( Invalid_Argument ); 216 goto Exit; 217 } 218 219 FT_Stream_OpenMemory( &stream, data, png_len ); 220 221 png = png_create_read_struct( PNG_LIBPNG_VER_STRING, 222 &error, 223 error_callback, 224 warning_callback ); 225 if ( !png ) 226 { 227 error = FT_THROW( Out_Of_Memory ); 228 goto Exit; 229 } 230 231 info = png_create_info_struct( png ); 232 if ( !info ) 233 { 234 error = FT_THROW( Out_Of_Memory ); 235 png_destroy_read_struct( &png, NULL, NULL ); 236 goto Exit; 237 } 238 239 if ( ft_setjmp( png_jmpbuf( png ) ) ) 240 { 241 error = FT_THROW( Invalid_File_Format ); 242 goto DestroyExit; 243 } 244 245 png_set_read_fn( png, &stream, read_data_from_FT_Stream ); 246 247 png_read_info( png, info ); 248 png_get_IHDR( png, info, 249 &imgWidth, &imgHeight, 250 &bitdepth, &color_type, &interlace, 251 NULL, NULL ); 252 253 if ( error || 254 ( !populate_map_and_metrics && 255 ( (FT_Int)imgWidth != metrics->width || 256 (FT_Int)imgHeight != metrics->height ) ) ) 257 goto DestroyExit; 258 259 if ( populate_map_and_metrics ) 260 { 261 FT_ULong size; 262 263 264 metrics->width = (FT_UShort)imgWidth; 265 metrics->height = (FT_UShort)imgHeight; 266 267 map->width = metrics->width; 268 map->rows = metrics->height; 269 map->pixel_mode = FT_PIXEL_MODE_BGRA; 270 map->pitch = (int)( map->width * 4 ); 271 map->num_grays = 256; 272 273 /* reject too large bitmaps similarly to the rasterizer */ 274 if ( map->rows > 0x7FFF || map->width > 0x7FFF ) 275 { 276 error = FT_THROW( Array_Too_Large ); 277 goto DestroyExit; 278 } 279 280 /* this doesn't overflow: 0x7FFF * 0x7FFF * 4 < 2^32 */ 281 size = map->rows * (FT_ULong)map->pitch; 282 283 error = ft_glyphslot_alloc_bitmap( slot, size ); 284 if ( error ) 285 goto DestroyExit; 286 } 287 288 /* convert palette/gray image to rgb */ 289 if ( color_type == PNG_COLOR_TYPE_PALETTE ) 290 png_set_palette_to_rgb( png ); 291 292 /* expand gray bit depth if needed */ 293 if ( color_type == PNG_COLOR_TYPE_GRAY ) 294 { 295 #if PNG_LIBPNG_VER >= 10209 296 png_set_expand_gray_1_2_4_to_8( png ); 297 #else 298 png_set_gray_1_2_4_to_8( png ); 299 #endif 300 } 301 302 /* transform transparency to alpha */ 303 if ( png_get_valid(png, info, PNG_INFO_tRNS ) ) 304 png_set_tRNS_to_alpha( png ); 305 306 if ( bitdepth == 16 ) 307 png_set_strip_16( png ); 308 309 if ( bitdepth < 8 ) 310 png_set_packing( png ); 311 312 /* convert grayscale to RGB */ 313 if ( color_type == PNG_COLOR_TYPE_GRAY || 314 color_type == PNG_COLOR_TYPE_GRAY_ALPHA ) 315 png_set_gray_to_rgb( png ); 316 317 if ( interlace != PNG_INTERLACE_NONE ) 318 png_set_interlace_handling( png ); 319 320 png_set_filler( png, 0xFF, PNG_FILLER_AFTER ); 321 322 /* recheck header after setting EXPAND options */ 323 png_read_update_info(png, info ); 324 png_get_IHDR( png, info, 325 &imgWidth, &imgHeight, 326 &bitdepth, &color_type, &interlace, 327 NULL, NULL ); 328 329 if ( bitdepth != 8 || 330 !( color_type == PNG_COLOR_TYPE_RGB || 331 color_type == PNG_COLOR_TYPE_RGB_ALPHA ) ) 332 { 333 error = FT_THROW( Invalid_File_Format ); 334 goto DestroyExit; 335 } 336 337 switch ( color_type ) 338 { 339 default: 340 /* Shouldn't happen, but fall through. */ 341 342 case PNG_COLOR_TYPE_RGB_ALPHA: 343 png_set_read_user_transform_fn( png, premultiply_data ); 344 break; 345 346 case PNG_COLOR_TYPE_RGB: 347 /* Humm, this smells. Carry on though. */ 348 png_set_read_user_transform_fn( png, convert_bytes_to_data ); 349 break; 350 } 351 352 if ( FT_NEW_ARRAY( rows, imgHeight ) ) 353 { 354 error = FT_THROW( Out_Of_Memory ); 355 goto DestroyExit; 356 } 357 358 for ( i = 0; i < (FT_Int)imgHeight; i++ ) 359 rows[i] = map->buffer + ( y_offset + i ) * map->pitch + x_offset * 4; 360 361 png_read_image( png, rows ); 362 363 FT_FREE( rows ); 364 365 png_read_end( png, info ); 366 367 DestroyExit: 368 png_destroy_read_struct( &png, &info, NULL ); 369 FT_Stream_Close( &stream ); 370 371 Exit: 372 return error; 373 } 374 375 #endif /* FT_CONFIG_OPTION_USE_PNG */ 376 377 378 /* END */ 379