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 )
  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 )
 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 )
 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 */