diff --git a/ChangeLog b/ChangeLog index e4f514873..108ea732a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,67 @@ +2013-05-23 Behdad Esfahbod + + Add support for color embedded bitmaps (eg. color emoji). + + A new load flag, FT_LOAD_COLOR, makes FreeType load color + embedded-bitmaps, following this draft specification + + https://color-emoji.googlecode.com/git/specification/v1.html + + which defines two new SFNT tables, `CBDT' and `CBLC' (named and + modeled after `EBDT' and `EBLC', respectively). The color bitmaps + are stored in the new FT_PIXEL_MODE_BGRA format to represent BGRA + pre-multiplied sRGB images. If PNG support is available, PNG color + images as defined in the same proposed specification are supported + also. + + Note that color bitmaps are converted to grayscale if client didn't + ask for color. + + * builds/unix/configure.raw: Search for libpng. + Add `--without-png' option. + + * devel/ftoption.h, include/freetype/config/ftoption.h + (FT_CONFIG_OPTION_USE_PNG): New macro. + + * include/freetype/freetype.h (FT_LOAD_COLOR): New load flag. + + * include/freetype/ftimage.h (FT_Pixel_Mode): Add + `FT_PIXEL_MODE_BGRA'. + + * include/freetype/tttags.h (TTAG_CBDT, TTAG_CBLC): New tags. + + * src/base/ftbitmap.c (FT_Bitmap_Embolden): Updated. + (ft_gray_for_premultiplied_srgb_bgra): New function. + (FT_Bitmap_Convert): Handle FT_PIXEL_MODE_BGRA. + + * src/sfnt/pngshim.c, src/sfnt/pngshim.h: New files. + + * src/sfnt/sfnt.c: Include `pngshim.c'. + + * src/sfnt/ttsbit.c: Include FT_BITMAP_H and `pngshim.h' + (tt_face_load_eblc): Load `CBLC'. + (tt_sbit_decoder_init): Load `CBDT'. + (tt_sbit_decoder_alloc_bitmap): Pass load flags to select between + color and grayscale bitmaps. + Set `num_grays'. This is used by `ftview' to choose the blending + algorithm. + (tt_sbit_decoder_load_byte_aligned, + tt_sbit_decoder_load_bit_aligned, tt_sbit_decoder_load_compound, + tt_sbit_decoder_load_image): Pass load flag. + s/write/pwrite/. + Don't call `tt_sbit_decoder_alloc_bitmap'. + Updated. + (tt_sbit_decoder_load_png) [FT_CONFIG_OPTION_USE_PNG]: New function. + (tt_sbit_decoder_load_bitmap): Pass load flag. + Handle new glyph formats 17, 18, and 19. + Call `tt_sbit_decoder_alloc_bitmap'. + Flatten color bitmaps if necessary. + (tt_face_load_sbit_image): Updated. + + * src/sfnt/rules.mk (SFNT_DRV_SRC): Add `pngshim.c'. + + * docs/CHANGES: Updated. + 2013-05-24 Guenter Apply Savannah patch #8055. diff --git a/builds/unix/configure.raw b/builds/unix/configure.raw index b691ab36c..3d6e901d7 100644 --- a/builds/unix/configure.raw +++ b/builds/unix/configure.raw @@ -254,12 +254,15 @@ AC_ARG_WITH([zlib], AS_HELP_STRING([--without-zlib], [use internal zlib instead of system-wide])) if test x$with_zlib != xno && test -z "$LIBZ"; then - AC_CHECK_LIB([z], [gzsetparams], [AC_CHECK_HEADER([zlib.h], [LIBZ='-lz'])]) + AC_CHECK_LIB([z], + [gzsetparams], + [AC_CHECK_HEADER([zlib.h], [LIBZ='-lz'])]) fi if test x$with_zlib != xno && test -n "$LIBZ"; then SYSTEM_ZLIB=yes fi + # check for system libbz2 # don't quote AS_HELP_STRING! @@ -267,12 +270,37 @@ AC_ARG_WITH([bzip2], AS_HELP_STRING([--without-bzip2], [do not support bzip2 compressed fonts])) if test x$with_bzip2 != xno && test -z "$LIBBZ2"; then - AC_CHECK_LIB([bz2], [BZ2_bzDecompress], [AC_CHECK_HEADER([bzlib.h], [LIBBZ2='-lbz2'])]) + AC_CHECK_LIB([bz2], + [BZ2_bzDecompress], + [AC_CHECK_HEADER([bzlib.h], [LIBBZ2='-lbz2'])]) fi if test x$with_bzip2 != xno && test -n "$LIBBZ2"; then SYSTEM_LIBBZ2=yes fi + +# check for system libpng + +HAVE_LIBPNG=no +AC_ARG_WITH([png], + AS_HELP_STRING([--without-png], + [do not support png compressed OpenType embedded bitmaps])) +if test x$with_png != xno; then + AC_MSG_CHECKING([for libpng]) + if test -z "$LIBPNG_CFLAGS" -a -z "$LIBPNG_LIBS"; then + if ! which libpng-config >/dev/null; then + AC_MSG_ERROR([`libpng-config' not found; +either set the LIBPNG_CFLAGS and LIBPNG_LIBS environment variables, +or pass `--without-png' to the `configure' script.]) + fi + LIBPNG_CFLAGS="`libpng-config --cflags`" + LIBPNG_LIBS="`libpng-config --libs`" + fi + HAVE_LIBPNG=yes + AC_MSG_RESULT([$LIBPNG_LIBS]) +fi + + # Some options handling SDKs/archs in CFLAGS should be copied # to LDFLAGS. Apple TechNote 2137 recommends to include these # options in CFLAGS but not in LDFLAGS. @@ -709,6 +737,10 @@ if test x$SYSTEM_LIBBZ2 = xyes; then CFLAGS="$CFLAGS -DFT_CONFIG_OPTION_USE_BZIP2" LDFLAGS="$LDFLAGS $LIBBZ2" fi +if test x$HAVE_LIBPNG = xyes; then + CFLAGS="$CFLAGS $LIBPNG_CFLAGS -DFT_CONFIG_OPTION_USE_PNG" + LDFLAGS="$LDFLAGS $LIBPNG_LIBS" +fi AC_SUBST([CFLAGS]) AC_SUBST([LDFLAGS]) diff --git a/devel/ftoption.h b/devel/ftoption.h index 2b370e508..069601805 100644 --- a/devel/ftoption.h +++ b/devel/ftoption.h @@ -203,6 +203,20 @@ FT_BEGIN_HEADER #define FT_CONFIG_OPTION_USE_BZIP2 + /*************************************************************************/ + /* */ + /* PNG bitmap support. */ + /* */ + /* FreeType now handles loading color bitmap glyphs in the PNG format. */ + /* This requires help from the external libpng library. Uncompressed */ + /* color bitmaps do not need any external libraries and will be */ + /* supported regardless of this configuration. */ + /* */ + /* Define this macro if you want to enable this `feature'. */ + /* */ +#define FT_CONFIG_OPTION_USE_PNG + + /*************************************************************************/ /* */ /* Define to disable the use of file stream functions and types, FILE, */ diff --git a/docs/CHANGES b/docs/CHANGES index 2dff54279..263679643 100644 --- a/docs/CHANGES +++ b/docs/CHANGES @@ -3,6 +3,24 @@ CHANGES BETWEEN 2.4.12 and 2.5 I. IMPORTANT CHANGES + - Behdad Esfahbod (on behalf of Google) contributed support for + color embedded bitmaps (eg. color emoji). + + A new load flag, FT_LOAD_COLOR, makes FreeType load color + embedded-bitmaps, following this draft specification + + https://color-emoji.googlecode.com/git/specification/v1.html + + which defines two new SFNT tables, `CBDT' and `CBLC' (named and + modeled after `EBDT' and `EBLC', respectively). The color + bitmaps are stored in the new FT_PIXEL_MODE_BGRA format to + represent BGRA pre-multiplied sRGB images. If PNG support is + available, PNG color images as defined in the same proposed + specification are supported also. + + Note that color bitmaps are converted to grayscale if client + didn't ask for color. + - As announced in the previous release, all code related to macro FT_CONFIG_OPTION_OLD_INTERNALS has been removed, thus becoming obsolete. diff --git a/include/freetype/config/ftoption.h b/include/freetype/config/ftoption.h index 84f1695cd..d2254bb52 100644 --- a/include/freetype/config/ftoption.h +++ b/include/freetype/config/ftoption.h @@ -214,6 +214,20 @@ FT_BEGIN_HEADER /* #define FT_CONFIG_OPTION_DISABLE_STREAM_SUPPORT */ + /*************************************************************************/ + /* */ + /* PNG bitmap support. */ + /* */ + /* FreeType now handles loading color bitmap glyphs in the PNG format. */ + /* This requires help from the external libpng library. Uncompressed */ + /* color bitmaps do not need any external libraries and will be */ + /* supported regardless of this configuration. */ + /* */ + /* Define this macro if you want to enable this `feature'. */ + /* */ +/* #define FT_CONFIG_OPTION_USE_PNG */ + + /*************************************************************************/ /* */ /* DLL export compilation */ diff --git a/include/freetype/freetype.h b/include/freetype/freetype.h index 57a03a79d..0d5680bdb 100644 --- a/include/freetype/freetype.h +++ b/include/freetype/freetype.h @@ -2523,6 +2523,14 @@ FT_BEGIN_HEADER * FT_LOAD_NO_AUTOHINT :: * Disable auto-hinter. See also the note below. * + * FT_LOAD_COLOR :: + * This flag is used to request loading of color embedded-bitmap + * images. The resulting color bitmaps, if available, will have the + * @FT_PIXEL_MODE_BGRA format. When the flag is not used and color + * bitmaps are found, they will be converted to 256-level gray + * bitmaps transparently. Those bitmaps will be in the + * @FT_PIXEL_MODE_GRAY format. + * * @note: * By default, hinting is enabled and the font's native hinter (see * @FT_FACE_FLAG_HINTER) is preferred over the auto-hinter. You can @@ -2560,6 +2568,8 @@ FT_BEGIN_HEADER #define FT_LOAD_MONOCHROME ( 1L << 12 ) #define FT_LOAD_LINEAR_DESIGN ( 1L << 13 ) #define FT_LOAD_NO_AUTOHINT ( 1L << 15 ) + /* Bits 16..19 are used by `FT_LOAD_TARGET_' */ +#define FT_LOAD_COLOR ( 1L << 20 ) /* */ diff --git a/include/freetype/ftbitmap.h b/include/freetype/ftbitmap.h index 92742369b..7dbf5ba3f 100644 --- a/include/freetype/ftbitmap.h +++ b/include/freetype/ftbitmap.h @@ -4,7 +4,7 @@ /* */ /* FreeType utility functions for bitmaps (specification). */ /* */ -/* Copyright 2004, 2005, 2006, 2008 by */ +/* Copyright 2004-2006, 2008, 2013 by */ /* David Turner, Robert Wilhelm, and Werner Lemberg. */ /* */ /* This file is part of the FreeType project, and may only be used, */ @@ -135,9 +135,9 @@ FT_BEGIN_HEADER /* FT_Bitmap_Convert */ /* */ /* */ - /* Convert a bitmap object with depth 1bpp, 2bpp, 4bpp, or 8bpp to a */ - /* bitmap object with depth 8bpp, making the number of used bytes per */ - /* line (a.k.a. the `pitch') a multiple of `alignment'. */ + /* Convert a bitmap object with depth 1bpp, 2bpp, 4bpp, 8bpp or 32bpp */ + /* to a bitmap object with depth 8bpp, making the number of used */ + /* bytes line (a.k.a. the `pitch') a multiple of `alignment'. */ /* */ /* */ /* library :: A handle to a library object. */ diff --git a/include/freetype/ftimage.h b/include/freetype/ftimage.h index 783430332..3b826b1d3 100644 --- a/include/freetype/ftimage.h +++ b/include/freetype/ftimage.h @@ -5,8 +5,7 @@ /* FreeType glyph image formats and default raster interface */ /* (specification). */ /* */ -/* Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, */ -/* 2010 by */ +/* Copyright 1996-2010, 2013 by */ /* David Turner, Robert Wilhelm, and Werner Lemberg. */ /* */ /* This file is part of the FreeType project, and may only be used, */ @@ -169,6 +168,15 @@ FT_BEGIN_HEADER /* times taller than the original glyph image. See also */ /* @FT_RENDER_MODE_LCD_V. */ /* */ + /* FT_PIXEL_MODE_BGRA :: */ + /* An image with four 8-bit channels per pixel, representing a */ + /* color image (such as emoticons) with alpha channel. For each */ + /* pixel, the format is BGRA, which means, the blue channel comes */ + /* first in memory. The color channels are pre-multiplied and in */ + /* the sRGB colorspace. For example, full red at half-translucent */ + /* opacity will be represented as `00,00,80,80', not `00,00,FF,80'. */ + /* See also @FT_LOAD_COLOR. */ + /* */ typedef enum FT_Pixel_Mode_ { FT_PIXEL_MODE_NONE = 0, @@ -178,6 +186,7 @@ FT_BEGIN_HEADER FT_PIXEL_MODE_GRAY4, FT_PIXEL_MODE_LCD, FT_PIXEL_MODE_LCD_V, + FT_PIXEL_MODE_BGRA, FT_PIXEL_MODE_MAX /* do not remove */ diff --git a/include/freetype/tttags.h b/include/freetype/tttags.h index 307ce4b63..be8c524ed 100644 --- a/include/freetype/tttags.h +++ b/include/freetype/tttags.h @@ -4,7 +4,7 @@ /* */ /* Tags for TrueType and OpenType tables (specification only). */ /* */ -/* Copyright 1996-2001, 2004, 2005, 2007, 2008 by */ +/* Copyright 1996-2001, 2004, 2005, 2007, 2008, 2013 by */ /* David Turner, Robert Wilhelm, and Werner Lemberg. */ /* */ /* This file is part of the FreeType project, and may only be used, */ @@ -40,6 +40,8 @@ FT_BEGIN_HEADER #define TTAG_bhed FT_MAKE_TAG( 'b', 'h', 'e', 'd' ) #define TTAG_bloc FT_MAKE_TAG( 'b', 'l', 'o', 'c' ) #define TTAG_bsln FT_MAKE_TAG( 'b', 's', 'l', 'n' ) +#define TTAG_CBDT FT_MAKE_TAG( 'C', 'B', 'D', 'T' ) +#define TTAG_CBLC FT_MAKE_TAG( 'C', 'B', 'L', 'C' ) #define TTAG_CFF FT_MAKE_TAG( 'C', 'F', 'F', ' ' ) #define TTAG_CID FT_MAKE_TAG( 'C', 'I', 'D', ' ' ) #define TTAG_cmap FT_MAKE_TAG( 'c', 'm', 'a', 'p' ) diff --git a/src/base/ftbitmap.c b/src/base/ftbitmap.c index bd25cbea6..a8602bd0a 100644 --- a/src/base/ftbitmap.c +++ b/src/base/ftbitmap.c @@ -279,6 +279,10 @@ case FT_PIXEL_MODE_LCD_V: ystr *= 3; break; + + case FT_PIXEL_MODE_BGRA: + /* We don't embolden color glyphs. */ + return FT_Err_Ok; } error = ft_bitmap_assure_buffer( library->memory, bitmap, xstr, ystr ); @@ -371,6 +375,59 @@ } + FT_Byte + ft_gray_for_premultiplied_srgb_bgra( const FT_Byte* bgra ) + { + FT_Long a = bgra[3]; + FT_Long b = bgra[0]; + FT_Long g = bgra[1]; + FT_Long r = bgra[2]; + FT_Long l; + + + /* + * Luminosity for sRGB is defined using ~0.2126,0.7152,0.0722 + * coefficients for RGB channels *on the linear colors*. + * A gamma of 2.2 is fair to assume. And then, we need to + * undo the premultiplication too. + * + * http://accessibility.kde.org/hsl-adjusted.php + * + * We do the computation with integers only. + */ + + /* Undo premultification, get the number in a 16.16 form. */ + b = FT_MulDiv( b, 65536, a ); + g = FT_MulDiv( g, 65536, a ); + r = FT_MulDiv( r, 65536, a ); + a = a * 256; + + /* Apply gamma of 2.0 instead of 2.2. */ + b = FT_MulFix( b, b ); + g = FT_MulFix( g, g ); + r = FT_MulFix( r, r ); + + /* Apply coefficients. */ + b = FT_MulFix( b, 4731 /* 0.0722 * 65536 */ ); + g = FT_MulFix( g, 46871 /* 0.7152 * 65536 */ ); + r = FT_MulFix( r, 13933 /* 0.2126 * 65536 */ ); + + l = r + g + b; + + /* + * Final transparency can be determined this way: + * + * - If alpha is zero, we want 0. + * - If alpha is zero and luminosity is zero, we want 255. + * - If alpha is zero and luminosity is one, we want 0. + * + * So the formula is a * (1 - l). + */ + + return FT_MulFix( 65535 - l, a ) >> 8; + } + + /* documentation is in ftbitmap.h */ FT_EXPORT_DEF( FT_Error ) @@ -396,6 +453,7 @@ case FT_PIXEL_MODE_GRAY4: case FT_PIXEL_MODE_LCD: case FT_PIXEL_MODE_LCD_V: + case FT_PIXEL_MODE_BGRA: { FT_Int pad; FT_Long old_size; @@ -608,6 +666,37 @@ } break; + case FT_PIXEL_MODE_BGRA: + { + FT_Byte* s = source->buffer; + FT_Byte* t = target->buffer; + FT_Int s_pitch = source->pitch; + FT_Int t_pitch = target->pitch; + FT_Int i; + + + target->num_grays = 256; + + for ( i = source->rows; i > 0; i-- ) + { + FT_Byte* ss = s; + FT_Byte* tt = t; + FT_Int j; + + + for ( j = source->width; j > 0; j-- ) + { + tt[0] = ft_gray_for_premultiplied_srgb_bgra( ss ); + + ss += 4; + tt += 1; + } + + s += s_pitch; + t += t_pitch; + } + } + break; default: ; diff --git a/src/sfnt/pngshim.c b/src/sfnt/pngshim.c new file mode 100644 index 000000000..408f879c3 --- /dev/null +++ b/src/sfnt/pngshim.c @@ -0,0 +1,336 @@ +/***************************************************************************/ +/* */ +/* pngshim.c */ +/* */ +/* PNG Bitmap glyph support. */ +/* */ +/* Copyright 2013 by Google, Inc. */ +/* Written by Stuart Gill and Behdad Esfahbod. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_STREAM_H +#include FT_TRUETYPE_TAGS_H +#include FT_CONFIG_STANDARD_LIBRARY_H + + +#ifdef FT_CONFIG_OPTION_USE_PNG + + /* We always include , so make libpng shut up! */ +#define PNG_SKIP_SETJMP_CHECK 1 +#include +#include "pngshim.h" + +#include "sferrors.h" + + + /* This code is freely based on cairo-png.c. There's so many ways */ + /* to call libpng, and the way cairo does it is defacto standard. */ + + static int + multiply_alpha( int alpha, + int color ) + { + int temp = ( alpha * color ) + 0x80; + + + return ( temp + ( temp >> 8 ) ) >> 8; + } + + + /* Premultiplies data and converts RGBA bytes => native endian. */ + static void + premultiply_data( png_structp png, + png_row_infop row_info, + png_bytep data ) + { + unsigned int i; + + FT_UNUSED( png ); + + + for ( i = 0; i < row_info->rowbytes; i += 4 ) + { + unsigned char* base = &data[i]; + unsigned int alpha = base[3]; + + + if ( alpha == 0 ) + base[0] = base[1] = base[2] = base[3] = 0; + + else + { + unsigned int red = base[0]; + unsigned int green = base[1]; + unsigned int blue = base[2]; + + + if ( alpha != 0xFF ) + { + red = multiply_alpha( alpha, red ); + green = multiply_alpha( alpha, green ); + blue = multiply_alpha( alpha, blue ); + } + + base[0] = blue; + base[1] = green; + base[2] = red; + base[3] = alpha; + } + } + } + + + /* Converts RGBx bytes to BGRA. */ + static void + convert_bytes_to_data( png_structp png, + png_row_infop row_info, + png_bytep data ) + { + unsigned int i; + + FT_UNUSED( png ); + + + for ( i = 0; i < row_info->rowbytes; i += 4 ) + { + unsigned char* base = &data[i]; + unsigned int red = base[0]; + unsigned int green = base[1]; + unsigned int blue = base[2]; + + + base[0] = blue; + base[1] = green; + base[2] = red; + base[3] = 0xFF; + } + } + + + /* Use error callback to avoid png writing to stderr. */ + static void + error_callback( png_structp png, + png_const_charp error_msg ) + { + FT_Error* error = png_get_error_ptr( png ); + + FT_UNUSED( error_msg ); + + + *error = FT_THROW( Out_Of_Memory ); +#ifdef PNG_SETJMP_SUPPORTED + longjmp( png_jmpbuf( png ), 1 ); +#endif + /* if we get here, then we have no choice but to abort ... */ + } + + + /* Use warning callback to avoid png writing to stderr. */ + static void + warning_callback( png_structp png, + png_const_charp error_msg ) + { + FT_UNUSED( png ); + FT_UNUSED( error_msg ); + + /* Just ignore warnings. */ + } + + + static void + read_data_from_FT_Stream( png_structp png, + png_bytep data, + png_size_t length ) + { + FT_Error error; + png_voidp p = png_get_io_ptr( png ); + FT_Stream stream = (FT_Stream)p; + + + if ( FT_FRAME_ENTER( length ) ) + { + FT_Error* e = png_get_error_ptr( png ); + + + *e = FT_THROW( Invalid_Stream_Read ); + png_error( png, NULL ); + + return; + } + + memcpy( data, stream->cursor, length ); + + FT_FRAME_EXIT(); + } + + + static FT_Error + Load_SBit_Png( FT_Bitmap* map, + 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_Error error = FT_Err_Ok; + FT_StreamRec stream; + + png_structp png; + png_infop info; + png_uint_32 imgWidth, imgHeight; + + int bitdepth, color_type, interlace; + FT_Int i; + png_byte* *rows; + + + if ( x_offset < 0 || x_offset + metrics->width > map->width || + y_offset < 0 || y_offset + metrics->height > map->rows || + pix_bits != 32 || map->pixel_mode != FT_PIXEL_MODE_BGRA ) + { + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + + FT_Stream_OpenMemory( &stream, data, png_len ); + + png = png_create_read_struct( PNG_LIBPNG_VER_STRING, + &error, + error_callback, + warning_callback ); + if ( !png ) + { + error = FT_THROW( Out_Of_Memory ); + goto Exit; + } + + info = png_create_info_struct( png ); + if ( !info ) + { + error = FT_THROW( Out_Of_Memory ); + png_destroy_read_struct( &png, NULL, NULL ); + goto Exit; + } + + if ( ft_setjmp( png_jmpbuf( png ) ) ) + { + error = FT_THROW( Invalid_File_Format ); + goto DestroyExit; + } + + png_set_read_fn( png, &stream, read_data_from_FT_Stream ); + + png_read_info( png, info ); + png_get_IHDR( png, info, + &imgWidth, &imgHeight, + &bitdepth, &color_type, &interlace, + NULL, NULL ); + + if ( error != FT_Err_Ok || + (FT_Int)imgWidth != metrics->width || + (FT_Int)imgHeight != metrics->height ) + goto DestroyExit; + + /* convert palette/gray image to rgb */ + if ( color_type == PNG_COLOR_TYPE_PALETTE ) + png_set_palette_to_rgb( png ); + + /* expand gray bit depth if needed */ + if ( color_type == PNG_COLOR_TYPE_GRAY ) + { +#if PNG_LIBPNG_VER >= 10209 + png_set_expand_gray_1_2_4_to_8( png ); +#else + png_set_gray_1_2_4_to_8( png ); +#endif + } + + /* transform transparency to alpha */ + if ( png_get_valid(png, info, PNG_INFO_tRNS ) ) + png_set_tRNS_to_alpha( png ); + + if ( bitdepth == 16 ) + png_set_strip_16( png ); + + if ( bitdepth < 8 ) + png_set_packing( png ); + + /* convert grayscale to RGB */ + if ( color_type == PNG_COLOR_TYPE_GRAY || + color_type == PNG_COLOR_TYPE_GRAY_ALPHA ) + png_set_gray_to_rgb( png ); + + if ( interlace != PNG_INTERLACE_NONE ) + png_set_interlace_handling( png ); + + png_set_filler( png, 0xFF, PNG_FILLER_AFTER ); + + /* recheck header after setting EXPAND options */ + png_read_update_info(png, info ); + png_get_IHDR( png, info, + &imgWidth, &imgHeight, + &bitdepth, &color_type, &interlace, + NULL, NULL ); + + if ( bitdepth != 8 || + !( color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_RGB_ALPHA ) ) + { + error = FT_THROW( Invalid_File_Format ); + goto DestroyExit; + } + + switch ( color_type ) + { + default: + /* Shouldn't happen, but fall through. */ + + case PNG_COLOR_TYPE_RGB_ALPHA: + png_set_read_user_transform_fn( png, premultiply_data ); + break; + + case PNG_COLOR_TYPE_RGB: + /* Humm, this smells. Carry on though. */ + png_set_read_user_transform_fn( png, convert_bytes_to_data ); + break; + } + + if ( FT_NEW_ARRAY( rows, imgHeight ) ) + { + error = FT_THROW( Out_Of_Memory ); + goto DestroyExit; + } + + for ( i = 0; i < (FT_Int)imgHeight; i++ ) + rows[i] = map->buffer + ( y_offset + i ) * map->pitch + x_offset * 4; + + png_read_image( png, rows ); + + FT_FREE( rows ); + + png_read_end( png, info ); + + DestroyExit: + png_destroy_read_struct( &png, &info, NULL ); + FT_Stream_Close( &stream ); + + Exit: + return error; + } + +#endif /* FT_CONFIG_OPTION_USE_PNG */ + + +/* END */ diff --git a/src/sfnt/pngshim.h b/src/sfnt/pngshim.h new file mode 100644 index 000000000..8a2e69ccf --- /dev/null +++ b/src/sfnt/pngshim.h @@ -0,0 +1,48 @@ +/***************************************************************************/ +/* */ +/* pngshim.h */ +/* */ +/* PNG Bitmap glyph support. */ +/* */ +/* Copyright 2013 by Google, Inc. */ +/* Written by Stuart Gill and Behdad Esfahbod. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef __PNGSHIM_H__ +#define __PNGSHIM_H__ + + +#include +#include "ttload.h" + + +FT_BEGIN_HEADER + +#ifdef FT_CONFIG_OPTION_USE_PNG + + FT_LOCAL( FT_Error ) + Load_SBit_Png( FT_Bitmap* map, + 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 ); + +#endif + +FT_END_HEADER + +#endif /* __PNGSHIM_H__ */ + + +/* END */ diff --git a/src/sfnt/rules.mk b/src/sfnt/rules.mk index 65dd1479c..a6c956ab6 100644 --- a/src/sfnt/rules.mk +++ b/src/sfnt/rules.mk @@ -34,7 +34,8 @@ SFNT_DRV_SRC := $(SFNT_DIR)/ttload.c \ $(SFNT_DIR)/ttbdf.c \ $(SFNT_DIR)/sfobjs.c \ $(SFNT_DIR)/sfdriver.c \ - $(SFNT_DIR)/sfntpic.c + $(SFNT_DIR)/sfntpic.c \ + $(SFNT_DIR)/pngshim.c # SFNT driver headers # diff --git a/src/sfnt/sfnt.c b/src/sfnt/sfnt.c index fc507b496..d62ed4e0b 100644 --- a/src/sfnt/sfnt.c +++ b/src/sfnt/sfnt.c @@ -4,7 +4,7 @@ /* */ /* Single object library component. */ /* */ -/* Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006 by */ +/* Copyright 1996-2006, 2013 by */ /* David Turner, Robert Wilhelm, and Werner Lemberg. */ /* */ /* This file is part of the FreeType project, and may only be used, */ @@ -28,6 +28,7 @@ #include "sfdriver.c" #ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS +#include "pngshim.c" #include "ttsbit.c" #endif diff --git a/src/sfnt/ttsbit.c b/src/sfnt/ttsbit.c index 05d7be920..7c21c2e78 100644 --- a/src/sfnt/ttsbit.c +++ b/src/sfnt/ttsbit.c @@ -7,6 +7,9 @@ /* Copyright 2005-2009, 2013 by */ /* David Turner, Robert Wilhelm, and Werner Lemberg. */ /* */ +/* Copyright 2013 by Google, Inc. */ +/* Google Author(s): Behdad Esfahbod. */ +/* */ /* This file is part of the FreeType project, and may only be used, */ /* modified, and distributed under the terms of the FreeType project */ /* license, LICENSE.TXT. By continuing to use, modify, or distribute */ @@ -20,10 +23,13 @@ #include FT_INTERNAL_DEBUG_H #include FT_INTERNAL_STREAM_H #include FT_TRUETYPE_TAGS_H +#include FT_BITMAP_H #include "ttsbit.h" #include "sferrors.h" +#include "pngshim.h" + /*************************************************************************/ /* */ @@ -50,7 +56,9 @@ face->sbit_num_strikes = 0; /* this table is optional */ - error = face->goto_table( face, TTAG_EBLC, stream, &table_size ); + error = face->goto_table( face, TTAG_CBLC, stream, &table_size ); + if ( error ) + error = face->goto_table( face, TTAG_EBLC, stream, &table_size ); if ( error ) error = face->goto_table( face, TTAG_bloc, stream, &table_size ); if ( error ) @@ -185,7 +193,9 @@ FT_ULong ebdt_size; - error = face->goto_table( face, TTAG_EBDT, stream, &ebdt_size ); + error = face->goto_table( face, TTAG_CBDT, stream, &ebdt_size ); + if ( error ) + error = face->goto_table( face, TTAG_EBDT, stream, &ebdt_size ); if ( error ) error = face->goto_table( face, TTAG_bdat, stream, &ebdt_size ); if ( error ) @@ -243,7 +253,8 @@ static FT_Error - tt_sbit_decoder_alloc_bitmap( TT_SBitDecoder decoder ) + tt_sbit_decoder_alloc_bitmap( TT_SBitDecoder decoder, + FT_UInt load_flags ) { FT_Error error = FT_Err_Ok; FT_UInt width, height; @@ -268,21 +279,40 @@ case 1: map->pixel_mode = FT_PIXEL_MODE_MONO; map->pitch = ( map->width + 7 ) >> 3; + map->num_grays = 2; break; case 2: map->pixel_mode = FT_PIXEL_MODE_GRAY2; map->pitch = ( map->width + 3 ) >> 2; + map->num_grays = 4; break; case 4: map->pixel_mode = FT_PIXEL_MODE_GRAY4; map->pitch = ( map->width + 1 ) >> 1; + map->num_grays = 16; break; case 8: map->pixel_mode = FT_PIXEL_MODE_GRAY; map->pitch = map->width; + map->num_grays = 256; + break; + + case 32: + if ( load_flags & FT_LOAD_COLOR ) + { + map->pixel_mode = FT_PIXEL_MODE_BGRA; + map->pitch = map->width * 4; + map->num_grays = 256; + } + else + { + map->pixel_mode = FT_PIXEL_MODE_GRAY; + map->pitch = map->width; + map->num_grays = 256; + } break; default: @@ -351,11 +381,13 @@ /* forward declaration */ static FT_Error tt_sbit_decoder_load_image( TT_SBitDecoder decoder, + FT_UInt load_flags, FT_UInt glyph_index, FT_Int x_pos, FT_Int y_pos ); typedef FT_Error (*TT_SBitDecoder_LoadFunc)( TT_SBitDecoder decoder, + FT_UInt load_flags, FT_Byte* p, FT_Byte* plimit, FT_Int x_pos, @@ -364,6 +396,7 @@ static FT_Error tt_sbit_decoder_load_byte_aligned( TT_SBitDecoder decoder, + FT_UInt load_flags, FT_Byte* p, FT_Byte* limit, FT_Int x_pos, @@ -374,13 +407,8 @@ FT_Int bit_height, bit_width, pitch, width, height, line_bits, h; FT_Bitmap* bitmap; + FT_UNUSED( load_flags ); - if ( !decoder->bitmap_allocated ) - { - error = tt_sbit_decoder_alloc_bitmap( decoder ); - if ( error ) - goto Exit; - } /* check that we can write the glyph into the bitmap */ bitmap = decoder->bitmap; @@ -415,35 +443,35 @@ { for ( h = height; h > 0; h--, line += pitch ) { - FT_Byte* write = line; + FT_Byte* pwrite = line; FT_Int w; for ( w = line_bits; w >= 8; w -= 8 ) { - write[0] = (FT_Byte)( write[0] | *p++ ); - write += 1; + pwrite[0] = (FT_Byte)( pwrite[0] | *p++ ); + pwrite += 1; } if ( w > 0 ) - write[0] = (FT_Byte)( write[0] | ( *p++ & ( 0xFF00U >> w ) ) ); + pwrite[0] = (FT_Byte)( pwrite[0] | ( *p++ & ( 0xFF00U >> w ) ) ); } } else /* x_pos > 0 */ { for ( h = height; h > 0; h--, line += pitch ) { - FT_Byte* write = line; + FT_Byte* pwrite = line; FT_Int w; FT_UInt wval = 0; for ( w = line_bits; w >= 8; w -= 8 ) { - wval = (FT_UInt)( wval | *p++ ); - write[0] = (FT_Byte)( write[0] | ( wval >> x_pos ) ); - write += 1; - wval <<= 8; + wval = (FT_UInt)( wval | *p++ ); + pwrite[0] = (FT_Byte)( pwrite[0] | ( wval >> x_pos ) ); + pwrite += 1; + wval <<= 8; } if ( w > 0 ) @@ -451,13 +479,13 @@ /* all bits read and there are `x_pos + w' bits to be written */ - write[0] = (FT_Byte)( write[0] | ( wval >> x_pos ) ); + pwrite[0] = (FT_Byte)( pwrite[0] | ( wval >> x_pos ) ); if ( x_pos + w > 8 ) { - write++; - wval <<= 8; - write[0] = (FT_Byte)( write[0] | ( wval >> x_pos ) ); + pwrite++; + wval <<= 8; + pwrite[0] = (FT_Byte)( pwrite[0] | ( wval >> x_pos ) ); } } } @@ -469,7 +497,7 @@ /* * Load a bit-aligned bitmap (with pointer `p') into a line-aligned bitmap - * (with pointer `write'). In the example below, the width is 3 pixel, + * (with pointer `pwrite'). In the example below, the width is 3 pixel, * and `x_pos' is 1 pixel. * * p p+1 @@ -484,26 +512,27 @@ * | | . * | 7 6 5 4 3 2 1 0 | . * | | . - * write . . + * pwrite . . * . . * v . * +-------+ . * | | * | 7 6 5 4 3 2 1 0 | * | | - * write+1 . + * pwrite+1 . * . * v * +-------+ * | | * | 7 6 5 4 3 2 1 0 | * | | - * write+2 + * pwrite+2 * */ static FT_Error tt_sbit_decoder_load_bit_aligned( TT_SBitDecoder decoder, + FT_UInt load_flags, FT_Byte* p, FT_Byte* limit, FT_Int x_pos, @@ -515,13 +544,8 @@ FT_Bitmap* bitmap; FT_UShort rval; + FT_UNUSED( load_flags ); - if ( !decoder->bitmap_allocated ) - { - error = tt_sbit_decoder_alloc_bitmap( decoder ); - if ( error ) - goto Exit; - } /* check that we can write the glyph into the bitmap */ bitmap = decoder->bitmap; @@ -560,8 +584,8 @@ for ( h = height; h > 0; h--, line += pitch ) { - FT_Byte* write = line; - FT_Int w = line_bits; + FT_Byte* pwrite = line; + FT_Int w = line_bits; /* handle initial byte (in target bitmap) specially if necessary */ @@ -586,9 +610,9 @@ nbits -= w; } - *write++ |= ( ( rval >> nbits ) & 0xFF ) & - ( ~( 0xFF << w ) << ( 8 - w - x_pos ) ); - rval <<= 8; + *pwrite++ |= ( ( rval >> nbits ) & 0xFF ) & + ( ~( 0xFF << w ) << ( 8 - w - x_pos ) ); + rval <<= 8; w = line_bits - w; } @@ -596,8 +620,8 @@ /* handle medial bytes */ for ( ; w >= 8; w -= 8 ) { - rval |= *p++; - *write++ |= ( rval >> nbits ) & 0xFF; + rval |= *p++; + *pwrite++ |= ( rval >> nbits ) & 0xFF; rval <<= 8; } @@ -609,15 +633,15 @@ { if ( p < limit ) rval |= *p++; - *write |= ( ( rval >> nbits ) & 0xFF ) & ( 0xFF00U >> w ); - nbits += 8 - w; + *pwrite |= ( ( rval >> nbits ) & 0xFF ) & ( 0xFF00U >> w ); + nbits += 8 - w; rval <<= 8; } else { - *write |= ( ( rval >> nbits ) & 0xFF ) & ( 0xFF00U >> w ); - nbits -= w; + *pwrite |= ( ( rval >> nbits ) & 0xFF ) & ( 0xFF00U >> w ); + nbits -= w; } } } @@ -629,6 +653,7 @@ static FT_Error tt_sbit_decoder_load_compound( TT_SBitDecoder decoder, + FT_UInt load_flags, FT_Byte* p, FT_Byte* limit, FT_Int x_pos, @@ -652,13 +677,6 @@ if ( p + 4 * num_components > limit ) goto Fail; - if ( !decoder->bitmap_allocated ) - { - error = tt_sbit_decoder_alloc_bitmap( decoder ); - if ( error ) - goto Exit; - } - for ( nn = 0; nn < num_components; nn++ ) { FT_UInt gindex = FT_NEXT_USHORT( p ); @@ -667,7 +685,7 @@ /* NB: a recursive call */ - error = tt_sbit_decoder_load_image( decoder, gindex, + error = tt_sbit_decoder_load_image( decoder, load_flags, gindex, x_pos + dx, y_pos + dy ); if ( error ) break; @@ -691,8 +709,54 @@ } +#ifdef FT_CONFIG_OPTION_USE_PNG + + static FT_Error + tt_sbit_decoder_load_png( TT_SBitDecoder decoder, + FT_UInt load_flags, + FT_Byte* p, + FT_Byte* limit, + FT_Int x_pos, + FT_Int y_pos ) + { + FT_Error error = FT_Err_Ok; + FT_ULong png_len; + + FT_UNUSED( load_flags ); + + + if ( limit - p < 4 ) + { + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + png_len = FT_NEXT_ULONG( p ); + if ( (FT_ULong)( limit - p ) < png_len ) + { + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + error = Load_SBit_Png( decoder->bitmap, + x_pos, + y_pos, + decoder->bit_depth, + decoder->metrics, + decoder->stream->memory, + p, + png_len ); + + Exit: + return error; + } + +#endif /* FT_CONFIG_OPTION_USE_PNG */ + + static FT_Error tt_sbit_decoder_load_bitmap( TT_SBitDecoder decoder, + FT_UInt load_flags, FT_UInt glyph_format, FT_ULong glyph_start, FT_ULong glyph_size, @@ -726,12 +790,14 @@ case 1: case 2: case 8: + case 17: error = tt_sbit_decoder_load_metrics( decoder, &p, p_limit, 0 ); break; case 6: case 7: case 9: + case 18: error = tt_sbit_decoder_load_metrics( decoder, &p, p_limit, 1 ); break; @@ -770,11 +836,77 @@ loader = tt_sbit_decoder_load_compound; break; +#ifdef FT_CONFIG_OPTION_USE_PNG + case 17: /* small metrics, PNG image data */ + case 18: /* big metrics, PNG image data */ + case 19: /* metrics in EBLC, PNG image data */ + loader = tt_sbit_decoder_load_png; + break; +#endif /* FT_CONFIG_OPTION_USE_PNG */ + default: + error = FT_THROW( Invalid_Table ); goto Fail; } - error = loader( decoder, p, p_limit, x_pos, y_pos ); + if ( !decoder->bitmap_allocated ) + { + error = tt_sbit_decoder_alloc_bitmap( decoder, load_flags ); + if ( error ) + goto Fail; + } + + if ( decoder->bit_depth == 32 && + decoder->bitmap->pixel_mode != FT_PIXEL_MODE_BGRA ) + { + /* Flatten color bitmaps if color was not requested. */ + + FT_Library library = decoder->face->root.glyph->library; + FT_Memory memory = decoder->stream->memory; + + FT_Bitmap color, *orig; + + + if ( decoder->bitmap->pixel_mode != FT_PIXEL_MODE_GRAY || + x_pos != 0 || y_pos != 0 ) + { + /* Shouldn't happen. */ + error = FT_THROW( Invalid_Table ); + goto Fail; + } + + FT_Bitmap_New( &color ); + + color.rows = decoder->bitmap->rows; + color.width = decoder->bitmap->width; + color.pitch = color.width * 4; + color.pixel_mode = FT_PIXEL_MODE_BGRA; + + if ( FT_ALLOC( color.buffer, color.rows * color.pitch ) ) + goto Fail; + + orig = decoder->bitmap; + decoder->bitmap = &color; + + error = loader( decoder, load_flags, p, p_limit, x_pos, y_pos ); + + decoder->bitmap = orig; + + if ( error || + ( error = FT_Bitmap_Convert( library, + &color, + decoder->bitmap, + 1 ) ) ) + { + FT_Bitmap_Done( library, &color ); + goto Fail; + } + + FT_Bitmap_Done( library, &color ); + } + + else + error = loader( decoder, load_flags, p, p_limit, x_pos, y_pos ); } Fail: @@ -787,6 +919,7 @@ static FT_Error tt_sbit_decoder_load_image( TT_SBitDecoder decoder, + FT_UInt load_flags, FT_UInt glyph_index, FT_Int x_pos, FT_Int y_pos ) @@ -915,6 +1048,7 @@ break; case 5: /* constant metrics with sparse glyph codes */ + case 19: { FT_ULong image_size, mm, num_glyphs; @@ -961,6 +1095,7 @@ image_start = image_offset + image_start; return tt_sbit_decoder_load_bitmap( decoder, + load_flags, image_format, image_start, image_end, @@ -995,11 +1130,16 @@ error = tt_sbit_decoder_init( decoder, face, strike_index, metrics ); if ( !error ) { - error = tt_sbit_decoder_load_image( decoder, glyph_index, 0, 0 ); + error = tt_sbit_decoder_load_image( decoder, + load_flags, + glyph_index, + 0, + 0 ); tt_sbit_decoder_done( decoder ); } return error; } + /* EOF */