[color] Minimal API for COLR/CPAL

This commit is contained in:
Khaled Hosny 2018-05-01 17:16:46 +02:00
parent 0229eaea29
commit d4e928b142
11 changed files with 253 additions and 136 deletions

View File

@ -170,6 +170,7 @@ HB_OT_RAGEL_sources = \
HB_OT_headers = \
hb-ot.h \
hb-ot-color.h \
hb-ot-font.h \
hb-ot-layout.h \
hb-ot-math.h \

View File

@ -83,13 +83,21 @@ static void svg_callback (const uint8_t* data, unsigned int length,
fclose (f);
}
static void colr_cpal_rendering (cairo_font_face_t *cairo_face, unsigned int upem, unsigned int num_glyphs,
const OT::COLR *colr, const OT::CPAL *cpal)
static void colr_cpal_rendering (hb_face_t *face, cairo_font_face_t *cairo_face)
{
for (unsigned int i = 0; i < num_glyphs; ++i)
unsigned int upem = hb_face_get_upem (face);
for (hb_codepoint_t gid = 0; gid < hb_face_get_glyph_count (face); ++gid)
{
unsigned int first_layer_index, num_layers;
if (colr->get_base_glyph_record (i, &first_layer_index, &num_layers))
unsigned int num_layers = hb_ot_color_get_color_layers (face, gid, 0, nullptr, nullptr, nullptr);
if (!num_layers)
continue;
hb_codepoint_t *layer_gids = (hb_codepoint_t*) calloc (num_layers, sizeof (hb_codepoint_t));
unsigned int *color_indices = (unsigned int*) calloc (num_layers, sizeof (unsigned int));
hb_ot_color_get_color_layers (face, gid, 0, &num_layers, layer_gids, color_indices);
if (num_layers)
{
// Measure
cairo_text_extents_t extents;
@ -101,12 +109,7 @@ static void colr_cpal_rendering (cairo_font_face_t *cairo_face, unsigned int upe
cairo_glyph_t *glyphs = (cairo_glyph_t *) calloc (num_layers, sizeof (cairo_glyph_t));
for (unsigned int j = 0; j < num_layers; ++j)
{
hb_codepoint_t glyph_id;
unsigned int color_index;
colr->get_layer_record (first_layer_index + j, &glyph_id, &color_index);
glyphs[j].index = glyph_id;
}
glyphs[j].index = layer_gids[j];
cairo_glyph_extents (cr, glyphs, num_layers, &extents);
free (glyphs);
cairo_surface_destroy (surface);
@ -120,45 +123,56 @@ static void colr_cpal_rendering (cairo_font_face_t *cairo_face, unsigned int upe
extents.y_bearing -= extents.height / 20;
// Render
unsigned int pallet_count = cpal->get_palette_count ();
unsigned int pallet_count = hb_ot_color_get_palette_count (face);
for (unsigned int pallet = 0; pallet < pallet_count; ++pallet) {
char output_path[255];
// If we have more than one pallet, use a better namin
if (pallet_count == 1)
sprintf (output_path, "out/colr-%d.svg", i);
else
sprintf (output_path, "out/colr-%d-%d.svg", i, pallet);
unsigned int num_colors = hb_ot_color_get_palette_colors (face, pallet, 0, nullptr, nullptr);
if (!num_colors)
continue;
cairo_surface_t *surface = cairo_svg_surface_create (output_path, extents.width, extents.height);
cairo_t *cr = cairo_create (surface);
cairo_set_font_face (cr, cairo_face);
cairo_set_font_size (cr, upem);
hb_ot_color_t *colors = (hb_ot_color_t*) calloc (num_colors, sizeof (hb_ot_color_t));
hb_ot_color_get_palette_colors (face, pallet, 0, &num_colors, colors);
if (num_colors)
{
// If we have more than one pallet, use a better namin
if (pallet_count == 1)
sprintf (output_path, "out/colr-%d.svg", gid);
else
sprintf (output_path, "out/colr-%d-%d.svg", gid, pallet);
for (unsigned int j = 0; j < num_layers; ++j)
{
hb_codepoint_t glyph_id;
unsigned int color_index;
colr->get_layer_record (first_layer_index + j, &glyph_id, &color_index);
cairo_surface_t *surface = cairo_svg_surface_create (output_path, extents.width, extents.height);
cairo_t *cr = cairo_create (surface);
cairo_set_font_face (cr, cairo_face);
cairo_set_font_size (cr, upem);
uint32_t color = cpal->get_color_record_argb (color_index, pallet);
int alpha = color & 0xFF;
int r = (color >> 8) & 0xFF;
int g = (color >> 16) & 0xFF;
int b = (color >> 24) & 0xFF;
cairo_set_source_rgba (cr, r / 255., g / 255., b / 255., alpha);
for (unsigned int layer = 0; layer < num_layers; ++layer)
{
uint32_t color = 0xFF;
if (color_indices[layer] != 0xFFFF)
color = colors[color_indices[layer]];
int alpha = color & 0xFF;
int r = (color >> 8) & 0xFF;
int g = (color >> 16) & 0xFF;
int b = (color >> 24) & 0xFF;
cairo_set_source_rgba (cr, r / 255., g / 255., b / 255., alpha);
cairo_glyph_t glyph;
glyph.index = glyph_id;
glyph.x = -extents.x_bearing;
glyph.y = -extents.y_bearing;
cairo_show_glyphs (cr, &glyph, 1);
}
cairo_glyph_t glyph;
glyph.index = layer_gids[layer];
glyph.x = -extents.x_bearing;
glyph.y = -extents.y_bearing;
cairo_show_glyphs (cr, &glyph, 1);
}
cairo_surface_destroy (surface);
cairo_destroy (cr);
cairo_surface_destroy (surface);
cairo_destroy (cr);
}
free (colors);
}
}
free (layer_gids);
free (color_indices);
}
}
@ -228,7 +242,7 @@ int main (int argc, char **argv)
font_name_file = fopen ("out/_font_name_file.txt", "w");
if (font_name_file == nullptr)
{
fprintf (stderr, "./out is not accessible, create it please\n");
fprintf (stderr, "./out is not accessible as a folder, create it please\n");
exit (1);
}
fwrite (argv[1], 1, strlen (argv[1]), font_name_file);
@ -253,12 +267,6 @@ int main (int argc, char **argv)
svg.dump (svg_callback);
svg.fini ();
hb_blob_t* colr_blob = hb_sanitize_context_t ().reference_table<OT::COLR> (face);
const OT::COLR *colr = colr_blob->as<OT::COLR> ();
hb_blob_t* cpal_blob = hb_sanitize_context_t ().reference_table<OT::CPAL> (face);
const OT::CPAL *cpal = cpal_blob->as<OT::CPAL> ();
cairo_font_face_t *cairo_face;
{
FT_Library library;
@ -269,7 +277,7 @@ int main (int argc, char **argv)
}
unsigned int num_glyphs = hb_face_get_glyph_count (face);
unsigned int upem = hb_face_get_upem (face);
colr_cpal_rendering (cairo_face, upem, num_glyphs, colr, cpal);
colr_cpal_rendering (face, cairo_face);
dump_glyphs (cairo_face, upem, num_glyphs);

View File

@ -105,24 +105,24 @@ struct COLR
if (unlikely (!record))
return false;
*first_layer = record->firstLayerIdx;
*num_layers = record->numLayers;
if (first_layer) *first_layer = record->firstLayerIdx;
if (num_layers) *num_layers = record->numLayers;
return true;
}
inline bool get_layer_record (unsigned int record,
hb_codepoint_t *glyph_id /* OUT */,
unsigned int *palette_index /* OUT */) const
unsigned int *color_index /* OUT */) const
{
if (unlikely (record >= numLayers))
{
*glyph_id = 0;
*palette_index = 0xFFFF;
*color_index = 0xFFFF;
return false;
}
const LayerRecord &layer = (this+layersZ)[record];
*glyph_id = layer.glyphid;
*palette_index = layer.colorIdx;
if (glyph_id) *glyph_id = layer.glyphid;
if (color_index) *color_index = layer.colorIdx;
return true;
}

