1 /**************************************************************************** 2 * 3 * ttcpal.c 4 * 5 * TrueType and OpenType color palette support (body). 6 * 7 * Copyright (C) 2018-2023 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