1/*
2 * Copyright © 2016 Google, Inc.
3 * Copyright © 2018 Ebrahim Byagowi
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): Sascha Brawer
26 */
27
28#ifndef OT_COLOR_CPAL_CPAL_HH
29#define OT_COLOR_CPAL_CPAL_HH
30
31#include "../../../hb-open-type.hh"
32#include "../../../hb-ot-color.h"
33#include "../../../hb-ot-name.h"
34
35
36/*
37 * CPAL -- Color Palette
38 * https://docs.microsoft.com/en-us/typography/opentype/spec/cpal
39 */
40#define HB_OT_TAG_CPAL HB_TAG('C','P','A','L')
41
42namespace OT {
43
44
45struct CPALV1Tail
46{
47 friend struct CPAL;
48
49 private:
50 hb_ot_color_palette_flags_t get_palette_flags (const void *base,
51 unsigned int palette_index,
52 unsigned int palette_count) const
53 {
54 if (!paletteFlagsZ) return HB_OT_COLOR_PALETTE_FLAG_DEFAULT;
55 return (hb_ot_color_palette_flags_t) (uint32_t)
56 (base+paletteFlagsZ).as_array (palette_count)[palette_index];
57 }
58
59 hb_ot_name_id_t get_palette_name_id (const void *base,
60 unsigned int palette_index,
61 unsigned int palette_count) const
62 {
63 if (!paletteLabelsZ) return HB_OT_NAME_ID_INVALID;
64 return (base+paletteLabelsZ).as_array (palette_count)[palette_index];
65 }
66
67 hb_ot_name_id_t get_color_name_id (const void *base,
68 unsigned int color_index,
69 unsigned int color_count) const
70 {
71 if (!colorLabelsZ) return HB_OT_NAME_ID_INVALID;
72 return (base+colorLabelsZ).as_array (color_count)[color_index];
73 }
74
75 public:
76 void collect_name_ids (const void *base,
77 unsigned palette_count,
78 unsigned color_count,
79 const hb_map_t *color_index_map,
80 hb_set_t *nameids_to_retain /* OUT */) const
81 {
82 if (paletteLabelsZ)
83 {
84 + (base+paletteLabelsZ).as_array (palette_count)
85 | hb_sink (nameids_to_retain)
86 ;
87 }
88
89 if (colorLabelsZ)
90 {
91 const hb_array_t<const NameID> colorLabels = (base+colorLabelsZ).as_array (color_count);
92 for (unsigned i = 0; i < color_count; i++)
93 {
94 if (!color_index_map->has (i)) continue;
95 nameids_to_retain->add (colorLabels[i]);
96 }
97 }
98 }
99
100 bool serialize (hb_serialize_context_t *c,
101 unsigned palette_count,
102 unsigned color_count,
103 const void *base,
104 const hb_map_t *color_index_map) const
105 {
106 TRACE_SERIALIZE (this);
107 auto *out = c->allocate_size<CPALV1Tail> (static_size);
108 if (unlikely (!out)) return_trace (false);
109
110 out->paletteFlagsZ = 0;
111 if (paletteFlagsZ)
112 out->paletteFlagsZ.serialize_copy (c, paletteFlagsZ, base, 0, hb_serialize_context_t::Head, palette_count);
113
114 out->paletteLabelsZ = 0;
115 if (paletteLabelsZ)
116 out->paletteLabelsZ.serialize_copy (c, paletteLabelsZ, base, 0, hb_serialize_context_t::Head, palette_count);
117
118 const hb_array_t<const NameID> colorLabels = (base+colorLabelsZ).as_array (color_count);
119 if (colorLabelsZ)
120 {
121 c->push ();
122 for (unsigned i = 0; i < color_count; i++)
123 {
124 if (!color_index_map->has (i)) continue;
125 if (!c->copy<NameID> (colorLabels[i]))
126 {
127 c->pop_discard ();
128 return_trace (false);
129 }
130 }
131 c->add_link (out->colorLabelsZ, c->pop_pack ());
132 }
133 return_trace (true);
134 }
135
136 bool sanitize (hb_sanitize_context_t *c,
137 const void *base,
138 unsigned int palette_count,
139 unsigned int color_count) const
140 {
141 TRACE_SANITIZE (this);
142 return_trace (c->check_struct (this) &&
143 (!paletteFlagsZ || (base+paletteFlagsZ).sanitize (c, palette_count)) &&
144 (!paletteLabelsZ || (base+paletteLabelsZ).sanitize (c, palette_count)) &&
145 (!colorLabelsZ || (base+colorLabelsZ).sanitize (c, color_count)));
146 }
147
148 protected:
149 // TODO(garretrieger): these offsets can hold nulls so we should not be using non-null offsets
150 // here. Currently they are needed since UnsizedArrayOf doesn't define null_size
151 NNOffset32To<UnsizedArrayOf<HBUINT32>>
152 paletteFlagsZ; /* Offset from the beginning of CPAL table to
153 * the Palette Type Array. Set to 0 if no array
154 * is provided. */
155 NNOffset32To<UnsizedArrayOf<NameID>>
156 paletteLabelsZ; /* Offset from the beginning of CPAL table to
157 * the palette labels array. Set to 0 if no
158 * array is provided. */
159 NNOffset32To<UnsizedArrayOf<NameID>>
160 colorLabelsZ; /* Offset from the beginning of CPAL table to
161 * the color labels array. Set to 0
162 * if no array is provided. */
163 public:
164 DEFINE_SIZE_STATIC (12);
165};
166
167typedef HBUINT32 BGRAColor;
168
169struct CPAL
170{
171 static constexpr hb_tag_t tableTag = HB_OT_TAG_CPAL;
172
173 bool has_data () const { return numPalettes; }
174
175 unsigned int get_size () const
176 { return min_size + numPalettes * sizeof (colorRecordIndicesZ[0]); }
177
178 unsigned int get_palette_count () const { return numPalettes; }
179 unsigned int get_color_count () const { return numColors; }
180
181 hb_ot_color_palette_flags_t get_palette_flags (unsigned int palette_index) const
182 { return v1 ().get_palette_flags (this, palette_index, numPalettes); }
183
184 hb_ot_name_id_t get_palette_name_id (unsigned int palette_index) const
185 { return v1 ().get_palette_name_id (this, palette_index, numPalettes); }
186
187 hb_ot_name_id_t get_color_name_id (unsigned int color_index) const
188 { return v1 ().get_color_name_id (this, color_index, numColors); }
189
190 unsigned int get_palette_colors (unsigned int palette_index,
191 unsigned int start_offset,
192 unsigned int *color_count, /* IN/OUT. May be NULL. */
193 hb_color_t *colors /* OUT. May be NULL. */) const
194 {
195 if (unlikely (palette_index >= numPalettes))
196 {
197 if (color_count) *color_count = 0;
198 return 0;
199 }
200 unsigned int start_index = colorRecordIndicesZ[palette_index];
201 hb_array_t<const BGRAColor> all_colors ((this+colorRecordsZ).arrayZ, numColorRecords);
202 hb_array_t<const BGRAColor> palette_colors = all_colors.sub_array (start_index,
203 numColors);
204 if (color_count)
205 {
206 + palette_colors.sub_array (start_offset, color_count)
207 | hb_sink (hb_array (colors, *color_count))
208 ;
209 }
210 return numColors;
211 }
212
213 void collect_name_ids (const hb_map_t *color_index_map,
214 hb_set_t *nameids_to_retain /* OUT */) const
215 {
216 if (version == 1)
217 v1 ().collect_name_ids (this, numPalettes, numColors, color_index_map, nameids_to_retain);
218 }
219
220 private:
221 const CPALV1Tail& v1 () const
222 {
223 if (version == 0) return Null (CPALV1Tail);
224 return StructAfter<CPALV1Tail> (*this);
225 }
226
227 public:
228 bool serialize (hb_serialize_context_t *c,
229 const hb_array_t<const HBUINT16> &color_record_indices,
230 const hb_array_t<const BGRAColor> &color_records,
231 const hb_vector_t<unsigned>& first_color_index_for_layer,
232 const hb_map_t& first_color_to_layer_index,
233 const hb_set_t &retained_color_indices) const
234 {
235 TRACE_SERIALIZE (this);
236
237 // TODO(grieger): limit total final size.
238
239 for (const auto idx : color_record_indices)
240 {
241 hb_codepoint_t layer_index = first_color_to_layer_index[idx];
242
243 HBUINT16 new_idx;
244 new_idx = layer_index * retained_color_indices.get_population ();
245 if (!c->copy<HBUINT16> (new_idx)) return_trace (false);
246 }
247
248 c->push ();
249 for (unsigned first_color_index : first_color_index_for_layer)
250 {
251 for (hb_codepoint_t color_index : retained_color_indices)
252 {
253 if (!c->copy<BGRAColor> (color_records[first_color_index + color_index]))
254 {
255 c->pop_discard ();
256 return_trace (false);
257 }
258 }
259 }
260
261 c->add_link (colorRecordsZ, c->pop_pack ());
262 return_trace (true);
263 }
264
265 bool subset (hb_subset_context_t *c) const
266 {
267 TRACE_SUBSET (this);
268 if (!numPalettes) return_trace (false);
269
270 const hb_map_t *color_index_map = &c->plan->colr_palettes;
271 if (color_index_map->is_empty ()) return_trace (false);
272
273 hb_set_t retained_color_indices;
274 for (const auto _ : color_index_map->keys ())
275 {
276 if (_ == 0xFFFF) continue;
277 retained_color_indices.add (_);
278 }
279 if (retained_color_indices.is_empty ()) return_trace (false);
280
281 auto *out = c->serializer->start_embed (*this);
282 if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
283
284
285 out->version = version;
286 out->numColors = retained_color_indices.get_population ();
287 out->numPalettes = numPalettes;
288
289 hb_vector_t<unsigned> first_color_index_for_layer;
290 hb_map_t first_color_to_layer_index;
291
292 const hb_array_t<const HBUINT16> colorRecordIndices = colorRecordIndicesZ.as_array (numPalettes);
293 for (const auto first_color_record_idx : colorRecordIndices)
294 {
295 if (first_color_to_layer_index.has (first_color_record_idx)) continue;
296
297 first_color_index_for_layer.push (first_color_record_idx);
298 first_color_to_layer_index.set (first_color_record_idx,
299 first_color_index_for_layer.length - 1);
300 }
301
302 out->numColorRecords = first_color_index_for_layer.length
303 * retained_color_indices.get_population ();
304
305 const hb_array_t<const BGRAColor> color_records = (this+colorRecordsZ).as_array (numColorRecords);
306 if (!out->serialize (c->serializer,
307 colorRecordIndices,
308 color_records,
309 first_color_index_for_layer,
310 first_color_to_layer_index,
311 retained_color_indices))
312 return_trace (false);
313
314 if (version == 1)
315 return_trace (v1 ().serialize (c->serializer, numPalettes, numColors, this, color_index_map));
316
317 return_trace (true);
318 }
319
320 bool sanitize (hb_sanitize_context_t *c) const
321 {
322 TRACE_SANITIZE (this);
323 return_trace (c->check_struct (this) &&
324 (this+colorRecordsZ).sanitize (c, numColorRecords) &&
325 colorRecordIndicesZ.sanitize (c, numPalettes) &&
326 (version == 0 || v1 ().sanitize (c, this, numPalettes, numColors)));
327 }
328
329 protected:
330 HBUINT16 version; /* Table version number */
331 /* Version 0 */
332 HBUINT16 numColors; /* Number of colors in each palette. */
333 HBUINT16 numPalettes; /* Number of palettes in the table. */
334 HBUINT16 numColorRecords; /* Total number of color records, combined for
335 * all palettes. */
336 NNOffset32To<UnsizedArrayOf<BGRAColor>>
337 colorRecordsZ; /* Offset from the beginning of CPAL table to
338 * the first ColorRecord. */
339 UnsizedArrayOf<HBUINT16>
340 colorRecordIndicesZ; /* Index of each palette’s first color record in
341 * the combined color record array. */
342/*CPALV1Tail v1;*/
343 public:
344 DEFINE_SIZE_ARRAY (12, colorRecordIndicesZ);
345};
346
347} /* namespace OT */
348
349
350#endif /* OT_COLOR_CPAL_CPAL_HH */
351