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