• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /****************************************************************************
2  *
3  * ttcpal.c
4  *
5  *   TrueType and OpenType color palette support (body).
6  *
7  * Copyright 2018 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  trace_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     cpal->color_indices = p;
125     cpal->colors        = (FT_Byte*)( table + colors_offset );
126 
127     if ( cpal->version == 1 )
128     {
129       FT_ULong    type_offset, label_offset, entry_label_offset;
130       FT_UShort*  array = NULL;
131       FT_UShort*  limit;
132       FT_UShort*  q;
133 
134 
135       if ( CPAL_V0_HEADER_BASE_SIZE             +
136            face->palette_data.num_palettes * 2U +
137            3U * 4                               > table_size )
138         goto InvalidTable;
139 
140       p += face->palette_data.num_palettes * 2;
141 
142       type_offset        = FT_NEXT_ULONG( p );
143       label_offset       = FT_NEXT_ULONG( p );
144       entry_label_offset = FT_NEXT_ULONG( p );
145 
146       if ( type_offset )
147       {
148         if ( type_offset >= table_size )
149           goto InvalidTable;
150         if ( face->palette_data.num_palettes * 2 >
151                table_size - type_offset )
152           goto InvalidTable;
153 
154         if ( FT_QNEW_ARRAY( array, face->palette_data.num_palettes ) )
155           goto NoCpal;
156 
157         p     = table + type_offset;
158         q     = array;
159         limit = q + face->palette_data.num_palettes;
160 
161         while ( q < limit )
162           *q++ = FT_NEXT_USHORT( p );
163 
164         face->palette_data.palette_flags = array;
165       }
166 
167       if ( label_offset )
168       {
169         if ( label_offset >= table_size )
170           goto InvalidTable;
171         if ( face->palette_data.num_palettes * 2 >
172                table_size - label_offset )
173           goto InvalidTable;
174 
175         if ( FT_QNEW_ARRAY( array, face->palette_data.num_palettes ) )
176           goto NoCpal;
177 
178         p     = table + label_offset;
179         q     = array;
180         limit = q + face->palette_data.num_palettes;
181 
182         while ( q < limit )
183           *q++ = FT_NEXT_USHORT( p );
184 
185         face->palette_data.palette_name_ids = array;
186       }
187 
188       if ( entry_label_offset )
189       {
190         if ( entry_label_offset >= table_size )
191           goto InvalidTable;
192         if ( face->palette_data.num_palette_entries * 2 >
193                table_size - entry_label_offset )
194           goto InvalidTable;
195 
196         if ( FT_QNEW_ARRAY( array, face->palette_data.num_palette_entries ) )
197           goto NoCpal;
198 
199         p     = table + entry_label_offset;
200         q     = array;
201         limit = q + face->palette_data.num_palette_entries;
202 
203         while ( q < limit )
204           *q++ = FT_NEXT_USHORT( p );
205 
206         face->palette_data.palette_entry_name_ids = array;
207       }
208     }
209 
210     cpal->table      = table;
211     cpal->table_size = table_size;
212 
213     face->cpal = cpal;
214 
215     /* set up default palette */
216     if ( FT_NEW_ARRAY( face->palette,
217                        face->palette_data.num_palette_entries ) )
218       goto NoCpal;
219 
220     tt_face_palette_set( face, 0 );
221 
222     return FT_Err_Ok;
223 
224   InvalidTable:
225     error = FT_THROW( Invalid_Table );
226 
227   NoCpal:
228     FT_FRAME_RELEASE( table );
229     FT_FREE( cpal );
230 
231     /* arrays in `face->palette_data' and `face->palette' */
232     /* are freed in `sfnt_done_face'                      */
233 
234     return error;
235   }
236 
237 
238   FT_LOCAL_DEF( void )
tt_face_free_cpal(TT_Face face)239   tt_face_free_cpal( TT_Face  face )
240   {
241     FT_Stream  stream = face->root.stream;
242     FT_Memory  memory = face->root.memory;
243 
244     Cpal*  cpal = (Cpal*)face->cpal;
245 
246 
247     if ( cpal )
248     {
249       FT_FRAME_RELEASE( cpal->table );
250       FT_FREE( cpal );
251     }
252   }
253 
254 
255   FT_LOCAL_DEF( FT_Error )
tt_face_palette_set(TT_Face face,FT_UInt palette_index)256   tt_face_palette_set( TT_Face  face,
257                        FT_UInt  palette_index )
258   {
259     Cpal*  cpal = (Cpal*)face->cpal;
260 
261     FT_Byte*   offset;
262     FT_Byte*   p;
263 
264     FT_Color*  q;
265     FT_Color*  limit;
266 
267     FT_ULong  record_offset;
268 
269 
270     if ( !cpal || palette_index >= face->palette_data.num_palettes )
271       return FT_THROW( Invalid_Argument );
272 
273     offset        = cpal->color_indices + 2 * palette_index;
274     record_offset = COLOR_SIZE * FT_PEEK_USHORT( offset );
275 
276     if ( record_offset + COLOR_SIZE * face->palette_data.num_palette_entries >
277            cpal->table_size )
278       return FT_THROW( Invalid_Table );
279 
280     p     = cpal->colors + record_offset;
281     q     = face->palette;
282     limit = q + face->palette_data.num_palette_entries;
283 
284     while ( q < limit )
285     {
286       q->blue  = FT_NEXT_BYTE( p );
287       q->green = FT_NEXT_BYTE( p );
288       q->red   = FT_NEXT_BYTE( p );
289       q->alpha = FT_NEXT_BYTE( p );
290 
291       q++;
292     }
293 
294     return FT_Err_Ok;
295   }
296 
297 
298 #else /* !TT_CONFIG_OPTION_COLOR_LAYERS */
299 
300   /* ANSI C doesn't like empty source files */
301   typedef int  _tt_cpal_dummy;
302 
303 #endif /* !TT_CONFIG_OPTION_COLOR_LAYERS */
304 
305 /* EOF */
306