hb-buffer.cc revision f9cd1014f8f4d0394b5e0e9eefc1e2af13c59cab
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 "hb-buffer-private.h"
29
30#include <string.h>
31
32/* Here is how the buffer works internally:
33 *
34 * There are two string pointers: in_string and out_string.  They
35 * always have same allocated size, but different length and positions.
36 *
37 * As an optimization, both in_string and out_string may point to the
38 * same piece of memory, which is owned by in_string.  This remains the
39 * case as long as out_length doesn't exceed in_length at any time.
40 * In that case, swap() is no-op and the glyph operations operate mostly
41 * in-place.
42 *
43 * As soon as out_string gets longer than in_string, out_string is moved over
44 * to an alternate buffer (alt_string), and its current contents (out_length
45 * entries) are copied to the alt buffer.  This should all remain transparent
46 * to the user.  swap() then switches in_string and alt_string.  alt_string is
47 * not allocated until its needed, but after that it's grown with in_string
48 * unconditionally.
49 */
50
51/* XXX err handling */
52
53/* Internal API */
54
55static void
56hb_buffer_ensure_separate (hb_buffer_t *buffer, unsigned int size)
57{
58  hb_buffer_ensure (buffer, size);
59  if (buffer->out_string == buffer->in_string)
60  {
61    if (!buffer->alt_string)
62      buffer->alt_string = malloc (buffer->allocated * sizeof (buffer->alt_string[0]));
63
64    buffer->out_string = buffer->alt_string;
65    memcpy (buffer->out_string, buffer->in_string, buffer->out_length * sizeof (buffer->out_string[0]));
66  }
67}
68
69/* Public API */
70
71hb_buffer_t *
72hb_buffer_new (unsigned int allocation_size)
73{
74  hb_buffer_t *buffer;
75
76  buffer = malloc (sizeof (hb_buffer_t));
77  if (HB_UNLIKELY (!buffer))
78    return NULL;
79
80  buffer->allocated = 0;
81  buffer->in_string = NULL;
82  buffer->alt_string = NULL;
83  buffer->positions = NULL;
84
85  hb_buffer_clear (buffer);
86
87  if (allocation_size)
88    hb_buffer_ensure(buffer, allocation_size);
89
90  return buffer;
91}
92
93void
94hb_buffer_free (hb_buffer_t *buffer)
95{
96  free (buffer->in_string);
97  free (buffer->alt_string);
98  free (buffer->positions);
99  free (buffer);
100}
101
102void
103hb_buffer_clear (hb_buffer_t *buffer)
104{
105  buffer->in_length = 0;
106  buffer->out_length = 0;
107  buffer->in_pos = 0;
108  buffer->out_pos = 0;
109  buffer->out_string = buffer->in_string;
110  buffer->max_lig_id = 0;
111}
112
113void
114hb_buffer_ensure (hb_buffer_t *buffer, unsigned int size)
115{
116  unsigned int new_allocated = buffer->allocated;
117
118  if (size > new_allocated)
119  {
120    while (size > new_allocated)
121      new_allocated += (new_allocated >> 1) + 8;
122
123    if (buffer->positions)
124      buffer->positions = realloc (buffer->positions, new_allocated * sizeof (buffer->positions[0]));
125
126    if (buffer->out_string != buffer->in_string)
127    {
128      buffer->in_string = realloc (buffer->in_string, new_allocated * sizeof (buffer->in_string[0]));
129      buffer->alt_string = realloc (buffer->alt_string, new_allocated * sizeof (buffer->alt_string[0]));
130      buffer->out_string = buffer->alt_string;
131    }
132    else
133    {
134      buffer->in_string = realloc (buffer->in_string, new_allocated * sizeof (buffer->in_string[0]));
135      buffer->out_string = buffer->in_string;
136
137      if (buffer->alt_string)
138      {
139	free (buffer->alt_string);
140	buffer->alt_string = NULL;
141      }
142    }
143
144    buffer->allocated = new_allocated;
145  }
146}
147
148void
149hb_buffer_add_glyph (hb_buffer_t    *buffer,
150		     hb_codepoint_t  glyph_index,
151		     unsigned int    properties,
152		     unsigned int    cluster)
153{
154  hb_glyph_info_t *glyph;
155
156  hb_buffer_ensure (buffer, buffer->in_length + 1);
157
158  glyph = &buffer->in_string[buffer->in_length];
159  glyph->gindex = glyph_index;
160  glyph->properties = properties;
161  glyph->cluster = cluster;
162  glyph->component = 0;
163  glyph->ligID = 0;
164  glyph->gproperty = HB_BUFFER_GLYPH_PROPERTIES_UNKNOWN;
165
166  buffer->in_length++;
167}
168
169/* HarfBuzz-Internal API */
170
171HB_INTERNAL void
172_hb_buffer_clear_output (hb_buffer_t *buffer)
173{
174  buffer->out_length = 0;
175  buffer->out_pos = 0;
176  buffer->out_string = buffer->in_string;
177}
178
179HB_INTERNAL void
180_hb_buffer_clear_positions (hb_buffer_t *buffer)
181{
182  _hb_buffer_clear_output (buffer);
183
184  if (HB_UNLIKELY (!buffer->positions))
185  {
186    buffer->positions = calloc (buffer->allocated, sizeof (buffer->positions[0]));
187    return;
188  }
189
190  memset (buffer->positions, 0, sizeof (buffer->positions[0]) * buffer->in_length);
191}
192
193HB_INTERNAL void
194_hb_buffer_swap (hb_buffer_t *buffer)
195{
196  unsigned int tmp;
197
198  if (buffer->out_string != buffer->in_string)
199  {
200    hb_glyph_info_t *tmp_string;
201    tmp_string = buffer->in_string;
202    buffer->in_string = buffer->out_string;
203    buffer->out_string = tmp_string;
204    buffer->alt_string = buffer->out_string;
205  }
206
207  tmp = buffer->in_length;
208  buffer->in_length = buffer->out_length;
209  buffer->out_length = tmp;
210
211  tmp = buffer->in_pos;
212  buffer->in_pos = buffer->out_pos;
213  buffer->out_pos = tmp;
214}
215
216/* The following function copies `num_out' elements from `glyph_data'
217   to `buffer->out_string', advancing the in array pointer in the structure
218   by `num_in' elements, and the out array pointer by `num_out' elements.
219   Finally, it sets the `length' field of `out' equal to
220   `pos' of the `out' structure.
221
222   If `component' is 0xFFFF, the component value from buffer->in_pos
223   will copied `num_out' times, otherwise `component' itself will
224   be used to fill the `component' fields.
225
226   If `ligID' is 0xFFFF, the ligID value from buffer->in_pos
227   will copied `num_out' times, otherwise `ligID' itself will
228   be used to fill the `ligID' fields.
229
230   The properties for all replacement glyphs are taken
231   from the glyph at position `buffer->in_pos'.
232
233   The cluster value for the glyph at position buffer->in_pos is used
234   for all replacement glyphs */
235HB_INTERNAL void
236_hb_buffer_add_output_glyphs (hb_buffer_t *buffer,
237			      unsigned int num_in,
238			      unsigned int num_out,
239			      const uint16_t *glyph_data_be,
240			      unsigned short component,
241			      unsigned short ligID)
242{
243  unsigned int i;
244  unsigned int properties;
245  unsigned int cluster;
246
247  if (buffer->out_string == buffer->in_string &&
248      buffer->out_pos + num_out > buffer->in_pos + num_in)
249  {
250    hb_buffer_ensure_separate (buffer, buffer->out_pos + num_out);
251  }
252
253  properties = buffer->in_string[buffer->in_pos].properties;
254  cluster = buffer->in_string[buffer->in_pos].cluster;
255  if (component == 0xFFFF)
256    component = buffer->in_string[buffer->in_pos].component;
257  if (ligID == 0xFFFF)
258    ligID = buffer->in_string[buffer->in_pos].ligID;
259
260  for (i = 0; i < num_out; i++)
261  {
262    hb_glyph_info_t *info = &buffer->out_string[buffer->out_pos + i];
263    info->gindex = hb_be_uint16_t (glyph_data_be[i]);
264    info->properties = properties;
265    info->cluster = cluster;
266    info->component = component;
267    info->ligID = ligID;
268    info->gproperty = HB_BUFFER_GLYPH_PROPERTIES_UNKNOWN;
269  }
270
271  buffer->in_pos  += num_in;
272  buffer->out_pos += num_out;
273  buffer->out_length = buffer->out_pos;
274}
275
276
277HB_INTERNAL void
278_hb_buffer_add_output_glyph (hb_buffer_t *buffer,
279			     hb_codepoint_t glyph_index,
280			     unsigned short component,
281			     unsigned short ligID)
282{
283  hb_glyph_info_t *info;
284
285  if (buffer->out_string != buffer->in_string)
286  {
287    hb_buffer_ensure (buffer, buffer->out_pos + 1);
288    buffer->out_string[buffer->out_pos] = buffer->in_string[buffer->in_pos];
289  }
290  else if (buffer->out_pos != buffer->in_pos)
291    buffer->out_string[buffer->out_pos] = buffer->in_string[buffer->in_pos];
292
293  info = &buffer->out_string[buffer->out_pos];
294  info->gindex = glyph_index;
295  if (component != 0xFFFF)
296    info->component = component;
297  if (ligID != 0xFFFF)
298    info->ligID = ligID;
299  info->gproperty = HB_BUFFER_GLYPH_PROPERTIES_UNKNOWN;
300
301  buffer->in_pos++;
302  buffer->out_pos++;
303  buffer->out_length = buffer->out_pos;
304}
305
306HB_INTERNAL void
307_hb_buffer_next_glyph (hb_buffer_t *buffer)
308{
309  if (buffer->out_string != buffer->in_string)
310  {
311    hb_buffer_ensure (buffer, buffer->out_pos + 1);
312    buffer->out_string[buffer->out_pos] = buffer->in_string[buffer->in_pos];
313  }
314  else if (buffer->out_pos != buffer->in_pos)
315    buffer->out_string[buffer->out_pos] = buffer->in_string[buffer->in_pos];
316
317  buffer->in_pos++;
318  buffer->out_pos++;
319  buffer->out_length = buffer->out_pos;
320}
321
322HB_INTERNAL void
323_hb_buffer_replace_glyph (hb_buffer_t *buffer,
324			  hb_codepoint_t glyph_index)
325{
326  _hb_buffer_add_output_glyph (buffer, glyph_index, 0xFFFF, 0xFFFF);
327}
328
329HB_INTERNAL unsigned short
330_hb_buffer_allocate_lig_id (hb_buffer_t *buffer)
331{
332  return ++buffer->max_lig_id;
333}
334