From a62554af89b8324c73c623e64f87ec822c757515 Mon Sep 17 00:00:00 2001 From: Ebrahim Byagowi Date: Tue, 10 Apr 2018 00:53:50 +0430 Subject: [PATCH] [colr/cpal] Improvements and add a sample renderer (#927) --- src/Makefile.am | 13 ++- src/dump-emoji.cc | 181 +++++++++++++++++++++++++++++++--- src/hb-ot-color-colr-table.hh | 43 +++++++- src/hb-ot-color-cpal-table.hh | 70 +++++++------ 4 files changed, 259 insertions(+), 48 deletions(-) diff --git a/src/Makefile.am b/src/Makefile.am index c5a01e75d..46fc141a4 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -362,15 +362,11 @@ dist_check_SCRIPTS += \ endif check_PROGRAMS += \ - dump-emoji \ dump-indic-data \ dump-khmer-data \ dump-myanmar-data \ dump-use-data \ $(NULL) -dump_emoji_SOURCES = dump-emoji.cc -dump_emoji_CPPFLAGS = $(HBCFLAGS) -dump_emoji_LDADD = libharfbuzz.la $(HBLIBS) dump_indic_data_SOURCES = dump-indic-data.cc hb-ot-shape-complex-indic-table.cc dump_indic_data_CPPFLAGS = $(HBCFLAGS) dump_indic_data_LDADD = libharfbuzz.la $(HBLIBS) @@ -384,6 +380,15 @@ dump_use_data_SOURCES = dump-use-data.cc hb-ot-shape-complex-use-table.cc dump_use_data_CPPFLAGS = $(HBCFLAGS) dump_use_data_LDADD = libharfbuzz.la $(HBLIBS) +if HAVE_FREETYPE +if HAVE_CAIRO_FT +check_PROGRAMS += dump-emoji +dump_emoji_SOURCES = dump-emoji.cc +dump_emoji_CPPFLAGS = $(HBCFLAGS) $(FREETYPE_CFLAGS) $(CAIRO_FT_CFLAGS) +dump_emoji_LDADD = libharfbuzz.la $(HBLIBS) $(FREETYPE_LIBS) $(CAIRO_LIBS) $(CAIRO_FT_LIBS) +endif # HAVE_CAIRO_FT +endif # HAVE_FREETYPE + check_PROGRAMS += test-ot-tag test-unicode-ranges TESTS += test-ot-tag test-unicode-ranges diff --git a/src/dump-emoji.cc b/src/dump-emoji.cc index a9595e41e..db586bd21 100644 --- a/src/dump-emoji.cc +++ b/src/dump-emoji.cc @@ -25,9 +25,21 @@ #include "hb.h" #include "hb-private.hh" #include "hb-ot-color-cbdt-table.hh" +#include "hb-ot-color-colr-table.hh" +#include "hb-ot-color-cpal-table.hh" #include "hb-ot-color-sbix-table.hh" #include "hb-ot-color-svg-table.hh" +#include "hb-ft.h" + +#include +#include FT_FREETYPE_H +#include FT_GLYPH_H + +#include +#include +#include + #ifdef HAVE_GLIB #include #endif @@ -41,9 +53,9 @@ const void * const OT::_hb_NullPool[HB_NULL_POOL_SIZE / sizeof (void *)] = {}; void cbdt_callback (const uint8_t* data, unsigned int length, unsigned int group, unsigned int gid) { - char outName[255]; - sprintf (outName, "out/cbdt-%d-%d.png", group, gid); - FILE *f = fopen (outName, "wb"); + char output_path[255]; + sprintf (output_path, "out/cbdt-%d-%d.png", group, gid); + FILE *f = fopen (output_path, "wb"); fwrite (data, 1, length, f); fclose (f); } @@ -51,9 +63,9 @@ void cbdt_callback (const uint8_t* data, unsigned int length, void sbix_callback (const uint8_t* data, unsigned int length, unsigned int group, unsigned int gid) { - char outName[255]; - sprintf (outName, "out/sbix-%d-%d.png", group, gid); - FILE *f = fopen (outName, "wb"); + char output_path[255]; + sprintf (output_path, "out/sbix-%d-%d.png", group, gid); + FILE *f = fopen (output_path, "wb"); fwrite (data, 1, length, f); fclose (f); } @@ -61,22 +73,145 @@ void sbix_callback (const uint8_t* data, unsigned int length, void svg_callback (const uint8_t* data, unsigned int length, unsigned int start_glyph, unsigned int end_glyph) { - char outName[255]; + char output_path[255]; if (start_glyph == end_glyph) - sprintf (outName, "out/svg-%d.svg", start_glyph); + sprintf (output_path, "out/svg-%d.svg", start_glyph); else - sprintf (outName, "out/svg-%d-%d.svg", start_glyph, end_glyph); + sprintf (output_path, "out/svg-%d-%d.svg", start_glyph, end_glyph); // append "z" if the content is gzipped if ((data[0] == 0x1F) && (data[1] == 0x8B)) - strcat (outName, "z"); + strcat (output_path, "z"); - FILE *f = fopen (outName, "wb"); + FILE *f = fopen (output_path, "wb"); fwrite (data, 1, length, f); fclose (f); } -int main(int argc, char **argv) +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) +{ + for (int i = 0; i < num_glyphs; ++i) + { + unsigned int first_layer_index, num_layers; + if (colr->get_base_glyph_record (i, &first_layer_index, &num_layers)) + { + // Measure + cairo_text_extents_t extents; + { + cairo_surface_t *surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 1, 1); + cairo_t *cr = cairo_create (surface); + cairo_set_font_face (cr, cairo_face); + cairo_set_font_size (cr, upem); + + 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; + } + cairo_glyph_extents (cr, glyphs, num_layers, &extents); + free (glyphs); + cairo_surface_destroy (surface); + cairo_destroy (cr); + } + + // Add a slight margin + extents.width += extents.width / 10; + extents.height += extents.height / 10; + extents.x_bearing -= extents.width / 20; + extents.y_bearing -= extents.height / 20; + + // Render + unsigned int pallet_count = cpal->get_palette_count (); + 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); + + 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); + + 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); + + 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.f, g / 255.f, b / 255.f, 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_surface_destroy (surface); + cairo_destroy (cr); + } + } + } +} + +void dump_glyphs (cairo_font_face_t *cairo_face, unsigned int upem, unsigned int num_glyphs) +{ + // Dump every glyph available on the font + for (int i = 0; i < num_glyphs; ++i) + { + cairo_text_extents_t extents; + cairo_glyph_t glyph = {0}; + glyph.index = i; + + // Measure + { + cairo_surface_t *surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 1, 1); + cairo_t *cr = cairo_create (surface); + cairo_set_font_face (cr, cairo_face); + cairo_set_font_size (cr, upem); + + cairo_glyph_extents (cr, &glyph, 1, &extents); + cairo_surface_destroy (surface); + cairo_destroy (cr); + } + + // Add a slight margin + extents.width += extents.width / 10; + extents.height += extents.height / 10; + extents.x_bearing -= extents.width / 20; + extents.y_bearing -= extents.height / 20; + + // Render + { + char output_path[255]; + sprintf (output_path, "out/%d.svg", i); + 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); + glyph.x = -extents.x_bearing; + glyph.y = -extents.y_bearing; + cairo_show_glyphs (cr, &glyph, 1); + cairo_surface_destroy (surface); + cairo_destroy (cr); + } + } +} + +int main (int argc, char **argv) { if (argc != 2) { fprintf (stderr, "usage: %s font-file.ttf\n", argv[0]); @@ -133,6 +268,28 @@ int main(int argc, char **argv) svg.dump (svg_callback); svg.fini (); + OT::Sanitizer sanitizerCOLR; + hb_blob_t* colr_blob = sanitizerCOLR.sanitize (face->reference_table (HB_OT_TAG_COLR)); + const OT::COLR *colr = OT::Sanitizer::lock_instance (colr_blob); + + OT::Sanitizer sanitizerCPAL; + hb_blob_t* cpal_blob = sanitizerCPAL.sanitize (face->reference_table (HB_OT_TAG_CPAL)); + const OT::CPAL *cpal = OT::Sanitizer::lock_instance (cpal_blob); + + cairo_font_face_t *cairo_face; + { + FT_Library library; + FT_Init_FreeType (&library); + FT_Face ftface; + FT_New_Face (library, argv[1], 0, &ftface); + cairo_face = cairo_ft_font_face_create_for_ft_face (ftface, 0); + } + 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); + dump_glyphs (cairo_face, upem, num_glyphs); + + hb_font_destroy (font); hb_face_destroy (face); hb_blob_destroy (blob); diff --git a/src/hb-ot-color-colr-table.hh b/src/hb-ot-color-colr-table.hh index c1cf6de57..5308b5a1f 100644 --- a/src/hb-ot-color-colr-table.hh +++ b/src/hb-ot-color-colr-table.hh @@ -64,6 +64,10 @@ struct BaseGlyphRecord return_trace (c->check_struct (this)); } + inline int cmp (hb_codepoint_t g) const { + return g < glyphid ? -1 : g > glyphid ? 1 : 0; + } + protected: GlyphID glyphid; /* Glyph ID of reference glyph */ HBUINT16 firstLayerIdx; /* Index to the layer record */ @@ -72,6 +76,13 @@ struct BaseGlyphRecord DEFINE_SIZE_STATIC (6); }; +static int compare_bgr (const void *pa, const void *pb) +{ + const hb_codepoint_t *a = (const hb_codepoint_t *) pa; + const BaseGlyphRecord *b = (const BaseGlyphRecord *) pb; + return b->cmp (*a); +} + struct COLR { static const hb_tag_t tableTag = HB_OT_TAG_COLR; @@ -80,17 +91,41 @@ struct COLR { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && - (this+baseGlyphs).sanitize (c, numBaseGlyphs) && - (this+layers).sanitize (c, numLayers)); + (this+baseGlyphsZ).sanitize (c, numBaseGlyphs) && + (this+layersZ).sanitize (c, numLayers)); + } + + inline bool get_base_glyph_record (hb_codepoint_t glyph_id, + unsigned int *first_layer /* OUT */, + unsigned int *num_layers /* OUT */) const + { + const BaseGlyphRecord* record; + record = (BaseGlyphRecord *) bsearch (&glyph_id, &(this+baseGlyphsZ), numBaseGlyphs, + sizeof (BaseGlyphRecord), compare_bgr); + if (!record) + return false; + + *first_layer = record->firstLayerIdx; + *num_layers = record->numLayers; + return true; + } + + inline void get_layer_record (unsigned int record, + hb_codepoint_t *glyph_id /* OUT */, + unsigned int *palette_index /* OUT */) const + { + const LayerRecord &layer = (this+layersZ)[record]; + *glyph_id = layer.glyphid; + *palette_index = layer.colorIdx; } protected: HBUINT16 version; /* Table version number */ HBUINT16 numBaseGlyphs; /* Number of Base Glyph Records */ LOffsetTo > - baseGlyphs; /* Offset to Base Glyph records. */ + baseGlyphsZ; /* Offset to Base Glyph records. */ LOffsetTo > - layers; /* Offset to Layer Records */ + layersZ; /* Offset to Layer Records */ HBUINT16 numLayers; /* Number of Layer Records */ public: DEFINE_SIZE_STATIC (14); diff --git a/src/hb-ot-color-cpal-table.hh b/src/hb-ot-color-cpal-table.hh index e364c8a44..7feb3e1bf 100644 --- a/src/hb-ot-color-cpal-table.hh +++ b/src/hb-ot-color-cpal-table.hh @@ -92,35 +92,44 @@ struct CPALV1Tail { friend struct CPAL; - inline bool sanitize (hb_sanitize_context_t *c, unsigned int palettes) const + inline bool + sanitize (hb_sanitize_context_t *c, const void *base, unsigned int palettes) const { TRACE_SANITIZE (this); - return_trace ( - c->check_struct (this) && - c->check_array ((const void*) &paletteFlags, sizeof (HBUINT32), palettes) && - c->check_array ((const void*) &paletteLabel, sizeof (HBUINT16), palettes) && - c->check_array ((const void*) &paletteEntryLabel, sizeof (HBUINT16), palettes)); + return_trace (c->check_struct (this) && + (base+paletteFlagsZ).sanitize (c, palettes) && + (base+paletteLabelZ).sanitize (c, palettes) && + (base+paletteEntryLabelZ).sanitize (c, palettes)); } private: inline hb_ot_color_palette_flags_t get_palette_flags (const void *base, unsigned int palette) const { - const HBUINT32* flags = &paletteFlags (base); - return (hb_ot_color_palette_flags_t) (uint32_t) flags[palette]; + // range checked at the CPAL caller + return (hb_ot_color_palette_flags_t) (uint32_t) (base+paletteFlagsZ)[palette]; } inline unsigned int get_palette_name_id (const void *base, unsigned int palette) const { - const HBUINT16* name_ids = &paletteLabel (base); - return name_ids[palette]; + // range checked at the CPAL caller + return (base+paletteLabelZ)[palette]; } protected: - LOffsetTo paletteFlags; - LOffsetTo paletteLabel; - LOffsetTo paletteEntryLabel; + LOffsetTo > + paletteFlagsZ; /* Offset from the beginning of CPAL table to + * the Palette Type Array. Set to 0 if no array + * is provided. */ + LOffsetTo > + paletteLabelZ; /* Offset from the beginning of CPAL table to + * the Palette Labels Array. Set to 0 if no + * array is provided. */ + LOffsetTo > + paletteEntryLabelZ; /* Offset from the beginning of CPAL table to + * the Palette Entry Label Array. Set to 0 + * if no array is provided. */ public: DEFINE_SIZE_STATIC (12); }; @@ -134,13 +143,13 @@ struct CPAL inline bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - if (!(c->check_struct (this) && // This checks colorRecordIndicesX sanity also, see #get_size - c->check_array ((const void*) &colorRecordsZ, sizeof (BGRAColor), numColorRecords))) + if (!(c->check_struct (this) && // it checks colorRecordIndices also, see #get_size + (this+colorRecordsZ).sanitize (c, numColorRecords))) return_trace (false); // Check for indices sanity so no need for doing it runtime for (unsigned int i = 0; i < numPalettes; ++i) - if (colorRecordIndicesX[i] + numPaletteEntries > numColorRecords) + if (colorRecordIndicesZ[i] + numPaletteEntries > numColorRecords) return_trace (false); // If version is zero, we are done here; otherwise we need to check tail also @@ -148,7 +157,7 @@ struct CPAL return_trace (true); const CPALV1Tail &v1 = StructAfter (*this); - return_trace (v1.sanitize (c, numPalettes)); + return_trace (v1.sanitize (c, this, numPalettes)); } inline unsigned int get_size (void) const @@ -179,27 +188,32 @@ struct CPAL return numPalettes; } - inline hb_ot_color_t get_color_record_argb (unsigned int color_index, unsigned int palette) const + inline hb_ot_color_t + get_color_record_argb (unsigned int color_index, unsigned int palette) const { if (color_index >= numPaletteEntries || palette >= numPalettes) return 0; - const BGRAColor* records = &colorRecordsZ(this); // No need for more range check as it is already done on #sanitize - return records[colorRecordIndicesX[palette] + color_index]; + return (this+colorRecordsZ)[colorRecordIndicesZ[palette] + color_index]; } protected: - HBUINT16 version; + HBUINT16 version; /* Table version number */ /* Version 0 */ - HBUINT16 numPaletteEntries; - HBUINT16 numPalettes; - HBUINT16 numColorRecords; - LOffsetTo colorRecordsZ; - HBUINT16 colorRecordIndicesX[VAR]; // VAR=numPalettes -/*CPALV1Tail v1[VAR];*/ + HBUINT16 numPaletteEntries; /* Number of palette entries in each palette. */ + HBUINT16 numPalettes; /* Number of palettes in the table. */ + HBUINT16 numColorRecords; /* Total number of color records, combined for + * all palettes. */ + LOffsetTo > + colorRecordsZ; /* Offset from the beginning of CPAL table to + * the first ColorRecord. */ + UnsizedArrayOf + colorRecordIndicesZ; /* Index of each paletteā€™s first color record in + * the combined color record array. */ +/*CPALV1Tail v1;*/ public: - DEFINE_SIZE_ARRAY (12, colorRecordIndicesX); + DEFINE_SIZE_ARRAY (12, colorRecordIndicesZ); }; } /* namespace OT */