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 10 11 12 13
 *
 * Portions Copyright 1996-2000 by
 * David Turner, Robert Wilhelm, and Werner Lemberg.
 */

#include "harfbuzz-impl.h"
#include "harfbuzz-buffer.h"
#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
 */

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

  if (size > new_allocated)
    {
49
      HB_Error error;
50 51 52 53

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

60
      if ( REALLOC_ARRAY( buffer->in_string, new_allocated, HB_GlyphItemRec ) )
61
	return error;
62

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

	  buffer->out_string = buffer->alt_string;
	}
      else
71 72 73 74 75
        {
	  buffer->out_string = buffer->in_string;

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

      buffer->allocated = new_allocated;
    }

84
  return HB_Err_Ok;
85 86
}

87 88 89 90 91 92 93 94 95 96 97 98 99
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]) );
100
  buffer->separate_out = TRUE;
101 102 103 104 105

  return HB_Err_Ok;
}

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

B
Behdad Esfahbod 已提交
114 115 116 117 118
  /* not these ones */
  buffer->allocated = 0;
  buffer->in_string = NULL;
  buffer->alt_string = NULL;
  buffer->positions = NULL;
119

B
Behdad Esfahbod 已提交
120 121 122 123 124 125 126 127 128 129
  /* these should be reset when reusing buffer */
  buffer->in_length = 0;
  buffer->out_length = 0;
  buffer->in_pos = 0;
  buffer->out_pos = 0;
  buffer->separate_out = FALSE;
  buffer->out_string = buffer->in_string;
  buffer->max_ligID = 0;

  *pbuffer = buffer;
130 131 132

  return HB_Err_Ok;
}
133

B
Behdad Esfahbod 已提交
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
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;
}

void
hb_buffer_clear_output( HB_Buffer buffer )
{
  buffer->out_length = 0;
  buffer->out_pos = 0;
  buffer->out_string = buffer->in_string;
  buffer->separate_out = FALSE;
}

164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
HB_Error
hb_buffer_clear_positions( HB_Buffer buffer )
{
  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;
}

180
void
181 182 183
hb_buffer_swap( HB_Buffer buffer )
{
  HB_GlyphItem tmp_string;
184 185
  int tmp_length;
  int tmp_pos;
186

187
  if ( buffer->separate_out )
188 189 190 191 192 193
    {
      tmp_string = buffer->in_string;
      buffer->in_string = buffer->out_string;
      buffer->out_string = tmp_string;
      buffer->alt_string = buffer->out_string;
    }
194

195
  tmp_length = buffer->in_length;
196
  buffer->in_length = buffer->out_length;
197
  buffer->out_length = tmp_length;
198

199 200 201
  tmp_pos = buffer->in_pos;
  buffer->in_pos = buffer->out_pos;
  buffer->out_pos = tmp_pos;
202 203
}

204
HB_Error
205 206 207 208 209
hb_buffer_add_glyph( HB_Buffer buffer,
		      FT_UInt    glyph_index,
		      FT_UInt    properties,
		      FT_UInt    cluster )
{
210
  HB_Error error;
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
  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++;

227
  return HB_Err_Ok;
228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248
}

/* 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 */
249
HB_Error
250 251 252 253 254 255 256
hb_buffer_add_output_glyphs( HB_Buffer buffer,
			      FT_UShort  num_in,
			      FT_UShort  num_out,
			      FT_UShort *glyph_data,
			      FT_UShort  component,
			      FT_UShort  ligID )
{
257
  HB_Error  error;
258 259 260 261 262 263 264 265
  FT_UShort i;
  FT_UInt properties;
  FT_UInt cluster;

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

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

273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296
  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;

297
  return HB_Err_Ok;
298 299
}

300
HB_Error
301 302 303 304 305 306 307 308 309 310 311
hb_buffer_add_output_glyph( HB_Buffer buffer,	
			     FT_UInt    glyph_index,
			     FT_UShort  component,
			     FT_UShort  ligID )
{
  FT_UShort glyph_data =  glyph_index;

  return hb_buffer_add_output_glyphs ( buffer, 1, 1,
					&glyph_data, component, ligID );
}

312
HB_Error
313 314
hb_buffer_copy_output_glyph ( HB_Buffer buffer )
{  
315
  HB_Error  error;
316 317 318 319 320

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

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

330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354
  return HB_Err_Ok;
}

HB_Error
hb_buffer_replace_output_glyph( HB_Buffer buffer,	
				FT_UInt   glyph_index,
				FT_Bool   inplace )
{

  HB_Error error;

  if ( inplace )
    {
      error = hb_buffer_copy_output_glyph ( buffer );
      if ( error )
	return error;

      buffer->out_string[buffer->out_pos-1].gindex = glyph_index;
    }
  else
    {
      return hb_buffer_add_output_glyph( buffer, glyph_index, 0xFFFF, 0xFFFF );
    }

  return HB_Err_Ok;
355 356 357 358 359
}

FT_UShort
hb_buffer_allocate_ligid( HB_Buffer buffer )
{
360
  return ++buffer->max_ligID;
361
}