Upload tizen 2.0 beta source
[external/pango1.0.git] / pango / pango-ot-buffer.c
1 /* Pango
2  * pango-ot-buffer.c: Buffer of glyphs for shaping/positioning
3  *
4  * Copyright (C) 2004 Red Hat Software
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 #include "config.h"
23
24 #include "pango-ot-private.h"
25 #include "pangofc-private.h"
26 #include "pango-impl-utils.h"
27
28 /* cache a single hb_buffer_t */
29 static hb_buffer_t *cached_buffer = NULL;
30 G_LOCK_DEFINE_STATIC (cached_buffer);
31
32 static hb_buffer_t *
33 acquire_buffer (gboolean *free_buffer)
34 {
35   hb_buffer_t *buffer;
36
37   if (G_LIKELY (G_TRYLOCK (cached_buffer)))
38     {
39       if (G_UNLIKELY (!cached_buffer))
40         cached_buffer = hb_buffer_create (64);
41
42       buffer = cached_buffer;
43       *free_buffer = FALSE;
44     }
45   else
46     {
47       buffer = hb_buffer_create (32);
48       *free_buffer = TRUE;
49     }
50
51   return buffer;
52 }
53
54 static void
55 release_buffer (hb_buffer_t *buffer, gboolean free_buffer)
56 {
57   if (G_LIKELY (!free_buffer) && hb_buffer_get_reference_count (buffer) == 1)
58     {
59       hb_buffer_clear (buffer);
60       G_UNLOCK (cached_buffer);
61     }
62   else
63     hb_buffer_destroy (buffer);
64 }
65
66 /**
67  * pango_ot_buffer_new
68  * @font: a #PangoFcFont
69  *
70  * Creates a new #PangoOTBuffer for the given OpenType font.
71  *
72  * Return value: the newly allocated #PangoOTBuffer, which should
73  *               be freed with pango_ot_buffer_destroy().
74  *
75  * Since: 1.4
76  **/
77 PangoOTBuffer *
78 pango_ot_buffer_new (PangoFcFont *font)
79 {
80   PangoOTBuffer *buffer = g_slice_new (PangoOTBuffer);
81
82   buffer->buffer = acquire_buffer (&buffer->should_free_hb_buffer);
83   buffer->font = g_object_ref (font);
84   buffer->applied_gpos = FALSE;
85   buffer->rtl = FALSE;
86   buffer->zero_width_marks = FALSE;
87
88   return buffer;
89 }
90
91 /**
92  * pango_ot_buffer_destroy
93  * @buffer: a #PangoOTBuffer
94  *
95  * Destroys a #PangoOTBuffer and free all associated memory.
96  *
97  * Since: 1.4
98  **/
99 void
100 pango_ot_buffer_destroy (PangoOTBuffer *buffer)
101 {
102   release_buffer (buffer->buffer, buffer->should_free_hb_buffer);
103   g_object_unref (buffer->font);
104   g_slice_free (PangoOTBuffer, buffer);
105 }
106
107 /**
108  * pango_ot_buffer_clear
109  * @buffer: a #PangoOTBuffer
110  *
111  * Empties a #PangoOTBuffer, make it ready to add glyphs to.
112  *
113  * Since: 1.4
114  **/
115 void
116 pango_ot_buffer_clear (PangoOTBuffer *buffer)
117 {
118   hb_buffer_clear (buffer->buffer);
119   buffer->applied_gpos = FALSE;
120 }
121
122 /**
123  * pango_ot_buffer_add_glyph
124  * @buffer: a #PangoOTBuffer
125  * @glyph: the glyph index to add, like a #PangoGlyph
126  * @properties: the glyph properties
127  * @cluster: the cluster that this glyph belongs to
128  *
129  * Appends a glyph to a #PangoOTBuffer, with @properties identifying which
130  * features should be applied on this glyph.  See pango_ruleset_add_feature().
131  *
132  * Since: 1.4
133  **/
134 void
135 pango_ot_buffer_add_glyph (PangoOTBuffer *buffer,
136                            guint          glyph,
137                            guint          properties,
138                            guint          cluster)
139 {
140   hb_buffer_add_glyph (buffer->buffer,
141                         glyph, properties, cluster);
142 }
143
144 /**
145  * pango_ot_buffer_set_rtl
146  * @buffer: a #PangoOTBuffer
147  * @rtl: %TRUE for right-to-left text
148  *
149  * Sets whether glyphs will be rendered right-to-left.  This setting
150  * is needed for proper horizontal positioning of right-to-left scripts.
151  *
152  * Since: 1.4
153  **/
154 void
155 pango_ot_buffer_set_rtl (PangoOTBuffer *buffer,
156                          gboolean       rtl)
157 {
158   buffer->rtl = rtl != FALSE;
159   hb_buffer_set_direction (buffer->buffer,
160                            buffer->rtl ? HB_DIRECTION_RTL : HB_DIRECTION_LTR);
161 }
162
163 /**
164  * pango_ot_buffer_set_zero_width_marks
165  * @buffer: a #PangoOTBuffer
166  * @zero_width_marks: %TRUE if characters with a mark class should
167  *  be forced to zero width.
168  *
169  * Sets whether characters with a mark class should be forced to zero width.
170  * This setting is needed for proper positioning of Arabic accents,
171  * but will produce incorrect results with standard OpenType Indic
172  * fonts.
173  *
174  * Since: 1.6
175  **/
176 void
177 pango_ot_buffer_set_zero_width_marks (PangoOTBuffer     *buffer,
178                                       gboolean           zero_width_marks)
179 {
180   buffer->zero_width_marks = zero_width_marks != FALSE;
181 }
182
183 /**
184  * pango_ot_buffer_get_glyphs
185  * @buffer: a #PangoOTBuffer
186  * @glyphs: location to store the array of glyphs, or %NULL
187  * @n_glyphs: location to store the number of glyphs, or %NULL
188  *
189  * Gets the glyph array contained in a #PangoOTBuffer.  The glyphs are
190  * owned by the buffer and should not be freed, and are only valid as long
191  * as buffer is not modified.
192  *
193  * Since: 1.4
194  **/
195 void
196 pango_ot_buffer_get_glyphs (const PangoOTBuffer  *buffer,
197                             PangoOTGlyph        **glyphs,
198                             int                  *n_glyphs)
199 {
200   if (glyphs)
201     *glyphs = (PangoOTGlyph *) hb_buffer_get_glyph_infos (buffer->buffer);
202
203   if (n_glyphs)
204     *n_glyphs = hb_buffer_get_len (buffer->buffer);
205 }
206
207 static void
208 apply_gpos_ltr (PangoGlyphString    *glyphs,
209                 hb_glyph_position_t *positions,
210                 gboolean             scale,
211                 double               xscale,
212                 double               yscale,
213                 gboolean             is_hinted)
214 {
215   int i;
216
217   for (i = 0; i < glyphs->num_glyphs; i++)
218     {
219       FT_Pos x_pos = positions[i].x_pos;
220       FT_Pos y_pos = positions[i].y_pos;
221       int back = i;
222       int j;
223       int adjustment;
224
225
226       adjustment = PANGO_UNITS_26_6(positions[i].x_advance);
227
228       if (is_hinted)
229         adjustment = PANGO_UNITS_ROUND (adjustment);
230       if (G_UNLIKELY (scale))
231         adjustment *= xscale;
232
233       if (positions[i].new_advance)
234         glyphs->glyphs[i].geometry.width  = adjustment;
235       else
236         glyphs->glyphs[i].geometry.width += adjustment;
237
238
239       while (positions[back].back != 0)
240         {
241           back  -= positions[back].back;
242           x_pos += positions[back].x_pos;
243           y_pos += positions[back].y_pos;
244         }
245
246       for (j = back; j < i; j++)
247         glyphs->glyphs[i].geometry.x_offset -= glyphs->glyphs[j].geometry.width;
248
249       if (G_UNLIKELY (scale))
250         {
251           glyphs->glyphs[i].geometry.x_offset += xscale * PANGO_UNITS_26_6(x_pos);
252           glyphs->glyphs[i].geometry.y_offset -= yscale * PANGO_UNITS_26_6(y_pos);
253         }
254       else
255         {
256           glyphs->glyphs[i].geometry.x_offset += PANGO_UNITS_26_6(x_pos);
257           glyphs->glyphs[i].geometry.y_offset -= PANGO_UNITS_26_6(y_pos);
258         }
259     }
260 }
261
262 static void
263 apply_gpos_rtl (PangoGlyphString    *glyphs,
264                 hb_glyph_position_t *positions,
265                 gboolean             scale,
266                 double               xscale,
267                 double               yscale,
268                 gboolean             is_hinted)
269 {
270   int i;
271
272   for (i = 0; i < glyphs->num_glyphs; i++)
273     {
274       int i_rev = glyphs->num_glyphs - i - 1;
275       int back_rev = i_rev;
276       int back;
277       FT_Pos x_pos = positions[i_rev].x_pos;
278       FT_Pos y_pos = positions[i_rev].y_pos;
279       int j;
280       int adjustment;
281
282
283       adjustment = PANGO_UNITS_26_6(positions[i_rev].x_advance);
284
285       if (is_hinted)
286         adjustment = PANGO_UNITS_ROUND (adjustment);
287       if (G_UNLIKELY (scale))
288         adjustment *= xscale;
289
290       if (positions[i_rev].new_advance)
291         glyphs->glyphs[i].geometry.width  = adjustment;
292       else
293         glyphs->glyphs[i].geometry.width += adjustment;
294
295
296       while (positions[back_rev].back != 0)
297         {
298           back_rev -= positions[back_rev].back;
299           x_pos += positions[back_rev].x_pos;
300           y_pos += positions[back_rev].y_pos;
301         }
302
303       back = glyphs->num_glyphs - back_rev - 1;
304
305       for (j = i; j < back; j++)
306         glyphs->glyphs[i].geometry.x_offset += glyphs->glyphs[j].geometry.width;
307
308       if (G_UNLIKELY (scale))
309         {
310           glyphs->glyphs[i].geometry.x_offset += xscale * PANGO_UNITS_26_6(x_pos);
311           glyphs->glyphs[i].geometry.y_offset -= yscale * PANGO_UNITS_26_6(y_pos);
312         }
313       else
314         {
315           glyphs->glyphs[i].geometry.x_offset += PANGO_UNITS_26_6(x_pos);
316           glyphs->glyphs[i].geometry.y_offset -= PANGO_UNITS_26_6(y_pos);
317         }
318     }
319 }
320
321 /**
322  * pango_ot_buffer_output
323  * @buffer: a #PangoOTBuffer
324  * @glyphs: a #PangoGlyphString
325  *
326  * Exports the glyphs in a #PangoOTBuffer into a #PangoGlyphString.  This is
327  * typically used after the OpenType layout processing is over, to convert the
328  * resulting glyphs into a generic Pango glyph string.
329  *
330  * Since: 1.4
331  **/
332 void
333 pango_ot_buffer_output (const PangoOTBuffer *buffer,
334                         PangoGlyphString    *glyphs)
335 {
336   FT_Face face;
337   hb_face_t *hb_face;
338   unsigned int i;
339   int last_cluster;
340
341   unsigned int len;
342   PangoOTGlyph *otglyphs;
343   hb_glyph_position_t *positions;
344
345   face = pango_fc_font_lock_face (buffer->font);
346   g_assert (face);
347
348   pango_ot_buffer_get_glyphs (buffer, &otglyphs, (int *) &len);
349
350   /* Copy glyphs into output glyph string */
351   pango_glyph_string_set_size (glyphs, len);
352
353   last_cluster = -1;
354   for (i = 0; i < len; i++)
355     {
356       PangoOTGlyph *otglyph = &otglyphs[i];
357
358       glyphs->glyphs[i].glyph = otglyph->glyph;
359
360       glyphs->log_clusters[i] = otglyph->cluster;
361       if (glyphs->log_clusters[i] != last_cluster)
362         glyphs->glyphs[i].attr.is_cluster_start = 1;
363       else
364         glyphs->glyphs[i].attr.is_cluster_start = 0;
365
366       last_cluster = glyphs->log_clusters[i];
367     }
368
369   hb_face = _pango_ot_info_get_hb_face (pango_ot_info_get (face));
370
371   /* Apply default positioning */
372   for (i = 0; i < (unsigned int)glyphs->num_glyphs; i++)
373     {
374       if (glyphs->glyphs[i].glyph)
375         {
376           PangoRectangle logical_rect;
377
378           if (buffer->zero_width_marks &&
379               hb_ot_layout_get_glyph_class (hb_face, glyphs->glyphs[i].glyph) == HB_OT_LAYOUT_GLYPH_CLASS_MARK)
380             {
381               glyphs->glyphs[i].geometry.width = 0;
382             }
383           else
384             {
385               pango_font_get_glyph_extents ((PangoFont *)buffer->font, glyphs->glyphs[i].glyph, NULL, &logical_rect);
386               glyphs->glyphs[i].geometry.width = logical_rect.width;
387             }
388         }
389       else
390         glyphs->glyphs[i].geometry.width = 0;
391
392       glyphs->glyphs[i].geometry.x_offset = 0;
393       glyphs->glyphs[i].geometry.y_offset = 0;
394     }
395
396   if (buffer->rtl)
397     {
398       /* Swap all glyphs */
399       pango_glyph_string_reverse_range (glyphs, 0, glyphs->num_glyphs);
400     }
401
402   positions = hb_buffer_get_glyph_positions (buffer->buffer);
403   if (buffer->applied_gpos)
404     {
405       gboolean scale = FALSE;
406       double xscale = 1, yscale = 1;
407       PangoFcFontKey *key = _pango_fc_font_get_font_key (buffer->font);
408
409       /* This is a kludge, and dupped in pango_fc_font_kern_glyphs().
410        * Should move the scale factor to PangoFcFont layer. */
411       if (key) {
412         const PangoMatrix *matrix = pango_fc_font_key_get_matrix (key);
413         PangoMatrix identity = PANGO_MATRIX_INIT;
414         if (G_UNLIKELY (matrix && 0 != memcmp (&identity, matrix, 4 * sizeof (double))))
415           {
416             scale = TRUE;
417             pango_matrix_get_font_scale_factors (matrix, &xscale, &yscale);
418             if (xscale) xscale = 1 / xscale;
419             if (yscale) yscale = 1 / yscale;
420           }
421       }
422
423       if (buffer->rtl)
424         apply_gpos_rtl (glyphs, positions, scale, xscale, yscale, buffer->font->is_hinted);
425       else
426         apply_gpos_ltr (glyphs, positions, scale, xscale, yscale, buffer->font->is_hinted);
427     }
428   else
429     {
430       /* FIXME we should only do this if the 'kern' feature was requested */
431       pango_fc_font_kern_glyphs (buffer->font, glyphs);
432     }
433
434   pango_fc_font_unlock_face (buffer->font);
435 }