harfbuzz-buffer.c 8.6 KB
Newer Older
1 2
/* harfbuzz-buffer.c: Buffer of glyphs for substitution/positioning
 *
3
 * Copyright 2004,2007 Red Hat Software
4 5 6 7 8 9
 *
 * Portions Copyright 1996-2000 by
 * David Turner, Robert Wilhelm, and Werner Lemberg.
 */

#include "harfbuzz-impl.h"
10
#include "harfbuzz-buffer-private.h"
11 12 13
#include "harfbuzz-gsub-private.h"
#include "harfbuzz-gpos-private.h"

14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
/* Here is how the buffer works internally:
 *
 * There are two string pointers: in_string and out_string.  They
 * always have same allocated size, but different length and positions.
 *
 * As an optimization, both in_string and out_string may point to the
 * same piece of memory, which is owned by in_string.  This remains the
 * case as long as:
 *
 *   - copy_glyph() is called
 *   - replace_glyph() is called with inplace=TRUE
 *   - add_output_glyph() and add_output_glyphs() are not called
 *
 * In that case swap(), and copy_glyph(), and replace_glyph() are all
 * mostly no-op.
 *
 * As soon an add_output_glyph[s]() or replace_glyph() with inplace=FALSE is
 * called, out_string is moved over to an alternate buffer (alt_string), and
 * its current contents (out_length entries) are copied to the alt buffer.
 * This should all remain transparent to the user.  swap() then switches
 * in_string and alt_string.  alt_string is not allocated until its needed,
 * but after that it's grown with in_string unconditionally.
 *
37 38
 * The buffer->separate_out boolean keeps status of whether out_string points
 * to in_string (FALSE) or alt_string (TRUE).
39 40
 */

41 42
/* Internal API */

43
static HB_Error
44 45 46 47 48 49 50
hb_buffer_ensure( HB_Buffer buffer,
		   FT_ULong   size )
{
  FT_ULong new_allocated = buffer->allocated;

  if (size > new_allocated)
    {
51
      HB_Error error;
52 53 54 55

      while (size > new_allocated)
	new_allocated += (new_allocated >> 1) + 8;
      
56 57 58 59 60 61
      if ( buffer->positions )
        {
	  if ( REALLOC_ARRAY( buffer->positions, new_allocated, HB_PositionRec ) )
	    return error;
	}

62
      if ( REALLOC_ARRAY( buffer->in_string, new_allocated, HB_GlyphItemRec ) )
63
	return error;
64

65 66 67 68 69 70 71 72
      if ( buffer->separate_out )
        {
	  if ( REALLOC_ARRAY( buffer->alt_string, new_allocated, HB_GlyphItemRec ) )
	    return error;

	  buffer->out_string = buffer->alt_string;
	}
      else
73 74 75 76 77
        {
	  buffer->out_string = buffer->in_string;

	  if ( buffer->alt_string )
	    {
78
	      if ( REALLOC_ARRAY( buffer->alt_string, new_allocated, HB_GlyphItemRec ) )
79 80 81
		return error;
	    }
	}
82 83 84 85

      buffer->allocated = new_allocated;
    }

86
  return HB_Err_Ok;
87 88
}

89 90 91 92 93 94 95 96 97 98 99 100 101
static HB_Error
hb_buffer_duplicate_out_buffer( HB_Buffer buffer )
{
  if ( !buffer->alt_string )
    {
      HB_Error error;

      if ( ALLOC_ARRAY( buffer->alt_string, buffer->allocated, HB_GlyphItemRec ) )
	return error;
    }

  buffer->out_string = buffer->alt_string;
  memcpy( buffer->out_string, buffer->in_string, buffer->out_length * sizeof (buffer->out_string[0]) );
102
  buffer->separate_out = TRUE;
103 104 105 106

  return HB_Err_Ok;
}

107 108
/* Public API */

