1 /****************************************************************************
2  *
3  * ttcpal.c
4  *
5  *   TrueType and OpenType color palette support (body).
6  *
7  * Copyright (C) 2018-2020 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 )
tt_face_load_cpal(TT_Face face,FT_Stream stream)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 )
tt_face_free_cpal(TT_Face face)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 )
tt_face_palette_set(TT_Face face,FT_UInt palette_index)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