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