View File

@ -29,21 +29,13 @@
#define HB_OT_COLOR_CPAL_TABLE_HH
#include "hb-open-type.hh"
#include "hb-ot-color.h"
/*
* Following parts to be moved to a public header.
*/
/**
* hb_ot_color_t:
* ARGB data type for holding color values.
*
* Since: REPLACEME
*/
typedef uint32_t hb_ot_color_t;
/**
* hb_ot_color_palette_flags_t:
* @HB_OT_COLOR_PALETTE_FLAG_DEFAULT: default indicating that there is nothing special to note about a color palette.
@ -58,32 +50,6 @@ typedef enum { /*< flags >*/
HB_OT_COLOR_PALETTE_FLAG_FOR_DARK_BACKGROUND = 0x00000002u,
} hb_ot_color_palette_flags_t;
// HB_EXTERN unsigned int
// hb_ot_color_get_palette_count (hb_face_t *face);
// HB_EXTERN unsigned int
// hb_ot_color_get_palette_name_id (hb_face_t *face, unsigned int palette);
// HB_EXTERN hb_ot_color_palette_flags_t
// hb_ot_color_get_palette_flags (hb_face_t *face, unsigned int palette);
// HB_EXTERN unsigned int
// hb_ot_color_get_palette_colors (hb_face_t *face,
// unsigned int palette, /* default=0 */
// unsigned int start_offset,
// unsigned int *color_count /* IN/OUT */,
// hb_ot_color_t *colors /* OUT */);
/*
* CPAL -- Color Palette
* https://docs.microsoft.com/en-us/typography/opentype/spec/cpal
*/
#define HB_OT_TAG_CPAL HB_TAG('C','P','A','L')
namespace OT {
@ -189,15 +155,22 @@ struct CPAL
return numPalettes;
}
inline hb_ot_color_t
get_color_record_argb (unsigned int color_index, unsigned int palette) const
inline unsigned int get_palette_entries_count () const
{
return numPaletteEntries;
}
bool
get_color_record_argb (unsigned int color_index, unsigned int palette, hb_ot_color_t* color) const
{
if (unlikely (color_index >= numPaletteEntries || palette >= numPalettes))
return 0;
return false;
// No need for more range check as it is already done on #sanitize
const UnsizedArrayOf<BGRAColor>& color_records = this+colorRecordsZ;
return color_records[colorRecordIndicesZ[palette] + color_index];
if (color)
*color = color_records[colorRecordIndicesZ[palette] + color_index];
return true;
}
protected:

