1/****************************************************************************
2 *
3 * ttcpal.c
4 *
5 * TrueType and OpenType color palette support (body).
6 *
7 * Copyright (C) 2018-2023 by
8 * David Turner, Robert Wilhelm, and Werner Lemberg.
9 *
10 * Originally written by Shao Yu Zhang <shaozhang@fb.com>.
11 *
12 * This file is part of the FreeType project, and may only be used,
13 * modified, and distributed under the terms of the FreeType project
14 * license, LICENSE.TXT. By continuing to use, modify, or distribute
15 * this file you indicate that you have read the license and
16 * understand and accept it fully.
17 *
18 */
19
20
21 /**************************************************************************
22 *
23 * `CPAL' table specification:
24 *
25 * https://www.microsoft.com/typography/otspec/cpal.htm
26 *
27 */
28
29
30#include <freetype/internal/ftdebug.h>
31#include <freetype/internal/ftstream.h>
32#include <freetype/tttags.h>
33#include <freetype/ftcolor.h>
34
35
36#ifdef TT_CONFIG_OPTION_COLOR_LAYERS
37
38#include "ttcpal.h"
39
40
41 /* NOTE: These are the table sizes calculated through the specs. */
42#define CPAL_V0_HEADER_BASE_SIZE 12U
43#define COLOR_SIZE 4U
44
45
46 /* all data from `CPAL' not covered in FT_Palette_Data */
47 typedef struct Cpal_
48 {
49 FT_UShort version; /* Table version number (0 or 1 supported). */
50 FT_UShort num_colors; /* Total number of color records, */
51 /* combined for all palettes. */
52 FT_Byte* colors; /* RGBA array of colors */
53 FT_Byte* color_indices; /* Index of each palette's first color record */
54 /* in the combined color record array. */
55
56 /* The memory which backs up the `CPAL' table. */
57 void* table;
58 FT_ULong table_size;
59
60 } Cpal;
61
62
63 /**************************************************************************
64 *
65 * The macro FT_COMPONENT is used in trace mode. It is an implicit
66 * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log
67 * messages during execution.
68 */
69#undef FT_COMPONENT
70#define FT_COMPONENT ttcpal
71
72
73 FT_LOCAL_DEF( FT_Error )
74 tt_face_load_cpal( TT_Face face,
75 FT_Stream stream )
76 {
77 FT_Error error;
78 FT_Memory memory = face->root.memory;
79
80 FT_Byte* table = NULL;
81 FT_Byte* p = NULL;
82
83 Cpal* cpal = NULL;
84
85 FT_ULong colors_offset;
86 FT_ULong table_size;
87
88
89 error = face->goto_table( face, TTAG_CPAL, stream, &table_size );
90 if ( error )
91 goto NoCpal;
92
93 if ( table_size < CPAL_V0_HEADER_BASE_SIZE )
94 goto InvalidTable;
95
96 if ( FT_FRAME_EXTRACT( table_size, table ) )
97 goto NoCpal;
98
99 p = table;
100
101 if ( FT_NEW( cpal ) )
102 goto NoCpal;
103
104 cpal->version = FT_NEXT_USHORT( p );
105 if ( cpal->version > 1 )
106 goto InvalidTable;
107
108 face->palette_data.num_palette_entries = FT_NEXT_USHORT( p );
109 face->palette_data.num_palettes = FT_NEXT_USHORT( p );
110
111 cpal->num_colors = FT_NEXT_USHORT( p );
112 colors_offset = FT_NEXT_ULONG( p );
113
114 if ( CPAL_V0_HEADER_BASE_SIZE +
115 face->palette_data.num_palettes * 2U > table_size )
116 goto InvalidTable;
117
118 if ( colors_offset >= table_size )
119 goto InvalidTable;
120 if ( cpal->num_colors * COLOR_SIZE > table_size - colors_offset )
121 goto InvalidTable;
122
123 if ( face->palette_data.num_palette_entries > cpal->num_colors )
124 goto InvalidTable;
125
126 cpal->color_indices = p;
127 cpal->colors = (FT_Byte*)( table + colors_offset );
128
129 if ( cpal->version == 1 )
130 {
131 FT_ULong type_offset, label_offset, entry_label_offset;
132 FT_UShort* array = NULL;
133 FT_UShort* limit;
134 FT_UShort* q;
135
136
137 if ( CPAL_V0_HEADER_BASE_SIZE +
138 face->palette_data.num_palettes * 2U +
139 3U * 4 > table_size )
140 goto InvalidTable;
141
142 p += face->palette_data.num_palettes * 2U;
143
144 type_offset = FT_NEXT_ULONG( p );
145 label_offset = FT_NEXT_ULONG( p );
146 entry_label_offset = FT_NEXT_ULONG( p );
147
148 if ( type_offset )
149 {
150 if ( type_offset >= table_size )
151 goto InvalidTable;
152 if ( face->palette_data.num_palettes * 2U >
153 table_size - type_offset )
154 goto InvalidTable;
155
156 if ( FT_QNEW_ARRAY( array, face->palette_data.num_palettes ) )
157 goto NoCpal;
158
159 p = table + type_offset;
160 q = array;
161 limit = q + face->palette_data.num_palettes;
162
163 while ( q < limit )
164 *q++ = FT_NEXT_USHORT( p );
165
166 face->palette_data.palette_flags = array;
167 }
168
169 if ( label_offset )
170 {
171 if ( label_offset >= table_size )
172 goto InvalidTable;
173 if ( face->palette_data.num_palettes * 2U >
174 table_size - label_offset )
175 goto InvalidTable;
176
177 if ( FT_QNEW_ARRAY( array, face->palette_data.num_palettes ) )
178 goto NoCpal;
179
180 p = table + label_offset;
181 q = array;
182 limit = q + face->palette_data.num_palettes;
183
184 while ( q < limit )
185 *q++ = FT_NEXT_USHORT( p );
186
187 face->palette_data.palette_name_ids = array;
188 }
189
190 if ( entry_label_offset )
191 {
192 if ( entry_label_offset >= table_size )
193 goto InvalidTable;
194 if ( face->palette_data.num_palette_entries * 2U >
195 table_size - entry_label_offset )
196 goto InvalidTable;
197
198 if ( FT_QNEW_ARRAY( array, face->palette_data.num_palette_entries ) )
199 goto NoCpal;
200
201 p = table + entry_label_offset;
202 q = array;
203 limit = q + face->palette_data.num_palette_entries;
204
205 while ( q < limit )
206 *q++ = FT_NEXT_USHORT( p );
207
208 face->palette_data.palette_entry_name_ids = array;
209 }
210 }
211
212 cpal->table = table;
213 cpal->table_size = table_size;
214
215 face->cpal = cpal;
216
217 /* set up default palette */
218 if ( FT_NEW_ARRAY( face->palette,
219 face->palette_data.num_palette_entries ) )
220 goto NoCpal;
221
222 if ( tt_face_palette_set( face, 0 ) )
223 goto InvalidTable;
224
225 return FT_Err_Ok;
226
227 InvalidTable:
228 error = FT_THROW( Invalid_Table );
229
230 NoCpal:
231 FT_FRAME_RELEASE( table );
232 FT_FREE( cpal );
233
234 face->cpal = NULL;
235
236 /* arrays in `face->palette_data' and `face->palette' */
237 /* are freed in `sfnt_done_face' */
238
239 return error;
240 }
241
242
243 FT_LOCAL_DEF( void )
244 tt_face_free_cpal( TT_Face face )
245 {
246 FT_Stream stream = face->root.stream;
247 FT_Memory memory = face->root.memory;
248
249 Cpal* cpal = (Cpal*)face->cpal;
250
251
252 if ( cpal )
253 {
254 FT_FRAME_RELEASE( cpal->table );
255 FT_FREE( cpal );
256 }
257 }
258
259
260 FT_LOCAL_DEF( FT_Error )
261 tt_face_palette_set( TT_Face face,
262 FT_UInt palette_index )
263 {
264 Cpal* cpal = (Cpal*)face->cpal;
265
266 FT_Byte* offset;
267 FT_Byte* p;
268
269 FT_Color* q;
270 FT_Color* limit;
271
272 FT_UShort color_index;
273
274
275 if ( !cpal || palette_index >= face->palette_data.num_palettes )
276 return FT_THROW( Invalid_Argument );
277
278 offset = cpal->color_indices + 2 * palette_index;
279 color_index = FT_PEEK_USHORT( offset );
280
281 if ( color_index + face->palette_data.num_palette_entries >
282 cpal->num_colors )
283 return FT_THROW( Invalid_Table );
284
285 p = cpal->colors + COLOR_SIZE * color_index;
286 q = face->palette;
287 limit = q + face->palette_data.num_palette_entries;
288
289 while ( q < limit )
290 {
291 q->blue = FT_NEXT_BYTE( p );
292 q->green = FT_NEXT_BYTE( p );
293 q->red = FT_NEXT_BYTE( p );
294 q->alpha = FT_NEXT_BYTE( p );
295
296 q++;
297 }
298
299 return FT_Err_Ok;
300 }
301
302
303#else /* !TT_CONFIG_OPTION_COLOR_LAYERS */
304
305 /* ANSI C doesn't like empty source files */
306 typedef int tt_cpal_dummy_;
307
308#endif /* !TT_CONFIG_OPTION_COLOR_LAYERS */
309
310/* EOF */
311