xref: /aosp_15_r20/external/harfbuzz_ng/src/hb-cairo.cc (revision 2d1272b857b1f7575e6e246373e1cb218663db8a)
1 /*
2  * Copyright © 2022  Red Hat, Inc.
3  *
4  *  This is part of HarfBuzz, a text shaping library.
5  *
6  * Permission is hereby granted, without written agreement and without
7  * license or royalty fees, to use, copy, modify, and distribute this
8  * software and its documentation for any purpose, provided that the
9  * above copyright notice and the following two paragraphs appear in
10  * all copies of this software.
11  *
12  * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
13  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
14  * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
15  * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
16  * DAMAGE.
17  *
18  * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
19  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
21  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
22  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
23  *
24  * Red Hat Author(s): Matthias Clasen
25  */
26 
27 #include "hb.hh"
28 
29 #ifdef HAVE_CAIRO
30 
31 #include "hb-cairo.h"
32 
33 #include "hb-cairo-utils.hh"
34 
35 #include "hb-machinery.hh"
36 #include "hb-utf.hh"
37 
38 
39 /**
40  * SECTION:hb-cairo
41  * @title: hb-cairo
42  * @short_description: Cairo integration
43  * @include: hb-cairo.h
44  *
45  * Functions for using HarfBuzz with the cairo library.
46  *
47  * HarfBuzz supports using cairo for rendering.
48  **/
49 
50 static void
hb_cairo_move_to(hb_draw_funcs_t * dfuncs HB_UNUSED,void * draw_data,hb_draw_state_t * st HB_UNUSED,float to_x,float to_y,void * user_data HB_UNUSED)51 hb_cairo_move_to (hb_draw_funcs_t *dfuncs HB_UNUSED,
52 		  void *draw_data,
53 		  hb_draw_state_t *st HB_UNUSED,
54 		  float to_x, float to_y,
55 		  void *user_data HB_UNUSED)
56 {
57   cairo_t *cr = (cairo_t *) draw_data;
58 
59   cairo_move_to (cr, (double) to_x, (double) to_y);
60 }
61 
62 static void
hb_cairo_line_to(hb_draw_funcs_t * dfuncs HB_UNUSED,void * draw_data,hb_draw_state_t * st HB_UNUSED,float to_x,float to_y,void * user_data HB_UNUSED)63 hb_cairo_line_to (hb_draw_funcs_t *dfuncs HB_UNUSED,
64 		  void *draw_data,
65 		  hb_draw_state_t *st HB_UNUSED,
66 		  float to_x, float to_y,
67 		  void *user_data HB_UNUSED)
68 {
69   cairo_t *cr = (cairo_t *) draw_data;
70 
71   cairo_line_to (cr, (double) to_x, (double) to_y);
72 }
73 
74 static void
hb_cairo_cubic_to(hb_draw_funcs_t * dfuncs HB_UNUSED,void * draw_data,hb_draw_state_t * st HB_UNUSED,float control1_x,float control1_y,float control2_x,float control2_y,float to_x,float to_y,void * user_data HB_UNUSED)75 hb_cairo_cubic_to (hb_draw_funcs_t *dfuncs HB_UNUSED,
76 		   void *draw_data,
77 		   hb_draw_state_t *st HB_UNUSED,
78 		   float control1_x, float control1_y,
79 		   float control2_x, float control2_y,
80 		   float to_x, float to_y,
81 		   void *user_data HB_UNUSED)
82 {
83   cairo_t *cr = (cairo_t *) draw_data;
84 
85   cairo_curve_to (cr,
86                   (double) control1_x, (double) control1_y,
87                   (double) control2_x, (double) control2_y,
88                   (double) to_x, (double) to_y);
89 }
90 
91 static void
hb_cairo_close_path(hb_draw_funcs_t * dfuncs HB_UNUSED,void * draw_data,hb_draw_state_t * st HB_UNUSED,void * user_data HB_UNUSED)92 hb_cairo_close_path (hb_draw_funcs_t *dfuncs HB_UNUSED,
93 		     void *draw_data,
94 		     hb_draw_state_t *st HB_UNUSED,
95 		     void *user_data HB_UNUSED)
96 {
97   cairo_t *cr = (cairo_t *) draw_data;
98 
99   cairo_close_path (cr);
100 }
101 
102 static inline void free_static_cairo_draw_funcs ();
103 
104 static struct hb_cairo_draw_funcs_lazy_loader_t : hb_draw_funcs_lazy_loader_t<hb_cairo_draw_funcs_lazy_loader_t>
105 {
createhb_cairo_draw_funcs_lazy_loader_t106   static hb_draw_funcs_t *create ()
107   {
108     hb_draw_funcs_t *funcs = hb_draw_funcs_create ();
109 
110     hb_draw_funcs_set_move_to_func (funcs, hb_cairo_move_to, nullptr, nullptr);
111     hb_draw_funcs_set_line_to_func (funcs, hb_cairo_line_to, nullptr, nullptr);
112     hb_draw_funcs_set_cubic_to_func (funcs, hb_cairo_cubic_to, nullptr, nullptr);
113     hb_draw_funcs_set_close_path_func (funcs, hb_cairo_close_path, nullptr, nullptr);
114 
115     hb_draw_funcs_make_immutable (funcs);
116 
117     hb_atexit (free_static_cairo_draw_funcs);
118 
119     return funcs;
120   }
121 } static_cairo_draw_funcs;
122 
123 static inline
free_static_cairo_draw_funcs()124 void free_static_cairo_draw_funcs ()
125 {
126   static_cairo_draw_funcs.free_instance ();
127 }
128 
129 static hb_draw_funcs_t *
hb_cairo_draw_get_funcs()130 hb_cairo_draw_get_funcs ()
131 {
132   return static_cairo_draw_funcs.get_unconst ();
133 }
134 
135 
136 #ifdef HAVE_CAIRO_USER_FONT_FACE_SET_RENDER_COLOR_GLYPH_FUNC
137 
138 static void
hb_cairo_push_transform(hb_paint_funcs_t * pfuncs HB_UNUSED,void * paint_data,float xx,float yx,float xy,float yy,float dx,float dy,void * user_data HB_UNUSED)139 hb_cairo_push_transform (hb_paint_funcs_t *pfuncs HB_UNUSED,
140 			 void *paint_data,
141 			 float xx, float yx,
142 			 float xy, float yy,
143 			 float dx, float dy,
144 			 void *user_data HB_UNUSED)
145 {
146   hb_cairo_context_t *c = (hb_cairo_context_t *) paint_data;
147   cairo_t *cr = c->cr;
148 
149   cairo_matrix_t m;
150 
151   cairo_save (cr);
152   cairo_matrix_init (&m, (double) xx, (double) yx,
153                          (double) xy, (double) yy,
154                          (double) dx, (double) dy);
155   cairo_transform (cr, &m);
156 }
157 
158 static void
hb_cairo_pop_transform(hb_paint_funcs_t * pfuncs HB_UNUSED,void * paint_data,void * user_data HB_UNUSED)159 hb_cairo_pop_transform (hb_paint_funcs_t *pfuncs HB_UNUSED,
160 		        void *paint_data,
161 		        void *user_data HB_UNUSED)
162 {
163   hb_cairo_context_t *c = (hb_cairo_context_t *) paint_data;
164   cairo_t *cr = c->cr;
165 
166   cairo_restore (cr);
167 }
168 
169 static hb_bool_t
hb_cairo_paint_color_glyph(hb_paint_funcs_t * pfuncs HB_UNUSED,void * paint_data,hb_codepoint_t glyph,hb_font_t * font,void * user_data HB_UNUSED)170 hb_cairo_paint_color_glyph (hb_paint_funcs_t *pfuncs HB_UNUSED,
171 			    void *paint_data,
172 			    hb_codepoint_t glyph,
173 			    hb_font_t *font,
174 			    void *user_data HB_UNUSED)
175 {
176   hb_cairo_context_t *c = (hb_cairo_context_t *) paint_data;
177   cairo_t *cr = c->cr;
178 
179   cairo_save (cr);
180 
181   hb_position_t x_scale, y_scale;
182   hb_font_get_scale (font, &x_scale, &y_scale);
183   cairo_scale (cr, x_scale, y_scale);
184 
185   cairo_glyph_t cairo_glyph = { glyph, 0, 0 };
186   cairo_set_scaled_font (cr, c->scaled_font);
187   cairo_set_font_size (cr, 1);
188   cairo_show_glyphs (cr, &cairo_glyph, 1);
189 
190   cairo_restore (cr);
191 
192   return true;
193 }
194 
195 static void
hb_cairo_push_clip_glyph(hb_paint_funcs_t * pfuncs HB_UNUSED,void * paint_data,hb_codepoint_t glyph,hb_font_t * font,void * user_data HB_UNUSED)196 hb_cairo_push_clip_glyph (hb_paint_funcs_t *pfuncs HB_UNUSED,
197 			  void *paint_data,
198 			  hb_codepoint_t glyph,
199 			  hb_font_t *font,
200 			  void *user_data HB_UNUSED)
201 {
202   hb_cairo_context_t *c = (hb_cairo_context_t *) paint_data;
203   cairo_t *cr = c->cr;
204 
205   cairo_save (cr);
206   cairo_new_path (cr);
207   hb_font_draw_glyph (font, glyph, hb_cairo_draw_get_funcs (), cr);
208   cairo_close_path (cr);
209   cairo_clip (cr);
210 }
211 
212 static void
hb_cairo_push_clip_rectangle(hb_paint_funcs_t * pfuncs HB_UNUSED,void * paint_data,float xmin,float ymin,float xmax,float ymax,void * user_data HB_UNUSED)213 hb_cairo_push_clip_rectangle (hb_paint_funcs_t *pfuncs HB_UNUSED,
214 			      void *paint_data,
215 			      float xmin, float ymin, float xmax, float ymax,
216 			      void *user_data HB_UNUSED)
217 {
218   hb_cairo_context_t *c = (hb_cairo_context_t *) paint_data;
219   cairo_t *cr = c->cr;
220 
221   cairo_save (cr);
222   cairo_rectangle (cr,
223                    (double) xmin, (double) ymin,
224                    (double) (xmax - xmin), (double) (ymax - ymin));
225   cairo_clip (cr);
226 }
227 
228 static void
hb_cairo_pop_clip(hb_paint_funcs_t * pfuncs HB_UNUSED,void * paint_data,void * user_data HB_UNUSED)229 hb_cairo_pop_clip (hb_paint_funcs_t *pfuncs HB_UNUSED,
230 		   void *paint_data,
231 		   void *user_data HB_UNUSED)
232 {
233   hb_cairo_context_t *c = (hb_cairo_context_t *) paint_data;
234   cairo_t *cr = c->cr;
235 
236   cairo_restore (cr);
237 }
238 
239 static void
hb_cairo_push_group(hb_paint_funcs_t * pfuncs HB_UNUSED,void * paint_data,void * user_data HB_UNUSED)240 hb_cairo_push_group (hb_paint_funcs_t *pfuncs HB_UNUSED,
241 		     void *paint_data,
242 		     void *user_data HB_UNUSED)
243 {
244   hb_cairo_context_t *c = (hb_cairo_context_t *) paint_data;
245   cairo_t *cr = c->cr;
246 
247   cairo_save (cr);
248   cairo_push_group (cr);
249 }
250 
251 static void
hb_cairo_pop_group(hb_paint_funcs_t * pfuncs HB_UNUSED,void * paint_data,hb_paint_composite_mode_t mode,void * user_data HB_UNUSED)252 hb_cairo_pop_group (hb_paint_funcs_t *pfuncs HB_UNUSED,
253 		    void *paint_data,
254 		    hb_paint_composite_mode_t mode,
255 		    void *user_data HB_UNUSED)
256 {
257   hb_cairo_context_t *c = (hb_cairo_context_t *) paint_data;
258   cairo_t *cr = c->cr;
259 
260   cairo_pop_group_to_source (cr);
261   cairo_set_operator (cr, _hb_paint_composite_mode_to_cairo (mode));
262   cairo_paint (cr);
263 
264   cairo_restore (cr);
265 }
266 
267 static void
hb_cairo_paint_color(hb_paint_funcs_t * pfuncs HB_UNUSED,void * paint_data,hb_bool_t use_foreground,hb_color_t color,void * user_data HB_UNUSED)268 hb_cairo_paint_color (hb_paint_funcs_t *pfuncs HB_UNUSED,
269 		      void *paint_data,
270 		      hb_bool_t use_foreground,
271 		      hb_color_t color,
272 		      void *user_data HB_UNUSED)
273 {
274   hb_cairo_context_t *c = (hb_cairo_context_t *) paint_data;
275   cairo_t *cr = c->cr;
276 
277   if (use_foreground)
278   {
279 #ifdef HAVE_CAIRO_USER_SCALED_FONT_GET_FOREGROUND_SOURCE
280     double r, g, b, a;
281     cairo_pattern_t *foreground = cairo_user_scaled_font_get_foreground_source (c->scaled_font);
282     if (cairo_pattern_get_rgba (foreground, &r, &g, &b, &a) == CAIRO_STATUS_SUCCESS)
283       cairo_set_source_rgba (cr, r, g, b, a * hb_color_get_alpha (color) / 255.);
284     else
285 #endif
286       cairo_set_source_rgba (cr, 0, 0, 0, hb_color_get_alpha (color) / 255.);
287   }
288   else
289     cairo_set_source_rgba (cr,
290 			   hb_color_get_red (color) / 255.,
291 			   hb_color_get_green (color) / 255.,
292 			   hb_color_get_blue (color) / 255.,
293 			   hb_color_get_alpha (color) / 255.);
294   cairo_paint (cr);
295 }
296 
297 static hb_bool_t
hb_cairo_paint_image(hb_paint_funcs_t * pfuncs HB_UNUSED,void * paint_data,hb_blob_t * blob,unsigned width,unsigned height,hb_tag_t format,float slant,hb_glyph_extents_t * extents,void * user_data HB_UNUSED)298 hb_cairo_paint_image (hb_paint_funcs_t *pfuncs HB_UNUSED,
299 		      void *paint_data,
300 		      hb_blob_t *blob,
301 		      unsigned width,
302 		      unsigned height,
303 		      hb_tag_t format,
304 		      float slant,
305 		      hb_glyph_extents_t *extents,
306 		      void *user_data HB_UNUSED)
307 {
308   hb_cairo_context_t *c = (hb_cairo_context_t *) paint_data;
309 
310   return _hb_cairo_paint_glyph_image (c, blob, width, height, format, slant, extents);
311 }
312 
313 static void
hb_cairo_paint_linear_gradient(hb_paint_funcs_t * pfuncs HB_UNUSED,void * paint_data,hb_color_line_t * color_line,float x0,float y0,float x1,float y1,float x2,float y2,void * user_data HB_UNUSED)314 hb_cairo_paint_linear_gradient (hb_paint_funcs_t *pfuncs HB_UNUSED,
315 				void *paint_data,
316 				hb_color_line_t *color_line,
317 				float x0, float y0,
318 				float x1, float y1,
319 				float x2, float y2,
320 				void *user_data HB_UNUSED)
321 {
322   hb_cairo_context_t *c = (hb_cairo_context_t *) paint_data;
323 
324   _hb_cairo_paint_linear_gradient (c, color_line, x0, y0, x1, y1, x2, y2);
325 }
326 
327 static void
hb_cairo_paint_radial_gradient(hb_paint_funcs_t * pfuncs HB_UNUSED,void * paint_data,hb_color_line_t * color_line,float x0,float y0,float r0,float x1,float y1,float r1,void * user_data HB_UNUSED)328 hb_cairo_paint_radial_gradient (hb_paint_funcs_t *pfuncs HB_UNUSED,
329 				void *paint_data,
330 				hb_color_line_t *color_line,
331 				float x0, float y0, float r0,
332 				float x1, float y1, float r1,
333 				void *user_data HB_UNUSED)
334 {
335   hb_cairo_context_t *c = (hb_cairo_context_t *) paint_data;
336 
337   _hb_cairo_paint_radial_gradient (c, color_line, x0, y0, r0, x1, y1, r1);
338 }
339 
340 static void
hb_cairo_paint_sweep_gradient(hb_paint_funcs_t * pfuncs HB_UNUSED,void * paint_data,hb_color_line_t * color_line,float x0,float y0,float start_angle,float end_angle,void * user_data HB_UNUSED)341 hb_cairo_paint_sweep_gradient (hb_paint_funcs_t *pfuncs HB_UNUSED,
342 			       void *paint_data,
343 			       hb_color_line_t *color_line,
344 			       float x0, float y0,
345 			       float start_angle, float end_angle,
346 			       void *user_data HB_UNUSED)
347 {
348   hb_cairo_context_t *c = (hb_cairo_context_t *) paint_data;
349 
350   _hb_cairo_paint_sweep_gradient (c, color_line, x0, y0, start_angle, end_angle);
351 }
352 
353 static const cairo_user_data_key_t color_cache_key = {0};
354 
355 static void
_hb_cairo_destroy_map(void * p)356 _hb_cairo_destroy_map (void *p)
357 {
358   hb_map_destroy ((hb_map_t *) p);
359 }
360 
361 static hb_bool_t
hb_cairo_paint_custom_palette_color(hb_paint_funcs_t * funcs,void * paint_data,unsigned int color_index,hb_color_t * color,void * user_data HB_UNUSED)362 hb_cairo_paint_custom_palette_color (hb_paint_funcs_t *funcs,
363                                      void *paint_data,
364                                      unsigned int color_index,
365                                      hb_color_t *color,
366                                      void *user_data HB_UNUSED)
367 {
368 #ifdef HAVE_CAIRO_FONT_OPTIONS_GET_CUSTOM_PALETTE_COLOR
369   hb_cairo_context_t *c = (hb_cairo_context_t *) paint_data;
370   cairo_t *cr = c->cr;
371 
372 #define HB_DEADBEEF HB_TAG(0xDE,0xAD,0xBE,0xEF)
373 
374   hb_map_t *color_cache = c->color_cache;
375   hb_codepoint_t *v;
376   if (likely (color_cache && color_cache->has (color_index, &v)))
377   {
378     if (*v == HB_DEADBEEF)
379       return false;
380     *color = *v;
381     return true;
382   }
383 
384   cairo_font_options_t *options;
385   double red, green, blue, alpha;
386 
387   options = cairo_font_options_create ();
388   cairo_get_font_options (cr, options);
389   if (CAIRO_STATUS_SUCCESS ==
390       cairo_font_options_get_custom_palette_color (options, color_index,
391                                                    &red, &green, &blue, &alpha))
392   {
393     cairo_font_options_destroy (options);
394     *color = HB_COLOR (round (255 * blue),
395 		       round (255 * green),
396 		       round (255 * red),
397 		       round (255 * alpha));
398 
399     if (likely (color_cache && *color != HB_DEADBEEF))
400       color_cache->set (color_index, *color);
401 
402     return true;
403   }
404   cairo_font_options_destroy (options);
405 
406   if (likely (color_cache))
407     color_cache->set (color_index, HB_DEADBEEF);
408 
409 #undef HB_DEADBEEF
410 
411 #endif
412 
413   return false;
414 }
415 
416 static inline void free_static_cairo_paint_funcs ();
417 
418 static struct hb_cairo_paint_funcs_lazy_loader_t : hb_paint_funcs_lazy_loader_t<hb_cairo_paint_funcs_lazy_loader_t>
419 {
createhb_cairo_paint_funcs_lazy_loader_t420   static hb_paint_funcs_t *create ()
421   {
422     hb_paint_funcs_t *funcs = hb_paint_funcs_create ();
423 
424     hb_paint_funcs_set_push_transform_func (funcs, hb_cairo_push_transform, nullptr, nullptr);
425     hb_paint_funcs_set_pop_transform_func (funcs, hb_cairo_pop_transform, nullptr, nullptr);
426     hb_paint_funcs_set_color_glyph_func (funcs, hb_cairo_paint_color_glyph, nullptr, nullptr);
427     hb_paint_funcs_set_push_clip_glyph_func (funcs, hb_cairo_push_clip_glyph, nullptr, nullptr);
428     hb_paint_funcs_set_push_clip_rectangle_func (funcs, hb_cairo_push_clip_rectangle, nullptr, nullptr);
429     hb_paint_funcs_set_pop_clip_func (funcs, hb_cairo_pop_clip, nullptr, nullptr);
430     hb_paint_funcs_set_push_group_func (funcs, hb_cairo_push_group, nullptr, nullptr);
431     hb_paint_funcs_set_pop_group_func (funcs, hb_cairo_pop_group, nullptr, nullptr);
432     hb_paint_funcs_set_color_func (funcs, hb_cairo_paint_color, nullptr, nullptr);
433     hb_paint_funcs_set_image_func (funcs, hb_cairo_paint_image, nullptr, nullptr);
434     hb_paint_funcs_set_linear_gradient_func (funcs, hb_cairo_paint_linear_gradient, nullptr, nullptr);
435     hb_paint_funcs_set_radial_gradient_func (funcs, hb_cairo_paint_radial_gradient, nullptr, nullptr);
436     hb_paint_funcs_set_sweep_gradient_func (funcs, hb_cairo_paint_sweep_gradient, nullptr, nullptr);
437     hb_paint_funcs_set_custom_palette_color_func (funcs, hb_cairo_paint_custom_palette_color, nullptr, nullptr);
438 
439     hb_paint_funcs_make_immutable (funcs);
440 
441     hb_atexit (free_static_cairo_paint_funcs);
442 
443     return funcs;
444   }
445 } static_cairo_paint_funcs;
446 
447 static inline
free_static_cairo_paint_funcs()448 void free_static_cairo_paint_funcs ()
449 {
450   static_cairo_paint_funcs.free_instance ();
451 }
452 
453 static hb_paint_funcs_t *
hb_cairo_paint_get_funcs()454 hb_cairo_paint_get_funcs ()
455 {
456   return static_cairo_paint_funcs.get_unconst ();
457 }
458 #endif
459 
460 static const cairo_user_data_key_t hb_cairo_face_user_data_key = {0};
461 static const cairo_user_data_key_t hb_cairo_font_user_data_key = {0};
462 static const cairo_user_data_key_t hb_cairo_font_init_func_user_data_key = {0};
463 static const cairo_user_data_key_t hb_cairo_font_init_user_data_user_data_key = {0};
464 static const cairo_user_data_key_t hb_cairo_scale_factor_user_data_key = {0};
465 
hb_cairo_face_destroy(void * p)466 static void hb_cairo_face_destroy (void *p) { hb_face_destroy ((hb_face_t *) p); }
hb_cairo_font_destroy(void * p)467 static void hb_cairo_font_destroy (void *p) { hb_font_destroy ((hb_font_t *) p); }
468 
469 static cairo_status_t
hb_cairo_init_scaled_font(cairo_scaled_font_t * scaled_font,cairo_t * cr HB_UNUSED,cairo_font_extents_t * extents)470 hb_cairo_init_scaled_font (cairo_scaled_font_t  *scaled_font,
471 			   cairo_t              *cr HB_UNUSED,
472 			   cairo_font_extents_t *extents)
473 {
474   cairo_font_face_t *font_face = cairo_scaled_font_get_font_face (scaled_font);
475 
476   hb_font_t *font = (hb_font_t *) cairo_font_face_get_user_data (font_face,
477 								 &hb_cairo_font_user_data_key);
478 
479   if (!font)
480   {
481     hb_face_t *face = (hb_face_t *) cairo_font_face_get_user_data (font_face,
482 								   &hb_cairo_face_user_data_key);
483     font = hb_font_create (face);
484 
485 #if !defined(HB_NO_VAR) && CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1,16,0)
486     cairo_font_options_t *font_options = cairo_font_options_create ();
487 
488     // Set variations
489     cairo_scaled_font_get_font_options (scaled_font, font_options);
490     const char *variations = cairo_font_options_get_variations (font_options);
491     hb_vector_t<hb_variation_t> vars;
492     const char *p = variations;
493     while (p && *p)
494     {
495       const char *end = strpbrk ((char *) p, ", ");
496       hb_variation_t var;
497       if (hb_variation_from_string (p, end ? end - p : -1, &var))
498 	vars.push (var);
499       p = end ? end + 1 : nullptr;
500     }
501     hb_font_set_variations (font, &vars[0], vars.length);
502 
503     cairo_font_options_destroy (font_options);
504 #endif
505 
506     // Set scale; Note: should NOT set slant, or we'll double-slant.
507     unsigned scale_factor = hb_cairo_font_face_get_scale_factor (font_face);
508     if (scale_factor)
509     {
510       cairo_matrix_t font_matrix;
511       cairo_scaled_font_get_scale_matrix (scaled_font, &font_matrix);
512       hb_font_set_scale (font,
513 			 round (font_matrix.xx * scale_factor),
514 			 round (font_matrix.yy * scale_factor));
515     }
516 
517     auto *init_func = (hb_cairo_font_init_func_t)
518 		      cairo_font_face_get_user_data (font_face,
519 						     &hb_cairo_font_init_func_user_data_key);
520     if (init_func)
521     {
522       void *user_data = cairo_font_face_get_user_data (font_face,
523 						       &hb_cairo_font_init_user_data_user_data_key);
524       font = init_func (font, scaled_font, user_data);
525     }
526 
527     hb_font_make_immutable (font);
528   }
529 
530   cairo_scaled_font_set_user_data (scaled_font,
531 				   &hb_cairo_font_user_data_key,
532 				   (void *) hb_font_reference (font),
533 				   hb_cairo_font_destroy);
534 
535   hb_position_t x_scale, y_scale;
536   hb_font_get_scale (font, &x_scale, &y_scale);
537 
538   hb_font_extents_t hb_extents;
539   hb_font_get_h_extents (font, &hb_extents);
540 
541   extents->ascent  = (double)  hb_extents.ascender  / y_scale;
542   extents->descent = (double) -hb_extents.descender / y_scale;
543   extents->height  = extents->ascent + extents->descent;
544 
545 #ifdef HAVE_CAIRO_USER_FONT_FACE_SET_RENDER_COLOR_GLYPH_FUNC
546   hb_map_t *color_cache = hb_map_create ();
547   if (unlikely (CAIRO_STATUS_SUCCESS != cairo_scaled_font_set_user_data (scaled_font,
548 									 &color_cache_key,
549 									 color_cache,
550 									 _hb_cairo_destroy_map)))
551     hb_map_destroy (color_cache);
552 #endif
553 
554   return CAIRO_STATUS_SUCCESS;
555 }
556 
557 static cairo_status_t
hb_cairo_text_to_glyphs(cairo_scaled_font_t * scaled_font,const char * utf8,int utf8_len,cairo_glyph_t ** glyphs,int * num_glyphs,cairo_text_cluster_t ** clusters,int * num_clusters,cairo_text_cluster_flags_t * cluster_flags)558 hb_cairo_text_to_glyphs (cairo_scaled_font_t        *scaled_font,
559 			 const char	            *utf8,
560 			 int		             utf8_len,
561 			 cairo_glyph_t	           **glyphs,
562 			 int		            *num_glyphs,
563 			 cairo_text_cluster_t      **clusters,
564 			 int		            *num_clusters,
565 			 cairo_text_cluster_flags_t *cluster_flags)
566 {
567   hb_font_t *font = (hb_font_t *) cairo_scaled_font_get_user_data (scaled_font,
568 								   &hb_cairo_font_user_data_key);
569 
570   hb_buffer_t *buffer = hb_buffer_create ();
571   hb_buffer_add_utf8 (buffer, utf8, utf8_len, 0, utf8_len);
572   hb_buffer_guess_segment_properties (buffer);
573   hb_shape (font, buffer, nullptr, 0);
574 
575   hb_cairo_glyphs_from_buffer (buffer,
576 			       true,
577 			       font->x_scale, font->y_scale,
578 			       0., 0.,
579 			       utf8, utf8_len,
580 			       glyphs, (unsigned *) num_glyphs,
581 			       clusters, (unsigned *) num_clusters,
582 			       cluster_flags);
583 
584   hb_buffer_destroy (buffer);
585 
586   return CAIRO_STATUS_SUCCESS;
587 }
588 
589 static cairo_status_t
hb_cairo_render_glyph(cairo_scaled_font_t * scaled_font,unsigned long glyph,cairo_t * cr,cairo_text_extents_t * extents)590 hb_cairo_render_glyph (cairo_scaled_font_t  *scaled_font,
591 		       unsigned long         glyph,
592 		       cairo_t              *cr,
593 		       cairo_text_extents_t *extents)
594 {
595   hb_font_t *font = (hb_font_t *) cairo_scaled_font_get_user_data (scaled_font,
596 								   &hb_cairo_font_user_data_key);
597 
598   hb_position_t x_scale, y_scale;
599   hb_font_get_scale (font, &x_scale, &y_scale);
600   cairo_scale (cr, +1./x_scale, -1./y_scale);
601 
602   hb_font_draw_glyph (font, glyph, hb_cairo_draw_get_funcs (), cr);
603 
604   cairo_fill (cr);
605 
606   return CAIRO_STATUS_SUCCESS;
607 }
608 
609 #ifdef HAVE_CAIRO_USER_FONT_FACE_SET_RENDER_COLOR_GLYPH_FUNC
610 
611 static cairo_status_t
hb_cairo_render_color_glyph(cairo_scaled_font_t * scaled_font,unsigned long glyph,cairo_t * cr,cairo_text_extents_t * extents)612 hb_cairo_render_color_glyph (cairo_scaled_font_t  *scaled_font,
613 			     unsigned long         glyph,
614 			     cairo_t              *cr,
615 			     cairo_text_extents_t *extents)
616 {
617   hb_font_t *font = (hb_font_t *) cairo_scaled_font_get_user_data (scaled_font,
618 								   &hb_cairo_font_user_data_key);
619 
620   unsigned int palette = 0;
621 #ifdef CAIRO_COLOR_PALETTE_DEFAULT
622   cairo_font_options_t *options = cairo_font_options_create ();
623   cairo_scaled_font_get_font_options (scaled_font, options);
624   palette = cairo_font_options_get_color_palette (options);
625   cairo_font_options_destroy (options);
626 #endif
627 
628   hb_color_t color = HB_COLOR (0, 0, 0, 255);
629   hb_position_t x_scale, y_scale;
630   hb_font_get_scale (font, &x_scale, &y_scale);
631   cairo_scale (cr, +1./x_scale, -1./y_scale);
632 
633   hb_cairo_context_t c;
634   c.scaled_font = scaled_font;
635   c.cr = cr;
636   c.color_cache = (hb_map_t *) cairo_scaled_font_get_user_data (scaled_font, &color_cache_key);
637 
638   hb_font_paint_glyph (font, glyph, hb_cairo_paint_get_funcs (), &c, palette, color);
639 
640 
641   return CAIRO_STATUS_SUCCESS;
642 }
643 
644 #endif
645 
646 static cairo_font_face_t *
user_font_face_create(hb_face_t * face)647 user_font_face_create (hb_face_t *face)
648 {
649   cairo_font_face_t *cairo_face;
650 
651   cairo_face = cairo_user_font_face_create ();
652   cairo_user_font_face_set_init_func (cairo_face, hb_cairo_init_scaled_font);
653   cairo_user_font_face_set_text_to_glyphs_func (cairo_face, hb_cairo_text_to_glyphs);
654   cairo_user_font_face_set_render_glyph_func (cairo_face, hb_cairo_render_glyph);
655 #ifdef HAVE_CAIRO_USER_FONT_FACE_SET_RENDER_COLOR_GLYPH_FUNC
656   if (hb_ot_color_has_png (face) || hb_ot_color_has_layers (face) || hb_ot_color_has_paint (face))
657     cairo_user_font_face_set_render_color_glyph_func (cairo_face, hb_cairo_render_color_glyph);
658 #endif
659 
660   if (unlikely (CAIRO_STATUS_SUCCESS != cairo_font_face_set_user_data (cairo_face,
661 								       &hb_cairo_face_user_data_key,
662 								       (void *) hb_face_reference (face),
663 								       hb_cairo_face_destroy)))
664     hb_face_destroy (face);
665 
666   return cairo_face;
667 }
668 
669 /**
670  * hb_cairo_font_face_create_for_font:
671  * @font: a #hb_font_t
672  *
673  * Creates a #cairo_font_face_t for rendering text according
674  * to @font.
675  *
676  * Note that the scale of @font does not affect the rendering,
677  * but the variations and slant that are set on @font do.
678  *
679  * Returns: (transfer full): a newly created #cairo_font_face_t
680  *
681  * Since: 7.0.0
682  */
683 cairo_font_face_t *
hb_cairo_font_face_create_for_font(hb_font_t * font)684 hb_cairo_font_face_create_for_font (hb_font_t *font)
685 {
686   hb_font_make_immutable (font);
687 
688   auto *cairo_face =  user_font_face_create (font->face);
689 
690   if (unlikely (CAIRO_STATUS_SUCCESS != cairo_font_face_set_user_data (cairo_face,
691 								       &hb_cairo_font_user_data_key,
692 								       (void *) hb_font_reference (font),
693 								       hb_cairo_font_destroy)))
694     hb_font_destroy (font);
695 
696   return cairo_face;
697 }
698 
699 /**
700  * hb_cairo_font_face_get_font:
701  * @font_face: a #cairo_font_face_t
702  *
703  * Gets the #hb_font_t that @font_face was created from.
704  *
705  * Returns: (nullable) (transfer none): the #hb_font_t that @font_face was created from
706  *
707  * Since: 7.0.0
708  */
709 hb_font_t *
hb_cairo_font_face_get_font(cairo_font_face_t * font_face)710 hb_cairo_font_face_get_font (cairo_font_face_t *font_face)
711 {
712   return (hb_font_t *) cairo_font_face_get_user_data (font_face,
713 						      &hb_cairo_font_user_data_key);
714 }
715 
716 /**
717  * hb_cairo_font_face_create_for_face:
718  * @face: a #hb_face_t
719  *
720  * Creates a #cairo_font_face_t for rendering text according
721  * to @face.
722  *
723  * Returns: (transfer full): a newly created #cairo_font_face_t
724  *
725  * Since: 7.0.0
726  */
727 cairo_font_face_t *
hb_cairo_font_face_create_for_face(hb_face_t * face)728 hb_cairo_font_face_create_for_face (hb_face_t *face)
729 {
730   hb_face_make_immutable (face);
731 
732   return user_font_face_create (face);
733 }
734 
735 /**
736  * hb_cairo_font_face_get_face:
737  * @font_face: a #cairo_font_face_t
738  *
739  * Gets the #hb_face_t associated with @font_face.
740  *
741  * Returns: (nullable) (transfer none): the #hb_face_t associated with @font_face
742  *
743  * Since: 7.0.0
744  */
745 hb_face_t *
hb_cairo_font_face_get_face(cairo_font_face_t * font_face)746 hb_cairo_font_face_get_face (cairo_font_face_t *font_face)
747 {
748   return (hb_face_t *) cairo_font_face_get_user_data (font_face,
749 						      &hb_cairo_face_user_data_key);
750 }
751 
752 /**
753  * hb_cairo_font_face_set_font_init_func:
754  * @font_face: a #cairo_font_face_t
755  * @func: The virtual method to use
756  * @user_data: user data accompanying the method
757  * @destroy: function to call when @user_data is not needed anymore
758  *
759  * Set the virtual method to be called when a cairo
760  * face created using hb_cairo_font_face_create_for_face()
761  * creates an #hb_font_t for a #cairo_scaled_font_t.
762  *
763  * Since: 7.0.0
764  */
765 void
hb_cairo_font_face_set_font_init_func(cairo_font_face_t * font_face,hb_cairo_font_init_func_t func,void * user_data,hb_destroy_func_t destroy)766 hb_cairo_font_face_set_font_init_func (cairo_font_face_t *font_face,
767 				       hb_cairo_font_init_func_t func,
768 				       void *user_data,
769 				       hb_destroy_func_t destroy)
770 {
771   cairo_font_face_set_user_data (font_face,
772 				 &hb_cairo_font_init_func_user_data_key,
773 				 (void *) func,
774 				 nullptr);
775   if (unlikely (CAIRO_STATUS_SUCCESS != cairo_font_face_set_user_data (font_face,
776 								       &hb_cairo_font_init_user_data_user_data_key,
777 								       (void *) user_data,
778 								       destroy)) && destroy)
779   {
780     destroy (user_data);
781     cairo_font_face_set_user_data (font_face,
782 				   &hb_cairo_font_init_func_user_data_key,
783 				   nullptr,
784 				   nullptr);
785   }
786 }
787 
788 /**
789  * hb_cairo_scaled_font_get_font:
790  * @scaled_font: a #cairo_scaled_font_t
791  *
792  * Gets the #hb_font_t associated with @scaled_font.
793  *
794  * Returns: (nullable) (transfer none): the #hb_font_t associated with @scaled_font
795  *
796  * Since: 7.0.0
797  */
798 hb_font_t *
hb_cairo_scaled_font_get_font(cairo_scaled_font_t * scaled_font)799 hb_cairo_scaled_font_get_font (cairo_scaled_font_t *scaled_font)
800 {
801   return (hb_font_t *) cairo_scaled_font_get_user_data (scaled_font, &hb_cairo_font_user_data_key);
802 }
803 
804 
805 /**
806  * hb_cairo_font_face_set_scale_factor:
807  * @scale_factor: The scale factor to use. See below
808  * @font_face: a #cairo_font_face_t
809  *
810  * Sets the scale factor of the @font_face. Default scale
811  * factor is zero.
812  *
813  * When a #cairo_font_face_t is created from a #hb_face_t using
814  * hb_cairo_font_face_create_for_face(), such face will create
815  * #hb_font_t objects during scaled-font creation.  The scale
816  * factor defines how the scale set on such #hb_font_t objects
817  * relates to the font-matrix (as such font size) of the cairo
818  * scaled-font.
819  *
820  * If the scale-factor is zero (default), then the scale of the
821  * #hb_font_t object will be left at default, which is the UPEM
822  * value of the respective #hb_face_t.
823  *
824  * If the scale-factor is set to non-zero, then the X and Y scale
825  * of the #hb_font_t object will be respectively set to the
826  * @scale_factor times the xx and yy elements of the scale-matrix
827  * of the cairo scaled-font being created.
828  *
829  * When using the hb_cairo_glyphs_from_buffer() API to convert the
830  * HarfBuzz glyph buffer that resulted from shaping with such a #hb_font_t,
831  * if the scale-factor was non-zero, you can pass it directly to
832  * that API as both X and Y scale factors.
833  *
834  * If the scale-factor was zero however, or the cairo face was
835  * created using the alternative constructor
836  * hb_cairo_font_face_create_for_font(), you need to calculate the
837  * correct X/Y scale-factors to pass to hb_cairo_glyphs_from_buffer()
838  * by dividing the #hb_font_t X/Y scale-factors by the
839  * cairo scaled-font's scale-matrix XX/YY components respectively
840  * and use those values.  Or if you know that relationship offhand
841  * (because you set the scale of the #hb_font_t yourself), use
842  * the conversion rate involved.
843  *
844  * Since: 7.0.0
845  */
846 void
hb_cairo_font_face_set_scale_factor(cairo_font_face_t * font_face,unsigned int scale_factor)847 hb_cairo_font_face_set_scale_factor (cairo_font_face_t *font_face,
848 				     unsigned int scale_factor)
849 {
850   cairo_font_face_set_user_data (font_face,
851 				 &hb_cairo_scale_factor_user_data_key,
852 				 (void *) (uintptr_t) scale_factor,
853 				 nullptr);
854 }
855 
856 /**
857  * hb_cairo_font_face_get_scale_factor:
858  * @font_face: a #cairo_font_face_t
859  *
860  * Gets the scale factor set on the @font_face. Defaults to zero.
861  * See hb_cairo_font_face_set_scale_factor() for details.
862  *
863  * Returns: the scale factor of @font_face
864  *
865  * Since: 7.0.0
866  */
867 unsigned int
hb_cairo_font_face_get_scale_factor(cairo_font_face_t * font_face)868 hb_cairo_font_face_get_scale_factor (cairo_font_face_t *font_face)
869 {
870   return (unsigned int) (uintptr_t)
871 	 cairo_font_face_get_user_data (font_face,
872 					&hb_cairo_scale_factor_user_data_key);
873 }
874 
875 
876 /**
877  * hb_cairo_glyphs_from_buffer:
878  * @buffer: a #hb_buffer_t containing glyphs
879  * @utf8_clusters: `true` if @buffer clusters are in bytes, instead of characters
880  * @x_scale_factor: scale factor to divide #hb_position_t Y values by
881  * @y_scale_factor: scale factor to divide #hb_position_t X values by
882  * @x: X position to place first glyph
883  * @y: Y position to place first glyph
884  * @utf8: (nullable): the text that was shaped in @buffer
885  * @utf8_len: the length of @utf8 in bytes
886  * @glyphs: (out): return location for an array of #cairo_glyph_t
887  * @num_glyphs: (inout): return location for the length of @glyphs
888  * @clusters: (out) (nullable): return location for an array of cluster positions
889  * @num_clusters: (inout) (nullable): return location for the length of @clusters
890  * @cluster_flags: (out) (nullable): return location for cluster flags
891  *
892  * Extracts information from @buffer in a form that can be
893  * passed to cairo_show_text_glyphs() or cairo_show_glyphs().
894  * This API is modeled after cairo_scaled_font_text_to_glyphs() and
895  * cairo_user_scaled_font_text_to_glyphs_func_t.
896  *
897  * The @num_glyphs argument should be preset to the number of glyph entries available
898  * in the @glyphs buffer. If the @glyphs buffer is `NULL`, the value of
899  * @num_glyphs must be zero.  If the provided glyph array is too short for
900  * the conversion (or for convenience), a new glyph array may be allocated
901  * using cairo_glyph_allocate() and placed in @glyphs.  Upon return,
902  * @num_glyphs should contain the number of generated glyphs.  If the value
903  * @glyphs points at has changed after the call, the caller will free the
904  * allocated glyph array using cairo_glyph_free().  The caller will also free
905  * the original value of @glyphs, so this function shouldn't do so.
906  *
907  * If @clusters is not `NULL`, then @num_clusters and @cluster_flags
908  * should not be either, and @utf8 must be provided, and cluster
909  * mapping will be computed. The semantics of how
910  * cluster array allocation works is similar to the glyph array.  That is,
911  * if @clusters initially points to a non-`NULL` value, that array may be used
912  * as a cluster buffer, and @num_clusters points to the number of cluster
913  * entries available there.  If the provided cluster array is too short for
914  * the conversion (or for convenience), a new cluster array may be allocated
915  * using cairo_text_cluster_allocate() and placed in @clusters.  In this case,
916  * the original value of @clusters will still be freed by the caller.  Upon
917  * return, @num_clusters will contain the number of generated clusters.
918  * If the value @clusters points at has changed after the call, the caller
919  * will free the allocated cluster array using cairo_text_cluster_free().
920  *
921  * See hb_cairo_font_face_set_scale_factor() for the details of
922  * the @scale_factor argument.
923  *
924  * The returned @glyphs vector actually has `@num_glyphs + 1` entries in
925  * it and the x,y values of the extra entry at the end add up the advance
926  * x,y of all the glyphs in the @buffer.
927  *
928  * Since: 7.0.0
929  */
930 void
hb_cairo_glyphs_from_buffer(hb_buffer_t * buffer,hb_bool_t utf8_clusters,double x_scale_factor,double y_scale_factor,double x,double y,const char * utf8,int utf8_len,cairo_glyph_t ** glyphs,unsigned int * num_glyphs,cairo_text_cluster_t ** clusters,unsigned int * num_clusters,cairo_text_cluster_flags_t * cluster_flags)931 hb_cairo_glyphs_from_buffer (hb_buffer_t *buffer,
932 			     hb_bool_t utf8_clusters,
933 			     double x_scale_factor,
934 			     double y_scale_factor,
935 			     double x,
936 			     double y,
937 			     const char *utf8,
938 			     int utf8_len,
939 			     cairo_glyph_t **glyphs,
940 			     unsigned int *num_glyphs,
941 			     cairo_text_cluster_t **clusters,
942 			     unsigned int *num_clusters,
943 			     cairo_text_cluster_flags_t *cluster_flags)
944 {
945   if (utf8 && utf8_len < 0)
946     utf8_len = strlen (utf8);
947 
948   unsigned orig_num_glyphs = *num_glyphs;
949   *num_glyphs = hb_buffer_get_length (buffer);
950   hb_glyph_info_t *hb_glyph = hb_buffer_get_glyph_infos (buffer, nullptr);
951   hb_glyph_position_t *hb_position = hb_buffer_get_glyph_positions (buffer, nullptr);
952   if (orig_num_glyphs < *num_glyphs + 1)
953     *glyphs = cairo_glyph_allocate (*num_glyphs + 1);
954 
955   if (clusters && utf8)
956   {
957     unsigned orig_num_clusters = *num_clusters;
958     *num_clusters = *num_glyphs ? 1 : 0;
959     for (unsigned int i = 1; i < *num_glyphs; i++)
960       if (hb_glyph[i].cluster != hb_glyph[i-1].cluster)
961 	(*num_clusters)++;
962     if (orig_num_clusters < *num_clusters)
963       *clusters = cairo_text_cluster_allocate (*num_clusters);
964   }
965 
966   double x_scale = x_scale_factor ? 1. / x_scale_factor : 0.;
967   double y_scale = y_scale_factor ? 1. / y_scale_factor : 0.;
968   hb_position_t hx = 0, hy = 0;
969   int i;
970   for (i = 0; i < (int) *num_glyphs; i++)
971   {
972     (*glyphs)[i].index = hb_glyph[i].codepoint;
973     (*glyphs)[i].x = x + (+hb_position->x_offset + hx) * x_scale;
974     (*glyphs)[i].y = y + (-hb_position->y_offset + hy) * y_scale;
975     hx +=  hb_position->x_advance;
976     hy += -hb_position->y_advance;
977 
978     hb_position++;
979   }
980   (*glyphs)[i].index = -1;
981   (*glyphs)[i].x = round (hx * x_scale);
982   (*glyphs)[i].y = round (hy * y_scale);
983 
984   if (clusters && *num_clusters && utf8)
985   {
986     hb_memset ((void *) *clusters, 0, *num_clusters * sizeof ((*clusters)[0]));
987     hb_bool_t backward = HB_DIRECTION_IS_BACKWARD (hb_buffer_get_direction (buffer));
988     *cluster_flags = backward ? CAIRO_TEXT_CLUSTER_FLAG_BACKWARD : (cairo_text_cluster_flags_t) 0;
989     unsigned int cluster = 0;
990     const char *start = utf8, *end;
991     (*clusters)[cluster].num_glyphs++;
992     if (backward)
993     {
994       for (i = *num_glyphs - 2; i >= 0; i--)
995       {
996 	if (hb_glyph[i].cluster != hb_glyph[i+1].cluster)
997 	{
998 	  assert (hb_glyph[i].cluster > hb_glyph[i+1].cluster);
999 	  if (utf8_clusters)
1000 	    end = start + hb_glyph[i].cluster - hb_glyph[i+1].cluster;
1001 	  else
1002 	    end = (const char *) hb_utf_offset_to_pointer<hb_utf8_t> ((const uint8_t *) start,
1003 								      (signed) (hb_glyph[i].cluster - hb_glyph[i+1].cluster));
1004 	  (*clusters)[cluster].num_bytes = end - start;
1005 	  start = end;
1006 	  cluster++;
1007 	}
1008 	(*clusters)[cluster].num_glyphs++;
1009       }
1010       (*clusters)[cluster].num_bytes = utf8 + utf8_len - start;
1011     }
1012     else
1013     {
1014       for (i = 1; i < (int) *num_glyphs; i++)
1015       {
1016 	if (hb_glyph[i].cluster != hb_glyph[i-1].cluster)
1017 	{
1018 	  assert (hb_glyph[i].cluster > hb_glyph[i-1].cluster);
1019 	  if (utf8_clusters)
1020 	    end = start + hb_glyph[i].cluster - hb_glyph[i-1].cluster;
1021 	  else
1022 	    end = (const char *) hb_utf_offset_to_pointer<hb_utf8_t> ((const uint8_t *) start,
1023 								      (signed) (hb_glyph[i].cluster - hb_glyph[i-1].cluster));
1024 	  (*clusters)[cluster].num_bytes = end - start;
1025 	  start = end;
1026 	  cluster++;
1027 	}
1028 	(*clusters)[cluster].num_glyphs++;
1029       }
1030       (*clusters)[cluster].num_bytes = utf8 + utf8_len - start;
1031     }
1032   }
1033   else if (num_clusters)
1034     *num_clusters = 0;
1035 }
1036 
1037 #endif
1038