harfbuzz-buffer.c 9.6 KB
Newer Older
1 2 3
/*
 * Copyright (C) 1998-2004  David Turner and Werner Lemberg
 * Copyright (C) 2004,2007  Red Hat, Inc.
4
 *
5
 * This is part of HarfBuzz, an OpenType Layout engine library.
6
 *
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
 * Permission is hereby granted, without written agreement and without
 * license or royalty fees, to use, copy, modify, and distribute this
 * software and its documentation for any purpose, provided that the
 * above copyright notice and the following two paragraphs appear in
 * all copies of this software.
 *
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 *
 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 *
 * Red Hat Author(s): Owen Taylor, Behdad Esfahbod
26 27 28
 */

#include "harfbuzz-impl.h"
29
#include "harfbuzz-buffer-private.h"
30 31 32
#include "harfbuzz-gsub-private.h"
#include "harfbuzz-gpos-private.h"

33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
/* 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.
 *
56 57
 * The buffer->separate_out boolean keeps status of whether out_string points
 * to in_string (FALSE) or alt_string (TRUE).
58 59
 */

60 61
/* Internal API */

62
/*static XXX */ HB_Error
63
hb_buffer_ensure( HB_Buffer buffer,
64
		   HB_UInt   size )
65
{
66
  HB_UInt new_allocated = buffer->allocated;
67 68 69

  if (size > new_allocated)
    {
70
      HB_Error error;
71 72 73

      while (size > new_allocated)
	new_allocated += (new_allocated >> 1) + 8;
B
Behdad Esfahbod 已提交
74

75 76 77 78 79 80
      if ( buffer->positions )
        {
	  if ( REALLOC_ARRAY( buffer->positions, new_allocated, HB_PositionRec ) )
	    return error;
	}

81
      if ( REALLOC_ARRAY( buffer->in_string, new_allocated, HB_GlyphItemRec ) )
82
	return error;
83

84 85 86 87 88 89 90 91
      if ( buffer->separate_out )
        {
	  if ( REALLOC_ARRAY( buffer->alt_string, new_allocated, HB_GlyphItemRec ) )
	    return error;

	  buffer->out_string = buffer->alt_string;
	}
      else
92 93 94 95 96
        {
	  buffer->out_string = buffer->in_string;

	  if ( buffer->alt_string )
	    {
97
	      if ( REALLOC_ARRAY( buffer->alt_string, new_allocated, HB_GlyphItemRec ) )
98 99 100
		return error;
	    }
	}
101 102 103 104

      buffer->allocated = new_allocated;
    }

105
  return HB_Err_Ok;
106 107
}

108 109 110 111 112 113 114 115 116 117 118 119 120
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]) );
121
  buffer->separate_out = TRUE;
122 123 124 125

  return HB_Err_Ok;
}

126 127
/* Public API */

128
HB_Error
B
Behdad Esfahbod 已提交
129
hb_buffer_new( HB_Buffer *pbuffer )
130
{
B
Behdad Esfahbod 已提交
131
  HB_Buffer buffer;
132
  HB_Error error;
133
  
B
Behdad Esfahbod 已提交
134
  if ( ALLOC( buffer, sizeof( HB_BufferRec ) ) )
135 136
    return error;

B
Behdad Esfahbod 已提交
137 138 139 140
  buffer->allocated = 0;
  buffer->in_string = NULL;
  buffer->alt_string = NULL;
  buffer->positions = NULL;
141

B
Behdad Esfahbod 已提交
142
  hb_buffer_clear( buffer );
B
Behdad Esfahbod 已提交
143 144

  *pbuffer = buffer;
145 146 147

  return HB_Err_Ok;
}
148

B
Behdad Esfahbod 已提交
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
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 已提交
168
  buffer->max_ligID = 0;
B
Behdad Esfahbod 已提交
169 170
}

171 172
HB_Error
hb_buffer_add_glyph( HB_Buffer buffer,
173 174 175
		      HB_UInt   glyph_index,
		      HB_UInt   properties,
		      HB_UInt   cluster )
176 177 178 179 180 181 182 183 184 185 186 187 188 189
{
  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;
B
Behdad Esfahbod 已提交
190
  glyph->gproperty = HB_GLYPH_PROPERTY_UNKNOWN;
191 192 193 194 195 196 197 198 199 200
  
  buffer->in_length++;

  return HB_Err_Ok;
}

