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