shithub: freetype+ttf2subf

ref: c4c373cee51dd2b81cc3a2fcf923e2a029197b5a
dir: /src/pfr/pfrsbit.c/

View raw version
#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_Int    width;     /* width in pixels/bits                  */
    FT_Int    rows;      /* number of remaining rows to scan      */
    FT_Int    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 * ( target->rows-1 );
      writer->pitch = -writer->pitch;
    }
  }


  static void
  pfr_bitwriter_decode_bytes( PFR_BitWriter  writer,
                              FT_Byte*       p,
                              FT_Byte*       limit )
  {
    FT_Int    n, reload;
    FT_Int    left = writer->width;
    FT_Byte*  cur  = writer->line;
    FT_UInt   mask = 0x80;
    FT_UInt   val  = 0;
    FT_UInt   c    = 0;

    n = (FT_Int)(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] = c;
        mask   = 0x80;
        c      = 0;
        cur ++;
      }
    }

    if ( mask != 0x80 )
      cur[0] = c;
  }


  static void
  pfr_bitwriter_decode_rle1( PFR_BitWriter  writer,
                             FT_Byte*       p,
                             FT_Byte*       limit )
  {
    FT_Int    n, phase, count, counts[2], reload;
    FT_Int    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] = 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    n, phase, count, reload;
    FT_Int    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] = 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_Int      count,
                          FT_Byte     flags,
                          FT_UInt     char_code,
                          FT_ULong*   found_offset,
                          FT_ULong*   found_size )
  {
    FT_UInt    left, right, char_len;
    FT_Bool    two = (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're 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 = 0;
    FT_Byte    flags;
    FT_Char    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_INT8(p);
        xpos = 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) << 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 = PFR_Err_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_UInt         decreasing,
                        FT_Bitmap*      target )
  {
    FT_Error          error = 0;
    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_Err_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_Err_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_Err_Invalid_Argument;
        goto Exit;
      }
    }

    /* get the bitmap metrics */
    {
      FT_Long   xpos, ypos, advance;
      FT_UInt   xsize, ysize, format;
      FT_Byte*  p;

      advance = FT_MulDiv( size->root.metrics.x_ppem << 8,
                           character->advance,
                           phys->metrics_resolution );

      /* XXX: handle linearHoriAdvance correctly !! */

      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 )
      {
        glyph->root.format = FT_GLYPH_FORMAT_BITMAP;
      
        /* Set up glyph bitmap and metrics */
        glyph->root.bitmap.width      = (FT_Int) xsize;
        glyph->root.bitmap.rows       = (FT_Int) ysize;
        glyph->root.bitmap.pitch      = (FT_Long)(xsize+7) >> 3;
        glyph->root.bitmap.pixel_mode = FT_PIXEL_MODE_MONO;

        glyph->root.metrics.width        = (FT_Long)xsize << 6;
        glyph->root.metrics.height       = (FT_Long)ysize << 6;
        glyph->root.metrics.horiBearingX = xpos << 6;
        glyph->root.metrics.horiBearingY = ypos << 6;
        glyph->root.metrics.horiAdvance  = ((advance >> 2) + 32) & -64;
        glyph->root.metrics.vertBearingX = - glyph->root.metrics.width >> 1;
        glyph->root.metrics.vertBearingY = 0;
        glyph->root.metrics.vertAdvance  = size->root.metrics.height;

        glyph->root.bitmap_left = xpos;
        glyph->root.bitmap_top  = ypos + ysize;

        /* Allocate and read bitmap data */
        {
          FT_Memory  memory = face->root.memory;
          FT_Long    len    = glyph->root.bitmap.pitch*ysize;

          if ( !FT_ALLOC( glyph->root.bitmap.buffer, len ) )
          {
            error = pfr_load_bitmap_bits( p,
                                          stream->limit,
                                          format,
                                          (face->header.color_flags & 2),
                                          &glyph->root.bitmap );
          }
        }
      }
      FT_FRAME_EXIT();
    }

  Exit:
    return error;
  }