/* HarfBuzz-Internal API */

HB_INTERNAL void
_hb_buffer_clear_output( HB_Buffer buffer )
B
Behdad Esfahbod 已提交
201 202 203 204 205 206 207
{
  buffer->out_length = 0;
  buffer->out_pos = 0;
  buffer->out_string = buffer->in_string;
  buffer->separate_out = FALSE;
}

208 209
HB_INTERNAL HB_Error
_hb_buffer_clear_positions( HB_Buffer buffer )
210 211 212 213 214 215 216 217 218 219 220 221 222 223
{
  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;
}

224 225
HB_INTERNAL void
_hb_buffer_swap( HB_Buffer buffer )
226 227
{
  HB_GlyphItem tmp_string;
228 229
  int tmp_length;
  int tmp_pos;
230

231
  if ( buffer->separate_out )
232 233 234 235 236 237
    {
      tmp_string = buffer->in_string;
      buffer->in_string = buffer->out_string;
      buffer->out_string = tmp_string;
      buffer->alt_string = buffer->out_string;
    }
238

239
  tmp_length = buffer->in_length;
240
  buffer->in_length = buffer->out_length;
241
  buffer->out_length = tmp_length;
242

243 244 245
  tmp_pos = buffer->in_pos;
  buffer->in_pos = buffer->out_pos;
  buffer->out_pos = tmp_pos;
246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266
}

/* 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 */
267 268
HB_INTERNAL HB_Error
_hb_buffer_add_output_glyphs( HB_Buffer  buffer,
269 270 271 272 273
			      HB_UShort  num_in,
			      HB_UShort  num_out,
			      HB_UShort *glyph_data,
			      HB_UShort  component,
			      HB_UShort  ligID )
274
{
275
  HB_Error  error;
276 277 278
  HB_UShort i;
  HB_UInt properties;
  HB_UInt cluster;
279 280 281 282 283

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

284
  if ( !buffer->separate_out )
285 286 287 288 289 290
    {
      error = hb_buffer_duplicate_out_buffer( buffer );
      if ( error )
	return error;
    }

291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306
  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;
B
Behdad Esfahbod 已提交
307
    item->gproperty = HB_GLYPH_PROPERTY_UNKNOWN;
308 309 310 311 312 313 314
  }

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

  buffer->out_length = buffer->out_pos;

315
  return HB_Err_Ok;
316 317
}

318
HB_INTERNAL HB_Error
B
Behdad Esfahbod 已提交
319
_hb_buffer_add_output_glyph( HB_Buffer buffer,
320 321 322
			     HB_UInt   glyph_index,
			     HB_UShort component,
			     HB_UShort ligID )
323
{
324
  HB_UShort glyph_data =  glyph_index;
325

326
  return _hb_buffer_add_output_glyphs ( buffer, 1, 1,
327 328 329
					&glyph_data, component, ligID );
}

330 331
HB_INTERNAL HB_Error
_hb_buffer_copy_output_glyph ( HB_Buffer buffer )
332
{  
333
  HB_Error  error;
334 335 336 337 338

  error = hb_buffer_ensure( buffer, buffer->out_pos + 1 );
  if ( error )
    return error;
  
339
  if ( buffer->separate_out )
340 341 342 343 344 345
    {
      buffer->out_string[buffer->out_pos] = buffer->in_string[buffer->in_pos];
    }

  buffer->in_pos++;
  buffer->out_pos++;
346 347
  buffer->out_length = buffer->out_pos;

348 349 350
  return HB_Err_Ok;
}

351
HB_INTERNAL HB_Error
B
Behdad Esfahbod 已提交
352
_hb_buffer_replace_output_glyph( HB_Buffer buffer,
353 354
				 HB_UInt   glyph_index,
				 HB_Bool   inplace )
355 356 357 358 359 360
{

  HB_Error error;

  if ( inplace )
    {
361
      error = _hb_buffer_copy_output_glyph ( buffer );
362 363 364 365 366 367 368
      if ( error )
	return error;

      buffer->out_string[buffer->out_pos-1].gindex = glyph_index;
    }
  else
    {
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 379 380 381 382
  buffer->max_ligID++;
  if (HB_UNLIKELY (buffer->max_ligID == 0))
    buffer->max_ligID++;

  return buffer->max_ligID;
383
}