/***************************************************************************/ /* */ /* pfrsbit.c */ /* */ /* FreeType PFR bitmap loader (body). */ /* */ /* Copyright 2002-2015 by */ /* David Turner, Robert Wilhelm, and Werner Lemberg. */ /* */ /* 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 "pfrsbit.h" #include "pfrload.h" #include FT_INTERNAL_DEBUG_H #include FT_INTERNAL_STREAM_H #include "pfrerror.h" #undef FT_COMPONENT #define FT_COMPONENT trace_pfr /*************************************************************************/ /*************************************************************************/ /***** *****/ /***** PFR BIT WRITER *****/ /***** *****/ /*************************************************************************/ /*************************************************************************/ typedef struct PFR_BitWriter_ { FT_Byte* line; /* current line start */ FT_Int pitch; /* line size in bytes */ FT_UInt width; /* width in pixels/bits */ FT_UInt rows; /* number of remaining rows to scan */ FT_UInt total; /* total number of bits to draw */ } PFR_BitWriterRec, *PFR_BitWriter; static void pfr_bitwriter_init( PFR_BitWriter writer, FT_Bitmap* target, FT_Bool decreasing ) { writer->line = target->buffer; writer->pitch = target->pitch; writer->width = target->width; writer->rows = target->rows; writer->total = writer->width * writer->rows; if ( !decreasing ) { writer->line += writer->pitch * (FT_Int)( target->rows - 1 ); writer->pitch = -writer->pitch; } } static void pfr_bitwriter_decode_bytes( PFR_BitWriter writer, FT_Byte* p, FT_Byte* limit ) { FT_UInt n, reload; FT_UInt left = writer->width; FT_Byte* cur = writer->line; FT_UInt mask = 0x80; FT_UInt val = 0; FT_UInt c = 0; n = (FT_UInt)( limit - p ) * 8; if ( n > writer->total ) n = writer->total; reload = n & 7; for ( ; n > 0; n-- ) { if ( ( n & 7 ) == reload ) val = *p++; if ( val & 0x80 ) c |= mask; val <<= 1; mask >>= 1; if ( --left <= 0 ) { cur[0] = (FT_Byte)c; left = writer->width; mask = 0x80; writer->line += writer->pitch; cur = writer->line; c = 0; } else if ( mask == 0 ) { cur[0] = (FT_Byte)c; mask = 0x80; c = 0; cur++; } } if ( mask != 0x80 ) cur[0] = (FT_Byte)c; } static void pfr_bitwriter_decode_rle1( PFR_BitWriter writer, FT_Byte* p, FT_Byte* limit ) { FT_Int phase, count, counts[2]; FT_UInt n, reload; FT_UInt left = writer->width; FT_Byte* cur = writer->line; FT_UInt mask = 0x80; FT_UInt c = 0; n = writer->total; phase = 1; counts[0] = 0; counts[1] = 0; count = 0; reload = 1; for ( ; n > 0; n-- ) { if ( reload ) { do { if ( phase ) { FT_Int v; if ( p >= limit ) break; v = *p++; counts[0] = v >> 4; counts[1] = v & 15; phase = 0; count = counts[0]; } else { phase = 1; count = counts[1]; } } while ( count == 0 ); } if ( phase ) c |= mask; mask >>= 1; if ( --left <= 0 ) { cur[0] = (FT_Byte)c; left = writer->width; mask = 0x80; writer->line += writer->pitch; cur = writer->line; c = 0; } else if ( mask == 0 ) { cur[0] = (FT_Byte)c; mask = 0x80; c = 0; cur++; } reload = ( --count <= 0 ); } if ( mask != 0x80 ) cur[0] = (FT_Byte) c; } static void pfr_bitwriter_decode_rle2( PFR_BitWriter writer, FT_Byte* p, FT_Byte* limit ) { FT_Int phase, count; FT_UInt n, reload; FT_UInt left = writer->width; FT_Byte* cur = writer->line; FT_UInt mask = 0x80; FT_UInt c = 0; n = writer->total; phase = 1; count = 0; reload = 1; for ( ; n > 0; n-- ) { if ( reload ) { do { if ( p >= limit ) break; count = *p++; phase = phase ^ 1; } while ( count == 0 ); } if ( phase ) c |= mask; mask >>= 1; if ( --left <= 0 ) { cur[0] = (FT_Byte)c; c = 0; mask = 0x80; left = writer->width; writer->line += writer->pitch; cur = writer->line; } else if ( mask == 0 ) { cur[0] = (FT_Byte)c; c = 0; mask = 0x80; cur++; } reload = ( --count <= 0 ); } if ( mask != 0x80 ) cur[0] = (FT_Byte) c; } /*************************************************************************/ /*************************************************************************/ /***** *****/ /***** BITMAP DATA DECODING *****/ /***** *****/ /*************************************************************************/ /*************************************************************************/ static void pfr_lookup_bitmap_data( FT_Byte* base, FT_Byte* limit, FT_UInt count, FT_UInt flags, FT_UInt char_code, FT_ULong* found_offset, FT_ULong* found_size ) { FT_UInt left, right, char_len; FT_Bool two = FT_BOOL( flags & 1 ); FT_Byte* buff; char_len = 4; if ( two ) char_len += 1; if ( flags & 2 ) char_len += 1; if ( flags & 4 ) char_len += 1; left = 0; right = count; while ( left < right ) { FT_UInt middle, code; middle = ( left + right ) >> 1; buff = base + middle * char_len; /* check that we are not outside of the table -- */ /* this is possible with broken fonts... */ if ( buff + char_len > limit ) goto Fail; if ( two ) code = PFR_NEXT_USHORT( buff ); else code = PFR_NEXT_BYTE( buff ); if ( code == char_code ) goto Found_It; if ( code < char_code ) left = middle; else right = middle; } Fail: /* Not found */ *found_size = 0; *found_offset = 0; return; Found_It: if ( flags & 2 ) *found_size = PFR_NEXT_USHORT( buff ); else *found_size = PFR_NEXT_BYTE( buff ); if ( flags & 4 ) *found_offset = PFR_NEXT_ULONG( buff ); else *found_offset = PFR_NEXT_USHORT( buff ); } /* load bitmap metrics. "*padvance" must be set to the default value */ /* before calling this function... */ /* */ static FT_Error pfr_load_bitmap_metrics( FT_Byte** pdata, FT_Byte* limit, FT_Long scaled_advance, FT_Long *axpos, FT_Long *aypos, FT_UInt *axsize, FT_UInt *aysize, FT_Long *aadvance, FT_UInt *aformat ) { FT_Error error = FT_Err_Ok; FT_Byte flags; FT_Char c; FT_Byte b; FT_Byte* p = *pdata; FT_Long xpos, ypos, advance; FT_UInt xsize, ysize; PFR_CHECK( 1 ); flags = PFR_NEXT_BYTE( p ); xpos = 0; ypos = 0; xsize = 0; ysize = 0; advance = 0; switch ( flags & 3 ) { case 0: PFR_CHECK( 1 ); c = PFR_NEXT_INT8( p ); xpos = c >> 4; ypos = ( (FT_Char)( c << 4 ) ) >> 4; break; case 1: PFR_CHECK( 2 ); xpos = PFR_NEXT_INT8( p ); ypos = PFR_NEXT_INT8( p ); break; case 2: PFR_CHECK( 4 ); xpos = PFR_NEXT_SHORT( p ); ypos = PFR_NEXT_SHORT( p ); break; case 3: PFR_CHECK( 6 ); xpos = PFR_NEXT_LONG( p ); ypos = PFR_NEXT_LONG( p ); break; default: ; } flags >>= 2; switch ( flags & 3 ) { case 0: /* blank image */ xsize = 0; ysize = 0; break; case 1: PFR_CHECK( 1 ); b = PFR_NEXT_BYTE( p ); xsize = ( b >> 4 ) & 0xF; ysize = b & 0xF; break; case 2: PFR_CHECK( 2 ); xsize = PFR_NEXT_BYTE( p ); ysize = PFR_NEXT_BYTE( p ); break; case 3: PFR_CHECK( 4 ); xsize = PFR_NEXT_USHORT( p ); ysize = PFR_NEXT_USHORT( p ); break; default: ; } flags >>= 2; switch ( flags & 3 ) { case 0: advance = scaled_advance; break; case 1: PFR_CHECK( 1 ); advance = PFR_NEXT_INT8( p ) << 8; break; case 2: PFR_CHECK( 2 ); advance = PFR_NEXT_SHORT( p ); break; case 3: PFR_CHECK( 3 ); advance = PFR_NEXT_LONG( p ); break; default: ; } *axpos = xpos; *aypos = ypos; *axsize = xsize; *aysize = ysize; *aadvance = advance; *aformat = flags >> 2; *pdata = p; Exit: return error; Too_Short: error = FT_THROW( Invalid_Table ); FT_ERROR(( "pfr_load_bitmap_metrics: invalid glyph data\n" )); goto Exit; } static FT_Error pfr_load_bitmap_bits( FT_Byte* p, FT_Byte* limit, FT_UInt format, FT_Bool decreasing, FT_Bitmap* target ) { FT_Error error = FT_Err_Ok; PFR_BitWriterRec writer; if ( target->rows > 0 && target->width > 0 ) { pfr_bitwriter_init( &writer, target, decreasing ); switch ( format ) { case 0: /* packed bits */ pfr_bitwriter_decode_bytes( &writer, p, limit ); break; case 1: /* RLE1 */ pfr_bitwriter_decode_rle1( &writer, p, limit ); break; case 2: /* RLE2 */ pfr_bitwriter_decode_rle2( &writer, p, limit ); break; default: FT_ERROR(( "pfr_read_bitmap_data: invalid image type\n" )); error = FT_THROW( Invalid_File_Format ); } } return error; } /*************************************************************************/ /*************************************************************************/ /***** *****/ /***** BITMAP LOADING *****/ /***** *****/ /*************************************************************************/ /*************************************************************************/ FT_LOCAL( FT_Error ) pfr_slot_load_bitmap( PFR_Slot glyph, PFR_Size size, FT_UInt glyph_index ) { FT_Error error; PFR_Face face = (PFR_Face) glyph->root.face; FT_Stream stream = face->root.stream; PFR_PhyFont phys = &face->phy_font; FT_ULong gps_offset; FT_ULong gps_size; PFR_Char character; PFR_Strike strike; character = &phys->chars[glyph_index]; /* Look-up a bitmap strike corresponding to the current */ /* character dimensions */ { FT_UInt n; strike = phys->strikes; for ( n = 0; n < phys->num_strikes; n++ ) { if ( strike->x_ppm == (FT_UInt)size->root.metrics.x_ppem && strike->y_ppm == (FT_UInt)size->root.metrics.y_ppem ) { goto Found_Strike; } strike++; } /* couldn't find it */ return FT_THROW( Invalid_Argument ); } Found_Strike: /* Now lookup the glyph's position within the file */ { FT_UInt char_len; char_len = 4; if ( strike->flags & 1 ) char_len += 1; if ( strike->flags & 2 ) char_len += 1; if ( strike->flags & 4 ) char_len += 1; /* Access data directly in the frame to speed lookups */ if ( FT_STREAM_SEEK( phys->bct_offset + strike->bct_offset ) || FT_FRAME_ENTER( char_len * strike->num_bitmaps ) ) goto Exit; pfr_lookup_bitmap_data( stream->cursor, stream->limit, strike->num_bitmaps, strike->flags, character->char_code, &gps_offset, &gps_size ); FT_FRAME_EXIT(); if ( gps_size == 0 ) { /* Could not find a bitmap program string for this glyph */ error = FT_THROW( Invalid_Argument ); goto Exit; } } /* get the bitmap metrics */ { FT_Long xpos = 0, ypos = 0, advance = 0; FT_UInt xsize = 0, ysize = 0, format = 0; FT_Byte* p; /* compute linear advance */ advance = character->advance; if ( phys->metrics_resolution != phys->outline_resolution ) advance = FT_MulDiv( advance, (FT_Long)phys->outline_resolution, (FT_Long)phys->metrics_resolution ); glyph->root.linearHoriAdvance = advance; /* compute default advance, i.e., scaled advance. This can be */ /* overridden in the bitmap header of certain glyphs. */ advance = FT_MulDiv( (FT_Fixed)size->root.metrics.x_ppem << 8, character->advance, (FT_Long)phys->metrics_resolution ); if ( FT_STREAM_SEEK( face->header.gps_section_offset + gps_offset ) || FT_FRAME_ENTER( gps_size ) ) goto Exit; p = stream->cursor; error = pfr_load_bitmap_metrics( &p, stream->limit, advance, &xpos, &ypos, &xsize, &ysize, &advance, &format ); /* * XXX: on 16bit systems we return an error for huge bitmaps * that cause size truncation, because truncated * size properties make bitmap glyphs broken. */ if ( xpos > FT_INT_MAX || xpos < FT_INT_MIN || ysize > FT_INT_MAX || ypos > FT_INT_MAX - (FT_Long)ysize || ypos + (FT_Long)ysize < FT_INT_MIN ) { FT_TRACE1(( "pfr_slot_load_bitmap:" )); FT_TRACE1(( "huge bitmap glyph %dx%d over FT_GlyphSlot\n", xpos, ypos )); error = FT_THROW( Invalid_Pixel_Size ); } if ( !error ) { glyph->root.format = FT_GLYPH_FORMAT_BITMAP; /* Set up glyph bitmap and metrics */ /* XXX: needs casts to fit FT_Bitmap.{width|rows|pitch} */ glyph->root.bitmap.width = xsize; glyph->root.bitmap.rows = ysize; glyph->root.bitmap.pitch = (FT_Int)( xsize + 7 ) >> 3; glyph->root.bitmap.pixel_mode = FT_PIXEL_MODE_MONO; /* XXX: needs casts to fit FT_Glyph_Metrics.{width|height} */ glyph->root.metrics.width = (FT_Pos)xsize << 6; glyph->root.metrics.height = (FT_Pos)ysize << 6; glyph->root.metrics.horiBearingX = xpos << 6; glyph->root.metrics.horiBearingY = ypos << 6; glyph->root.metrics.horiAdvance = FT_PIX_ROUND( ( advance >> 2 ) ); glyph->root.metrics.vertBearingX = - glyph->root.metrics.width >> 1; glyph->root.metrics.vertBearingY = 0; glyph->root.metrics.vertAdvance = size->root.metrics.height; /* XXX: needs casts fit FT_GlyphSlotRec.bitmap_{left|top} */ glyph->root.bitmap_left = (FT_Int)xpos; glyph->root.bitmap_top = (FT_Int)( ypos + (FT_Long)ysize ); /* Allocate and read bitmap data */ { FT_ULong len = (FT_ULong)glyph->root.bitmap.pitch * ysize; error = ft_glyphslot_alloc_bitmap( &glyph->root, len ); if ( !error ) { error = pfr_load_bitmap_bits( p, stream->limit, format, FT_BOOL(face->header.color_flags & 2), &glyph->root.bitmap ); } } } FT_FRAME_EXIT(); } Exit: return error; } /* END */