View File

@ -28,33 +28,45 @@
#include "hb-open-type.hh"
#include "hb-ot-color-colr-table.hh"
#include "hb-ot-color-cpal-table.hh"
#include "hb-ot-face.hh"
#include "hb-ot.h"
#include <stdlib.h>
#include <string.h>
#include "hb-ot-layout.hh"
#include "hb-shaper.hh"
#if 0
HB_MARK_AS_FLAG_T (hb_ot_color_palette_flags_t)
//HB_SHAPER_DATA_ENSURE_DECLARE(ot, face) Hmm?
#endif
static inline const OT::COLR&
_get_colr (hb_face_t *face)
{
if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return Null(OT::COLR);
return *(hb_ot_face_data (face)->colr.get ());
return *(hb_ot_face_data (face)->COLR.get ());
}
static inline const OT::CPAL&
_get_cpal (hb_face_t *face)
{
if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return Null(OT::CPAL);
return *(hb_ot_face_data (face)->cpal.get ());
return *(hb_ot_face_data (face)->CPAL.get ());
}
HB_EXTERN hb_bool_t
hb_ot_color_has_cpal_data (hb_face_t *face)
{
return &_get_cpal (face) != &OT::Null(OT::CPAL);
}
HB_EXTERN hb_bool_t
hb_ot_color_has_colr_data (hb_face_t *face)
{
return &_get_colr (face) != &OT::Null(OT::COLR);
}
/**
* hb_ot_color_get_palette_count:
@ -72,7 +84,7 @@ hb_ot_color_get_palette_count (hb_face_t *face)
return cpal.get_palette_count ();
}
#if 0
/**
* hb_ot_color_get_palette_name_id:
* @face: a font face.
@ -114,6 +126,7 @@ hb_ot_color_get_palette_flags (hb_face_t *face, unsigned int palette)
const OT::CPAL& cpal = _get_cpal(face);
return cpal.get_palette_flags (palette);
}
#endif
/**
@ -125,7 +138,7 @@ hb_ot_color_get_palette_flags (hb_face_t *face, unsigned int palette)
* @color_count: (inout) (optional): on input, how many colors
* can be maximally stored into the @colors array;
* on output, how many colors were actually stored.
* @colors: (array length=color_count) (optional):
* @colors: (array length=color_count) (out) (optional):
* an array of #hb_ot_color_t records. After calling
* this function, @colors will be filled with
* the palette colors. If @colors is NULL, the function
@ -144,38 +157,60 @@ hb_ot_color_get_palette_flags (hb_face_t *face, unsigned int palette)
* Since: REPLACEME
*/
unsigned int
hb_ot_color_get_palette_colors (hb_face_t *face,
unsigned int palette, /* default=0 */
unsigned int start_offset,
unsigned int *color_count /* IN/OUT */,
hb_ot_color_t *colors /* OUT */)
hb_ot_color_get_palette_colors (hb_face_t *face,
unsigned int palette, /* default=0 */
unsigned int start_offset,
unsigned int *count /* IN/OUT */,
hb_ot_color_t *colors /* OUT */)
{
const OT::CPAL& cpal = _get_cpal(face);
if (unlikely (palette >= cpal.numPalettes))
if (unlikely (palette >= cpal.get_palette_count ()))
{
if (color_count) *color_count = 0;
if (count) *count = 0;
return 0;
}
const OT::ColorRecord* crec = &cpal.offsetFirstColorRecord (&cpal);
crec += cpal.colorRecordIndices[palette];
unsigned int num_results = 0;
if (likely (color_count && colors))
if (count)
{
for (unsigned int i = start_offset;
i < cpal.numPaletteEntries && num_results < *color_count; ++i)
unsigned int platte_count = MIN<unsigned int>(*count, cpal.get_palette_entries_count () - start_offset);
for (unsigned int i = 0; i < platte_count; i++)
{
hb_ot_color_t* result = &colors[num_results];
result->red = crec[i].red;
result->green = crec[i].green;
result->blue = crec[i].blue;
result->alpha = crec[i].alpha;
++num_results;
if (cpal.get_color_record_argb(start_offset + i, palette, &colors[num_results]))
++num_results;
}
}
if (likely (color_count)) *color_count = num_results;
return cpal.numPaletteEntries;
if (likely (count)) *count = num_results;
return cpal.get_palette_entries_count ();
}
unsigned int
hb_ot_color_get_color_layers (hb_face_t *face,
hb_codepoint_t gid,
unsigned int start_offset,
unsigned int *count, /* IN/OUT. May be NULL. */
hb_codepoint_t *gids, /* OUT. May be NULL. */
unsigned int *color_indices /* OUT. May be NULL. */)
{
const OT::COLR& colr = _get_colr (face);
unsigned int num_results = 0;
unsigned int start_layer_index, num_layers = 0;
if (colr.get_base_glyph_record (gid, &start_layer_index, &num_layers))
{
if (count)
{
unsigned int layer_count = MIN<unsigned int>(*count, num_layers - start_offset);
printf ("%d ", *count);
for (unsigned int i = 0; i < layer_count; i++)
{
if (colr.get_layer_record (start_layer_index + start_offset + i,
&gids[num_results], &color_indices[num_results]))
++num_results;
}
}
}
if (likely (count)) *count = num_results;
return num_layers;
}
#endif

