xref: /aosp_15_r20/external/harfbuzz_ng/test/api/test-paint.c (revision 2d1272b857b1f7575e6e246373e1cb218663db8a)
1 /*
2  * Copyright © 2022 Matthias Clasen
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 
25 #include "hb-test.h"
26 
27 #include <hb-features.h>
28 #include <hb-ot.h>
29 
30 #ifdef HB_HAS_FREETYPE
31 #include <hb-ft.h>
32 
33 #if (FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) >= 21300
34 #include FT_COLOR_H
35 #endif
36 #endif
37 
38 static inline hb_bool_t
have_ft_colrv1(void)39 have_ft_colrv1 (void)
40 {
41 #if defined(HB_HAS_FREETYPE) && (FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) >= 21300
42   return TRUE;
43 #else
44   return FALSE;
45 #endif
46 }
47 
48 /* Unit tests for hb-paint.h */
49 
50 /* ---- */
51 
52 typedef struct {
53   int level;
54   GString *string;
55 } paint_data_t;
56 
57 static void print (paint_data_t *data, const char *format, ...) G_GNUC_PRINTF (2, 3);
58 
59 static void
print(paint_data_t * data,const char * format,...)60 print (paint_data_t *data,
61        const char *format,
62        ...)
63 {
64   va_list args;
65 
66   g_string_append_printf (data->string, "%*s", 2 * data->level, "");
67 
68   va_start (args, format);
69   g_string_append_vprintf (data->string, format, args);
70   va_end (args);
71 
72   g_string_append (data->string, "\n");
73 }
74 
75 static void
push_transform(hb_paint_funcs_t * funcs HB_UNUSED,void * paint_data,float xx,float yx,float xy,float yy,float dx,float dy,void * user_data HB_UNUSED)76 push_transform (hb_paint_funcs_t *funcs HB_UNUSED,
77                 void *paint_data,
78                 float xx, float yx,
79                 float xy, float yy,
80                 float dx, float dy,
81                 void *user_data HB_UNUSED)
82 {
83   paint_data_t *data = paint_data;
84 
85   print (data, "start transform %.3g %.3g %.3g %.3g %.3g %.3g", xx, yx, xy, yy, dx, dy);
86   data->level++;
87 }
88 
89 static void
pop_transform(hb_paint_funcs_t * funcs HB_UNUSED,void * paint_data,void * user_data HB_UNUSED)90 pop_transform (hb_paint_funcs_t *funcs HB_UNUSED,
91                void *paint_data,
92                void *user_data HB_UNUSED)
93 {
94   paint_data_t *data = paint_data;
95 
96   data->level--;
97   print (data, "end transform");
98 }
99 
100 static hb_bool_t
paint_color_glyph(hb_paint_funcs_t * funcs HB_UNUSED,void * paint_data,hb_codepoint_t glyph,hb_font_t * font HB_UNUSED,void * user_data HB_UNUSED)101 paint_color_glyph (hb_paint_funcs_t *funcs HB_UNUSED,
102                    void *paint_data,
103                    hb_codepoint_t glyph,
104                    hb_font_t *font HB_UNUSED,
105                    void *user_data HB_UNUSED)
106 {
107   paint_data_t *data = paint_data;
108 
109   print (data, "paint color glyph %u; acting as failed", glyph);
110 
111   return FALSE;
112 }
113 
114 static void
push_clip_glyph(hb_paint_funcs_t * funcs HB_UNUSED,void * paint_data,hb_codepoint_t glyph,hb_font_t * font HB_UNUSED,void * user_data HB_UNUSED)115 push_clip_glyph (hb_paint_funcs_t *funcs HB_UNUSED,
116                  void *paint_data,
117                  hb_codepoint_t glyph,
118                  hb_font_t *font HB_UNUSED,
119                  void *user_data HB_UNUSED)
120 {
121   paint_data_t *data = paint_data;
122 
123   print (data, "start clip glyph %u", glyph);
124   data->level++;
125 }
126 
127 static void
push_clip_rectangle(hb_paint_funcs_t * funcs HB_UNUSED,void * paint_data,float xmin,float ymin,float xmax,float ymax,void * user_data HB_UNUSED)128 push_clip_rectangle (hb_paint_funcs_t *funcs HB_UNUSED,
129                      void *paint_data,
130                      float xmin, float ymin, float xmax, float ymax,
131                      void *user_data HB_UNUSED)
132 {
133   paint_data_t *data = paint_data;
134 
135   print (data, "start clip rectangle %.3g %.3g %.3g %.3g", xmin, ymin, xmax, ymax);
136   data->level++;
137 }
138 
139 static void
pop_clip(hb_paint_funcs_t * funcs HB_UNUSED,void * paint_data,void * user_data HB_UNUSED)140 pop_clip (hb_paint_funcs_t *funcs HB_UNUSED,
141           void *paint_data,
142           void *user_data HB_UNUSED)
143 {
144   paint_data_t *data = paint_data;
145 
146   data->level--;
147   print (data, "end clip");
148 }
149 
150 static void
paint_color(hb_paint_funcs_t * funcs HB_UNUSED,void * paint_data,hb_bool_t use_foreground HB_UNUSED,hb_color_t color,void * user_data HB_UNUSED)151 paint_color (hb_paint_funcs_t *funcs HB_UNUSED,
152              void *paint_data,
153              hb_bool_t use_foreground HB_UNUSED,
154              hb_color_t color,
155              void *user_data HB_UNUSED)
156 {
157   paint_data_t *data = paint_data;
158 
159   print (data, "solid %d %d %d %d",
160          hb_color_get_red (color),
161          hb_color_get_green (color),
162          hb_color_get_blue (color),
163          hb_color_get_alpha (color));
164 }
165 
166 static hb_bool_t
paint_image(hb_paint_funcs_t * funcs HB_UNUSED,void * paint_data,hb_blob_t * blob HB_UNUSED,unsigned int width,unsigned int height,hb_tag_t format,float slant,hb_glyph_extents_t * extents,void * user_data HB_UNUSED)167 paint_image (hb_paint_funcs_t *funcs HB_UNUSED,
168              void *paint_data,
169              hb_blob_t *blob HB_UNUSED,
170              unsigned int width,
171              unsigned int height,
172              hb_tag_t format,
173              float slant,
174              hb_glyph_extents_t *extents,
175              void *user_data HB_UNUSED)
176 {
177   paint_data_t *data = paint_data;
178   char buf[5] = { 0, };
179 
180   hb_tag_to_string (format, buf);
181   print (data, "image type %s size %u %u slant %.3g extents %d %d %d %d\n",
182          buf, width, height, slant,
183          extents->x_bearing, extents->y_bearing, extents->width, extents->height);
184 
185   return TRUE;
186 }
187 
188 static void
print_color_line(paint_data_t * data,hb_color_line_t * color_line)189 print_color_line (paint_data_t *data,
190                   hb_color_line_t *color_line)
191 {
192   hb_color_stop_t *stops;
193   unsigned int len;
194 
195   len = hb_color_line_get_color_stops (color_line, 0, NULL, NULL);
196   stops = alloca (len * sizeof (hb_color_stop_t));
197   hb_color_line_get_color_stops (color_line, 0, &len, stops);
198 
199   print (data, "colors %d", hb_color_line_get_extend (color_line));
200   data->level += 1;
201   for (unsigned int i = 0; i < len; i++)
202     print (data, "%.3g %d %d %d %d",
203            stops[i].offset,
204            hb_color_get_red (stops[i].color),
205            hb_color_get_green (stops[i].color),
206            hb_color_get_blue (stops[i].color),
207            hb_color_get_alpha (stops[i].color));
208   data->level -= 1;
209 }
210 
211 static void
paint_linear_gradient(hb_paint_funcs_t * funcs 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)212 paint_linear_gradient (hb_paint_funcs_t *funcs HB_UNUSED,
213                        void *paint_data,
214                        hb_color_line_t *color_line,
215                        float x0, float y0,
216                        float x1, float y1,
217                        float x2, float y2,
218                        void *user_data HB_UNUSED)
219 {
220   paint_data_t *data = paint_data;
221 
222   print (data, "linear gradient");
223   data->level += 1;
224   print (data, "p0 %.3g %.3g", x0, y0);
225   print (data, "p1 %.3g %.3g", x1, y1);
226   print (data, "p2 %.3g %.3g", x2, y2);
227 
228   print_color_line (data, color_line);
229   data->level -= 1;
230 }
231 
232 static void
paint_radial_gradient(hb_paint_funcs_t * funcs 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)233 paint_radial_gradient (hb_paint_funcs_t *funcs HB_UNUSED,
234                        void *paint_data,
235                        hb_color_line_t *color_line,
236                        float x0, float y0, float r0,
237                        float x1, float y1, float r1,
238                        void *user_data HB_UNUSED)
239 {
240   paint_data_t *data = paint_data;
241 
242   print (data, "radial gradient");
243   data->level += 1;
244   print (data, "p0 %.3g %.3g radius %.3g", x0, y0, r0);
245   print (data, "p1 %.3g %.3g radius %.3g", x1, y1, r1);
246 
247   print_color_line (data, color_line);
248   data->level -= 1;
249 }
250 
251 static void
paint_sweep_gradient(hb_paint_funcs_t * funcs HB_UNUSED,void * paint_data,hb_color_line_t * color_line,float cx,float cy,float start_angle,float end_angle,void * user_data HB_UNUSED)252 paint_sweep_gradient (hb_paint_funcs_t *funcs HB_UNUSED,
253                       void *paint_data,
254                       hb_color_line_t *color_line,
255                       float cx, float cy,
256                       float start_angle,
257                       float end_angle,
258                       void *user_data HB_UNUSED)
259 {
260   paint_data_t *data = paint_data;
261 
262   print (data, "sweep gradient");
263   data->level++;
264   print (data, "center %.3g %.3g", cx, cy);
265   print (data, "angles %.3g %.3g", start_angle, end_angle);
266 
267   print_color_line (data, color_line);
268   data->level -= 1;
269 }
270 
271 static void
push_group(hb_paint_funcs_t * funcs HB_UNUSED,void * paint_data,void * user_data HB_UNUSED)272 push_group (hb_paint_funcs_t *funcs HB_UNUSED,
273             void *paint_data,
274             void *user_data HB_UNUSED)
275 {
276   paint_data_t *data = paint_data;
277   print (data, "push group");
278   data->level++;
279 }
280 
281 static void
pop_group(hb_paint_funcs_t * funcs HB_UNUSED,void * paint_data,hb_paint_composite_mode_t mode,void * user_data HB_UNUSED)282 pop_group (hb_paint_funcs_t *funcs HB_UNUSED,
283            void *paint_data,
284            hb_paint_composite_mode_t mode,
285            void *user_data HB_UNUSED)
286 {
287   paint_data_t *data = paint_data;
288   data->level--;
289   print (data, "pop group mode %d", mode);
290 }
291 
292 static hb_paint_funcs_t *
get_test_paint_funcs(void)293 get_test_paint_funcs (void)
294 {
295   static hb_paint_funcs_t *funcs = NULL;
296 
297   if (!funcs)
298   {
299     funcs = hb_paint_funcs_create ();
300 
301     hb_paint_funcs_set_push_transform_func (funcs, push_transform, NULL, NULL);
302     hb_paint_funcs_set_pop_transform_func (funcs, pop_transform, NULL, NULL);
303     hb_paint_funcs_set_color_glyph_func (funcs, paint_color_glyph, NULL, NULL);
304     hb_paint_funcs_set_push_clip_glyph_func (funcs, push_clip_glyph, NULL, NULL);
305     hb_paint_funcs_set_push_clip_rectangle_func (funcs, push_clip_rectangle, NULL, NULL);
306     hb_paint_funcs_set_pop_clip_func (funcs, pop_clip, NULL, NULL);
307     hb_paint_funcs_set_push_group_func (funcs, push_group, NULL, NULL);
308     hb_paint_funcs_set_pop_group_func (funcs, pop_group, NULL, NULL);
309     hb_paint_funcs_set_color_func (funcs, paint_color, NULL, NULL);
310     hb_paint_funcs_set_image_func (funcs, paint_image, NULL, NULL);
311     hb_paint_funcs_set_linear_gradient_func (funcs, paint_linear_gradient, NULL, NULL);
312     hb_paint_funcs_set_radial_gradient_func (funcs, paint_radial_gradient, NULL, NULL);
313     hb_paint_funcs_set_sweep_gradient_func (funcs, paint_sweep_gradient, NULL, NULL);
314 
315     hb_paint_funcs_make_immutable (funcs);
316   }
317 
318   return funcs;
319 }
320 
321 typedef struct {
322   const char *font_file;
323   float slant;
324   hb_codepoint_t glyph;
325   unsigned int palette;
326   const char *output;
327 } paint_test_t;
328 
329 #define NOTO_HAND   "fonts/noto_handwriting-cff2_colr_1.otf"
330 #define TEST_GLYPHS "fonts/test_glyphs-glyf_colr_1.ttf"
331 #define TEST_GLYPHS_VF "fonts/test_glyphs-glyf_colr_1_variable.ttf"
332 #define BAD_COLRV1  "fonts/bad_colrv1.ttf"
333 #define ROCHER_ABC  "fonts/RocherColorGX.abc.ttf"
334 
335 /* To verify the rendering visually, use
336  *
337  * hb-view --font-slant SLANT --font-palette PALETTE FONT --glyphs [gidGID=0+1000]
338  *
339  * where GID is the glyph value of the test.
340  */
341 static paint_test_t paint_tests[] = {
342   /* COLRv1 */
343   { NOTO_HAND,   0.,  10,   0, "hand-10" },
344   { NOTO_HAND,   0.2f,10,   0, "hand-10.2" },
345 
346   { TEST_GLYPHS, 0,    6,   0, "test-6" },   // linear gradient
347   { TEST_GLYPHS, 0,   10,   0, "test-10" },  // sweep gradient
348   { TEST_GLYPHS, 0,   92,   0, "test-92" },  // radial gradient
349   { TEST_GLYPHS, 0,  106,   0, "test-106" },
350   { TEST_GLYPHS, 0,  116,   0, "test-116" }, // compositing
351   { TEST_GLYPHS, 0,  123,   0, "test-123" },
352   { TEST_GLYPHS, 0,  154,   0, "test-154" },
353   { TEST_GLYPHS, 0,  165,   0, "test-165" }, // linear gradient
354   { TEST_GLYPHS, 0,  175,   0, "test-175" }, // layers
355 
356   { TEST_GLYPHS_VF, 0,    6,   0, "testvf-6" },
357   { TEST_GLYPHS_VF, 0,   10,   0, "testvf-10" },
358   { TEST_GLYPHS_VF, 0,   92,   0, "testvf-92" },
359   { TEST_GLYPHS_VF, 0,  106,   0, "testvf-106" },
360   { TEST_GLYPHS_VF, 0,  116,   0, "testvf-116" },
361   { TEST_GLYPHS_VF, 0,  123,   0, "testvf-123" },
362   { TEST_GLYPHS_VF, 0,  154,   0, "testvf-154" },
363   { TEST_GLYPHS_VF, 0,  165,   0, "testvf-165" },
364   { TEST_GLYPHS_VF, 0,  175,   0, "testvf-175" },
365 
366   { BAD_COLRV1,  0,  154,   0, "bad-154" },  // recursion
367 
368   /* COLRv0 */
369   { ROCHER_ABC, 0.3f, 1,   0, "rocher-1" },
370   { ROCHER_ABC, 0.3f, 2,   2, "rocher-2" },
371   { ROCHER_ABC, 0,    3, 200, "rocher-3" },
372 };
373 
374 static void
test_hb_paint(gconstpointer d,hb_bool_t use_ft HB_UNUSED)375 test_hb_paint (gconstpointer d,
376                hb_bool_t     use_ft HB_UNUSED)
377 {
378   const paint_test_t *test = d;
379   hb_face_t *face;
380   hb_font_t *font;
381   hb_paint_funcs_t *funcs;
382   paint_data_t data;
383   char *file;
384   char *buffer;
385   gsize len;
386   GError *error = NULL;
387 
388   face = hb_test_open_font_file (test->font_file);
389   font = hb_font_create (face);
390 
391   hb_font_set_synthetic_slant (font, test->slant);
392 
393 #ifdef HB_HAS_FREETYPE
394   if (use_ft)
395     hb_ft_font_set_funcs (font);
396 #endif
397 
398   funcs = get_test_paint_funcs ();
399 
400   data.string = g_string_new ("");
401   data.level = 0;
402 
403   hb_font_paint_glyph (font, test->glyph, funcs, &data, 0, HB_COLOR (0, 0, 0, 255));
404 
405   /* Run
406    *
407    * GENERATE_DATA=1 G_TEST_SRCDIR=./test/api ./build/test/api/test-paint -p TESTCASE > test/api/results/OUTPUT
408    *
409    * to produce the expected results file.
410    */
411   if (getenv ("GENERATE_DATA"))
412     {
413       g_print ("%s", data.string->str);
414       exit (0);
415     }
416 
417   file = g_test_build_filename (G_TEST_DIST, "results", test->output, NULL);
418   if (!g_file_get_contents (file, &buffer, &len, &error))
419   {
420     g_test_message ("File %s not found.", file);
421     g_test_fail ();
422     return;
423   }
424 
425   char **lines = g_strsplit (data.string->str, "\n", 0);
426   char **expected;
427   if (strstr (buffer, "\r\n"))
428     expected = g_strsplit (buffer, "\r\n", 0);
429   else
430     expected = g_strsplit (buffer, "\n", 0);
431 
432   /* Strip initial comments */
433   int i;
434   for (i = 0; expected[i]; i++)
435     {
436       if (expected[i][0] != '#')
437         {
438           if (i > 0)
439             {
440               char **tmp = g_strdupv (expected + i);
441               g_strfreev (expected);
442               expected = tmp;
443             }
444           break;
445         }
446     }
447 
448   if (g_strv_length (lines) != g_strv_length (expected))
449   {
450     g_test_message ("Unexpected number of lines in output (%d instead of %d):\n%s", g_strv_length (lines), g_strv_length (expected), data.string->str);
451     g_test_fail ();
452   }
453   else
454   {
455     unsigned int length = g_strv_length (lines);
456     for (unsigned int i = 0; i < length; i++)
457     {
458       if (strcmp (lines[i], expected[i]) != 0)
459       {
460         int pos;
461         for (pos = 0; lines[i][pos]; pos++)
462           if (lines[i][pos] != expected[i][pos])
463             break;
464 
465         g_test_message ("Unexpected output at %d:%d (%c instead of %c):\n%s", i, pos, lines[i][pos], expected[i][pos], data.string->str);
466         g_test_fail ();
467       }
468     }
469   }
470 
471   g_strfreev (lines);
472   g_strfreev (expected);
473 
474   g_free (buffer);
475   g_free (file);
476 
477   g_string_free (data.string, TRUE);
478 
479   hb_font_destroy (font);
480   hb_face_destroy (face);
481 }
482 
483 static void
test_compare_ot_ft(const char * file,hb_codepoint_t glyph)484 test_compare_ot_ft (const char *file, hb_codepoint_t glyph)
485 {
486   hb_face_t *face;
487   hb_font_t *font;
488   hb_paint_funcs_t *funcs;
489   GString *ot_str;
490   paint_data_t data;
491 
492   face = hb_test_open_font_file (file);
493   font = hb_font_create (face);
494 
495   funcs = get_test_paint_funcs ();
496 
497   data.string = g_string_new ("");
498   data.level = 0;
499 
500   hb_font_paint_glyph (font, glyph, funcs, &data, 0, HB_COLOR (0, 0, 0, 255));
501 
502   g_assert_true (data.level == 0);
503 
504   ot_str = data.string;
505 
506 #ifdef HB_HAS_FREETYPE
507   hb_ft_font_set_funcs (font);
508 #endif
509 
510   data.string = g_string_new ("");
511   data.level = 0;
512 
513   hb_font_paint_glyph (font, glyph, funcs, &data, 0, HB_COLOR (0, 0, 0, 255));
514 
515   g_assert_true (data.level == 0);
516 
517   g_assert_cmpstr (ot_str->str, ==, data.string->str);
518 
519   g_string_free (data.string, TRUE);
520   hb_font_destroy (font);
521   hb_face_destroy (face);
522 
523   g_string_free (ot_str, TRUE);
524 }
525 
526 static void
test_hb_paint_ot(gconstpointer data)527 test_hb_paint_ot (gconstpointer data)
528 {
529   test_hb_paint (data, 0);
530 }
531 
532 static void
test_hb_paint_ft(gconstpointer data)533 test_hb_paint_ft (gconstpointer data)
534 {
535   if (have_ft_colrv1 ())
536     test_hb_paint (data, 1);
537   else
538     g_test_skip ("FreeType COLRv1 support not present");
539 }
540 
541 static void
test_compare_ot_ft_novf(gconstpointer d)542 test_compare_ot_ft_novf (gconstpointer d)
543 {
544   if (have_ft_colrv1 ())
545     test_compare_ot_ft (TEST_GLYPHS, GPOINTER_TO_UINT (d));
546   else
547     g_test_skip ("FreeType COLRv1 support not present");
548 }
549 
550 static void
test_compare_ot_ft_vf(gconstpointer d)551 test_compare_ot_ft_vf (gconstpointer d)
552 {
553   if (have_ft_colrv1 ())
554     test_compare_ot_ft (TEST_GLYPHS_VF, GPOINTER_TO_UINT (d));
555   else
556     g_test_skip ("FreeType COLRv1 support not present");
557 }
558 
559 static void
scrutinize_linear_gradient(hb_paint_funcs_t * funcs HB_UNUSED,void * paint_data,hb_color_line_t * color_line,float x0 HB_UNUSED,float y0 HB_UNUSED,float x1 HB_UNUSED,float y1 HB_UNUSED,float x2 HB_UNUSED,float y2 HB_UNUSED,void * user_data HB_UNUSED)560 scrutinize_linear_gradient (hb_paint_funcs_t *funcs HB_UNUSED,
561                             void *paint_data,
562                             hb_color_line_t *color_line,
563                             float x0 HB_UNUSED, float y0 HB_UNUSED,
564                             float x1 HB_UNUSED, float y1 HB_UNUSED,
565                             float x2 HB_UNUSED, float y2 HB_UNUSED,
566                             void *user_data HB_UNUSED)
567 {
568   hb_bool_t *result = paint_data;
569   hb_color_stop_t *stops;
570   unsigned int len;
571   hb_color_stop_t *stops2;
572   unsigned int len2;
573 
574   *result = FALSE;
575 
576   len = hb_color_line_get_color_stops (color_line, 0, NULL, NULL);
577   if (len == 0)
578     return;
579 
580   stops = malloc (len * sizeof (hb_color_stop_t));
581   stops2 = malloc (len * sizeof (hb_color_stop_t));
582 
583   hb_color_line_get_color_stops (color_line, 0, &len, stops);
584   hb_color_line_get_color_stops (color_line, 0, &len, stops2);
585 
586   // check that we can get stops twice
587   if (memcmp (stops, stops2, len * sizeof (hb_color_stop_t)) != 0)
588   {
589     free (stops);
590     free (stops2);
591     return;
592   }
593 
594   // check that we can get a single stop in the middle
595   len2 = 1;
596   hb_color_line_get_color_stops (color_line, len - 1, &len2, stops2);
597   if (memcmp (&stops[len - 1], stops2, sizeof (hb_color_stop_t)) != 0)
598   {
599     free (stops);
600     free (stops2);
601     return;
602   }
603 
604   free (stops);
605   free (stops2);
606 
607   *result = TRUE;
608 }
609 
610 static void
test_color_stops(hb_bool_t use_ft HB_UNUSED)611 test_color_stops (hb_bool_t use_ft HB_UNUSED)
612 {
613   hb_face_t *face;
614   hb_font_t *font;
615   hb_paint_funcs_t *funcs;
616   hb_bool_t result = FALSE;
617 
618   face = hb_test_open_font_file (NOTO_HAND);
619   font = hb_font_create (face);
620 
621 #ifdef HB_HAS_FREETYPE
622   if (use_ft)
623     hb_ft_font_set_funcs (font);
624 #endif
625 
626   funcs = hb_paint_funcs_create ();
627   hb_paint_funcs_set_linear_gradient_func (funcs, scrutinize_linear_gradient, NULL, NULL);
628 
629   hb_font_paint_glyph (font, 10, funcs, &result, 0, HB_COLOR (0, 0, 0, 255));
630 
631   g_assert_true (result);
632 
633   hb_paint_funcs_destroy (funcs);
634   hb_font_destroy (font);
635   hb_face_destroy (face);
636 }
637 
638 static void
test_color_stops_ot(void)639 test_color_stops_ot (void)
640 {
641   test_color_stops (0);
642 }
643 
644 static void
test_color_stops_ft(void)645 test_color_stops_ft (void)
646 {
647   if (have_ft_colrv1 ())
648     test_color_stops (1);
649   else
650     g_test_skip ("FreeType COLRv1 support not present");
651 }
652 
653 int
main(int argc,char ** argv)654 main (int argc, char **argv)
655 {
656   int status = 0;
657 
658   hb_test_init (&argc, &argv);
659   for (unsigned int i = 0; i < G_N_ELEMENTS (paint_tests); i++)
660   {
661     hb_test_add_data_flavor (&paint_tests[i], paint_tests[i].output, test_hb_paint_ot);
662     hb_test_add_data_flavor (&paint_tests[i], paint_tests[i].output, test_hb_paint_ft);
663   }
664 
665   hb_face_t *face = hb_test_open_font_file (TEST_GLYPHS);
666   unsigned glyph_count = hb_face_get_glyph_count (face);
667   for (unsigned int i = 1; i < glyph_count; i++)
668   {
669     char buf[20];
670     snprintf (buf, 20, "test-%u", i);
671     hb_test_add_data_flavor (GUINT_TO_POINTER (i), buf, test_compare_ot_ft_novf);
672     hb_test_add_data_flavor (GUINT_TO_POINTER (i), buf, test_compare_ot_ft_vf);
673   }
674   hb_face_destroy (face);
675 
676   hb_test_add (test_color_stops_ot);
677   hb_test_add (test_color_stops_ft);
678 
679   status = hb_test_run();
680 
681   return status;
682 }
683