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 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 | |