85
src/hb-ot-color.h Normal file
View File

@ -0,0 +1,85 @@
/*
* Copyright © 2016 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Google Author(s): Sascha Brawer
*/
#ifndef HB_OT_H_IN
#error "Include <hb-ot.h> instead."
#endif
#ifndef HB_OT_COLOR_H
#define HB_OT_COLOR_H
#include "hb.h"
HB_BEGIN_DECLS
/*
* CPAL -- Color Palette
* https://docs.microsoft.com/en-us/typography/opentype/spec/cpal
*/
#define HB_OT_TAG_CPAL HB_TAG('C','P','A','L')
/**
* hb_ot_color_t:
* ARGB data type for holding color values.
*
* Since: REPLACEME
*/
typedef uint32_t hb_ot_color_t;
HB_EXTERN hb_bool_t
hb_ot_color_has_cpal_data (hb_face_t *face);
HB_EXTERN hb_bool_t
hb_ot_color_has_colr_data (hb_face_t *face);
HB_EXTERN unsigned int
hb_ot_color_get_palette_count (hb_face_t *face);
// HB_EXTERN unsigned int
// hb_ot_color_get_palette_name_id (hb_face_t *face, unsigned int palette);
// HB_EXTERN hb_ot_color_palette_flags_t
// hb_ot_color_get_palette_flags (hb_face_t *face, unsigned int palette);
HB_EXTERN unsigned int
hb_ot_color_get_palette_colors (hb_face_t *face,
unsigned int palette, /* default=0 */
unsigned int start_offset,
unsigned int *color_count /* IN/OUT */,
hb_ot_color_t *colors /* OUT */);
HB_EXTERN unsigned int
hb_ot_color_get_color_layers (hb_face_t *face,
hb_codepoint_t gid,
unsigned int offset,
unsigned int *count, /* IN/OUT */
hb_codepoint_t *gids, /* OUT */
unsigned int *color_indices /* OUT */);
HB_END_DECLS
#endif /* HB_OT_COLOR_H */