109
HB_Error
B
Behdad Esfahbod 已提交
110
hb_buffer_new( HB_Buffer *pbuffer )
111
{
B
Behdad Esfahbod 已提交
112
  HB_Buffer buffer;
113
  HB_Error error;
114
  
B
Behdad Esfahbod 已提交
115
  if ( ALLOC( buffer, sizeof( HB_BufferRec ) ) )
116 117
    return error;

B
Behdad Esfahbod 已提交
118 119 120 121
  buffer->allocated = 0;
  buffer->in_string = NULL;
  buffer->alt_string = NULL;
  buffer->positions = NULL;
122

B
Behdad Esfahbod 已提交
123
  hb_buffer_clear( buffer );
B
Behdad Esfahbod 已提交
124 125

  *pbuffer = buffer;
126 127 128

  return HB_Err_Ok;
}
129

B
Behdad Esfahbod 已提交
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
void
hb_buffer_free( HB_Buffer buffer )
{
  FREE( buffer->in_string );
  FREE( buffer->alt_string );
  buffer->out_string = NULL;
  FREE( buffer->positions );
  FREE( buffer );
}

void
hb_buffer_clear( HB_Buffer buffer )
{
  buffer->in_length = 0;
  buffer->out_length = 0;
  buffer->in_pos = 0;
  buffer->out_pos = 0;
  buffer->out_string = buffer->in_string;
  buffer->separate_out = FALSE;
B
Behdad Esfahbod 已提交
149
  buffer->max_ligID = 0;
B
Behdad Esfahbod 已提交
150 151
}

152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
HB_Error
hb_buffer_add_glyph( HB_Buffer buffer,
		      FT_UInt   glyph_index,
		      FT_UInt   properties,
		      FT_UInt   cluster )
{
  HB_Error error;
  HB_GlyphItem glyph;
  
  error = hb_buffer_ensure( buffer, buffer->in_length + 1 );
  if ( error )
    return error;

  glyph = &buffer->in_string[buffer->in_length];
  glyph->gindex = glyph_index;
  glyph->properties = properties;
  glyph->cluster = cluster;
  glyph->component = 0;
  glyph->ligID = 0;
  glyph->gproperties = HB_GLYPH_PROPERTIES_UNKNOWN;
  
  buffer->in_length++;

  return HB_Err_Ok;
}

/* HarfBuzz-Internal API */

HB_INTERNAL void
_hb_buffer_clear_output( HB_Buffer buffer )
B
Behdad Esfahbod 已提交
182 183 184 185 186 187 188
{
  buffer->out_length = 0;
  buffer->out_pos = 0;
  buffer->out_string = buffer->in_string;
  buffer->separate_out = FALSE;
}

189 190
HB_INTERNAL HB_Error
_hb_buffer_clear_positions( HB_Buffer buffer )
191 192 193 194 195 196 197 198 199 200 201 202 203 204
{
  if ( !buffer->positions )
    {
      HB_Error error;

      if ( ALLOC_ARRAY( buffer->positions, buffer->allocated, HB_PositionRec ) )
	return error;
    }

  memset (buffer->positions, 0, sizeof (buffer->positions[0]) * buffer->in_length);

  return HB_Err_Ok;
}

205 206
HB_INTERNAL void
_hb_buffer_swap( HB_Buffer buffer )
207 208
{
  HB_GlyphItem tmp_string;
209 210
  int tmp_length;
  int tmp_pos;
211

212
  if ( buffer->separate_out )
213 214 215 216 217 218
    {
      tmp_string = buffer->in_string;
      buffer->in_string = buffer->out_string;
      buffer->out_string = tmp_string;
      buffer->alt_string = buffer->out_string;
    }
219

220
  tmp_length = buffer->in_length;
221
  buffer->in_length = buffer->out_length;
222
  buffer->out_length = tmp_length;
223

224 225 226
  tmp_pos = buffer->in_pos;
  buffer->in_pos = buffer->out_pos;
  buffer->out_pos = tmp_pos;
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247
}

/* The following function copies `num_out' elements from `glyph_data'
   to `buffer->out_string', advancing the in array pointer in the structure
   by `num_in' elements, and the out array pointer by `num_out' elements.
   Finally, it sets the `length' field of `out' equal to
   `pos' of the `out' structure.

   If `component' is 0xFFFF, the component value from buffer->in_pos
   will copied `num_out' times, otherwise `component' itself will
   be used to fill the `component' fields.

   If `ligID' is 0xFFFF, the ligID value from buffer->in_pos
   will copied `num_out' times, otherwise `ligID' itself will
   be used to fill the `ligID' fields.

   The properties for all replacement glyphs are taken
   from the glyph at position `buffer->in_pos'.

   The cluster value for the glyph at position buffer->in_pos is used
   for all replacement glyphs */
