Home | History | Annotate | Download | only in src
      1 /*
      2  * Copyright (C) 1998-2004  David Turner and Werner Lemberg
      3  * Copyright (C) 2004,2007  Red Hat, Inc.
      4  *
      5  * This is part of HarfBuzz, an OpenType Layout engine library.
      6  *
      7  * Permission is hereby granted, without written agreement and without
      8  * license or royalty fees, to use, copy, modify, and distribute this
      9  * software and its documentation for any purpose, provided that the
     10  * above copyright notice and the following two paragraphs appear in
     11  * all copies of this software.
     12  *
     13  * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
     14  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
     15  * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
     16  * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
     17  * DAMAGE.
     18  *
     19  * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
     20  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
     21  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
     22  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
     23  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
     24  *
     25  * Red Hat Author(s): Owen Taylor, Behdad Esfahbod
     26  */
     27 
     28 #include "harfbuzz-impl.h"
     29 #include "harfbuzz-buffer-private.h"
     30 #include "harfbuzz-gsub-private.h"
     31 #include "harfbuzz-gpos-private.h"
     32 
     33 /* Here is how the buffer works internally:
     34  *
     35  * There are two string pointers: in_string and out_string.  They
     36  * always have same allocated size, but different length and positions.
     37  *
     38  * As an optimization, both in_string and out_string may point to the
     39  * same piece of memory, which is owned by in_string.  This remains the
     40  * case as long as:
     41  *
     42  *   - copy_glyph() is called
     43  *   - replace_glyph() is called with inplace=TRUE
     44  *   - add_output_glyph() and add_output_glyphs() are not called
     45  *
     46  * In that case swap(), and copy_glyph(), and replace_glyph() are all
     47  * mostly no-op.
     48  *
     49  * As soon an add_output_glyph[s]() or replace_glyph() with inplace=FALSE is
     50  * called, out_string is moved over to an alternate buffer (alt_string), and
     51  * its current contents (out_length entries) are copied to the alt buffer.
     52  * This should all remain transparent to the user.  swap() then switches
     53  * in_string and alt_string.  alt_string is not allocated until its needed,
     54  * but after that it's grown with in_string unconditionally.
     55  *
     56  * The buffer->separate_out boolean keeps status of whether out_string points
     57  * to in_string (FALSE) or alt_string (TRUE).
     58  */
     59 
     60 /* Internal API */
     61 
     62 static HB_Error
     63 hb_buffer_ensure( HB_Buffer buffer,
     64 		   HB_UInt   size )
     65 {
     66   HB_UInt new_allocated = buffer->allocated;
     67 
     68   if (size > new_allocated)
     69     {
     70       HB_Error error;
     71 
     72       while (size > new_allocated)
     73 	new_allocated += (new_allocated >> 1) + 8;
     74 
     75       if ( buffer->positions )
     76         {
     77 	  if ( REALLOC_ARRAY( buffer->positions, new_allocated, HB_PositionRec ) )
     78 	    return error;
     79 	}
     80 
     81       if ( REALLOC_ARRAY( buffer->in_string, new_allocated, HB_GlyphItemRec ) )
     82 	return error;
     83 
     84       if ( buffer->separate_out )
     85         {
     86 	  if ( REALLOC_ARRAY( buffer->alt_string, new_allocated, HB_GlyphItemRec ) )
     87 	    return error;
     88 
     89 	  buffer->out_string = buffer->alt_string;
     90 	}
     91       else
     92         {
     93 	  buffer->out_string = buffer->in_string;
     94 
     95 	  if ( buffer->alt_string )
     96 	    {
     97 	      if ( REALLOC_ARRAY( buffer->alt_string, new_allocated, HB_GlyphItemRec ) )
     98 		return error;
     99 	    }
    100 	}
    101 
    102       buffer->allocated = new_allocated;
    103     }
    104 
    105   return HB_Err_Ok;
    106 }
    107 
    108 static HB_Error
    109 hb_buffer_duplicate_out_buffer( HB_Buffer buffer )
    110 {
    111   if ( !buffer->alt_string )
    112     {
    113       HB_Error error;
    114 
    115       if ( ALLOC_ARRAY( buffer->alt_string, buffer->allocated, HB_GlyphItemRec ) )
    116 	return error;
    117     }
    118 
    119   buffer->out_string = buffer->alt_string;
    120   memcpy( buffer->out_string, buffer->in_string, buffer->out_length * sizeof (buffer->out_string[0]) );
    121   buffer->separate_out = TRUE;
    122 
    123   return HB_Err_Ok;
    124 }
    125 
    126 /* Public API */
    127 
    128 HB_Error
    129 hb_buffer_new( HB_Buffer *pbuffer )
    130 {
    131   HB_Buffer buffer;
    132   HB_Error error;
    133 
    134   if ( ALLOC( buffer, sizeof( HB_BufferRec ) ) )
    135     return error;
    136 
    137   buffer->allocated = 0;
    138   buffer->in_string = NULL;
    139   buffer->alt_string = NULL;
    140   buffer->positions = NULL;
    141 
    142   hb_buffer_clear( buffer );
    143 
    144   *pbuffer = buffer;
    145 
    146   return HB_Err_Ok;
    147 }
    148 
    149 void
    150 hb_buffer_free( HB_Buffer buffer )
    151 {
    152   FREE( buffer->in_string );
    153   FREE( buffer->alt_string );
    154   buffer->out_string = NULL;
    155   FREE( buffer->positions );
    156   FREE( buffer );
    157 }
    158 
    159 void
    160 hb_buffer_clear( HB_Buffer buffer )
    161 {
    162   buffer->in_length = 0;
    163   buffer->out_length = 0;
    164   buffer->in_pos = 0;
    165   buffer->out_pos = 0;
    166   buffer->out_string = buffer->in_string;
    167   buffer->separate_out = FALSE;
    168   buffer->max_ligID = 0;
    169 }
    170 
    171 HB_Error
    172 hb_buffer_add_glyph( HB_Buffer buffer,
    173 		      HB_UInt   glyph_index,
    174 		      HB_UInt   properties,
    175 		      HB_UInt   cluster )
    176 {
    177   HB_Error error;
    178   HB_GlyphItem glyph;
    179 
    180   error = hb_buffer_ensure( buffer, buffer->in_length + 1 );
    181   if ( error )
    182     return error;
    183 
    184   glyph = &buffer->in_string[buffer->in_length];
    185   glyph->gindex = glyph_index;
    186   glyph->properties = properties;
    187   glyph->cluster = cluster;
    188   glyph->component = 0;
    189   glyph->ligID = 0;
    190   glyph->gproperties = HB_GLYPH_PROPERTIES_UNKNOWN;
    191 
    192   buffer->in_length++;
    193 
    194   return HB_Err_Ok;
    195 }
    196 
    197 /* HarfBuzz-Internal API */
    198 
    199 HB_INTERNAL void
    200 _hb_buffer_clear_output( HB_Buffer buffer )
    201 {
    202   buffer->out_length = 0;
    203   buffer->out_pos = 0;
    204   buffer->out_string = buffer->in_string;
    205   buffer->separate_out = FALSE;
    206 }
    207 
    208 HB_INTERNAL HB_Error
    209 _hb_buffer_clear_positions( HB_Buffer buffer )
    210 {
    211   if ( !buffer->positions )
    212     {
    213       HB_Error error;
    214 
    215       if ( ALLOC_ARRAY( buffer->positions, buffer->allocated, HB_PositionRec ) )
    216 	return error;
    217     }
    218 
    219   memset (buffer->positions, 0, sizeof (buffer->positions[0]) * buffer->in_length);
    220 
    221   return HB_Err_Ok;
    222 }
    223 
    224 HB_INTERNAL void
    225 _hb_buffer_swap( HB_Buffer buffer )
    226 {
    227   HB_GlyphItem tmp_string;
    228   int tmp_length;
    229   int tmp_pos;
    230 
    231   if ( buffer->separate_out )
    232     {
    233       tmp_string = buffer->in_string;
    234       buffer->in_string = buffer->out_string;
    235       buffer->out_string = tmp_string;
    236       buffer->alt_string = buffer->out_string;
    237     }
    238 
    239   tmp_length = buffer->in_length;
    240   buffer->in_length = buffer->out_length;
    241   buffer->out_length = tmp_length;
    242 
    243   tmp_pos = buffer->in_pos;
    244   buffer->in_pos = buffer->out_pos;
    245   buffer->out_pos = tmp_pos;
    246 }
    247 
    248 /* The following function copies `num_out' elements from `glyph_data'
    249    to `buffer->out_string', advancing the in array pointer in the structure
    250    by `num_in' elements, and the out array pointer by `num_out' elements.
    251    Finally, it sets the `length' field of `out' equal to
    252    `pos' of the `out' structure.
    253 
    254    If `component' is 0xFFFF, the component value from buffer->in_pos
    255    will copied `num_out' times, otherwise `component' itself will
    256    be used to fill the `component' fields.
    257 
    258    If `ligID' is 0xFFFF, the ligID value from buffer->in_pos
    259    will copied `num_out' times, otherwise `ligID' itself will
    260    be used to fill the `ligID' fields.
    261 
    262    The properties for all replacement glyphs are taken
    263    from the glyph at position `buffer->in_pos'.
    264 
    265    The cluster value for the glyph at position buffer->in_pos is used
    266    for all replacement glyphs */
    267 HB_INTERNAL HB_Error
    268 _hb_buffer_add_output_glyphs( HB_Buffer  buffer,
    269 			      HB_UShort  num_in,
    270 			      HB_UShort  num_out,
    271 			      HB_UShort *glyph_data,
    272 			      HB_UShort  component,
    273 			      HB_UShort  ligID )
    274 {
    275   HB_Error  error;
    276   HB_UShort i;
    277   HB_UInt properties;
    278   HB_UInt cluster;
    279 
    280   error = hb_buffer_ensure( buffer, buffer->out_pos + num_out );
    281   if ( error )
    282     return error;
    283 
    284   if ( !buffer->separate_out )
    285     {
    286       error = hb_buffer_duplicate_out_buffer( buffer );
    287       if ( error )
    288 	return error;
    289     }
    290 
    291   properties = buffer->in_string[buffer->in_pos].properties;
    292   cluster = buffer->in_string[buffer->in_pos].cluster;
    293   if ( component == 0xFFFF )
    294     component = buffer->in_string[buffer->in_pos].component;
    295   if ( ligID == 0xFFFF )
    296     ligID = buffer->in_string[buffer->in_pos].ligID;
    297 
    298   for ( i = 0; i < num_out; i++ )
    299   {
    300     HB_GlyphItem item = &buffer->out_string[buffer->out_pos + i];
    301 
    302     item->gindex = glyph_data[i];
    303     item->properties = properties;
    304     item->cluster = cluster;
    305     item->component = component;
    306     item->ligID = ligID;
    307     item->gproperties = HB_GLYPH_PROPERTIES_UNKNOWN;
    308   }
    309 
    310   buffer->in_pos  += num_in;
    311   buffer->out_pos += num_out;
    312 
    313   buffer->out_length = buffer->out_pos;
    314 
    315   return HB_Err_Ok;
    316 }
    317 
    318 HB_INTERNAL HB_Error
    319 _hb_buffer_add_output_glyph( HB_Buffer buffer,
    320 			     HB_UInt   glyph_index,
    321 			     HB_UShort component,
    322 			     HB_UShort ligID )
    323 {
    324   HB_UShort glyph_data =  glyph_index;
    325 
    326   return _hb_buffer_add_output_glyphs ( buffer, 1, 1,
    327 					&glyph_data, component, ligID );
    328 }
    329 
    330 HB_INTERNAL HB_Error
    331 _hb_buffer_copy_output_glyph ( HB_Buffer buffer )
    332 {
    333   HB_Error  error;
    334 
    335   error = hb_buffer_ensure( buffer, buffer->out_pos + 1 );
    336   if ( error )
    337     return error;
    338 
    339   if ( buffer->separate_out )
    340     {
    341       buffer->out_string[buffer->out_pos] = buffer->in_string[buffer->in_pos];
    342     }
    343 
    344   buffer->in_pos++;
    345   buffer->out_pos++;
    346   buffer->out_length = buffer->out_pos;
    347 
    348   return HB_Err_Ok;
    349 }
    350 
    351 HB_INTERNAL HB_Error
    352 _hb_buffer_replace_output_glyph( HB_Buffer buffer,
    353 				 HB_UInt   glyph_index,
    354 				 HB_Bool   inplace )
    355 {
    356 
    357   HB_Error error;
    358 
    359   if ( inplace )
    360     {
    361       error = _hb_buffer_copy_output_glyph ( buffer );
    362       if ( error )
    363 	return error;
    364 
    365       buffer->out_string[buffer->out_pos-1].gindex = glyph_index;
    366     }
    367   else
    368     {
    369       return _hb_buffer_add_output_glyph( buffer, glyph_index, 0xFFFF, 0xFFFF );
    370     }
    371 
    372   return HB_Err_Ok;
    373 }
    374 
    375 HB_INTERNAL HB_UShort
    376 _hb_buffer_allocate_ligid( HB_Buffer buffer )
    377 {
    378   buffer->max_ligID++;
    379   if (HB_UNLIKELY (buffer->max_ligID == 0))
    380     buffer->max_ligID++;
    381 
    382   return buffer->max_ligID;
    383 }
    384