View File

@ -47,6 +47,9 @@
/* OpenType shaping. */ \
HB_OT_TABLE(OT, JSTF) \
HB_OT_TABLE(OT, BASE) \
/* OpenType color */ \
HB_OT_TABLE(OT, COLR) \
HB_OT_TABLE(OT, CPAL) \
/* AAT shaping. */ \
HB_OT_TABLE(AAT, morx) \
HB_OT_TABLE(AAT, kerx) \

View File

@ -30,6 +30,7 @@
#include "hb.h"
#include "hb-ot-color.h"
#include "hb-ot-font.h"
#include "hb-ot-layout.h"
#include "hb-ot-math.h"

View File

@ -98,27 +98,25 @@ static hb_face_t *cpal_v0 = NULL;
*/
static hb_face_t *cpal_v1 = NULL;
#if 0
#define assert_color_rgba(colors, i, r, g, b, a) G_STMT_START { \
const hb_ot_color_t *_colors = (colors); \
const size_t _i = (i); \
const uint8_t red = (r), green = (g), blue = (b), alpha = (a); \
if (_colors[_i].red != red) { \
if ((_colors[_i] >> 16 & 0xff) != red) { \
g_assertion_message_cmpnum (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \
"colors[" #i "].red", _colors[_i].red, "==", red, 'x'); \
"colors[" #i "]", _colors[_i], "==", red, 'x'); \
} \
if (_colors[_i].green != green) { \
if ((_colors[_i] >> 8 & 0xff) != green) { \
g_assertion_message_cmpnum (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \
"colors[" #i "].green", _colors[_i].green, "==", green, 'x'); \
"colors[" #i "]", _colors[_i], "==", green, 'x'); \
} \
if (_colors[_i].blue != blue) { \
if ((_colors[_i] & 0xff) != blue) { \
g_assertion_message_cmpnum (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \
"colors[" #i "].blue", colors[i].blue, "==", blue, 'x'); \
"colors[" #i "]", colors[_i], "==", blue, 'x'); \
} \
if (_colors[_i].alpha != alpha) { \
if ((_colors[_i] >> 24 & 0xff) != alpha) { \
g_assertion_message_cmpnum (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \
"colors[" #i "].alpha", _colors[_i].alpha, "==", alpha, 'x'); \
"colors[" #i "]", _colors[_i], "==", alpha, 'x'); \
} \
} G_STMT_END
@ -132,6 +130,7 @@ test_hb_ot_color_get_palette_count (void)
}
#if 0
static void
test_hb_ot_color_get_palette_name_id_empty (void)
{
@ -193,6 +192,7 @@ test_hb_ot_color_get_palette_flags_v1 (void)
/* numPalettes=3, so palette #3 is out of bounds */
g_assert_cmpint (hb_ot_color_get_palette_flags (cpal_v0, 3), ==, HB_OT_COLOR_PALETTE_FLAG_DEFAULT);
}
#endif
static void
@ -292,26 +292,37 @@ test_hb_ot_color_get_palette_colors_v1 (void)
assert_color_rgba (colors, 1, 0x77, 0x77, 0x77, 0x77); /* untouched */
assert_color_rgba (colors, 2, 0x77, 0x77, 0x77, 0x77); /* untouched */
}
static inline hb_face_t *
open_font (const char *font_path)
{
#if GLIB_CHECK_VERSION(2,37,2)
char* path = g_test_build_filename(G_TEST_DIST, font_path, NULL);
#else
char* path = g_strdup(font_path);
#endif
return hb_face_create (hb_blob_create_from_file (path), 0);
}
int
main (int argc, char **argv)
{
int status = 0;
hb_test_init (&argc, &argv);
// cpal_v0 = hb_test_load_face ("../shaping/data/in-house/fonts/e90374e5e439e00725b4fe7a8d73db57c5a97f82.ttf");
// cpal_v1 = hb_test_load_face ("../shaping/data/in-house/fonts/319f5d7ebffbefc5c5e6569f8cea73444d7a7268.ttf");
// hb_test_add (test_hb_ot_color_get_palette_count);
cpal_v0 = open_font ("fonts/cpal-v0.ttf");
cpal_v1 = open_font ("fonts/cpal-v1.ttf");
hb_test_add (test_hb_ot_color_get_palette_count);
// hb_test_add (test_hb_ot_color_get_palette_name_id_empty);
// hb_test_add (test_hb_ot_color_get_palette_name_id_v0);
// hb_test_add (test_hb_ot_color_get_palette_name_id_v1);
// hb_test_add (test_hb_ot_color_get_palette_flags_empty);
// hb_test_add (test_hb_ot_color_get_palette_flags_v0);
// hb_test_add (test_hb_ot_color_get_palette_flags_v1);
// hb_test_add (test_hb_ot_color_get_palette_colors_empty);
// hb_test_add (test_hb_ot_color_get_palette_colors_v0);
// hb_test_add (test_hb_ot_color_get_palette_colors_v1);
hb_test_add (test_hb_ot_color_get_palette_colors_empty);
hb_test_add (test_hb_ot_color_get_palette_colors_v0);
hb_test_add (test_hb_ot_color_get_palette_colors_v1);
status = hb_test_run();
hb_face_destroy (cpal_v0);
hb_face_destroy (cpal_v1);