248 249
HB_INTERNAL HB_Error
_hb_buffer_add_output_glyphs( HB_Buffer  buffer,
250 251 252 253 254 255
			      FT_UShort  num_in,
			      FT_UShort  num_out,
			      FT_UShort *glyph_data,
			      FT_UShort  component,
			      FT_UShort  ligID )
{
256
  HB_Error  error;
257 258 259 260 261 262 263 264
  FT_UShort i;
  FT_UInt properties;
  FT_UInt cluster;

  error = hb_buffer_ensure( buffer, buffer->out_pos + num_out );
  if ( error )
    return error;

265
  if ( !buffer->separate_out )
266 267 268 269 270 271
    {
      error = hb_buffer_duplicate_out_buffer( buffer );
      if ( error )
	return error;
    }

272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295
  properties = buffer->in_string[buffer->in_pos].properties;
  cluster = buffer->in_string[buffer->in_pos].cluster;
  if ( component == 0xFFFF )
    component = buffer->in_string[buffer->in_pos].component;
  if ( ligID == 0xFFFF )
    ligID = buffer->in_string[buffer->in_pos].ligID;

  for ( i = 0; i < num_out; i++ )
  {
    HB_GlyphItem item = &buffer->out_string[buffer->out_pos + i];

    item->gindex = glyph_data[i];
    item->properties = properties;
    item->cluster = cluster;
    item->component = component;
    item->ligID = ligID;
    item->gproperties = HB_GLYPH_PROPERTIES_UNKNOWN;
  }

  buffer->in_pos  += num_in;
  buffer->out_pos += num_out;

  buffer->out_length = buffer->out_pos;

296
  return HB_Err_Ok;
297 298
}

299 300 301 302 303
HB_INTERNAL HB_Error
_hb_buffer_add_output_glyph( HB_Buffer buffer,	
			     FT_UInt   glyph_index,
			     FT_UShort component,
			     FT_UShort ligID )
304 305 306
{
  FT_UShort glyph_data =  glyph_index;

307
  return _hb_buffer_add_output_glyphs ( buffer, 1, 1,
308 309 310
					&glyph_data, component, ligID );
}

311 312
HB_INTERNAL HB_Error
_hb_buffer_copy_output_glyph ( HB_Buffer buffer )
313
{  
314
  HB_Error  error;
315 316 317 318 319

  error = hb_buffer_ensure( buffer, buffer->out_pos + 1 );
  if ( error )
    return error;
  
320
  if ( buffer->separate_out )
321 322 323 324 325 326
    {
      buffer->out_string[buffer->out_pos] = buffer->in_string[buffer->in_pos];
    }

  buffer->in_pos++;
  buffer->out_pos++;
327 328
  buffer->out_length = buffer->out_pos;

329 330 331
  return HB_Err_Ok;
}

332 333 334 335
HB_INTERNAL HB_Error
_hb_buffer_replace_output_glyph( HB_Buffer buffer,	
				 FT_UInt   glyph_index,
				 FT_Bool   inplace )
336 337 338 339 340 341
{

  HB_Error error;

  if ( inplace )
    {
342
      error = _hb_buffer_copy_output_glyph ( buffer );
343 344 345 346 347 348 349
      if ( error )
	return error;

      buffer->out_string[buffer->out_pos-1].gindex = glyph_index;
    }
  else
    {
350
      return _hb_buffer_add_output_glyph( buffer, glyph_index, 0xFFFF, 0xFFFF );
351 352 353
    }

  return HB_Err_Ok;
354 355
}

356 357
HB_INTERNAL FT_UShort
_hb_buffer_allocate_ligid( HB_Buffer buffer )
358
{
359 360 361 362 363
  buffer->max_ligID++;
  if (HB_UNLIKELY (buffer->max_ligID == 0))
    buffer->max_ligID++;

  return buffer->max_ligID;
364
}