2018-06-14 11:32:47 +02:00
|
|
|
/****************************************************************************
|
|
|
|
*
|
|
|
|
* ttcpal.c
|
|
|
|
*
|
|
|
|
* TrueType and OpenType color palette support (body).
|
|
|
|
*
|
2022-01-11 10:54:10 +01:00
|
|
|
* Copyright (C) 2018-2022 by
|
2018-06-14 11:32:47 +02:00
|
|
|
* David Turner, Robert Wilhelm, and Werner Lemberg.
|
|
|
|
*
|
|
|
|
* Originally written by Shao Yu Zhang <shaozhang@fb.com>.
|
|
|
|
*
|
|
|
|
* This file is part of the FreeType project, and may only be used,
|
|
|
|
* modified, and distributed under the terms of the FreeType project
|
|
|
|
* license, LICENSE.TXT. By continuing to use, modify, or distribute
|
|
|
|
* this file you indicate that you have read the license and
|
|
|
|
* understand and accept it fully.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
/**************************************************************************
|
|
|
|
*
|
|
|
|
* `CPAL' table specification:
|
|
|
|
*
|
|
|
|
* https://www.microsoft.com/typography/otspec/cpal.htm
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
2020-06-08 13:31:55 +02:00
|
|
|
#include <freetype/internal/ftdebug.h>
|
|
|
|
#include <freetype/internal/ftstream.h>
|
|
|
|
#include <freetype/tttags.h>
|
|
|
|
#include <freetype/ftcolor.h>
|
2018-06-14 11:32:47 +02:00
|
|
|
|
|
|
|
|
|
|
|
#ifdef TT_CONFIG_OPTION_COLOR_LAYERS
|
|
|
|
|
|
|
|
#include "ttcpal.h"
|
|
|
|
|
|
|
|
|
|
|
|
/* NOTE: These are the table sizes calculated through the specs. */
|
2020-10-18 13:36:57 +02:00
|
|
|
#define CPAL_V0_HEADER_BASE_SIZE 12U
|
|
|
|
#define COLOR_SIZE 4U
|
2018-06-14 11:32:47 +02:00
|
|
|
|
|
|
|
|
|
|
|
/* all data from `CPAL' not covered in FT_Palette_Data */
|
|
|
|
typedef struct Cpal_
|
|
|
|
{
|
|
|
|
FT_UShort version; /* Table version number (0 or 1 supported). */
|
|
|
|
FT_UShort num_colors; /* Total number of color records, */
|
|
|
|
/* combined for all palettes. */
|
|
|
|
FT_Byte* colors; /* RGBA array of colors */
|
|
|
|
FT_Byte* color_indices; /* Index of each palette's first color record */
|
|
|
|
/* in the combined color record array. */
|
|
|
|
|
|
|
|
/* The memory which backs up the `CPAL' table. */
|
2018-06-16 21:45:13 +02:00
|
|
|
void* table;
|
|
|
|
FT_ULong table_size;
|
2018-06-14 11:32:47 +02:00
|
|
|
|
|
|
|
} Cpal;
|
|
|
|
|
|
|
|
|
|
|
|
/**************************************************************************
|
|
|
|
*
|
|
|
|
* The macro FT_COMPONENT is used in trace mode. It is an implicit
|
|
|
|
* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log
|
|
|
|
* messages during execution.
|
|
|
|
*/
|
|
|
|
#undef FT_COMPONENT
|
2018-08-15 18:13:17 +02:00
|
|
|
#define FT_COMPONENT ttcpal
|
2018-06-14 11:32:47 +02:00
|
|
|
|
|
|
|
|
|
|
|
FT_LOCAL_DEF( FT_Error )
|
|
|
|
tt_face_load_cpal( TT_Face face,
|
|
|
|
FT_Stream stream )
|
|
|
|
{
|
|
|
|
FT_Error error;
|
|
|
|
FT_Memory memory = face->root.memory;
|
|
|
|
|
|
|
|
FT_Byte* table = NULL;
|
|
|
|
FT_Byte* p = NULL;
|
|
|
|
|
|
|
|
Cpal* cpal = NULL;
|
|
|
|
|
|
|
|
FT_ULong colors_offset;
|
|
|
|
FT_ULong table_size;
|
|
|
|
|
|
|
|
|
|
|
|
error = face->goto_table( face, TTAG_CPAL, stream, &table_size );
|
|
|
|
if ( error )
|
|
|
|
goto NoCpal;
|
|
|
|
|
|
|
|
if ( table_size < CPAL_V0_HEADER_BASE_SIZE )
|
|
|
|
goto InvalidTable;
|
|
|
|
|
|
|
|
if ( FT_FRAME_EXTRACT( table_size, table ) )
|
|
|
|
goto NoCpal;
|
|
|
|
|
|
|
|
p = table;
|
|
|
|
|
|
|
|
if ( FT_NEW( cpal ) )
|
|
|
|
goto NoCpal;
|
|
|
|
|
|
|
|
cpal->version = FT_NEXT_USHORT( p );
|
|
|
|
if ( cpal->version > 1 )
|
|
|
|
goto InvalidTable;
|
|
|
|
|
|
|
|
face->palette_data.num_palette_entries = FT_NEXT_USHORT( p );
|
|
|
|
face->palette_data.num_palettes = FT_NEXT_USHORT( p );
|
|
|
|
|
|
|
|
cpal->num_colors = FT_NEXT_USHORT( p );
|
|
|
|
colors_offset = FT_NEXT_ULONG( p );
|
|
|
|
|
2018-07-05 23:05:53 +02:00
|
|
|
if ( CPAL_V0_HEADER_BASE_SIZE +
|
|
|
|
face->palette_data.num_palettes * 2U > table_size )
|
|
|
|
goto InvalidTable;
|
|
|
|
|
2018-06-14 11:32:47 +02:00
|
|
|
if ( colors_offset >= table_size )
|
|
|
|
goto InvalidTable;
|
|
|
|
if ( cpal->num_colors * COLOR_SIZE > table_size - colors_offset )
|
|
|
|
goto InvalidTable;
|
|
|
|
|
2018-08-21 10:52:14 +02:00
|
|
|
if ( face->palette_data.num_palette_entries > cpal->num_colors )
|
|
|
|
goto InvalidTable;
|
|
|
|
|
2018-06-14 11:32:47 +02:00
|
|
|
cpal->color_indices = p;
|
|
|
|
cpal->colors = (FT_Byte*)( table + colors_offset );
|
|
|
|
|
|
|
|
if ( cpal->version == 1 )
|
|
|
|
{
|
|
|
|
FT_ULong type_offset, label_offset, entry_label_offset;
|
|
|
|
FT_UShort* array = NULL;
|
|
|
|
FT_UShort* limit;
|
|
|
|
FT_UShort* q;
|
|
|
|
|
|
|
|
|
2018-07-05 23:05:53 +02:00
|
|
|
if ( CPAL_V0_HEADER_BASE_SIZE +
|
|
|
|
face->palette_data.num_palettes * 2U +
|
|
|
|
3U * 4 > table_size )
|
2018-06-19 20:09:31 +02:00
|
|
|
goto InvalidTable;
|
|
|
|
|
2020-10-18 13:36:57 +02:00
|
|
|
p += face->palette_data.num_palettes * 2U;
|
2018-06-14 11:32:47 +02:00
|
|
|
|
|
|
|
type_offset = FT_NEXT_ULONG( p );
|
|
|
|
label_offset = FT_NEXT_ULONG( p );
|
|
|
|
entry_label_offset = FT_NEXT_ULONG( p );
|
|
|
|
|
|
|
|
if ( type_offset )
|
|
|
|
{
|
|
|
|
if ( type_offset >= table_size )
|
|
|
|
goto InvalidTable;
|
2020-10-18 13:36:57 +02:00
|
|
|
if ( face->palette_data.num_palettes * 2U >
|
2018-06-14 11:32:47 +02:00
|
|
|
table_size - type_offset )
|
|
|
|
goto InvalidTable;
|
|
|
|
|
|
|
|
if ( FT_QNEW_ARRAY( array, face->palette_data.num_palettes ) )
|
|
|
|
goto NoCpal;
|
|
|
|
|
|
|
|
p = table + type_offset;
|
|
|
|
q = array;
|
|
|
|
limit = q + face->palette_data.num_palettes;
|
|
|
|
|
|
|
|
while ( q < limit )
|
|
|
|
*q++ = FT_NEXT_USHORT( p );
|
|
|
|
|
2018-07-02 11:50:04 +02:00
|
|
|
face->palette_data.palette_flags = array;
|
2018-06-14 11:32:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if ( label_offset )
|
|
|
|
{
|
|
|
|
if ( label_offset >= table_size )
|
|
|
|
goto InvalidTable;
|
2020-10-18 13:36:57 +02:00
|
|
|
if ( face->palette_data.num_palettes * 2U >
|
2018-06-14 11:32:47 +02:00
|
|
|
table_size - label_offset )
|
|
|
|
goto InvalidTable;
|
|
|
|
|
|
|
|
if ( FT_QNEW_ARRAY( array, face->palette_data.num_palettes ) )
|
|
|
|
goto NoCpal;
|
|
|
|
|
|
|
|
p = table + label_offset;
|
|
|
|
q = array;
|
|
|
|
limit = q + face->palette_data.num_palettes;
|
|
|
|
|
|
|
|
while ( q < limit )
|
|
|
|
*q++ = FT_NEXT_USHORT( p );
|
|
|
|
|
|
|
|
face->palette_data.palette_name_ids = array;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( entry_label_offset )
|
|
|
|
{
|
|
|
|
if ( entry_label_offset >= table_size )
|
|
|
|
goto InvalidTable;
|
2020-10-18 13:36:57 +02:00
|
|
|
if ( face->palette_data.num_palette_entries * 2U >
|
2018-06-14 11:32:47 +02:00
|
|
|
table_size - entry_label_offset )
|
|
|
|
goto InvalidTable;
|
|
|
|
|
|
|
|
if ( FT_QNEW_ARRAY( array, face->palette_data.num_palette_entries ) )
|
|
|
|
goto NoCpal;
|
|
|
|
|
|
|
|
p = table + entry_label_offset;
|
|
|
|
q = array;
|
|
|
|
limit = q + face->palette_data.num_palette_entries;
|
|
|
|
|
|
|
|
while ( q < limit )
|
|
|
|
*q++ = FT_NEXT_USHORT( p );
|
|
|
|
|
|
|
|
face->palette_data.palette_entry_name_ids = array;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-16 21:45:13 +02:00
|
|
|
cpal->table = table;
|
|
|
|
cpal->table_size = table_size;
|
2018-06-14 11:32:47 +02:00
|
|
|
|
|
|
|
face->cpal = cpal;
|
|
|
|
|
|
|
|
/* set up default palette */
|
|
|
|
if ( FT_NEW_ARRAY( face->palette,
|
|
|
|
face->palette_data.num_palette_entries ) )
|
|
|
|
goto NoCpal;
|
|
|
|
|
2018-09-03 09:00:58 +02:00
|
|
|
if ( tt_face_palette_set( face, 0 ) )
|
|
|
|
goto InvalidTable;
|
2018-06-14 11:32:47 +02:00
|
|
|
|
|
|
|
return FT_Err_Ok;
|
|
|
|
|
|
|
|
InvalidTable:
|
|
|
|
error = FT_THROW( Invalid_Table );
|
|
|
|
|
|
|
|
NoCpal:
|
|
|
|
FT_FRAME_RELEASE( table );
|
|
|
|
FT_FREE( cpal );
|
|
|
|
|
2018-09-03 09:00:58 +02:00
|
|
|
face->cpal = NULL;
|
|
|
|
|
2018-06-14 11:32:47 +02:00
|
|
|
/* arrays in `face->palette_data' and `face->palette' */
|
|
|
|
/* are freed in `sfnt_done_face' */
|
|
|
|
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
FT_LOCAL_DEF( void )
|
|
|
|
tt_face_free_cpal( TT_Face face )
|
|
|
|
{
|
|
|
|
FT_Stream stream = face->root.stream;
|
|
|
|
FT_Memory memory = face->root.memory;
|
|
|
|
|
|
|
|
Cpal* cpal = (Cpal*)face->cpal;
|
|
|
|
|
|
|
|
|
|
|
|
if ( cpal )
|
|
|
|
{
|
|
|
|
FT_FRAME_RELEASE( cpal->table );
|
|
|
|
FT_FREE( cpal );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
FT_LOCAL_DEF( FT_Error )
|
|
|
|
tt_face_palette_set( TT_Face face,
|
|
|
|
FT_UInt palette_index )
|
|
|
|
{
|
|
|
|
Cpal* cpal = (Cpal*)face->cpal;
|
|
|
|
|
|
|
|
FT_Byte* offset;
|
|
|
|
FT_Byte* p;
|
|
|
|
|
|
|
|
FT_Color* q;
|
|
|
|
FT_Color* limit;
|
|
|
|
|
2018-09-03 09:00:58 +02:00
|
|
|
FT_UShort color_index;
|
2018-06-16 21:45:13 +02:00
|
|
|
|
2018-06-14 11:32:47 +02:00
|
|
|
|
2018-06-19 07:15:21 +02:00
|
|
|
if ( !cpal || palette_index >= face->palette_data.num_palettes )
|
2018-06-14 11:32:47 +02:00
|
|
|
return FT_THROW( Invalid_Argument );
|
|
|
|
|
2018-09-03 09:00:58 +02:00
|
|
|
offset = cpal->color_indices + 2 * palette_index;
|
|
|
|
color_index = FT_PEEK_USHORT( offset );
|
2018-06-16 21:45:13 +02:00
|
|
|
|
2018-09-03 09:00:58 +02:00
|
|
|
if ( color_index + face->palette_data.num_palette_entries >
|
|
|
|
cpal->num_colors )
|
2018-06-16 21:45:13 +02:00
|
|
|
return FT_THROW( Invalid_Table );
|
2018-06-14 11:32:47 +02:00
|
|
|
|
2018-09-03 09:00:58 +02:00
|
|
|
p = cpal->colors + COLOR_SIZE * color_index;
|
2018-06-14 11:32:47 +02:00
|
|
|
q = face->palette;
|
|
|
|
limit = q + face->palette_data.num_palette_entries;
|
|
|
|
|
|
|
|
while ( q < limit )
|
|
|
|
{
|
|
|
|
q->blue = FT_NEXT_BYTE( p );
|
|
|
|
q->green = FT_NEXT_BYTE( p );
|
|
|
|
q->red = FT_NEXT_BYTE( p );
|
|
|
|
q->alpha = FT_NEXT_BYTE( p );
|
|
|
|
|
|
|
|
q++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return FT_Err_Ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#else /* !TT_CONFIG_OPTION_COLOR_LAYERS */
|
|
|
|
|
|
|
|
/* ANSI C doesn't like empty source files */
|
|
|
|
typedef int _tt_cpal_dummy;
|
|
|
|
|
|
|
|
#endif /* !TT_CONFIG_OPTION_COLOR_LAYERS */
|
|
|
|
|
|
|
|
/* EOF */
|