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 <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 )
tt_face_load_cpal(TT_Face face,FT_Stream stream)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 )
tt_face_free_cpal(TT_Face face)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 )
tt_face_palette_set(TT_Face face,FT_UInt palette_index)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