1/*
2 * Copyright © 2018 Ebrahim Byagowi
3 * Copyright © 2020 Google, Inc.
4 *
5 * This is part of HarfBuzz, a text shaping library.
6 *
7 * Permission is hereby granted, without written agreement and without
8 * license or royalty fees, to use, copy, modify, and distribute this
9 * software and its documentation for any purpose, provided that the
10 * above copyright notice and the following two paragraphs appear in
11 * all copies of this software.
12 *
13 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
14 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
15 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
16 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
17 * DAMAGE.
18 *
19 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
20 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
21 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
22 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
23 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
24 *
25 * Google Author(s): Calder Kitagawa
26 */
27
28#ifndef OT_COLOR_COLR_COLR_HH
29#define OT_COLOR_COLR_COLR_HH
30
31#include "../../../hb.hh"
32#include "../../../hb-open-type.hh"
33#include "../../../hb-ot-var-common.hh"
34#include "../../../hb-paint.hh"
35#include "../../../hb-paint-extents.hh"
36
37/*
38 * COLR -- Color
39 * https://docs.microsoft.com/en-us/typography/opentype/spec/colr
40 */
41#define HB_OT_TAG_COLR HB_TAG('C','O','L','R')
42
43namespace OT {
44struct hb_paint_context_t;
45}
46
47namespace OT {
48
49struct COLR;
50
51struct Paint;
52
53struct hb_paint_context_t :
54 hb_dispatch_context_t<hb_paint_context_t>
55{
56 template <typename T>
57 return_t dispatch (const T &obj) { obj.paint_glyph (this); return hb_empty_t (); }
58 static return_t default_return_value () { return hb_empty_t (); }
59
60 const COLR* get_colr_table () const
61 { return reinterpret_cast<const COLR *> (base); }
62
63public:
64 const void *base;
65 hb_paint_funcs_t *funcs;
66 void *data;
67 hb_font_t *font;
68 unsigned int palette_index;
69 hb_color_t foreground;
70 VarStoreInstancer &instancer;
71 int depth_left = HB_MAX_NESTING_LEVEL;
72 int edge_count = HB_COLRV1_MAX_EDGE_COUNT;
73
74 hb_paint_context_t (const void *base_,
75 hb_paint_funcs_t *funcs_,
76 void *data_,
77 hb_font_t *font_,
78 unsigned int palette_,
79 hb_color_t foreground_,
80 VarStoreInstancer &instancer_) :
81 base (base_),
82 funcs (funcs_),
83 data (data_),
84 font (font_),
85 palette_index (palette_),
86 foreground (foreground_),
87 instancer (instancer_)
88 { }
89
90 hb_color_t get_color (unsigned int color_index, float alpha, hb_bool_t *is_foreground)
91 {
92 hb_color_t color = foreground;
93
94 *is_foreground = true;
95
96 if (color_index != 0xffff)
97 {
98 if (!funcs->custom_palette_color (data, color_index, &color))
99 {
100 unsigned int clen = 1;
101 hb_face_t *face = hb_font_get_face (font);
102
103 hb_ot_color_palette_get_colors (face, palette_index, color_index, &clen, &color);
104 }
105
106 *is_foreground = false;
107 }
108
109 return HB_COLOR (hb_color_get_blue (color),
110 hb_color_get_green (color),
111 hb_color_get_red (color),
112 hb_color_get_alpha (color) * alpha);
113 }
114
115 inline void recurse (const Paint &paint);
116};
117
118struct hb_colrv1_closure_context_t :
119 hb_dispatch_context_t<hb_colrv1_closure_context_t>
120{
121 template <typename T>
122 return_t dispatch (const T &obj)
123 {
124 if (unlikely (nesting_level_left == 0))
125 return hb_empty_t ();
126
127 if (paint_visited (&obj))
128 return hb_empty_t ();
129
130 nesting_level_left--;
131 obj.closurev1 (this);
132 nesting_level_left++;
133 return hb_empty_t ();
134 }
135 static return_t default_return_value () { return hb_empty_t (); }
136
137 bool paint_visited (const void *paint)
138 {
139 hb_codepoint_t delta = (hb_codepoint_t) ((uintptr_t) paint - (uintptr_t) base);
140 if (visited_paint.in_error() || visited_paint.has (delta))
141 return true;
142
143 visited_paint.add (delta);
144 return false;
145 }
146
147 const COLR* get_colr_table () const
148 { return reinterpret_cast<const COLR *> (base); }
149
150 void add_glyph (unsigned glyph_id)
151 { glyphs->add (glyph_id); }
152
153 void add_layer_indices (unsigned first_layer_index, unsigned num_of_layers)
154 { layer_indices->add_range (first_layer_index, first_layer_index + num_of_layers - 1); }
155
156 void add_palette_index (unsigned palette_index)
157 { palette_indices->add (palette_index); }
158
159 public:
160 const void *base;
161 hb_set_t visited_paint;
162 hb_set_t *glyphs;
163 hb_set_t *layer_indices;
164 hb_set_t *palette_indices;
165 unsigned nesting_level_left;
166
167 hb_colrv1_closure_context_t (const void *base_,
168 hb_set_t *glyphs_,
169 hb_set_t *layer_indices_,
170 hb_set_t *palette_indices_,
171 unsigned nesting_level_left_ = HB_MAX_NESTING_LEVEL) :
172 base (base_),
173 glyphs (glyphs_),
174 layer_indices (layer_indices_),
175 palette_indices (palette_indices_),
176 nesting_level_left (nesting_level_left_)
177 {}
178};
179
180struct LayerRecord
181{
182 operator hb_ot_color_layer_t () const { return {glyphId, colorIdx}; }
183
184 bool sanitize (hb_sanitize_context_t *c) const
185 {
186 TRACE_SANITIZE (this);
187 return_trace (c->check_struct (this));
188 }
189
190 public:
191 HBGlyphID16 glyphId; /* Glyph ID of layer glyph */
192 Index colorIdx; /* Index value to use with a
193 * selected color palette.
194 * An index value of 0xFFFF
195 * is a special case indicating
196 * that the text foreground
197 * color (defined by a
198 * higher-level client) should
199 * be used and shall not be
200 * treated as actual index
201 * into CPAL ColorRecord array. */
202 public:
203 DEFINE_SIZE_STATIC (4);
204};
205
206struct BaseGlyphRecord
207{
208 int cmp (hb_codepoint_t g) const
209 { return g < glyphId ? -1 : g > glyphId ? 1 : 0; }
210
211 bool sanitize (hb_sanitize_context_t *c) const
212 {
213 TRACE_SANITIZE (this);
214 return_trace (c->check_struct (this));
215 }
216
217 public:
218 HBGlyphID16 glyphId; /* Glyph ID of reference glyph */
219 HBUINT16 firstLayerIdx; /* Index (from beginning of
220 * the Layer Records) to the
221 * layer record. There will be
222 * numLayers consecutive entries
223 * for this base glyph. */
224 HBUINT16 numLayers; /* Number of color layers
225 * associated with this glyph */
226 public:
227 DEFINE_SIZE_STATIC (6);
228};
229
230template <typename T>
231struct Variable
232{
233 static constexpr bool is_variable = true;
234
235 Variable<T>* copy (hb_serialize_context_t *c) const
236 {
237 TRACE_SERIALIZE (this);
238 return_trace (c->embed (this));
239 }
240
241 void closurev1 (hb_colrv1_closure_context_t* c) const
242 { value.closurev1 (c); }
243
244 bool subset (hb_subset_context_t *c,
245 const VarStoreInstancer &instancer) const
246 {
247 TRACE_SUBSET (this);
248 if (!value.subset (c, instancer, varIdxBase)) return_trace (false);
249 if (c->plan->all_axes_pinned)
250 return_trace (true);
251
252 //TODO: update varIdxBase for partial-instancing
253 return_trace (c->serializer->embed (varIdxBase));
254 }
255
256 bool sanitize (hb_sanitize_context_t *c) const
257 {
258 TRACE_SANITIZE (this);
259 return_trace (c->check_struct (this) && value.sanitize (c));
260 }
261
262 void paint_glyph (hb_paint_context_t *c) const
263 {
264 value.paint_glyph (c, varIdxBase);
265 }
266
267 void get_color_stop (hb_paint_context_t *c,
268 hb_color_stop_t *stop,
269 const VarStoreInstancer &instancer) const
270 {
271 value.get_color_stop (c, stop, varIdxBase, instancer);
272 }
273
274 hb_paint_extend_t get_extend () const
275 {
276 return value.get_extend ();
277 }
278
279 protected:
280 T value;
281 public:
282 VarIdx varIdxBase;
283 public:
284 DEFINE_SIZE_STATIC (4 + T::static_size);
285};
286
287template <typename T>
288struct NoVariable
289{
290 static constexpr bool is_variable = false;
291
292 static constexpr uint32_t varIdxBase = VarIdx::NO_VARIATION;
293
294 NoVariable<T>* copy (hb_serialize_context_t *c) const
295 {
296 TRACE_SERIALIZE (this);
297 return_trace (c->embed (this));
298 }
299
300 void closurev1 (hb_colrv1_closure_context_t* c) const
301 { value.closurev1 (c); }
302
303 bool subset (hb_subset_context_t *c,
304 const VarStoreInstancer &instancer) const
305 {
306 TRACE_SUBSET (this);
307 return_trace (value.subset (c, instancer, varIdxBase));
308 }
309
310 bool sanitize (hb_sanitize_context_t *c) const
311 {
312 TRACE_SANITIZE (this);
313 return_trace (c->check_struct (this) && value.sanitize (c));
314 }
315
316 void paint_glyph (hb_paint_context_t *c) const
317 {
318 value.paint_glyph (c, varIdxBase);
319 }
320
321 void get_color_stop (hb_paint_context_t *c,
322 hb_color_stop_t *stop,
323 const VarStoreInstancer &instancer) const
324 {
325 value.get_color_stop (c, stop, VarIdx::NO_VARIATION, instancer);
326 }
327
328 hb_paint_extend_t get_extend () const
329 {
330 return value.get_extend ();
331 }
332
333 T value;
334 public:
335 DEFINE_SIZE_STATIC (T::static_size);
336};
337
338// Color structures
339
340struct ColorStop
341{
342 void closurev1 (hb_colrv1_closure_context_t* c) const
343 { c->add_palette_index (paletteIndex); }
344
345 bool subset (hb_subset_context_t *c,
346 const VarStoreInstancer &instancer,
347 uint32_t varIdxBase) const
348 {
349 TRACE_SUBSET (this);
350 auto *out = c->serializer->embed (*this);
351 if (unlikely (!out)) return_trace (false);
352
353 if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
354 {
355 out->stopOffset.set_float (stopOffset.to_float(instancer (varIdxBase, 0)));
356 out->alpha.set_float (alpha.to_float (instancer (varIdxBase, 1)));
357 }
358
359 return_trace (c->serializer->check_assign (out->paletteIndex, c->plan->colr_palettes.get (paletteIndex),
360 HB_SERIALIZE_ERROR_INT_OVERFLOW));
361 }
362
363 bool sanitize (hb_sanitize_context_t *c) const
364 {
365 TRACE_SANITIZE (this);
366 return_trace (c->check_struct (this));
367 }
368
369 void get_color_stop (hb_paint_context_t *c,
370 hb_color_stop_t *out,
371 uint32_t varIdx,
372 const VarStoreInstancer &instancer) const
373 {
374 out->offset = stopOffset.to_float(instancer (varIdx, 0));
375 out->color = c->get_color (paletteIndex,
376 alpha.to_float (instancer (varIdx, 1)),
377 &out->is_foreground);
378 }
379
380 F2DOT14 stopOffset;
381 HBUINT16 paletteIndex;
382 F2DOT14 alpha;
383 public:
384 DEFINE_SIZE_STATIC (2 + 2 * F2DOT14::static_size);
385};
386
387struct Extend : HBUINT8
388{
389 enum {
390 EXTEND_PAD = 0,
391 EXTEND_REPEAT = 1,
392 EXTEND_REFLECT = 2,
393 };
394 public:
395 DEFINE_SIZE_STATIC (1);
396};
397
398template <template<typename> class Var>
399struct ColorLine
400{
401 void closurev1 (hb_colrv1_closure_context_t* c) const
402 {
403 for (const auto &stop : stops.iter ())
404 stop.closurev1 (c);
405 }
406
407 bool subset (hb_subset_context_t *c,
408 const VarStoreInstancer &instancer) const
409 {
410 TRACE_SUBSET (this);
411 auto *out = c->serializer->start_embed (this);
412 if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
413
414 if (!c->serializer->check_assign (out->extend, extend, HB_SERIALIZE_ERROR_INT_OVERFLOW)) return_trace (false);
415 if (!c->serializer->check_assign (out->stops.len, stops.len, HB_SERIALIZE_ERROR_ARRAY_OVERFLOW)) return_trace (false);
416
417 for (const auto& stop : stops.iter ())
418 {
419 if (!stop.subset (c, instancer)) return_trace (false);
420 }
421 return_trace (true);
422 }
423
424 bool sanitize (hb_sanitize_context_t *c) const
425 {
426 TRACE_SANITIZE (this);
427 return_trace (c->check_struct (this) &&
428 stops.sanitize (c));
429 }
430
431 /* get up to count stops from start */
432 unsigned int
433 get_color_stops (hb_paint_context_t *c,
434 unsigned int start,
435 unsigned int *count,
436 hb_color_stop_t *color_stops,
437 const VarStoreInstancer &instancer) const
438 {
439 unsigned int len = stops.len;
440
441 if (count && color_stops)
442 {
443 unsigned int i;
444 for (i = 0; i < *count && start + i < len; i++)
445 stops[start + i].get_color_stop (c, &color_stops[i], instancer);
446 *count = i;
447 }
448
449 return len;
450 }
451
452 HB_INTERNAL static unsigned int static_get_color_stops (hb_color_line_t *color_line,
453 void *color_line_data,
454 unsigned int start,
455 unsigned int *count,
456 hb_color_stop_t *color_stops,
457 void *user_data)
458 {
459 const ColorLine *thiz = (const ColorLine *) color_line_data;
460 hb_paint_context_t *c = (hb_paint_context_t *) user_data;
461 return thiz->get_color_stops (c, start, count, color_stops, c->instancer);
462 }
463
464 hb_paint_extend_t get_extend () const
465 {
466 return (hb_paint_extend_t) (unsigned int) extend;
467 }
468
469 HB_INTERNAL static hb_paint_extend_t static_get_extend (hb_color_line_t *color_line,
470 void *color_line_data,
471 void *user_data)
472 {
473 const ColorLine *thiz = (const ColorLine *) color_line_data;
474 return thiz->get_extend ();
475 }
476
477 Extend extend;
478 Array16Of<Var<ColorStop>> stops;
479 public:
480 DEFINE_SIZE_ARRAY_SIZED (3, stops);
481};
482
483// Composition modes
484
485// Compositing modes are taken from https://www.w3.org/TR/compositing-1/
486// NOTE: a brief audit of major implementations suggests most support most
487// or all of the specified modes.
488struct CompositeMode : HBUINT8
489{
490 enum {
491 // Porter-Duff modes
492 // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators
493 COMPOSITE_CLEAR = 0, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_clear
494 COMPOSITE_SRC = 1, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_src
495 COMPOSITE_DEST = 2, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_dst
496 COMPOSITE_SRC_OVER = 3, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_srcover
497 COMPOSITE_DEST_OVER = 4, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_dstover
498 COMPOSITE_SRC_IN = 5, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_srcin
499 COMPOSITE_DEST_IN = 6, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_dstin
500 COMPOSITE_SRC_OUT = 7, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_srcout
501 COMPOSITE_DEST_OUT = 8, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_dstout
502 COMPOSITE_SRC_ATOP = 9, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_srcatop
503 COMPOSITE_DEST_ATOP = 10, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_dstatop
504 COMPOSITE_XOR = 11, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_xor
505 COMPOSITE_PLUS = 12, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_plus
506
507 // Blend modes
508 // https://www.w3.org/TR/compositing-1/#blending
509 COMPOSITE_SCREEN = 13, // https://www.w3.org/TR/compositing-1/#blendingscreen
510 COMPOSITE_OVERLAY = 14, // https://www.w3.org/TR/compositing-1/#blendingoverlay
511 COMPOSITE_DARKEN = 15, // https://www.w3.org/TR/compositing-1/#blendingdarken
512 COMPOSITE_LIGHTEN = 16, // https://www.w3.org/TR/compositing-1/#blendinglighten
513 COMPOSITE_COLOR_DODGE = 17, // https://www.w3.org/TR/compositing-1/#blendingcolordodge
514 COMPOSITE_COLOR_BURN = 18, // https://www.w3.org/TR/compositing-1/#blendingcolorburn
515 COMPOSITE_HARD_LIGHT = 19, // https://www.w3.org/TR/compositing-1/#blendinghardlight
516 COMPOSITE_SOFT_LIGHT = 20, // https://www.w3.org/TR/compositing-1/#blendingsoftlight
517 COMPOSITE_DIFFERENCE = 21, // https://www.w3.org/TR/compositing-1/#blendingdifference
518 COMPOSITE_EXCLUSION = 22, // https://www.w3.org/TR/compositing-1/#blendingexclusion
519 COMPOSITE_MULTIPLY = 23, // https://www.w3.org/TR/compositing-1/#blendingmultiply
520
521 // Modes that, uniquely, do not operate on components
522 // https://www.w3.org/TR/compositing-1/#blendingnonseparable
523 COMPOSITE_HSL_HUE = 24, // https://www.w3.org/TR/compositing-1/#blendinghue
524 COMPOSITE_HSL_SATURATION = 25, // https://www.w3.org/TR/compositing-1/#blendingsaturation
525 COMPOSITE_HSL_COLOR = 26, // https://www.w3.org/TR/compositing-1/#blendingcolor
526 COMPOSITE_HSL_LUMINOSITY = 27, // https://www.w3.org/TR/compositing-1/#blendingluminosity
527 };
528 public:
529 DEFINE_SIZE_STATIC (1);
530};
531
532struct Affine2x3
533{
534 bool sanitize (hb_sanitize_context_t *c) const
535 {
536 TRACE_SANITIZE (this);
537 return_trace (c->check_struct (this));
538 }
539
540 bool subset (hb_subset_context_t *c,
541 const VarStoreInstancer &instancer,
542 uint32_t varIdxBase) const
543 {
544 TRACE_SUBSET (this);
545 auto *out = c->serializer->embed (*this);
546 if (unlikely (!out)) return_trace (false);
547 if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
548 {
549 out->xx.set_float (xx.to_float(instancer (varIdxBase, 0)));
550 out->yx.set_float (yx.to_float(instancer (varIdxBase, 1)));
551 out->xy.set_float (xy.to_float(instancer (varIdxBase, 2)));
552 out->yy.set_float (yy.to_float(instancer (varIdxBase, 3)));
553 out->dx.set_float (dx.to_float(instancer (varIdxBase, 4)));
554 out->dy.set_float (dy.to_float(instancer (varIdxBase, 5)));
555 }
556 return_trace (true);
557 }
558
559 void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
560 {
561 c->funcs->push_transform (c->data,
562 xx.to_float (c->instancer (varIdxBase, 0)),
563 yx.to_float (c->instancer (varIdxBase, 1)),
564 xy.to_float (c->instancer (varIdxBase, 2)),
565 yy.to_float (c->instancer (varIdxBase, 3)),
566 dx.to_float (c->instancer (varIdxBase, 4)),
567 dy.to_float (c->instancer (varIdxBase, 5)));
568 }
569
570 F16DOT16 xx;
571 F16DOT16 yx;
572 F16DOT16 xy;
573 F16DOT16 yy;
574 F16DOT16 dx;
575 F16DOT16 dy;
576 public:
577 DEFINE_SIZE_STATIC (6 * F16DOT16::static_size);
578};
579
580struct PaintColrLayers
581{
582 void closurev1 (hb_colrv1_closure_context_t* c) const;
583
584 bool subset (hb_subset_context_t *c,
585 const VarStoreInstancer &instancer HB_UNUSED) const
586 {
587 TRACE_SUBSET (this);
588 auto *out = c->serializer->embed (this);
589 if (unlikely (!out)) return_trace (false);
590 return_trace (c->serializer->check_assign (out->firstLayerIndex, c->plan->colrv1_layers.get (firstLayerIndex),
591 HB_SERIALIZE_ERROR_INT_OVERFLOW));
592
593 return_trace (true);
594 }
595
596 bool sanitize (hb_sanitize_context_t *c) const
597 {
598 TRACE_SANITIZE (this);
599 return_trace (c->check_struct (this));
600 }
601
602 inline void paint_glyph (hb_paint_context_t *c) const;
603
604 HBUINT8 format; /* format = 1 */
605 HBUINT8 numLayers;
606 HBUINT32 firstLayerIndex; /* index into COLRv1::layerList */
607 public:
608 DEFINE_SIZE_STATIC (6);
609};
610
611struct PaintSolid
612{
613 void closurev1 (hb_colrv1_closure_context_t* c) const
614 { c->add_palette_index (paletteIndex); }
615
616 bool subset (hb_subset_context_t *c,
617 const VarStoreInstancer &instancer,
618 uint32_t varIdxBase) const
619 {
620 TRACE_SUBSET (this);
621 auto *out = c->serializer->embed (*this);
622 if (unlikely (!out)) return_trace (false);
623
624 if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
625 out->alpha.set_float (alpha.to_float (instancer (varIdxBase, 0)));
626
627 if (format == 3 && c->plan->all_axes_pinned)
628 out->format = 2;
629
630 return_trace (c->serializer->check_assign (out->paletteIndex, c->plan->colr_palettes.get (paletteIndex),
631 HB_SERIALIZE_ERROR_INT_OVERFLOW));
632 }
633
634 bool sanitize (hb_sanitize_context_t *c) const
635 {
636 TRACE_SANITIZE (this);
637 return_trace (c->check_struct (this));
638 }
639
640 void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
641 {
642 hb_bool_t is_foreground;
643 hb_color_t color;
644
645 color = c->get_color (paletteIndex,
646 alpha.to_float (c->instancer (varIdxBase, 0)),
647 &is_foreground);
648 c->funcs->color (c->data, is_foreground, color);
649 }
650
651 HBUINT8 format; /* format = 2(noVar) or 3(Var)*/
652 HBUINT16 paletteIndex;
653 F2DOT14 alpha;
654 public:
655 DEFINE_SIZE_STATIC (3 + F2DOT14::static_size);
656};
657
658template <template<typename> class Var>
659struct PaintLinearGradient
660{
661 void closurev1 (hb_colrv1_closure_context_t* c) const
662 { (this+colorLine).closurev1 (c); }
663
664 bool subset (hb_subset_context_t *c,
665 const VarStoreInstancer &instancer,
666 uint32_t varIdxBase) const
667 {
668 TRACE_SUBSET (this);
669 auto *out = c->serializer->embed (this);
670 if (unlikely (!out)) return_trace (false);
671
672 if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
673 {
674 out->x0 = x0 + (int) roundf (instancer (varIdxBase, 0));
675 out->y0 = y0 + (int) roundf (instancer (varIdxBase, 1));
676 out->x1 = x1 + (int) roundf (instancer (varIdxBase, 2));
677 out->y1 = y1 + (int) roundf (instancer (varIdxBase, 3));
678 out->x2 = x2 + (int) roundf (instancer (varIdxBase, 4));
679 out->y2 = y2 + (int) roundf (instancer (varIdxBase, 5));
680 }
681
682 if (format == 5 && c->plan->all_axes_pinned)
683 out->format = 4;
684
685 return_trace (out->colorLine.serialize_subset (c, colorLine, this, instancer));
686 }
687
688 bool sanitize (hb_sanitize_context_t *c) const
689 {
690 TRACE_SANITIZE (this);
691 return_trace (c->check_struct (this) && colorLine.sanitize (c, this));
692 }
693
694 void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
695 {
696 hb_color_line_t cl = {
697 (void *) &(this+colorLine),
698 (this+colorLine).static_get_color_stops, c,
699 (this+colorLine).static_get_extend, nullptr
700 };
701
702 c->funcs->linear_gradient (c->data, &cl,
703 x0 + c->instancer (varIdxBase, 0),
704 y0 + c->instancer (varIdxBase, 1),
705 x1 + c->instancer (varIdxBase, 2),
706 y1 + c->instancer (varIdxBase, 3),
707 x2 + c->instancer (varIdxBase, 4),
708 y2 + c->instancer (varIdxBase, 5));
709 }
710
711 HBUINT8 format; /* format = 4(noVar) or 5 (Var) */
712 Offset24To<ColorLine<Var>> colorLine; /* Offset (from beginning of PaintLinearGradient
713 * table) to ColorLine subtable. */
714 FWORD x0;
715 FWORD y0;
716 FWORD x1;
717 FWORD y1;
718 FWORD x2;
719 FWORD y2;
720 public:
721 DEFINE_SIZE_STATIC (4 + 6 * FWORD::static_size);
722};
723
724template <template<typename> class Var>
725struct PaintRadialGradient
726{
727 void closurev1 (hb_colrv1_closure_context_t* c) const
728 { (this+colorLine).closurev1 (c); }
729
730 bool subset (hb_subset_context_t *c,
731 const VarStoreInstancer &instancer,
732 uint32_t varIdxBase) const
733 {
734 TRACE_SUBSET (this);
735 auto *out = c->serializer->embed (this);
736 if (unlikely (!out)) return_trace (false);
737
738 if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
739 {
740 out->x0 = x0 + (int) roundf (instancer (varIdxBase, 0));
741 out->y0 = y0 + (int) roundf (instancer (varIdxBase, 1));
742 out->radius0 = radius0 + (unsigned) roundf (instancer (varIdxBase, 2));
743 out->x1 = x1 + (int) roundf (instancer (varIdxBase, 3));
744 out->y1 = y1 + (int) roundf (instancer (varIdxBase, 4));
745 out->radius1 = radius1 + (unsigned) roundf (instancer (varIdxBase, 5));
746 }
747
748 if (format == 7 && c->plan->all_axes_pinned)
749 out->format = 6;
750
751 return_trace (out->colorLine.serialize_subset (c, colorLine, this, instancer));
752 }
753
754 bool sanitize (hb_sanitize_context_t *c) const
755 {
756 TRACE_SANITIZE (this);
757 return_trace (c->check_struct (this) && colorLine.sanitize (c, this));
758 }
759
760 void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
761 {
762 hb_color_line_t cl = {
763 (void *) &(this+colorLine),
764 (this+colorLine).static_get_color_stops, c,
765 (this+colorLine).static_get_extend, nullptr
766 };
767
768 c->funcs->radial_gradient (c->data, &cl,
769 x0 + c->instancer (varIdxBase, 0),
770 y0 + c->instancer (varIdxBase, 1),
771 radius0 + c->instancer (varIdxBase, 2),
772 x1 + c->instancer (varIdxBase, 3),
773 y1 + c->instancer (varIdxBase, 4),
774 radius1 + c->instancer (varIdxBase, 5));
775 }
776
777 HBUINT8 format; /* format = 6(noVar) or 7 (Var) */
778 Offset24To<ColorLine<Var>> colorLine; /* Offset (from beginning of PaintRadialGradient
779 * table) to ColorLine subtable. */
780 FWORD x0;
781 FWORD y0;
782 UFWORD radius0;
783 FWORD x1;
784 FWORD y1;
785 UFWORD radius1;
786 public:
787 DEFINE_SIZE_STATIC (4 + 6 * FWORD::static_size);
788};
789
790template <template<typename> class Var>
791struct PaintSweepGradient
792{
793 void closurev1 (hb_colrv1_closure_context_t* c) const
794 { (this+colorLine).closurev1 (c); }
795
796 bool subset (hb_subset_context_t *c,
797 const VarStoreInstancer &instancer,
798 uint32_t varIdxBase) const
799 {
800 TRACE_SUBSET (this);
801 auto *out = c->serializer->embed (this);
802 if (unlikely (!out)) return_trace (false);
803
804 if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
805 {
806 out->centerX = centerX + (int) roundf (instancer (varIdxBase, 0));
807 out->centerY = centerY + (int) roundf (instancer (varIdxBase, 1));
808 out->startAngle.set_float (startAngle.to_float (instancer (varIdxBase, 2)));
809 out->endAngle.set_float (endAngle.to_float (instancer (varIdxBase, 3)));
810 }
811
812 if (format == 9 && c->plan->all_axes_pinned)
813 out->format = 8;
814
815 return_trace (out->colorLine.serialize_subset (c, colorLine, this, instancer));
816 }
817
818 bool sanitize (hb_sanitize_context_t *c) const
819 {
820 TRACE_SANITIZE (this);
821 return_trace (c->check_struct (this) && colorLine.sanitize (c, this));
822 }
823
824 void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
825 {
826 hb_color_line_t cl = {
827 (void *) &(this+colorLine),
828 (this+colorLine).static_get_color_stops, c,
829 (this+colorLine).static_get_extend, nullptr
830 };
831
832 c->funcs->sweep_gradient (c->data, &cl,
833 centerX + c->instancer (varIdxBase, 0),
834 centerY + c->instancer (varIdxBase, 1),
835 (startAngle.to_float (c->instancer (varIdxBase, 2)) + 1) * HB_PI,
836 (endAngle.to_float (c->instancer (varIdxBase, 3)) + 1) * HB_PI);
837 }
838
839 HBUINT8 format; /* format = 8(noVar) or 9 (Var) */
840 Offset24To<ColorLine<Var>> colorLine; /* Offset (from beginning of PaintSweepGradient
841 * table) to ColorLine subtable. */
842 FWORD centerX;
843 FWORD centerY;
844 F2DOT14 startAngle;
845 F2DOT14 endAngle;
846 public:
847 DEFINE_SIZE_STATIC (4 + 2 * FWORD::static_size + 2 * F2DOT14::static_size);
848};
849
850// Paint a non-COLR glyph, filled as indicated by paint.
851struct PaintGlyph
852{
853 void closurev1 (hb_colrv1_closure_context_t* c) const;
854
855 bool subset (hb_subset_context_t *c,
856 const VarStoreInstancer &instancer) const
857 {
858 TRACE_SUBSET (this);
859 auto *out = c->serializer->embed (this);
860 if (unlikely (!out)) return_trace (false);
861
862 if (! c->serializer->check_assign (out->gid, c->plan->glyph_map->get (gid),
863 HB_SERIALIZE_ERROR_INT_OVERFLOW))
864 return_trace (false);
865
866 return_trace (out->paint.serialize_subset (c, paint, this, instancer));
867 }
868
869 bool sanitize (hb_sanitize_context_t *c) const
870 {
871 TRACE_SANITIZE (this);
872 return_trace (c->check_struct (this) && paint.sanitize (c, this));
873 }
874
875 void paint_glyph (hb_paint_context_t *c) const
876 {
877 c->funcs->push_inverse_root_transform (c->data, c->font);
878 c->funcs->push_clip_glyph (c->data, gid, c->font);
879 c->funcs->push_root_transform (c->data, c->font);
880 c->recurse (this+paint);
881 c->funcs->pop_transform (c->data);
882 c->funcs->pop_clip (c->data);
883 c->funcs->pop_transform (c->data);
884 }
885
886 HBUINT8 format; /* format = 10 */
887 Offset24To<Paint> paint; /* Offset (from beginning of PaintGlyph table) to Paint subtable. */
888 HBUINT16 gid;
889 public:
890 DEFINE_SIZE_STATIC (6);
891};
892
893struct PaintColrGlyph
894{
895 void closurev1 (hb_colrv1_closure_context_t* c) const;
896
897 bool subset (hb_subset_context_t *c,
898 const VarStoreInstancer &instancer HB_UNUSED) const
899 {
900 TRACE_SUBSET (this);
901 auto *out = c->serializer->embed (this);
902 if (unlikely (!out)) return_trace (false);
903
904 return_trace (c->serializer->check_assign (out->gid, c->plan->glyph_map->get (gid),
905 HB_SERIALIZE_ERROR_INT_OVERFLOW));
906 }
907
908 bool sanitize (hb_sanitize_context_t *c) const
909 {
910 TRACE_SANITIZE (this);
911 return_trace (c->check_struct (this));
912 }
913
914 inline void paint_glyph (hb_paint_context_t *c) const;
915
916 HBUINT8 format; /* format = 11 */
917 HBUINT16 gid;
918 public:
919 DEFINE_SIZE_STATIC (3);
920};
921
922template <template<typename> class Var>
923struct PaintTransform
924{
925 HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
926
927 bool subset (hb_subset_context_t *c,
928 const VarStoreInstancer &instancer) const
929 {
930 TRACE_SUBSET (this);
931 auto *out = c->serializer->embed (this);
932 if (unlikely (!out)) return_trace (false);
933 if (!out->transform.serialize_subset (c, transform, this, instancer)) return_trace (false);
934 if (format == 13 && c->plan->all_axes_pinned)
935 out->format = 12;
936 return_trace (out->src.serialize_subset (c, src, this, instancer));
937 }
938
939 bool sanitize (hb_sanitize_context_t *c) const
940 {
941 TRACE_SANITIZE (this);
942 return_trace (c->check_struct (this) &&
943 src.sanitize (c, this) &&
944 transform.sanitize (c, this));
945 }
946
947 void paint_glyph (hb_paint_context_t *c) const
948 {
949 (this+transform).paint_glyph (c);
950 c->recurse (this+src);
951 c->funcs->pop_transform (c->data);
952 }
953
954 HBUINT8 format; /* format = 12(noVar) or 13 (Var) */
955 Offset24To<Paint> src; /* Offset (from beginning of PaintTransform table) to Paint subtable. */
956 Offset24To<Var<Affine2x3>> transform;
957 public:
958 DEFINE_SIZE_STATIC (7);
959};
960
961struct PaintTranslate
962{
963 HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
964
965 bool subset (hb_subset_context_t *c,
966 const VarStoreInstancer &instancer,
967 uint32_t varIdxBase) const
968 {
969 TRACE_SUBSET (this);
970 auto *out = c->serializer->embed (this);
971 if (unlikely (!out)) return_trace (false);
972
973 if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
974 {
975 out->dx = dx + (int) roundf (instancer (varIdxBase, 0));
976 out->dy = dy + (int) roundf (instancer (varIdxBase, 1));
977 }
978
979 if (format == 15 && c->plan->all_axes_pinned)
980 out->format = 14;
981
982 return_trace (out->src.serialize_subset (c, src, this, instancer));
983 }
984
985 bool sanitize (hb_sanitize_context_t *c) const
986 {
987 TRACE_SANITIZE (this);
988 return_trace (c->check_struct (this) && src.sanitize (c, this));
989 }
990
991 void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
992 {
993 float ddx = dx + c->instancer (varIdxBase, 0);
994 float ddy = dy + c->instancer (varIdxBase, 1);
995
996 bool p1 = c->funcs->push_translate (c->data, ddx, ddy);
997 c->recurse (this+src);
998 if (p1) c->funcs->pop_transform (c->data);
999 }
1000
1001 HBUINT8 format; /* format = 14(noVar) or 15 (Var) */
1002 Offset24To<Paint> src; /* Offset (from beginning of PaintTranslate table) to Paint subtable. */
1003 FWORD dx;
1004 FWORD dy;
1005 public:
1006 DEFINE_SIZE_STATIC (4 + 2 * FWORD::static_size);
1007};
1008
1009struct PaintScale
1010{
1011 HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
1012
1013 bool subset (hb_subset_context_t *c,
1014 const VarStoreInstancer &instancer,
1015 uint32_t varIdxBase) const
1016 {
1017 TRACE_SUBSET (this);
1018 auto *out = c->serializer->embed (this);
1019 if (unlikely (!out)) return_trace (false);
1020
1021 if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
1022 {
1023 out->scaleX.set_float (scaleX.to_float (instancer (varIdxBase, 0)));
1024 out->scaleY.set_float (scaleY.to_float (instancer (varIdxBase, 1)));
1025 }
1026
1027 if (format == 17 && c->plan->all_axes_pinned)
1028 out->format = 16;
1029
1030 return_trace (out->src.serialize_subset (c, src, this, instancer));
1031 }
1032
1033 bool sanitize (hb_sanitize_context_t *c) const
1034 {
1035 TRACE_SANITIZE (this);
1036 return_trace (c->check_struct (this) && src.sanitize (c, this));
1037 }
1038
1039 void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
1040 {
1041 float sx = scaleX.to_float (c->instancer (varIdxBase, 0));
1042 float sy = scaleY.to_float (c->instancer (varIdxBase, 1));
1043
1044 bool p1 = c->funcs->push_scale (c->data, sx, sy);
1045 c->recurse (this+src);
1046 if (p1) c->funcs->pop_transform (c->data);
1047 }
1048
1049 HBUINT8 format; /* format = 16 (noVar) or 17(Var) */
1050 Offset24To<Paint> src; /* Offset (from beginning of PaintScale table) to Paint subtable. */
1051 F2DOT14 scaleX;
1052 F2DOT14 scaleY;
1053 public:
1054 DEFINE_SIZE_STATIC (4 + 2 * F2DOT14::static_size);
1055};
1056
1057struct PaintScaleAroundCenter
1058{
1059 HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
1060
1061 bool subset (hb_subset_context_t *c,
1062 const VarStoreInstancer &instancer,
1063 uint32_t varIdxBase) const
1064 {
1065 TRACE_SUBSET (this);
1066 auto *out = c->serializer->embed (this);
1067 if (unlikely (!out)) return_trace (false);
1068
1069 if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
1070 {
1071 out->scaleX.set_float (scaleX.to_float (instancer (varIdxBase, 0)));
1072 out->scaleY.set_float (scaleY.to_float (instancer (varIdxBase, 1)));
1073 out->centerX = centerX + (int) roundf (instancer (varIdxBase, 2));
1074 out->centerY = centerY + (int) roundf (instancer (varIdxBase, 3));
1075 }
1076
1077 if (format == 19 && c->plan->all_axes_pinned)
1078 out->format = 18;
1079
1080 return_trace (out->src.serialize_subset (c, src, this, instancer));
1081 }
1082
1083 bool sanitize (hb_sanitize_context_t *c) const
1084 {
1085 TRACE_SANITIZE (this);
1086 return_trace (c->check_struct (this) && src.sanitize (c, this));
1087 }
1088
1089 void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
1090 {
1091 float sx = scaleX.to_float (c->instancer (varIdxBase, 0));
1092 float sy = scaleY.to_float (c->instancer (varIdxBase, 1));
1093 float tCenterX = centerX + c->instancer (varIdxBase, 2);
1094 float tCenterY = centerY + c->instancer (varIdxBase, 3);
1095
1096 bool p1 = c->funcs->push_translate (c->data, +tCenterX, +tCenterY);
1097 bool p2 = c->funcs->push_scale (c->data, sx, sy);
1098 bool p3 = c->funcs->push_translate (c->data, -tCenterX, -tCenterY);
1099 c->recurse (this+src);
1100 if (p3) c->funcs->pop_transform (c->data);
1101 if (p2) c->funcs->pop_transform (c->data);
1102 if (p1) c->funcs->pop_transform (c->data);
1103 }
1104
1105 HBUINT8 format; /* format = 18 (noVar) or 19(Var) */
1106 Offset24To<Paint> src; /* Offset (from beginning of PaintScaleAroundCenter table) to Paint subtable. */
1107 F2DOT14 scaleX;
1108 F2DOT14 scaleY;
1109 FWORD centerX;
1110 FWORD centerY;
1111 public:
1112 DEFINE_SIZE_STATIC (4 + 2 * F2DOT14::static_size + 2 * FWORD::static_size);
1113};
1114
1115struct PaintScaleUniform
1116{
1117 HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
1118
1119 bool subset (hb_subset_context_t *c,
1120 const VarStoreInstancer &instancer,
1121 uint32_t varIdxBase) const
1122 {
1123 TRACE_SUBSET (this);
1124 auto *out = c->serializer->embed (this);
1125 if (unlikely (!out)) return_trace (false);
1126
1127 if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
1128 out->scale.set_float (scale.to_float (instancer (varIdxBase, 0)));
1129
1130 if (format == 21 && c->plan->all_axes_pinned)
1131 out->format = 20;
1132
1133 return_trace (out->src.serialize_subset (c, src, this, instancer));
1134 }
1135
1136 bool sanitize (hb_sanitize_context_t *c) const
1137 {
1138 TRACE_SANITIZE (this);
1139 return_trace (c->check_struct (this) && src.sanitize (c, this));
1140 }
1141
1142 void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
1143 {
1144 float s = scale.to_float (c->instancer (varIdxBase, 0));
1145
1146 bool p1 = c->funcs->push_scale (c->data, s, s);
1147 c->recurse (this+src);
1148 if (p1) c->funcs->pop_transform (c->data);
1149 }
1150
1151 HBUINT8 format; /* format = 20 (noVar) or 21(Var) */
1152 Offset24To<Paint> src; /* Offset (from beginning of PaintScaleUniform table) to Paint subtable. */
1153 F2DOT14 scale;
1154 public:
1155 DEFINE_SIZE_STATIC (4 + F2DOT14::static_size);
1156};
1157
1158struct PaintScaleUniformAroundCenter
1159{
1160 HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
1161
1162 bool subset (hb_subset_context_t *c,
1163 const VarStoreInstancer &instancer,
1164 uint32_t varIdxBase) const
1165 {
1166 TRACE_SUBSET (this);
1167 auto *out = c->serializer->embed (this);
1168 if (unlikely (!out)) return_trace (false);
1169
1170 if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
1171 {
1172 out->scale.set_float (scale.to_float (instancer (varIdxBase, 0)));
1173 out->centerX = centerX + (int) roundf (instancer (varIdxBase, 1));
1174 out->centerY = centerY + (int) roundf (instancer (varIdxBase, 2));
1175 }
1176
1177 if (format == 23 && c->plan->all_axes_pinned)
1178 out->format = 22;
1179
1180 return_trace (out->src.serialize_subset (c, src, this, instancer));
1181 }
1182
1183 bool sanitize (hb_sanitize_context_t *c) const
1184 {
1185 TRACE_SANITIZE (this);
1186 return_trace (c->check_struct (this) && src.sanitize (c, this));
1187 }
1188
1189 void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
1190 {
1191 float s = scale.to_float (c->instancer (varIdxBase, 0));
1192 float tCenterX = centerX + c->instancer (varIdxBase, 1);
1193 float tCenterY = centerY + c->instancer (varIdxBase, 2);
1194
1195 bool p1 = c->funcs->push_translate (c->data, +tCenterX, +tCenterY);
1196 bool p2 = c->funcs->push_scale (c->data, s, s);
1197 bool p3 = c->funcs->push_translate (c->data, -tCenterX, -tCenterY);
1198 c->recurse (this+src);
1199 if (p3) c->funcs->pop_transform (c->data);
1200 if (p2) c->funcs->pop_transform (c->data);
1201 if (p1) c->funcs->pop_transform (c->data);
1202 }
1203
1204 HBUINT8 format; /* format = 22 (noVar) or 23(Var) */
1205 Offset24To<Paint> src; /* Offset (from beginning of PaintScaleUniformAroundCenter table) to Paint subtable. */
1206 F2DOT14 scale;
1207 FWORD centerX;
1208 FWORD centerY;
1209 public:
1210 DEFINE_SIZE_STATIC (4 + F2DOT14::static_size + 2 * FWORD::static_size);
1211};
1212
1213struct PaintRotate
1214{
1215 HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
1216
1217 bool subset (hb_subset_context_t *c,
1218 const VarStoreInstancer &instancer,
1219 uint32_t varIdxBase) const
1220 {
1221 TRACE_SUBSET (this);
1222 auto *out = c->serializer->embed (this);
1223 if (unlikely (!out)) return_trace (false);
1224
1225 if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
1226 out->angle.set_float (angle.to_float (instancer (varIdxBase, 0)));
1227
1228 if (format == 25 && c->plan->all_axes_pinned)
1229 out->format = 24;
1230
1231 return_trace (out->src.serialize_subset (c, src, this, instancer));
1232 }
1233
1234 bool sanitize (hb_sanitize_context_t *c) const
1235 {
1236 TRACE_SANITIZE (this);
1237 return_trace (c->check_struct (this) && src.sanitize (c, this));
1238 }
1239
1240 void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
1241 {
1242 float a = angle.to_float (c->instancer (varIdxBase, 0));
1243
1244 bool p1 = c->funcs->push_rotate (c->data, a);
1245 c->recurse (this+src);
1246 if (p1) c->funcs->pop_transform (c->data);
1247 }
1248
1249 HBUINT8 format; /* format = 24 (noVar) or 25(Var) */
1250 Offset24To<Paint> src; /* Offset (from beginning of PaintRotate table) to Paint subtable. */
1251 F2DOT14 angle;
1252 public:
1253 DEFINE_SIZE_STATIC (4 + F2DOT14::static_size);
1254};
1255
1256struct PaintRotateAroundCenter
1257{
1258 HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
1259
1260 bool subset (hb_subset_context_t *c,
1261 const VarStoreInstancer &instancer,
1262 uint32_t varIdxBase) const
1263 {
1264 TRACE_SUBSET (this);
1265 auto *out = c->serializer->embed (this);
1266 if (unlikely (!out)) return_trace (false);
1267
1268 if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
1269 {
1270 out->angle.set_float (angle.to_float (instancer (varIdxBase, 0)));
1271 out->centerX = centerX + (int) roundf (instancer (varIdxBase, 1));
1272 out->centerY = centerY + (int) roundf (instancer (varIdxBase, 2));
1273 }
1274
1275 if (format ==27 && c->plan->all_axes_pinned)
1276 out->format = 26;
1277
1278 return_trace (out->src.serialize_subset (c, src, this, instancer));
1279 }
1280
1281 bool sanitize (hb_sanitize_context_t *c) const
1282 {
1283 TRACE_SANITIZE (this);
1284 return_trace (c->check_struct (this) && src.sanitize (c, this));
1285 }
1286
1287 void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
1288 {
1289 float a = angle.to_float (c->instancer (varIdxBase, 0));
1290 float tCenterX = centerX + c->instancer (varIdxBase, 1);
1291 float tCenterY = centerY + c->instancer (varIdxBase, 2);
1292
1293 bool p1 = c->funcs->push_translate (c->data, +tCenterX, +tCenterY);
1294 bool p2 = c->funcs->push_rotate (c->data, a);
1295 bool p3 = c->funcs->push_translate (c->data, -tCenterX, -tCenterY);
1296 c->recurse (this+src);
1297 if (p3) c->funcs->pop_transform (c->data);
1298 if (p2) c->funcs->pop_transform (c->data);
1299 if (p1) c->funcs->pop_transform (c->data);
1300 }
1301
1302 HBUINT8 format; /* format = 26 (noVar) or 27(Var) */
1303 Offset24To<Paint> src; /* Offset (from beginning of PaintRotateAroundCenter table) to Paint subtable. */
1304 F2DOT14 angle;
1305 FWORD centerX;
1306 FWORD centerY;
1307 public:
1308 DEFINE_SIZE_STATIC (4 + F2DOT14::static_size + 2 * FWORD::static_size);
1309};
1310
1311struct PaintSkew
1312{
1313 HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
1314
1315 bool subset (hb_subset_context_t *c,
1316 const VarStoreInstancer &instancer,
1317 uint32_t varIdxBase) const
1318 {
1319 TRACE_SUBSET (this);
1320 auto *out = c->serializer->embed (this);
1321 if (unlikely (!out)) return_trace (false);
1322
1323 if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
1324 {
1325 out->xSkewAngle.set_float (xSkewAngle.to_float (instancer (varIdxBase, 0)));
1326 out->ySkewAngle.set_float (ySkewAngle.to_float (instancer (varIdxBase, 1)));
1327 }
1328
1329 if (format == 29 && c->plan->all_axes_pinned)
1330 out->format = 28;
1331
1332 return_trace (out->src.serialize_subset (c, src, this, instancer));
1333 }
1334
1335 bool sanitize (hb_sanitize_context_t *c) const
1336 {
1337 TRACE_SANITIZE (this);
1338 return_trace (c->check_struct (this) && src.sanitize (c, this));
1339 }
1340
1341 void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
1342 {
1343 float sx = xSkewAngle.to_float(c->instancer (varIdxBase, 0));
1344 float sy = ySkewAngle.to_float(c->instancer (varIdxBase, 1));
1345
1346 bool p1 = c->funcs->push_skew (c->data, sx, sy);
1347 c->recurse (this+src);
1348 if (p1) c->funcs->pop_transform (c->data);
1349 }
1350
1351 HBUINT8 format; /* format = 28(noVar) or 29 (Var) */
1352 Offset24To<Paint> src; /* Offset (from beginning of PaintSkew table) to Paint subtable. */
1353 F2DOT14 xSkewAngle;
1354 F2DOT14 ySkewAngle;
1355 public:
1356 DEFINE_SIZE_STATIC (4 + 2 * F2DOT14::static_size);
1357};
1358
1359struct PaintSkewAroundCenter
1360{
1361 HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
1362
1363 bool subset (hb_subset_context_t *c,
1364 const VarStoreInstancer &instancer,
1365 uint32_t varIdxBase) const
1366 {
1367 TRACE_SUBSET (this);
1368 auto *out = c->serializer->embed (this);
1369 if (unlikely (!out)) return_trace (false);
1370
1371 if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
1372 {
1373 out->xSkewAngle.set_float (xSkewAngle.to_float (instancer (varIdxBase, 0)));
1374 out->ySkewAngle.set_float (ySkewAngle.to_float (instancer (varIdxBase, 1)));
1375 out->centerX = centerX + (int) roundf (instancer (varIdxBase, 2));
1376 out->centerY = centerY + (int) roundf (instancer (varIdxBase, 3));
1377 }
1378
1379 if (format == 31 && c->plan->all_axes_pinned)
1380 out->format = 30;
1381
1382 return_trace (out->src.serialize_subset (c, src, this, instancer));
1383 }
1384
1385 bool sanitize (hb_sanitize_context_t *c) const
1386 {
1387 TRACE_SANITIZE (this);
1388 return_trace (c->check_struct (this) && src.sanitize (c, this));
1389 }
1390
1391 void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
1392 {
1393 float sx = xSkewAngle.to_float(c->instancer (varIdxBase, 0));
1394 float sy = ySkewAngle.to_float(c->instancer (varIdxBase, 1));
1395 float tCenterX = centerX + c->instancer (varIdxBase, 2);
1396 float tCenterY = centerY + c->instancer (varIdxBase, 3);
1397
1398 bool p1 = c->funcs->push_translate (c->data, +tCenterX, +tCenterY);
1399 bool p2 = c->funcs->push_skew (c->data, sx, sy);
1400 bool p3 = c->funcs->push_translate (c->data, -tCenterX, -tCenterY);
1401 c->recurse (this+src);
1402 if (p3) c->funcs->pop_transform (c->data);
1403 if (p2) c->funcs->pop_transform (c->data);
1404 if (p1) c->funcs->pop_transform (c->data);
1405 }
1406
1407 HBUINT8 format; /* format = 30(noVar) or 31 (Var) */
1408 Offset24To<Paint> src; /* Offset (from beginning of PaintSkewAroundCenter table) to Paint subtable. */
1409 F2DOT14 xSkewAngle;
1410 F2DOT14 ySkewAngle;
1411 FWORD centerX;
1412 FWORD centerY;
1413 public:
1414 DEFINE_SIZE_STATIC (4 + 2 * F2DOT14::static_size + 2 * FWORD::static_size);
1415};
1416
1417struct PaintComposite
1418{
1419 void closurev1 (hb_colrv1_closure_context_t* c) const;
1420
1421 bool subset (hb_subset_context_t *c,
1422 const VarStoreInstancer &instancer) const
1423 {
1424 TRACE_SUBSET (this);
1425 auto *out = c->serializer->embed (this);
1426 if (unlikely (!out)) return_trace (false);
1427
1428 if (!out->src.serialize_subset (c, src, this, instancer)) return_trace (false);
1429 return_trace (out->backdrop.serialize_subset (c, backdrop, this, instancer));
1430 }
1431
1432 bool sanitize (hb_sanitize_context_t *c) const
1433 {
1434 TRACE_SANITIZE (this);
1435 return_trace (c->check_struct (this) &&
1436 c->check_ops (this->min_size) && // PainComposite can get exponential
1437 src.sanitize (c, this) &&
1438 backdrop.sanitize (c, this));
1439 }
1440
1441 void paint_glyph (hb_paint_context_t *c) const
1442 {
1443 c->recurse (this+backdrop);
1444 c->funcs->push_group (c->data);
1445 c->recurse (this+src);
1446 c->funcs->pop_group (c->data, (hb_paint_composite_mode_t) (int) mode);
1447 }
1448
1449 HBUINT8 format; /* format = 32 */
1450 Offset24To<Paint> src; /* Offset (from beginning of PaintComposite table) to source Paint subtable. */
1451 CompositeMode mode; /* If mode is unrecognized use COMPOSITE_CLEAR */
1452 Offset24To<Paint> backdrop; /* Offset (from beginning of PaintComposite table) to backdrop Paint subtable. */
1453 public:
1454 DEFINE_SIZE_STATIC (8);
1455};
1456
1457struct ClipBoxData
1458{
1459 int xMin, yMin, xMax, yMax;
1460};
1461
1462struct ClipBoxFormat1
1463{
1464 bool sanitize (hb_sanitize_context_t *c) const
1465 {
1466 TRACE_SANITIZE (this);
1467 return_trace (c->check_struct (this));
1468 }
1469
1470 void get_clip_box (ClipBoxData &clip_box, const VarStoreInstancer &instancer HB_UNUSED) const
1471 {
1472 clip_box.xMin = xMin;
1473 clip_box.yMin = yMin;
1474 clip_box.xMax = xMax;
1475 clip_box.yMax = yMax;
1476 }
1477
1478 bool subset (hb_subset_context_t *c,
1479 const VarStoreInstancer &instancer,
1480 uint32_t varIdxBase) const
1481 {
1482 TRACE_SUBSET (this);
1483 auto *out = c->serializer->embed (*this);
1484 if (unlikely (!out)) return_trace (false);
1485
1486 if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
1487 {
1488 out->xMin = xMin + (int) roundf (instancer (varIdxBase, 0));
1489 out->yMin = yMin + (int) roundf (instancer (varIdxBase, 1));
1490 out->xMax = xMax + (int) roundf (instancer (varIdxBase, 2));
1491 out->yMax = yMax + (int) roundf (instancer (varIdxBase, 3));
1492 }
1493
1494 if (format == 2 && c->plan->all_axes_pinned)
1495 out->format = 1;
1496
1497 return_trace (true);
1498 }
1499
1500 public:
1501 HBUINT8 format; /* format = 1(noVar) or 2(Var)*/
1502 FWORD xMin;
1503 FWORD yMin;
1504 FWORD xMax;
1505 FWORD yMax;
1506 public:
1507 DEFINE_SIZE_STATIC (1 + 4 * FWORD::static_size);
1508};
1509
1510struct ClipBoxFormat2 : Variable<ClipBoxFormat1>
1511{
1512 void get_clip_box (ClipBoxData &clip_box, const VarStoreInstancer &instancer) const
1513 {
1514 value.get_clip_box(clip_box, instancer);
1515 if (instancer)
1516 {
1517 clip_box.xMin += roundf (instancer (varIdxBase, 0));
1518 clip_box.yMin += roundf (instancer (varIdxBase, 1));
1519 clip_box.xMax += roundf (instancer (varIdxBase, 2));
1520 clip_box.yMax += roundf (instancer (varIdxBase, 3));
1521 }
1522 }
1523};
1524
1525struct ClipBox
1526{
1527 bool subset (hb_subset_context_t *c,
1528 const VarStoreInstancer &instancer) const
1529 {
1530 TRACE_SUBSET (this);
1531 switch (u.format) {
1532 case 1: return_trace (u.format1.subset (c, instancer, VarIdx::NO_VARIATION));
1533 case 2: return_trace (u.format2.subset (c, instancer));
1534 default:return_trace (c->default_return_value ());
1535 }
1536 }
1537
1538 template <typename context_t, typename ...Ts>
1539 typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
1540 {
1541 if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value ();
1542 TRACE_DISPATCH (this, u.format);
1543 switch (u.format) {
1544 case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
1545 case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...));
1546 default:return_trace (c->default_return_value ());
1547 }
1548 }
1549
1550 bool get_extents (hb_glyph_extents_t *extents,
1551 const VarStoreInstancer &instancer) const
1552 {
1553 ClipBoxData clip_box;
1554 switch (u.format) {
1555 case 1:
1556 u.format1.get_clip_box (clip_box, instancer);
1557 break;
1558 case 2:
1559 u.format2.get_clip_box (clip_box, instancer);
1560 break;
1561 default:
1562 return false;
1563 }
1564
1565 extents->x_bearing = clip_box.xMin;
1566 extents->y_bearing = clip_box.yMax;
1567 extents->width = clip_box.xMax - clip_box.xMin;
1568 extents->height = clip_box.yMin - clip_box.yMax;
1569 return true;
1570 }
1571
1572 protected:
1573 union {
1574 HBUINT8 format; /* Format identifier */
1575 ClipBoxFormat1 format1;
1576 ClipBoxFormat2 format2;
1577 } u;
1578};
1579
1580struct ClipRecord
1581{
1582 int cmp (hb_codepoint_t g) const
1583 { return g < startGlyphID ? -1 : g <= endGlyphID ? 0 : +1; }
1584
1585 bool subset (hb_subset_context_t *c,
1586 const void *base,
1587 const VarStoreInstancer &instancer) const
1588 {
1589 TRACE_SUBSET (this);
1590 auto *out = c->serializer->embed (*this);
1591 if (unlikely (!out)) return_trace (false);
1592
1593 return_trace (out->clipBox.serialize_subset (c, clipBox, base, instancer));
1594 }
1595
1596 bool sanitize (hb_sanitize_context_t *c, const void *base) const
1597 {
1598 TRACE_SANITIZE (this);
1599 return_trace (c->check_struct (this) && clipBox.sanitize (c, base));
1600 }
1601
1602 bool get_extents (hb_glyph_extents_t *extents,
1603 const void *base,
1604 const VarStoreInstancer &instancer) const
1605 {
1606 return (base+clipBox).get_extents (extents, instancer);
1607 }
1608
1609 public:
1610 HBUINT16 startGlyphID; // first gid clip applies to
1611 HBUINT16 endGlyphID; // last gid clip applies to, inclusive
1612 Offset24To<ClipBox> clipBox; // Box or VarBox
1613 public:
1614 DEFINE_SIZE_STATIC (7);
1615};
1616DECLARE_NULL_NAMESPACE_BYTES (OT, ClipRecord);
1617
1618struct ClipList
1619{
1620 unsigned serialize_clip_records (hb_subset_context_t *c,
1621 const VarStoreInstancer &instancer,
1622 const hb_set_t& gids,
1623 const hb_map_t& gid_offset_map) const
1624 {
1625 TRACE_SERIALIZE (this);
1626 if (gids.is_empty () ||
1627 gid_offset_map.get_population () != gids.get_population ())
1628 return_trace (0);
1629
1630 unsigned count = 0;
1631
1632 hb_codepoint_t start_gid= gids.get_min ();
1633 hb_codepoint_t prev_gid = start_gid;
1634
1635 unsigned offset = gid_offset_map.get (start_gid);
1636 unsigned prev_offset = offset;
1637 for (const hb_codepoint_t _ : gids.iter ())
1638 {
1639 if (_ == start_gid) continue;
1640
1641 offset = gid_offset_map.get (_);
1642 if (_ == prev_gid + 1 && offset == prev_offset)
1643 {
1644 prev_gid = _;
1645 continue;
1646 }
1647
1648 ClipRecord record;
1649 record.startGlyphID = start_gid;
1650 record.endGlyphID = prev_gid;
1651 record.clipBox = prev_offset;
1652
1653 if (!record.subset (c, this, instancer)) return_trace (0);
1654 count++;
1655
1656 start_gid = _;
1657 prev_gid = _;
1658 prev_offset = offset;
1659 }
1660
1661 //last one
1662 {
1663 ClipRecord record;
1664 record.startGlyphID = start_gid;
1665 record.endGlyphID = prev_gid;
1666 record.clipBox = prev_offset;
1667 if (!record.subset (c, this, instancer)) return_trace (0);
1668 count++;
1669 }
1670 return_trace (count);
1671 }
1672
1673 bool subset (hb_subset_context_t *c,
1674 const VarStoreInstancer &instancer) const
1675 {
1676 TRACE_SUBSET (this);
1677 auto *out = c->serializer->start_embed (*this);
1678 if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
1679 if (!c->serializer->check_assign (out->format, format, HB_SERIALIZE_ERROR_INT_OVERFLOW)) return_trace (false);
1680
1681 const hb_set_t& glyphset = c->plan->_glyphset_colred;
1682 const hb_map_t &glyph_map = *c->plan->glyph_map;
1683
1684 hb_map_t new_gid_offset_map;
1685 hb_set_t new_gids;
1686 for (const ClipRecord& record : clips.iter ())
1687 {
1688 unsigned start_gid = record.startGlyphID;
1689 unsigned end_gid = record.endGlyphID;
1690 for (unsigned gid = start_gid; gid <= end_gid; gid++)
1691 {
1692 if (!glyphset.has (gid) || !glyph_map.has (gid)) continue;
1693 unsigned new_gid = glyph_map.get (gid);
1694 new_gid_offset_map.set (new_gid, record.clipBox);
1695 new_gids.add (new_gid);
1696 }
1697 }
1698
1699 unsigned count = serialize_clip_records (c, instancer, new_gids, new_gid_offset_map);
1700 if (!count) return_trace (false);
1701 return_trace (c->serializer->check_assign (out->clips.len, count, HB_SERIALIZE_ERROR_INT_OVERFLOW));
1702 }
1703
1704 bool sanitize (hb_sanitize_context_t *c) const
1705 {
1706 TRACE_SANITIZE (this);
1707 // TODO Make a formatted struct!
1708 return_trace (c->check_struct (this) && clips.sanitize (c, this));
1709 }
1710
1711 bool
1712 get_extents (hb_codepoint_t gid,
1713 hb_glyph_extents_t *extents,
1714 const VarStoreInstancer &instancer) const
1715 {
1716 auto *rec = clips.as_array ().bsearch (gid);
1717 if (rec)
1718 {
1719 rec->get_extents (extents, this, instancer);
1720 return true;
1721 }
1722 return false;
1723 }
1724
1725 HBUINT8 format; // Set to 1.
1726 SortedArray32Of<ClipRecord> clips; // Clip records, sorted by startGlyphID
1727 public:
1728 DEFINE_SIZE_ARRAY_SIZED (5, clips);
1729};
1730
1731struct Paint
1732{
1733
1734 template <typename ...Ts>
1735 bool sanitize (hb_sanitize_context_t *c, Ts&&... ds) const
1736 {
1737 TRACE_SANITIZE (this);
1738
1739 if (unlikely (!c->check_start_recursion (HB_MAX_NESTING_LEVEL)))
1740 return_trace (c->no_dispatch_return_value ());
1741
1742 return_trace (c->end_recursion (this->dispatch (c, std::forward<Ts> (ds)...)));
1743 }
1744
1745 template <typename context_t, typename ...Ts>
1746 typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
1747 {
1748 if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value ();
1749 TRACE_DISPATCH (this, u.format);
1750 switch (u.format) {
1751 case 1: return_trace (c->dispatch (u.paintformat1, std::forward<Ts> (ds)...));
1752 case 2: return_trace (c->dispatch (u.paintformat2, std::forward<Ts> (ds)...));
1753 case 3: return_trace (c->dispatch (u.paintformat3, std::forward<Ts> (ds)...));
1754 case 4: return_trace (c->dispatch (u.paintformat4, std::forward<Ts> (ds)...));
1755 case 5: return_trace (c->dispatch (u.paintformat5, std::forward<Ts> (ds)...));
1756 case 6: return_trace (c->dispatch (u.paintformat6, std::forward<Ts> (ds)...));
1757 case 7: return_trace (c->dispatch (u.paintformat7, std::forward<Ts> (ds)...));
1758 case 8: return_trace (c->dispatch (u.paintformat8, std::forward<Ts> (ds)...));
1759 case 9: return_trace (c->dispatch (u.paintformat9, std::forward<Ts> (ds)...));
1760 case 10: return_trace (c->dispatch (u.paintformat10, std::forward<Ts> (ds)...));
1761 case 11: return_trace (c->dispatch (u.paintformat11, std::forward<Ts> (ds)...));
1762 case 12: return_trace (c->dispatch (u.paintformat12, std::forward<Ts> (ds)...));
1763 case 13: return_trace (c->dispatch (u.paintformat13, std::forward<Ts> (ds)...));
1764 case 14: return_trace (c->dispatch (u.paintformat14, std::forward<Ts> (ds)...));
1765 case 15: return_trace (c->dispatch (u.paintformat15, std::forward<Ts> (ds)...));
1766 case 16: return_trace (c->dispatch (u.paintformat16, std::forward<Ts> (ds)...));
1767 case 17: return_trace (c->dispatch (u.paintformat17, std::forward<Ts> (ds)...));
1768 case 18: return_trace (c->dispatch (u.paintformat18, std::forward<Ts> (ds)...));
1769 case 19: return_trace (c->dispatch (u.paintformat19, std::forward<Ts> (ds)...));
1770 case 20: return_trace (c->dispatch (u.paintformat20, std::forward<Ts> (ds)...));
1771 case 21: return_trace (c->dispatch (u.paintformat21, std::forward<Ts> (ds)...));
1772 case 22: return_trace (c->dispatch (u.paintformat22, std::forward<Ts> (ds)...));
1773 case 23: return_trace (c->dispatch (u.paintformat23, std::forward<Ts> (ds)...));
1774 case 24: return_trace (c->dispatch (u.paintformat24, std::forward<Ts> (ds)...));
1775 case 25: return_trace (c->dispatch (u.paintformat25, std::forward<Ts> (ds)...));
1776 case 26: return_trace (c->dispatch (u.paintformat26, std::forward<Ts> (ds)...));
1777 case 27: return_trace (c->dispatch (u.paintformat27, std::forward<Ts> (ds)...));
1778 case 28: return_trace (c->dispatch (u.paintformat28, std::forward<Ts> (ds)...));
1779 case 29: return_trace (c->dispatch (u.paintformat29, std::forward<Ts> (ds)...));
1780 case 30: return_trace (c->dispatch (u.paintformat30, std::forward<Ts> (ds)...));
1781 case 31: return_trace (c->dispatch (u.paintformat31, std::forward<Ts> (ds)...));
1782 case 32: return_trace (c->dispatch (u.paintformat32, std::forward<Ts> (ds)...));
1783 default:return_trace (c->default_return_value ());
1784 }
1785 }
1786
1787 protected:
1788 union {
1789 HBUINT8 format;
1790 PaintColrLayers paintformat1;
1791 NoVariable<PaintSolid> paintformat2;
1792 Variable<PaintSolid> paintformat3;
1793 NoVariable<PaintLinearGradient<NoVariable>> paintformat4;
1794 Variable<PaintLinearGradient<Variable>> paintformat5;
1795 NoVariable<PaintRadialGradient<NoVariable>> paintformat6;
1796 Variable<PaintRadialGradient<Variable>> paintformat7;
1797 NoVariable<PaintSweepGradient<NoVariable>> paintformat8;
1798 Variable<PaintSweepGradient<Variable>> paintformat9;
1799 PaintGlyph paintformat10;
1800 PaintColrGlyph paintformat11;
1801 PaintTransform<NoVariable> paintformat12;
1802 PaintTransform<Variable> paintformat13;
1803 NoVariable<PaintTranslate> paintformat14;
1804 Variable<PaintTranslate> paintformat15;
1805 NoVariable<PaintScale> paintformat16;
1806 Variable<PaintScale> paintformat17;
1807 NoVariable<PaintScaleAroundCenter> paintformat18;
1808 Variable<PaintScaleAroundCenter> paintformat19;
1809 NoVariable<PaintScaleUniform> paintformat20;
1810 Variable<PaintScaleUniform> paintformat21;
1811 NoVariable<PaintScaleUniformAroundCenter> paintformat22;
1812 Variable<PaintScaleUniformAroundCenter> paintformat23;
1813 NoVariable<PaintRotate> paintformat24;
1814 Variable<PaintRotate> paintformat25;
1815 NoVariable<PaintRotateAroundCenter> paintformat26;
1816 Variable<PaintRotateAroundCenter> paintformat27;
1817 NoVariable<PaintSkew> paintformat28;
1818 Variable<PaintSkew> paintformat29;
1819 NoVariable<PaintSkewAroundCenter> paintformat30;
1820 Variable<PaintSkewAroundCenter> paintformat31;
1821 PaintComposite paintformat32;
1822 } u;
1823 public:
1824 DEFINE_SIZE_MIN (2);
1825};
1826
1827struct BaseGlyphPaintRecord
1828{
1829 int cmp (hb_codepoint_t g) const
1830 { return g < glyphId ? -1 : g > glyphId ? 1 : 0; }
1831
1832 bool serialize (hb_serialize_context_t *s, const hb_map_t* glyph_map,
1833 const void* src_base, hb_subset_context_t *c,
1834 const VarStoreInstancer &instancer) const
1835 {
1836 TRACE_SERIALIZE (this);
1837 auto *out = s->embed (this);
1838 if (unlikely (!out)) return_trace (false);
1839 if (!s->check_assign (out->glyphId, glyph_map->get (glyphId),
1840 HB_SERIALIZE_ERROR_INT_OVERFLOW))
1841 return_trace (false);
1842
1843 return_trace (out->paint.serialize_subset (c, paint, src_base, instancer));
1844 }
1845
1846 bool sanitize (hb_sanitize_context_t *c, const void *base) const
1847 {
1848 TRACE_SANITIZE (this);
1849 return_trace (likely (c->check_struct (this) && paint.sanitize (c, base)));
1850 }
1851
1852 public:
1853 HBGlyphID16 glyphId; /* Glyph ID of reference glyph */
1854 Offset32To<Paint> paint; /* Offset (from beginning of BaseGlyphPaintRecord array) to Paint,
1855 * Typically PaintColrLayers */
1856 public:
1857 DEFINE_SIZE_STATIC (6);
1858};
1859
1860struct BaseGlyphList : SortedArray32Of<BaseGlyphPaintRecord>
1861{
1862 bool subset (hb_subset_context_t *c,
1863 const VarStoreInstancer &instancer) const
1864 {
1865 TRACE_SUBSET (this);
1866 auto *out = c->serializer->start_embed (this);
1867 if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
1868 const hb_set_t* glyphset = &c->plan->_glyphset_colred;
1869
1870 for (const auto& _ : as_array ())
1871 {
1872 unsigned gid = _.glyphId;
1873 if (!glyphset->has (gid)) continue;
1874
1875 if (_.serialize (c->serializer, c->plan->glyph_map, this, c, instancer)) out->len++;
1876 else return_trace (false);
1877 }
1878
1879 return_trace (out->len != 0);
1880 }
1881
1882 bool sanitize (hb_sanitize_context_t *c) const
1883 {
1884 TRACE_SANITIZE (this);
1885 return_trace (SortedArray32Of<BaseGlyphPaintRecord>::sanitize (c, this));
1886 }
1887};
1888
1889struct LayerList : Array32OfOffset32To<Paint>
1890{
1891 const Paint& get_paint (unsigned i) const
1892 { return this+(*this)[i]; }
1893
1894 bool subset (hb_subset_context_t *c,
1895 const VarStoreInstancer &instancer) const
1896 {
1897 TRACE_SUBSET (this);
1898 auto *out = c->serializer->start_embed (this);
1899 if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
1900
1901 for (const auto& _ : + hb_enumerate (*this)
1902 | hb_filter (c->plan->colrv1_layers, hb_first))
1903
1904 {
1905 auto *o = out->serialize_append (c->serializer);
1906 if (unlikely (!o) || !o->serialize_subset (c, _.second, this, instancer))
1907 return_trace (false);
1908 }
1909 return_trace (true);
1910 }
1911
1912 bool sanitize (hb_sanitize_context_t *c) const
1913 {
1914 TRACE_SANITIZE (this);
1915 return_trace (Array32OfOffset32To<Paint>::sanitize (c, this));
1916 }
1917};
1918
1919struct COLR
1920{
1921 static constexpr hb_tag_t tableTag = HB_OT_TAG_COLR;
1922
1923 bool has_v0_data () const { return numBaseGlyphs; }
1924 bool has_v1_data () const
1925 {
1926 if (version == 1)
1927 return (this+baseGlyphList).len > 0;
1928
1929 return false;
1930 }
1931
1932 unsigned int get_glyph_layers (hb_codepoint_t glyph,
1933 unsigned int start_offset,
1934 unsigned int *count, /* IN/OUT. May be NULL. */
1935 hb_ot_color_layer_t *layers /* OUT. May be NULL. */) const
1936 {
1937 const BaseGlyphRecord &record = (this+baseGlyphsZ).bsearch (numBaseGlyphs, glyph);
1938
1939 hb_array_t<const LayerRecord> all_layers = (this+layersZ).as_array (numLayers);
1940 hb_array_t<const LayerRecord> glyph_layers = all_layers.sub_array (record.firstLayerIdx,
1941 record.numLayers);
1942 if (count)
1943 {
1944 + glyph_layers.sub_array (start_offset, count)
1945 | hb_sink (hb_array (layers, *count))
1946 ;
1947 }
1948 return glyph_layers.length;
1949 }
1950
1951 struct accelerator_t
1952 {
1953 accelerator_t (hb_face_t *face)
1954 { colr = hb_sanitize_context_t ().reference_table<COLR> (face); }
1955 ~accelerator_t () { this->colr.destroy (); }
1956
1957 bool is_valid () { return colr.get_blob ()->length; }
1958
1959 void closure_glyphs (hb_codepoint_t glyph,
1960 hb_set_t *related_ids /* OUT */) const
1961 { colr->closure_glyphs (glyph, related_ids); }
1962
1963 void closure_V0palette_indices (const hb_set_t *glyphs,
1964 hb_set_t *palettes /* OUT */) const
1965 { colr->closure_V0palette_indices (glyphs, palettes); }
1966
1967 void closure_forV1 (hb_set_t *glyphset,
1968 hb_set_t *layer_indices,
1969 hb_set_t *palette_indices) const
1970 { colr->closure_forV1 (glyphset, layer_indices, palette_indices); }
1971
1972 private:
1973 hb_blob_ptr_t<COLR> colr;
1974 };
1975
1976 void closure_glyphs (hb_codepoint_t glyph,
1977 hb_set_t *related_ids /* OUT */) const
1978 {
1979 const BaseGlyphRecord *record = get_base_glyph_record (glyph);
1980 if (!record) return;
1981
1982 auto glyph_layers = (this+layersZ).as_array (numLayers).sub_array (record->firstLayerIdx,
1983 record->numLayers);
1984 if (!glyph_layers.length) return;
1985 related_ids->add_array (&glyph_layers[0].glyphId, glyph_layers.length, LayerRecord::min_size);
1986 }
1987
1988 void closure_V0palette_indices (const hb_set_t *glyphs,
1989 hb_set_t *palettes /* OUT */) const
1990 {
1991 if (!numBaseGlyphs || !numLayers) return;
1992 hb_array_t<const BaseGlyphRecord> baseGlyphs = (this+baseGlyphsZ).as_array (numBaseGlyphs);
1993 hb_array_t<const LayerRecord> all_layers = (this+layersZ).as_array (numLayers);
1994
1995 for (const BaseGlyphRecord record : baseGlyphs)
1996 {
1997 if (!glyphs->has (record.glyphId)) continue;
1998 hb_array_t<const LayerRecord> glyph_layers = all_layers.sub_array (record.firstLayerIdx,
1999 record.numLayers);
2000 for (const LayerRecord layer : glyph_layers)
2001 palettes->add (layer.colorIdx);
2002 }
2003 }
2004
2005 void closure_forV1 (hb_set_t *glyphset,
2006 hb_set_t *layer_indices,
2007 hb_set_t *palette_indices) const
2008 {
2009 if (version != 1) return;
2010 hb_set_t visited_glyphs;
2011
2012 hb_colrv1_closure_context_t c (this, &visited_glyphs, layer_indices, palette_indices);
2013 const BaseGlyphList &baseglyph_paintrecords = this+baseGlyphList;
2014
2015 for (const BaseGlyphPaintRecord &baseglyph_paintrecord: baseglyph_paintrecords.iter ())
2016 {
2017 unsigned gid = baseglyph_paintrecord.glyphId;
2018 if (!glyphset->has (gid)) continue;
2019
2020 const Paint &paint = &baseglyph_paintrecords+baseglyph_paintrecord.paint;
2021 paint.dispatch (&c);
2022 }
2023 hb_set_union (glyphset, &visited_glyphs);
2024 }
2025
2026 const LayerList& get_layerList () const
2027 { return (this+layerList); }
2028
2029 const BaseGlyphList& get_baseglyphList () const
2030 { return (this+baseGlyphList); }
2031
2032 bool sanitize (hb_sanitize_context_t *c) const
2033 {
2034 TRACE_SANITIZE (this);
2035 return_trace (c->check_struct (this) &&
2036 (this+baseGlyphsZ).sanitize (c, numBaseGlyphs) &&
2037 (this+layersZ).sanitize (c, numLayers) &&
2038 (version == 0 ||
2039 (version == 1 &&
2040 baseGlyphList.sanitize (c, this) &&
2041 layerList.sanitize (c, this) &&
2042 clipList.sanitize (c, this) &&
2043 varIdxMap.sanitize (c, this) &&
2044 varStore.sanitize (c, this))));
2045 }
2046
2047 template<typename BaseIterator, typename LayerIterator,
2048 hb_requires (hb_is_iterator (BaseIterator)),
2049 hb_requires (hb_is_iterator (LayerIterator))>
2050 bool serialize_V0 (hb_serialize_context_t *c,
2051 unsigned version,
2052 BaseIterator base_it,
2053 LayerIterator layer_it)
2054 {
2055 TRACE_SERIALIZE (this);
2056 if (unlikely (base_it.len () != layer_it.len ()))
2057 return_trace (false);
2058
2059 this->version = version;
2060 numLayers = 0;
2061 numBaseGlyphs = base_it.len ();
2062 if (numBaseGlyphs == 0)
2063 {
2064 baseGlyphsZ = 0;
2065 layersZ = 0;
2066 return_trace (true);
2067 }
2068
2069 c->push ();
2070 for (const hb_item_type<BaseIterator> _ : + base_it.iter ())
2071 {
2072 auto* record = c->embed (_);
2073 if (unlikely (!record)) return_trace (false);
2074 record->firstLayerIdx = numLayers;
2075 numLayers += record->numLayers;
2076 }
2077 c->add_link (baseGlyphsZ, c->pop_pack ());
2078
2079 c->push ();
2080 for (const hb_item_type<LayerIterator>& _ : + layer_it.iter ())
2081 _.as_array ().copy (c);
2082
2083 c->add_link (layersZ, c->pop_pack ());
2084
2085 return_trace (true);
2086 }
2087
2088 const BaseGlyphRecord* get_base_glyph_record (hb_codepoint_t gid) const
2089 {
2090 const BaseGlyphRecord* record = &(this+baseGlyphsZ).bsearch (numBaseGlyphs, (unsigned int) gid);
2091 if (record == &Null (BaseGlyphRecord) ||
2092 (record && (hb_codepoint_t) record->glyphId != gid))
2093 record = nullptr;
2094 return record;
2095 }
2096
2097 const BaseGlyphPaintRecord* get_base_glyph_paintrecord (hb_codepoint_t gid) const
2098 {
2099 const BaseGlyphPaintRecord* record = &(this+baseGlyphList).bsearch ((unsigned) gid);
2100 if ((record && (hb_codepoint_t) record->glyphId != gid))
2101 record = nullptr;
2102 return record;
2103 }
2104
2105 bool subset (hb_subset_context_t *c) const
2106 {
2107 TRACE_SUBSET (this);
2108 const hb_map_t &reverse_glyph_map = *c->plan->reverse_glyph_map;
2109 const hb_set_t& glyphset = c->plan->_glyphset_colred;
2110
2111 auto base_it =
2112 + hb_range (c->plan->num_output_glyphs ())
2113 | hb_filter ([&](hb_codepoint_t new_gid)
2114 {
2115 hb_codepoint_t old_gid = reverse_glyph_map.get (new_gid);
2116 if (glyphset.has (old_gid)) return true;
2117 return false;
2118 })
2119 | hb_map_retains_sorting ([&](hb_codepoint_t new_gid)
2120 {
2121 hb_codepoint_t old_gid = reverse_glyph_map.get (new_gid);
2122
2123 const BaseGlyphRecord* old_record = get_base_glyph_record (old_gid);
2124 if (unlikely (!old_record))
2125 return hb_pair_t<bool, BaseGlyphRecord> (false, Null (BaseGlyphRecord));
2126 BaseGlyphRecord new_record = {};
2127 new_record.glyphId = new_gid;
2128 new_record.numLayers = old_record->numLayers;
2129 return hb_pair_t<bool, BaseGlyphRecord> (true, new_record);
2130 })
2131 | hb_filter (hb_first)
2132 | hb_map_retains_sorting (hb_second)
2133 ;
2134
2135 auto layer_it =
2136 + hb_range (c->plan->num_output_glyphs ())
2137 | hb_map (reverse_glyph_map)
2138 | hb_filter (glyphset)
2139 | hb_map_retains_sorting ([&](hb_codepoint_t old_gid)
2140 {
2141 const BaseGlyphRecord* old_record = get_base_glyph_record (old_gid);
2142 hb_vector_t<LayerRecord> out_layers;
2143
2144 if (unlikely (!old_record ||
2145 old_record->firstLayerIdx >= numLayers ||
2146 old_record->firstLayerIdx + old_record->numLayers > numLayers))
2147 return hb_pair_t<bool, hb_vector_t<LayerRecord>> (false, out_layers);
2148
2149 auto layers = (this+layersZ).as_array (numLayers).sub_array (old_record->firstLayerIdx,
2150 old_record->numLayers);
2151 out_layers.resize (layers.length);
2152 for (unsigned int i = 0; i < layers.length; i++) {
2153 out_layers[i] = layers[i];
2154 hb_codepoint_t new_gid = 0;
2155 if (unlikely (!c->plan->new_gid_for_old_gid (out_layers[i].glyphId, &new_gid)))
2156 return hb_pair_t<bool, hb_vector_t<LayerRecord>> (false, out_layers);
2157 out_layers[i].glyphId = new_gid;
2158 out_layers[i].colorIdx = c->plan->colr_palettes.get (layers[i].colorIdx);
2159 }
2160
2161 return hb_pair_t<bool, hb_vector_t<LayerRecord>> (true, out_layers);
2162 })
2163 | hb_filter (hb_first)
2164 | hb_map_retains_sorting (hb_second)
2165 ;
2166
2167 if (version == 0 && (!base_it || !layer_it))
2168 return_trace (false);
2169
2170 auto *colr_prime = c->serializer->start_embed<COLR> ();
2171 if (unlikely (!c->serializer->extend_min (colr_prime))) return_trace (false);
2172
2173 if (version == 0)
2174 return_trace (colr_prime->serialize_V0 (c->serializer, version, base_it, layer_it));
2175
2176 auto snap = c->serializer->snapshot ();
2177 if (!c->serializer->allocate_size<void> (5 * HBUINT32::static_size)) return_trace (false);
2178
2179 VarStoreInstancer instancer (varStore ? &(this+varStore) : nullptr,
2180 varIdxMap ? &(this+varIdxMap) : nullptr,
2181 c->plan->normalized_coords.as_array ());
2182
2183 if (!colr_prime->baseGlyphList.serialize_subset (c, baseGlyphList, this, instancer))
2184 {
2185 if (c->serializer->in_error ()) return_trace (false);
2186 //no more COLRv1 glyphs: downgrade to version 0
2187 c->serializer->revert (snap);
2188 return_trace (colr_prime->serialize_V0 (c->serializer, 0, base_it, layer_it));
2189 }
2190
2191 if (!colr_prime->serialize_V0 (c->serializer, version, base_it, layer_it)) return_trace (false);
2192
2193 colr_prime->layerList.serialize_subset (c, layerList, this, instancer);
2194 colr_prime->clipList.serialize_subset (c, clipList, this, instancer);
2195 if (!varStore || c->plan->all_axes_pinned)
2196 return_trace (true);
2197
2198 colr_prime->varIdxMap.serialize_copy (c->serializer, varIdxMap, this);
2199 colr_prime->varStore.serialize_copy (c->serializer, varStore, this);
2200 return_trace (true);
2201 }
2202
2203 const Paint *get_base_glyph_paint (hb_codepoint_t glyph) const
2204 {
2205 const BaseGlyphList &baseglyph_paintrecords = this+baseGlyphList;
2206 const BaseGlyphPaintRecord* record = get_base_glyph_paintrecord (glyph);
2207 if (record)
2208 {
2209 const Paint &paint = &baseglyph_paintrecords+record->paint;
2210 return &paint;
2211 }
2212 else
2213 return nullptr;
2214 }
2215
2216#ifndef HB_NO_PAINT
2217 bool
2218 get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const
2219 {
2220 if (version != 1)
2221 return false;
2222
2223 VarStoreInstancer instancer (&(this+varStore),
2224 &(this+varIdxMap),
2225 hb_array (font->coords, font->num_coords));
2226
2227 if (get_clip (glyph, extents, instancer))
2228 {
2229 font->scale_glyph_extents (extents);
2230 return true;
2231 }
2232
2233 auto *extents_funcs = hb_paint_extents_get_funcs ();
2234 hb_paint_extents_context_t extents_data;
2235 bool ret = paint_glyph (font, glyph, extents_funcs, &extents_data, 0, HB_COLOR(0,0,0,0));
2236
2237 hb_extents_t e = extents_data.get_extents ();
2238 if (e.is_void ())
2239 {
2240 extents->x_bearing = 0;
2241 extents->y_bearing = 0;
2242 extents->width = 0;
2243 extents->height = 0;
2244 }
2245 else
2246 {
2247 extents->x_bearing = e.xmin;
2248 extents->y_bearing = e.ymax;
2249 extents->width = e.xmax - e.xmin;
2250 extents->height = e.ymin - e.ymax;
2251 }
2252
2253 return ret;
2254 }
2255#endif
2256
2257 bool
2258 has_paint_for_glyph (hb_codepoint_t glyph) const
2259 {
2260 if (version == 1)
2261 {
2262 const Paint *paint = get_base_glyph_paint (glyph);
2263
2264 return paint != nullptr;
2265 }
2266
2267 return false;
2268 }
2269
2270 bool get_clip (hb_codepoint_t glyph,
2271 hb_glyph_extents_t *extents,
2272 const VarStoreInstancer instancer) const
2273 {
2274 return (this+clipList).get_extents (glyph,
2275 extents,
2276 instancer);
2277 }
2278
2279#ifndef HB_NO_PAINT
2280 bool
2281 paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, unsigned int palette_index, hb_color_t foreground, bool clip = true) const
2282 {
2283 VarStoreInstancer instancer (&(this+varStore),
2284 &(this+varIdxMap),
2285 hb_array (font->coords, font->num_coords));
2286 hb_paint_context_t c (this, funcs, data, font, palette_index, foreground, instancer);
2287
2288 if (version == 1)
2289 {
2290 const Paint *paint = get_base_glyph_paint (glyph);
2291 if (paint)
2292 {
2293 // COLRv1 glyph
2294
2295 VarStoreInstancer instancer (&(this+varStore),
2296 &(this+varIdxMap),
2297 hb_array (font->coords, font->num_coords));
2298
2299 bool is_bounded = true;
2300 if (clip)
2301 {
2302 hb_glyph_extents_t extents;
2303 if (get_clip (glyph, &extents, instancer))
2304 {
2305 font->scale_glyph_extents (&extents);
2306 c.funcs->push_clip_rectangle (c.data,
2307 extents.x_bearing,
2308 extents.y_bearing + extents.height,
2309 extents.x_bearing + extents.width,
2310 extents.y_bearing);
2311 }
2312 else
2313 {
2314 auto *extents_funcs = hb_paint_extents_get_funcs ();
2315 hb_paint_extents_context_t extents_data;
2316
2317 paint_glyph (font, glyph,
2318 extents_funcs, &extents_data,
2319 palette_index, foreground,
2320 false);
2321
2322 hb_extents_t extents = extents_data.get_extents ();
2323 is_bounded = extents_data.is_bounded ();
2324
2325 c.funcs->push_clip_rectangle (c.data,
2326 extents.xmin,
2327 extents.ymin,
2328 extents.xmax,
2329 extents.ymax);
2330 }
2331 }
2332
2333 c.funcs->push_root_transform (c.data, font);
2334
2335 if (is_bounded)
2336 c.recurse (*paint);
2337
2338 c.funcs->pop_transform (c.data);
2339
2340 if (clip)
2341 c.funcs->pop_clip (c.data);
2342
2343 return true;
2344 }
2345 }
2346
2347 const BaseGlyphRecord *record = get_base_glyph_record (glyph);
2348 if (record && ((hb_codepoint_t) record->glyphId == glyph))
2349 {
2350 // COLRv0 glyph
2351 for (const auto &r : (this+layersZ).as_array (numLayers)
2352 .sub_array (record->firstLayerIdx, record->numLayers))
2353 {
2354 hb_bool_t is_foreground;
2355 hb_color_t color = c.get_color (r.colorIdx, 1., &is_foreground);
2356 c.funcs->push_clip_glyph (c.data, r.glyphId, c.font);
2357 c.funcs->color (c.data, is_foreground, color);
2358 c.funcs->pop_clip (c.data);
2359 }
2360
2361 return true;
2362 }
2363
2364 return false;
2365 }
2366#endif
2367
2368 protected:
2369 HBUINT16 version; /* Table version number (starts at 0). */
2370 HBUINT16 numBaseGlyphs; /* Number of Base Glyph Records. */
2371 NNOffset32To<SortedUnsizedArrayOf<BaseGlyphRecord>>
2372 baseGlyphsZ; /* Offset to Base Glyph records. */
2373 NNOffset32To<UnsizedArrayOf<LayerRecord>>
2374 layersZ; /* Offset to Layer Records. */
2375 HBUINT16 numLayers; /* Number of Layer Records. */
2376 // Version-1 additions
2377 Offset32To<BaseGlyphList> baseGlyphList;
2378 Offset32To<LayerList> layerList;
2379 Offset32To<ClipList> clipList; // Offset to ClipList table (may be NULL)
2380 Offset32To<DeltaSetIndexMap> varIdxMap; // Offset to DeltaSetIndexMap table (may be NULL)
2381 Offset32To<VariationStore> varStore;
2382 public:
2383 DEFINE_SIZE_MIN (14);
2384};
2385
2386struct COLR_accelerator_t : COLR::accelerator_t {
2387 COLR_accelerator_t (hb_face_t *face) : COLR::accelerator_t (face) {}
2388};
2389
2390void
2391hb_paint_context_t::recurse (const Paint &paint)
2392{
2393 if (unlikely (depth_left <= 0 || edge_count <= 0)) return;
2394 depth_left--;
2395 edge_count--;
2396 paint.dispatch (this);
2397 depth_left++;
2398}
2399
2400void PaintColrLayers::paint_glyph (hb_paint_context_t *c) const
2401{
2402 const LayerList &paint_offset_lists = c->get_colr_table ()->get_layerList ();
2403 for (unsigned i = firstLayerIndex; i < firstLayerIndex + numLayers; i++)
2404 {
2405 const Paint &paint = paint_offset_lists.get_paint (i);
2406 c->funcs->push_group (c->data);
2407 c->recurse (paint);
2408 c->funcs->pop_group (c->data, HB_PAINT_COMPOSITE_MODE_SRC_OVER);
2409 }
2410}
2411
2412void PaintColrGlyph::paint_glyph (hb_paint_context_t *c) const
2413{
2414 const COLR *colr_table = c->get_colr_table ();
2415 const Paint *paint = colr_table->get_base_glyph_paint (gid);
2416
2417 hb_glyph_extents_t extents = {0};
2418 bool has_clip_box = colr_table->get_clip (gid, &extents, c->instancer);
2419
2420 if (has_clip_box)
2421 c->funcs->push_clip_rectangle (c->data,
2422 extents.x_bearing,
2423 extents.y_bearing + extents.height,
2424 extents.x_bearing + extents.width,
2425 extents.y_bearing);
2426
2427 if (paint)
2428 c->recurse (*paint);
2429
2430 if (has_clip_box)
2431 c->funcs->pop_clip (c->data);
2432}
2433
2434} /* namespace OT */
2435
2436#endif /* OT_COLOR_COLR_COLR_HH */
2437