Edit

kc3-lang/freetype/src/pfr/pfrsbit.c

Branch :

  • Show log

    Commit

  • Author : Werner Lemberg
    Date : 2023-02-08 20:56:19
    Hash : 663486a7
    Message : Fix `FT_LOCAL` and `FT_LOCAL_DEF` tags.

  • src/pfr/pfrsbit.c
  • /****************************************************************************
     *
     * pfrsbit.c
     *
     *   FreeType PFR bitmap loader (body).
     *
     * Copyright (C) 2002-2023 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 <freetype/internal/ftdebug.h>
    #include <freetype/internal/ftstream.h>
    
    #include "pfrerror.h"
    
    #undef  FT_COMPONENT
    #define FT_COMPONENT  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   min, max, mid, char_len;
        FT_Bool   two = FT_BOOL( *flags & PFR_BITMAP_2BYTE_CHARCODE );
        FT_Byte*  buff;
    
    
        char_len = 4;
        if ( two )
          char_len += 1;
        if ( *flags & PFR_BITMAP_2BYTE_SIZE )
          char_len += 1;
        if ( *flags & PFR_BITMAP_3BYTE_OFFSET )
          char_len += 1;
    
        if ( !( *flags & PFR_BITMAP_CHARCODES_VALIDATED ) )
        {
          FT_Byte*  p;
          FT_Byte*  lim;
          FT_UInt   code;
          FT_Long   prev_code;
    
    
          *flags    |= PFR_BITMAP_VALID_CHARCODES;
          prev_code  = -1;
          lim        = base + count * char_len;
    
          if ( lim > limit )
          {
            FT_TRACE0(( "pfr_lookup_bitmap_data:"
                        " number of bitmap records too large,\n" ));
            FT_TRACE0(( "                       "
                        " thus ignoring all bitmaps in this strike\n" ));
            *flags &= ~PFR_BITMAP_VALID_CHARCODES;
          }
          else
          {
            /* check whether records are sorted by code */
            for ( p = base; p < lim; p += char_len )
            {
              if ( two )
                code = FT_PEEK_USHORT( p );
              else
                code = *p;
    
              if ( (FT_Long)code <= prev_code )
              {
                FT_TRACE0(( "pfr_lookup_bitmap_data:"
                            " bitmap records are not sorted,\n" ));
                FT_TRACE0(( "                       "
                            " thus ignoring all bitmaps in this strike\n" ));
                *flags &= ~PFR_BITMAP_VALID_CHARCODES;
                break;
              }
    
              prev_code = code;
            }
          }
    
          *flags |= PFR_BITMAP_CHARCODES_VALIDATED;
        }
    
        /* ignore bitmaps in case table is not valid     */
        /* (this might be sanitized, but PFR is dead...) */
        if ( !( *flags & PFR_BITMAP_VALID_CHARCODES ) )
          goto Fail;
    
        min = 0;
        max = count;
        mid = min + ( max - min ) / 2;
    
        /* binary search */
        while ( min < max )
        {
          FT_UInt  code;
    
    
          buff = base + mid * char_len;
    
          if ( two )
            code = PFR_NEXT_USHORT( buff );
          else
            code = PFR_NEXT_BYTE( buff );
    
          if ( char_code < code )
            max = mid;
          else if ( char_code > code )
            min = mid + 1;
          else
            goto Found_It;
    
          /* reasonable prediction in a continuous block */
          mid += char_code - code;
          if ( mid >= max || mid < min )
            mid = min + ( max - min ) / 2;
        }
    
      Fail:
        /* Not found */
        *found_size   = 0;
        *found_offset = 0;
        return;
    
      Found_It:
        if ( *flags & PFR_BITMAP_2BYTE_SIZE )
          *found_size = PFR_NEXT_USHORT( buff );
        else
          *found_size = PFR_NEXT_BYTE( buff );
    
        if ( *flags & PFR_BITMAP_3BYTE_OFFSET )
          *found_offset = PFR_NEXT_ULONG( buff );
        else
          *found_offset = PFR_NEXT_USHORT( buff );
      }
    
    
      /* load bitmap metrics.  `*aadvance' 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_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 );
          b    = PFR_NEXT_BYTE( p );
          xpos = (FT_Char)b >> 4;
          ypos = ( (FT_Char)( b << 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 ) * 256;
          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:
            ;
          }
        }
    
        return error;
      }
    
    
      /*************************************************************************/
      /*************************************************************************/
      /*****                                                               *****/
      /*****                     BITMAP LOADING                            *****/
      /*****                                                               *****/
      /*************************************************************************/
      /*************************************************************************/
    
      FT_LOCAL_DEF( FT_Error )
      pfr_slot_load_bitmap( PFR_Slot  glyph,
                            PFR_Size  size,
                            FT_UInt   glyph_index,
                            FT_Bool   metrics_only )
      {
        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 look up the glyph's position within the file */
        {
          FT_UInt  char_len;
    
    
          char_len = 4;
          if ( strike->flags & PFR_BITMAP_2BYTE_CHARCODE )
            char_len += 1;
          if ( strike->flags & PFR_BITMAP_2BYTE_SIZE )
            char_len += 1;
          if ( strike->flags & PFR_BITMAP_3BYTE_OFFSET )
            char_len += 1;
    
          /* access data directly in the frame to speed up 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 );
          if ( error )
            goto Exit1;
    
          /*
           * Before allocating the target bitmap, we check whether the given
           * bitmap dimensions are valid, depending on the image format.
           *
           * Format 0: We have a stream of pixels (with 8 pixels per byte).
           *
           *             (xsize * ysize + 7) / 8 <= gps_size
           *
           * Format 1: Run-length encoding; the high nibble holds the number of
           *           white bits, the low nibble the number of black bits.  In
           *           other words, a single byte can represent at most 15
           *           pixels.
           *
           *             xsize * ysize <= 15 * gps_size
           *
           * Format 2: Run-length encoding; the high byte holds the number of
           *           white bits, the low byte the number of black bits.  In
           *           other words, two bytes can represent at most 255 pixels.
           *
           *             xsize * ysize <= 255 * (gps_size + 1) / 2
           */
          switch ( format )
          {
          case 0:
            if ( ( (FT_ULong)xsize * ysize + 7 ) / 8 > gps_size )
              error = FT_THROW( Invalid_Table );
            break;
          case 1:
            if ( (FT_ULong)xsize * ysize > 15 * gps_size )
              error = FT_THROW( Invalid_Table );
            break;
          case 2:
            if ( (FT_ULong)xsize * ysize > 255 * ( ( gps_size + 1 ) / 2 ) )
              error = FT_THROW( Invalid_Table );
            break;
          default:
            FT_ERROR(( "pfr_slot_load_bitmap: invalid image type\n" ));
            error = FT_THROW( Invalid_Table );
          }
    
          if ( error )
          {
            if ( FT_ERR_EQ( error, Invalid_Table ) )
              FT_ERROR(( "pfr_slot_load_bitmap: invalid bitmap dimensions\n" ));
            goto Exit1;
          }
    
          /*
           * 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:"
                        " huge bitmap glyph %ldx%ld 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 * 64;
            glyph->root.metrics.horiBearingY = ypos * 64;
            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 );
    
            if ( metrics_only )
              goto Exit1;
    
            /* 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 &
                                   PFR_FLAG_INVERT_BITMAP   ),
                          &glyph->root.bitmap );
            }
          }
    
        Exit1:
          FT_FRAME_EXIT();
        }
    
      Exit:
        return error;
      }
    
    
    /* END */