Bug 1883518 - Remove a bunch of unused ServoBindings.toml entries. r=firefox-style...
[gecko.git] / gfx / harfbuzz / src / hb-ft-colr.hh
blob1afbbbb1836e39f7cfe409b17a0f400829b93493
1 /*
2 * Copyright © 2022 Behdad Esfahbod
4 * This is part of HarfBuzz, a text shaping library.
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.
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.
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.
25 #ifndef HB_FT_COLR_HH
26 #define HB_FT_COLR_HH
28 #include "hb.hh"
30 #include "hb-paint-extents.hh"
32 #include FT_COLOR_H
35 static hb_paint_composite_mode_t
36 _hb_ft_paint_composite_mode (FT_Composite_Mode mode)
38 switch (mode)
40 case FT_COLR_COMPOSITE_CLEAR: return HB_PAINT_COMPOSITE_MODE_CLEAR;
41 case FT_COLR_COMPOSITE_SRC: return HB_PAINT_COMPOSITE_MODE_SRC;
42 case FT_COLR_COMPOSITE_DEST: return HB_PAINT_COMPOSITE_MODE_DEST;
43 case FT_COLR_COMPOSITE_SRC_OVER: return HB_PAINT_COMPOSITE_MODE_SRC_OVER;
44 case FT_COLR_COMPOSITE_DEST_OVER: return HB_PAINT_COMPOSITE_MODE_DEST_OVER;
45 case FT_COLR_COMPOSITE_SRC_IN: return HB_PAINT_COMPOSITE_MODE_SRC_IN;
46 case FT_COLR_COMPOSITE_DEST_IN: return HB_PAINT_COMPOSITE_MODE_DEST_IN;
47 case FT_COLR_COMPOSITE_SRC_OUT: return HB_PAINT_COMPOSITE_MODE_SRC_OUT;
48 case FT_COLR_COMPOSITE_DEST_OUT: return HB_PAINT_COMPOSITE_MODE_DEST_OUT;
49 case FT_COLR_COMPOSITE_SRC_ATOP: return HB_PAINT_COMPOSITE_MODE_SRC_ATOP;
50 case FT_COLR_COMPOSITE_DEST_ATOP: return HB_PAINT_COMPOSITE_MODE_DEST_ATOP;
51 case FT_COLR_COMPOSITE_XOR: return HB_PAINT_COMPOSITE_MODE_XOR;
52 case FT_COLR_COMPOSITE_PLUS: return HB_PAINT_COMPOSITE_MODE_PLUS;
53 case FT_COLR_COMPOSITE_SCREEN: return HB_PAINT_COMPOSITE_MODE_SCREEN;
54 case FT_COLR_COMPOSITE_OVERLAY: return HB_PAINT_COMPOSITE_MODE_OVERLAY;
55 case FT_COLR_COMPOSITE_DARKEN: return HB_PAINT_COMPOSITE_MODE_DARKEN;
56 case FT_COLR_COMPOSITE_LIGHTEN: return HB_PAINT_COMPOSITE_MODE_LIGHTEN;
57 case FT_COLR_COMPOSITE_COLOR_DODGE: return HB_PAINT_COMPOSITE_MODE_COLOR_DODGE;
58 case FT_COLR_COMPOSITE_COLOR_BURN: return HB_PAINT_COMPOSITE_MODE_COLOR_BURN;
59 case FT_COLR_COMPOSITE_HARD_LIGHT: return HB_PAINT_COMPOSITE_MODE_HARD_LIGHT;
60 case FT_COLR_COMPOSITE_SOFT_LIGHT: return HB_PAINT_COMPOSITE_MODE_SOFT_LIGHT;
61 case FT_COLR_COMPOSITE_DIFFERENCE: return HB_PAINT_COMPOSITE_MODE_DIFFERENCE;
62 case FT_COLR_COMPOSITE_EXCLUSION: return HB_PAINT_COMPOSITE_MODE_EXCLUSION;
63 case FT_COLR_COMPOSITE_MULTIPLY: return HB_PAINT_COMPOSITE_MODE_MULTIPLY;
64 case FT_COLR_COMPOSITE_HSL_HUE: return HB_PAINT_COMPOSITE_MODE_HSL_HUE;
65 case FT_COLR_COMPOSITE_HSL_SATURATION: return HB_PAINT_COMPOSITE_MODE_HSL_SATURATION;
66 case FT_COLR_COMPOSITE_HSL_COLOR: return HB_PAINT_COMPOSITE_MODE_HSL_COLOR;
67 case FT_COLR_COMPOSITE_HSL_LUMINOSITY: return HB_PAINT_COMPOSITE_MODE_HSL_LUMINOSITY;
69 case FT_COLR_COMPOSITE_MAX: HB_FALLTHROUGH;
70 default: return HB_PAINT_COMPOSITE_MODE_CLEAR;
74 typedef struct hb_ft_paint_context_t hb_ft_paint_context_t;
76 static void
77 _hb_ft_paint (hb_ft_paint_context_t *c,
78 FT_OpaquePaint opaque_paint);
80 struct hb_ft_paint_context_t
82 hb_ft_paint_context_t (const hb_ft_font_t *ft_font,
83 hb_font_t *font,
84 hb_paint_funcs_t *paint_funcs, void *paint_data,
85 FT_Color *palette,
86 unsigned palette_index,
87 hb_color_t foreground) :
88 ft_font (ft_font), font(font),
89 funcs (paint_funcs), data (paint_data),
90 palette (palette), palette_index (palette_index), foreground (foreground) {}
92 void recurse (FT_OpaquePaint paint)
94 if (unlikely (depth_left <= 0 || edge_count <= 0)) return;
95 depth_left--;
96 edge_count--;
97 _hb_ft_paint (this, paint);
98 depth_left++;
101 const hb_ft_font_t *ft_font;
102 hb_font_t *font;
103 hb_paint_funcs_t *funcs;
104 void *data;
105 FT_Color *palette;
106 unsigned palette_index;
107 hb_color_t foreground;
108 hb_map_t current_glyphs;
109 hb_map_t current_layers;
110 int depth_left = HB_MAX_NESTING_LEVEL;
111 int edge_count = HB_COLRV1_MAX_EDGE_COUNT;
114 static unsigned
115 _hb_ft_color_line_get_color_stops (hb_color_line_t *color_line,
116 void *color_line_data,
117 unsigned int start,
118 unsigned int *count,
119 hb_color_stop_t *color_stops,
120 void *user_data)
122 FT_ColorLine *cl = (FT_ColorLine *) color_line_data;
123 hb_ft_paint_context_t *c = (hb_ft_paint_context_t *) user_data;
125 if (count)
127 FT_ColorStop stop;
128 unsigned wrote = 0;
129 FT_ColorStopIterator iter = cl->color_stop_iterator;
131 if (start >= cl->color_stop_iterator.num_color_stops)
133 *count = 0;
134 return cl->color_stop_iterator.num_color_stops;
137 while (cl->color_stop_iterator.current_color_stop < start)
138 FT_Get_Colorline_Stops(c->ft_font->ft_face,
139 &stop,
140 &cl->color_stop_iterator);
142 while (count && *count &&
143 FT_Get_Colorline_Stops(c->ft_font->ft_face,
144 &stop,
145 &cl->color_stop_iterator))
147 // https://github.com/harfbuzz/harfbuzz/issues/4013
148 if (sizeof stop.stop_offset == 2)
149 color_stops->offset = stop.stop_offset / 16384.f;
150 else
151 color_stops->offset = stop.stop_offset / 65536.f;
153 color_stops->is_foreground = stop.color.palette_index == 0xFFFF;
154 if (color_stops->is_foreground)
155 color_stops->color = HB_COLOR (hb_color_get_blue (c->foreground),
156 hb_color_get_green (c->foreground),
157 hb_color_get_red (c->foreground),
158 (hb_color_get_alpha (c->foreground) * stop.color.alpha) >> 14);
159 else
161 hb_color_t color;
162 if (c->funcs->custom_palette_color (c->data, stop.color.palette_index, &color))
164 color_stops->color = HB_COLOR (hb_color_get_blue (color),
165 hb_color_get_green (color),
166 hb_color_get_red (color),
167 (hb_color_get_alpha (color) * stop.color.alpha) >> 14);
169 else
171 FT_Color ft_color = c->palette[stop.color.palette_index];
172 color_stops->color = HB_COLOR (ft_color.blue,
173 ft_color.green,
174 ft_color.red,
175 (ft_color.alpha * stop.color.alpha) >> 14);
179 color_stops++;
180 wrote++;
183 *count = wrote;
185 // reset the iterator for next time
186 cl->color_stop_iterator = iter;
189 return cl->color_stop_iterator.num_color_stops;
192 static hb_paint_extend_t
193 _hb_ft_color_line_get_extend (hb_color_line_t *color_line,
194 void *color_line_data,
195 void *user_data)
197 FT_ColorLine *c = (FT_ColorLine *) color_line_data;
198 switch (c->extend)
200 default:
201 case FT_COLR_PAINT_EXTEND_PAD: return HB_PAINT_EXTEND_PAD;
202 case FT_COLR_PAINT_EXTEND_REPEAT: return HB_PAINT_EXTEND_REPEAT;
203 case FT_COLR_PAINT_EXTEND_REFLECT: return HB_PAINT_EXTEND_REFLECT;
207 void
208 _hb_ft_paint (hb_ft_paint_context_t *c,
209 FT_OpaquePaint opaque_paint)
211 FT_Face ft_face = c->ft_font->ft_face;
212 FT_COLR_Paint paint;
213 if (!FT_Get_Paint (ft_face, opaque_paint, &paint))
214 return;
216 switch (paint.format)
218 case FT_COLR_PAINTFORMAT_COLR_LAYERS:
220 FT_OpaquePaint other_paint = {0};
221 while (FT_Get_Paint_Layers (ft_face,
222 &paint.u.colr_layers.layer_iterator,
223 &other_paint))
225 unsigned i = paint.u.colr_layers.layer_iterator.layer;
227 if (unlikely (c->current_layers.has (i)))
228 continue;
230 c->current_layers.add (i);
232 c->funcs->push_group (c->data);
233 c->recurse (other_paint);
234 c->funcs->pop_group (c->data, HB_PAINT_COMPOSITE_MODE_SRC_OVER);
236 c->current_layers.del (i);
239 break;
240 case FT_COLR_PAINTFORMAT_SOLID:
242 bool is_foreground = paint.u.solid.color.palette_index == 0xFFFF;
243 hb_color_t color;
244 if (is_foreground)
245 color = HB_COLOR (hb_color_get_blue (c->foreground),
246 hb_color_get_green (c->foreground),
247 hb_color_get_red (c->foreground),
248 (hb_color_get_alpha (c->foreground) * paint.u.solid.color.alpha) >> 14);
249 else
251 if (c->funcs->custom_palette_color (c->data, paint.u.solid.color.palette_index, &color))
253 color = HB_COLOR (hb_color_get_blue (color),
254 hb_color_get_green (color),
255 hb_color_get_red (color),
256 (hb_color_get_alpha (color) * paint.u.solid.color.alpha) >> 14);
258 else
260 FT_Color ft_color = c->palette[paint.u.solid.color.palette_index];
261 color = HB_COLOR (ft_color.blue,
262 ft_color.green,
263 ft_color.red,
264 (ft_color.alpha * paint.u.solid.color.alpha) >> 14);
267 c->funcs->color (c->data, is_foreground, color);
269 break;
270 case FT_COLR_PAINTFORMAT_LINEAR_GRADIENT:
272 hb_color_line_t cl = {
273 &paint.u.linear_gradient.colorline,
274 _hb_ft_color_line_get_color_stops, c,
275 _hb_ft_color_line_get_extend, nullptr
278 c->funcs->linear_gradient (c->data, &cl,
279 paint.u.linear_gradient.p0.x / 65536.f,
280 paint.u.linear_gradient.p0.y / 65536.f,
281 paint.u.linear_gradient.p1.x / 65536.f,
282 paint.u.linear_gradient.p1.y / 65536.f,
283 paint.u.linear_gradient.p2.x / 65536.f,
284 paint.u.linear_gradient.p2.y / 65536.f);
286 break;
287 case FT_COLR_PAINTFORMAT_RADIAL_GRADIENT:
289 hb_color_line_t cl = {
290 &paint.u.linear_gradient.colorline,
291 _hb_ft_color_line_get_color_stops, c,
292 _hb_ft_color_line_get_extend, nullptr
295 c->funcs->radial_gradient (c->data, &cl,
296 paint.u.radial_gradient.c0.x / 65536.f,
297 paint.u.radial_gradient.c0.y / 65536.f,
298 paint.u.radial_gradient.r0 / 65536.f,
299 paint.u.radial_gradient.c1.x / 65536.f,
300 paint.u.radial_gradient.c1.y / 65536.f,
301 paint.u.radial_gradient.r1 / 65536.f);
303 break;
304 case FT_COLR_PAINTFORMAT_SWEEP_GRADIENT:
306 hb_color_line_t cl = {
307 &paint.u.linear_gradient.colorline,
308 _hb_ft_color_line_get_color_stops, c,
309 _hb_ft_color_line_get_extend, nullptr
312 c->funcs->sweep_gradient (c->data, &cl,
313 paint.u.sweep_gradient.center.x / 65536.f,
314 paint.u.sweep_gradient.center.y / 65536.f,
315 (paint.u.sweep_gradient.start_angle / 65536.f + 1) * HB_PI,
316 (paint.u.sweep_gradient.end_angle / 65536.f + 1) * HB_PI);
318 break;
319 case FT_COLR_PAINTFORMAT_GLYPH:
321 c->funcs->push_inverse_root_transform (c->data, c->font);
322 c->ft_font->lock.unlock ();
323 c->funcs->push_clip_glyph (c->data, paint.u.glyph.glyphID, c->font);
324 c->ft_font->lock.lock ();
325 c->funcs->push_root_transform (c->data, c->font);
326 c->recurse (paint.u.glyph.paint);
327 c->funcs->pop_transform (c->data);
328 c->funcs->pop_clip (c->data);
329 c->funcs->pop_transform (c->data);
331 break;
332 case FT_COLR_PAINTFORMAT_COLR_GLYPH:
334 hb_codepoint_t gid = paint.u.colr_glyph.glyphID;
336 if (unlikely (c->current_glyphs.has (gid)))
337 return;
339 c->current_glyphs.add (gid);
341 c->funcs->push_inverse_root_transform (c->data, c->font);
342 c->ft_font->lock.unlock ();
343 if (c->funcs->color_glyph (c->data, gid, c->font))
345 c->ft_font->lock.lock ();
346 c->funcs->pop_transform (c->data);
347 c->current_glyphs.del (gid);
348 return;
350 c->ft_font->lock.lock ();
351 c->funcs->pop_transform (c->data);
353 FT_OpaquePaint other_paint = {0};
354 if (FT_Get_Color_Glyph_Paint (ft_face, gid,
355 FT_COLOR_NO_ROOT_TRANSFORM,
356 &other_paint))
358 bool has_clip_box;
359 FT_ClipBox clip_box;
360 has_clip_box = FT_Get_Color_Glyph_ClipBox (ft_face, paint.u.colr_glyph.glyphID, &clip_box);
362 if (has_clip_box)
364 /* The FreeType ClipBox is in scaled coordinates, whereas we need
365 * unscaled clipbox here. Oh well...
368 float upem = c->font->face->get_upem ();
369 float xscale = upem / (c->font->x_scale ? c->font->x_scale : upem);
370 float yscale = upem / (c->font->y_scale ? c->font->y_scale : upem);
372 c->funcs->push_clip_rectangle (c->data,
373 clip_box.bottom_left.x * xscale,
374 clip_box.bottom_left.y * yscale,
375 clip_box.top_right.x * xscale,
376 clip_box.top_right.y * yscale);
379 c->recurse (other_paint);
381 if (has_clip_box)
382 c->funcs->pop_clip (c->data);
384 c->current_glyphs.del (gid);
387 break;
388 case FT_COLR_PAINTFORMAT_TRANSFORM:
390 c->funcs->push_transform (c->data,
391 paint.u.transform.affine.xx / 65536.f,
392 paint.u.transform.affine.yx / 65536.f,
393 paint.u.transform.affine.xy / 65536.f,
394 paint.u.transform.affine.yy / 65536.f,
395 paint.u.transform.affine.dx / 65536.f,
396 paint.u.transform.affine.dy / 65536.f);
397 c->recurse (paint.u.transform.paint);
398 c->funcs->pop_transform (c->data);
400 break;
401 case FT_COLR_PAINTFORMAT_TRANSLATE:
403 float dx = paint.u.translate.dx / 65536.f;
404 float dy = paint.u.translate.dy / 65536.f;
406 bool p1 = c->funcs->push_translate (c->data, dx, dy);
407 c->recurse (paint.u.translate.paint);
408 if (p1) c->funcs->pop_transform (c->data);
410 break;
411 case FT_COLR_PAINTFORMAT_SCALE:
413 float dx = paint.u.scale.center_x / 65536.f;
414 float dy = paint.u.scale.center_y / 65536.f;
415 float sx = paint.u.scale.scale_x / 65536.f;
416 float sy = paint.u.scale.scale_y / 65536.f;
418 bool p1 = c->funcs->push_translate (c->data, +dx, +dy);
419 bool p2 = c->funcs->push_scale (c->data, sx, sy);
420 bool p3 = c->funcs->push_translate (c->data, -dx, -dy);
421 c->recurse (paint.u.scale.paint);
422 if (p3) c->funcs->pop_transform (c->data);
423 if (p2) c->funcs->pop_transform (c->data);
424 if (p1) c->funcs->pop_transform (c->data);
426 break;
427 case FT_COLR_PAINTFORMAT_ROTATE:
429 float dx = paint.u.rotate.center_x / 65536.f;
430 float dy = paint.u.rotate.center_y / 65536.f;
431 float a = paint.u.rotate.angle / 65536.f;
433 bool p1 = c->funcs->push_translate (c->data, +dx, +dy);
434 bool p2 = c->funcs->push_rotate (c->data, a);
435 bool p3 = c->funcs->push_translate (c->data, -dx, -dy);
436 c->recurse (paint.u.rotate.paint);
437 if (p3) c->funcs->pop_transform (c->data);
438 if (p2) c->funcs->pop_transform (c->data);
439 if (p1) c->funcs->pop_transform (c->data);
441 break;
442 case FT_COLR_PAINTFORMAT_SKEW:
444 float dx = paint.u.skew.center_x / 65536.f;
445 float dy = paint.u.skew.center_y / 65536.f;
446 float sx = paint.u.skew.x_skew_angle / 65536.f;
447 float sy = paint.u.skew.y_skew_angle / 65536.f;
449 bool p1 = c->funcs->push_translate (c->data, +dx, +dy);
450 bool p2 = c->funcs->push_skew (c->data, sx, sy);
451 bool p3 = c->funcs->push_translate (c->data, -dx, -dy);
452 c->recurse (paint.u.skew.paint);
453 if (p3) c->funcs->pop_transform (c->data);
454 if (p2) c->funcs->pop_transform (c->data);
455 if (p1) c->funcs->pop_transform (c->data);
457 break;
458 case FT_COLR_PAINTFORMAT_COMPOSITE:
460 c->recurse (paint.u.composite.backdrop_paint);
461 c->funcs->push_group (c->data);
462 c->recurse (paint.u.composite.source_paint);
463 c->funcs->pop_group (c->data, _hb_ft_paint_composite_mode (paint.u.composite.composite_mode));
465 break;
467 case FT_COLR_PAINT_FORMAT_MAX: break;
468 default: HB_FALLTHROUGH;
469 case FT_COLR_PAINTFORMAT_UNSUPPORTED: break;
474 static bool
475 hb_ft_paint_glyph_colr (hb_font_t *font,
476 void *font_data,
477 hb_codepoint_t gid,
478 hb_paint_funcs_t *paint_funcs, void *paint_data,
479 unsigned int palette_index,
480 hb_color_t foreground,
481 void *user_data)
483 const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data;
484 FT_Face ft_face = ft_font->ft_face;
486 /* Face is locked. */
488 FT_Error error;
489 FT_Color* palette;
490 FT_LayerIterator iterator;
492 FT_Bool have_layers;
493 FT_UInt layer_glyph_index;
494 FT_UInt layer_color_index;
496 error = FT_Palette_Select(ft_face, palette_index, &palette);
497 if (error)
498 palette = NULL;
500 /* COLRv1 */
501 FT_OpaquePaint paint = {0};
502 if (FT_Get_Color_Glyph_Paint (ft_face, gid,
503 FT_COLOR_NO_ROOT_TRANSFORM,
504 &paint))
506 hb_ft_paint_context_t c (ft_font, font,
507 paint_funcs, paint_data,
508 palette, palette_index, foreground);
509 c.current_glyphs.add (gid);
511 bool is_bounded = true;
512 FT_ClipBox clip_box;
513 if (FT_Get_Color_Glyph_ClipBox (ft_face, gid, &clip_box))
515 c.funcs->push_clip_rectangle (c.data,
516 clip_box.bottom_left.x +
517 roundf (hb_min (font->slant_xy * clip_box.bottom_left.y,
518 font->slant_xy * clip_box.top_left.y)),
519 clip_box.bottom_left.y,
520 clip_box.top_right.x +
521 roundf (hb_max (font->slant_xy * clip_box.bottom_right.y,
522 font->slant_xy * clip_box.top_right.y)),
523 clip_box.top_right.y);
525 else
528 auto *extents_funcs = hb_paint_extents_get_funcs ();
529 hb_paint_extents_context_t extents_data;
530 hb_ft_paint_context_t ce (ft_font, font,
531 extents_funcs, &extents_data,
532 palette, palette_index, foreground);
533 ce.current_glyphs.add (gid);
534 ce.funcs->push_root_transform (ce.data, font);
535 ce.recurse (paint);
536 ce.funcs->pop_transform (ce.data);
537 hb_extents_t extents = extents_data.get_extents ();
538 is_bounded = extents_data.is_bounded ();
540 c.funcs->push_clip_rectangle (c.data,
541 extents.xmin,
542 extents.ymin,
543 extents.xmax,
544 extents.ymax);
547 c.funcs->push_root_transform (c.data, font);
549 if (is_bounded)
550 c.recurse (paint);
552 c.funcs->pop_transform (c.data);
553 c.funcs->pop_clip (c.data);
555 return true;
558 /* COLRv0 */
559 iterator.p = NULL;
560 have_layers = FT_Get_Color_Glyph_Layer(ft_face,
561 gid,
562 &layer_glyph_index,
563 &layer_color_index,
564 &iterator);
566 if (palette && have_layers)
570 hb_bool_t is_foreground = true;
571 hb_color_t color = foreground;
573 if ( layer_color_index != 0xFFFF )
575 FT_Color layer_color = palette[layer_color_index];
576 color = HB_COLOR (layer_color.blue,
577 layer_color.green,
578 layer_color.red,
579 layer_color.alpha);
580 is_foreground = false;
583 ft_font->lock.unlock ();
584 paint_funcs->push_clip_glyph (paint_data, layer_glyph_index, font);
585 ft_font->lock.lock ();
586 paint_funcs->color (paint_data, is_foreground, color);
587 paint_funcs->pop_clip (paint_data);
589 } while (FT_Get_Color_Glyph_Layer(ft_face,
590 gid,
591 &layer_glyph_index,
592 &layer_color_index,
593 &iterator));
594 return true;
597 return false;
601 #endif /* HB_FT_COLR_HH */