Update HarfBuzz to 13.1.1 and optionally use new hb-raster API to render glyphs.
This commit is contained in:
@@ -125,6 +125,8 @@
|
||||
<param index="6" name="spacing_space" type="int" default="0" />
|
||||
<param index="7" name="spacing_glyph" type="int" default="0" />
|
||||
<param index="8" name="baseline_offset" type="float" default="0.0" />
|
||||
<param index="9" name="palette_index" type="int" default="0" />
|
||||
<param index="10" name="custom_colors" type="PackedColorArray" default="PackedColorArray()" />
|
||||
<description>
|
||||
Returns [TextServer] RID of the font cache for specific variation.
|
||||
</description>
|
||||
@@ -226,6 +228,26 @@
|
||||
Returns [Dictionary] with OpenType font name strings (localized font names, version, description, license information, sample text, etc.).
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_palette_colors" qualifiers="const">
|
||||
<return type="PackedColorArray" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<description>
|
||||
Returns the array in the predefined color palette at [param index]. Palette contains all colors used to render font glyphs. Each palette has the same number of colors. Colors can be overridden using [FontVariation].
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_palette_count" qualifiers="const">
|
||||
<return type="int" />
|
||||
<description>
|
||||
Returns the number of predefined color palettes. Palette contains all colors used to render font glyphs. Each palette has the same number of colors.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_palette_name" qualifiers="const">
|
||||
<return type="String" />
|
||||
<param index="0" name="index" type="int" />
|
||||
<description>
|
||||
Returns the name of the predefined color palette at [param index]. Palette contains all colors used to render font glyphs. Each palette has the same number of colors.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_rids" qualifiers="const">
|
||||
<return type="RID[]" />
|
||||
<description>
|
||||
|
||||
@@ -52,6 +52,12 @@
|
||||
<member name="opentype_features" type="Dictionary" setter="set_opentype_features" getter="get_opentype_features" default="{}">
|
||||
A set of OpenType feature tags. More info: [url=https://docs.microsoft.com/en-us/typography/opentype/spec/featuretags]OpenType feature tags[/url].
|
||||
</member>
|
||||
<member name="palette_custom_colors" type="PackedColorArray" setter="set_palette_custom_colors" getter="get_palette_custom_colors" default="PackedColorArray()">
|
||||
An array of colors to override predefined palette. Use [code]Color(0, 0, 0, 0)[/code], to keep predefined palette color at specific position.
|
||||
</member>
|
||||
<member name="palette_index" type="int" setter="set_palette_index" getter="get_palette_index" default="0">
|
||||
A palette index.
|
||||
</member>
|
||||
<member name="spacing_bottom" type="int" setter="set_spacing" getter="get_spacing" default="0">
|
||||
Extra spacing at the bottom of the line in pixels.
|
||||
</member>
|
||||
|
||||
@@ -406,6 +406,36 @@
|
||||
Returns oversampling factor override. If set to a positive value, overrides the oversampling factor of the viewport this font is used in. See [member Viewport.oversampling]. This value doesn't override the [code skip-lint]oversampling[/code] parameter of [code skip-lint]draw_*[/code] methods. Used by dynamic fonts only.
|
||||
</description>
|
||||
</method>
|
||||
<method name="font_get_palette_colors" qualifiers="const">
|
||||
<return type="PackedColorArray" />
|
||||
<param index="0" name="font_rid" type="RID" />
|
||||
<param index="1" name="index" type="int" />
|
||||
<description>
|
||||
Returns the array in the predefined color palette at [param index]. Palette contains all colors used to render font glyphs. Each palette has the same number of colors. Colors can be overridden using [method font_set_palette_custom_colors].
|
||||
</description>
|
||||
</method>
|
||||
<method name="font_get_palette_count" qualifiers="const">
|
||||
<return type="int" />
|
||||
<param index="0" name="font_rid" type="RID" />
|
||||
<description>
|
||||
Returns the number of predefined color palettes. Palette contains all colors used to render font glyphs. Each palette has the same number of colors.
|
||||
</description>
|
||||
</method>
|
||||
<method name="font_get_palette_custom_colors" qualifiers="const">
|
||||
<return type="PackedColorArray" />
|
||||
<param index="0" name="font_rid" type="RID" />
|
||||
<description>
|
||||
Returns array of custom colors to override predefined palette.
|
||||
</description>
|
||||
</method>
|
||||
<method name="font_get_palette_name" qualifiers="const">
|
||||
<return type="String" />
|
||||
<param index="0" name="font_rid" type="RID" />
|
||||
<param index="1" name="index" type="int" />
|
||||
<description>
|
||||
Returns the name of the predefined color palette at [param index]. Palette contains all colors used to render font glyphs. Each palette has the same number of colors.
|
||||
</description>
|
||||
</method>
|
||||
<method name="font_get_scale" qualifiers="const">
|
||||
<return type="float" />
|
||||
<param index="0" name="font_rid" type="RID" />
|
||||
@@ -542,6 +572,13 @@
|
||||
Returns thickness of the underline in pixels.
|
||||
</description>
|
||||
</method>
|
||||
<method name="font_get_used_palette" qualifiers="const">
|
||||
<return type="int" />
|
||||
<param index="0" name="font_rid" type="RID" />
|
||||
<description>
|
||||
Returns used palette index.
|
||||
</description>
|
||||
</method>
|
||||
<method name="font_get_variation_coordinates" qualifiers="const">
|
||||
<return type="Dictionary" />
|
||||
<param index="0" name="font_rid" type="RID" />
|
||||
@@ -882,7 +919,7 @@
|
||||
<method name="font_set_modulate_color_glyphs">
|
||||
<return type="void" />
|
||||
<param index="0" name="font_rid" type="RID" />
|
||||
<param index="1" name="force_autohinter" type="bool" />
|
||||
<param index="1" name="modulate" type="bool" />
|
||||
<description>
|
||||
If set to [code]true[/code], color modulation is applied when drawing colored glyphs, otherwise it's applied to the monochrome glyphs only.
|
||||
</description>
|
||||
@@ -936,6 +973,14 @@
|
||||
If set to a positive value, overrides the oversampling factor of the viewport this font is used in. See [member Viewport.oversampling]. This value doesn't override the [code skip-lint]oversampling[/code] parameter of [code skip-lint]draw_*[/code] methods. Used by dynamic fonts only.
|
||||
</description>
|
||||
</method>
|
||||
<method name="font_set_palette_custom_colors">
|
||||
<return type="void" />
|
||||
<param index="0" name="font_rid" type="RID" />
|
||||
<param index="1" name="colors" type="PackedColorArray" />
|
||||
<description>
|
||||
Sets array of custom colors to override predefined palette. Set to empty array to reset overrides. Use [code]Color(0, 0, 0, 0)[/code], to keep predefined palette color at specific position.
|
||||
</description>
|
||||
</method>
|
||||
<method name="font_set_scale">
|
||||
<return type="void" />
|
||||
<param index="0" name="font_rid" type="RID" />
|
||||
@@ -1044,6 +1089,14 @@
|
||||
Sets thickness of the underline in pixels.
|
||||
</description>
|
||||
</method>
|
||||
<method name="font_set_used_palette">
|
||||
<return type="void" />
|
||||
<param index="0" name="font_rid" type="RID" />
|
||||
<param index="1" name="index" type="int" />
|
||||
<description>
|
||||
Sets used palette index.
|
||||
</description>
|
||||
</method>
|
||||
<method name="font_set_variation_coordinates">
|
||||
<return type="void" />
|
||||
<param index="0" name="font_rid" type="RID" />
|
||||
|
||||
@@ -384,6 +384,36 @@
|
||||
Returns oversampling factor override. If set to a positive value, overrides the oversampling factor of the viewport this font is used in. See [member Viewport.oversampling]. This value doesn't override the [code skip-lint]oversampling[/code] parameter of [code skip-lint]draw_*[/code] methods. Used by dynamic fonts only.
|
||||
</description>
|
||||
</method>
|
||||
<method name="_font_get_palette_colors" qualifiers="virtual const">
|
||||
<return type="PackedColorArray" />
|
||||
<param index="0" name="font_rid" type="RID" />
|
||||
<param index="1" name="index" type="int" />
|
||||
<description>
|
||||
Returns the array in the predefined color palette at [param index]. Palette contains all colors used to render font glyphs. Each palette has the same number of colors. Colors can be overridden using [method _font_set_palette_custom_colors].
|
||||
</description>
|
||||
</method>
|
||||
<method name="_font_get_palette_count" qualifiers="virtual const">
|
||||
<return type="int" />
|
||||
<param index="0" name="font_rid" type="RID" />
|
||||
<description>
|
||||
Returns the number of predefined color palettes. Palette contains all colors used to render font glyphs. Each palette has the same number of colors.
|
||||
</description>
|
||||
</method>
|
||||
<method name="_font_get_palette_custom_colors" qualifiers="virtual const">
|
||||
<return type="PackedColorArray" />
|
||||
<param index="0" name="font_rid" type="RID" />
|
||||
<description>
|
||||
Returns array of custom colors to override predefined palette.
|
||||
</description>
|
||||
</method>
|
||||
<method name="_font_get_palette_name" qualifiers="virtual const">
|
||||
<return type="String" />
|
||||
<param index="0" name="font_rid" type="RID" />
|
||||
<param index="1" name="index" type="int" />
|
||||
<description>
|
||||
Returns the name of the predefined color palette at [param index]. Palette contains all colors used to render font glyphs. Each palette has the same number of colors.
|
||||
</description>
|
||||
</method>
|
||||
<method name="_font_get_scale" qualifiers="virtual required const">
|
||||
<return type="float" />
|
||||
<param index="0" name="font_rid" type="RID" />
|
||||
@@ -520,6 +550,13 @@
|
||||
Returns thickness of the underline in pixels.
|
||||
</description>
|
||||
</method>
|
||||
<method name="_font_get_used_palette" qualifiers="virtual const">
|
||||
<return type="int" />
|
||||
<param index="0" name="font_rid" type="RID" />
|
||||
<description>
|
||||
Returns used palette index.
|
||||
</description>
|
||||
</method>
|
||||
<method name="_font_get_variation_coordinates" qualifiers="virtual const">
|
||||
<return type="Dictionary" />
|
||||
<param index="0" name="font_rid" type="RID" />
|
||||
@@ -919,6 +956,14 @@
|
||||
If set to a positive value, overrides the oversampling factor of the viewport this font is used in. See [member Viewport.oversampling]. This value doesn't override the [code skip-lint]oversampling[/code] parameter of [code skip-lint]draw_*[/code] methods. Used by dynamic fonts only.
|
||||
</description>
|
||||
</method>
|
||||
<method name="_font_set_palette_custom_colors" qualifiers="virtual">
|
||||
<return type="void" />
|
||||
<param index="0" name="font_rid" type="RID" />
|
||||
<param index="1" name="colors" type="PackedColorArray" />
|
||||
<description>
|
||||
Sets array of custom colors to override predefined palette. Set to empty array to reset overrides. Use [code]Color(0, 0, 0, 0)[/code], to keep predefined palette color at specific position.
|
||||
</description>
|
||||
</method>
|
||||
<method name="_font_set_scale" qualifiers="virtual required">
|
||||
<return type="void" />
|
||||
<param index="0" name="font_rid" type="RID" />
|
||||
@@ -1024,6 +1069,14 @@
|
||||
Sets thickness of the underline in pixels.
|
||||
</description>
|
||||
</method>
|
||||
<method name="_font_set_used_palette" qualifiers="virtual">
|
||||
<return type="void" />
|
||||
<param index="0" name="font_rid" type="RID" />
|
||||
<param index="1" name="index" type="int" />
|
||||
<description>
|
||||
Sets used palette index.
|
||||
</description>
|
||||
</method>
|
||||
<method name="_font_set_variation_coordinates" qualifiers="virtual">
|
||||
<return type="void" />
|
||||
<param index="0" name="font_rid" type="RID" />
|
||||
|
||||
@@ -167,6 +167,7 @@ EditorPropertyNameProcessor::EditorPropertyNameProcessor() {
|
||||
capitalize_string_remaps["bptc"] = "BPTC";
|
||||
capitalize_string_remaps["bvh"] = "BVH";
|
||||
capitalize_string_remaps["ca"] = "CA";
|
||||
capitalize_string_remaps["colr"] = "COLR";
|
||||
capitalize_string_remaps["ccdik"] = "CCDIK";
|
||||
capitalize_string_remaps["cd"] = "CD";
|
||||
capitalize_string_remaps["cpu"] = "CPU";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
GH-80954
|
||||
--------
|
||||
Validate extension JSON: Error: Field 'classes/Font/methods/find_variation/arguments': size changed value in new API, from 4 to 9.
|
||||
Validate extension JSON: Error: Field 'classes/Font/methods/find_variation/arguments': size changed value in new API, from 4 to 11.
|
||||
|
||||
Added optional arguments. Compatibility method registered.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
GH-87668
|
||||
--------
|
||||
Validate extension JSON: Error: Field 'classes/Font/methods/find_variation/arguments': size changed value in new API, from 8 to 9.
|
||||
Validate extension JSON: Error: Field 'classes/Font/methods/find_variation/arguments': size changed value in new API, from 8 to 11.
|
||||
|
||||
Added optional "baseline_offset" argument. Compatibility method registered.
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
GH-117149
|
||||
---------
|
||||
Validate extension JSON: Error: Field 'classes/Font/methods/find_variation/arguments': size changed value in new API, from 9 to 11.
|
||||
|
||||
Optional "palette_index" and "custom_colors" arguments added. Compatibility method registered.
|
||||
@@ -99,6 +99,7 @@ if env["builtin_harfbuzz"]:
|
||||
# "src/hb-subset-cff-common.cc",
|
||||
# "src/hb-subset-cff1.cc",
|
||||
# "src/hb-subset-cff2.cc",
|
||||
# "src/hb-subset-cff2-to-cff1.cc",
|
||||
# "src/hb-subset-input.cc",
|
||||
# "src/hb-subset-instancer-iup.cc",
|
||||
# "src/hb-subset-instancer-solver.cc",
|
||||
@@ -109,9 +110,30 @@ if env["builtin_harfbuzz"]:
|
||||
# "src/hb-subset-table-layout.cc",
|
||||
# "src/hb-subset-table-other.cc",
|
||||
# "src/hb-subset-table-var.cc",
|
||||
"src/hb-raster.cc",
|
||||
"src/hb-raster-draw.cc",
|
||||
"src/hb-raster-image.cc",
|
||||
"src/hb-raster-paint.cc",
|
||||
"src/hb-raster-svg-base.cc",
|
||||
"src/hb-raster-svg-bbox.cc",
|
||||
"src/hb-raster-svg-color.cc",
|
||||
"src/hb-raster-svg-clip.cc",
|
||||
"src/hb-raster-svg-defs.cc",
|
||||
"src/hb-raster-svg-defs-scan.cc",
|
||||
"src/hb-raster-svg-gradient.cc",
|
||||
"src/hb-raster-svg-fill.cc",
|
||||
"src/hb-raster-svg-render.cc",
|
||||
"src/hb-raster-svg-parse.cc",
|
||||
"src/hb-raster-svg-use.cc",
|
||||
"src/hb-vector-svg-draw.cc",
|
||||
"src/hb-vector-svg-paint.cc",
|
||||
"src/hb-vector-svg-path.cc",
|
||||
"src/hb-vector-svg-subset.cc",
|
||||
"src/hb-vector-svg-utils.cc",
|
||||
"src/hb-subset.cc",
|
||||
"src/hb-ucd.cc",
|
||||
"src/hb-unicode.cc",
|
||||
"src/hb-zlib.cc",
|
||||
# "src/hb-uniscribe.cc",
|
||||
"src/OT/Var/VARC/VARC.cc",
|
||||
]
|
||||
@@ -128,7 +150,7 @@ if env["builtin_harfbuzz"]:
|
||||
|
||||
env_harfbuzz.Prepend(CPPPATH=["#thirdparty/harfbuzz/src"])
|
||||
|
||||
env_harfbuzz.Append(CPPDEFINES=["HAVE_ICU"])
|
||||
env_harfbuzz.Append(CPPDEFINES=["HAVE_ICU", "HAVE_ZLIB", "HAVE_PNG"])
|
||||
if env["builtin_icu4c"]:
|
||||
env_harfbuzz.Prepend(CPPPATH=["#thirdparty/icu4c/common/", "#thirdparty/icu4c/i18n/"])
|
||||
env_harfbuzz.Append(
|
||||
|
||||
@@ -1085,6 +1085,72 @@ _FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_msdf(
|
||||
#endif
|
||||
|
||||
#ifdef MODULE_FREETYPE_ENABLED
|
||||
#if HB_VERSION_ATLEAST(13, 0, 0)
|
||||
_FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_hb_bitmap(FontForSizeAdvanced *p_data, int p_rect_margin, hb_raster_image_t *p_image, const hb_raster_extents_t &p_ext, const Vector2 &p_advance, bool p_bgra) const {
|
||||
FontGlyph chr;
|
||||
chr.advance = p_advance * p_data->scale;
|
||||
chr.found = true;
|
||||
|
||||
int w = p_ext.width;
|
||||
int h = p_ext.height;
|
||||
|
||||
if (w == 0 || h == 0 || p_image == nullptr) {
|
||||
chr.texture_idx = -1;
|
||||
chr.uv_rect = Rect2();
|
||||
chr.rect = Rect2();
|
||||
return chr;
|
||||
}
|
||||
|
||||
int color_size = p_bgra ? 4 : 2;
|
||||
|
||||
int mw = w + p_rect_margin * 4;
|
||||
int mh = h + p_rect_margin * 4;
|
||||
|
||||
ERR_FAIL_COND_V(mw > 4096, FontGlyph());
|
||||
ERR_FAIL_COND_V(mh > 4096, FontGlyph());
|
||||
|
||||
Image::Format require_format = color_size == 4 ? Image::FORMAT_RGBA8 : Image::FORMAT_LA8;
|
||||
|
||||
FontTexturePosition tex_pos = find_texture_pos_for_glyph(p_data, color_size, require_format, mw, mh, false);
|
||||
ERR_FAIL_COND_V(tex_pos.index < 0, FontGlyph());
|
||||
|
||||
const uint8_t *img_src = hb_raster_image_get_buffer(p_image);
|
||||
|
||||
// Fit character in char texture.
|
||||
ShelfPackTexture &tex = p_data->textures.write[tex_pos.index];
|
||||
|
||||
{
|
||||
uint8_t *wr = tex.image->ptrw();
|
||||
|
||||
for (int i = 0; i < h; i++) {
|
||||
for (int j = 0; j < w; j++) {
|
||||
int ofs = ((i + tex_pos.y + p_rect_margin * 2) * tex.texture_w + j + tex_pos.x + p_rect_margin * 2) * color_size;
|
||||
ERR_FAIL_COND_V(ofs >= tex.image->get_data_size(), FontGlyph());
|
||||
if (p_bgra) {
|
||||
int ofs_color = i * p_ext.stride + (j << 2);
|
||||
wr[ofs + 2] = img_src[ofs_color + 0];
|
||||
wr[ofs + 1] = img_src[ofs_color + 1];
|
||||
wr[ofs + 0] = img_src[ofs_color + 2];
|
||||
wr[ofs + 3] = img_src[ofs_color + 3];
|
||||
} else {
|
||||
wr[ofs + 0] = 255; // grayscale as 1
|
||||
wr[ofs + 1] = img_src[i * p_ext.stride + j];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tex.dirty = true;
|
||||
|
||||
chr.texture_idx = tex_pos.index;
|
||||
|
||||
chr.uv_rect = Rect2(tex_pos.x + p_rect_margin, tex_pos.y + p_rect_margin, w + p_rect_margin * 2, h + p_rect_margin * 2);
|
||||
chr.rect.position = Vector2(p_ext.x_origin - p_rect_margin, p_ext.y_origin - p_rect_margin) * p_data->scale;
|
||||
chr.rect.size = chr.uv_rect.size * p_data->scale;
|
||||
return chr;
|
||||
}
|
||||
#endif
|
||||
|
||||
_FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_bitmap(FontForSizeAdvanced *p_data, int p_rect_margin, FT_Bitmap p_bitmap, int p_yofs, int p_xofs, const Vector2 &p_advance, bool p_bgra) const {
|
||||
FontGlyph chr;
|
||||
chr.advance = p_advance * p_data->scale;
|
||||
@@ -1093,7 +1159,7 @@ _FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_bitma
|
||||
int w = p_bitmap.width;
|
||||
int h = p_bitmap.rows;
|
||||
|
||||
if (w == 0 || h == 0) {
|
||||
if (w == 0 || h == 0 || p_bitmap.buffer == nullptr) {
|
||||
chr.texture_idx = -1;
|
||||
chr.uv_rect = Rect2();
|
||||
chr.rect = Rect2();
|
||||
@@ -1335,21 +1401,85 @@ bool TextServerAdvanced::_ensure_glyph(FontAdvanced *p_font_data, const Vector2i
|
||||
}
|
||||
|
||||
FT_GlyphSlot slot = p_font_data->face->glyph;
|
||||
bool from_svg = (slot->format == FT_GLYPH_FORMAT_SVG); // Need to check before FT_Render_Glyph as it will change format to bitmap.
|
||||
bool fix_edge = (slot->format == FT_GLYPH_FORMAT_SVG); // Need to check before FT_Render_Glyph as it will change format to bitmap.
|
||||
bool from_bitmap = (slot->format == FT_GLYPH_FORMAT_BITMAP);
|
||||
if (!outline) {
|
||||
if (!p_font_data->msdf) {
|
||||
error = FT_Render_Glyph(slot, aa_mode);
|
||||
}
|
||||
if (!error) {
|
||||
if (p_font_data->msdf) {
|
||||
if (p_font_data->msdf) {
|
||||
#ifdef MODULE_MSDFGEN_ENABLED
|
||||
gl = rasterize_msdf(p_font_data, fd, p_font_data->msdf_range, rect_range, &slot->outline, Vector2((h + (1 << 9)) >> 10, (v + (1 << 9)) >> 10) / 64.0);
|
||||
gl = rasterize_msdf(p_font_data, fd, p_font_data->msdf_range, rect_range, &slot->outline, Vector2((h + (1 << 9)) >> 10, (v + (1 << 9)) >> 10) / 64.0);
|
||||
#else
|
||||
fd->glyph_map[p_glyph] = FontGlyph();
|
||||
ERR_FAIL_V_MSG(false, "Compiled without MSDFGEN support!");
|
||||
fd->glyph_map[p_glyph] = FontGlyph();
|
||||
ERR_FAIL_V_MSG(false, "Compiled without MSDFGEN support!");
|
||||
#endif
|
||||
} else {
|
||||
#if HB_VERSION_ATLEAST(13, 0, 0)
|
||||
if (hb_rdr && hb_mono && p_font_data->antialiasing == FONT_ANTIALIASING_GRAY && !from_bitmap) {
|
||||
bool is_rasterized = false;
|
||||
float xshift = 0.0;
|
||||
if ((p_font_data->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (p_font_data->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && p_size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE * 64)) {
|
||||
xshift = float((int)((p_glyph >> 27) & 3) << 4);
|
||||
} else if ((p_font_data->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (p_font_data->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && p_size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE * 64)) {
|
||||
xshift = float((int)((p_glyph >> 27) & 3) << 5);
|
||||
}
|
||||
xshift += (p_font_data->embolden * double(p_size.x) / 64.0);
|
||||
fix_edge = false;
|
||||
if (fd->color_paint) {
|
||||
hb_raster_paint_reset(hb_rdr);
|
||||
if (p_font_data->transform != Transform2D()) {
|
||||
hb_raster_paint_set_transform(hb_rdr, p_font_data->transform[0][0], p_font_data->transform[1][0], p_font_data->transform[0][1], -p_font_data->transform[1][1], 0.f, 0.f);
|
||||
} else {
|
||||
hb_raster_paint_set_transform(hb_rdr, 1.f, 0.f, 0.f, -1.f, 0.f, 0.f);
|
||||
}
|
||||
hb_raster_paint_set_scale_factor(hb_rdr, 64.0, 64.0);
|
||||
hb_raster_paint_clear_custom_palette_colors(hb_rdr);
|
||||
if (!p_font_data->palette_custom_colors_hb.is_empty()) {
|
||||
for (int col = 0; col < p_font_data->palette_custom_colors_hb.size(); col++) {
|
||||
if (p_font_data->palette_custom_colors_hb[col] != 0) {
|
||||
hb_raster_paint_set_custom_palette_color(hb_rdr, col, p_font_data->palette_custom_colors_hb[col]);
|
||||
}
|
||||
}
|
||||
}
|
||||
bool ok = hb_raster_paint_glyph(hb_rdr, fd->hb_handle, (hb_codepoint_t)glyph_index, xshift, 0, p_font_data->palette_index, (hb_color_t)0xFFFFFFFF);
|
||||
if (ok) {
|
||||
is_rasterized = true;
|
||||
hb_raster_image_t *img = hb_raster_paint_render(hb_rdr);
|
||||
hb_raster_extents_t ext = { 0, 0, 0, 0, 0 };
|
||||
if (img) {
|
||||
hb_raster_image_get_extents(img, &ext);
|
||||
gl = rasterize_hb_bitmap(fd, rect_range, img, ext, Vector2((h + (1 << 9)) >> 10, (v + (1 << 9)) >> 10) / 64.0, true);
|
||||
hb_raster_paint_recycle_image(hb_rdr, img);
|
||||
} else {
|
||||
gl = rasterize_hb_bitmap(fd, rect_range, nullptr, ext, Vector2((h + (1 << 9)) >> 10, (v + (1 << 9)) >> 10) / 64.0, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!is_rasterized) {
|
||||
hb_raster_draw_reset(hb_mono);
|
||||
if (p_font_data->transform != Transform2D()) {
|
||||
hb_raster_draw_set_transform(hb_mono, p_font_data->transform[0][0], p_font_data->transform[1][0], p_font_data->transform[0][1], -p_font_data->transform[1][1], 0.f, 0.f);
|
||||
} else {
|
||||
hb_raster_draw_set_transform(hb_mono, 1.f, 0.f, 0.f, -1.f, 0.f, 0.f);
|
||||
}
|
||||
hb_raster_draw_set_scale_factor(hb_mono, 64.0, 64.0);
|
||||
hb_raster_draw_glyph(hb_mono, fd->hb_handle, (hb_codepoint_t)glyph_index, xshift, 0);
|
||||
hb_raster_image_t *img = hb_raster_draw_render(hb_mono);
|
||||
hb_raster_extents_t ext = { 0, 0, 0, 0, 0 };
|
||||
if (img) {
|
||||
hb_raster_image_get_extents(img, &ext);
|
||||
gl = rasterize_hb_bitmap(fd, rect_range, img, ext, Vector2((h + (1 << 9)) >> 10, (v + (1 << 9)) >> 10) / 64.0, false);
|
||||
hb_raster_draw_recycle_image(hb_mono, img);
|
||||
} else {
|
||||
gl = rasterize_hb_bitmap(fd, rect_range, nullptr, ext, Vector2((h + (1 << 9)) >> 10, (v + (1 << 9)) >> 10) / 64.0, false);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
gl = rasterize_bitmap(fd, rect_range, slot->bitmap, slot->bitmap_top, slot->bitmap_left, Vector2((h + (1 << 9)) >> 10, (v + (1 << 9)) >> 10) / 64.0, bgra);
|
||||
#else
|
||||
{
|
||||
#endif
|
||||
error = FT_Render_Glyph(slot, aa_mode);
|
||||
if (!error) {
|
||||
gl = rasterize_bitmap(fd, rect_range, slot->bitmap, slot->bitmap_top, slot->bitmap_left, Vector2((h + (1 << 9)) >> 10, (v + (1 << 9)) >> 10) / 64.0, bgra);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -1380,7 +1510,7 @@ bool TextServerAdvanced::_ensure_glyph(FontAdvanced *p_font_data, const Vector2i
|
||||
cleanup_stroker:
|
||||
FT_Stroker_Done(stroker);
|
||||
}
|
||||
gl.from_svg = from_svg;
|
||||
gl.fix_edge = fix_edge;
|
||||
E = fd->glyph_map.insert(p_glyph, gl);
|
||||
r_glyph = E->value;
|
||||
return gl.found;
|
||||
@@ -1501,6 +1631,10 @@ bool TextServerAdvanced::_ensure_cache_for_size(FontAdvanced *p_font_data, const
|
||||
|
||||
fd->hb_handle = hb_ft_font_create(p_font_data->face, nullptr);
|
||||
|
||||
if (p_font_data->embolden != 0.0) {
|
||||
hb_font_set_synthetic_bold(fd->hb_handle, p_font_data->embolden / 16.0, p_font_data->embolden / 16.0, true);
|
||||
}
|
||||
|
||||
fd->ascent = (p_font_data->face->size->metrics.ascender / 64.0) * fd->scale;
|
||||
fd->descent = (-p_font_data->face->size->metrics.descender / 64.0) * fd->scale;
|
||||
fd->underline_position = (-FT_MulFix(p_font_data->face->underline_position, p_font_data->face->size->metrics.y_scale) / 64.0) * fd->scale;
|
||||
@@ -1512,16 +1646,56 @@ bool TextServerAdvanced::_ensure_cache_for_size(FontAdvanced *p_font_data, const
|
||||
#ifndef _MSC_VER
|
||||
#warning Building with HarfBuzz < 3.3.0, synthetic slant offset correction disabled.
|
||||
#endif
|
||||
#endif
|
||||
#if HB_VERSION_ATLEAST(13, 0, 0)
|
||||
fd->hb_face = hb_font_get_face(fd->hb_handle);
|
||||
fd->color_paint = (hb_ot_color_has_paint(fd->hb_face) || hb_ot_color_has_layers(fd->hb_face));
|
||||
#endif
|
||||
|
||||
if (!p_font_data->face_init) {
|
||||
const static hb_language_t english = hb_language_from_string("en", -1);
|
||||
#if HB_VERSION_ATLEAST(13, 0, 0)
|
||||
hb_face_t *hb_face = fd->hb_face;
|
||||
|
||||
p_font_data->palette_names.clear();
|
||||
p_font_data->palette_colors.clear();
|
||||
|
||||
unsigned int pal_count = hb_ot_color_palette_get_count(hb_face);
|
||||
for (unsigned int pal = 0; pal < pal_count; pal++) {
|
||||
String pal_name;
|
||||
Vector<Color> pal_colors;
|
||||
// Get name.
|
||||
hb_ot_name_id_t name_id = hb_ot_color_palette_get_name_id(hb_face, pal);
|
||||
if (name_id == HB_OT_NAME_ID_INVALID) {
|
||||
pal_name = vformat("Palette %d", pal + 1);
|
||||
} else {
|
||||
unsigned int text_size = hb_ot_name_get_utf32(hb_face, name_id, english, nullptr, nullptr) + 1;
|
||||
pal_name.resize_uninitialized(text_size);
|
||||
hb_ot_name_get_utf32(hb_face, name_id, english, &text_size, (uint32_t *)pal_name.ptrw());
|
||||
}
|
||||
// Get colors.
|
||||
unsigned int color_count = 0;
|
||||
color_count = hb_ot_color_palette_get_colors(hb_face, pal, 0, &color_count, nullptr);
|
||||
Vector<hb_color_t> pal_colors_hb;
|
||||
pal_colors_hb.resize_uninitialized(color_count);
|
||||
hb_ot_color_palette_get_colors(hb_face, pal, 0, &color_count, pal_colors_hb.ptrw());
|
||||
for (unsigned int col = 0; col < color_count; col++) {
|
||||
Color c = Color::from_rgba8(hb_color_get_red(pal_colors_hb[col]), hb_color_get_green(pal_colors_hb[col]), hb_color_get_blue(pal_colors_hb[col]), hb_color_get_alpha(pal_colors_hb[col]));
|
||||
pal_colors.push_back(c);
|
||||
}
|
||||
|
||||
p_font_data->palette_names.push_back(pal_name);
|
||||
p_font_data->palette_colors.push_back(pal_colors);
|
||||
}
|
||||
#else
|
||||
hb_face_t *hb_face = hb_font_get_face(fd->hb_handle);
|
||||
#endif
|
||||
// When a font does not provide a `family_name`, FreeType tries to synthesize one based on other names.
|
||||
// FreeType automatically converts non-ASCII characters to "?" in the synthesized name.
|
||||
// To avoid that behavior, use the format-specific name directly if available.
|
||||
hb_face_t *hb_face = hb_font_get_face(fd->hb_handle);
|
||||
|
||||
unsigned int num_entries = 0;
|
||||
const hb_ot_name_entry_t *names = hb_ot_name_list_names(hb_face, &num_entries);
|
||||
const hb_language_t english = hb_language_from_string("en", -1);
|
||||
for (unsigned int i = 0; i < num_entries; i++) {
|
||||
if (names[i].name_id != HB_OT_NAME_ID_FONT_FAMILY) {
|
||||
continue;
|
||||
@@ -2603,6 +2777,90 @@ bool TextServerAdvanced::_font_is_modulate_color_glyphs(const RID &p_font_rid) c
|
||||
return fd->modulate_color_glyphs;
|
||||
}
|
||||
|
||||
int64_t TextServerAdvanced::_font_get_palette_count(const RID &p_font_rid) const {
|
||||
FontAdvanced *fd = _get_font_data(p_font_rid);
|
||||
ERR_FAIL_NULL_V(fd, 0);
|
||||
|
||||
#if HB_VERSION_ATLEAST(13, 0, 0)
|
||||
return fd->palette_names.size();
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
String TextServerAdvanced::_font_get_palette_name(const RID &p_font_rid, int64_t p_index) const {
|
||||
FontAdvanced *fd = _get_font_data(p_font_rid);
|
||||
ERR_FAIL_NULL_V(fd, String());
|
||||
|
||||
#if HB_VERSION_ATLEAST(13, 0, 0)
|
||||
ERR_FAIL_INDEX_V(p_index, fd->palette_names.size(), String());
|
||||
return fd->palette_names[p_index];
|
||||
#else
|
||||
return String();
|
||||
#endif
|
||||
}
|
||||
|
||||
Vector<Color> TextServerAdvanced::_font_get_palette_colors(const RID &p_font_rid, int64_t p_index) const {
|
||||
FontAdvanced *fd = _get_font_data(p_font_rid);
|
||||
ERR_FAIL_NULL_V(fd, Vector<Color>());
|
||||
|
||||
#if HB_VERSION_ATLEAST(13, 0, 0)
|
||||
ERR_FAIL_INDEX_V(p_index, fd->palette_names.size(), Vector<Color>());
|
||||
return fd->palette_colors[p_index];
|
||||
#else
|
||||
return Vector<Color>();
|
||||
#endif
|
||||
}
|
||||
|
||||
void TextServerAdvanced::_font_set_palette_custom_colors(const RID &p_font_rid, const Vector<Color> &p_colors) {
|
||||
FontAdvanced *fd = _get_font_data(p_font_rid);
|
||||
ERR_FAIL_NULL(fd);
|
||||
|
||||
#if HB_VERSION_ATLEAST(13, 0, 0)
|
||||
if (fd->palette_custom_colors != p_colors) {
|
||||
fd->palette_custom_colors = p_colors;
|
||||
fd->palette_custom_colors_hb.resize_uninitialized(p_colors.size());
|
||||
for (int col = 0; col < fd->palette_custom_colors.size(); col++) {
|
||||
const Color &c = fd->palette_custom_colors[col];
|
||||
fd->palette_custom_colors_hb.write[col] = HB_COLOR(c.get_b8(), c.get_g8(), c.get_r8(), c.get_a8());
|
||||
}
|
||||
_font_clear_cache(fd);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
Vector<Color> TextServerAdvanced::_font_get_palette_custom_colors(const RID &p_font_rid) const {
|
||||
FontAdvanced *fd = _get_font_data(p_font_rid);
|
||||
ERR_FAIL_NULL_V(fd, Vector<Color>());
|
||||
|
||||
#if HB_VERSION_ATLEAST(13, 0, 0)
|
||||
return fd->palette_custom_colors;
|
||||
#else
|
||||
return Vector<Color>();
|
||||
#endif
|
||||
}
|
||||
|
||||
int64_t TextServerAdvanced::_font_get_used_palette(const RID &p_font_rid) const {
|
||||
FontAdvanced *fd = _get_font_data(p_font_rid);
|
||||
ERR_FAIL_NULL_V(fd, 0);
|
||||
#if HB_VERSION_ATLEAST(13, 0, 0)
|
||||
return fd->palette_index;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void TextServerAdvanced::_font_set_used_palette(const RID &p_font_rid, int64_t p_index) {
|
||||
FontAdvanced *fd = _get_font_data(p_font_rid);
|
||||
ERR_FAIL_NULL(fd);
|
||||
#if HB_VERSION_ATLEAST(13, 0, 0)
|
||||
if (fd->palette_index != p_index) {
|
||||
fd->palette_index = p_index;
|
||||
_font_clear_cache(fd);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void TextServerAdvanced::_font_set_hinting(const RID &p_font_rid, TextServer::Hinting p_hinting) {
|
||||
FontAdvanced *fd = _get_font_data(p_font_rid);
|
||||
ERR_FAIL_NULL(fd);
|
||||
@@ -3508,7 +3766,7 @@ RID TextServerAdvanced::_font_get_glyph_texture_rid(const RID &p_font_rid, const
|
||||
if (ffsd->textures[fgl.texture_idx].dirty) {
|
||||
ShelfPackTexture &tex = ffsd->textures.write[fgl.texture_idx];
|
||||
Ref<Image> img = tex.image;
|
||||
if (fgl.from_svg) {
|
||||
if (fgl.fix_edge) {
|
||||
// Same as the "fix alpha border" process option when importing SVGs
|
||||
img->fix_alpha_edges();
|
||||
}
|
||||
@@ -3558,7 +3816,7 @@ Size2 TextServerAdvanced::_font_get_glyph_texture_size(const RID &p_font_rid, co
|
||||
if (ffsd->textures[fgl.texture_idx].dirty) {
|
||||
ShelfPackTexture &tex = ffsd->textures.write[fgl.texture_idx];
|
||||
Ref<Image> img = tex.image;
|
||||
if (fgl.from_svg) {
|
||||
if (fgl.fix_edge) {
|
||||
// Same as the "fix alpha border" process option when importing SVGs
|
||||
img->fix_alpha_edges();
|
||||
}
|
||||
@@ -4034,7 +4292,7 @@ void TextServerAdvanced::_font_draw_glyph(const RID &p_font_rid, const RID &p_ca
|
||||
if (ffsd->textures[fgl.texture_idx].dirty) {
|
||||
ShelfPackTexture &tex = ffsd->textures.write[fgl.texture_idx];
|
||||
Ref<Image> img = tex.image;
|
||||
if (fgl.from_svg) {
|
||||
if (fgl.fix_edge) {
|
||||
// Same as the "fix alpha border" process option when importing SVGs
|
||||
img->fix_alpha_edges();
|
||||
}
|
||||
@@ -8119,7 +8377,10 @@ void TextServerAdvanced::_update_settings() {
|
||||
|
||||
TextServerAdvanced::TextServerAdvanced() {
|
||||
os_locale = OS::get_singleton()->get_locale();
|
||||
|
||||
#if HB_VERSION_ATLEAST(13, 0, 0)
|
||||
hb_rdr = hb_raster_paint_create_or_fail();
|
||||
hb_mono = hb_raster_draw_create_or_fail();
|
||||
#endif
|
||||
_insert_feature_sets();
|
||||
_bmp_create_font_funcs();
|
||||
_update_settings();
|
||||
@@ -8144,6 +8405,14 @@ void TextServerAdvanced::_cleanup() {
|
||||
|
||||
TextServerAdvanced::~TextServerAdvanced() {
|
||||
_bmp_free_font_funcs();
|
||||
#if HB_VERSION_ATLEAST(13, 0, 0)
|
||||
if (hb_rdr != nullptr) {
|
||||
hb_raster_paint_destroy(hb_rdr);
|
||||
}
|
||||
if (hb_mono != nullptr) {
|
||||
hb_raster_draw_destroy(hb_mono);
|
||||
}
|
||||
#endif
|
||||
#ifdef MODULE_FREETYPE_ENABLED
|
||||
if (ft_library != nullptr) {
|
||||
FT_Done_FreeType(ft_library);
|
||||
|
||||
@@ -91,6 +91,9 @@ GODOT_CLANG_WARNING_POP
|
||||
|
||||
#include <hb-icu.h>
|
||||
#include <hb.h>
|
||||
#if HB_VERSION_ATLEAST(13, 0, 0)
|
||||
#include <hb-raster.h>
|
||||
#endif
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
@@ -228,7 +231,7 @@ class TextServerAdvanced : public TextServerExtension {
|
||||
Rect2 rect;
|
||||
Rect2 uv_rect;
|
||||
Vector2 advance;
|
||||
bool from_svg = false;
|
||||
bool fix_edge = false;
|
||||
};
|
||||
|
||||
struct FontAdvanced;
|
||||
@@ -249,6 +252,10 @@ class TextServerAdvanced : public TextServerExtension {
|
||||
HashMap<int32_t, FontGlyph> glyph_map;
|
||||
HashMap<Vector2i, Vector2> kerning_map;
|
||||
hb_font_t *hb_handle = nullptr;
|
||||
#if HB_VERSION_ATLEAST(13, 0, 0)
|
||||
hb_face_t *hb_face = nullptr;
|
||||
bool color_paint = false;
|
||||
#endif
|
||||
|
||||
#ifdef MODULE_FREETYPE_ENABLED
|
||||
FT_Size fsize = nullptr;
|
||||
@@ -311,6 +318,14 @@ class TextServerAdvanced : public TextServerExtension {
|
||||
|
||||
HashMap<Vector2i, FontForSizeAdvanced *> cache;
|
||||
|
||||
#if HB_VERSION_ATLEAST(13, 0, 0)
|
||||
Vector<String> palette_names;
|
||||
Vector<PackedColorArray> palette_colors;
|
||||
PackedColorArray palette_custom_colors;
|
||||
Vector<hb_color_t> palette_custom_colors_hb;
|
||||
unsigned int palette_index = 0;
|
||||
#endif
|
||||
|
||||
bool face_init = false;
|
||||
HashSet<uint32_t> supported_scripts;
|
||||
Dictionary supported_features;
|
||||
@@ -350,6 +365,9 @@ class TextServerAdvanced : public TextServerExtension {
|
||||
#endif
|
||||
#ifdef MODULE_FREETYPE_ENABLED
|
||||
_FORCE_INLINE_ FontGlyph rasterize_bitmap(FontForSizeAdvanced *p_data, int p_rect_margin, FT_Bitmap p_bitmap, int p_yofs, int p_xofs, const Vector2 &p_advance, bool p_bgra) const;
|
||||
#if HB_VERSION_ATLEAST(13, 0, 0)
|
||||
_FORCE_INLINE_ FontGlyph rasterize_hb_bitmap(FontForSizeAdvanced *p_data, int p_rect_margin, hb_raster_image_t *p_image, const hb_raster_extents_t &p_ext, const Vector2 &p_advance, bool p_bgra) const;
|
||||
#endif
|
||||
#endif
|
||||
bool _ensure_glyph(FontAdvanced *p_font_data, const Vector2i &p_size, int32_t p_glyph, FontGlyph &r_glyph, uint32_t p_oversampling = 0) const;
|
||||
bool _ensure_cache_for_size(FontAdvanced *p_font_data, const Vector2i &p_size, FontForSizeAdvanced *&r_cache_for_size, bool p_silent = false, uint32_t p_oversampling = 0) const;
|
||||
@@ -555,6 +573,11 @@ class TextServerAdvanced : public TextServerExtension {
|
||||
mutable RID_PtrOwner<FontAdvanced> font_owner;
|
||||
mutable RID_PtrOwner<ShapedTextDataAdvanced> shaped_owner;
|
||||
|
||||
#if HB_VERSION_ATLEAST(13, 0, 0)
|
||||
hb_raster_paint_t *hb_rdr = nullptr;
|
||||
hb_raster_draw_t *hb_mono = nullptr;
|
||||
#endif
|
||||
|
||||
_FORCE_INLINE_ FontAdvanced *_get_font_data(const RID &p_font_rid) const {
|
||||
RID rid = p_font_rid;
|
||||
FontAdvancedLinkedVariation *fdv = font_var_owner.get_or_null(rid);
|
||||
@@ -870,6 +893,14 @@ public:
|
||||
MODBIND2(font_set_modulate_color_glyphs, const RID &, bool);
|
||||
MODBIND1RC(bool, font_is_modulate_color_glyphs, const RID &);
|
||||
|
||||
MODBIND1RC(int64_t, font_get_palette_count, const RID &);
|
||||
MODBIND2RC(String, font_get_palette_name, const RID &, int64_t);
|
||||
MODBIND2RC(Vector<Color>, font_get_palette_colors, const RID &, int64_t);
|
||||
MODBIND2(font_set_palette_custom_colors, const RID &, const Vector<Color> &);
|
||||
MODBIND1RC(Vector<Color>, font_get_palette_custom_colors, const RID &);
|
||||
MODBIND1RC(int64_t, font_get_used_palette, const RID &);
|
||||
MODBIND2(font_set_used_palette, const RID &, int64_t);
|
||||
|
||||
MODBIND2(font_set_subpixel_positioning, const RID &, SubpixelPositioning);
|
||||
MODBIND1RC(SubpixelPositioning, font_get_subpixel_positioning, const RID &);
|
||||
|
||||
|
||||
@@ -1549,6 +1549,51 @@ bool TextServerFallback::_font_is_modulate_color_glyphs(const RID &p_font_rid) c
|
||||
return fd->modulate_color_glyphs;
|
||||
}
|
||||
|
||||
int64_t TextServerFallback::_font_get_palette_count(const RID &p_font_rid) const {
|
||||
FontFallback *fd = _get_font_data(p_font_rid);
|
||||
ERR_FAIL_NULL_V(fd, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
String TextServerFallback::_font_get_palette_name(const RID &p_font_rid, int64_t p_index) const {
|
||||
FontFallback *fd = _get_font_data(p_font_rid);
|
||||
ERR_FAIL_NULL_V(fd, String());
|
||||
|
||||
return String();
|
||||
}
|
||||
|
||||
Vector<Color> TextServerFallback::_font_get_palette_colors(const RID &p_font_rid, int64_t p_index) const {
|
||||
FontFallback *fd = _get_font_data(p_font_rid);
|
||||
ERR_FAIL_NULL_V(fd, Vector<Color>());
|
||||
|
||||
return Vector<Color>();
|
||||
}
|
||||
|
||||
void TextServerFallback::_font_set_palette_custom_colors(const RID &p_font_rid, const Vector<Color> &p_colors) {
|
||||
FontFallback *fd = _get_font_data(p_font_rid);
|
||||
ERR_FAIL_NULL(fd);
|
||||
}
|
||||
|
||||
Vector<Color> TextServerFallback::_font_get_palette_custom_colors(const RID &p_font_rid) const {
|
||||
FontFallback *fd = _get_font_data(p_font_rid);
|
||||
ERR_FAIL_NULL_V(fd, Vector<Color>());
|
||||
|
||||
return Vector<Color>();
|
||||
}
|
||||
|
||||
int64_t TextServerFallback::_font_get_used_palette(const RID &p_font_rid) const {
|
||||
FontFallback *fd = _get_font_data(p_font_rid);
|
||||
ERR_FAIL_NULL_V(fd, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void TextServerFallback::_font_set_used_palette(const RID &p_font_rid, int64_t p_index) {
|
||||
FontFallback *fd = _get_font_data(p_font_rid);
|
||||
ERR_FAIL_NULL(fd);
|
||||
}
|
||||
|
||||
void TextServerFallback::_font_set_hinting(const RID &p_font_rid, TextServer::Hinting p_hinting) {
|
||||
FontFallback *fd = _get_font_data(p_font_rid);
|
||||
ERR_FAIL_NULL(fd);
|
||||
|
||||
@@ -649,6 +649,14 @@ public:
|
||||
MODBIND2(font_set_modulate_color_glyphs, const RID &, bool);
|
||||
MODBIND1RC(bool, font_is_modulate_color_glyphs, const RID &);
|
||||
|
||||
MODBIND1RC(int64_t, font_get_palette_count, const RID &);
|
||||
MODBIND2RC(String, font_get_palette_name, const RID &, int64_t);
|
||||
MODBIND2RC(Vector<Color>, font_get_palette_colors, const RID &, int64_t);
|
||||
MODBIND2(font_set_palette_custom_colors, const RID &, const Vector<Color> &);
|
||||
MODBIND1RC(Vector<Color>, font_get_palette_custom_colors, const RID &);
|
||||
MODBIND1RC(int64_t, font_get_used_palette, const RID &);
|
||||
MODBIND2(font_set_used_palette, const RID &, int64_t);
|
||||
|
||||
MODBIND2(font_set_subpixel_positioning, const RID &, SubpixelPositioning);
|
||||
MODBIND1RC(SubpixelPositioning, font_get_subpixel_positioning, const RID &);
|
||||
|
||||
|
||||
@@ -59,11 +59,15 @@ real_t Font::_draw_char_outline_bind_compat_104872(RID p_canvas_item, const Poin
|
||||
}
|
||||
|
||||
RID Font::_find_variation_bind_compat_80954(const Dictionary &p_variation_coordinates, int p_face_index, float p_strength, Transform2D p_transform) const {
|
||||
return find_variation(p_variation_coordinates, p_face_index, p_strength, p_transform, 0, 0, 0, 0, 0.0);
|
||||
return find_variation(p_variation_coordinates, p_face_index, p_strength, p_transform, 0, 0, 0, 0, 0.0, 0, Vector<Color>());
|
||||
}
|
||||
|
||||
RID Font::_find_variation_bind_compat_87668(const Dictionary &p_variation_coordinates, int p_face_index, float p_strength, Transform2D p_transform, int p_spacing_top, int p_spacing_bottom, int p_spacing_space, int p_spacing_glyph) const {
|
||||
return find_variation(p_variation_coordinates, p_face_index, p_strength, p_transform, p_spacing_top, p_spacing_bottom, p_spacing_space, p_spacing_glyph, 0.0);
|
||||
return find_variation(p_variation_coordinates, p_face_index, p_strength, p_transform, p_spacing_top, p_spacing_bottom, p_spacing_space, p_spacing_glyph, 0.0, 0, Vector<Color>());
|
||||
}
|
||||
|
||||
RID Font::_find_variation_bind_compat_117149(const Dictionary &p_variation_coordinates, int p_face_index, float p_strength, Transform2D p_transform, int p_spacing_top, int p_spacing_bottom, int p_spacing_space, int p_spacing_glyph, float p_baseline_offset) const {
|
||||
return find_variation(p_variation_coordinates, p_face_index, p_strength, p_transform, p_spacing_top, p_spacing_bottom, p_spacing_space, p_spacing_glyph, p_baseline_offset, 0, Vector<Color>());
|
||||
}
|
||||
|
||||
void Font::_bind_compatibility_methods() {
|
||||
@@ -78,6 +82,7 @@ void Font::_bind_compatibility_methods() {
|
||||
|
||||
ClassDB::bind_compatibility_method(D_METHOD("find_variation", "variation_coordinates", "face_index", "strength", "transform"), &Font::_find_variation_bind_compat_80954, DEFVAL(0), DEFVAL(0.0), DEFVAL(Transform2D()));
|
||||
ClassDB::bind_compatibility_method(D_METHOD("find_variation", "variation_coordinates", "face_index", "strength", "transform", "spacing_top", "spacing_bottom", "spacing_space", "spacing_glyph"), &Font::_find_variation_bind_compat_87668, DEFVAL(0), DEFVAL(0.0), DEFVAL(Transform2D()), DEFVAL(0), DEFVAL(0), DEFVAL(0), DEFVAL(0));
|
||||
ClassDB::bind_compatibility_method(D_METHOD("find_variation", "variation_coordinates", "face_index", "strength", "transform", "spacing_top", "spacing_bottom", "spacing_space", "spacing_glyph", "baseline_offset"), &Font::_find_variation_bind_compat_117149, DEFVAL(0), DEFVAL(0.0), DEFVAL(Transform2D()), DEFVAL(0), DEFVAL(0), DEFVAL(0), DEFVAL(0), DEFVAL(0.0));
|
||||
}
|
||||
|
||||
#endif // DISABLE_DEPRECATED
|
||||
|
||||
+100
-8
@@ -53,7 +53,7 @@ void Font::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("get_fallbacks"), &Font::get_fallbacks);
|
||||
|
||||
// Output.
|
||||
ClassDB::bind_method(D_METHOD("find_variation", "variation_coordinates", "face_index", "strength", "transform", "spacing_top", "spacing_bottom", "spacing_space", "spacing_glyph", "baseline_offset"), &Font::find_variation, DEFVAL(0), DEFVAL(0.0), DEFVAL(Transform2D()), DEFVAL(0), DEFVAL(0), DEFVAL(0), DEFVAL(0), DEFVAL(0.0));
|
||||
ClassDB::bind_method(D_METHOD("find_variation", "variation_coordinates", "face_index", "strength", "transform", "spacing_top", "spacing_bottom", "spacing_space", "spacing_glyph", "baseline_offset", "palette_index", "custom_colors"), &Font::find_variation, DEFVAL(0), DEFVAL(0.0), DEFVAL(Transform2D()), DEFVAL(0), DEFVAL(0), DEFVAL(0), DEFVAL(0), DEFVAL(0.0), DEFVAL(0), DEFVAL(Vector<Color>()));
|
||||
ClassDB::bind_method(D_METHOD("get_rids"), &Font::get_rids);
|
||||
|
||||
// Font metrics.
|
||||
@@ -70,6 +70,10 @@ void Font::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("get_font_weight"), &Font::get_font_weight);
|
||||
ClassDB::bind_method(D_METHOD("get_font_stretch"), &Font::get_font_stretch);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_palette_count"), &Font::get_palette_count);
|
||||
ClassDB::bind_method(D_METHOD("get_palette_name", "index"), &Font::get_palette_name);
|
||||
ClassDB::bind_method(D_METHOD("get_palette_colors", "index"), &Font::get_palette_colors);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_spacing", "spacing"), &Font::get_spacing);
|
||||
ClassDB::bind_method(D_METHOD("get_opentype_features"), &Font::get_opentype_features);
|
||||
|
||||
@@ -270,6 +274,18 @@ String Font::get_font_name() const {
|
||||
return TS->font_get_name(_get_rid());
|
||||
}
|
||||
|
||||
int64_t Font::get_palette_count() const {
|
||||
return TS->font_get_palette_count(_get_rid());
|
||||
}
|
||||
|
||||
String Font::get_palette_name(int64_t p_index) const {
|
||||
return TS->font_get_palette_name(_get_rid(), p_index);
|
||||
}
|
||||
|
||||
Vector<Color> Font::get_palette_colors(int64_t p_index) const {
|
||||
return TS->font_get_palette_colors(_get_rid(), p_index);
|
||||
}
|
||||
|
||||
Dictionary Font::get_ot_name_strings() const {
|
||||
return TS->font_get_ot_name_strings(_get_rid());
|
||||
}
|
||||
@@ -2366,7 +2382,7 @@ real_t FontFile::get_oversampling() const {
|
||||
return oversampling_override;
|
||||
}
|
||||
|
||||
RID FontFile::find_variation(const Dictionary &p_variation_coordinates, int p_face_index, float p_strength, Transform2D p_transform, int p_spacing_top, int p_spacing_bottom, int p_spacing_space, int p_spacing_glyph, float p_baseline_offset) const {
|
||||
RID FontFile::find_variation(const Dictionary &p_variation_coordinates, int p_face_index, float p_strength, Transform2D p_transform, int p_spacing_top, int p_spacing_bottom, int p_spacing_space, int p_spacing_glyph, float p_baseline_offset, int64_t p_palette_index, const Vector<Color> &p_custom_colors) const {
|
||||
// Find existing variation cache.
|
||||
const Dictionary &supported_coords = get_supported_variation_list();
|
||||
int make_linked_from = -1;
|
||||
@@ -2378,6 +2394,8 @@ RID FontFile::find_variation(const Dictionary &p_variation_coordinates, int p_fa
|
||||
match = match && (TS->font_get_face_index(cache[i]) == p_face_index);
|
||||
match = match && (TS->font_get_embolden(cache[i]) == p_strength);
|
||||
match = match && (TS->font_get_transform(cache[i]) == p_transform);
|
||||
match = match && (TS->font_get_used_palette(cache[i]) == p_palette_index);
|
||||
match = match && (TS->font_get_palette_custom_colors(cache[i]) == p_custom_colors);
|
||||
match_linked = match_linked && (TS->font_get_spacing(cache[i], TextServer::SPACING_TOP) == p_spacing_top);
|
||||
match_linked = match_linked && (TS->font_get_spacing(cache[i], TextServer::SPACING_BOTTOM) == p_spacing_bottom);
|
||||
match_linked = match_linked && (TS->font_get_spacing(cache[i], TextServer::SPACING_SPACE) == p_spacing_space);
|
||||
@@ -2427,6 +2445,8 @@ RID FontFile::find_variation(const Dictionary &p_variation_coordinates, int p_fa
|
||||
TS->font_set_spacing(cache[idx], TextServer::SPACING_SPACE, p_spacing_space);
|
||||
TS->font_set_spacing(cache[idx], TextServer::SPACING_GLYPH, p_spacing_glyph);
|
||||
TS->font_set_baseline_offset(cache[idx], p_baseline_offset);
|
||||
TS->font_set_used_palette(cache[idx], p_palette_index);
|
||||
TS->font_set_palette_custom_colors(cache[idx], p_custom_colors);
|
||||
} else {
|
||||
_ensure_rid(idx);
|
||||
TS->font_set_variation_coordinates(cache[idx], p_variation_coordinates);
|
||||
@@ -2438,6 +2458,8 @@ RID FontFile::find_variation(const Dictionary &p_variation_coordinates, int p_fa
|
||||
TS->font_set_spacing(cache[idx], TextServer::SPACING_SPACE, p_spacing_space);
|
||||
TS->font_set_spacing(cache[idx], TextServer::SPACING_GLYPH, p_spacing_glyph);
|
||||
TS->font_set_baseline_offset(cache[idx], p_baseline_offset);
|
||||
TS->font_set_used_palette(cache[idx], p_palette_index);
|
||||
TS->font_set_palette_custom_colors(cache[idx], p_custom_colors);
|
||||
}
|
||||
return cache[idx];
|
||||
}
|
||||
@@ -2905,6 +2927,12 @@ void FontVariation::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_baseline_offset", "baseline_offset"), &FontVariation::set_baseline_offset);
|
||||
ClassDB::bind_method(D_METHOD("get_baseline_offset"), &FontVariation::get_baseline_offset);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_palette_index"), &FontVariation::get_palette_index);
|
||||
ClassDB::bind_method(D_METHOD("set_palette_index", "palette_index"), &FontVariation::set_palette_index);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_palette_custom_colors"), &FontVariation::get_palette_custom_colors);
|
||||
ClassDB::bind_method(D_METHOD("set_palette_custom_colors", "colors"), &FontVariation::set_palette_custom_colors);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "base_font", PROPERTY_HINT_RESOURCE_TYPE, Font::get_class_static()), "set_base_font", "get_base_font");
|
||||
|
||||
ADD_GROUP("Variation", "variation_");
|
||||
@@ -2924,6 +2952,46 @@ void FontVariation::_bind_methods() {
|
||||
|
||||
ADD_GROUP("Baseline", "baseline_");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "baseline_offset", PROPERTY_HINT_RANGE, "-2,2,0.005"), "set_baseline_offset", "get_baseline_offset");
|
||||
|
||||
ADD_GROUP("Palette", "palette_");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "palette_index", PROPERTY_HINT_NONE, ""), "set_palette_index", "get_palette_index");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::PACKED_COLOR_ARRAY, "palette_custom_colors", PROPERTY_HINT_NONE, ""), "set_palette_custom_colors", "get_palette_custom_colors");
|
||||
}
|
||||
|
||||
void FontVariation::_validate_property(PropertyInfo &p_property) const {
|
||||
if (p_property.name == "palette_custom_colors") {
|
||||
Ref<Font> f = _get_base_font_or_default();
|
||||
if (f.is_valid()) {
|
||||
int64_t pal_count = f->get_palette_count();
|
||||
if (pal_count == 0 || f->get_palette_colors(0).is_empty()) {
|
||||
p_property.usage = PROPERTY_USAGE_NO_EDITOR;
|
||||
}
|
||||
} else {
|
||||
p_property.usage = PROPERTY_USAGE_NO_EDITOR;
|
||||
}
|
||||
} else if (p_property.name == "palette_index") {
|
||||
Ref<Font> f = _get_base_font_or_default();
|
||||
if (f.is_valid()) {
|
||||
p_property.hint = PROPERTY_HINT_ENUM;
|
||||
p_property.hint_string = String();
|
||||
|
||||
int64_t pal_count = f->get_palette_count();
|
||||
if (pal_count == 0) {
|
||||
p_property.usage = PROPERTY_USAGE_NO_EDITOR;
|
||||
} else {
|
||||
for (int64_t i = 0; i < pal_count; i++) {
|
||||
if (!p_property.hint_string.is_empty()) {
|
||||
p_property.hint_string += ",";
|
||||
}
|
||||
p_property.hint_string += f->get_palette_name(i);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
p_property.hint = PROPERTY_HINT_NONE;
|
||||
p_property.hint_string = String();
|
||||
p_property.usage = PROPERTY_USAGE_NO_EDITOR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FontVariation::_update_rids() const {
|
||||
@@ -2965,6 +3033,8 @@ void FontVariation::reset_state() {
|
||||
extra_spacing[i] = 0;
|
||||
}
|
||||
baseline_offset = 0.0;
|
||||
palette_index = 0;
|
||||
custom_colors.clear();
|
||||
|
||||
Font::reset_state();
|
||||
}
|
||||
@@ -3120,10 +3190,32 @@ float FontVariation::get_baseline_offset() const {
|
||||
return baseline_offset;
|
||||
}
|
||||
|
||||
RID FontVariation::find_variation(const Dictionary &p_variation_coordinates, int p_face_index, float p_strength, Transform2D p_transform, int p_spacing_top, int p_spacing_bottom, int p_spacing_space, int p_spacing_glyph, float p_baseline_offset) const {
|
||||
void FontVariation::set_palette_index(int64_t p_palette_index) {
|
||||
if (palette_index != p_palette_index) {
|
||||
palette_index = p_palette_index;
|
||||
_invalidate_rids();
|
||||
}
|
||||
}
|
||||
|
||||
int64_t FontVariation::get_palette_index() const {
|
||||
return palette_index;
|
||||
}
|
||||
|
||||
void FontVariation::set_palette_custom_colors(const Vector<Color> &p_colors) {
|
||||
if (custom_colors != p_colors) {
|
||||
custom_colors = p_colors;
|
||||
_invalidate_rids();
|
||||
}
|
||||
}
|
||||
|
||||
Vector<Color> FontVariation::get_palette_custom_colors() const {
|
||||
return custom_colors;
|
||||
}
|
||||
|
||||
RID FontVariation::find_variation(const Dictionary &p_variation_coordinates, int p_face_index, float p_strength, Transform2D p_transform, int p_spacing_top, int p_spacing_bottom, int p_spacing_space, int p_spacing_glyph, float p_baseline_offset, int64_t p_palette_index, const Vector<Color> &p_custom_colors) const {
|
||||
Ref<Font> f = _get_base_font_or_default();
|
||||
if (f.is_valid()) {
|
||||
return f->find_variation(p_variation_coordinates, p_face_index, p_strength, p_transform, p_spacing_top, p_spacing_bottom, p_spacing_space, p_spacing_glyph, p_baseline_offset);
|
||||
return f->find_variation(p_variation_coordinates, p_face_index, p_strength, p_transform, p_spacing_top, p_spacing_bottom, p_spacing_space, p_spacing_glyph, p_baseline_offset, p_palette_index, p_custom_colors);
|
||||
}
|
||||
return RID();
|
||||
}
|
||||
@@ -3131,7 +3223,7 @@ RID FontVariation::find_variation(const Dictionary &p_variation_coordinates, int
|
||||
RID FontVariation::_get_rid() const {
|
||||
Ref<Font> f = _get_base_font_or_default();
|
||||
if (f.is_valid()) {
|
||||
return f->find_variation(variation.opentype, variation.face_index, variation.embolden, variation.transform, extra_spacing[TextServer::SPACING_TOP], extra_spacing[TextServer::SPACING_BOTTOM], extra_spacing[TextServer::SPACING_SPACE], extra_spacing[TextServer::SPACING_GLYPH], baseline_offset);
|
||||
return f->find_variation(variation.opentype, variation.face_index, variation.embolden, variation.transform, extra_spacing[TextServer::SPACING_TOP], extra_spacing[TextServer::SPACING_BOTTOM], extra_spacing[TextServer::SPACING_SPACE], extra_spacing[TextServer::SPACING_GLYPH], baseline_offset, palette_index, custom_colors);
|
||||
}
|
||||
return RID();
|
||||
}
|
||||
@@ -3651,7 +3743,7 @@ int SystemFont::get_spacing(TextServer::SpacingType p_spacing) const {
|
||||
}
|
||||
}
|
||||
|
||||
RID SystemFont::find_variation(const Dictionary &p_variation_coordinates, int p_face_index, float p_strength, Transform2D p_transform, int p_spacing_top, int p_spacing_bottom, int p_spacing_space, int p_spacing_glyph, float p_baseline_offset) const {
|
||||
RID SystemFont::find_variation(const Dictionary &p_variation_coordinates, int p_face_index, float p_strength, Transform2D p_transform, int p_spacing_top, int p_spacing_bottom, int p_spacing_space, int p_spacing_glyph, float p_baseline_offset, int64_t p_palette_index, const Vector<Color> &p_custom_colors) const {
|
||||
Ref<Font> f = _get_base_font_or_default();
|
||||
if (f.is_valid()) {
|
||||
Dictionary var = p_variation_coordinates;
|
||||
@@ -3667,9 +3759,9 @@ RID SystemFont::find_variation(const Dictionary &p_variation_coordinates, int p_
|
||||
|
||||
if (!face_indices.is_empty()) {
|
||||
int face_index = CLAMP(p_face_index, 0, face_indices.size() - 1);
|
||||
return f->find_variation(var, face_indices[face_index], p_strength, p_transform, p_spacing_top, p_spacing_bottom, p_spacing_space, p_spacing_glyph, p_baseline_offset);
|
||||
return f->find_variation(var, face_indices[face_index], p_strength, p_transform, p_spacing_top, p_spacing_bottom, p_spacing_space, p_spacing_glyph, p_baseline_offset, p_palette_index, p_custom_colors);
|
||||
} else {
|
||||
return f->find_variation(var, 0, p_strength, p_transform, p_spacing_top, p_spacing_bottom, p_spacing_space, p_spacing_glyph, p_baseline_offset);
|
||||
return f->find_variation(var, 0, p_strength, p_transform, p_spacing_top, p_spacing_bottom, p_spacing_space, p_spacing_glyph, p_baseline_offset, p_palette_index, p_custom_colors);
|
||||
}
|
||||
}
|
||||
return RID();
|
||||
|
||||
+18
-4
@@ -108,6 +108,7 @@ protected:
|
||||
real_t _draw_char_outline_bind_compat_104872(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, int p_font_size = DEFAULT_FONT_SIZE, int p_size = 1, const Color &p_modulate = Color(1.0, 1.0, 1.0)) const;
|
||||
RID _find_variation_bind_compat_80954(const Dictionary &p_variation_coordinates, int p_face_index = 0, float p_strength = 0.0, Transform2D p_transform = Transform2D()) const;
|
||||
RID _find_variation_bind_compat_87668(const Dictionary &p_variation_coordinates, int p_face_index = 0, float p_strength = 0.0, Transform2D p_transform = Transform2D(), int p_spacing_top = 0, int p_spacing_bottom = 0, int p_spacing_space = 0, int p_spacing_glyph = 0) const;
|
||||
RID _find_variation_bind_compat_117149(const Dictionary &p_variation_coordinates, int p_face_index = 0, float p_strength = 0.0, Transform2D p_transform = Transform2D(), int p_spacing_top = 0, int p_spacing_bottom = 0, int p_spacing_space = 0, int p_spacing_glyph = 0, float p_baseline_offset = 0.0) const;
|
||||
static void _bind_compatibility_methods();
|
||||
#endif
|
||||
|
||||
@@ -123,7 +124,7 @@ public:
|
||||
virtual TypedArray<Font> get_fallbacks() const;
|
||||
|
||||
// Output.
|
||||
virtual RID find_variation(const Dictionary &p_variation_coordinates, int p_face_index = 0, float p_strength = 0.0, Transform2D p_transform = Transform2D(), int p_spacing_top = 0, int p_spacing_bottom = 0, int p_spacing_space = 0, int p_spacing_glyph = 0, float p_baseline_offset = 0.0) const { return RID(); }
|
||||
virtual RID find_variation(const Dictionary &p_variation_coordinates, int p_face_index = 0, float p_strength = 0.0, Transform2D p_transform = Transform2D(), int p_spacing_top = 0, int p_spacing_bottom = 0, int p_spacing_space = 0, int p_spacing_glyph = 0, float p_baseline_offset = 0.0, int64_t p_palette_index = 0, const Vector<Color> &p_custom_colors = Vector<Color>()) const { return RID(); }
|
||||
virtual RID _get_rid() const { return RID(); }
|
||||
virtual TypedArray<RID> get_rids() const;
|
||||
|
||||
@@ -141,6 +142,10 @@ public:
|
||||
virtual int get_font_weight() const;
|
||||
virtual int get_font_stretch() const;
|
||||
|
||||
virtual int64_t get_palette_count() const;
|
||||
virtual String get_palette_name(int64_t p_index) const;
|
||||
virtual Vector<Color> get_palette_colors(int64_t p_index) const;
|
||||
|
||||
virtual int get_spacing(TextServer::SpacingType p_spacing) const { return 0; }
|
||||
virtual Dictionary get_opentype_features() const;
|
||||
|
||||
@@ -300,7 +305,7 @@ public:
|
||||
virtual real_t get_oversampling() const;
|
||||
|
||||
// Cache.
|
||||
virtual RID find_variation(const Dictionary &p_variation_coordinates, int p_face_index = 0, float p_strength = 0.0, Transform2D p_transform = Transform2D(), int p_spacing_top = 0, int p_spacing_bottom = 0, int p_spacing_space = 0, int p_spacing_glyph = 0, float p_baseline_offset = 0.0) const override;
|
||||
virtual RID find_variation(const Dictionary &p_variation_coordinates, int p_face_index = 0, float p_strength = 0.0, Transform2D p_transform = Transform2D(), int p_spacing_top = 0, int p_spacing_bottom = 0, int p_spacing_space = 0, int p_spacing_glyph = 0, float p_baseline_offset = 0.0, int64_t p_palette_index = 0, const Vector<Color> &p_custom_colors = Vector<Color>()) const override;
|
||||
virtual RID _get_rid() const override;
|
||||
|
||||
virtual int get_cache_count() const;
|
||||
@@ -427,9 +432,12 @@ class FontVariation : public Font {
|
||||
Dictionary opentype_features;
|
||||
int extra_spacing[TextServer::SPACING_MAX];
|
||||
float baseline_offset = 0.0;
|
||||
int64_t palette_index = 0;
|
||||
Vector<Color> custom_colors;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
void _validate_property(PropertyInfo &p_property) const;
|
||||
|
||||
virtual void _update_rids() const override;
|
||||
|
||||
@@ -461,8 +469,14 @@ public:
|
||||
virtual float get_baseline_offset() const;
|
||||
virtual void set_baseline_offset(float p_baseline_offset);
|
||||
|
||||
virtual int64_t get_palette_index() const;
|
||||
virtual void set_palette_index(int64_t p_palette_index);
|
||||
|
||||
virtual Vector<Color> get_palette_custom_colors() const;
|
||||
virtual void set_palette_custom_colors(const Vector<Color> &p_colors);
|
||||
|
||||
// Output.
|
||||
virtual RID find_variation(const Dictionary &p_variation_coordinates, int p_face_index = 0, float p_strength = 0.0, Transform2D p_transform = Transform2D(), int p_spacing_top = 0, int p_spacing_bottom = 0, int p_spacing_space = 0, int p_spacing_glyph = 0, float p_baseline_offset = 0.0) const override;
|
||||
virtual RID find_variation(const Dictionary &p_variation_coordinates, int p_face_index = 0, float p_strength = 0.0, Transform2D p_transform = Transform2D(), int p_spacing_top = 0, int p_spacing_bottom = 0, int p_spacing_space = 0, int p_spacing_glyph = 0, float p_baseline_offset = 0.0, int64_t p_palette_index = 0, const Vector<Color> &p_custom_colors = Vector<Color>()) const override;
|
||||
virtual RID _get_rid() const override;
|
||||
|
||||
FontVariation();
|
||||
@@ -568,7 +582,7 @@ public:
|
||||
|
||||
virtual int get_spacing(TextServer::SpacingType p_spacing) const override;
|
||||
|
||||
virtual RID find_variation(const Dictionary &p_variation_coordinates, int p_face_index = 0, float p_strength = 0.0, Transform2D p_transform = Transform2D(), int p_spacing_top = 0, int p_spacing_bottom = 0, int p_spacing_space = 0, int p_spacing_glyph = 0, float p_baseline_offset = 0.0) const override;
|
||||
virtual RID find_variation(const Dictionary &p_variation_coordinates, int p_face_index = 0, float p_strength = 0.0, Transform2D p_transform = Transform2D(), int p_spacing_top = 0, int p_spacing_bottom = 0, int p_spacing_space = 0, int p_spacing_glyph = 0, float p_baseline_offset = 0.0, int64_t p_palette_index = 0, const Vector<Color> &p_custom_colors = Vector<Color>()) const override;
|
||||
virtual RID _get_rid() const override;
|
||||
|
||||
int64_t get_face_count() const override;
|
||||
|
||||
@@ -276,9 +276,17 @@ void TextServer::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("font_set_force_autohinter", "font_rid", "force_autohinter"), &TextServer::font_set_force_autohinter);
|
||||
ClassDB::bind_method(D_METHOD("font_is_force_autohinter", "font_rid"), &TextServer::font_is_force_autohinter);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("font_set_modulate_color_glyphs", "font_rid", "force_autohinter"), &TextServer::font_set_modulate_color_glyphs);
|
||||
ClassDB::bind_method(D_METHOD("font_set_modulate_color_glyphs", "font_rid", "modulate"), &TextServer::font_set_modulate_color_glyphs);
|
||||
ClassDB::bind_method(D_METHOD("font_is_modulate_color_glyphs", "font_rid"), &TextServer::font_is_modulate_color_glyphs);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("font_get_palette_count", "font_rid"), &TextServer::font_get_palette_count);
|
||||
ClassDB::bind_method(D_METHOD("font_get_palette_name", "font_rid", "index"), &TextServer::font_get_palette_name);
|
||||
ClassDB::bind_method(D_METHOD("font_get_palette_colors", "font_rid", "index"), &TextServer::font_get_palette_colors);
|
||||
ClassDB::bind_method(D_METHOD("font_set_palette_custom_colors", "font_rid", "colors"), &TextServer::font_set_palette_custom_colors);
|
||||
ClassDB::bind_method(D_METHOD("font_get_palette_custom_colors", "font_rid"), &TextServer::font_get_palette_custom_colors);
|
||||
ClassDB::bind_method(D_METHOD("font_get_used_palette", "font_rid"), &TextServer::font_get_used_palette);
|
||||
ClassDB::bind_method(D_METHOD("font_set_used_palette", "font_rid", "index"), &TextServer::font_set_used_palette);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("font_set_hinting", "font_rid", "hinting"), &TextServer::font_set_hinting);
|
||||
ClassDB::bind_method(D_METHOD("font_get_hinting", "font_rid"), &TextServer::font_get_hinting);
|
||||
|
||||
|
||||
@@ -332,6 +332,14 @@ public:
|
||||
virtual void font_set_modulate_color_glyphs(const RID &p_font_rid, bool p_modulate) = 0;
|
||||
virtual bool font_is_modulate_color_glyphs(const RID &p_font_rid) const = 0;
|
||||
|
||||
virtual int64_t font_get_palette_count(const RID &p_font_rid) const = 0;
|
||||
virtual String font_get_palette_name(const RID &p_font_rid, int64_t p_index) const = 0;
|
||||
virtual Vector<Color> font_get_palette_colors(const RID &p_font_rid, int64_t p_index) const = 0;
|
||||
virtual void font_set_palette_custom_colors(const RID &p_font_rid, const Vector<Color> &p_colors) = 0;
|
||||
virtual Vector<Color> font_get_palette_custom_colors(const RID &p_font_rid) const = 0;
|
||||
virtual int64_t font_get_used_palette(const RID &p_font_rid) const = 0;
|
||||
virtual void font_set_used_palette(const RID &p_font_rid, int64_t p_index) = 0;
|
||||
|
||||
virtual void font_set_hinting(const RID &p_font_rid, Hinting p_hinting) = 0;
|
||||
virtual Hinting font_get_hinting(const RID &p_font_rid) const = 0;
|
||||
|
||||
|
||||
@@ -115,6 +115,14 @@ void TextServerExtension::_bind_methods() {
|
||||
GDVIRTUAL_BIND(_font_set_modulate_color_glyphs, "font_rid", "modulate");
|
||||
GDVIRTUAL_BIND(_font_is_modulate_color_glyphs, "font_rid");
|
||||
|
||||
GDVIRTUAL_BIND(_font_get_palette_count, "font_rid");
|
||||
GDVIRTUAL_BIND(_font_get_palette_name, "font_rid", "index");
|
||||
GDVIRTUAL_BIND(_font_get_palette_colors, "font_rid", "index");
|
||||
GDVIRTUAL_BIND(_font_set_palette_custom_colors, "font_rid", "colors");
|
||||
GDVIRTUAL_BIND(_font_get_palette_custom_colors, "font_rid");
|
||||
GDVIRTUAL_BIND(_font_get_used_palette, "font_rid");
|
||||
GDVIRTUAL_BIND(_font_set_used_palette, "font_rid", "index");
|
||||
|
||||
GDVIRTUAL_BIND(_font_set_hinting, "font_rid", "hinting");
|
||||
GDVIRTUAL_BIND(_font_get_hinting, "font_rid");
|
||||
|
||||
@@ -690,6 +698,43 @@ bool TextServerExtension::font_is_modulate_color_glyphs(const RID &p_font_rid) c
|
||||
return ret;
|
||||
}
|
||||
|
||||
int64_t TextServerExtension::font_get_palette_count(const RID &p_font_rid) const {
|
||||
int64_t ret = 0;
|
||||
GDVIRTUAL_CALL(_font_get_palette_count, p_font_rid, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
String TextServerExtension::font_get_palette_name(const RID &p_font_rid, int64_t p_index) const {
|
||||
String ret;
|
||||
GDVIRTUAL_CALL(_font_get_palette_name, p_font_rid, p_index, ret);
|
||||
return ret;
|
||||
}
|
||||
Vector<Color> TextServerExtension::font_get_palette_colors(const RID &p_font_rid, int64_t p_index) const {
|
||||
Vector<Color> ret;
|
||||
GDVIRTUAL_CALL(_font_get_palette_colors, p_font_rid, p_index, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void TextServerExtension::font_set_palette_custom_colors(const RID &p_font_rid, const Vector<Color> &p_colors) {
|
||||
GDVIRTUAL_CALL(_font_set_palette_custom_colors, p_font_rid, p_colors);
|
||||
}
|
||||
|
||||
Vector<Color> TextServerExtension::font_get_palette_custom_colors(const RID &p_font_rid) const {
|
||||
Vector<Color> ret;
|
||||
GDVIRTUAL_CALL(_font_get_palette_custom_colors, p_font_rid, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int64_t TextServerExtension::font_get_used_palette(const RID &p_font_rid) const {
|
||||
int64_t ret = 0;
|
||||
GDVIRTUAL_CALL(_font_get_used_palette, p_font_rid, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void TextServerExtension::font_set_used_palette(const RID &p_font_rid, int64_t p_index) {
|
||||
GDVIRTUAL_CALL(_font_set_used_palette, p_font_rid, p_index);
|
||||
}
|
||||
|
||||
void TextServerExtension::font_set_hinting(const RID &p_font_rid, TextServer::Hinting p_hinting) {
|
||||
GDVIRTUAL_CALL(_font_set_hinting, p_font_rid, p_hinting);
|
||||
}
|
||||
|
||||
@@ -213,6 +213,21 @@ public:
|
||||
GDVIRTUAL2(_font_set_modulate_color_glyphs, RID, bool);
|
||||
GDVIRTUAL1RC(bool, _font_is_modulate_color_glyphs, RID);
|
||||
|
||||
virtual int64_t font_get_palette_count(const RID &p_font_rid) const override;
|
||||
GDVIRTUAL1RC(int64_t, _font_get_palette_count, const RID &);
|
||||
virtual String font_get_palette_name(const RID &p_font_rid, int64_t p_index) const override;
|
||||
GDVIRTUAL2RC(String, _font_get_palette_name, const RID &, int64_t);
|
||||
virtual Vector<Color> font_get_palette_colors(const RID &p_font_rid, int64_t p_index) const override;
|
||||
GDVIRTUAL2RC(Vector<Color>, _font_get_palette_colors, const RID &, int64_t);
|
||||
virtual void font_set_palette_custom_colors(const RID &p_font_rid, const Vector<Color> &p_colors) override;
|
||||
GDVIRTUAL2(_font_set_palette_custom_colors, const RID &, const Vector<Color> &);
|
||||
virtual Vector<Color> font_get_palette_custom_colors(const RID &p_font_rid) const override;
|
||||
GDVIRTUAL1RC(Vector<Color>, _font_get_palette_custom_colors, const RID &);
|
||||
virtual int64_t font_get_used_palette(const RID &p_font_rid) const override;
|
||||
GDVIRTUAL1RC(int64_t, _font_get_used_palette, const RID &);
|
||||
virtual void font_set_used_palette(const RID &p_font_rid, int64_t p_index) override;
|
||||
GDVIRTUAL2(_font_set_used_palette, const RID &, int64_t);
|
||||
|
||||
virtual void font_set_hinting(const RID &p_font_rid, Hinting p_hinting) override;
|
||||
virtual Hinting font_get_hinting(const RID &p_font_rid) const override;
|
||||
GDVIRTUAL2(_font_set_hinting, RID, Hinting);
|
||||
|
||||
Vendored
+1
-1
@@ -468,7 +468,7 @@ Patches:
|
||||
## harfbuzz
|
||||
|
||||
- Upstream: https://github.com/harfbuzz/harfbuzz
|
||||
- Version: 12.3.2 (b42511e071162fe76102f613a6ccc009726c99af, 2026)
|
||||
- Version: 13.1.1 (70b812d8d2fb4a2b6386c04bfd8565a0fcc05f9b, 2026)
|
||||
- License: MIT
|
||||
|
||||
Files extracted from upstream source:
|
||||
|
||||
+1
-4
@@ -1814,10 +1814,7 @@ struct ClipList
|
||||
{
|
||||
auto *rec = clips.as_array ().bsearch (gid);
|
||||
if (rec)
|
||||
{
|
||||
rec->get_extents (extents, this, instancer);
|
||||
return true;
|
||||
}
|
||||
return rec->get_extents (extents, this, instancer);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
+682
-4
@@ -27,7 +27,12 @@
|
||||
|
||||
#include "../../../hb-open-type.hh"
|
||||
#include "../../../hb-blob.hh"
|
||||
#include "../../../hb-limits.hh"
|
||||
#include "../../../hb-map.hh"
|
||||
#include "../../../hb-paint.hh"
|
||||
#include "../../../hb-zlib.hh"
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
* SVG -- SVG (Scalable Vector Graphics)
|
||||
@@ -39,12 +44,62 @@
|
||||
|
||||
namespace OT {
|
||||
|
||||
static inline hb_blob_t *
|
||||
hb_ot_svg_reference_normalized_blob (hb_blob_t *image,
|
||||
const char **svg,
|
||||
unsigned *len)
|
||||
{
|
||||
hb_blob_t *blob = hb_blob_reference (image);
|
||||
unsigned data_len = 0;
|
||||
const char *data = hb_blob_get_data (blob, &data_len);
|
||||
|
||||
if (!data || !data_len)
|
||||
goto fail;
|
||||
|
||||
if (hb_blob_is_gzip (data, data_len))
|
||||
{
|
||||
uint32_t expected_size = 0;
|
||||
if (hb_gzip_get_uncompressed_size (data, data_len, &expected_size) &&
|
||||
unlikely ((size_t) expected_size > (size_t) HB_SVG_MAX_DOCUMENT_SIZE))
|
||||
goto fail;
|
||||
|
||||
hb_blob_t *uncompressed = hb_blob_decompress_gzip (blob,
|
||||
HB_SVG_MAX_DOCUMENT_SIZE);
|
||||
if (!uncompressed)
|
||||
goto fail;
|
||||
|
||||
hb_blob_destroy (blob);
|
||||
blob = uncompressed;
|
||||
data = hb_blob_get_data (blob, &data_len);
|
||||
if (!data || !data_len)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (unlikely ((size_t) data_len > (size_t) HB_SVG_MAX_DOCUMENT_SIZE))
|
||||
goto fail;
|
||||
|
||||
if (svg) *svg = data;
|
||||
if (len) *len = data_len;
|
||||
return blob;
|
||||
|
||||
fail:
|
||||
hb_blob_destroy (blob);
|
||||
if (svg) *svg = nullptr;
|
||||
if (len) *len = 0;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
struct SVGDocumentIndexEntry
|
||||
{
|
||||
int cmp (hb_codepoint_t g) const
|
||||
{ return g < startGlyphID ? -1 : g > endGlyphID ? 1 : 0; }
|
||||
|
||||
hb_codepoint_t get_start_glyph () const
|
||||
{ return startGlyphID; }
|
||||
|
||||
hb_codepoint_t get_end_glyph () const
|
||||
{ return endGlyphID; }
|
||||
|
||||
hb_blob_t *reference_blob (hb_blob_t *svg_blob, unsigned int index_offset) const
|
||||
{
|
||||
return hb_blob_create_sub_blob (svg_blob,
|
||||
@@ -78,13 +133,50 @@ struct SVG
|
||||
{
|
||||
static constexpr hb_tag_t tableTag = HB_OT_TAG_SVG;
|
||||
|
||||
struct svg_id_span_t
|
||||
{
|
||||
const char *p;
|
||||
unsigned len;
|
||||
|
||||
bool operator == (const svg_id_span_t &o) const
|
||||
{
|
||||
return len == o.len && !memcmp (p, o.p, len);
|
||||
}
|
||||
|
||||
uint32_t hash () const
|
||||
{
|
||||
uint32_t h = hb_hash (len);
|
||||
for (unsigned i = 0; i < len; i++)
|
||||
h = h * 33u + (unsigned char) p[i];
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
||||
struct svg_defs_entry_t
|
||||
{
|
||||
svg_id_span_t id;
|
||||
unsigned start;
|
||||
unsigned end;
|
||||
};
|
||||
|
||||
struct svg_doc_cache_t
|
||||
{
|
||||
hb_blob_t *blob = nullptr;
|
||||
const char *svg = nullptr;
|
||||
unsigned len = 0;
|
||||
hb_vector_t<svg_defs_entry_t> defs_entries;
|
||||
hb_codepoint_t start_glyph = HB_CODEPOINT_INVALID;
|
||||
hb_codepoint_t end_glyph = HB_CODEPOINT_INVALID;
|
||||
hb_vector_t<hb_pair_t<uint32_t, uint32_t>> glyph_spans;
|
||||
hb_hashmap_t<svg_id_span_t, hb_pair_t<uint32_t, uint32_t>> id_spans;
|
||||
};
|
||||
|
||||
bool has_data () const { return svgDocEntries; }
|
||||
|
||||
struct accelerator_t
|
||||
{
|
||||
accelerator_t (hb_face_t *face)
|
||||
{ table = hb_sanitize_context_t ().reference_table<SVG> (face); }
|
||||
~accelerator_t () { table.destroy (); }
|
||||
accelerator_t (hb_face_t *face);
|
||||
~accelerator_t ();
|
||||
|
||||
hb_blob_t *reference_blob_for_glyph (hb_codepoint_t glyph_id) const
|
||||
{
|
||||
@@ -92,8 +184,52 @@ struct SVG
|
||||
table->svgDocEntries);
|
||||
}
|
||||
|
||||
unsigned get_document_count () const
|
||||
{ return table->get_document_count (); }
|
||||
|
||||
bool get_glyph_document_index (hb_codepoint_t glyph_id, unsigned *index) const
|
||||
{ return table->get_glyph_document_index (glyph_id, index); }
|
||||
|
||||
bool get_document_glyph_range (unsigned index,
|
||||
hb_codepoint_t *start_glyph,
|
||||
hb_codepoint_t *end_glyph) const
|
||||
{ return table->get_document_glyph_range (index, start_glyph, end_glyph); }
|
||||
|
||||
bool has_data () const { return table->has_data (); }
|
||||
|
||||
const svg_doc_cache_t *
|
||||
get_or_create_doc_cache (hb_blob_t *image,
|
||||
const char *svg,
|
||||
unsigned len,
|
||||
unsigned doc_index,
|
||||
hb_codepoint_t start_glyph,
|
||||
hb_codepoint_t end_glyph) const;
|
||||
|
||||
const char *
|
||||
doc_cache_get_svg (const svg_doc_cache_t *doc,
|
||||
unsigned *len) const;
|
||||
|
||||
const hb_vector_t<svg_defs_entry_t> *
|
||||
doc_cache_get_defs_entries (const svg_doc_cache_t *doc) const;
|
||||
|
||||
bool
|
||||
doc_cache_get_glyph_span (const svg_doc_cache_t *doc,
|
||||
hb_codepoint_t glyph,
|
||||
unsigned *start,
|
||||
unsigned *end) const;
|
||||
|
||||
bool
|
||||
doc_cache_find_id_span (const svg_doc_cache_t *doc,
|
||||
svg_id_span_t id,
|
||||
unsigned *start,
|
||||
unsigned *end) const;
|
||||
|
||||
bool
|
||||
doc_cache_find_id_cstr (const svg_doc_cache_t *doc,
|
||||
const char *id,
|
||||
unsigned *start,
|
||||
unsigned *end) const;
|
||||
|
||||
bool paint_glyph (hb_font_t *font HB_UNUSED, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data) const
|
||||
{
|
||||
if (!has_data ())
|
||||
@@ -117,14 +253,56 @@ struct SVG
|
||||
}
|
||||
|
||||
private:
|
||||
svg_doc_cache_t *
|
||||
make_doc_cache (hb_blob_t *image,
|
||||
const char *svg,
|
||||
unsigned len,
|
||||
hb_codepoint_t start_glyph,
|
||||
hb_codepoint_t end_glyph) const;
|
||||
|
||||
static void destroy_doc_cache (svg_doc_cache_t *doc);
|
||||
|
||||
hb_blob_ptr_t<SVG> table;
|
||||
mutable hb_vector_t<hb_atomic_t<svg_doc_cache_t *>> doc_caches;
|
||||
public:
|
||||
DEFINE_SIZE_STATIC (sizeof (hb_blob_ptr_t<SVG>));
|
||||
DEFINE_SIZE_STATIC (sizeof (hb_blob_ptr_t<SVG>) +
|
||||
sizeof (hb_vector_t<hb_atomic_t<svg_doc_cache_t *>>));
|
||||
};
|
||||
|
||||
const SVGDocumentIndexEntry &get_glyph_entry (hb_codepoint_t glyph_id) const
|
||||
{ return (this+svgDocEntries).bsearch (glyph_id); }
|
||||
|
||||
unsigned get_document_count () const
|
||||
{
|
||||
if (!has_data ())
|
||||
return 0;
|
||||
return (this + svgDocEntries).len;
|
||||
}
|
||||
|
||||
bool get_glyph_document_index (hb_codepoint_t glyph_id, unsigned *index) const
|
||||
{
|
||||
if (!has_data ())
|
||||
return false;
|
||||
return (this + svgDocEntries).bfind (glyph_id, index);
|
||||
}
|
||||
|
||||
bool get_document_glyph_range (unsigned index,
|
||||
hb_codepoint_t *start_glyph,
|
||||
hb_codepoint_t *end_glyph) const
|
||||
{
|
||||
if (!has_data ())
|
||||
return false;
|
||||
|
||||
const auto &entries = this + svgDocEntries;
|
||||
if (index >= entries.len)
|
||||
return false;
|
||||
|
||||
const auto &entry = entries.arrayZ[index];
|
||||
if (start_glyph) *start_glyph = entry.get_start_glyph ();
|
||||
if (end_glyph) *end_glyph = entry.get_end_glyph ();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool sanitize (hb_sanitize_context_t *c) const
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
@@ -143,6 +321,506 @@ struct SVG
|
||||
DEFINE_SIZE_STATIC (10);
|
||||
};
|
||||
|
||||
namespace _hb_svg_cache_impl {
|
||||
|
||||
struct glyph_entry_t
|
||||
{
|
||||
hb_codepoint_t glyph;
|
||||
uint32_t start;
|
||||
uint32_t end;
|
||||
};
|
||||
|
||||
struct id_entry_t
|
||||
{
|
||||
SVG::svg_id_span_t id;
|
||||
uint32_t start;
|
||||
uint32_t end;
|
||||
};
|
||||
|
||||
struct open_elem_t
|
||||
{
|
||||
unsigned start;
|
||||
SVG::svg_id_span_t id;
|
||||
bool in_defs_content;
|
||||
bool is_defs;
|
||||
};
|
||||
|
||||
static const unsigned MAX_DEPTH = 128;
|
||||
|
||||
static inline int
|
||||
find_substr (const char *s,
|
||||
unsigned n,
|
||||
unsigned from,
|
||||
const char *needle,
|
||||
unsigned needle_len)
|
||||
{
|
||||
if (!needle_len || from >= n || needle_len > n)
|
||||
return -1;
|
||||
for (unsigned i = from; i + needle_len <= n; i++)
|
||||
if (s[i] == needle[0] && !memcmp (s + i, needle, needle_len))
|
||||
return (int) i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
parse_id_in_start_tag (const char *svg,
|
||||
unsigned tag_start,
|
||||
unsigned tag_end,
|
||||
SVG::svg_id_span_t *id)
|
||||
{
|
||||
unsigned p = tag_start;
|
||||
while (p + 4 <= tag_end)
|
||||
{
|
||||
if (!memcmp (svg + p, "id=\"", 4))
|
||||
{
|
||||
unsigned b = p + 4;
|
||||
unsigned e = b;
|
||||
while (e < tag_end && svg[e] != '"') e++;
|
||||
if (e <= tag_end && e > b)
|
||||
{
|
||||
*id = {svg + b, e - b};
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (!memcmp (svg + p, "id='", 4))
|
||||
{
|
||||
unsigned b = p + 4;
|
||||
unsigned e = b;
|
||||
while (e < tag_end && svg[e] != '\'') e++;
|
||||
if (e <= tag_end && e > b)
|
||||
{
|
||||
*id = {svg + b, e - b};
|
||||
return true;
|
||||
}
|
||||
}
|
||||
p++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
parse_glyph_id_span (const SVG::svg_id_span_t &id,
|
||||
hb_codepoint_t *glyph)
|
||||
{
|
||||
if (id.len <= 5 || memcmp (id.p, "glyph", 5))
|
||||
return false;
|
||||
|
||||
hb_codepoint_t gid = 0;
|
||||
for (unsigned i = 5; i < id.len; i++)
|
||||
{
|
||||
unsigned char c = (unsigned char) id.p[i];
|
||||
if (c < '0' || c > '9')
|
||||
return false;
|
||||
hb_codepoint_t digit = (hb_codepoint_t) (c - '0');
|
||||
if (unlikely (gid > HB_CODEPOINT_INVALID / 10 ||
|
||||
(gid == HB_CODEPOINT_INVALID / 10 &&
|
||||
digit > HB_CODEPOINT_INVALID % 10)))
|
||||
return false;
|
||||
gid = (hb_codepoint_t) (gid * 10 + digit);
|
||||
}
|
||||
|
||||
*glyph = gid;
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
parse_cache_entries_linear (const char *svg,
|
||||
unsigned len,
|
||||
hb_vector_t<SVG::svg_defs_entry_t> *defs_entries,
|
||||
hb_vector_t<glyph_entry_t> *glyph_spans,
|
||||
hb_vector_t<id_entry_t> *id_entries)
|
||||
{
|
||||
open_elem_t stack[MAX_DEPTH];
|
||||
unsigned depth = 0;
|
||||
defs_entries->alloc (256);
|
||||
id_entries->alloc (256);
|
||||
|
||||
unsigned defs_depth = 0;
|
||||
unsigned i = 0;
|
||||
while (i < len)
|
||||
{
|
||||
if (svg[i] != '<')
|
||||
{
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i + 4 <= len && !memcmp (svg + i, "<!--", 4))
|
||||
{
|
||||
int cend = find_substr (svg, len, i + 4, "-->", 3);
|
||||
if (cend < 0) return false;
|
||||
i = (unsigned) cend + 3;
|
||||
continue;
|
||||
}
|
||||
if (i + 9 <= len && !memcmp (svg + i, "<![CDATA[", 9))
|
||||
{
|
||||
int cend = find_substr (svg, len, i + 9, "]]>", 3);
|
||||
if (cend < 0) return false;
|
||||
i = (unsigned) cend + 3;
|
||||
continue;
|
||||
}
|
||||
|
||||
bool closing = (i + 1 < len && svg[i + 1] == '/');
|
||||
bool special = (i + 1 < len && (svg[i + 1] == '!' || svg[i + 1] == '?'));
|
||||
|
||||
unsigned gt = i + 1;
|
||||
char quote = 0;
|
||||
while (gt < len)
|
||||
{
|
||||
char c = svg[gt];
|
||||
if (quote)
|
||||
{
|
||||
if (c == quote) quote = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (c == '"' || c == '\'')
|
||||
quote = c;
|
||||
else if (c == '>')
|
||||
break;
|
||||
}
|
||||
gt++;
|
||||
}
|
||||
if (gt >= len)
|
||||
return false;
|
||||
|
||||
if (special)
|
||||
{
|
||||
i = gt + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
unsigned p = i + (closing ? 2 : 1);
|
||||
while (p < gt && isspace ((unsigned char) svg[p])) p++;
|
||||
const char *name = svg + p;
|
||||
unsigned name_len = 0;
|
||||
while (p + name_len < gt)
|
||||
{
|
||||
unsigned char c = (unsigned char) name[name_len];
|
||||
if (!(isalnum (c) || c == '_' || c == '-' || c == ':'))
|
||||
break;
|
||||
name_len++;
|
||||
}
|
||||
bool is_defs = (name_len == 4 && !memcmp (name, "defs", 4));
|
||||
|
||||
if (closing)
|
||||
{
|
||||
if (!depth)
|
||||
{
|
||||
i = gt + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
open_elem_t e = stack[--depth];
|
||||
unsigned end = gt + 1;
|
||||
|
||||
if (e.id.len)
|
||||
{
|
||||
auto *id_slot = id_entries->push ();
|
||||
if (unlikely (!id_slot))
|
||||
return false;
|
||||
*id_slot = {e.id, (uint32_t) e.start, (uint32_t) end};
|
||||
|
||||
if (e.in_defs_content)
|
||||
{
|
||||
auto *slot = defs_entries->push ();
|
||||
if (unlikely (!slot))
|
||||
return false;
|
||||
slot->id = e.id;
|
||||
slot->start = e.start;
|
||||
slot->end = end;
|
||||
}
|
||||
|
||||
hb_codepoint_t gid;
|
||||
if (parse_glyph_id_span (e.id, &gid))
|
||||
{
|
||||
auto *span = glyph_spans->push ();
|
||||
if (unlikely (!span))
|
||||
return false;
|
||||
span->glyph = gid;
|
||||
span->start = (uint32_t) e.start;
|
||||
span->end = (uint32_t) end;
|
||||
}
|
||||
}
|
||||
|
||||
if (e.is_defs && defs_depth)
|
||||
defs_depth--;
|
||||
|
||||
i = end;
|
||||
continue;
|
||||
}
|
||||
|
||||
SVG::svg_id_span_t id = {nullptr, 0};
|
||||
parse_id_in_start_tag (svg, i, gt, &id);
|
||||
|
||||
unsigned r = gt;
|
||||
while (r > i && isspace ((unsigned char) svg[r - 1])) r--;
|
||||
bool self_closing = (r > i && svg[r - 1] == '/');
|
||||
|
||||
open_elem_t e = {i, id, defs_depth > 0, is_defs};
|
||||
|
||||
if (self_closing)
|
||||
{
|
||||
unsigned end = gt + 1;
|
||||
if (e.id.len)
|
||||
{
|
||||
auto *id_slot = id_entries->push ();
|
||||
if (unlikely (!id_slot))
|
||||
return false;
|
||||
*id_slot = {e.id, (uint32_t) e.start, (uint32_t) end};
|
||||
|
||||
if (e.in_defs_content)
|
||||
{
|
||||
auto *slot = defs_entries->push ();
|
||||
if (unlikely (!slot))
|
||||
return false;
|
||||
slot->id = e.id;
|
||||
slot->start = e.start;
|
||||
slot->end = end;
|
||||
}
|
||||
|
||||
hb_codepoint_t gid;
|
||||
if (parse_glyph_id_span (e.id, &gid))
|
||||
{
|
||||
auto *span = glyph_spans->push ();
|
||||
if (unlikely (!span))
|
||||
return false;
|
||||
span->glyph = gid;
|
||||
span->start = (uint32_t) e.start;
|
||||
span->end = (uint32_t) end;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (unlikely (depth >= MAX_DEPTH))
|
||||
return false;
|
||||
stack[depth++] = e;
|
||||
if (is_defs)
|
||||
defs_depth++;
|
||||
}
|
||||
|
||||
i = gt + 1;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} /* namespace _hb_svg_cache_impl */
|
||||
|
||||
inline
|
||||
SVG::accelerator_t::accelerator_t (hb_face_t *face)
|
||||
{
|
||||
table = hb_sanitize_context_t ().reference_table<SVG> (face);
|
||||
doc_caches.init ();
|
||||
unsigned doc_count = table->get_document_count ();
|
||||
if (doc_count && unlikely (!doc_caches.resize (doc_count)))
|
||||
doc_caches.resize (0);
|
||||
for (unsigned i = 0; i < doc_caches.length; i++)
|
||||
doc_caches.arrayZ[i].set_relaxed (nullptr);
|
||||
}
|
||||
|
||||
inline
|
||||
SVG::accelerator_t::~accelerator_t ()
|
||||
{
|
||||
for (unsigned i = 0; i < doc_caches.length; i++)
|
||||
destroy_doc_cache (doc_caches.arrayZ[i].get_relaxed ());
|
||||
doc_caches.fini ();
|
||||
table.destroy ();
|
||||
}
|
||||
|
||||
inline void
|
||||
SVG::accelerator_t::destroy_doc_cache (svg_doc_cache_t *doc)
|
||||
{
|
||||
if (!doc)
|
||||
return;
|
||||
doc->glyph_spans.fini ();
|
||||
doc->defs_entries.fini ();
|
||||
doc->id_spans.fini ();
|
||||
hb_blob_destroy (doc->blob);
|
||||
hb_free (doc);
|
||||
}
|
||||
|
||||
inline SVG::svg_doc_cache_t *
|
||||
SVG::accelerator_t::make_doc_cache (hb_blob_t *image,
|
||||
const char *svg,
|
||||
unsigned len,
|
||||
hb_codepoint_t start_glyph,
|
||||
hb_codepoint_t end_glyph) const
|
||||
{
|
||||
static const uint32_t INVALID_SPAN = 0xFFFFFFFFu;
|
||||
|
||||
auto *doc = (svg_doc_cache_t *) hb_malloc (sizeof (svg_doc_cache_t));
|
||||
if (!doc)
|
||||
return nullptr;
|
||||
|
||||
doc->blob = nullptr;
|
||||
doc->svg = nullptr;
|
||||
doc->len = 0;
|
||||
doc->defs_entries.init ();
|
||||
doc->start_glyph = HB_CODEPOINT_INVALID;
|
||||
doc->end_glyph = HB_CODEPOINT_INVALID;
|
||||
doc->glyph_spans.init ();
|
||||
doc->id_spans.init ();
|
||||
|
||||
doc->blob = hb_blob_reference (image);
|
||||
doc->svg = svg;
|
||||
doc->len = len;
|
||||
doc->start_glyph = start_glyph;
|
||||
doc->end_glyph = end_glyph;
|
||||
|
||||
if (unlikely (start_glyph == HB_CODEPOINT_INVALID || end_glyph < start_glyph))
|
||||
{
|
||||
destroy_doc_cache (doc);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
unsigned glyph_count = end_glyph - start_glyph + 1;
|
||||
if (!doc->glyph_spans.resize ((int) glyph_count))
|
||||
{
|
||||
destroy_doc_cache (doc);
|
||||
return nullptr;
|
||||
}
|
||||
for (unsigned i = 0; i < glyph_count; i++)
|
||||
doc->glyph_spans.arrayZ[i] = hb_pair_t<uint32_t, uint32_t> (INVALID_SPAN, INVALID_SPAN);
|
||||
|
||||
hb_vector_t<_hb_svg_cache_impl::glyph_entry_t> glyph_spans;
|
||||
glyph_spans.init ();
|
||||
hb_vector_t<_hb_svg_cache_impl::id_entry_t> id_entries;
|
||||
id_entries.init ();
|
||||
if (!_hb_svg_cache_impl::parse_cache_entries_linear (svg, len,
|
||||
&doc->defs_entries,
|
||||
&glyph_spans,
|
||||
&id_entries))
|
||||
{
|
||||
id_entries.fini ();
|
||||
glyph_spans.fini ();
|
||||
destroy_doc_cache (doc);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < glyph_spans.length; i++)
|
||||
{
|
||||
const auto &span = glyph_spans.arrayZ[i];
|
||||
if (unlikely (span.glyph < start_glyph || span.glyph > end_glyph))
|
||||
continue;
|
||||
doc->glyph_spans.arrayZ[span.glyph - start_glyph] = hb_pair_t<uint32_t, uint32_t> (span.start, span.end);
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < id_entries.length; i++)
|
||||
{
|
||||
const auto &e = id_entries.arrayZ[i];
|
||||
hb_pair_t<uint32_t, uint32_t> *out = nullptr;
|
||||
if (doc->id_spans.has (e.id, &out))
|
||||
continue;
|
||||
if (unlikely (!doc->id_spans.set (e.id, hb_pair_t<uint32_t, uint32_t> (e.start, e.end))))
|
||||
{
|
||||
id_entries.fini ();
|
||||
glyph_spans.fini ();
|
||||
destroy_doc_cache (doc);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
id_entries.fini ();
|
||||
glyph_spans.fini ();
|
||||
return doc;
|
||||
}
|
||||
|
||||
inline const SVG::svg_doc_cache_t *
|
||||
SVG::accelerator_t::get_or_create_doc_cache (hb_blob_t *image,
|
||||
const char *svg,
|
||||
unsigned len,
|
||||
unsigned doc_index,
|
||||
hb_codepoint_t start_glyph,
|
||||
hb_codepoint_t end_glyph) const
|
||||
{
|
||||
if (doc_index >= doc_caches.length)
|
||||
return nullptr;
|
||||
|
||||
auto &slot = doc_caches.arrayZ[doc_index];
|
||||
auto *doc = slot.get_acquire ();
|
||||
if (doc)
|
||||
return doc;
|
||||
|
||||
auto *fresh = make_doc_cache (image, svg, len, start_glyph, end_glyph);
|
||||
if (!fresh)
|
||||
return nullptr;
|
||||
|
||||
auto *expected = (svg_doc_cache_t *) nullptr;
|
||||
if (slot.cmpexch (expected, fresh))
|
||||
return fresh;
|
||||
|
||||
destroy_doc_cache (fresh);
|
||||
return expected;
|
||||
}
|
||||
|
||||
inline const char *
|
||||
SVG::accelerator_t::doc_cache_get_svg (const svg_doc_cache_t *doc,
|
||||
unsigned *len) const
|
||||
{
|
||||
if (!doc)
|
||||
{
|
||||
if (len) *len = 0;
|
||||
return nullptr;
|
||||
}
|
||||
if (len) *len = doc->len;
|
||||
return doc->svg;
|
||||
}
|
||||
|
||||
inline const hb_vector_t<SVG::svg_defs_entry_t> *
|
||||
SVG::accelerator_t::doc_cache_get_defs_entries (const svg_doc_cache_t *doc) const
|
||||
{
|
||||
return doc ? &doc->defs_entries : nullptr;
|
||||
}
|
||||
|
||||
inline bool
|
||||
SVG::accelerator_t::doc_cache_get_glyph_span (const svg_doc_cache_t *doc,
|
||||
hb_codepoint_t glyph,
|
||||
unsigned *start,
|
||||
unsigned *end) const
|
||||
{
|
||||
static const uint32_t INVALID_SPAN = 0xFFFFFFFFu;
|
||||
if (!doc || doc->start_glyph == HB_CODEPOINT_INVALID ||
|
||||
glyph < doc->start_glyph || glyph > doc->end_glyph)
|
||||
return false;
|
||||
|
||||
const auto &span = doc->glyph_spans.arrayZ[glyph - doc->start_glyph];
|
||||
if (span.first == INVALID_SPAN)
|
||||
return false;
|
||||
|
||||
if (start) *start = span.first;
|
||||
if (end) *end = span.second;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool
|
||||
SVG::accelerator_t::doc_cache_find_id_span (const svg_doc_cache_t *doc,
|
||||
svg_id_span_t id,
|
||||
unsigned *start,
|
||||
unsigned *end) const
|
||||
{
|
||||
if (!doc || !id.p || !id.len)
|
||||
return false;
|
||||
hb_pair_t<uint32_t, uint32_t> *span = nullptr;
|
||||
if (!doc->id_spans.has (id, &span))
|
||||
return false;
|
||||
if (start) *start = span->first;
|
||||
if (end) *end = span->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool
|
||||
SVG::accelerator_t::doc_cache_find_id_cstr (const svg_doc_cache_t *doc,
|
||||
const char *id,
|
||||
unsigned *start,
|
||||
unsigned *end) const
|
||||
{
|
||||
if (!id) return false;
|
||||
svg_id_span_t key = {id, (unsigned) strlen (id)};
|
||||
return doc_cache_find_id_span (doc, key, start, end);
|
||||
}
|
||||
|
||||
struct SVG_accelerator_t : SVG::accelerator_t {
|
||||
SVG_accelerator_t (hb_face_t *face) : SVG::accelerator_t (face) {}
|
||||
};
|
||||
|
||||
@@ -100,7 +100,7 @@ struct SinglePosFormat1 : ValueBase
|
||||
if (likely (index == NOT_COVERED)) return false;
|
||||
|
||||
/* This is ugly... */
|
||||
hb_buffer_t buffer;
|
||||
hb_buffer_t buffer {};
|
||||
buffer.props.direction = direction;
|
||||
OT::hb_ot_apply_context_t c (1, font, &buffer, table_blob);
|
||||
|
||||
|
||||
@@ -104,7 +104,7 @@ struct SinglePosFormat2 : ValueBase
|
||||
if (unlikely (index >= valueCount)) return false;
|
||||
|
||||
/* This is ugly... */
|
||||
hb_buffer_t buffer;
|
||||
hb_buffer_t buffer {};
|
||||
buffer.props.direction = direction;
|
||||
OT::hb_ot_apply_context_t c (1, font, &buffer, table_blob);
|
||||
|
||||
|
||||
+34
-34
@@ -228,28 +228,28 @@ VarComponent::get_path_at (const hb_varc_context_t &c,
|
||||
|
||||
#define PROCESS_TRANSFORM_COMPONENTS \
|
||||
HB_STMT_START { \
|
||||
PROCESS_TRANSFORM_COMPONENT (FWORD, 1.0f, HAVE_TRANSLATE_X, translateX); \
|
||||
PROCESS_TRANSFORM_COMPONENT (FWORD, 1.0f, HAVE_TRANSLATE_Y, translateY); \
|
||||
PROCESS_TRANSFORM_COMPONENT (F4DOT12, HB_PI, HAVE_ROTATION, rotation); \
|
||||
PROCESS_TRANSFORM_COMPONENT (F6DOT10, 1.0f, HAVE_SCALE_X, scaleX); \
|
||||
PROCESS_TRANSFORM_COMPONENT (F6DOT10, 1.0f, HAVE_SCALE_Y, scaleY); \
|
||||
PROCESS_TRANSFORM_COMPONENT (F4DOT12, HB_PI, HAVE_SKEW_X, skewX); \
|
||||
PROCESS_TRANSFORM_COMPONENT (F4DOT12, HB_PI, HAVE_SKEW_Y, skewY); \
|
||||
PROCESS_TRANSFORM_COMPONENT (FWORD, 1.0f, HAVE_TCENTER_X, tCenterX); \
|
||||
PROCESS_TRANSFORM_COMPONENT (FWORD, 1.0f, HAVE_TCENTER_Y, tCenterY); \
|
||||
PROCESS_TRANSFORM_COMPONENT ( 0, FWORD, HAVE_TRANSLATE_X, translateX); \
|
||||
PROCESS_TRANSFORM_COMPONENT ( 0, FWORD, HAVE_TRANSLATE_Y, translateY); \
|
||||
PROCESS_TRANSFORM_COMPONENT (12, F4DOT12, HAVE_ROTATION, rotation); \
|
||||
PROCESS_TRANSFORM_COMPONENT (10, F6DOT10, HAVE_SCALE_X, scaleX); \
|
||||
PROCESS_TRANSFORM_COMPONENT (10, F6DOT10, HAVE_SCALE_Y, scaleY); \
|
||||
PROCESS_TRANSFORM_COMPONENT (12, F4DOT12, HAVE_SKEW_X, skewX); \
|
||||
PROCESS_TRANSFORM_COMPONENT (12, F4DOT12, HAVE_SKEW_Y, skewY); \
|
||||
PROCESS_TRANSFORM_COMPONENT ( 0, FWORD, HAVE_TCENTER_X, tCenterX); \
|
||||
PROCESS_TRANSFORM_COMPONENT ( 0, FWORD, HAVE_TCENTER_Y, tCenterY); \
|
||||
} HB_STMT_END
|
||||
|
||||
hb_transform_decomposed_t<> transform;
|
||||
|
||||
// Read transform components
|
||||
#define PROCESS_TRANSFORM_COMPONENT(type, mult, flag, name) \
|
||||
#define PROCESS_TRANSFORM_COMPONENT(shift, type, flag, name) \
|
||||
if (flags & (unsigned) flags_t::flag) \
|
||||
{ \
|
||||
static_assert (type::static_size == HBINT16::static_size, ""); \
|
||||
if (unlikely (unsigned (end - record) < HBINT16::static_size)) \
|
||||
return hb_ubytes_t (); \
|
||||
hb_barrier (); \
|
||||
transform.name = mult * * (const HBINT16 *) record; \
|
||||
transform.name = * (const HBINT16 *) record; \
|
||||
record += HBINT16::static_size; \
|
||||
}
|
||||
PROCESS_TRANSFORM_COMPONENTS;
|
||||
@@ -270,9 +270,8 @@ VarComponent::get_path_at (const hb_varc_context_t &c,
|
||||
{
|
||||
// Only use coord_setter if there's actually any axis overrides.
|
||||
coord_setter_t coord_setter (axisIndices ? component_coords : hb_array<int> ());
|
||||
// Go backwards, to reduce coord_setter vector reallocations.
|
||||
for (unsigned i = axisIndices.length; i; i--)
|
||||
coord_setter[axisIndices[i - 1]] = axisValues[i - 1];
|
||||
for (unsigned i = 0; i < axisIndices.length; i++)
|
||||
coord_setter[axisIndices[i]] = roundf (axisValues[i]);
|
||||
if (axisIndices)
|
||||
component_coords = coord_setter.get_coords ();
|
||||
|
||||
@@ -281,39 +280,35 @@ VarComponent::get_path_at (const hb_varc_context_t &c,
|
||||
{
|
||||
float transformValues[9];
|
||||
unsigned numTransformValues = 0;
|
||||
#define PROCESS_TRANSFORM_COMPONENT(type, mult, flag, name) \
|
||||
#define PROCESS_TRANSFORM_COMPONENT(shift, type, flag, name) \
|
||||
if (flags & (unsigned) flags_t::flag) \
|
||||
transformValues[numTransformValues++] = transform.name / mult;
|
||||
transformValues[numTransformValues++] = transform.name;
|
||||
PROCESS_TRANSFORM_COMPONENTS;
|
||||
#undef PROCESS_TRANSFORM_COMPONENT
|
||||
varStore.get_delta (transformVarIdx, coords, hb_array (transformValues, numTransformValues), cache);
|
||||
numTransformValues = 0;
|
||||
#define PROCESS_TRANSFORM_COMPONENT(type, mult, flag, name) \
|
||||
#define PROCESS_TRANSFORM_COMPONENT(shift, type, flag, name) \
|
||||
if (flags & (unsigned) flags_t::flag) \
|
||||
transform.name = transformValues[numTransformValues++] * mult;
|
||||
transform.name = transformValues[numTransformValues++];
|
||||
PROCESS_TRANSFORM_COMPONENTS;
|
||||
#undef PROCESS_TRANSFORM_COMPONENT
|
||||
}
|
||||
|
||||
// Divide them by their divisors
|
||||
#define PROCESS_TRANSFORM_COMPONENT(type, mult, flag, name) \
|
||||
if (flags & (unsigned) flags_t::flag) \
|
||||
{ \
|
||||
HBINT16 int_v; \
|
||||
int_v = roundf (transform.name); \
|
||||
type typed_v = * (const type *) &int_v; \
|
||||
float float_v = (float) typed_v; \
|
||||
transform.name = float_v; \
|
||||
}
|
||||
#define PROCESS_TRANSFORM_COMPONENT(shift, type, flag, name) \
|
||||
if (shift && (flags & (unsigned) flags_t::flag)) \
|
||||
transform.name *= 1.f / (1 << shift);
|
||||
PROCESS_TRANSFORM_COMPONENTS;
|
||||
#undef PROCESS_TRANSFORM_COMPONENT
|
||||
|
||||
if (!(flags & (unsigned) flags_t::HAVE_SCALE_Y))
|
||||
transform.scaleY = transform.scaleX;
|
||||
|
||||
transform.rotation *= HB_PI;
|
||||
transform.skewX *= HB_PI;
|
||||
transform.skewY *= HB_PI;
|
||||
|
||||
total_transform.transform (transform.to_transform ());
|
||||
total_transform.scale (c.font->x_mult ? 1.f / c.font->x_multf : 0.f,
|
||||
c.font->y_mult ? 1.f / c.font->y_multf : 0.f);
|
||||
|
||||
bool same_coords = component_coords.length == coords.length &&
|
||||
component_coords.arrayZ == coords.arrayZ;
|
||||
@@ -348,14 +343,18 @@ VARC::get_path_at (const hb_varc_context_t &c,
|
||||
{
|
||||
if (c.draw_session)
|
||||
{
|
||||
hb_transform_t<> leaf_transform = transform;
|
||||
leaf_transform.x0 *= c.font->x_multf;
|
||||
leaf_transform.y0 *= c.font->y_multf;
|
||||
|
||||
// Build a transforming pen to apply the transform.
|
||||
hb_draw_funcs_t *transformer_funcs = hb_transforming_pen_get_funcs ();
|
||||
hb_transforming_pen_context_t context {transform,
|
||||
hb_transforming_pen_context_t context {leaf_transform,
|
||||
c.draw_session->funcs,
|
||||
c.draw_session->draw_data,
|
||||
&c.draw_session->st};
|
||||
hb_draw_session_t transformer_session {transformer_funcs, &context};
|
||||
hb_draw_session_t &shape_draw_session = transform.is_identity () ? *c.draw_session : transformer_session;
|
||||
hb_draw_session_t &shape_draw_session = leaf_transform.is_identity () ? *c.draw_session : transformer_session;
|
||||
|
||||
if (c.font->face->table.glyf->get_path_at (c.font, glyph, shape_draw_session, coords, c.scratch.glyf_scratch)) return true;
|
||||
#ifndef HB_NO_CFF
|
||||
@@ -375,7 +374,10 @@ VARC::get_path_at (const hb_varc_context_t &c,
|
||||
return false;
|
||||
|
||||
hb_extents_t<> comp_extents (glyph_extents);
|
||||
transform.transform_extents (comp_extents);
|
||||
hb_transform_t<> leaf_transform = transform;
|
||||
leaf_transform.x0 *= c.font->x_multf;
|
||||
leaf_transform.y0 *= c.font->y_multf;
|
||||
leaf_transform.transform_extents (comp_extents);
|
||||
c.extents->union_ (comp_extents);
|
||||
}
|
||||
return true;
|
||||
@@ -399,8 +401,6 @@ VARC::get_path_at (const hb_varc_context_t &c,
|
||||
parent_cache :
|
||||
(this+varStore).create_cache (&static_cache);
|
||||
|
||||
transform.scale (c.font->x_multf, c.font->y_multf);
|
||||
|
||||
VarCompositeGlyph::get_path_at (c,
|
||||
glyph,
|
||||
coords, transform,
|
||||
|
||||
+16
-12
@@ -384,20 +384,24 @@ struct Glyph
|
||||
{
|
||||
#ifndef HB_NO_BEYOND_64K
|
||||
if (glyf_accelerator.GVAR->has_data ())
|
||||
glyf_accelerator.GVAR->apply_deltas_to_points (gid,
|
||||
coords,
|
||||
points.as_array ().sub_array (old_length),
|
||||
scratch,
|
||||
gvar_cache,
|
||||
phantom_only && type == SIMPLE);
|
||||
{
|
||||
if (!glyf_accelerator.GVAR->apply_deltas_to_points (gid,
|
||||
coords,
|
||||
points.as_array ().sub_array (old_length),
|
||||
scratch,
|
||||
gvar_cache,
|
||||
phantom_only && type == SIMPLE))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
glyf_accelerator.gvar->apply_deltas_to_points (gid,
|
||||
coords,
|
||||
points.as_array ().sub_array (old_length),
|
||||
scratch,
|
||||
gvar_cache,
|
||||
phantom_only && type == SIMPLE);
|
||||
if (!glyf_accelerator.gvar->apply_deltas_to_points (gid,
|
||||
coords,
|
||||
points.as_array ().sub_array (old_length),
|
||||
scratch,
|
||||
gvar_cache,
|
||||
phantom_only && type == SIMPLE))
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
Vendored
+58
-26
@@ -89,7 +89,16 @@ static inline constexpr uint32_t hb_uint32_swap (uint32_t v)
|
||||
{ return (hb_uint16_swap (v) << 16) | hb_uint16_swap (v >> 16); }
|
||||
|
||||
template <typename Type>
|
||||
struct __attribute__((packed)) hb_packed_t { Type v; };
|
||||
struct __attribute__((packed)) hb_packed_t
|
||||
{
|
||||
hb_packed_t () = default;
|
||||
constexpr hb_packed_t (Type V) : v (V) {}
|
||||
operator Type () const { return v; }
|
||||
hb_packed_t & operator = (Type V) { v = V; return *this; }
|
||||
|
||||
private:
|
||||
Type v;
|
||||
};
|
||||
|
||||
#ifndef HB_FAST_NUM_ACCESS
|
||||
|
||||
@@ -134,9 +143,9 @@ struct HBInt<BE, Type, 2>
|
||||
#if HB_FAST_NUM_ACCESS
|
||||
{
|
||||
if (BE == (__BYTE_ORDER == __BIG_ENDIAN))
|
||||
((hb_packed_t<uint16_t> *) v)->v = V;
|
||||
*((hb_packed_t<uint16_t> *) v) = V;
|
||||
else
|
||||
((hb_packed_t<uint16_t> *) v)->v = __builtin_bswap16 (V);
|
||||
*((hb_packed_t<uint16_t> *) v) = __builtin_bswap16 (V);
|
||||
}
|
||||
#else
|
||||
: v {BE ? uint8_t ((V >> 8) & 0xFF) : uint8_t ((V ) & 0xFF),
|
||||
@@ -147,9 +156,9 @@ struct HBInt<BE, Type, 2>
|
||||
{
|
||||
#if HB_FAST_NUM_ACCESS
|
||||
return (BE == (__BYTE_ORDER == __BIG_ENDIAN)) ?
|
||||
((const hb_packed_t<uint16_t> *) v)->v
|
||||
(uint16_t) *((const hb_packed_t<uint16_t> *) v)
|
||||
:
|
||||
__builtin_bswap16 (((const hb_packed_t<uint16_t> *) v)->v)
|
||||
__builtin_bswap16 ((uint16_t) *((const hb_packed_t<uint16_t> *) v))
|
||||
;
|
||||
#else
|
||||
return (BE ? (v[0] << 8) : (v[0] ))
|
||||
@@ -186,9 +195,9 @@ struct HBInt<BE, Type, 4>
|
||||
#if HB_FAST_NUM_ACCESS
|
||||
{
|
||||
if (BE == (__BYTE_ORDER == __BIG_ENDIAN))
|
||||
((hb_packed_t<uint32_t> *) v)->v = V;
|
||||
*((hb_packed_t<uint32_t> *) v) = V;
|
||||
else
|
||||
((hb_packed_t<uint32_t> *) v)->v = __builtin_bswap32 (V);
|
||||
*((hb_packed_t<uint32_t> *) v) = __builtin_bswap32 (V);
|
||||
}
|
||||
#else
|
||||
: v {BE ? uint8_t ((V >> 24) & 0xFF) : uint8_t ((V ) & 0xFF),
|
||||
@@ -200,9 +209,9 @@ struct HBInt<BE, Type, 4>
|
||||
constexpr operator Type () const {
|
||||
#if HB_FAST_NUM_ACCESS
|
||||
return (BE == (__BYTE_ORDER == __BIG_ENDIAN)) ?
|
||||
((const hb_packed_t<uint32_t> *) v)->v
|
||||
(uint32_t) *((const hb_packed_t<uint32_t> *) v)
|
||||
:
|
||||
__builtin_bswap32 (((const hb_packed_t<uint32_t> *) v)->v)
|
||||
__builtin_bswap32 ((uint32_t) *((const hb_packed_t<uint32_t> *) v))
|
||||
;
|
||||
#else
|
||||
return (BE ? (v[0] << 24) : (v[0] ))
|
||||
@@ -226,9 +235,9 @@ struct HBInt<BE, Type, 8>
|
||||
#if HB_FAST_NUM_ACCESS
|
||||
{
|
||||
if (BE == (__BYTE_ORDER == __BIG_ENDIAN))
|
||||
((hb_packed_t<uint64_t> *) v)->v = V;
|
||||
*((hb_packed_t<uint64_t> *) v) = V;
|
||||
else
|
||||
((hb_packed_t<uint64_t> *) v)->v = __builtin_bswap64 (V);
|
||||
*((hb_packed_t<uint64_t> *) v) = __builtin_bswap64 (V);
|
||||
}
|
||||
#else
|
||||
: v {BE ? uint8_t ((V >> 56) & 0xFF) : uint8_t ((V ) & 0xFF),
|
||||
@@ -244,9 +253,9 @@ struct HBInt<BE, Type, 8>
|
||||
constexpr operator Type () const {
|
||||
#if HB_FAST_NUM_ACCESS
|
||||
return (BE == (__BYTE_ORDER == __BIG_ENDIAN)) ?
|
||||
((const hb_packed_t<uint64_t> *) v)->v
|
||||
(uint64_t) *((const hb_packed_t<uint64_t> *) v)
|
||||
:
|
||||
__builtin_bswap64 (((const hb_packed_t<uint64_t> *) v)->v)
|
||||
__builtin_bswap64 ((uint64_t) *((const hb_packed_t<uint64_t> *) v))
|
||||
;
|
||||
#else
|
||||
return (BE ? (uint64_t (v[0]) << 56) : (uint64_t (v[0]) ))
|
||||
@@ -276,12 +285,12 @@ struct HBFloat
|
||||
{
|
||||
#if HB_FAST_NUM_ACCESS
|
||||
{
|
||||
if (BE == (__BYTE_ORDER == __BIG_ENDIAN))
|
||||
{
|
||||
((hb_packed_t<Type> *) v)->v = V;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (BE == (__BYTE_ORDER == __BIG_ENDIAN))
|
||||
{
|
||||
*((hb_packed_t<Type> *) v) = V;
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
union {
|
||||
@@ -289,7 +298,7 @@ struct HBFloat
|
||||
hb_packed_t<IntType> i;
|
||||
} u = {{V}};
|
||||
|
||||
const HBInt<BE, IntType> I = u.i.v;
|
||||
const HBInt<BE, IntType> I = (IntType) u.i;
|
||||
for (unsigned i = 0; i < Bytes; i++)
|
||||
v[i] = I.v[i];
|
||||
}
|
||||
@@ -297,10 +306,10 @@ struct HBFloat
|
||||
/* c++14 constexpr */ operator Type () const
|
||||
{
|
||||
#if HB_FAST_NUM_ACCESS
|
||||
{
|
||||
if (BE == (__BYTE_ORDER == __BIG_ENDIAN))
|
||||
return ((const hb_packed_t<Type> *) v)->v;
|
||||
}
|
||||
{
|
||||
if (BE == (__BYTE_ORDER == __BIG_ENDIAN))
|
||||
return (Type) *((const hb_packed_t<Type> *) v);
|
||||
}
|
||||
#endif
|
||||
|
||||
HBInt<BE, IntType> I;
|
||||
@@ -312,7 +321,7 @@ struct HBFloat
|
||||
hb_packed_t<Type> f;
|
||||
} u = {{I}};
|
||||
|
||||
return u.f.v;
|
||||
return (Type) u.f;
|
||||
}
|
||||
private: uint8_t v[Bytes];
|
||||
};
|
||||
@@ -1191,6 +1200,21 @@ hb_unsigned_mul_overflows (unsigned int count, unsigned int size, unsigned *resu
|
||||
return (size > 0) && (count >= ((unsigned int) -1) / size);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
hb_unsigned_add_overflows (unsigned int a, unsigned int b, unsigned *result = nullptr)
|
||||
{
|
||||
#if hb_has_builtin(__builtin_add_overflow)
|
||||
unsigned stack_result;
|
||||
if (!result)
|
||||
result = &stack_result;
|
||||
return __builtin_add_overflow (a, b, result);
|
||||
#endif
|
||||
|
||||
if (result)
|
||||
*result = a + b;
|
||||
return b > (unsigned int) -1 - a;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Sort and search.
|
||||
@@ -1664,6 +1688,13 @@ double solve_itp (func_t f,
|
||||
double min_y, double max_y,
|
||||
double &ya, double &yb, double &y)
|
||||
{
|
||||
// Guard against degenerate interval
|
||||
if (b - a <= 0.0)
|
||||
{
|
||||
y = ya;
|
||||
return a;
|
||||
}
|
||||
|
||||
unsigned n1_2 = (unsigned) (hb_max (ceil (log2 ((b - a) / epsilon)) - 1.0, 0.0));
|
||||
const unsigned n0 = 1; // Hardwired
|
||||
const double k1 = 0.2 / (b - a); // Hardwired.
|
||||
@@ -1674,7 +1705,8 @@ double solve_itp (func_t f,
|
||||
{
|
||||
double x1_2 = 0.5 * (a + b);
|
||||
double r = scaled_epsilon - 0.5 * (b - a);
|
||||
double xf = (yb * a - ya * b) / (yb - ya);
|
||||
// Guard against yb == ya to prevent division by zero
|
||||
double xf = (yb != ya) ? (yb * a - ya * b) / (yb - ya) : x1_2;
|
||||
double sigma = x1_2 - xf;
|
||||
double b_a = b - a;
|
||||
// This has k2 = 2 hardwired for efficiency.
|
||||
|
||||
Vendored
+3
@@ -193,6 +193,9 @@ struct hb_atomic_t<T *>
|
||||
T *get_acquire () const { return v.load (std::memory_order_acquire); }
|
||||
bool cmpexch (T *old, T *new_) { return v.compare_exchange_weak (old, new_, std::memory_order_acq_rel, std::memory_order_relaxed); }
|
||||
|
||||
hb_atomic_t &operator= (const hb_atomic_t& o) { set_relaxed (o.get_relaxed ()); return *this; }
|
||||
hb_atomic_t &operator= (hb_atomic_t&& o){ set_relaxed (o.get_relaxed ()); o.set_relaxed ({}); return *this; }
|
||||
|
||||
operator bool () const { return get_acquire () != nullptr; }
|
||||
T *operator->() const { return get_acquire (); }
|
||||
template <typename C>
|
||||
|
||||
+8
-1
@@ -173,7 +173,14 @@ struct hb_bit_set_invertible_t
|
||||
bool is_subset (const hb_bit_set_invertible_t &larger_set) const
|
||||
{
|
||||
if (unlikely (inverted != larger_set.inverted))
|
||||
return hb_all (hb_iter (s) | hb_map (larger_set.s));
|
||||
{
|
||||
if (inverted)
|
||||
return hb_all (iter (), larger_set.s);
|
||||
else
|
||||
// larger set is inverted so larger_set.s is the set of things that are not present
|
||||
// in larger_set, therefore if s has any of those it can't be a subset.
|
||||
return !s.intersects (larger_set.s);
|
||||
}
|
||||
else
|
||||
return unlikely (inverted) ? larger_set.s.is_subset (s) : s.is_subset (larger_set.s);
|
||||
}
|
||||
|
||||
+13
-5
@@ -438,23 +438,31 @@ struct hb_bit_set_t
|
||||
return false;
|
||||
|
||||
uint32_t spi = 0;
|
||||
for (uint32_t lpi = 0; spi < page_map.length && lpi < larger_set.page_map.length; lpi++)
|
||||
uint32_t lpi = 0;
|
||||
while (spi < page_map.length && lpi < larger_set.page_map.length)
|
||||
{
|
||||
uint32_t spm = page_map.arrayZ[spi].major;
|
||||
uint32_t lpm = larger_set.page_map.arrayZ[lpi].major;
|
||||
auto sp = page_at (spi);
|
||||
|
||||
if (spm < lpm && !sp.is_empty ())
|
||||
return false;
|
||||
|
||||
if (lpm < spm)
|
||||
if (spm < lpm) {
|
||||
if (!sp.is_empty ())
|
||||
return false;
|
||||
spi++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (lpm < spm) {
|
||||
lpi++;
|
||||
continue;
|
||||
}
|
||||
|
||||
auto lp = larger_set.page_at (lpi);
|
||||
if (!sp.is_subset (lp))
|
||||
return false;
|
||||
|
||||
spi++;
|
||||
lpi++;
|
||||
}
|
||||
|
||||
while (spi < page_map.length)
|
||||
|
||||
Vendored
+14
@@ -50,6 +50,20 @@ struct hb_blob_t
|
||||
}
|
||||
}
|
||||
|
||||
void replace_buffer (const char *new_data,
|
||||
unsigned new_length,
|
||||
hb_memory_mode_t new_mode,
|
||||
void *new_user_data,
|
||||
hb_destroy_func_t new_destroy)
|
||||
{
|
||||
destroy_user_data ();
|
||||
data = new_data;
|
||||
length = new_length;
|
||||
mode = new_mode;
|
||||
user_data = new_user_data;
|
||||
destroy = new_destroy;
|
||||
}
|
||||
|
||||
HB_INTERNAL bool try_make_writable ();
|
||||
HB_INTERNAL bool try_make_writable_inplace ();
|
||||
HB_INTERNAL bool try_make_writable_inplace_unix ();
|
||||
|
||||
+4
-4
@@ -32,7 +32,7 @@
|
||||
#include "hb.hh"
|
||||
|
||||
|
||||
#line 33 "hb-buffer-deserialize-json.hh"
|
||||
#line 36 "hb-buffer-deserialize-json.hh"
|
||||
static const unsigned char _deserialize_json_trans_keys[] = {
|
||||
0u, 0u, 9u, 34u, 97u, 121u, 120u, 121u, 34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u,
|
||||
9u, 125u, 9u, 125u, 9u, 93u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u,
|
||||
@@ -597,12 +597,12 @@ _hb_buffer_deserialize_json (hb_buffer_t *buffer,
|
||||
hb_glyph_info_t info = {0};
|
||||
hb_glyph_position_t pos = {0};
|
||||
|
||||
#line 594 "hb-buffer-deserialize-json.hh"
|
||||
#line 601 "hb-buffer-deserialize-json.hh"
|
||||
{
|
||||
cs = deserialize_json_start;
|
||||
}
|
||||
|
||||
#line 597 "hb-buffer-deserialize-json.hh"
|
||||
#line 606 "hb-buffer-deserialize-json.hh"
|
||||
{
|
||||
int _slen;
|
||||
int _trans;
|
||||
@@ -712,7 +712,7 @@ _resume:
|
||||
#line 56 "hb-buffer-deserialize-json.rl"
|
||||
{ if (unlikely (!buffer->ensure_unicode ())) return false; }
|
||||
break;
|
||||
#line 689 "hb-buffer-deserialize-json.hh"
|
||||
#line 716 "hb-buffer-deserialize-json.hh"
|
||||
}
|
||||
|
||||
_again:
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
#include "hb.hh"
|
||||
|
||||
|
||||
#line 33 "hb-buffer-deserialize-text-glyphs.hh"
|
||||
#line 36 "hb-buffer-deserialize-text-glyphs.hh"
|
||||
static const unsigned char _deserialize_text_glyphs_trans_keys[] = {
|
||||
0u, 0u, 35u, 124u, 48u, 57u, 60u, 124u, 45u, 57u, 48u, 57u, 44u, 44u, 45u, 57u,
|
||||
48u, 57u, 44u, 44u, 45u, 57u, 48u, 57u, 44u, 44u, 45u, 57u, 48u, 57u, 62u, 62u,
|
||||
@@ -389,12 +389,12 @@ _hb_buffer_deserialize_text_glyphs (hb_buffer_t *buffer,
|
||||
hb_glyph_info_t info = {0};
|
||||
hb_glyph_position_t pos = {0};
|
||||
|
||||
#line 386 "hb-buffer-deserialize-text-glyphs.hh"
|
||||
#line 393 "hb-buffer-deserialize-text-glyphs.hh"
|
||||
{
|
||||
cs = deserialize_text_glyphs_start;
|
||||
}
|
||||
|
||||
#line 389 "hb-buffer-deserialize-text-glyphs.hh"
|
||||
#line 398 "hb-buffer-deserialize-text-glyphs.hh"
|
||||
{
|
||||
int _slen;
|
||||
int _trans;
|
||||
@@ -552,7 +552,7 @@ _resume:
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
#line 523 "hb-buffer-deserialize-text-glyphs.hh"
|
||||
#line 556 "hb-buffer-deserialize-text-glyphs.hh"
|
||||
}
|
||||
|
||||
_again:
|
||||
@@ -573,7 +573,7 @@ _again:
|
||||
*end_ptr = p;
|
||||
}
|
||||
break;
|
||||
#line 542 "hb-buffer-deserialize-text-glyphs.hh"
|
||||
#line 577 "hb-buffer-deserialize-text-glyphs.hh"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
#include "hb.hh"
|
||||
|
||||
|
||||
#line 33 "hb-buffer-deserialize-text-unicode.hh"
|
||||
#line 36 "hb-buffer-deserialize-text-unicode.hh"
|
||||
static const unsigned char _deserialize_text_unicode_trans_keys[] = {
|
||||
0u, 0u, 43u, 102u, 48u, 102u, 48u, 124u, 48u, 57u, 62u, 124u, 48u, 124u, 60u, 117u,
|
||||
85u, 117u, 85u, 117u, 0
|
||||
@@ -150,12 +150,12 @@ _hb_buffer_deserialize_text_unicode (hb_buffer_t *buffer,
|
||||
hb_glyph_info_t info = {0};
|
||||
const hb_glyph_position_t pos = {0};
|
||||
|
||||
#line 147 "hb-buffer-deserialize-text-unicode.hh"
|
||||
#line 154 "hb-buffer-deserialize-text-unicode.hh"
|
||||
{
|
||||
cs = deserialize_text_unicode_start;
|
||||
}
|
||||
|
||||
#line 150 "hb-buffer-deserialize-text-unicode.hh"
|
||||
#line 159 "hb-buffer-deserialize-text-unicode.hh"
|
||||
{
|
||||
int _slen;
|
||||
int _trans;
|
||||
@@ -215,7 +215,7 @@ _resume:
|
||||
hb_memset (&info, 0, sizeof (info));
|
||||
}
|
||||
break;
|
||||
#line 203 "hb-buffer-deserialize-text-unicode.hh"
|
||||
#line 219 "hb-buffer-deserialize-text-unicode.hh"
|
||||
}
|
||||
|
||||
_again:
|
||||
@@ -238,7 +238,7 @@ _again:
|
||||
*end_ptr = p;
|
||||
}
|
||||
break;
|
||||
#line 224 "hb-buffer-deserialize-text-unicode.hh"
|
||||
#line 242 "hb-buffer-deserialize-text-unicode.hh"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+33
-11
@@ -114,6 +114,17 @@ _hb_buffer_serialize_glyphs_json (hb_buffer_t *buffer,
|
||||
|
||||
*buf_consumed = 0;
|
||||
hb_position_t x = 0, y = 0;
|
||||
|
||||
/* Calculate the advance of the previous glyphs */
|
||||
if (pos && (flags & HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES))
|
||||
{
|
||||
for (unsigned int i = 0; i < start; i++)
|
||||
{
|
||||
x += pos[i].x_advance;
|
||||
y += pos[i].y_advance;
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned int i = start; i < end; i++)
|
||||
{
|
||||
char b[1024];
|
||||
@@ -151,7 +162,7 @@ _hb_buffer_serialize_glyphs_json (hb_buffer_t *buffer,
|
||||
p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"cl\":%u", info[i].cluster));
|
||||
}
|
||||
|
||||
if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS))
|
||||
if (pos && !(flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS))
|
||||
{
|
||||
p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"dx\":%d,\"dy\":%d",
|
||||
x+pos[i].x_offset, y+pos[i].y_offset));
|
||||
@@ -272,6 +283,17 @@ _hb_buffer_serialize_glyphs_text (hb_buffer_t *buffer,
|
||||
|
||||
*buf_consumed = 0;
|
||||
hb_position_t x = 0, y = 0;
|
||||
|
||||
/* Calculate the advance of the previous glyphs */
|
||||
if (pos && (flags & HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES))
|
||||
{
|
||||
for (unsigned int i = 0; i < start; i++)
|
||||
{
|
||||
x += pos[i].x_advance;
|
||||
y += pos[i].y_advance;
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned int i = start; i < end; i++)
|
||||
{
|
||||
char b[1024];
|
||||
@@ -297,7 +319,7 @@ _hb_buffer_serialize_glyphs_text (hb_buffer_t *buffer,
|
||||
p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "=%u", info[i].cluster));
|
||||
}
|
||||
|
||||
if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS))
|
||||
if (pos && !(flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS))
|
||||
{
|
||||
if (x+pos[i].x_offset || y+pos[i].y_offset)
|
||||
p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "@%d,%d", x+pos[i].x_offset, y+pos[i].y_offset));
|
||||
@@ -417,9 +439,9 @@ _hb_buffer_serialize_unicode_text (hb_buffer_t *buffer,
|
||||
* A human-readable, plain text format.
|
||||
* The serialized glyphs will look something like:
|
||||
*
|
||||
* ```
|
||||
* |[<!-- language="plain" -->
|
||||
* [uni0651=0@518,0+0|uni0628=0+1897]
|
||||
* ```
|
||||
* ]|
|
||||
*
|
||||
* - The serialized glyphs are delimited with `[` and `]`.
|
||||
* - Glyphs are separated with `|`
|
||||
@@ -435,10 +457,10 @@ _hb_buffer_serialize_unicode_text (hb_buffer_t *buffer,
|
||||
* A machine-readable, structured format.
|
||||
* The serialized glyphs will look something like:
|
||||
*
|
||||
* ```
|
||||
* |[<!-- language="plain" -->
|
||||
* [{"g":"uni0651","cl":0,"dx":518,"dy":0,"ax":0,"ay":0},
|
||||
* {"g":"uni0628","cl":0,"dx":0,"dy":0,"ax":1897,"ay":0}]
|
||||
* ```
|
||||
* {"g":"uni0628","cl":0,"dx":0,"dy":0,"ax":1897,"ay":0}]
|
||||
* ]|
|
||||
*
|
||||
* Each glyph is a JSON object, with the following properties:
|
||||
* - `g`: the glyph name or glyph index if
|
||||
@@ -530,9 +552,9 @@ hb_buffer_serialize_glyphs (hb_buffer_t *buffer,
|
||||
* A human-readable, plain text format.
|
||||
* The serialized codepoints will look something like:
|
||||
*
|
||||
* ```
|
||||
* |[<!-- language="plain" -->
|
||||
* <U+0651=0|U+0628=1>
|
||||
* ```
|
||||
* ]|
|
||||
*
|
||||
* - Glyphs are separated with `|`
|
||||
* - Unicode codepoints are expressed as zero-padded four (or more)
|
||||
@@ -550,9 +572,9 @@ hb_buffer_serialize_glyphs (hb_buffer_t *buffer,
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
* ```
|
||||
* |[<!-- language="plain" -->
|
||||
* [{u:1617,cl:0},{u:1576,cl:1}]
|
||||
* ```
|
||||
* ]|
|
||||
*
|
||||
* Return value:
|
||||
* The number of serialized items.
|
||||
|
||||
Vendored
+40
-15
@@ -547,12 +547,6 @@ void
|
||||
hb_buffer_t::merge_clusters_impl (unsigned int start,
|
||||
unsigned int end)
|
||||
{
|
||||
if (!HB_BUFFER_CLUSTER_LEVEL_IS_MONOTONE (cluster_level))
|
||||
{
|
||||
unsafe_to_break (start, end);
|
||||
return;
|
||||
}
|
||||
|
||||
max_ops -= end - start;
|
||||
if (unlikely (max_ops < 0))
|
||||
successful = false;
|
||||
@@ -581,15 +575,9 @@ hb_buffer_t::merge_clusters_impl (unsigned int start,
|
||||
set_cluster (info[i], cluster);
|
||||
}
|
||||
void
|
||||
hb_buffer_t::merge_out_clusters (unsigned int start,
|
||||
unsigned int end)
|
||||
hb_buffer_t::merge_out_clusters_impl (unsigned int start,
|
||||
unsigned int end)
|
||||
{
|
||||
if (!HB_BUFFER_CLUSTER_LEVEL_IS_MONOTONE (cluster_level))
|
||||
return;
|
||||
|
||||
if (unlikely (end - start < 2))
|
||||
return;
|
||||
|
||||
max_ops -= end - start;
|
||||
if (unlikely (max_ops < 0))
|
||||
successful = false;
|
||||
@@ -972,6 +960,9 @@ void
|
||||
hb_buffer_set_content_type (hb_buffer_t *buffer,
|
||||
hb_buffer_content_type_t content_type)
|
||||
{
|
||||
if (unlikely (hb_object_is_immutable (buffer)))
|
||||
return;
|
||||
|
||||
buffer->content_type = content_type;
|
||||
}
|
||||
|
||||
@@ -2290,6 +2281,22 @@ hb_buffer_diff (hb_buffer_t *buffer,
|
||||
* Debugging.
|
||||
*/
|
||||
|
||||
void
|
||||
hb_buffer_t::changed ()
|
||||
{
|
||||
#ifdef HB_NO_BUFFER_MESSAGE
|
||||
return;
|
||||
#else
|
||||
if (!message_depth)
|
||||
return;
|
||||
|
||||
if (changed_func)
|
||||
changed_func (this, changed_data);
|
||||
else
|
||||
update_digest ();
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef HB_NO_BUFFER_MESSAGE
|
||||
/**
|
||||
* hb_buffer_set_message_func:
|
||||
@@ -2307,7 +2314,8 @@ hb_buffer_set_message_func (hb_buffer_t *buffer,
|
||||
hb_buffer_message_func_t func,
|
||||
void *user_data, hb_destroy_func_t destroy)
|
||||
{
|
||||
if (unlikely (hb_object_is_immutable (buffer)))
|
||||
if (unlikely (hb_object_is_immutable (buffer)) ||
|
||||
unlikely (buffer->message_depth))
|
||||
{
|
||||
if (destroy)
|
||||
destroy (user_data);
|
||||
@@ -2327,6 +2335,23 @@ hb_buffer_set_message_func (hb_buffer_t *buffer,
|
||||
buffer->message_destroy = nullptr;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* hb_buffer_changed:
|
||||
* @buffer: An #hb_buffer_t
|
||||
*
|
||||
* Called by a message callback after modifying buffer glyph indices,
|
||||
* to update internal caches.
|
||||
*
|
||||
* If not called from inside a message callback, does nothing.
|
||||
*
|
||||
* Since: 13.0.0
|
||||
**/
|
||||
void
|
||||
hb_buffer_changed (hb_buffer_t *buffer)
|
||||
{
|
||||
buffer->changed ();
|
||||
}
|
||||
|
||||
bool
|
||||
hb_buffer_t::message_impl (hb_font_t *font, const char *fmt, va_list ap)
|
||||
{
|
||||
|
||||
Vendored
+11
-3
@@ -495,7 +495,7 @@ typedef enum {
|
||||
(1u << HB_BUFFER_CLUSTER_LEVEL_GRAPHEMES))))
|
||||
|
||||
/**
|
||||
* HB_BUFFER_CLUSTER_LEVEL_IS_CHARACTERS
|
||||
* HB_BUFFER_CLUSTER_LEVEL_IS_CHARACTERS:
|
||||
* @level: #hb_buffer_cluster_level_t to test
|
||||
*
|
||||
* Tests whether a cluster level does not group cluster values by graphemes.
|
||||
@@ -505,7 +505,7 @@ typedef enum {
|
||||
*/
|
||||
#define HB_BUFFER_CLUSTER_LEVEL_IS_CHARACTERS(level) \
|
||||
((bool) ((1u << (unsigned) (level)) & \
|
||||
((1u << HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARCATERS) | \
|
||||
((1u << HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS) | \
|
||||
(1u << HB_BUFFER_CLUSTER_LEVEL_CHARACTERS))))
|
||||
|
||||
HB_EXTERN void
|
||||
@@ -672,7 +672,12 @@ hb_buffer_normalize_glyphs (hb_buffer_t *buffer);
|
||||
* @HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS: serialize glyph extents.
|
||||
* @HB_BUFFER_SERIALIZE_FLAG_GLYPH_FLAGS: serialize glyph flags. Since: 1.5.0
|
||||
* @HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES: do not serialize glyph advances,
|
||||
* glyph offsets will reflect absolute glyph positions. Since: 1.8.0
|
||||
* glyph offsets will reflect absolute glyph positions. Since: 1.8.0.
|
||||
* Note: when this flag is used with a partial range of the buffer (i.e.
|
||||
* @start is not 0), calculating the absolute positions has a cost
|
||||
* proportional to @start. If the buffer is serialized in many small
|
||||
* chunks, this can lead to quadratic behavior. It is recommended to
|
||||
* use a larger @buf_size to minimize this cost.
|
||||
* @HB_BUFFER_SERIALIZE_FLAG_DEFINED: All currently defined flags. Since: 4.4.0
|
||||
*
|
||||
* Flags that control what glyph information are serialized in hb_buffer_serialize_glyphs().
|
||||
@@ -865,6 +870,9 @@ hb_buffer_set_message_func (hb_buffer_t *buffer,
|
||||
hb_buffer_message_func_t func,
|
||||
void *user_data, hb_destroy_func_t destroy);
|
||||
|
||||
HB_EXTERN void
|
||||
hb_buffer_changed (hb_buffer_t *buffer);
|
||||
|
||||
|
||||
HB_END_DECLS
|
||||
|
||||
|
||||
Vendored
+38
-1
@@ -130,9 +130,13 @@ struct hb_buffer_t
|
||||
*/
|
||||
|
||||
#ifndef HB_NO_BUFFER_MESSAGE
|
||||
typedef void (*changed_func_t) (hb_buffer_t *buffer, void *user_data);
|
||||
|
||||
hb_buffer_message_func_t message_func;
|
||||
void *message_data;
|
||||
hb_destroy_func_t message_destroy;
|
||||
changed_func_t changed_func;
|
||||
void *changed_data;
|
||||
unsigned message_depth; /* How deeply are we inside a message callback? */
|
||||
#else
|
||||
static constexpr unsigned message_depth = 0u;
|
||||
@@ -398,10 +402,42 @@ struct hb_buffer_t
|
||||
{
|
||||
if (end - start < 2)
|
||||
return;
|
||||
if (!HB_BUFFER_CLUSTER_LEVEL_IS_MONOTONE (cluster_level))
|
||||
{
|
||||
unsafe_to_break (start, end);
|
||||
return;
|
||||
}
|
||||
merge_clusters_impl (start, end);
|
||||
}
|
||||
void merge_grapheme_clusters (unsigned int start, unsigned int end)
|
||||
{
|
||||
if (end - start < 2)
|
||||
return;
|
||||
if (!HB_BUFFER_CLUSTER_LEVEL_IS_GRAPHEMES (cluster_level))
|
||||
{
|
||||
unsafe_to_break (start, end);
|
||||
return;
|
||||
}
|
||||
merge_clusters_impl (start, end);
|
||||
}
|
||||
HB_INTERNAL void merge_clusters_impl (unsigned int start, unsigned int end);
|
||||
HB_INTERNAL void merge_out_clusters (unsigned int start, unsigned int end);
|
||||
void merge_out_clusters (unsigned int start, unsigned int end)
|
||||
{
|
||||
if (end - start < 2)
|
||||
return;
|
||||
if (!HB_BUFFER_CLUSTER_LEVEL_IS_MONOTONE (cluster_level))
|
||||
return;
|
||||
merge_out_clusters_impl (start, end);
|
||||
}
|
||||
void merge_out_grapheme_clusters (unsigned int start, unsigned int end)
|
||||
{
|
||||
if (end - start < 2)
|
||||
return;
|
||||
if (!HB_BUFFER_CLUSTER_LEVEL_IS_GRAPHEMES (cluster_level))
|
||||
return;
|
||||
merge_out_clusters_impl (start, end);
|
||||
}
|
||||
HB_INTERNAL void merge_out_clusters_impl (unsigned int start, unsigned int end);
|
||||
/* Merge clusters for deleting current glyph, and skip it. */
|
||||
HB_INTERNAL void delete_glyph ();
|
||||
HB_INTERNAL void delete_glyphs_inplace (bool (*filter) (const hb_glyph_info_t *info));
|
||||
@@ -605,6 +641,7 @@ struct hb_buffer_t
|
||||
#endif
|
||||
}
|
||||
HB_INTERNAL bool message_impl (hb_font_t *font, const char *fmt, va_list ap) HB_PRINTF_FUNC(3, 0);
|
||||
HB_INTERNAL void changed ();
|
||||
|
||||
static void
|
||||
set_cluster (hb_glyph_info_t &inf, unsigned int cluster, unsigned int mask = 0)
|
||||
|
||||
Vendored
+1
-2
@@ -602,10 +602,10 @@ hb_cairo_render_glyph (cairo_scaled_font_t *scaled_font,
|
||||
|
||||
hb_position_t x_scale, y_scale;
|
||||
hb_font_get_scale (font, &x_scale, &y_scale);
|
||||
|
||||
cairo_scale (cr,
|
||||
+1. / (x_scale ? x_scale : 1),
|
||||
-1. / (y_scale ? y_scale : 1));
|
||||
|
||||
if (hb_font_draw_glyph_or_fail (font, glyph, hb_cairo_draw_get_funcs (), cr))
|
||||
cairo_fill (cr);
|
||||
|
||||
@@ -882,7 +882,6 @@ hb_cairo_font_face_get_scale_factor (cairo_font_face_t *font_face)
|
||||
&hb_cairo_scale_factor_user_data_key);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* hb_cairo_glyphs_from_buffer:
|
||||
* @buffer: a #hb_buffer_t containing glyphs
|
||||
|
||||
+20
-21
@@ -344,25 +344,25 @@ struct cff_stack_t
|
||||
{
|
||||
ELEM& operator [] (unsigned int i)
|
||||
{
|
||||
if (unlikely (i >= count))
|
||||
if (unlikely (i >= length))
|
||||
{
|
||||
set_error ();
|
||||
return Crap (ELEM);
|
||||
}
|
||||
return elements[i];
|
||||
return arrayZ[i];
|
||||
}
|
||||
|
||||
void push (const ELEM &v)
|
||||
{
|
||||
if (likely (count < LIMIT))
|
||||
elements[count++] = v;
|
||||
if (likely (length < LIMIT))
|
||||
arrayZ[length++] = v;
|
||||
else
|
||||
set_error ();
|
||||
}
|
||||
ELEM &push ()
|
||||
{
|
||||
if (likely (count < LIMIT))
|
||||
return elements[count++];
|
||||
if (likely (length < LIMIT))
|
||||
return arrayZ[length++];
|
||||
else
|
||||
{
|
||||
set_error ();
|
||||
@@ -372,8 +372,8 @@ struct cff_stack_t
|
||||
|
||||
ELEM& pop ()
|
||||
{
|
||||
if (likely (count > 0))
|
||||
return elements[--count];
|
||||
if (likely (length > 0))
|
||||
return arrayZ[--length];
|
||||
else
|
||||
{
|
||||
set_error ();
|
||||
@@ -382,45 +382,44 @@ struct cff_stack_t
|
||||
}
|
||||
void pop (unsigned int n)
|
||||
{
|
||||
if (likely (count >= n))
|
||||
count -= n;
|
||||
if (likely (length >= n))
|
||||
length -= n;
|
||||
else
|
||||
set_error ();
|
||||
}
|
||||
|
||||
const ELEM& peek ()
|
||||
{
|
||||
if (unlikely (count == 0))
|
||||
if (unlikely (length == 0))
|
||||
{
|
||||
set_error ();
|
||||
return Null (ELEM);
|
||||
}
|
||||
return elements[count - 1];
|
||||
return arrayZ[length - 1];
|
||||
}
|
||||
|
||||
void unpop ()
|
||||
{
|
||||
if (likely (count < LIMIT))
|
||||
count++;
|
||||
if (likely (length < LIMIT))
|
||||
length++;
|
||||
else
|
||||
set_error ();
|
||||
}
|
||||
|
||||
void clear () { count = 0; }
|
||||
void clear () { length = 0; }
|
||||
|
||||
bool in_error () const { return (error); }
|
||||
void set_error () { error = true; }
|
||||
|
||||
unsigned int get_count () const { return count; }
|
||||
bool is_empty () const { return !count; }
|
||||
unsigned int get_count () const { return length; }
|
||||
bool is_empty () const { return !length; }
|
||||
|
||||
hb_array_t<const ELEM> sub_array (unsigned start, unsigned length) const
|
||||
{ return hb_array_t<const ELEM> (elements).sub_array (start, length); }
|
||||
{ return hb_array_t<const ELEM> (arrayZ).sub_array (start, length); }
|
||||
|
||||
private:
|
||||
bool error = false;
|
||||
unsigned int count = 0;
|
||||
ELEM elements[LIMIT];
|
||||
unsigned int length = 0;
|
||||
ELEM arrayZ[LIMIT];
|
||||
};
|
||||
|
||||
/* argument stack */
|
||||
|
||||
+273
@@ -0,0 +1,273 @@
|
||||
/*
|
||||
* CFF CharString Specializer
|
||||
*
|
||||
* Optimizes CharString bytecode by using specialized operators
|
||||
* (hlineto, vlineto, hhcurveto, etc.) to save bytes and respects
|
||||
* CFF1 stack limit (48 values).
|
||||
*
|
||||
* Based on fontTools.cffLib.specializer
|
||||
*/
|
||||
|
||||
#ifndef HB_CFF_SPECIALIZER_HH
|
||||
#define HB_CFF_SPECIALIZER_HH
|
||||
|
||||
#include "hb.hh"
|
||||
#include "hb-cff-interp-cs-common.hh"
|
||||
|
||||
namespace CFF {
|
||||
|
||||
/* CharString command representation - forward declared in hb-subset-cff-common.hh */
|
||||
|
||||
/* Check if a value is effectively zero */
|
||||
static inline bool
|
||||
is_zero (const number_t &n)
|
||||
{
|
||||
return n.to_int () == 0;
|
||||
}
|
||||
|
||||
/* Generalize CharString commands to canonical form
|
||||
*
|
||||
* Converts all operators to their general forms and breaks down
|
||||
* multi-segment operators into single segments. This ensures we
|
||||
* start from a clean baseline before specialization.
|
||||
*
|
||||
* Based on fontTools.cffLib.specializer.generalizeCommands
|
||||
*/
|
||||
static void
|
||||
generalize_commands (hb_vector_t<cs_command_t> &commands)
|
||||
{
|
||||
hb_vector_t<cs_command_t> result;
|
||||
result.alloc (commands.length * 2); /* Estimate: might expand */
|
||||
|
||||
for (unsigned i = 0; i < commands.length; i++)
|
||||
{
|
||||
auto &cmd = commands[i];
|
||||
|
||||
switch (cmd.op)
|
||||
{
|
||||
case OpCode_hmoveto:
|
||||
case OpCode_vmoveto:
|
||||
{
|
||||
/* Convert to rmoveto with explicit dx,dy */
|
||||
cs_command_t gen (OpCode_rmoveto);
|
||||
gen.args.alloc (2);
|
||||
|
||||
if (cmd.op == OpCode_hmoveto && cmd.args.length >= 1)
|
||||
{
|
||||
gen.args.push (cmd.args[0]); /* dx */
|
||||
number_t zero; zero.set_int (0);
|
||||
gen.args.push (zero); /* dy = 0 */
|
||||
}
|
||||
else if (cmd.op == OpCode_vmoveto && cmd.args.length >= 1)
|
||||
{
|
||||
number_t zero; zero.set_int (0);
|
||||
gen.args.push (zero); /* dx = 0 */
|
||||
gen.args.push (cmd.args[0]); /* dy */
|
||||
}
|
||||
result.push (gen);
|
||||
break;
|
||||
}
|
||||
|
||||
case OpCode_hlineto:
|
||||
case OpCode_vlineto:
|
||||
{
|
||||
/* Convert h/v lineto to rlineto, breaking into single segments
|
||||
* hlineto alternates: dx1 (→ dx1,0) dy1 (→ 0,dy1) dx2 (→ dx2,0) ...
|
||||
* vlineto alternates: dy1 (→ 0,dy1) dx1 (→ dx1,0) dy2 (→ 0,dy2) ... */
|
||||
bool is_h = (cmd.op == OpCode_hlineto);
|
||||
number_t zero; zero.set_int (0);
|
||||
|
||||
for (unsigned j = 0; j < cmd.args.length; j++)
|
||||
{
|
||||
cs_command_t seg (OpCode_rlineto);
|
||||
seg.args.alloc (2);
|
||||
|
||||
bool is_horizontal = is_h ? (j % 2 == 0) : (j % 2 == 1);
|
||||
if (is_horizontal)
|
||||
{
|
||||
seg.args.push (cmd.args[j]); /* dx */
|
||||
seg.args.push (zero); /* dy = 0 */
|
||||
}
|
||||
else
|
||||
{
|
||||
seg.args.push (zero); /* dx = 0 */
|
||||
seg.args.push (cmd.args[j]); /* dy */
|
||||
}
|
||||
result.push (seg);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case OpCode_rlineto:
|
||||
{
|
||||
/* Break into single segments (dx,dy pairs) */
|
||||
for (unsigned j = 0; j + 1 < cmd.args.length; j += 2)
|
||||
{
|
||||
cs_command_t seg (OpCode_rlineto);
|
||||
seg.args.alloc (2);
|
||||
seg.args.push (cmd.args[j]);
|
||||
seg.args.push (cmd.args[j + 1]);
|
||||
result.push (seg);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case OpCode_rrcurveto:
|
||||
{
|
||||
/* Break into single segments (6 args each) */
|
||||
for (unsigned j = 0; j + 5 < cmd.args.length; j += 6)
|
||||
{
|
||||
cs_command_t seg (OpCode_rrcurveto);
|
||||
seg.args.alloc (6);
|
||||
for (unsigned k = 0; k < 6; k++)
|
||||
seg.args.push (cmd.args[j + k]);
|
||||
result.push (seg);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
/* Keep other operators as-is */
|
||||
result.push (cmd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Replace commands with generalized result */
|
||||
commands.resize (0);
|
||||
for (unsigned i = 0; i < result.length; i++)
|
||||
commands.push (result[i]);
|
||||
}
|
||||
|
||||
/* Specialize CharString commands to optimize bytecode size
|
||||
*
|
||||
* Follows fontTools approach:
|
||||
* 0. Generalize: Break down to canonical single-segment form
|
||||
* 1. Specialize: Convert rmoveto/rlineto to h/v variants when dx or dy is zero
|
||||
* 2. Combine: Merge adjacent compatible operators
|
||||
* 3. Enforce: Respect maxstack limit (default 48 for CFF1)
|
||||
*
|
||||
* This ensures we never exceed stack depth while optimizing bytecode.
|
||||
*/
|
||||
static void
|
||||
specialize_commands (hb_vector_t<cs_command_t> &commands,
|
||||
unsigned maxstack = 48)
|
||||
{
|
||||
if (commands.length == 0) return;
|
||||
|
||||
/* Pass 0: Generalize to canonical form (fontTools does this first) */
|
||||
generalize_commands (commands);
|
||||
|
||||
/* Pass 1: Specialize rmoveto/rlineto into h/v variants */
|
||||
for (unsigned i = 0; i < commands.length; i++)
|
||||
{
|
||||
auto &cmd = commands[i];
|
||||
|
||||
if ((cmd.op == OpCode_rmoveto || cmd.op == OpCode_rlineto) &&
|
||||
cmd.args.length == 2)
|
||||
{
|
||||
bool dx_zero = is_zero (cmd.args[0]);
|
||||
bool dy_zero = is_zero (cmd.args[1]);
|
||||
|
||||
if (dx_zero && !dy_zero)
|
||||
{
|
||||
/* Vertical movement (dx=0): keep only dy */
|
||||
cmd.op = (cmd.op == OpCode_rmoveto) ? OpCode_vmoveto : OpCode_vlineto;
|
||||
/* Shift dy to position 0 */
|
||||
cmd.args[0] = cmd.args[1];
|
||||
cmd.args.resize (1);
|
||||
}
|
||||
else if (!dx_zero && dy_zero)
|
||||
{
|
||||
/* Horizontal movement (dy=0): keep only dx */
|
||||
cmd.op = (cmd.op == OpCode_rmoveto) ? OpCode_hmoveto : OpCode_hlineto;
|
||||
cmd.args.resize (1); /* Keep only dx */
|
||||
}
|
||||
/* else: both zero or both non-zero, keep as rmoveto/rlineto */
|
||||
}
|
||||
}
|
||||
|
||||
/* Pass 2: Combine adjacent hlineto/vlineto operators
|
||||
* hlineto can take multiple args alternating with vlineto
|
||||
* This saves operator bytes */
|
||||
for (int i = (int)commands.length - 1; i > 0; i--)
|
||||
{
|
||||
auto &cmd = commands[i];
|
||||
auto &prev = commands[i-1];
|
||||
|
||||
/* Combine adjacent hlineto + vlineto or vlineto + hlineto */
|
||||
if ((prev.op == OpCode_hlineto && cmd.op == OpCode_vlineto) ||
|
||||
(prev.op == OpCode_vlineto && cmd.op == OpCode_hlineto))
|
||||
{
|
||||
/* Check stack depth */
|
||||
unsigned combined_args = prev.args.length + cmd.args.length;
|
||||
if (combined_args < maxstack)
|
||||
{
|
||||
/* Merge into first command, keep its operator */
|
||||
for (unsigned j = 0; j < cmd.args.length; j++)
|
||||
prev.args.push (cmd.args[j]);
|
||||
commands.remove_ordered (i);
|
||||
i++; /* Adjust for removed element */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Pass 3: Combine adjacent identical operators */
|
||||
for (int i = (int)commands.length - 1; i > 0; i--)
|
||||
{
|
||||
auto &cmd = commands[i];
|
||||
auto &prev = commands[i-1];
|
||||
|
||||
/* Combine same operators (e.g., rlineto + rlineto) */
|
||||
if (prev.op == cmd.op &&
|
||||
(cmd.op == OpCode_rlineto || cmd.op == OpCode_hlineto ||
|
||||
cmd.op == OpCode_vlineto || cmd.op == OpCode_rrcurveto))
|
||||
{
|
||||
/* Check stack depth */
|
||||
unsigned combined_args = prev.args.length + cmd.args.length;
|
||||
if (combined_args < maxstack)
|
||||
{
|
||||
/* Merge args */
|
||||
for (unsigned j = 0; j < cmd.args.length; j++)
|
||||
prev.args.push (cmd.args[j]);
|
||||
commands.remove_ordered (i);
|
||||
i++; /* Adjust for removed element */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Encode commands back to binary CharString */
|
||||
static bool
|
||||
encode_commands (const hb_vector_t<cs_command_t> &commands,
|
||||
str_buff_t &output)
|
||||
{
|
||||
for (const auto &cmd : commands)
|
||||
{
|
||||
str_encoder_t encoder (output);
|
||||
|
||||
/* Encode arguments */
|
||||
for (const auto &arg : cmd.args)
|
||||
encoder.encode_num_cs (arg);
|
||||
|
||||
/* Encode operator */
|
||||
if (cmd.op != OpCode_Invalid)
|
||||
encoder.encode_op (cmd.op);
|
||||
|
||||
/* hintmask/cntrmask are followed by raw mask bytes. */
|
||||
if (cmd.op == OpCode_hintmask || cmd.op == OpCode_cntrmask)
|
||||
{
|
||||
for (const auto &byte : cmd.mask_bytes)
|
||||
encoder.encode_byte (byte);
|
||||
}
|
||||
|
||||
if (encoder.in_error ())
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} /* namespace CFF */
|
||||
|
||||
#endif /* HB_CFF_SPECIALIZER_HH */
|
||||
+207
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
* CFF Width Optimizer
|
||||
*
|
||||
* Determines optimal defaultWidthX and nominalWidthX values
|
||||
* to minimize CharString byte cost.
|
||||
*
|
||||
* Based on fontTools.cffLib.width
|
||||
*/
|
||||
|
||||
#ifndef HB_CFF_WIDTH_OPTIMIZER_HH
|
||||
#define HB_CFF_WIDTH_OPTIMIZER_HH
|
||||
|
||||
#include "hb.hh"
|
||||
|
||||
namespace CFF {
|
||||
|
||||
/* Calculate byte cost for encoding a width delta */
|
||||
static inline unsigned
|
||||
width_delta_cost (int delta)
|
||||
{
|
||||
delta = abs (delta);
|
||||
if (delta <= 107) return 1;
|
||||
if (delta <= 1131) return 2;
|
||||
return 5;
|
||||
}
|
||||
|
||||
/* Cumulative sum forward */
|
||||
static void
|
||||
cumsum_forward (const hb_hashmap_t<unsigned, unsigned> &freq,
|
||||
unsigned min_w, unsigned max_w,
|
||||
hb_vector_t<unsigned> &cumsum)
|
||||
{
|
||||
cumsum.resize (max_w - min_w + 1);
|
||||
unsigned v = 0;
|
||||
for (unsigned x = min_w; x <= max_w; x++)
|
||||
{
|
||||
v += freq.get (x);
|
||||
cumsum[x - min_w] = v;
|
||||
}
|
||||
}
|
||||
|
||||
/* Cumulative max forward */
|
||||
static void
|
||||
cummax_forward (const hb_hashmap_t<unsigned, unsigned> &freq,
|
||||
unsigned min_w, unsigned max_w,
|
||||
hb_vector_t<unsigned> &cummax)
|
||||
{
|
||||
cummax.resize (max_w - min_w + 1);
|
||||
unsigned v = 0;
|
||||
for (unsigned x = min_w; x <= max_w; x++)
|
||||
{
|
||||
v = hb_max (v, freq.get (x));
|
||||
cummax[x - min_w] = v;
|
||||
}
|
||||
}
|
||||
|
||||
/* Cumulative sum backward */
|
||||
static void
|
||||
cumsum_backward (const hb_hashmap_t<unsigned, unsigned> &freq,
|
||||
unsigned min_w, unsigned max_w,
|
||||
hb_vector_t<unsigned> &cumsum)
|
||||
{
|
||||
cumsum.resize (max_w - min_w + 1);
|
||||
unsigned v = 0;
|
||||
for (int x = (int) max_w; x >= (int) min_w; x--)
|
||||
{
|
||||
v += freq.get ((unsigned) x);
|
||||
cumsum[x - min_w] = v;
|
||||
}
|
||||
}
|
||||
|
||||
/* Cumulative max backward */
|
||||
static void
|
||||
cummax_backward (const hb_hashmap_t<unsigned, unsigned> &freq,
|
||||
unsigned min_w, unsigned max_w,
|
||||
hb_vector_t<unsigned> &cummax)
|
||||
{
|
||||
cummax.resize (max_w - min_w + 1);
|
||||
unsigned v = 0;
|
||||
for (int x = (int) max_w; x >= (int) min_w; x--)
|
||||
{
|
||||
v = hb_max (v, freq.get ((unsigned) x));
|
||||
cummax[x - min_w] = v;
|
||||
}
|
||||
}
|
||||
|
||||
/* Helper to safely get cumulative value with bounds checking */
|
||||
static inline unsigned
|
||||
safe_get (const hb_vector_t<unsigned> &vec, int x, unsigned min_w, unsigned max_w)
|
||||
{
|
||||
if (x < (int) min_w || x > (int) max_w) return 0;
|
||||
return vec[x - min_w];
|
||||
}
|
||||
|
||||
/* Optimize defaultWidthX and nominalWidthX from a list of widths
|
||||
* O(UPEM+numGlyphs) algorithm from fontTools.cffLib.width */
|
||||
static void
|
||||
optimize_widths (const hb_vector_t<unsigned> &width_list,
|
||||
unsigned &default_width,
|
||||
unsigned &nominal_width)
|
||||
{
|
||||
if (width_list.length == 0)
|
||||
{
|
||||
default_width = nominal_width = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Build frequency map */
|
||||
hb_hashmap_t<unsigned, unsigned> widths;
|
||||
unsigned min_w = width_list[0];
|
||||
unsigned max_w = width_list[0];
|
||||
|
||||
for (unsigned w : width_list)
|
||||
{
|
||||
widths.set (w, widths.get (w) + 1);
|
||||
min_w = hb_min (min_w, w);
|
||||
max_w = hb_max (max_w, w);
|
||||
}
|
||||
|
||||
/* Cumulative sum/max forward/backward */
|
||||
hb_vector_t<unsigned> cumFrqU, cumMaxU, cumFrqD, cumMaxD;
|
||||
cumsum_forward (widths, min_w, max_w, cumFrqU);
|
||||
cummax_forward (widths, min_w, max_w, cumMaxU);
|
||||
cumsum_backward (widths, min_w, max_w, cumFrqD);
|
||||
cummax_backward (widths, min_w, max_w, cumMaxD);
|
||||
|
||||
/* Cost per nominal choice, without default consideration */
|
||||
auto nomnCost = [&] (unsigned x) -> unsigned {
|
||||
return safe_get (cumFrqU, x, min_w, max_w) +
|
||||
safe_get (cumFrqU, x - 108, min_w, max_w) +
|
||||
safe_get (cumFrqU, x - 1132, min_w, max_w) * 3 +
|
||||
safe_get (cumFrqD, x, min_w, max_w) +
|
||||
safe_get (cumFrqD, x + 108, min_w, max_w) +
|
||||
safe_get (cumFrqD, x + 1132, min_w, max_w) * 3 -
|
||||
widths.get (x);
|
||||
};
|
||||
|
||||
/* Cost-saving per nominal choice, by best default choice */
|
||||
auto dfltCost = [&] (unsigned x) -> unsigned {
|
||||
unsigned u = hb_max (hb_max (safe_get (cumMaxU, x, min_w, max_w),
|
||||
safe_get (cumMaxU, x - 108, min_w, max_w) * 2),
|
||||
safe_get (cumMaxU, x - 1132, min_w, max_w) * 5);
|
||||
unsigned d = hb_max (hb_max (safe_get (cumMaxD, x, min_w, max_w),
|
||||
safe_get (cumMaxD, x + 108, min_w, max_w) * 2),
|
||||
safe_get (cumMaxD, x + 1132, min_w, max_w) * 5);
|
||||
return hb_max (u, d);
|
||||
};
|
||||
|
||||
/* Find best nominal */
|
||||
unsigned best_nominal = min_w;
|
||||
unsigned best_cost = nomnCost (min_w) - dfltCost (min_w);
|
||||
|
||||
for (unsigned x = min_w + 1; x <= max_w; x++)
|
||||
{
|
||||
unsigned cost = nomnCost (x) - dfltCost (x);
|
||||
if (cost < best_cost)
|
||||
{
|
||||
best_cost = cost;
|
||||
best_nominal = x;
|
||||
}
|
||||
}
|
||||
|
||||
/* Work back the best default */
|
||||
unsigned best_default = best_nominal;
|
||||
unsigned best_default_cost = (unsigned) -1;
|
||||
|
||||
/* Check candidates around best_nominal */
|
||||
int candidates[] = {
|
||||
(int) best_nominal,
|
||||
(int) best_nominal - 108,
|
||||
(int) best_nominal - 1132,
|
||||
(int) best_nominal + 108,
|
||||
(int) best_nominal + 1132
|
||||
};
|
||||
|
||||
for (int candidate : candidates)
|
||||
{
|
||||
if (candidate < (int) min_w || candidate > (int) max_w)
|
||||
continue;
|
||||
|
||||
/* Compute actual cost with this default */
|
||||
unsigned cost = 0;
|
||||
for (auto kv : widths.iter ())
|
||||
{
|
||||
unsigned w = kv.first;
|
||||
unsigned freq = kv.second;
|
||||
|
||||
if (w == (unsigned) candidate)
|
||||
continue;
|
||||
|
||||
cost += freq * width_delta_cost ((int) w - (int) best_nominal);
|
||||
}
|
||||
|
||||
if (cost < best_default_cost)
|
||||
{
|
||||
best_default_cost = cost;
|
||||
best_default = (unsigned) candidate;
|
||||
}
|
||||
}
|
||||
|
||||
default_width = best_default;
|
||||
nominal_width = best_nominal;
|
||||
}
|
||||
|
||||
} /* namespace CFF */
|
||||
|
||||
#endif /* HB_CFF_WIDTH_OPTIMIZER_HH */
|
||||
+1
-1
@@ -291,7 +291,7 @@ struct cff2_cs_opset_t : cs_opset_t<ELEM, OPSET, cff2_cs_interp_env_t<ELEM>, PAR
|
||||
for (unsigned int i = 0; i < n; i++)
|
||||
{
|
||||
const hb_array_t<const ELEM> blends = env.argStack.sub_array (start + n + (i * k), k);
|
||||
process_arg_blend (env, env.argStack[start + i], blends, n, i);
|
||||
process_arg_blend (env, env.argStack.arrayZ[start + i], blends, n, i);
|
||||
}
|
||||
|
||||
/* pop off blend values leaving default values now adorned with blend values */
|
||||
|
||||
Vendored
+1
@@ -43,6 +43,7 @@
|
||||
#ifdef HB_TINY
|
||||
#define HB_LEAN
|
||||
#define HB_MINI
|
||||
#define HB_NO_SVG
|
||||
#define HB_OPTIMIZE_SIZE
|
||||
#define HB_OPTIMIZE_SIZE_MORE
|
||||
#define HB_MINIMIZE_MEMORY_USAGE
|
||||
|
||||
+1
-1
@@ -199,7 +199,7 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan,
|
||||
hb_glyph_info_t *info = buffer->info;
|
||||
for (unsigned int i = 1; i < count; i++)
|
||||
if (HB_UNICODE_GENERAL_CATEGORY_IS_MARK (unicode->general_category (info[i].codepoint)))
|
||||
buffer->merge_clusters (i - 1, i + 1);
|
||||
buffer->merge_grapheme_clusters (i - 1, i + 1);
|
||||
}
|
||||
|
||||
hb_vector_t<range_record_t> range_records;
|
||||
|
||||
+34
-30
@@ -138,52 +138,56 @@ struct vtable_t
|
||||
static constexpr auto get_user_data = _get_user_data;
|
||||
};
|
||||
|
||||
#define HB_DEFINE_VTABLE(name) \
|
||||
#define HB_DEFINE_VTABLE(name, empty) \
|
||||
template<> \
|
||||
struct vtable<hb_##name##_t> \
|
||||
: vtable_t<hb_##name##_t, \
|
||||
&hb_##name##_get_empty, \
|
||||
empty, \
|
||||
&hb_##name##_reference, \
|
||||
&hb_##name##_destroy, \
|
||||
&hb_##name##_set_user_data, \
|
||||
&hb_##name##_get_user_data> {}
|
||||
|
||||
HB_DEFINE_VTABLE (buffer);
|
||||
HB_DEFINE_VTABLE (blob);
|
||||
HB_DEFINE_VTABLE (face);
|
||||
HB_DEFINE_VTABLE (font);
|
||||
HB_DEFINE_VTABLE (font_funcs);
|
||||
HB_DEFINE_VTABLE (map);
|
||||
HB_DEFINE_VTABLE (set);
|
||||
HB_DEFINE_VTABLE (shape_plan);
|
||||
HB_DEFINE_VTABLE (unicode_funcs);
|
||||
HB_DEFINE_VTABLE (draw_funcs);
|
||||
HB_DEFINE_VTABLE (paint_funcs);
|
||||
|
||||
#undef HB_DEFINE_VTABLE
|
||||
HB_DEFINE_VTABLE (buffer, &hb_buffer_get_empty);
|
||||
HB_DEFINE_VTABLE (blob, &hb_blob_get_empty);
|
||||
HB_DEFINE_VTABLE (face, &hb_face_get_empty);
|
||||
HB_DEFINE_VTABLE (font, &hb_font_get_empty);
|
||||
HB_DEFINE_VTABLE (font_funcs, &hb_font_funcs_get_empty);
|
||||
HB_DEFINE_VTABLE (map, &hb_map_get_empty);
|
||||
HB_DEFINE_VTABLE (set, &hb_set_get_empty);
|
||||
HB_DEFINE_VTABLE (shape_plan, &hb_shape_plan_get_empty);
|
||||
HB_DEFINE_VTABLE (unicode_funcs, &hb_unicode_funcs_get_empty);
|
||||
HB_DEFINE_VTABLE (draw_funcs, &hb_draw_funcs_get_empty);
|
||||
HB_DEFINE_VTABLE (paint_funcs, &hb_paint_funcs_get_empty);
|
||||
|
||||
|
||||
#ifdef HB_SUBSET_H
|
||||
|
||||
#define HB_DEFINE_VTABLE(name) \
|
||||
template<> \
|
||||
struct vtable<hb_##name##_t> \
|
||||
: vtable_t<hb_##name##_t, \
|
||||
nullptr, \
|
||||
&hb_##name##_reference, \
|
||||
&hb_##name##_destroy, \
|
||||
&hb_##name##_set_user_data, \
|
||||
&hb_##name##_get_user_data> {}
|
||||
|
||||
|
||||
HB_DEFINE_VTABLE (subset_input);
|
||||
HB_DEFINE_VTABLE (subset_plan);
|
||||
|
||||
#undef HB_DEFINE_VTABLE
|
||||
HB_DEFINE_VTABLE (subset_input, nullptr);
|
||||
HB_DEFINE_VTABLE (subset_plan, nullptr);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef HB_RASTER_H
|
||||
|
||||
HB_DEFINE_VTABLE (raster_image, nullptr);
|
||||
HB_DEFINE_VTABLE (raster_draw, nullptr);
|
||||
HB_DEFINE_VTABLE (raster_paint, nullptr);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef HB_VECTOR_H
|
||||
|
||||
HB_DEFINE_VTABLE (vector_draw, nullptr);
|
||||
HB_DEFINE_VTABLE (vector_paint, nullptr);
|
||||
|
||||
#endif
|
||||
|
||||
#undef HB_DEFINE_VTABLE
|
||||
|
||||
|
||||
} // namespace hb
|
||||
|
||||
/* Workaround for GCC < 7, see:
|
||||
|
||||
+3
@@ -452,6 +452,7 @@ hb_directwrite_font_get_dw_font_face (hb_font_t *font)
|
||||
return (IDWriteFontFace *) (const void *) font->data.directwrite;
|
||||
}
|
||||
|
||||
#ifndef HB_DISABLE_DEPRECATED
|
||||
|
||||
/**
|
||||
* hb_directwrite_font_get_dw_font:
|
||||
@@ -471,3 +472,5 @@ hb_directwrite_font_get_dw_font (hb_font_t *font)
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
Vendored
+12
@@ -108,5 +108,17 @@
|
||||
#define HB_MAX_COMPOSITE_OPERATIONS_PER_GLYPH 64
|
||||
#endif
|
||||
|
||||
#ifndef HB_SVG_MAX_PATH_SEGMENTS
|
||||
#define HB_SVG_MAX_PATH_SEGMENTS 262144
|
||||
#endif
|
||||
|
||||
#ifndef HB_SVG_MAX_DOCUMENT_SIZE
|
||||
#define HB_SVG_MAX_DOCUMENT_SIZE ((size_t) 16 << 20)
|
||||
#endif
|
||||
|
||||
#ifndef HB_RASTER_MAX_BUFFER_SIZE
|
||||
#define HB_RASTER_MAX_BUFFER_SIZE ((size_t) 1 << 24)
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* HB_LIMITS_HH */
|
||||
|
||||
+4
-4
@@ -31,7 +31,7 @@
|
||||
#include "hb.hh"
|
||||
|
||||
|
||||
#line 32 "hb-number-parser.hh"
|
||||
#line 35 "hb-number-parser.hh"
|
||||
static const unsigned char _double_parser_trans_keys[] = {
|
||||
0u, 0u, 43u, 57u, 46u, 57u, 48u, 57u, 43u, 57u, 48u, 57u, 48u, 101u, 48u, 57u,
|
||||
46u, 101u, 0
|
||||
@@ -135,12 +135,12 @@ strtod_rl (const char *p, const char **end_ptr /* IN/OUT */)
|
||||
|
||||
int cs;
|
||||
|
||||
#line 132 "hb-number-parser.hh"
|
||||
#line 139 "hb-number-parser.hh"
|
||||
{
|
||||
cs = double_parser_start;
|
||||
}
|
||||
|
||||
#line 135 "hb-number-parser.hh"
|
||||
#line 144 "hb-number-parser.hh"
|
||||
{
|
||||
int _slen;
|
||||
int _trans;
|
||||
@@ -198,7 +198,7 @@ _resume:
|
||||
exp_overflow = true;
|
||||
}
|
||||
break;
|
||||
#line 187 "hb-number-parser.hh"
|
||||
#line 202 "hb-number-parser.hh"
|
||||
}
|
||||
|
||||
_again:
|
||||
|
||||
Vendored
+16
-1
@@ -272,6 +272,8 @@ static inline void hb_object_make_immutable (const Type *obj)
|
||||
obj->header.writable = false;
|
||||
}
|
||||
template <typename Type>
|
||||
static inline void hb_object_fini (Type *obj);
|
||||
template <typename Type>
|
||||
static inline Type *hb_object_reference (Type *obj)
|
||||
{
|
||||
hb_object_trace (obj, HB_FUNC);
|
||||
@@ -282,7 +284,7 @@ static inline Type *hb_object_reference (Type *obj)
|
||||
return obj;
|
||||
}
|
||||
template <typename Type>
|
||||
static inline bool hb_object_destroy (Type *obj)
|
||||
static inline bool hb_object_should_destroy (Type *obj)
|
||||
{
|
||||
hb_object_trace (obj, HB_FUNC);
|
||||
if (unlikely (!obj || obj->header.is_inert ()))
|
||||
@@ -290,12 +292,25 @@ static inline bool hb_object_destroy (Type *obj)
|
||||
assert (hb_object_is_valid (obj));
|
||||
if (obj->header.ref_count.dec () != 1)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
static inline void hb_object_actually_destroy (Type *obj)
|
||||
{
|
||||
hb_object_fini (obj);
|
||||
|
||||
if (!std::is_trivially_destructible<Type>::value)
|
||||
obj->~Type ();
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
static inline bool hb_object_destroy (Type *obj)
|
||||
{
|
||||
if (!hb_object_should_destroy (obj))
|
||||
return false;
|
||||
|
||||
hb_object_actually_destroy (obj);
|
||||
return true;
|
||||
}
|
||||
template <typename Type>
|
||||
|
||||
+13
-8
@@ -1275,11 +1275,22 @@ struct CFFIndex
|
||||
if (unlikely (!serialize_header (c, +it, data_size, min_off_size))) return_trace (false);
|
||||
unsigned char *ret = c->allocate_size<unsigned char> (data_size, false);
|
||||
if (unlikely (!ret)) return_trace (false);
|
||||
unsigned remaining = data_size;
|
||||
for (const auto &_ : +it)
|
||||
{
|
||||
unsigned len = _.length;
|
||||
|
||||
if (!len)
|
||||
continue;
|
||||
|
||||
if (unlikely (len > remaining)) {
|
||||
// We have more bytes to write then the computed data size, so the size calculation
|
||||
// must have encountered overflow.
|
||||
return_trace (c->check_success (false, HB_SERIALIZE_ERROR_INT_OVERFLOW));
|
||||
}
|
||||
|
||||
remaining -= len;
|
||||
|
||||
if (len <= 1)
|
||||
{
|
||||
*ret++ = *_.arrayZ;
|
||||
@@ -1948,6 +1959,7 @@ struct TupleValues
|
||||
return true;
|
||||
}
|
||||
|
||||
public:
|
||||
void skip (unsigned n)
|
||||
{
|
||||
while (n)
|
||||
@@ -1961,6 +1973,7 @@ struct TupleValues
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
template <bool scaled>
|
||||
void _add_to (hb_array_t<float> out, float scale = 1.0f)
|
||||
{
|
||||
@@ -2035,14 +2048,6 @@ struct TupleValues
|
||||
public:
|
||||
void add_to (hb_array_t<float> out, float scale = 1.0f)
|
||||
{
|
||||
unsigned n = out.length;
|
||||
|
||||
if (scale == 0.0f)
|
||||
{
|
||||
skip (n);
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef HB_OPTIMIZE_SIZE
|
||||
// The following branch is supposed to speed things up by avoiding
|
||||
// the multiplication in _add_to<> if scale is 1.0f.
|
||||
|
||||
+126
-44
@@ -237,8 +237,6 @@ struct CmapSubtableFormat0
|
||||
|
||||
struct CmapSubtableFormat4
|
||||
{
|
||||
|
||||
|
||||
template<typename Iterator,
|
||||
typename Writer,
|
||||
hb_requires (hb_is_iterator (Iterator))>
|
||||
@@ -521,9 +519,10 @@ struct CmapSubtableFormat4
|
||||
struct accelerator_t
|
||||
{
|
||||
accelerator_t () {}
|
||||
accelerator_t (const CmapSubtableFormat4 *subtable) { init (subtable); }
|
||||
accelerator_t (const CmapSubtableFormat4 *subtable) = delete;
|
||||
|
||||
void init (const CmapSubtableFormat4 *subtable)
|
||||
void init (const CmapSubtableFormat4 *subtable,
|
||||
unsigned int subtable_data_size)
|
||||
{
|
||||
segCount = subtable->segCountX2 / 2;
|
||||
endCount = subtable->values.arrayZ;
|
||||
@@ -531,7 +530,11 @@ struct CmapSubtableFormat4
|
||||
idDelta = startCount + segCount;
|
||||
idRangeOffset = idDelta + segCount;
|
||||
glyphIdArray = idRangeOffset + segCount;
|
||||
glyphIdArrayLength = (subtable->length - 16 - 8 * segCount) / 2;
|
||||
|
||||
unsigned int values_offset = 16 + 8 * segCount;
|
||||
glyphIdArrayLength = subtable_data_size > values_offset
|
||||
? (subtable_data_size - values_offset) / 2
|
||||
: 0;
|
||||
}
|
||||
|
||||
bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const
|
||||
@@ -671,23 +674,36 @@ struct CmapSubtableFormat4
|
||||
unsigned int glyphIdArrayLength;
|
||||
};
|
||||
|
||||
bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const
|
||||
bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph,
|
||||
unsigned int subtable_data_size) const
|
||||
{
|
||||
accelerator_t accel (this);
|
||||
accelerator_t accel;
|
||||
accel.init (this, subtable_data_size);
|
||||
return accel.get_glyph_func (&accel, codepoint, glyph);
|
||||
}
|
||||
void collect_unicodes (hb_set_t *out) const
|
||||
bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const
|
||||
{ return false; }
|
||||
|
||||
void collect_unicodes (hb_set_t *out, unsigned int subtable_data_size) const
|
||||
{
|
||||
accelerator_t accel (this);
|
||||
accelerator_t accel;
|
||||
accel.init (this, subtable_data_size);
|
||||
accel.collect_unicodes (out);
|
||||
}
|
||||
void collect_unicodes (hb_set_t *out) const
|
||||
{ collect_unicodes (out, length); }
|
||||
|
||||
void collect_mapping (hb_set_t *unicodes, /* OUT */
|
||||
hb_map_t *mapping /* OUT */) const
|
||||
hb_map_t *mapping, /* OUT */
|
||||
unsigned int subtable_data_size) const
|
||||
{
|
||||
accelerator_t accel (this);
|
||||
accelerator_t accel;
|
||||
accel.init (this, subtable_data_size);
|
||||
accel.collect_mapping (unicodes, mapping);
|
||||
}
|
||||
void collect_mapping (hb_set_t *unicodes, /* OUT */
|
||||
hb_map_t *mapping /* OUT */) const
|
||||
{ collect_mapping (unicodes, mapping, length); }
|
||||
|
||||
bool sanitize (hb_sanitize_context_t *c) const
|
||||
{
|
||||
@@ -696,14 +712,9 @@ struct CmapSubtableFormat4
|
||||
return_trace (false);
|
||||
hb_barrier ();
|
||||
|
||||
if (unlikely (!c->check_range (this, length)))
|
||||
return_trace (false);
|
||||
|
||||
return_trace (16 + 4 * (unsigned int) segCountX2 <= length);
|
||||
return_trace (c->check_range (values, 2 + 4 * segCountX2));
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected:
|
||||
HBUINT16 format; /* Format number is set to 4. */
|
||||
HBUINT16 length; /* This is the length in bytes of the
|
||||
@@ -1485,11 +1496,14 @@ struct CmapSubtable
|
||||
/* Note: We intentionally do NOT implement subtable formats 2 and 8. */
|
||||
|
||||
bool get_glyph (hb_codepoint_t codepoint,
|
||||
hb_codepoint_t *glyph) const
|
||||
hb_codepoint_t *glyph,
|
||||
unsigned int subtable_data_size = 0) const
|
||||
{
|
||||
switch (u.format.v) {
|
||||
case 0: hb_barrier (); return u.format0 .get_glyph (codepoint, glyph);
|
||||
case 4: hb_barrier (); return u.format4 .get_glyph (codepoint, glyph);
|
||||
case 4: hb_barrier (); return subtable_data_size
|
||||
? u.format4.get_glyph (codepoint, glyph, subtable_data_size)
|
||||
: false;
|
||||
case 6: hb_barrier (); return u.format6 .get_glyph (codepoint, glyph);
|
||||
case 10: hb_barrier (); return u.format10.get_glyph (codepoint, glyph);
|
||||
case 12: hb_barrier (); return u.format12.get_glyph (codepoint, glyph);
|
||||
@@ -1498,11 +1512,17 @@ struct CmapSubtable
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
void collect_unicodes (hb_set_t *out, unsigned int num_glyphs = UINT_MAX) const
|
||||
void collect_unicodes (hb_set_t *out,
|
||||
unsigned int num_glyphs = UINT_MAX,
|
||||
unsigned int subtable_data_size = 0) const
|
||||
{
|
||||
switch (u.format.v) {
|
||||
case 0: hb_barrier (); u.format0 .collect_unicodes (out); return;
|
||||
case 4: hb_barrier (); u.format4 .collect_unicodes (out); return;
|
||||
case 4: hb_barrier (); if (subtable_data_size)
|
||||
u.format4.collect_unicodes (out, subtable_data_size);
|
||||
else
|
||||
u.format4.collect_unicodes (out);
|
||||
return;
|
||||
case 6: hb_barrier (); u.format6 .collect_unicodes (out); return;
|
||||
case 10: hb_barrier (); u.format10.collect_unicodes (out); return;
|
||||
case 12: hb_barrier (); u.format12.collect_unicodes (out, num_glyphs); return;
|
||||
@@ -1514,11 +1534,16 @@ struct CmapSubtable
|
||||
|
||||
void collect_mapping (hb_set_t *unicodes, /* OUT */
|
||||
hb_map_t *mapping, /* OUT */
|
||||
unsigned num_glyphs = UINT_MAX) const
|
||||
unsigned num_glyphs = UINT_MAX,
|
||||
unsigned int subtable_data_size = 0) const
|
||||
{
|
||||
switch (u.format.v) {
|
||||
case 0: hb_barrier (); u.format0 .collect_mapping (unicodes, mapping); return;
|
||||
case 4: hb_barrier (); u.format4 .collect_mapping (unicodes, mapping); return;
|
||||
case 4: hb_barrier (); if (subtable_data_size)
|
||||
u.format4.collect_mapping (unicodes, mapping, subtable_data_size);
|
||||
else
|
||||
u.format4.collect_mapping (unicodes, mapping);
|
||||
return;
|
||||
case 6: hb_barrier (); u.format6 .collect_mapping (unicodes, mapping); return;
|
||||
case 10: hb_barrier (); u.format10.collect_mapping (unicodes, mapping); return;
|
||||
case 12: hb_barrier (); u.format12.collect_mapping (unicodes, mapping, num_glyphs); return;
|
||||
@@ -1633,7 +1658,7 @@ struct EncodingRecord
|
||||
CmapSubtable *cmapsubtable = c->push<CmapSubtable> ();
|
||||
unsigned origin_length = c->length ();
|
||||
cmapsubtable->serialize (c, it, format, plan, &(base+subtable));
|
||||
if (c->length () - origin_length > 0 && !c->in_error()) *objidx = c->pop_pack ();
|
||||
if (c->length () > origin_length && !c->in_error()) *objidx = c->pop_pack ();
|
||||
else c->pop_discard ();
|
||||
}
|
||||
|
||||
@@ -1662,6 +1687,7 @@ struct SubtableUnicodesCache {
|
||||
private:
|
||||
hb_blob_ptr_t<cmap> base_blob;
|
||||
const char* base;
|
||||
unsigned int table_length;
|
||||
hb_hashmap_t<unsigned, hb::unique_ptr<hb_set_t>> cached_unicodes;
|
||||
|
||||
public:
|
||||
@@ -1686,15 +1712,18 @@ struct SubtableUnicodesCache {
|
||||
hb_free (cache);
|
||||
}
|
||||
|
||||
SubtableUnicodesCache(const void* cmap_base)
|
||||
SubtableUnicodesCache(const void* cmap_base,
|
||||
unsigned int table_length_ = 0)
|
||||
: base_blob(),
|
||||
base ((const char*) cmap_base),
|
||||
table_length (table_length_),
|
||||
cached_unicodes ()
|
||||
{}
|
||||
|
||||
SubtableUnicodesCache(hb_blob_ptr_t<cmap> base_blob_)
|
||||
: base_blob(base_blob_),
|
||||
base ((const char *) base_blob.get()),
|
||||
table_length (base_blob.get_length ()),
|
||||
cached_unicodes ()
|
||||
{}
|
||||
|
||||
@@ -1725,7 +1754,10 @@ struct SubtableUnicodesCache {
|
||||
if (unlikely (s->in_error ()))
|
||||
return hb_set_get_empty ();
|
||||
|
||||
(base+record->subtable).collect_unicodes (s);
|
||||
unsigned int subtable_data_size = record->subtable < table_length
|
||||
? table_length - (unsigned int) record->subtable
|
||||
: 0;
|
||||
(base+record->subtable).collect_unicodes (s, UINT_MAX, subtable_data_size);
|
||||
|
||||
if (unlikely (!cached_unicodes.set ((unsigned) ((const char *) record - base), hb::unique_ptr<hb_set_t> {s})))
|
||||
return hb_set_get_empty ();
|
||||
@@ -1737,7 +1769,7 @@ struct SubtableUnicodesCache {
|
||||
|
||||
};
|
||||
|
||||
static inline uint_fast16_t
|
||||
static inline uint16_t
|
||||
_hb_symbol_pua_map (unsigned codepoint)
|
||||
{
|
||||
if (codepoint <= 0x00FFu)
|
||||
@@ -1784,7 +1816,8 @@ struct cmap
|
||||
EncodingRecIter encodingrec_iter,
|
||||
const void *base,
|
||||
hb_subset_plan_t *plan,
|
||||
bool drop_format_4 = false)
|
||||
bool drop_format_4 = false,
|
||||
unsigned int source_table_length = 0)
|
||||
{
|
||||
if (unlikely (!c->extend_min ((*this)))) return false;
|
||||
this->version = 0;
|
||||
@@ -1792,7 +1825,7 @@ struct cmap
|
||||
unsigned format4objidx = 0, format12objidx = 0, format14objidx = 0;
|
||||
auto snap = c->snapshot ();
|
||||
|
||||
SubtableUnicodesCache local_unicodes_cache (base);
|
||||
SubtableUnicodesCache local_unicodes_cache (base, source_table_length);
|
||||
const SubtableUnicodesCache* unicodes_cache = &local_unicodes_cache;
|
||||
|
||||
if (plan->accelerator &&
|
||||
@@ -1821,7 +1854,8 @@ struct cmap
|
||||
encodingrec_iter,
|
||||
base,
|
||||
plan,
|
||||
true);
|
||||
true,
|
||||
source_table_length);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1838,9 +1872,11 @@ struct cmap
|
||||
}
|
||||
else if (format == 14) c->copy (_, it, 14u, base, plan, &format14objidx);
|
||||
}
|
||||
c->check_assign(this->encodingRecord.len,
|
||||
(c->length () - cmap::min_size)/EncodingRecord::static_size,
|
||||
HB_SERIALIZE_ERROR_INT_OVERFLOW);
|
||||
unsigned length = c->length ();
|
||||
unsigned available = length > cmap::min_size ? length - cmap::min_size : 0;
|
||||
c->check_assign(this->encodingRecord.len,
|
||||
available / EncodingRecord::static_size,
|
||||
HB_SERIALIZE_ERROR_INT_OVERFLOW);
|
||||
|
||||
// Fail if format 4 was dropped and there is no cmap12.
|
||||
return !drop_format_4 || format12objidx;
|
||||
@@ -1955,7 +1991,9 @@ struct cmap
|
||||
it,
|
||||
encodingrec_iter,
|
||||
this,
|
||||
c->plan));
|
||||
c->plan,
|
||||
false,
|
||||
c->source_blob->length));
|
||||
}
|
||||
|
||||
const CmapSubtable *find_best_subtable (bool *symbol = nullptr,
|
||||
@@ -2034,32 +2072,60 @@ struct cmap
|
||||
|
||||
this->get_glyph_data = subtable;
|
||||
#ifndef HB_NO_CMAP_LEGACY_SUBTABLES
|
||||
bool is_format4 = subtable->u.format.v == 4;
|
||||
auto set_format4_getter = [this] (bool (*func) (const void *,
|
||||
hb_codepoint_t,
|
||||
hb_codepoint_t *))
|
||||
{
|
||||
this->format4_accel.init (&this->subtable->u.format4,
|
||||
get_subtable_data_size (this->subtable));
|
||||
this->get_glyph_data = &this->format4_accel;
|
||||
this->get_glyph_funcZ = func;
|
||||
};
|
||||
if (unlikely (symbol))
|
||||
{
|
||||
switch ((unsigned) face->table.OS2->get_font_page ()) {
|
||||
case OS2::font_page_t::FONT_PAGE_NONE:
|
||||
this->get_glyph_funcZ = get_glyph_from_symbol<CmapSubtable, _hb_symbol_pua_map>;
|
||||
if (is_format4)
|
||||
set_format4_getter (get_glyph_from_symbol<CmapSubtableFormat4::accelerator_t, _hb_symbol_pua_map>);
|
||||
else
|
||||
this->get_glyph_funcZ = get_glyph_from_symbol<CmapSubtable, _hb_symbol_pua_map>;
|
||||
break;
|
||||
#ifndef HB_NO_OT_SHAPER_ARABIC_FALLBACK
|
||||
case OS2::font_page_t::FONT_PAGE_SIMP_ARABIC:
|
||||
this->get_glyph_funcZ = get_glyph_from_symbol<CmapSubtable, _hb_arabic_pua_simp_map>;
|
||||
if (is_format4)
|
||||
set_format4_getter (get_glyph_from_symbol<CmapSubtableFormat4::accelerator_t, _hb_arabic_pua_simp_map>);
|
||||
else
|
||||
this->get_glyph_funcZ = get_glyph_from_symbol<CmapSubtable, _hb_arabic_pua_simp_map>;
|
||||
break;
|
||||
case OS2::font_page_t::FONT_PAGE_TRAD_ARABIC:
|
||||
this->get_glyph_funcZ = get_glyph_from_symbol<CmapSubtable, _hb_arabic_pua_trad_map>;
|
||||
if (is_format4)
|
||||
set_format4_getter (get_glyph_from_symbol<CmapSubtableFormat4::accelerator_t, _hb_arabic_pua_trad_map>);
|
||||
else
|
||||
this->get_glyph_funcZ = get_glyph_from_symbol<CmapSubtable, _hb_arabic_pua_trad_map>;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
this->get_glyph_funcZ = get_glyph_from<CmapSubtable>;
|
||||
if (is_format4)
|
||||
set_format4_getter (get_glyph_from<CmapSubtableFormat4::accelerator_t>);
|
||||
else
|
||||
this->get_glyph_funcZ = get_glyph_from<CmapSubtable>;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (unlikely (macroman))
|
||||
{
|
||||
this->get_glyph_funcZ = get_glyph_from_macroman<CmapSubtable>;
|
||||
if (is_format4)
|
||||
set_format4_getter (get_glyph_from_macroman<CmapSubtableFormat4::accelerator_t>);
|
||||
else
|
||||
this->get_glyph_funcZ = get_glyph_from_macroman<CmapSubtable>;
|
||||
}
|
||||
else if (unlikely (mac))
|
||||
{
|
||||
this->get_glyph_funcZ = get_glyph_from_ascii<CmapSubtable>;
|
||||
if (is_format4)
|
||||
set_format4_getter (get_glyph_from_ascii<CmapSubtableFormat4::accelerator_t>);
|
||||
else
|
||||
this->get_glyph_funcZ = get_glyph_from_ascii<CmapSubtable>;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
@@ -2074,7 +2140,8 @@ struct cmap
|
||||
break;
|
||||
case 4:
|
||||
{
|
||||
this->format4_accel.init (&subtable->u.format4);
|
||||
this->format4_accel.init (&subtable->u.format4,
|
||||
get_subtable_data_size (subtable));
|
||||
this->get_glyph_data = &this->format4_accel;
|
||||
this->get_glyph_funcZ = this->format4_accel.get_glyph_func;
|
||||
break;
|
||||
@@ -2155,10 +2222,10 @@ struct cmap
|
||||
}
|
||||
|
||||
void collect_unicodes (hb_set_t *out, unsigned int num_glyphs) const
|
||||
{ subtable->collect_unicodes (out, num_glyphs); }
|
||||
{ subtable->collect_unicodes (out, num_glyphs, get_subtable_data_size (subtable)); }
|
||||
void collect_mapping (hb_set_t *unicodes, hb_map_t *mapping,
|
||||
unsigned num_glyphs = UINT_MAX) const
|
||||
{ subtable->collect_mapping (unicodes, mapping, num_glyphs); }
|
||||
{ subtable->collect_mapping (unicodes, mapping, num_glyphs, get_subtable_data_size (subtable)); }
|
||||
void collect_variation_selectors (hb_set_t *out) const
|
||||
{ subtable_uvs->collect_variation_selectors (out); }
|
||||
void collect_variation_unicodes (hb_codepoint_t variation_selector,
|
||||
@@ -2169,7 +2236,7 @@ struct cmap
|
||||
typedef bool (*hb_cmap_get_glyph_func_t) (const void *obj,
|
||||
hb_codepoint_t codepoint,
|
||||
hb_codepoint_t *glyph);
|
||||
typedef uint_fast16_t (*hb_pua_remap_func_t) (unsigned);
|
||||
typedef uint16_t (*hb_pua_remap_func_t) (unsigned);
|
||||
|
||||
template <typename Type>
|
||||
HB_INTERNAL static bool get_glyph_from (const void *obj,
|
||||
@@ -2217,6 +2284,21 @@ struct cmap
|
||||
return c && typed_obj->get_glyph (c, glyph);
|
||||
}
|
||||
|
||||
unsigned int get_subtable_data_size (const CmapSubtable *subtable) const
|
||||
{
|
||||
unsigned int table_length = this->table.get_length ();
|
||||
uintptr_t table_start = (uintptr_t) (const void *) this->table.get ();
|
||||
uintptr_t subtable_addr = (uintptr_t) (const void *) subtable;
|
||||
if (unlikely (subtable_addr < table_start))
|
||||
return 0;
|
||||
|
||||
uintptr_t subtable_offset = subtable_addr - table_start;
|
||||
if (unlikely (subtable_offset >= table_length))
|
||||
return 0;
|
||||
|
||||
return table_length - (unsigned int) subtable_offset;
|
||||
}
|
||||
|
||||
private:
|
||||
hb_nonnull_ptr_t<const CmapSubtable> subtable;
|
||||
hb_nonnull_ptr_t<const CmapSubtableFormat14> subtable_uvs;
|
||||
|
||||
+68
@@ -35,7 +35,9 @@
|
||||
#include "OT/Color/COLR/COLR.hh"
|
||||
#include "OT/Color/CPAL/CPAL.hh"
|
||||
#include "OT/Color/sbix/sbix.hh"
|
||||
#ifndef HB_NO_SVG
|
||||
#include "OT/Color/svg/svg.hh"
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
@@ -274,6 +276,7 @@ hb_ot_color_glyph_get_layers (hb_face_t *face,
|
||||
* SVG
|
||||
*/
|
||||
|
||||
#ifndef HB_NO_SVG
|
||||
/**
|
||||
* hb_ot_color_has_svg:
|
||||
* @face: #hb_face_t to work upon.
|
||||
@@ -290,6 +293,70 @@ hb_ot_color_has_svg (hb_face_t *face)
|
||||
return face->table.SVG->has_data ();
|
||||
}
|
||||
|
||||
/**
|
||||
* hb_ot_color_get_svg_document_count:
|
||||
* @face: #hb_face_t to work upon.
|
||||
*
|
||||
* Gets the number of SVG documents in the face `SVG` table.
|
||||
*
|
||||
* Return value: number of SVG documents in the face.
|
||||
*
|
||||
* Since: 12.1.0
|
||||
*/
|
||||
unsigned int
|
||||
hb_ot_color_get_svg_document_count (hb_face_t *face)
|
||||
{
|
||||
return face->table.SVG->get_document_count ();
|
||||
}
|
||||
|
||||
/**
|
||||
* hb_ot_color_glyph_get_svg_document_index:
|
||||
* @face: #hb_face_t to work upon.
|
||||
* @glyph: glyph ID to query.
|
||||
* @svg_document_index: (out) (nullable): output SVG document index.
|
||||
*
|
||||
* Gets the `SVG`-table document index associated with a glyph.
|
||||
*
|
||||
* Return value: `true` if @glyph maps to an SVG document, `false` otherwise.
|
||||
*
|
||||
* Since: 12.1.0
|
||||
*/
|
||||
hb_bool_t
|
||||
hb_ot_color_glyph_get_svg_document_index (hb_face_t *face,
|
||||
hb_codepoint_t glyph,
|
||||
unsigned int *svg_document_index)
|
||||
{
|
||||
unsigned doc_index = 0;
|
||||
hb_bool_t ret = face->table.SVG->get_glyph_document_index (glyph, &doc_index);
|
||||
if (ret && svg_document_index)
|
||||
*svg_document_index = doc_index;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* hb_ot_color_get_svg_document_glyph_range:
|
||||
* @face: #hb_face_t to work upon.
|
||||
* @svg_document_index: SVG document index.
|
||||
* @start_glyph_id: (out) (nullable): output start glyph ID.
|
||||
* @end_glyph_id: (out) (nullable): output end glyph ID.
|
||||
*
|
||||
* Gets the glyph range covered by an `SVG`-table document index.
|
||||
*
|
||||
* Return value: `true` if @svg_document_index is valid, `false` otherwise.
|
||||
*
|
||||
* Since: 13.0.0
|
||||
*/
|
||||
hb_bool_t
|
||||
hb_ot_color_get_svg_document_glyph_range (hb_face_t *face,
|
||||
unsigned int svg_document_index,
|
||||
hb_codepoint_t *start_glyph_id,
|
||||
hb_codepoint_t *end_glyph_id)
|
||||
{
|
||||
return face->table.SVG->get_document_glyph_range (svg_document_index,
|
||||
start_glyph_id,
|
||||
end_glyph_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* hb_ot_color_glyph_reference_svg:
|
||||
* @face: #hb_face_t to work upon
|
||||
@@ -308,6 +375,7 @@ hb_ot_color_glyph_reference_svg (hb_face_t *face, hb_codepoint_t glyph)
|
||||
{
|
||||
return face->table.SVG->reference_blob_for_glyph (glyph);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
|
||||
+14
@@ -136,6 +136,20 @@ hb_ot_color_glyph_has_paint (hb_face_t *face,
|
||||
HB_EXTERN hb_bool_t
|
||||
hb_ot_color_has_svg (hb_face_t *face);
|
||||
|
||||
HB_EXTERN unsigned int
|
||||
hb_ot_color_get_svg_document_count (hb_face_t *face);
|
||||
|
||||
HB_EXTERN hb_bool_t
|
||||
hb_ot_color_glyph_get_svg_document_index (hb_face_t *face,
|
||||
hb_codepoint_t glyph,
|
||||
unsigned int *svg_document_index /* OUT */);
|
||||
|
||||
HB_EXTERN hb_bool_t
|
||||
hb_ot_color_get_svg_document_glyph_range (hb_face_t *face,
|
||||
unsigned int svg_document_index,
|
||||
hb_codepoint_t *start_glyph_id, /* OUT */
|
||||
hb_codepoint_t *end_glyph_id /* OUT */);
|
||||
|
||||
HB_EXTERN hb_blob_t *
|
||||
hb_ot_color_glyph_reference_svg (hb_face_t *face, hb_codepoint_t glyph);
|
||||
|
||||
|
||||
@@ -140,8 +140,10 @@ HB_OT_ACCELERATOR (OT, COLR)
|
||||
HB_OT_CORE_TABLE (OT, CPAL)
|
||||
HB_OT_ACCELERATOR (OT, CBDT)
|
||||
HB_OT_ACCELERATOR (OT, sbix)
|
||||
#ifndef HB_NO_SVG
|
||||
HB_OT_ACCELERATOR (OT, SVG)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* OpenType math. */
|
||||
#ifndef HB_NO_MATH
|
||||
|
||||
+2
@@ -937,7 +937,9 @@ hb_ot_paint_glyph_or_fail (hb_font_t *font,
|
||||
{
|
||||
#ifndef HB_NO_COLOR
|
||||
if (font->face->table.COLR->paint_glyph (font, glyph, paint_funcs, paint_data, palette, foreground)) return true;
|
||||
#ifndef HB_NO_SVG
|
||||
if (font->face->table.SVG->paint_glyph (font, glyph, paint_funcs, paint_data)) return true;
|
||||
#endif
|
||||
#ifndef HB_NO_OT_FONT_BITMAP
|
||||
if (font->face->table.CBDT->paint_glyph (font, glyph, paint_funcs, paint_data)) return true;
|
||||
if (font->face->table.sbix->paint_glyph (font, glyph, paint_funcs, paint_data)) return true;
|
||||
|
||||
+21
-6
@@ -2543,13 +2543,13 @@ struct SparseVarRegionAxis
|
||||
struct hb_scalar_cache_t
|
||||
{
|
||||
private:
|
||||
static constexpr unsigned STATIC_LENGTH = 16;
|
||||
static constexpr unsigned STATIC_LENGTH = 128;
|
||||
static constexpr int INVALID = INT_MIN;
|
||||
static constexpr float MULTIPLIER = 1 << ((sizeof (int) * 8) - 2);
|
||||
static constexpr float DIVISOR = 1.f / MULTIPLIER;
|
||||
|
||||
public:
|
||||
hb_scalar_cache_t () : length (STATIC_LENGTH) { clear (); }
|
||||
hb_scalar_cache_t () {}
|
||||
|
||||
hb_scalar_cache_t (const hb_scalar_cache_t&) = delete;
|
||||
hb_scalar_cache_t (hb_scalar_cache_t&&) = delete;
|
||||
@@ -2561,8 +2561,9 @@ struct hb_scalar_cache_t
|
||||
{
|
||||
if (!count) return (hb_scalar_cache_t *) &Null(hb_scalar_cache_t);
|
||||
|
||||
if (scratch_cache && count <= scratch_cache->length)
|
||||
if (scratch_cache && count <= STATIC_LENGTH)
|
||||
{
|
||||
scratch_cache->length = count;
|
||||
scratch_cache->clear ();
|
||||
return scratch_cache;
|
||||
}
|
||||
@@ -2970,7 +2971,9 @@ struct VarData
|
||||
}
|
||||
|
||||
if (unlikely (!c->extend_min (this))) return_trace (false);
|
||||
itemCount = row_count;
|
||||
if (unlikely (!c->check_assign (itemCount, row_count,
|
||||
HB_SERIALIZE_ERROR_INT_OVERFLOW)))
|
||||
return_trace (false);
|
||||
|
||||
int min_threshold = has_long ? -65536 : -128;
|
||||
int max_threshold = has_long ? +65535 : +127;
|
||||
@@ -3256,12 +3259,24 @@ struct MultiVarData
|
||||
|
||||
auto values_iter = deltaSets.fetcher (inner);
|
||||
unsigned regionCount = regionIndices.len;
|
||||
unsigned skip = 0;
|
||||
for (unsigned regionIndex = 0; regionIndex < regionCount; regionIndex++)
|
||||
{
|
||||
float scalar = regions.evaluate (regionIndices.arrayZ[regionIndex],
|
||||
coords, coord_count,
|
||||
cache);
|
||||
values_iter.add_to (out, scalar);
|
||||
// We skip lazily. Helps with the tail end.
|
||||
if (scalar == 0.0f)
|
||||
skip += out.length;
|
||||
else
|
||||
{
|
||||
if (skip)
|
||||
{
|
||||
values_iter.skip (skip);
|
||||
skip = 0;
|
||||
}
|
||||
values_iter.add_to (out, scalar);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4041,7 +4056,7 @@ struct ConditionValue
|
||||
bool evaluate (const int *coords, unsigned int coord_len,
|
||||
Instancer *instancer) const
|
||||
{
|
||||
signed value = defaultValue;
|
||||
float value = defaultValue;
|
||||
value += (*instancer)[varIdx];
|
||||
return value > 0;
|
||||
}
|
||||
|
||||
+54
-2
@@ -743,6 +743,10 @@ struct hb_ot_apply_context_t :
|
||||
|
||||
hb_vector_t<uint32_t> match_positions;
|
||||
uint32_t stack_match_positions[8];
|
||||
#ifndef HB_NO_BUFFER_MESSAGE
|
||||
hb_buffer_t::changed_func_t orig_changed_func = nullptr;
|
||||
void *orig_changed_data = nullptr;
|
||||
#endif
|
||||
|
||||
hb_ot_apply_context_t (unsigned int table_index_,
|
||||
hb_font_t *font_,
|
||||
@@ -773,6 +777,30 @@ struct hb_ot_apply_context_t :
|
||||
{
|
||||
init_iters ();
|
||||
match_positions.set_storage (stack_match_positions);
|
||||
|
||||
#ifndef HB_NO_BUFFER_MESSAGE
|
||||
if (buffer->messaging ())
|
||||
{
|
||||
assert (buffer->changed_func == nullptr ||
|
||||
buffer->changed_func == buffer_changed_trampoline);
|
||||
orig_changed_func = buffer->changed_func;
|
||||
orig_changed_data = buffer->changed_data;
|
||||
buffer->changed_func = buffer_changed_trampoline;
|
||||
buffer->changed_data = this;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
~hb_ot_apply_context_t ()
|
||||
{
|
||||
#ifndef HB_NO_BUFFER_MESSAGE
|
||||
if (buffer->messaging ())
|
||||
{
|
||||
assert (buffer->changed_func == buffer_changed_trampoline);
|
||||
assert (buffer->changed_data == this);
|
||||
buffer->changed_func = orig_changed_func;
|
||||
buffer->changed_data = orig_changed_data;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void init_iters ()
|
||||
@@ -801,6 +829,18 @@ struct hb_ot_apply_context_t :
|
||||
return buffer->random_state;
|
||||
}
|
||||
|
||||
static void buffer_changed_trampoline (hb_buffer_t *buffer HB_UNUSED, void *user_data)
|
||||
{
|
||||
((hb_ot_apply_context_t *) user_data)->buffer_changed ();
|
||||
}
|
||||
void buffer_changed ()
|
||||
{
|
||||
buffer->update_digest ();
|
||||
if (likely (has_glyph_classes))
|
||||
for (unsigned i = 0; i < buffer->len; i++)
|
||||
_set_glyph_class_props (buffer->info[i]);
|
||||
}
|
||||
|
||||
HB_ALWAYS_INLINE
|
||||
HB_HOT
|
||||
bool match_properties_mark (const hb_glyph_info_t *info,
|
||||
@@ -870,8 +910,7 @@ struct hb_ot_apply_context_t :
|
||||
props |= HB_OT_LAYOUT_GLYPH_PROPS_MULTIPLIED;
|
||||
if (likely (has_glyph_classes))
|
||||
{
|
||||
props &= HB_OT_LAYOUT_GLYPH_PROPS_PRESERVE;
|
||||
_hb_glyph_info_set_glyph_props (&buffer->cur(), props | gdef_accel.get_glyph_props (glyph_index));
|
||||
_set_glyph_class_props (buffer->cur(), props, glyph_index);
|
||||
}
|
||||
else if (class_guess)
|
||||
{
|
||||
@@ -881,6 +920,19 @@ struct hb_ot_apply_context_t :
|
||||
else
|
||||
_hb_glyph_info_set_glyph_props (&buffer->cur(), props);
|
||||
}
|
||||
void _set_glyph_class_props (hb_glyph_info_t &info,
|
||||
unsigned int props,
|
||||
hb_codepoint_t glyph_index)
|
||||
{
|
||||
props &= HB_OT_LAYOUT_GLYPH_PROPS_PRESERVE;
|
||||
_hb_glyph_info_set_glyph_props (&info, props | gdef_accel.get_glyph_props (glyph_index));
|
||||
}
|
||||
void _set_glyph_class_props (hb_glyph_info_t &info)
|
||||
{
|
||||
_set_glyph_class_props (info,
|
||||
_hb_glyph_info_get_glyph_props (&info),
|
||||
info.codepoint);
|
||||
}
|
||||
|
||||
void replace_glyph (hb_codepoint_t glyph_index)
|
||||
{
|
||||
|
||||
+2
-6
@@ -580,12 +580,8 @@ hb_form_clusters (hb_buffer_t *buffer)
|
||||
if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_CONTINUATIONS))
|
||||
return;
|
||||
|
||||
if (HB_BUFFER_CLUSTER_LEVEL_IS_GRAPHEMES (buffer->cluster_level))
|
||||
foreach_grapheme (buffer, start, end)
|
||||
buffer->merge_clusters (start, end);
|
||||
else
|
||||
foreach_grapheme (buffer, start, end)
|
||||
buffer->unsafe_to_break (start, end);
|
||||
foreach_grapheme (buffer, start, end)
|
||||
buffer->merge_grapheme_clusters (start, end);
|
||||
}
|
||||
|
||||
static void
|
||||
|
||||
+67
-71
@@ -9,41 +9,43 @@
|
||||
#ifndef HB_OT_SHAPER_ARABIC_PUA_HH
|
||||
#define HB_OT_SHAPER_ARABIC_PUA_HH
|
||||
|
||||
static const uint8_t
|
||||
_hb_arabic_u8[464] =
|
||||
#include "hb.hh"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
static const uint8_t _hb_arabic_pua_u8[453]=
|
||||
{
|
||||
84, 86, 85, 85, 85, 85, 85,213, 16, 34, 34, 34, 34, 34, 35, 34,
|
||||
34, 34, 34, 34, 34, 34, 34, 34, 36, 34, 34, 34, 34, 34, 34, 34,
|
||||
34, 34, 34, 34, 34, 34, 82, 16, 0, 0, 0, 0, 1, 2, 3, 4,
|
||||
0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 7,
|
||||
0, 0, 8, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 11, 12, 13, 14, 15,
|
||||
16, 17, 18, 19, 20, 21, 0, 0, 0, 22, 0, 23, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 24, 25, 26, 27, 28, 29, 30, 31,
|
||||
32, 33, 34, 35, 36, 37, 38, 39, 16, 34, 34, 34, 35, 34, 34, 34,
|
||||
34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,
|
||||
34, 34, 34, 34, 34, 34, 34, 66, 16, 50, 68, 68, 68, 68, 68, 68,
|
||||
68, 68, 68, 68,101, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68,
|
||||
71, 68, 68, 68, 68, 68, 68, 68,152,186, 76, 77, 68,254, 16, 50,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 0, 0, 5, 6,
|
||||
0, 0, 0, 0, 0, 0, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 10, 0,
|
||||
0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, 0, 0, 3, 0,
|
||||
4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 13, 0, 0, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
|
||||
24, 25, 26, 27, 28, 23, 23, 29, 30, 31, 32, 33, 0, 0, 0, 0,
|
||||
0, 0, 0, 34, 0, 0, 0, 35, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 36, 37, 38, 0, 0, 0, 0, 0, 0, 0, 39, 0, 0, 40,
|
||||
41, 42, 0, 43, 44, 0, 0, 45, 46, 0, 47, 48, 49, 0, 0, 0,
|
||||
0, 50, 0, 0, 51, 52, 0, 53, 54, 55, 56, 57, 58, 0, 0, 0,
|
||||
0, 0, 59, 60, 61, 62, 63, 64, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 0, 0, 66,
|
||||
0, 0, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
|
||||
84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
|
||||
0, 0, 0, 1, 2, 3, 4, 0, 0, 0, 5, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 6, 0, 7, 0, 0, 8, 0, 0, 0, 9, 0, 0,
|
||||
10, 0, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 0, 0, 0,
|
||||
22, 0, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24,
|
||||
25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 33,
|
||||
0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 33, 67, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0,101, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0,152,
|
||||
186, 12, 13, 0,254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 4,
|
||||
5, 0, 0, 6, 7, 0, 0, 0, 0, 0, 0, 8, 9, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10,
|
||||
0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0,
|
||||
0, 0, 13, 0, 0, 0, 0, 0, 14, 0, 0, 15, 16, 17, 18, 19,
|
||||
20, 21, 22, 23, 1, 24, 25, 26, 27, 28, 1, 1, 29, 30, 31, 32,
|
||||
33, 0, 0, 0, 0, 0, 0, 0, 34, 0, 0, 0, 35, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 36, 37, 38, 0, 0, 0, 0, 0, 0,
|
||||
0, 39, 0, 0, 40, 41, 42, 0, 43, 44, 0, 0, 45, 46, 0, 47,
|
||||
48, 49, 0, 0, 0, 0, 50, 0, 0, 51, 52, 0, 53, 54, 55, 56,
|
||||
57, 58, 0, 0, 0, 0, 0, 59, 60, 61, 62, 63, 64, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 65, 0, 0, 66, 0, 0, 67, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78,
|
||||
79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94,
|
||||
95, 96, 97, 98, 99,
|
||||
};
|
||||
static const uint16_t
|
||||
_hb_arabic_u16[720] =
|
||||
static const uint16_t _hb_arabic_pua_u16[717]=
|
||||
{
|
||||
0, 0, 0, 0, 0, 0, 0, 0,61728,61729,61730, 0, 0,61733, 0, 0,
|
||||
61736,61737,61738,61739,61790,61741,61742,61743,61872,61873,61874,61875,61876,61877,61878,61879,
|
||||
@@ -65,52 +67,46 @@ _hb_arabic_u16[720] =
|
||||
61808,61813,61813,61811,61812,61816,61816,61814,61815,61818,61818,61817,61817,61820,61820,61819,
|
||||
61819,61822,61822,61821,61821,61921,61921,61823,61823,61860,61859,61857,61858,61861,61861,61868,
|
||||
61867,61864,61863,61862,61862,61888,61889,61886,61887,61890,61891,61885,61884, 0, 0, 0,
|
||||
0, 0, 0, 0,61984,61985,61986, 0, 0,61989, 0, 0,61992,61993,61994,61995,
|
||||
62046,61997,61998,61999, 0, 0,62010,62011, 0,62013, 0,62015, 0, 0, 0,62043,
|
||||
0,62045, 0, 0, 0, 0, 0,61987, 0, 0, 0,61988, 0, 0, 0,61990,
|
||||
0, 0, 0,61991,61996, 0, 0, 0, 0, 0, 0,62011, 0, 0, 0,62015,
|
||||
0,62165,62021,62019,62170,62023,62169,62017,62028,62161,62032,62036,62040,62048,62052,62053,
|
||||
62055,62057,62059,62064,62068,62072,62078,62114,62115,62122,62126,61952,61952,61952,61952,61952,
|
||||
62047,62130,62134,62138,62142,62146,62150,62154,62155,62164,62160,62183,62184,62187,62180,62181,
|
||||
62186,62185,62182,61952,61952,61952,61952, 0,62000,62001,62002,62003,62004,62005,62006,62007,
|
||||
62008,62009, 0,62046,62046, 0, 0, 0,61964,61965,61966,61967,62012,62014, 0, 0,
|
||||
61954, 0,61981, 0, 0, 0,61955, 0,61982, 0,61956, 0, 0, 0,62111, 0,
|
||||
0, 0, 0,61970,61971,61972,61957, 0,61980, 0, 0, 0, 0, 0,61958, 0,
|
||||
61983, 0, 0, 0, 0, 0,62191, 0,62188,62189,62192, 0, 0, 0,61973, 0,
|
||||
0,62098, 0, 0,61974, 0, 0,62099, 0, 0,62101, 0, 0,61975, 0, 0,
|
||||
62100, 0, 0, 0,62080,62081,62082,62102, 0,62083,62084,62085,62103, 0, 0, 0,
|
||||
62106, 0,62107, 0,62108, 0, 0, 0,61976, 0, 0, 0, 0,62086,62087,62088,
|
||||
62109,61978,62089,62090,62091,62110,62093,62094, 0,62104, 0, 0, 0, 0,62095,62096,
|
||||
62097,62105, 0, 0,61977, 0, 0, 0, 0, 0,62075,62077,61968, 0, 0, 0,
|
||||
0,62021,62022,62019,62020,62170,62171,62023,62024,62169,62168,62166,62167,62017,62018,62028,
|
||||
62027,62025,62026,62161,62162,62032,62031,62029,62030,62036,62035,62033,62034,62040,62039,62037,
|
||||
62038,62048,62044,62041,62042,62052,62051,62049,62050,62053,62054,62055,62056,62057,62058,62059,
|
||||
62060,62064,62063,62061,62062,62068,62067,62065,62066,62072,62071,62069,62070,62078,62076,62073,
|
||||
62074,62114,62113,62079,62193,62118,62117,62115,62116,62122,62121,62119,62120,62126,62125,62123,
|
||||
62124,62130,62129,62127,62128,62134,62133,62131,62132,62138,62137,62135,62136,62142,62141,62139,
|
||||
62140,62146,62145,62143,62144,62150,62149,62147,62148,62154,62153,62151,62152,62155,62156,62164,
|
||||
62163,62160,62159,62157,62158,62176,62177,62174,62175,62178,62179,62172,62173, 0, 0, 0,
|
||||
0,61952,61952,61952,61952,61984,61985,61986, 0, 0,61989, 0, 0,61992,61993,61994,
|
||||
61995,62046,61997,61998,61999, 0, 0,62010,62011, 0,62013, 0,62015, 0, 0, 0,
|
||||
62043, 0,62045, 0, 0, 0, 0, 0,61987, 0, 0, 0,61988, 0, 0, 0,
|
||||
61990, 0, 0, 0,61991,61996, 0, 0, 0, 0, 0, 0,62011, 0, 0, 0,
|
||||
62015, 0,62165,62021,62019,62170,62023,62169,62017,62028,62161,62032,62036,62040,62048,62052,
|
||||
62053,62055,62057,62059,62064,62068,62072,62078,62114,62115,62122,62126,61952,62047,62130,62134,
|
||||
62138,62142,62146,62150,62154,62155,62164,62160,62183,62184,62187,62180,62181,62186,62185,62182,
|
||||
61952,61952,61952,61952, 0,62000,62001,62002,62003,62004,62005,62006,62007,62008,62009, 0,
|
||||
62046,62046, 0, 0, 0,61964,61965,61966,61967,62012,62014, 0, 0,61954, 0,61981,
|
||||
0, 0, 0,61955, 0,61982, 0,61956, 0, 0, 0,62111, 0, 0, 0, 0,
|
||||
61970,61971,61972,61957, 0,61980, 0, 0, 0, 0, 0,61958, 0,61983, 0, 0,
|
||||
0, 0, 0,62191, 0,62188,62189,62192, 0, 0, 0,61973, 0, 0,62098, 0,
|
||||
0,61974, 0, 0,62099, 0, 0,62101, 0, 0,61975, 0, 0,62100, 0, 0,
|
||||
0,62080,62081,62082,62102, 0,62083,62084,62085,62103, 0, 0, 0,62106, 0,62107,
|
||||
0,62108, 0, 0, 0,61976, 0, 0, 0, 0,62086,62087,62088,62109,61978,62089,
|
||||
62090,62091,62110,62093,62094, 0,62104, 0, 0, 0, 0,62095,62096,62097,62105, 0,
|
||||
0,61977, 0, 0, 0, 0, 0,62075,62077,61968, 0, 0, 0, 0,62021,62022,
|
||||
62019,62020,62170,62171,62023,62024,62169,62168,62166,62167,62017,62018,62028,62027,62025,62026,
|
||||
62161,62162,62032,62031,62029,62030,62036,62035,62033,62034,62040,62039,62037,62038,62048,62044,
|
||||
62041,62042,62052,62051,62049,62050,62053,62054,62055,62056,62057,62058,62059,62060,62064,62063,
|
||||
62061,62062,62068,62067,62065,62066,62072,62071,62069,62070,62078,62076,62073,62074,62114,62113,
|
||||
62079,62193,62118,62117,62115,62116,62122,62121,62119,62120,62126,62125,62123,62124,62130,62129,
|
||||
62127,62128,62134,62133,62131,62132,62138,62137,62135,62136,62142,62141,62139,62140,62146,62145,
|
||||
62143,62144,62150,62149,62147,62148,62154,62153,62151,62152,62155,62156,62164,62163,62160,62159,
|
||||
62157,62158,62176,62177,62174,62175,62178,62179,62172,62173, 0, 0, 0,
|
||||
};
|
||||
|
||||
static inline unsigned
|
||||
_hb_arabic_b2 (const uint8_t* a, unsigned i)
|
||||
static inline uint8_t _hb_arabic_pua_b4 (const uint8_t* a, unsigned i)
|
||||
{
|
||||
return (a[i>>2]>>((i&3u)<<1))&3u;
|
||||
return (a[i>>1]>>((i&1)<<2))&15;
|
||||
}
|
||||
static inline unsigned
|
||||
_hb_arabic_b4 (const uint8_t* a, unsigned i)
|
||||
static inline uint16_t _hb_arabic_pua_simp_map (unsigned u)
|
||||
{
|
||||
return (a[i>>1]>>((i&1u)<<2))&15u;
|
||||
/* packtab: [2^2,2^4,2^4,2^3] */
|
||||
return u<65277u ? (uint16_t)(_hb_arabic_pua_u16[((_hb_arabic_pua_u8[31u+((_hb_arabic_pua_b4(_hb_arabic_pua_u8,((((13835058055282164225ULL>>((((((((u)>>3))>>4))>>4))<<1))&3))<<4)+((((((u)>>3))>>4))&15)))<<4)+((((u)>>3))&15)])<<3)+((u)&7)]) : 0;
|
||||
}
|
||||
static inline uint_fast16_t
|
||||
_hb_arabic_pua_simp_map (unsigned u)
|
||||
static inline uint16_t _hb_arabic_pua_trad_map (unsigned u)
|
||||
{
|
||||
return u<65277u?_hb_arabic_u16[((_hb_arabic_u8[40+(((_hb_arabic_b4(8+_hb_arabic_u8,((_hb_arabic_b2(_hb_arabic_u8,u>>3>>4>>4))<<4)+((u>>3>>4)&15u)))<<4)+((u>>3)&15u))])<<3)+((u)&7u)]:0;
|
||||
}
|
||||
static inline uint_fast16_t
|
||||
_hb_arabic_pua_trad_map (unsigned u)
|
||||
{
|
||||
return u<65277u?_hb_arabic_u16[320+(((_hb_arabic_u8[208+(((_hb_arabic_b4(168+_hb_arabic_u8,((_hb_arabic_b4(136+_hb_arabic_u8,u>>2>>4>>4))<<4)+((u>>2>>4)&15u)))<<4)+((u>>2)&15u))])<<2)+((u)&3u))]:0;
|
||||
/* packtab: [2^4,2^4,2^4,2^2] */
|
||||
return u<65277u ? (uint16_t)(_hb_arabic_pua_u16[317u+((_hb_arabic_pua_u8[197u+((_hb_arabic_pua_b4(_hb_arabic_pua_u8+159u,((_hb_arabic_pua_b4(_hb_arabic_pua_u8+127u,((((((u)>>2))>>4))>>4)))<<4)+((((((u)>>2))>>4))&15)))<<4)+((((u)>>2))&15)])<<2)+((u)&3)]) : 0;
|
||||
}
|
||||
|
||||
#endif /* HB_OT_SHAPER_ARABIC_PUA_HH */
|
||||
|
||||
+61
-206
@@ -16,222 +16,77 @@
|
||||
#ifndef HB_OT_SHAPER_ARABIC_TABLE_HH
|
||||
#define HB_OT_SHAPER_ARABIC_TABLE_HH
|
||||
|
||||
#include "hb.hh"
|
||||
|
||||
#define A JOINING_GROUP_ALAPH
|
||||
#define DR JOINING_GROUP_DALATH_RISH
|
||||
#define C JOINING_TYPE_C
|
||||
#define D JOINING_TYPE_D
|
||||
#define L JOINING_TYPE_L
|
||||
#define R JOINING_TYPE_R
|
||||
#define T JOINING_TYPE_T
|
||||
#define U JOINING_TYPE_U
|
||||
#define X JOINING_TYPE_X
|
||||
#include <stdint.h>
|
||||
|
||||
static const uint8_t joining_table[] =
|
||||
static const uint8_t _hb_arabic_joining_u8[737]=
|
||||
{
|
||||
0, 16, 2, 0, 0, 0, 3, 0, 4, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 96,135, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 8, 16, 24, 32, 40, 48, 0, 56, 0, 64, 72, 80, 0,
|
||||
0, 0, 0, 88, 96,104, 0, 0, 0, 0, 0,112,120, 0, 0, 0,
|
||||
0, 0, 0, 0,128, 0, 0, 0, 0, 0, 0, 0, 0, 0,136, 0,
|
||||
0,144, 0, 0, 0, 0, 0,152, 0, 0, 0, 0, 0, 0,160,168,
|
||||
176,184,192, 0, 0,200,208, 0, 0, 0, 0, 0, 0, 0, 0,216,
|
||||
224, 0, 0, 0, 0, 0, 0, 0, 0, 8, 9, 0, 0, 10, 11, 12,
|
||||
1, 1, 13, 0, 0, 0, 14, 15, 1, 1, 2, 2, 4, 1, 1, 1,
|
||||
1, 16, 17, 18, 3, 0, 19, 0, 20, 0, 21, 22, 23, 1, 24, 0,
|
||||
0, 0, 25, 1, 26, 1, 27, 28, 4, 0, 29, 1, 1, 1, 30, 0,
|
||||
31, 32, 5, 33, 34, 35, 36, 2, 2, 37, 38, 6, 0, 1, 39, 40,
|
||||
5, 1, 7, 0, 0, 41, 0, 0, 0, 42, 43, 0, 0, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 7, 44, 1, 1, 1, 1, 45, 0,
|
||||
0, 0, 46, 0, 0, 0, 47, 0, 0, 0, 0, 0, 0, 48, 6, 0,
|
||||
0, 1, 1, 1, 1, 1, 1, 49, 0, 50, 51, 52, 53, 54, 55, 0,
|
||||
0, 56, 57, 58, 0, 0, 59, 0, 0, 60, 1, 1, 1, 61, 0, 0,
|
||||
0, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63,
|
||||
1, 64, 0, 65, 0, 0, 0, 66, 1, 67, 0, 0, 0, 0, 0, 68,
|
||||
69, 70, 71, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
3, 0, 3, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 72, 73, 0, 0, 0, 0, 0, 0,119,119,119,119, 51, 51, 51,
|
||||
51, 34, 34, 34, 34,119,119, 7,119, 34, 51, 51, 51, 35, 51, 51,
|
||||
51, 0,119,119,119,115,119,119,119, 0, 0, 0,119,112, 7,119,
|
||||
119, 3, 34, 34, 35, 35, 51, 51, 35, 34, 50, 51, 51, 50,115,119,
|
||||
119,119,119,119, 51, 39, 34, 32, 34, 50, 35, 34, 34, 34, 34, 35,
|
||||
35, 51, 34, 39,119,119,119,119, 34,119, 51,115, 55,119,119,119,
|
||||
103,116, 51, 83, 37, 34, 51, 51, 50, 50, 53, 50, 83,119,119, 39,
|
||||
51, 35, 34, 51, 51, 51, 35, 50, 51, 35, 35, 50, 51,119, 51, 51,
|
||||
51, 51,115,119,119,119,115,119,119, 50, 51, 51, 34, 51, 51, 50,
|
||||
34,114,119,119,119, 3, 51, 51, 32, 35,114,119,119, 34, 50, 51,
|
||||
3, 48, 51, 51, 50, 51, 34, 2, 50, 35, 50, 51, 51,119,112,119,
|
||||
119,119,119,119, 48,119,115,119,112, 0, 0, 96, 54,115,115,119,
|
||||
119,119,119, 48,119,119,119,119, 7,119,119,119, 0, 51, 1,119,
|
||||
119, 51, 51, 35, 32, 32, 2, 16, 34, 34, 50, 51, 19, 51, 51, 35,
|
||||
51, 35, 0,114,119,119, 55, 51, 35, 35, 35, 34, 51, 35, 51, 50,
|
||||
34, 35,119,119,119, 39, 34, 50, 3, 49, 51, 51, 51, 51, 50,119,
|
||||
119,119, 50,115, 51, 51, 35, 51, 51, 51, 51, 3,119, 55, 51,114,
|
||||
119, 51, 51, 34, 51, 51,119,119,119, 3, 51, 34, 2, 35, 50, 35,
|
||||
51, 48, 34, 3, 0, 32, 19,119,119, 51, 51,119,119,119,103,119,
|
||||
119,
|
||||
};
|
||||
|
||||
#define joining_offset_0x0600u 0
|
||||
|
||||
/* Arabic */
|
||||
|
||||
/* 0600 */ U,U,U,U,U,U,X,X,U,X,X,U,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
|
||||
/* 0620 */ D,U,R,R,R,R,D,R,D,R,D,D,D,D,D,R,R,R,R,D,D,D,D,D,D,D,D,D,D,D,D,D,
|
||||
/* 0640 */ C,D,D,D,D,D,D,D,R,D,D,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
|
||||
/* 0660 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,D,D,X,R,R,R,U,R,R,R,D,D,D,D,D,D,D,D,
|
||||
/* 0680 */ D,D,D,D,D,D,D,D,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,D,D,D,D,D,D,
|
||||
/* 06A0 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,
|
||||
/* 06C0 */ R,D,D,R,R,R,R,R,R,R,R,R,D,R,D,R,D,D,R,R,X,R,X,X,X,X,X,X,X,U,X,X,
|
||||
/* 06E0 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,R,R,X,X,X,X,X,X,X,X,X,X,D,D,D,X,X,D,
|
||||
|
||||
/* Syriac */
|
||||
|
||||
/* 0700 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,T,A,X,D,D,D,DR,DR,R,R,R,D,D,D,D,R,D,
|
||||
/* 0720 */ D,D,D,D,D,D,D,D,R,D,DR,D,R,D,D,DR,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
|
||||
/* 0740 */ X,X,X,X,X,X,X,X,X,X,X,X,X,R,D,D,
|
||||
|
||||
/* Arabic Supplement */
|
||||
|
||||
/* 0740 */ D,D,D,D,D,D,D,D,D,R,R,R,D,D,D,D,
|
||||
/* 0760 */ D,D,D,D,D,D,D,D,D,D,D,R,R,D,D,D,D,R,D,R,R,D,D,D,R,R,D,D,D,D,D,D,
|
||||
|
||||
/* FILLER */
|
||||
|
||||
/* 0780 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
|
||||
/* 07A0 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
|
||||
|
||||
/* NKo */
|
||||
|
||||
/* 07C0 */ X,X,X,X,X,X,X,X,X,X,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,
|
||||
/* 07E0 */ D,D,D,D,D,D,D,D,D,D,D,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,C,X,X,X,X,X,
|
||||
|
||||
/* FILLER */
|
||||
|
||||
/* 0800 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
|
||||
/* 0820 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
|
||||
|
||||
/* Mandaic */
|
||||
|
||||
/* 0840 */ R,D,D,D,D,D,R,R,D,R,D,D,D,D,D,D,D,D,D,D,R,D,R,R,R,X,X,X,X,X,X,X,
|
||||
|
||||
/* Syriac Supplement */
|
||||
|
||||
/* 0860 */ D,U,D,D,D,D,U,R,D,R,R,X,X,X,X,X,
|
||||
|
||||
/* Arabic Extended-B */
|
||||
|
||||
/* 0860 */ R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,
|
||||
/* 0880 */ R,R,R,C,C,C,D,U,U,D,D,D,D,D,R,D,U,U,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
|
||||
|
||||
/* Arabic Extended-A */
|
||||
|
||||
/* 08A0 */ D,D,D,D,D,D,D,D,D,D,R,R,R,U,R,D,D,R,R,D,D,D,D,D,D,R,D,D,D,D,D,D,
|
||||
/* 08C0 */ D,D,D,D,D,D,D,D,D,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
|
||||
/* 08E0 */ X,X,U,
|
||||
|
||||
#define joining_offset_0x1806u 739
|
||||
|
||||
/* Mongolian */
|
||||
|
||||
/* 1800 */ U,D,X,X,C,X,X,X,U,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
|
||||
/* 1820 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,
|
||||
/* 1840 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,
|
||||
/* 1860 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,X,X,X,X,X,X,X,
|
||||
/* 1880 */ U,U,U,U,U,T,T,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,
|
||||
/* 18A0 */ D,D,D,D,D,D,D,D,D,X,D,
|
||||
|
||||
#define joining_offset_0x200cu 904
|
||||
|
||||
/* General Punctuation */
|
||||
|
||||
/* 2000 */ U,C,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
|
||||
/* 2020 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,U,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
|
||||
/* 2040 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
|
||||
/* 2060 */ X,X,X,X,X,X,U,U,U,U,
|
||||
|
||||
#define joining_offset_0xa840u 998
|
||||
|
||||
/* Phags-pa */
|
||||
|
||||
/* A840 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,
|
||||
/* A860 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,L,U,
|
||||
|
||||
#define joining_offset_0x10ac0u 1050
|
||||
|
||||
/* Manichaean */
|
||||
|
||||
/* 10AC0 */ D,D,D,D,D,R,U,R,U,R,R,U,U,L,R,R,R,R,R,D,D,D,D,L,D,D,D,D,D,R,D,D,
|
||||
/* 10AE0 */ D,R,U,U,R,X,X,X,X,X,X,D,D,D,D,R,
|
||||
|
||||
#define joining_offset_0x10b80u 1098
|
||||
|
||||
/* Psalter Pahlavi */
|
||||
|
||||
/* 10B80 */ D,R,D,R,R,R,D,D,D,R,D,D,R,D,R,R,D,R,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
|
||||
/* 10BA0 */ X,X,X,X,X,X,X,X,X,R,R,R,R,D,D,U,
|
||||
|
||||
#define joining_offset_0x10d00u 1146
|
||||
|
||||
/* Hanifi Rohingya */
|
||||
|
||||
/* 10D00 */ L,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,
|
||||
/* 10D20 */ D,D,R,D,
|
||||
|
||||
#define joining_offset_0x10ec2u 1182
|
||||
|
||||
/* Arabic Extended-C */
|
||||
|
||||
/* 10EC0 */ R,D,D,X,D,D,
|
||||
|
||||
#define joining_offset_0x10f30u 1188
|
||||
|
||||
/* Sogdian */
|
||||
|
||||
/* 10F20 */ D,D,D,R,D,D,D,D,D,D,D,D,D,D,D,D,
|
||||
/* 10F40 */ D,D,D,D,D,U,X,X,X,X,X,X,X,X,X,X,X,D,D,D,R,X,X,X,X,X,X,X,X,X,X,X,
|
||||
/* 10F60 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
|
||||
|
||||
/* Old Uyghur */
|
||||
|
||||
/* 10F60 */ D,D,D,D,R,R,D,D,D,D,D,D,D,D,D,D,
|
||||
/* 10F80 */ D,D,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
|
||||
/* 10FA0 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
|
||||
|
||||
/* Chorasmian */
|
||||
|
||||
/* 10FA0 */ D,U,D,D,R,R,R,U,D,R,R,D,D,R,D,D,
|
||||
/* 10FC0 */ U,D,R,R,D,U,U,U,U,R,D,L,
|
||||
|
||||
#define joining_offset_0x110bdu 1344
|
||||
|
||||
/* Kaithi */
|
||||
|
||||
/* 110A0 */ U,X,X,
|
||||
/* 110C0 */ X,X,X,X,X,X,X,X,X,X,X,X,X,U,
|
||||
|
||||
#define joining_offset_0x1e900u 1361
|
||||
|
||||
/* Adlam */
|
||||
|
||||
/* 1E900 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,
|
||||
/* 1E920 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,
|
||||
/* 1E940 */ D,D,D,D,X,X,X,X,X,X,X,T,
|
||||
|
||||
}; /* Table items: 1437; occupancy: 58% */
|
||||
|
||||
static inline uint8_t _hb_arabic_joining_b4 (const uint8_t* a, unsigned i)
|
||||
{
|
||||
return (a[i>>1]>>((i&1)<<2))&15;
|
||||
}
|
||||
static inline uint8_t _hb_arabic_joining_joining_type (unsigned u)
|
||||
{
|
||||
/* packtab: [2^4,2^3,2^3,2^3] */
|
||||
return u<125260u ? (uint8_t)(_hb_arabic_joining_b4(_hb_arabic_joining_u8+441u,((_hb_arabic_joining_u8[209u+_hb_arabic_joining_u8[123u+((_hb_arabic_joining_b4(_hb_arabic_joining_u8,((((((u)>>3))>>3))>>3)))<<3)+((((((u)>>3))>>3))&7)]+((((u)>>3))&7)])<<3)+((u)&7))) : 7;
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
joining_type (hb_codepoint_t u)
|
||||
{
|
||||
switch (u >> 12)
|
||||
{
|
||||
case 0x0u:
|
||||
if (hb_in_range<hb_codepoint_t> (u, 0x0600u, 0x08E2u)) return joining_table[u - 0x0600u + joining_offset_0x0600u];
|
||||
break;
|
||||
|
||||
case 0x1u:
|
||||
if (hb_in_range<hb_codepoint_t> (u, 0x1806u, 0x18AAu)) return joining_table[u - 0x1806u + joining_offset_0x1806u];
|
||||
break;
|
||||
|
||||
case 0x2u:
|
||||
if (hb_in_range<hb_codepoint_t> (u, 0x200Cu, 0x2069u)) return joining_table[u - 0x200Cu + joining_offset_0x200cu];
|
||||
break;
|
||||
|
||||
case 0xAu:
|
||||
if (hb_in_range<hb_codepoint_t> (u, 0xA840u, 0xA873u)) return joining_table[u - 0xA840u + joining_offset_0xa840u];
|
||||
break;
|
||||
|
||||
case 0x10u:
|
||||
if (hb_in_range<hb_codepoint_t> (u, 0x10AC0u, 0x10AEFu)) return joining_table[u - 0x10AC0u + joining_offset_0x10ac0u];
|
||||
if (hb_in_range<hb_codepoint_t> (u, 0x10B80u, 0x10BAFu)) return joining_table[u - 0x10B80u + joining_offset_0x10b80u];
|
||||
if (hb_in_range<hb_codepoint_t> (u, 0x10D00u, 0x10D23u)) return joining_table[u - 0x10D00u + joining_offset_0x10d00u];
|
||||
if (hb_in_range<hb_codepoint_t> (u, 0x10EC2u, 0x10EC7u)) return joining_table[u - 0x10EC2u + joining_offset_0x10ec2u];
|
||||
if (hb_in_range<hb_codepoint_t> (u, 0x10F30u, 0x10FCBu)) return joining_table[u - 0x10F30u + joining_offset_0x10f30u];
|
||||
break;
|
||||
|
||||
case 0x11u:
|
||||
if (hb_in_range<hb_codepoint_t> (u, 0x110BDu, 0x110CDu)) return joining_table[u - 0x110BDu + joining_offset_0x110bdu];
|
||||
break;
|
||||
|
||||
case 0x1Eu:
|
||||
if (hb_in_range<hb_codepoint_t> (u, 0x1E900u, 0x1E94Bu)) return joining_table[u - 0x1E900u + joining_offset_0x1e900u];
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return X;
|
||||
return _hb_arabic_joining_joining_type (u);
|
||||
}
|
||||
|
||||
#undef A
|
||||
#undef DR
|
||||
#undef C
|
||||
#undef D
|
||||
#undef L
|
||||
#undef R
|
||||
#undef T
|
||||
#undef U
|
||||
#undef X
|
||||
|
||||
|
||||
static const uint16_t shaping_table[][4] =
|
||||
{
|
||||
|
||||
+35
-12
@@ -77,8 +77,8 @@ enum hb_arabic_joining_type_t {
|
||||
JOINING_GROUP_DALATH_RISH = 5,
|
||||
NUM_STATE_MACHINE_COLS = 6,
|
||||
|
||||
JOINING_TYPE_T = 7,
|
||||
JOINING_TYPE_X = 8 /* means: use general-category to choose between U or T. */
|
||||
JOINING_TYPE_T = 6,
|
||||
JOINING_TYPE_X = 7 /* means: use general-category to choose between U or T. */
|
||||
};
|
||||
|
||||
#include "hb-ot-shaper-arabic-table.hh"
|
||||
@@ -561,20 +561,29 @@ apply_stch (const hb_ot_shape_plan_t *plan HB_UNUSED,
|
||||
DEBUG_MSG (ARABIC, nullptr, "fixed tiles: count=%d width=%" PRId32, n_fixed, w_fixed);
|
||||
DEBUG_MSG (ARABIC, nullptr, "repeating tiles: count=%d width=%" PRId32, n_repeating, w_repeating);
|
||||
|
||||
/* Number of additional times to repeat each repeating tile. */
|
||||
int n_copies = 0;
|
||||
static constexpr unsigned STCH_MAX_GLYPHS = 256;
|
||||
|
||||
hb_position_t w_remaining = w_total - w_fixed;
|
||||
if (sign * w_remaining > sign * w_repeating && sign * w_repeating > 0)
|
||||
n_copies = (sign * w_remaining) / (sign * w_repeating) - 1;
|
||||
/* Number of additional times to repeat each repeating tile. */
|
||||
unsigned int n_copies = 0;
|
||||
|
||||
int64_t w_remaining_signed = (int64_t) w_total - w_fixed;
|
||||
int64_t w_repeating_signed = w_repeating;
|
||||
if (sign < 0)
|
||||
{
|
||||
w_remaining_signed = -w_remaining_signed;
|
||||
w_repeating_signed = -w_repeating_signed;
|
||||
}
|
||||
hb_position_t w_remaining = (hb_position_t) (w_total - w_fixed);
|
||||
if (w_remaining_signed > w_repeating_signed && w_repeating_signed > 0)
|
||||
n_copies = w_remaining_signed / w_repeating_signed - 1;
|
||||
|
||||
/* See if we can improve the fit by adding an extra repeat and squeezing them together a bit. */
|
||||
hb_position_t extra_repeat_overlap = 0;
|
||||
hb_position_t shortfall = sign * w_remaining - sign * w_repeating * (n_copies + 1);
|
||||
int64_t shortfall = w_remaining_signed - w_repeating_signed * (n_copies + 1);
|
||||
if (shortfall > 0 && n_repeating > 0)
|
||||
{
|
||||
++n_copies;
|
||||
hb_position_t excess = (n_copies + 1) * sign * w_repeating - sign * w_remaining;
|
||||
int64_t excess = (n_copies + 1) * w_repeating_signed - w_remaining_signed;
|
||||
if (excess > 0)
|
||||
{
|
||||
extra_repeat_overlap = excess / (n_copies * n_repeating);
|
||||
@@ -582,10 +591,22 @@ apply_stch (const hb_ot_shape_plan_t *plan HB_UNUSED,
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int max_copies = 0;
|
||||
if (n_repeating > 0)
|
||||
{
|
||||
unsigned int base_glyphs = n_fixed + n_repeating;
|
||||
if (base_glyphs < STCH_MAX_GLYPHS)
|
||||
max_copies = (STCH_MAX_GLYPHS - base_glyphs) / n_repeating;
|
||||
}
|
||||
n_copies = hb_min (n_copies, max_copies);
|
||||
|
||||
if (step == MEASURE)
|
||||
{
|
||||
extra_glyphs_needed += n_copies * n_repeating;
|
||||
DEBUG_MSG (ARABIC, nullptr, "will add extra %d copies of repeating tiles", n_copies);
|
||||
unsigned int added_glyphs = 0;
|
||||
if (unlikely (hb_unsigned_mul_overflows (n_copies, n_repeating, &added_glyphs) ||
|
||||
hb_unsigned_add_overflows (extra_glyphs_needed, added_glyphs, &extra_glyphs_needed)))
|
||||
break;
|
||||
DEBUG_MSG (ARABIC, nullptr, "will add extra %u copies of repeating tiles", n_copies);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -629,7 +650,9 @@ apply_stch (const hb_ot_shape_plan_t *plan HB_UNUSED,
|
||||
|
||||
if (step == MEASURE)
|
||||
{
|
||||
if (unlikely (!buffer->ensure (count + extra_glyphs_needed)))
|
||||
unsigned int total_glyphs = 0;
|
||||
if (unlikely (hb_unsigned_add_overflows (count, extra_glyphs_needed, &total_glyphs) ||
|
||||
!buffer->ensure (total_glyphs)))
|
||||
break;
|
||||
}
|
||||
else
|
||||
|
||||
+2
-2
@@ -298,7 +298,7 @@ preprocess_text_hangul (const hb_ot_shape_plan_t *plan HB_UNUSED,
|
||||
end = start + 2;
|
||||
if (unlikely (!buffer->successful))
|
||||
break;
|
||||
buffer->merge_out_clusters (start, end);
|
||||
buffer->merge_out_grapheme_clusters (start, end);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -371,7 +371,7 @@ preprocess_text_hangul (const hb_ot_shape_plan_t *plan HB_UNUSED,
|
||||
if (i < end)
|
||||
info[i++].hangul_shaping_feature() = TJMO;
|
||||
|
||||
buffer->merge_out_clusters (start, end);
|
||||
buffer->merge_out_grapheme_clusters (start, end);
|
||||
continue;
|
||||
}
|
||||
else if ((!tindex && buffer->idx + 1 < count && isT (buffer->cur(+1).codepoint)))
|
||||
|
||||
+7
-7
@@ -53,7 +53,7 @@ enum indic_syllable_type_t {
|
||||
};
|
||||
|
||||
|
||||
#line 54 "hb-ot-shaper-indic-machine.hh"
|
||||
#line 57 "hb-ot-shaper-indic-machine.hh"
|
||||
#define indic_syllable_machine_ex_A 9u
|
||||
#define indic_syllable_machine_ex_C 1u
|
||||
#define indic_syllable_machine_ex_CM 16u
|
||||
@@ -77,7 +77,7 @@ enum indic_syllable_type_t {
|
||||
#define indic_syllable_machine_ex_ZWNJ 5u
|
||||
|
||||
|
||||
#line 76 "hb-ot-shaper-indic-machine.hh"
|
||||
#line 81 "hb-ot-shaper-indic-machine.hh"
|
||||
static const unsigned char _indic_syllable_machine_trans_keys[] = {
|
||||
8u, 57u, 4u, 57u, 5u, 57u, 5u, 57u, 13u, 13u, 4u, 57u, 4u, 57u, 4u, 57u,
|
||||
8u, 57u, 5u, 57u, 5u, 57u, 13u, 13u, 4u, 57u, 4u, 57u, 4u, 57u, 4u, 57u,
|
||||
@@ -1126,7 +1126,7 @@ find_syllables_indic (hb_buffer_t *buffer)
|
||||
int cs;
|
||||
hb_glyph_info_t *info = buffer->info;
|
||||
|
||||
#line 1119 "hb-ot-shaper-indic-machine.hh"
|
||||
#line 1130 "hb-ot-shaper-indic-machine.hh"
|
||||
{
|
||||
cs = indic_syllable_machine_start;
|
||||
ts = 0;
|
||||
@@ -1142,7 +1142,7 @@ find_syllables_indic (hb_buffer_t *buffer)
|
||||
|
||||
unsigned int syllable_serial = 1;
|
||||
|
||||
#line 1131 "hb-ot-shaper-indic-machine.hh"
|
||||
#line 1146 "hb-ot-shaper-indic-machine.hh"
|
||||
{
|
||||
int _slen;
|
||||
int _trans;
|
||||
@@ -1156,7 +1156,7 @@ _resume:
|
||||
#line 1 "NONE"
|
||||
{ts = p;}
|
||||
break;
|
||||
#line 1143 "hb-ot-shaper-indic-machine.hh"
|
||||
#line 1160 "hb-ot-shaper-indic-machine.hh"
|
||||
}
|
||||
|
||||
_keys = _indic_syllable_machine_trans_keys + (cs<<1);
|
||||
@@ -1268,7 +1268,7 @@ _eof_trans:
|
||||
#line 117 "hb-ot-shaper-indic-machine.rl"
|
||||
{act = 7;}
|
||||
break;
|
||||
#line 1232 "hb-ot-shaper-indic-machine.hh"
|
||||
#line 1272 "hb-ot-shaper-indic-machine.hh"
|
||||
}
|
||||
|
||||
_again:
|
||||
@@ -1277,7 +1277,7 @@ _again:
|
||||
#line 1 "NONE"
|
||||
{ts = 0;}
|
||||
break;
|
||||
#line 1239 "hb-ot-shaper-indic-machine.hh"
|
||||
#line 1281 "hb-ot-shaper-indic-machine.hh"
|
||||
}
|
||||
|
||||
if ( ++p != pe )
|
||||
|
||||
+102
-382
@@ -130,392 +130,112 @@ static_assert (OT_VPst == M_Cat(VPst), "");
|
||||
#define _(S,M) INDIC_COMBINE_CATEGORIES (_OT_##S, _POS_##M)
|
||||
|
||||
|
||||
static const uint16_t indic_table[] = {
|
||||
|
||||
|
||||
#define indic_offset_0x0028u 0
|
||||
|
||||
|
||||
/* Basic Latin */
|
||||
|
||||
/* 0028 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(GB,C), _(X,X), _(X,X),
|
||||
/* 0030 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C),
|
||||
/* 0038 */ _(GB,C), _(GB,C), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X),
|
||||
|
||||
#define indic_offset_0x00b0u 24
|
||||
|
||||
|
||||
/* Latin-1 Supplement */
|
||||
|
||||
/* 00B0 */ _(X,X), _(X,X),_(SP,SM),_(SP,SM), _(X,X), _(X,X), _(X,X), _(X,X),
|
||||
/* 00B8 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X),
|
||||
/* 00C0 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X),
|
||||
/* 00C8 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X),
|
||||
/* 00D0 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(GB,C),
|
||||
|
||||
#define indic_offset_0x0900u 64
|
||||
|
||||
|
||||
/* Devanagari */
|
||||
|
||||
/* 0900 */_(SM,SM),_(SM,SM),_(SM,SM),_(SM,SM), _(V,C), _(V,C), _(V,C), _(V,C),
|
||||
/* 0908 */ _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C),
|
||||
/* 0910 */ _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(C,C), _(C,C), _(C,C),
|
||||
/* 0918 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
|
||||
/* 0920 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
|
||||
/* 0928 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
|
||||
/* 0930 */ _(R,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
|
||||
/* 0938 */ _(C,C), _(C,C), _(M,AS), _(M,AS), _(N,X), _(S,SM), _(M,AS), _(M,LM),
|
||||
/* 0940 */ _(M,AS), _(M,AS), _(M,AS), _(M,AS), _(M,AS), _(M,AS), _(M,AS), _(M,AS),
|
||||
/* 0948 */ _(M,AS), _(M,AS), _(M,AS), _(M,AS), _(M,AS), _(H,B), _(M,LM), _(M,AS),
|
||||
/* 0950 */ _(X,X), _(A,SM), _(A,SM),_(SM,SM),_(SM,SM), _(M,AS), _(M,AS), _(M,AS),
|
||||
/* 0958 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
|
||||
/* 0960 */ _(V,C), _(V,C), _(M,AS), _(M,AS), _(X,X), _(X,X), _(GB,C), _(GB,C),
|
||||
/* 0968 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C),
|
||||
/* 0970 */ _(X,X), _(X,X), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C),
|
||||
/* 0978 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
|
||||
|
||||
/* Bengali */
|
||||
|
||||
/* 0980 */ _(GB,C),_(SM,SM),_(SM,SM),_(SM,SM), _(X,X), _(V,C), _(V,C), _(V,C),
|
||||
/* 0988 */ _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(X,X), _(X,X), _(V,C),
|
||||
/* 0990 */ _(V,C), _(X,X), _(X,X), _(V,C), _(V,C), _(C,C), _(C,C), _(C,C),
|
||||
/* 0998 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
|
||||
/* 09A0 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
|
||||
/* 09A8 */ _(C,C), _(X,X), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
|
||||
/* 09B0 */ _(R,C), _(X,X), _(C,C), _(X,X), _(X,X), _(X,X), _(C,C), _(C,C),
|
||||
/* 09B8 */ _(C,C), _(C,C), _(X,X), _(X,X), _(N,X), _(S,SM), _(M,AP), _(M,LM),
|
||||
/* 09C0 */ _(M,AP), _(M,AS), _(M,AS), _(M,AS), _(M,AS), _(X,X), _(X,X), _(M,LM),
|
||||
/* 09C8 */ _(M,LM), _(X,X), _(X,X), _(M,AP), _(M,AP), _(H,B), _(C,C), _(X,X),
|
||||
/* 09D0 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(M,AP),
|
||||
/* 09D8 */ _(X,X), _(X,X), _(X,X), _(X,X), _(C,C), _(C,C), _(X,X), _(C,C),
|
||||
/* 09E0 */ _(V,C), _(V,C), _(M,AS), _(M,AS), _(X,X), _(X,X), _(GB,C), _(GB,C),
|
||||
/* 09E8 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C),
|
||||
/* 09F0 */ _(R,C), _(C,C), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X),
|
||||
/* 09F8 */ _(X,X), _(X,X), _(X,X), _(X,X), _(GB,C), _(X,X),_(SM,SM), _(X,X),
|
||||
|
||||
/* Gurmukhi */
|
||||
|
||||
/* 0A00 */ _(X,X),_(SM,SM),_(SM,SM),_(SM,SM), _(X,X), _(V,C), _(V,C), _(V,C),
|
||||
/* 0A08 */ _(V,C), _(V,C), _(V,C), _(X,X), _(X,X), _(X,X), _(X,X), _(V,C),
|
||||
/* 0A10 */ _(V,C), _(X,X), _(X,X), _(V,C), _(V,C), _(C,C), _(C,C), _(C,C),
|
||||
/* 0A18 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
|
||||
/* 0A20 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
|
||||
/* 0A28 */ _(C,C), _(X,X), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
|
||||
/* 0A30 */ _(R,C), _(X,X), _(C,C), _(C,C), _(X,X), _(C,C), _(C,C), _(X,X),
|
||||
/* 0A38 */ _(C,C), _(C,C), _(X,X), _(X,X), _(N,X), _(X,X), _(M,AP), _(M,LM),
|
||||
/* 0A40 */_(MP,AP), _(M,AP), _(M,AP), _(X,X), _(X,X), _(X,X), _(X,X), _(M,AP),
|
||||
/* 0A48 */ _(M,AP), _(X,X), _(X,X), _(M,AP), _(M,AP), _(H,B), _(X,X), _(X,X),
|
||||
/* 0A50 */ _(X,X), _(M,B), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X),
|
||||
/* 0A58 */ _(X,X), _(C,C), _(C,C), _(C,C), _(C,C), _(X,X), _(C,C), _(X,X),
|
||||
/* 0A60 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(GB,C), _(GB,C),
|
||||
/* 0A68 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C),
|
||||
/* 0A70 */_(SM,SM),_(SM,SM), _(C,C), _(C,C), _(X,X), _(CM,C), _(X,X), _(X,X),
|
||||
/* 0A78 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X),
|
||||
|
||||
/* Gujarati */
|
||||
|
||||
/* 0A80 */ _(X,X),_(SM,SM),_(SM,SM),_(SM,SM), _(X,X), _(V,C), _(V,C), _(V,C),
|
||||
/* 0A88 */ _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(X,X), _(V,C),
|
||||
/* 0A90 */ _(V,C), _(V,C), _(X,X), _(V,C), _(V,C), _(C,C), _(C,C), _(C,C),
|
||||
/* 0A98 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
|
||||
/* 0AA0 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
|
||||
/* 0AA8 */ _(C,C), _(X,X), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
|
||||
/* 0AB0 */ _(R,C), _(X,X), _(C,C), _(C,C), _(X,X), _(C,C), _(C,C), _(C,C),
|
||||
/* 0AB8 */ _(C,C), _(C,C), _(X,X), _(X,X), _(N,X), _(S,SM), _(M,AP), _(M,LM),
|
||||
/* 0AC0 */ _(M,AP), _(M,AP), _(M,AP), _(M,AP), _(M,AP), _(M,AS), _(X,X), _(M,AS),
|
||||
/* 0AC8 */ _(M,AS), _(M,AP), _(X,X), _(M,AP), _(M,AP), _(H,B), _(X,X), _(X,X),
|
||||
/* 0AD0 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X),
|
||||
/* 0AD8 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X),
|
||||
/* 0AE0 */ _(V,C), _(V,C), _(M,AP), _(M,AP), _(X,X), _(X,X), _(GB,C), _(GB,C),
|
||||
/* 0AE8 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C),
|
||||
/* 0AF0 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X),
|
||||
/* 0AF8 */ _(X,X), _(C,C), _(A,SM), _(N,X), _(A,SM), _(N,X), _(N,X), _(N,X),
|
||||
|
||||
/* Oriya */
|
||||
|
||||
/* 0B00 */ _(X,X),_(SM,BS),_(SM,SM),_(SM,SM), _(X,X), _(V,C), _(V,C), _(V,C),
|
||||
/* 0B08 */ _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(X,X), _(X,X), _(V,C),
|
||||
/* 0B10 */ _(V,C), _(X,X), _(X,X), _(V,C), _(V,C), _(C,C), _(C,C), _(C,C),
|
||||
/* 0B18 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
|
||||
/* 0B20 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
|
||||
/* 0B28 */ _(C,C), _(X,X), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
|
||||
/* 0B30 */ _(R,C), _(X,X), _(C,C), _(C,C), _(X,X), _(C,C), _(C,C), _(C,C),
|
||||
/* 0B38 */ _(C,C), _(C,C), _(X,X), _(X,X), _(N,X), _(S,SM), _(M,AP), _(M,A),
|
||||
/* 0B40 */ _(M,AP), _(M,AS), _(M,AS), _(M,AS), _(M,AS), _(X,X), _(X,X), _(M,LM),
|
||||
/* 0B48 */ _(M,A), _(X,X), _(X,X), _(M,AP), _(M,AP), _(H,B), _(X,X), _(X,X),
|
||||
/* 0B50 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(N,X), _(M,A), _(M,AP),
|
||||
/* 0B58 */ _(X,X), _(X,X), _(X,X), _(X,X), _(C,C), _(C,C), _(X,X), _(C,C),
|
||||
/* 0B60 */ _(V,C), _(V,C), _(M,AS), _(M,AS), _(X,X), _(X,X), _(GB,C), _(GB,C),
|
||||
/* 0B68 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C),
|
||||
/* 0B70 */ _(X,X), _(C,C), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X),
|
||||
/* 0B78 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X),
|
||||
|
||||
/* Tamil */
|
||||
|
||||
/* 0B80 */ _(X,X), _(X,X),_(SM,SM), _(X,X), _(X,X), _(V,C), _(V,C), _(V,C),
|
||||
/* 0B88 */ _(V,C), _(V,C), _(V,C), _(X,X), _(X,X), _(X,X), _(V,C), _(V,C),
|
||||
/* 0B90 */ _(V,C), _(X,X), _(V,C), _(V,C), _(V,C), _(C,C), _(X,X), _(X,X),
|
||||
/* 0B98 */ _(X,X), _(C,C), _(C,C), _(X,X), _(C,C), _(X,X), _(C,C), _(C,C),
|
||||
/* 0BA0 */ _(X,X), _(X,X), _(X,X), _(C,C), _(C,C), _(X,X), _(X,X), _(X,X),
|
||||
/* 0BA8 */ _(C,C), _(C,C), _(C,C), _(X,X), _(X,X), _(X,X), _(C,C), _(C,C),
|
||||
/* 0BB0 */ _(R,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
|
||||
/* 0BB8 */ _(C,C), _(C,C), _(X,X), _(X,X), _(X,X), _(X,X), _(M,AP), _(M,AP),
|
||||
/* 0BC0 */ _(M,AS), _(M,AP), _(M,AP), _(X,X), _(X,X), _(X,X), _(M,LM), _(M,LM),
|
||||
/* 0BC8 */ _(M,LM), _(X,X), _(M,AP), _(M,AP), _(M,AP), _(H,T), _(X,X), _(X,X),
|
||||
/* 0BD0 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(M,AP),
|
||||
/* 0BD8 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X),
|
||||
/* 0BE0 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(GB,C), _(GB,C),
|
||||
/* 0BE8 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C),
|
||||
/* 0BF0 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X),
|
||||
/* 0BF8 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X),
|
||||
|
||||
/* Telugu */
|
||||
|
||||
/* 0C00 */_(SM,SM),_(SM,SM),_(SM,SM),_(SM,SM),_(SM,SM), _(V,C), _(V,C), _(V,C),
|
||||
/* 0C08 */ _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(X,X), _(V,C), _(V,C),
|
||||
/* 0C10 */ _(V,C), _(X,X), _(V,C), _(V,C), _(V,C), _(C,C), _(C,C), _(C,C),
|
||||
/* 0C18 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
|
||||
/* 0C20 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
|
||||
/* 0C28 */ _(C,C), _(X,X), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
|
||||
/* 0C30 */ _(R,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
|
||||
/* 0C38 */ _(C,C), _(C,C), _(X,X), _(X,X), _(N,X), _(S,SM), _(M,BS), _(M,BS),
|
||||
/* 0C40 */ _(M,BS), _(M,BS), _(M,BS), _(M,AS), _(M,AS), _(X,X), _(M,BS), _(M,BS),
|
||||
/* 0C48 */ _(M,BS), _(X,X), _(M,BS), _(M,BS), _(M,BS), _(H,T), _(X,X), _(X,X),
|
||||
/* 0C50 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(M,BS), _(M,BS), _(X,X),
|
||||
/* 0C58 */ _(C,C), _(C,C), _(C,C), _(X,X), _(X,X), _(C,C), _(X,X), _(X,X),
|
||||
/* 0C60 */ _(V,C), _(V,C), _(M,BS), _(M,BS), _(X,X), _(X,X), _(GB,C), _(GB,C),
|
||||
/* 0C68 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C),
|
||||
/* 0C70 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X),
|
||||
/* 0C78 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X),
|
||||
|
||||
/* Kannada */
|
||||
|
||||
/* 0C80 */ _(GB,C),_(SM,SM),_(SM,SM),_(SM,SM), _(X,X), _(V,C), _(V,C), _(V,C),
|
||||
/* 0C88 */ _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(X,X), _(V,C), _(V,C),
|
||||
/* 0C90 */ _(V,C), _(X,X), _(V,C), _(V,C), _(V,C), _(C,C), _(C,C), _(C,C),
|
||||
/* 0C98 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
|
||||
/* 0CA0 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
|
||||
/* 0CA8 */ _(C,C), _(X,X), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
|
||||
/* 0CB0 */ _(R,C), _(C,C), _(C,C), _(C,C), _(X,X), _(C,C), _(C,C), _(C,C),
|
||||
/* 0CB8 */ _(C,C), _(C,C), _(X,X), _(X,X), _(N,X), _(S,SM), _(M,BS), _(M,BS),
|
||||
/* 0CC0 */ _(M,BS), _(M,BS), _(M,BS), _(M,AS), _(M,AS), _(X,X), _(M,BS), _(M,AS),
|
||||
/* 0CC8 */ _(M,AS), _(X,X), _(M,AS), _(M,AS), _(M,BS), _(H,T), _(X,X), _(X,X),
|
||||
/* 0CD0 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(M,AS), _(M,AS), _(X,X),
|
||||
/* 0CD8 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(C,C), _(C,C), _(X,X),
|
||||
/* 0CE0 */ _(V,C), _(V,C), _(M,BS), _(M,BS), _(X,X), _(X,X), _(GB,C), _(GB,C),
|
||||
/* 0CE8 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C),
|
||||
/* 0CF0 */ _(X,X), _(CS,C), _(CS,C),_(SM,SM), _(X,X), _(X,X), _(X,X), _(X,X),
|
||||
/* 0CF8 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X),
|
||||
|
||||
/* Malayalam */
|
||||
|
||||
/* 0D00 */_(SM,SM),_(SM,SM),_(SM,SM),_(SM,SM), _(GB,C), _(V,C), _(V,C), _(V,C),
|
||||
/* 0D08 */ _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(X,X), _(V,C), _(V,C),
|
||||
/* 0D10 */ _(V,C), _(X,X), _(V,C), _(V,C), _(V,C), _(C,C), _(C,C), _(C,C),
|
||||
/* 0D18 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
|
||||
/* 0D20 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
|
||||
/* 0D28 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
|
||||
/* 0D30 */ _(R,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
|
||||
/* 0D38 */ _(C,C), _(C,C), _(C,C), _(M,AS), _(M,AS), _(S,SM), _(M,AP), _(M,AP),
|
||||
/* 0D40 */ _(M,AP), _(M,AP), _(M,AP), _(M,AP), _(M,AP), _(X,X), _(M,LM), _(M,LM),
|
||||
/* 0D48 */ _(M,LM), _(X,X), _(M,AP), _(M,AP), _(M,AP), _(H,T), _(Rf,X), _(X,X),
|
||||
/* 0D50 */ _(X,X), _(X,X), _(X,X), _(X,X), _(C,C), _(C,C), _(C,C), _(M,AP),
|
||||
/* 0D58 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(V,C),
|
||||
/* 0D60 */ _(V,C), _(V,C), _(M,AP), _(M,AP), _(X,X), _(X,X), _(GB,C), _(GB,C),
|
||||
/* 0D68 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C),
|
||||
/* 0D70 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X),
|
||||
/* 0D78 */ _(X,X), _(X,X), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
|
||||
|
||||
#define indic_offset_0x1000u 1216
|
||||
|
||||
|
||||
/* Myanmar */
|
||||
|
||||
/* 1000 */ _(C,C), _(C,C), _(C,C), _(C,C), _(R,C), _(C,C), _(C,C), _(C,C),
|
||||
/* 1008 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
|
||||
/* 1010 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
|
||||
/* 1018 */ _(C,C), _(C,C), _(C,C), _(R,C), _(C,C), _(C,C), _(C,C), _(C,C),
|
||||
/* 1020 */ _(C,C), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C),
|
||||
/* 1028 */ _(V,C), _(V,C), _(V,C), _(VR,R), _(VR,R), _(VA,T), _(VA,T), _(VB,B),
|
||||
/* 1030 */ _(VB,B), _(VL,L), _(A,SM), _(VA,T), _(VA,T), _(VA,T), _(A,SM), _(N,X),
|
||||
/* 1038 */_(SM,SM), _(H,X), _(As,X), _(MY,X), _(MR,X), _(MW,X), _(MH,X), _(C,C),
|
||||
/* 1040 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C),
|
||||
/* 1048 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(X,X), _(X,X), _(C,C), _(X,X),
|
||||
/* 1050 */ _(C,C), _(C,C), _(V,C), _(V,C), _(V,C), _(V,C), _(VR,R), _(VR,R),
|
||||
/* 1058 */ _(VB,B), _(VB,B), _(R,C), _(C,C), _(C,C), _(C,C), _(MY,X), _(MY,X),
|
||||
/* 1060 */ _(ML,X), _(C,C), _(VR,R), _(PT,X), _(PT,X), _(C,C), _(C,C), _(VR,R),
|
||||
/* 1068 */ _(VR,R), _(PT,X), _(PT,X), _(PT,X), _(PT,X), _(PT,X), _(C,C), _(C,C),
|
||||
/* 1070 */ _(C,C), _(VA,T), _(VA,T), _(VA,T), _(VA,T), _(C,C), _(C,C), _(C,C),
|
||||
/* 1078 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
|
||||
/* 1080 */ _(C,C), _(C,C), _(MW,X), _(VR,R), _(VL,L), _(VA,T), _(VA,T),_(SM,SM),
|
||||
/* 1088 */_(SM,SM),_(SM,SM),_(SM,SM),_(SM,SM),_(SM,SM),_(SM,SM), _(C,C),_(SM,SM),
|
||||
/* 1090 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C),
|
||||
/* 1098 */ _(GB,C), _(GB,C),_(SM,SM),_(SM,SM),_(SM,SM), _(VA,T), _(X,X), _(X,X),
|
||||
|
||||
#define indic_offset_0x1780u 1376
|
||||
|
||||
|
||||
/* Khmer */
|
||||
|
||||
/* 1780 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
|
||||
/* 1788 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
|
||||
/* 1790 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
|
||||
/* 1798 */ _(C,C), _(C,C), _(R,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
|
||||
/* 17A0 */ _(C,C), _(C,C), _(C,C), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C),
|
||||
/* 17A8 */ _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C),
|
||||
/* 17B0 */ _(V,C), _(V,C), _(V,C), _(V,C), _(X,X), _(X,X), _(VR,R), _(VA,T),
|
||||
/* 17B8 */ _(VA,T), _(VA,T), _(VA,T), _(VB,B), _(VB,B), _(VB,B), _(VA,T), _(VR,R),
|
||||
/* 17C0 */ _(VR,R), _(VL,L), _(VL,L), _(VL,L), _(VR,R), _(VR,R), _(Xg,X), _(Yg,X),
|
||||
/* 17C8 */ _(Yg,X), _(Rt,X), _(Rt,X), _(Xg,X), _(Rt,X), _(Xg,X), _(Xg,X), _(Xg,X),
|
||||
/* 17D0 */ _(Xg,X), _(Xg,X), _(H,X), _(Yg,X), _(X,X), _(X,X), _(X,X), _(X,X),
|
||||
/* 17D8 */ _(X,X), _(GB,C), _(X,X), _(X,X), _(S,SM), _(Yg,X), _(X,X), _(X,X),
|
||||
/* 17E0 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C),
|
||||
/* 17E8 */ _(GB,C), _(GB,C), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X),
|
||||
|
||||
#define indic_offset_0x1cd0u 1488
|
||||
|
||||
|
||||
/* Vedic Extensions */
|
||||
|
||||
/* 1CD0 */ _(A,SM), _(A,SM), _(A,SM), _(X,X), _(A,SM), _(A,SM), _(A,SM), _(A,SM),
|
||||
/* 1CD8 */ _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM),
|
||||
/* 1CE0 */ _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM),
|
||||
/* 1CE8 */ _(A,SM), _(S,SM), _(S,SM), _(S,SM), _(S,SM), _(A,SM), _(S,SM), _(S,SM),
|
||||
/* 1CF0 */ _(S,SM), _(S,SM), _(C,C), _(C,C), _(A,SM), _(C,C), _(C,C), _(A,SM),
|
||||
/* 1CF8 */ _(A,SM), _(A,SM), _(GB,C), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X),
|
||||
|
||||
#define indic_offset_0x2008u 1536
|
||||
|
||||
|
||||
/* General Punctuation */
|
||||
|
||||
/* 2008 */ _(X,X), _(X,X), _(X,X), _(X,X),_(ZWNJ,X),_(ZWJ,X), _(X,X), _(X,X),
|
||||
/* 2010 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(X,X), _(X,X),
|
||||
/* 2018 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X),
|
||||
/* 2020 */ _(X,X), _(X,X), _(GB,C), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X),
|
||||
|
||||
#define indic_offset_0x2070u 1568
|
||||
|
||||
|
||||
/* Superscripts and Subscripts */
|
||||
|
||||
/* 2070 */ _(X,X), _(X,X), _(X,X), _(X,X),_(SP,SM), _(X,X), _(X,X), _(X,X),
|
||||
/* 2078 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X),
|
||||
/* 2080 */ _(X,X), _(X,X),_(SP,SM),_(SP,SM),_(SP,SM), _(X,X), _(X,X), _(X,X),
|
||||
|
||||
#define indic_offset_0x25f8u 1592
|
||||
|
||||
|
||||
/* Geometric Shapes */
|
||||
|
||||
/* 25F8 */ _(X,X), _(X,X), _(X,X), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(X,X),
|
||||
|
||||
#define indic_offset_0xa8e0u 1600
|
||||
|
||||
|
||||
/* Devanagari Extended */
|
||||
|
||||
/* A8E0 */ _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM),
|
||||
/* A8E8 */ _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM),
|
||||
/* A8F0 */ _(A,SM), _(A,SM), _(S,SM), _(S,SM), _(S,SM), _(S,SM), _(S,SM), _(S,SM),
|
||||
/* A8F8 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(V,C), _(M,AS),
|
||||
|
||||
#define indic_offset_0xa9e0u 1632
|
||||
|
||||
|
||||
/* Myanmar Extended-B */
|
||||
|
||||
/* A9E0 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(VA,T), _(X,X), _(C,C),
|
||||
/* A9E8 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
|
||||
/* A9F0 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C),
|
||||
/* A9F8 */ _(GB,C), _(GB,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(X,X),
|
||||
|
||||
#define indic_offset_0xaa60u 1664
|
||||
|
||||
|
||||
/* Myanmar Extended-A */
|
||||
|
||||
/* AA60 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
|
||||
/* AA68 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
|
||||
/* AA70 */ _(X,X), _(C,C), _(C,C), _(C,C), _(GB,C), _(GB,C), _(GB,C), _(X,X),
|
||||
/* AA78 */ _(X,X), _(X,X), _(C,C), _(PT,X), _(N,X), _(N,X), _(C,C), _(C,C),
|
||||
|
||||
#define indic_offset_0xfe00u 1696
|
||||
|
||||
|
||||
/* Variation Selectors */
|
||||
|
||||
/* FE00 */ _(VS,X), _(VS,X), _(VS,X), _(VS,X), _(VS,X), _(VS,X), _(VS,X), _(VS,X),
|
||||
/* FE08 */ _(VS,X), _(VS,X), _(VS,X), _(VS,X), _(VS,X), _(VS,X), _(VS,X), _(VS,X),
|
||||
|
||||
#define indic_offset_0x11300u 1712
|
||||
|
||||
|
||||
/* Grantha */
|
||||
|
||||
/* 11300 */ _(X,X),_(SM,SM),_(SM,SM),_(SM,SM), _(X,X), _(X,X), _(X,X), _(X,X),
|
||||
|
||||
#define indic_offset_0x11338u 1720
|
||||
|
||||
/* 11338 */ _(X,X), _(X,X), _(X,X), _(N,X), _(N,X), _(X,X), _(X,X), _(X,X),
|
||||
|
||||
#define indic_offset_0x116d0u 1728
|
||||
|
||||
|
||||
/* Myanmar Extended-C */
|
||||
|
||||
/* 116D0 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C),
|
||||
/* 116D8 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C),
|
||||
/* 116E0 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(X,X), _(X,X), _(X,X), _(X,X),
|
||||
|
||||
}; /* Table items: 1752; occupancy: 71% */
|
||||
#include <stdint.h>
|
||||
|
||||
static const uint16_t _hb_indic_values[42]=
|
||||
{
|
||||
_(A,SM), _(As,X), _(C,C), _(CM,C), _(CS,C), _(DC,C), _(GB,C), _(H,B),
|
||||
_(H,T), _(H,X), _(M,A), _(M,AP), _(M,AS), _(M,B), _(M,BS), _(M,LM),
|
||||
_(MH,X), _(ML,X), _(MP,AP), _(MR,X), _(MW,X), _(MY,X), _(N,X), _(PT,X),
|
||||
_(R,C), _(Rf,X), _(Rt,X), _(S,SM), _(SM,BS), _(SM,SM), _(SP,SM), _(V,C),
|
||||
_(VA,T), _(VB,B), _(VL,L), _(VR,R), _(VS,X), _(X,X), _(Xg,X), _(Yg,X),
|
||||
_(ZWJ,X),_(ZWNJ,X),
|
||||
};
|
||||
static const uint8_t _hb_indic_u8[1220]=
|
||||
{
|
||||
1, 0, 50, 4, 5, 96, 0, 7, 8, 9, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,186, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,192,
|
||||
0, 0, 0, 0,208,224, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
|
||||
2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5, 6, 7, 8, 9,
|
||||
10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 0, 0, 22, 23,
|
||||
24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25, 26, 0, 0,
|
||||
0, 27, 0, 0, 0, 0, 28, 29, 30, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 31, 0, 0, 0, 32, 0, 0, 0, 33, 0, 34,
|
||||
0, 0, 0, 0, 0, 0, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 36, 0, 0, 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 25, 2, 10, 0, 0, 0, 0, 26, 0,
|
||||
27, 0, 0, 0, 28, 0, 0, 0, 0, 0, 29, 11, 30, 1, 1, 1,
|
||||
4, 31, 32, 33, 34, 1, 6, 2, 35, 1, 12, 13, 7, 1, 1, 3,
|
||||
36, 14, 15, 37, 16, 17, 6, 2, 38, 39, 18, 40, 7, 1, 1, 3,
|
||||
41, 42, 43, 44, 45, 46, 19, 2, 47, 0, 18, 48, 49, 1, 1, 3,
|
||||
20, 14, 50, 51, 0, 0, 21, 2, 0, 52, 53, 13, 7, 1, 1, 3,
|
||||
20, 54, 15, 55, 56, 17, 6, 2, 57, 0, 58, 59, 60, 61, 62, 63,
|
||||
4, 64, 65, 66, 16, 0, 19, 2, 0, 0, 67, 8, 9, 1, 1, 3,
|
||||
4, 22, 68, 69, 70, 71, 23, 2, 0, 0, 12, 8, 9, 1, 1, 3,
|
||||
72, 22, 73, 74, 75, 76, 23, 2, 77, 0, 78, 8, 9, 1, 1, 1,
|
||||
4, 79, 80, 81, 82, 83, 21, 2, 0, 84, 85, 1, 1, 86, 87, 88,
|
||||
89, 90, 2, 91, 92, 93, 94, 95, 96, 1, 97, 98, 2, 99, 0, 0,
|
||||
0, 0, 1, 1, 1,100,101, 11,102,103,104,105,106,107, 2, 10,
|
||||
0, 0, 0, 0,108, 5, 5,109,110,111, 0,112,113, 0,114, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,115, 0,116, 0, 0, 0, 0, 0,
|
||||
0, 0, 0,117, 0, 0, 0, 0, 0,118, 0, 0, 0, 0, 5, 5,
|
||||
119,120, 0, 0, 0, 0,121, 1, 2,122, 0, 0, 0, 0, 1, 1,
|
||||
123,124, 24, 24, 0, 0, 0, 0, 0, 0,125, 0, 0, 0, 0, 0,
|
||||
0,126, 0, 0, 2, 2, 2, 10, 0, 0, 0, 0, 2, 2, 2, 2,
|
||||
4, 4, 4, 4, 10, 2, 2, 2, 26, 2, 2, 2, 8, 8, 8, 8,
|
||||
6, 18, 0, 4, 20, 14, 24, 2, 6, 6, 20, 6, 20, 6, 24, 2,
|
||||
4, 0, 0, 0, 6, 6, 6, 6, 66, 12, 14, 6, 6, 6, 20, 14,
|
||||
2, 0, 36, 50, 52, 18, 38, 68, 0, 0, 0, 32, 0, 0, 2, 16,
|
||||
56, 12, 14, 6, 0, 0, 0, 4, 44, 2, 16, 2, 6, 22, 0, 4,
|
||||
2, 0, 36, 28, 6, 28, 0, 4, 30, 30, 30, 30, 0, 0, 42, 0,
|
||||
34, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 42, 12, 12, 6, 6,
|
||||
6, 6, 24, 2, 2, 18, 36,112, 18, 18, 18, 18, 18, 18,114,116,
|
||||
118,120,122, 18, 0, 6, 6, 6, 44, 10, 0, 2, 54, 32, 46, 10,
|
||||
26, 0, 0, 0, 0, 0, 34, 70, 6, 20, 0, 14, 44, 2, 16, 10,
|
||||
2, 0, 72, 50,124, 48, 0, 32, 48, 32, 46, 0,126, 0, 0, 0,
|
||||
16, 2, 10, 10, 12, 2,128, 0, 6, 6, 6, 14, 6, 14, 24, 2,
|
||||
22, 22, 52, 74, 76, 32, 46, 0, 16, 58, 58, 78,130, 12, 14, 6,
|
||||
2, 0, 36,132,134, 32, 46, 0, 0, 0, 80,136, 16, 0, 0, 0,
|
||||
0, 70, 14, 6, 6, 20, 0, 6, 20, 6, 24, 0, 16, 10, 10, 2,
|
||||
0, 16, 10, 0, 2, 10, 0, 2, 2, 0, 0, 22, 76, 48, 0, 82,
|
||||
54, 22, 84, 0, 12, 12,138, 6, 28, 60, 38, 28, 86, 28, 88, 0,
|
||||
0, 0,140, 86, 2, 10, 16, 0, 26, 2, 16, 2, 28, 60, 38, 60,
|
||||
38, 18, 88, 0, 0, 0, 74, 38, 0, 0, 16, 10,142,144, 0, 0,
|
||||
12, 12,146, 6, 2,148,150, 22, 22, 22, 48, 82, 54, 22, 84,152,
|
||||
0, 0, 2,154, 0, 0, 0, 14, 0, 2, 2, 2, 2, 2, 26, 2,
|
||||
2,156, 2, 2, 90, 6, 6, 6, 6,158, 92, 94,160,162, 62, 58,
|
||||
164,166,168,170, 4, 4, 0, 10, 2, 6, 6, 96, 98, 26, 2,172,
|
||||
174,100,176,178,100,102,102, 2,104, 62,180, 2, 2,182,184,186,
|
||||
12, 12, 12,188, 4, 12,190, 0, 2, 26, 2, 2, 2, 90, 6, 6,
|
||||
6, 6, 0, 92, 62, 94, 98,192,194,196, 96,198,200,106,106,108,
|
||||
108,202, 0, 0, 42, 0,204, 0, 8,206, 8, 8,208, 40,210, 40,
|
||||
40, 2,212,214, 8, 34, 0, 0, 0, 0,216, 0, 4, 4, 4, 0,
|
||||
0, 34, 0, 0, 0, 0,110, 0, 0, 64,110, 0, 0, 0,218, 0,
|
||||
0, 42, 4, 34, 8, 40, 40, 40, 0, 0, 0,220, 2, 2,104, 16,
|
||||
4, 2, 2, 10, 16, 2, 4, 34, 0,222, 78, 2, 56, 12, 0, 0,
|
||||
0, 80, 72, 0, 37, 37, 2, 2, 6, 6, 31, 31, 0, 0, 2, 37,
|
||||
29, 29, 37, 31, 37, 2, 12, 12, 31, 37, 11, 11, 31, 2, 24, 2,
|
||||
14, 14, 36, 36, 37, 11, 6, 37, 22, 27, 12, 37, 27, 27, 37, 6,
|
||||
24, 37, 11, 7, 11, 37, 11, 15, 11, 12, 15, 37, 37, 29, 0, 22,
|
||||
14, 12, 32, 32, 30, 30, 6, 29, 37, 15, 29, 37, 22, 37, 37, 12,
|
||||
12, 11, 22, 22, 37, 22, 15, 15, 11, 8, 14, 37, 14, 8, 2, 31,
|
||||
35, 32, 32, 33, 35, 35, 33, 33, 35, 23, 23, 23, 2, 32, 26, 38,
|
||||
38, 38, 30, 37, 12, 15, 12, 7, 15, 12, 37, 0, 0, 29, 29, 12,
|
||||
18, 11, 37, 13, 37, 3, 37, 28, 11, 10, 10, 37, 10, 11, 29, 31,
|
||||
37, 14, 37, 4, 4, 29, 6, 31, 2, 12, 12, 27, 25, 37, 2, 11,
|
||||
2, 24, 31, 35, 33, 34, 0, 32, 29, 9, 1, 21, 19, 20, 16, 2,
|
||||
21, 21, 17, 2, 23, 2, 2, 35, 32, 2, 20, 35, 34, 32, 32, 29,
|
||||
2, 29, 29, 32, 32, 35, 35, 34, 34, 34, 38, 39, 39, 26, 9, 39,
|
||||
27, 39, 0, 37, 0, 27, 27, 0, 0, 2, 2, 0, 41, 40, 5, 37,
|
||||
31, 12, 2, 23,
|
||||
};
|
||||
|
||||
static inline uint8_t _hb_indic_b4 (const uint8_t* a, unsigned i)
|
||||
{
|
||||
return (a[i>>1]>>((i&1)<<2))&15;
|
||||
}
|
||||
static inline uint8_t _hb_indic_get_categories_index (unsigned u)
|
||||
{
|
||||
/* packtab: [2^4,2^3,2^3,2^2,2^1] */
|
||||
return u<71396u ? (uint8_t)(_hb_indic_u8[996u+_hb_indic_u8[488u+((_hb_indic_u8[186u+((_hb_indic_u8[70u+((_hb_indic_b4(_hb_indic_u8,((((((((u)>>1))>>2))>>3))>>3)))<<3)+((((((((u)>>1))>>2))>>3))&7)])<<3)+((((((u)>>1))>>2))&7)])<<2)+((((u)>>1))&3)]+((u)&1)]) : 37;
|
||||
}
|
||||
|
||||
uint16_t
|
||||
hb_indic_get_categories (hb_codepoint_t u)
|
||||
{
|
||||
switch (u >> 12)
|
||||
{
|
||||
case 0x0u:
|
||||
if (unlikely (u == 0x00A0u)) return _(GB,C);
|
||||
if (hb_in_range<hb_codepoint_t> (u, 0x0028u, 0x003Fu)) return indic_table[u - 0x0028u + indic_offset_0x0028u];
|
||||
if (hb_in_range<hb_codepoint_t> (u, 0x00B0u, 0x00D7u)) return indic_table[u - 0x00B0u + indic_offset_0x00b0u];
|
||||
if (hb_in_range<hb_codepoint_t> (u, 0x0900u, 0x0D7Fu)) return indic_table[u - 0x0900u + indic_offset_0x0900u];
|
||||
break;
|
||||
|
||||
case 0x1u:
|
||||
if (hb_in_range<hb_codepoint_t> (u, 0x1000u, 0x109Fu)) return indic_table[u - 0x1000u + indic_offset_0x1000u];
|
||||
if (hb_in_range<hb_codepoint_t> (u, 0x1780u, 0x17EFu)) return indic_table[u - 0x1780u + indic_offset_0x1780u];
|
||||
if (hb_in_range<hb_codepoint_t> (u, 0x1CD0u, 0x1CFFu)) return indic_table[u - 0x1CD0u + indic_offset_0x1cd0u];
|
||||
break;
|
||||
|
||||
case 0x2u:
|
||||
if (unlikely (u == 0x25CCu)) return _(DC,C);
|
||||
if (hb_in_range<hb_codepoint_t> (u, 0x2008u, 0x2027u)) return indic_table[u - 0x2008u + indic_offset_0x2008u];
|
||||
if (hb_in_range<hb_codepoint_t> (u, 0x2070u, 0x2087u)) return indic_table[u - 0x2070u + indic_offset_0x2070u];
|
||||
if (hb_in_range<hb_codepoint_t> (u, 0x25F8u, 0x25FFu)) return indic_table[u - 0x25F8u + indic_offset_0x25f8u];
|
||||
break;
|
||||
|
||||
case 0xAu:
|
||||
if (hb_in_range<hb_codepoint_t> (u, 0xA8E0u, 0xA8FFu)) return indic_table[u - 0xA8E0u + indic_offset_0xa8e0u];
|
||||
if (hb_in_range<hb_codepoint_t> (u, 0xA9E0u, 0xA9FFu)) return indic_table[u - 0xA9E0u + indic_offset_0xa9e0u];
|
||||
if (hb_in_range<hb_codepoint_t> (u, 0xAA60u, 0xAA7Fu)) return indic_table[u - 0xAA60u + indic_offset_0xaa60u];
|
||||
break;
|
||||
|
||||
case 0xFu:
|
||||
if (hb_in_range<hb_codepoint_t> (u, 0xFE00u, 0xFE0Fu)) return indic_table[u - 0xFE00u + indic_offset_0xfe00u];
|
||||
break;
|
||||
|
||||
case 0x11u:
|
||||
if (hb_in_range<hb_codepoint_t> (u, 0x11300u, 0x11307u)) return indic_table[u - 0x11300u + indic_offset_0x11300u];
|
||||
if (hb_in_range<hb_codepoint_t> (u, 0x11338u, 0x1133Fu)) return indic_table[u - 0x11338u + indic_offset_0x11338u];
|
||||
if (hb_in_range<hb_codepoint_t> (u, 0x116D0u, 0x116E7u)) return indic_table[u - 0x116D0u + indic_offset_0x116d0u];
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return _(X,X);
|
||||
return _hb_indic_values[_hb_indic_get_categories_index (u)];
|
||||
}
|
||||
|
||||
#undef _
|
||||
|
||||
+7
-7
@@ -48,7 +48,7 @@ enum khmer_syllable_type_t {
|
||||
};
|
||||
|
||||
|
||||
#line 49 "hb-ot-shaper-khmer-machine.hh"
|
||||
#line 52 "hb-ot-shaper-khmer-machine.hh"
|
||||
#define khmer_syllable_machine_ex_C 1u
|
||||
#define khmer_syllable_machine_ex_DOTTEDCIRCLE 11u
|
||||
#define khmer_syllable_machine_ex_H 4u
|
||||
@@ -66,7 +66,7 @@ enum khmer_syllable_type_t {
|
||||
#define khmer_syllable_machine_ex_ZWNJ 5u
|
||||
|
||||
|
||||
#line 65 "hb-ot-shaper-khmer-machine.hh"
|
||||
#line 70 "hb-ot-shaper-khmer-machine.hh"
|
||||
static const unsigned char _khmer_syllable_machine_trans_keys[] = {
|
||||
5u, 26u, 5u, 26u, 1u, 15u, 5u, 26u, 5u, 26u, 5u, 26u, 5u, 26u, 5u, 26u,
|
||||
5u, 26u, 5u, 26u, 5u, 26u, 5u, 26u, 5u, 26u, 1u, 15u, 5u, 26u, 5u, 26u,
|
||||
@@ -294,7 +294,7 @@ find_syllables_khmer (hb_buffer_t *buffer)
|
||||
int cs;
|
||||
hb_glyph_info_t *info = buffer->info;
|
||||
|
||||
#line 287 "hb-ot-shaper-khmer-machine.hh"
|
||||
#line 298 "hb-ot-shaper-khmer-machine.hh"
|
||||
{
|
||||
cs = khmer_syllable_machine_start;
|
||||
ts = 0;
|
||||
@@ -310,7 +310,7 @@ find_syllables_khmer (hb_buffer_t *buffer)
|
||||
|
||||
unsigned int syllable_serial = 1;
|
||||
|
||||
#line 299 "hb-ot-shaper-khmer-machine.hh"
|
||||
#line 314 "hb-ot-shaper-khmer-machine.hh"
|
||||
{
|
||||
int _slen;
|
||||
int _trans;
|
||||
@@ -324,7 +324,7 @@ _resume:
|
||||
#line 1 "NONE"
|
||||
{ts = p;}
|
||||
break;
|
||||
#line 311 "hb-ot-shaper-khmer-machine.hh"
|
||||
#line 328 "hb-ot-shaper-khmer-machine.hh"
|
||||
}
|
||||
|
||||
_keys = _khmer_syllable_machine_trans_keys + (cs<<1);
|
||||
@@ -394,7 +394,7 @@ _eof_trans:
|
||||
#line 98 "hb-ot-shaper-khmer-machine.rl"
|
||||
{act = 3;}
|
||||
break;
|
||||
#line 368 "hb-ot-shaper-khmer-machine.hh"
|
||||
#line 398 "hb-ot-shaper-khmer-machine.hh"
|
||||
}
|
||||
|
||||
_again:
|
||||
@@ -403,7 +403,7 @@ _again:
|
||||
#line 1 "NONE"
|
||||
{ts = 0;}
|
||||
break;
|
||||
#line 375 "hb-ot-shaper-khmer-machine.hh"
|
||||
#line 407 "hb-ot-shaper-khmer-machine.hh"
|
||||
}
|
||||
|
||||
if ( ++p != pe )
|
||||
|
||||
+7
-7
@@ -50,7 +50,7 @@ enum myanmar_syllable_type_t {
|
||||
};
|
||||
|
||||
|
||||
#line 51 "hb-ot-shaper-myanmar-machine.hh"
|
||||
#line 54 "hb-ot-shaper-myanmar-machine.hh"
|
||||
#define myanmar_syllable_machine_ex_A 9u
|
||||
#define myanmar_syllable_machine_ex_As 32u
|
||||
#define myanmar_syllable_machine_ex_C 1u
|
||||
@@ -78,7 +78,7 @@ enum myanmar_syllable_type_t {
|
||||
#define myanmar_syllable_machine_ex_ZWNJ 5u
|
||||
|
||||
|
||||
#line 77 "hb-ot-shaper-myanmar-machine.hh"
|
||||
#line 82 "hb-ot-shaper-myanmar-machine.hh"
|
||||
static const unsigned char _myanmar_syllable_machine_trans_keys[] = {
|
||||
1u, 57u, 3u, 57u, 5u, 57u, 5u, 57u, 3u, 57u, 5u, 57u, 3u, 57u, 3u, 57u,
|
||||
3u, 57u, 3u, 57u, 3u, 57u, 5u, 57u, 1u, 15u, 3u, 57u, 3u, 57u, 3u, 57u,
|
||||
@@ -549,7 +549,7 @@ find_syllables_myanmar (hb_buffer_t *buffer)
|
||||
int cs;
|
||||
hb_glyph_info_t *info = buffer->info;
|
||||
|
||||
#line 542 "hb-ot-shaper-myanmar-machine.hh"
|
||||
#line 553 "hb-ot-shaper-myanmar-machine.hh"
|
||||
{
|
||||
cs = myanmar_syllable_machine_start;
|
||||
ts = 0;
|
||||
@@ -565,7 +565,7 @@ find_syllables_myanmar (hb_buffer_t *buffer)
|
||||
|
||||
unsigned int syllable_serial = 1;
|
||||
|
||||
#line 554 "hb-ot-shaper-myanmar-machine.hh"
|
||||
#line 569 "hb-ot-shaper-myanmar-machine.hh"
|
||||
{
|
||||
int _slen;
|
||||
int _trans;
|
||||
@@ -579,7 +579,7 @@ _resume:
|
||||
#line 1 "NONE"
|
||||
{ts = p;}
|
||||
break;
|
||||
#line 566 "hb-ot-shaper-myanmar-machine.hh"
|
||||
#line 583 "hb-ot-shaper-myanmar-machine.hh"
|
||||
}
|
||||
|
||||
_keys = _myanmar_syllable_machine_trans_keys + (cs<<1);
|
||||
@@ -649,7 +649,7 @@ _eof_trans:
|
||||
#line 113 "hb-ot-shaper-myanmar-machine.rl"
|
||||
{act = 3;}
|
||||
break;
|
||||
#line 623 "hb-ot-shaper-myanmar-machine.hh"
|
||||
#line 653 "hb-ot-shaper-myanmar-machine.hh"
|
||||
}
|
||||
|
||||
_again:
|
||||
@@ -658,7 +658,7 @@ _again:
|
||||
#line 1 "NONE"
|
||||
{ts = 0;}
|
||||
break;
|
||||
#line 630 "hb-ot-shaper-myanmar-machine.hh"
|
||||
#line 662 "hb-ot-shaper-myanmar-machine.hh"
|
||||
}
|
||||
|
||||
if ( ++p != pe )
|
||||
|
||||
+5
-7
@@ -356,13 +356,11 @@ preprocess_text_thai (const hb_ot_shape_plan_t *plan,
|
||||
sizeof (buffer->out_info[0]) * (end - start - 2));
|
||||
buffer->out_info[start] = t;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Since we decomposed, and NIKHAHIT is combining, merge clusters with the
|
||||
* previous cluster. */
|
||||
if (start)
|
||||
buffer->merge_out_clusters (start - 1, end);
|
||||
}
|
||||
|
||||
/* Since we decomposed, and NIKHAHIT is combining, merge clusters with the
|
||||
* previous cluster. */
|
||||
if (start)
|
||||
buffer->merge_out_grapheme_clusters (start - 1, end);
|
||||
}
|
||||
buffer->sync ();
|
||||
|
||||
|
||||
+7
-7
@@ -53,7 +53,7 @@ enum use_syllable_type_t {
|
||||
};
|
||||
|
||||
|
||||
#line 54 "hb-ot-shaper-use-machine.hh"
|
||||
#line 57 "hb-ot-shaper-use-machine.hh"
|
||||
#define use_syllable_machine_ex_B 1u
|
||||
#define use_syllable_machine_ex_CGJ 6u
|
||||
#define use_syllable_machine_ex_CMAbv 31u
|
||||
@@ -100,7 +100,7 @@ enum use_syllable_type_t {
|
||||
#define use_syllable_machine_ex_ZWNJ 14u
|
||||
|
||||
|
||||
#line 99 "hb-ot-shaper-use-machine.hh"
|
||||
#line 104 "hb-ot-shaper-use-machine.hh"
|
||||
static const unsigned char _use_syllable_machine_trans_keys[] = {
|
||||
49u, 51u, 0u, 56u, 11u, 56u, 11u, 56u, 1u, 53u, 14u, 48u, 14u, 47u, 14u, 47u,
|
||||
14u, 47u, 14u, 46u, 14u, 46u, 14u, 14u, 14u, 48u, 14u, 48u, 14u, 48u, 1u, 14u,
|
||||
@@ -929,7 +929,7 @@ find_syllables_use (hb_buffer_t *buffer)
|
||||
unsigned int act HB_UNUSED;
|
||||
int cs;
|
||||
|
||||
#line 922 "hb-ot-shaper-use-machine.hh"
|
||||
#line 933 "hb-ot-shaper-use-machine.hh"
|
||||
{
|
||||
cs = use_syllable_machine_start;
|
||||
ts = 0;
|
||||
@@ -942,7 +942,7 @@ find_syllables_use (hb_buffer_t *buffer)
|
||||
|
||||
unsigned int syllable_serial = 1;
|
||||
|
||||
#line 931 "hb-ot-shaper-use-machine.hh"
|
||||
#line 946 "hb-ot-shaper-use-machine.hh"
|
||||
{
|
||||
int _slen;
|
||||
int _trans;
|
||||
@@ -956,7 +956,7 @@ _resume:
|
||||
#line 1 "NONE"
|
||||
{ts = p;}
|
||||
break;
|
||||
#line 943 "hb-ot-shaper-use-machine.hh"
|
||||
#line 960 "hb-ot-shaper-use-machine.hh"
|
||||
}
|
||||
|
||||
_keys = _use_syllable_machine_trans_keys + (cs<<1);
|
||||
@@ -1078,7 +1078,7 @@ _eof_trans:
|
||||
#line 181 "hb-ot-shaper-use-machine.rl"
|
||||
{act = 9;}
|
||||
break;
|
||||
#line 1039 "hb-ot-shaper-use-machine.hh"
|
||||
#line 1082 "hb-ot-shaper-use-machine.hh"
|
||||
}
|
||||
|
||||
_again:
|
||||
@@ -1087,7 +1087,7 @@ _again:
|
||||
#line 1 "NONE"
|
||||
{ts = 0;}
|
||||
break;
|
||||
#line 1046 "hb-ot-shaper-use-machine.hh"
|
||||
#line 1091 "hb-ot-shaper-use-machine.hh"
|
||||
}
|
||||
|
||||
if ( ++p != pe )
|
||||
|
||||
+527
-525
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,7 @@
|
||||
/*
|
||||
* The following functions are generated by running:
|
||||
*
|
||||
* ./gen-vowel-constraints.py ms-use/IndicShapingInvalidCluster.txt Scripts.txt
|
||||
* gen-vowel-constraints.py ms-use/IndicShapingInvalidCluster.txt Scripts.txt
|
||||
*
|
||||
* on files with these headers:
|
||||
*
|
||||
|
||||
+598
-680
File diff suppressed because it is too large
Load Diff
Vendored
+126
-27
@@ -234,6 +234,29 @@ lang_matches (const char *lang_str,
|
||||
(lang_str[spec_len] == '\0' || lang_str[spec_len] == '-');
|
||||
}
|
||||
|
||||
static bool
|
||||
bfind_tag (const hb_tag_t *array,
|
||||
unsigned len,
|
||||
hb_tag_t key)
|
||||
{
|
||||
unsigned min = 0;
|
||||
unsigned max = len;
|
||||
|
||||
while (min < max)
|
||||
{
|
||||
unsigned mid = min + (max - min) / 2;
|
||||
hb_tag_t val = array[mid];
|
||||
if (key < val)
|
||||
max = mid;
|
||||
else if (key > val)
|
||||
min = mid + 1;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
struct LangTag
|
||||
{
|
||||
hb_tag_t language;
|
||||
@@ -247,6 +270,20 @@ struct LangTag
|
||||
{ return cmp (that->language); }
|
||||
};
|
||||
|
||||
struct LangTagRange
|
||||
{
|
||||
hb_tag_t language;
|
||||
uint16_t offset;
|
||||
uint8_t count;
|
||||
|
||||
int cmp (hb_tag_t a) const
|
||||
{
|
||||
return a < this->language ? -1 : a > this->language ? +1 : 0;
|
||||
}
|
||||
int cmp (const LangTagRange *that) const
|
||||
{ return cmp (that->language); }
|
||||
};
|
||||
|
||||
#include "hb-ot-tag-table.hh"
|
||||
|
||||
/* The corresponding languages IDs for the following IDs are unclear,
|
||||
@@ -307,46 +344,84 @@ hb_ot_tags_from_language (const char *lang_str,
|
||||
lang_str = s + 1;
|
||||
}
|
||||
#endif
|
||||
const LangTag *ot_languages = nullptr;
|
||||
unsigned ot_languages_len = 0;
|
||||
const char *dash = strchr (lang_str, '-');
|
||||
unsigned first_len = dash ? dash - lang_str : limit - lang_str;
|
||||
hb_tag_t lang_tag = hb_tag_from_string (lang_str, first_len);
|
||||
|
||||
if (first_len == 2)
|
||||
{
|
||||
ot_languages = ot_languages2;
|
||||
ot_languages_len = ARRAY_LENGTH (ot_languages2);
|
||||
static hb_atomic_t<unsigned> last_tag_idx_2 = 0; /* Poor man's cache. */
|
||||
unsigned tag_idx = last_tag_idx_2;
|
||||
|
||||
if (likely (tag_idx < ARRAY_LENGTH (ot_languages2) &&
|
||||
ot_languages2[tag_idx].language == lang_tag) ||
|
||||
hb_sorted_array (ot_languages2).bfind (lang_tag, &tag_idx))
|
||||
{
|
||||
last_tag_idx_2 = tag_idx;
|
||||
unsigned int i;
|
||||
while (tag_idx != 0 &&
|
||||
ot_languages2[tag_idx].language == ot_languages2[tag_idx - 1].language)
|
||||
tag_idx--;
|
||||
for (i = 0;
|
||||
i < *count &&
|
||||
tag_idx + i < ARRAY_LENGTH (ot_languages2) &&
|
||||
ot_languages2[tag_idx + i].tag != HB_TAG_NONE &&
|
||||
ot_languages2[tag_idx + i].language == ot_languages2[tag_idx].language;
|
||||
i++)
|
||||
tags[i] = ot_languages2[tag_idx + i].tag;
|
||||
*count = i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
#ifndef HB_NO_LANGUAGE_LONG
|
||||
else if (first_len == 3)
|
||||
{
|
||||
ot_languages = ot_languages3;
|
||||
ot_languages_len = ARRAY_LENGTH (ot_languages3);
|
||||
}
|
||||
#endif
|
||||
static hb_atomic_t<unsigned> last_tag_idx_3 = 0; /* Poor man's cache. */
|
||||
unsigned tag_idx = last_tag_idx_3;
|
||||
|
||||
hb_tag_t lang_tag = hb_tag_from_string (lang_str, first_len);
|
||||
if (likely (tag_idx < ARRAY_LENGTH (ot_languages3) &&
|
||||
ot_languages3[tag_idx].language == lang_tag) ||
|
||||
hb_sorted_array (ot_languages3).bfind (lang_tag, &tag_idx))
|
||||
{
|
||||
last_tag_idx_3 = tag_idx;
|
||||
if (*count)
|
||||
{
|
||||
tags[0] = ot_languages3[tag_idx].tag;
|
||||
*count = 1;
|
||||
}
|
||||
else
|
||||
*count = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
static hb_atomic_t<unsigned> last_tag_idx = 0; /* Poor man's cache. */
|
||||
unsigned tag_idx = last_tag_idx;
|
||||
static hb_atomic_t<unsigned> last_tag_idx_3_multi = 0; /* Poor man's cache. */
|
||||
unsigned multi_tag_idx = last_tag_idx_3_multi;
|
||||
|
||||
if (likely (tag_idx < ot_languages_len && ot_languages[tag_idx].language == lang_tag) ||
|
||||
hb_sorted_array (ot_languages, ot_languages_len).bfind (lang_tag, &tag_idx))
|
||||
{
|
||||
last_tag_idx = tag_idx;
|
||||
unsigned int i;
|
||||
while (tag_idx != 0 &&
|
||||
ot_languages[tag_idx].language == ot_languages[tag_idx - 1].language)
|
||||
tag_idx--;
|
||||
for (i = 0;
|
||||
i < *count &&
|
||||
tag_idx + i < ot_languages_len &&
|
||||
ot_languages[tag_idx + i].tag != HB_TAG_NONE &&
|
||||
ot_languages[tag_idx + i].language == ot_languages[tag_idx].language;
|
||||
i++)
|
||||
tags[i] = ot_languages[tag_idx + i].tag;
|
||||
*count = i;
|
||||
if (likely (multi_tag_idx < ARRAY_LENGTH (ot_languages3_multi) &&
|
||||
ot_languages3_multi[multi_tag_idx].language == lang_tag) ||
|
||||
hb_sorted_array (ot_languages3_multi).bfind (lang_tag, &multi_tag_idx))
|
||||
{
|
||||
last_tag_idx_3_multi = multi_tag_idx;
|
||||
const LangTagRange &range = ot_languages3_multi[multi_tag_idx];
|
||||
unsigned int i;
|
||||
for (i = 0; i < *count && i < range.count; i++)
|
||||
tags[i] = ot_languages3_multi_values[range.offset + i];
|
||||
*count = i;
|
||||
return;
|
||||
}
|
||||
|
||||
if (bfind_tag (ot_languages3_blocked, ARRAY_LENGTH (ot_languages3_blocked), lang_tag))
|
||||
{
|
||||
*count = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Assume it's ISO-639-3 and upper-case and use it. */
|
||||
tags[0] = lang_tag & ~0x20202000u;
|
||||
*count = 1;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef HB_NO_LANGUAGE_LONG
|
||||
@@ -525,6 +600,13 @@ hb_ot_tag_to_language (hb_tag_t tag)
|
||||
hb_tag_to_string (ot_languages3[i].language, buf);
|
||||
return hb_language_from_string (buf, 3);
|
||||
}
|
||||
for (i = 0; i < ARRAY_LENGTH (ot_languages3_multi); i++)
|
||||
for (unsigned int j = 0; j < ot_languages3_multi[i].count; j++)
|
||||
if (ot_languages3_multi_values[ot_languages3_multi[i].offset + j] == tag)
|
||||
{
|
||||
hb_tag_to_string (ot_languages3_multi[i].language, buf);
|
||||
return hb_language_from_string (buf, 3);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Return a custom language in the form of "x-hbot-AABBCCDD".
|
||||
@@ -642,6 +724,23 @@ test_langs_sorted ()
|
||||
abort();
|
||||
}
|
||||
}
|
||||
for (unsigned int i = 1; i < ARRAY_LENGTH (ot_languages3_multi); i++)
|
||||
{
|
||||
int c = ot_languages3_multi[i].cmp (&ot_languages3_multi[i - 1]);
|
||||
if (c > 0)
|
||||
{
|
||||
fprintf (stderr, "ot_languages3_multi not sorted at index %u: %08x %d %08x\n",
|
||||
i, ot_languages3_multi[i-1].language, c, ot_languages3_multi[i].language);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
for (unsigned int i = 1; i < ARRAY_LENGTH (ot_languages3_blocked); i++)
|
||||
if (ot_languages3_blocked[i] < ot_languages3_blocked[i - 1])
|
||||
{
|
||||
fprintf (stderr, "ot_languages3_blocked not sorted at index %u: %08x < %08x\n",
|
||||
i, ot_languages3_blocked[i], ot_languages3_blocked[i - 1]);
|
||||
abort();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
+42
-6
@@ -409,6 +409,13 @@ struct tuple_delta_t
|
||||
return;
|
||||
}
|
||||
|
||||
if (!axis_limit.is_point () &&
|
||||
!(-1.0 <= axis_limit.minimum &&
|
||||
axis_limit.minimum <= axis_limit.middle &&
|
||||
axis_limit.middle <= axis_limit.maximum &&
|
||||
axis_limit.maximum <= +1.0))
|
||||
return;
|
||||
|
||||
rebase_tent_result_t &solutions = scratch.first;
|
||||
rebase_tent (*tent, axis_limit, axis_triple_distances, solutions, scratch.second);
|
||||
for (unsigned i = 0; i < solutions.length; i++)
|
||||
@@ -1700,6 +1707,16 @@ struct item_variations_t
|
||||
const hb_map_t& get_varidx_map () const
|
||||
{ return varidx_map; }
|
||||
|
||||
bool add_vardata_encoding_for_testing (hb_vector_t<const hb_vector_t<int>*> &&rows,
|
||||
unsigned num_cols)
|
||||
{
|
||||
encodings.push (delta_row_encoding_t (std::move (rows), num_cols));
|
||||
return !encodings.in_error ();
|
||||
}
|
||||
|
||||
bool compile_varidx_map_for_testing (const hb_hashmap_t<unsigned, const hb_vector_t<int>*>& front_mapping)
|
||||
{ return compile_varidx_map (front_mapping); }
|
||||
|
||||
bool instantiate (const ItemVariationStore& varStore,
|
||||
const hb_subset_plan_t *plan,
|
||||
bool optimize=true,
|
||||
@@ -2046,27 +2063,46 @@ struct item_variations_t
|
||||
{
|
||||
/* full encoding_row -> new VarIdxes mapping */
|
||||
hb_hashmap_t<const hb_vector_t<int>*, unsigned> back_mapping;
|
||||
hb_vector_t<delta_row_encoding_t> split_encodings;
|
||||
|
||||
for (unsigned major = 0; major < encodings.length; major++)
|
||||
for (unsigned i = 0; i < encodings.length; i++)
|
||||
{
|
||||
delta_row_encoding_t& encoding = encodings[major];
|
||||
delta_row_encoding_t& encoding = encodings[i];
|
||||
/* just sanity check, this shouldn't happen */
|
||||
if (encoding.is_empty ())
|
||||
return false;
|
||||
|
||||
unsigned num_rows = encoding.items.length;
|
||||
unsigned num_cols = encoding.chars.length;
|
||||
|
||||
/* sort rows, make result deterministic */
|
||||
encoding.items.qsort (_cmp_row);
|
||||
|
||||
/* compile old to new var_idxes mapping */
|
||||
for (unsigned minor = 0; minor < num_rows; minor++)
|
||||
for (unsigned start = 0; start < num_rows; start += 0xFFFFu)
|
||||
{
|
||||
unsigned new_varidx = (major << 16) + minor;
|
||||
back_mapping.set (encoding.items.arrayZ[minor], new_varidx);
|
||||
unsigned chunk_len = hb_min (num_rows - start, 0xFFFFu);
|
||||
hb_vector_t<const hb_vector_t<int>*> rows;
|
||||
|
||||
if (!rows.alloc (chunk_len))
|
||||
return false;
|
||||
|
||||
unsigned major = split_encodings.length;
|
||||
for (unsigned minor = 0; minor < chunk_len; minor++)
|
||||
{
|
||||
const hb_vector_t<int> *row = encoding.items.arrayZ[start + minor];
|
||||
rows.push (row);
|
||||
if (!back_mapping.set (row, (major << 16) + minor))
|
||||
return false;
|
||||
}
|
||||
|
||||
split_encodings.push (delta_row_encoding_t (std::move (rows), num_cols));
|
||||
}
|
||||
}
|
||||
|
||||
encodings = std::move (split_encodings);
|
||||
if (encodings.in_error () || back_mapping.in_error ())
|
||||
return false;
|
||||
|
||||
for (auto _ : front_mapping.iter ())
|
||||
{
|
||||
unsigned old_varidx = _.first;
|
||||
|
||||
+107
-2
@@ -634,6 +634,83 @@ struct gvar_GVAR
|
||||
static unsigned int next_index (unsigned int i, unsigned int start, unsigned int end)
|
||||
{ return (i >= end) ? start : (i + 1); }
|
||||
|
||||
#ifndef HB_OPTIMIZE_SIZE
|
||||
template <bool is_x>
|
||||
#endif
|
||||
static bool decompile_deltas_add_to_points (const HBUINT8 *&p /* IN/OUT */,
|
||||
hb_array_t<contour_point_t> points,
|
||||
float scalar,
|
||||
const HBUINT8 *end,
|
||||
unsigned start
|
||||
#ifdef HB_OPTIMIZE_SIZE
|
||||
, bool is_x
|
||||
#endif
|
||||
)
|
||||
{
|
||||
unsigned i = 0;
|
||||
unsigned count = points.length;
|
||||
while (i < count)
|
||||
{
|
||||
if (unlikely (p + 1 > end)) return false;
|
||||
unsigned control = *p++;
|
||||
unsigned run_count = (control & TupleValues::VALUE_RUN_COUNT_MASK) + 1;
|
||||
unsigned stop = i + run_count;
|
||||
if (unlikely (stop > count)) return false;
|
||||
|
||||
unsigned skip = i < start ? hb_min (start - i, run_count) : 0;
|
||||
i += skip;
|
||||
|
||||
switch (control & TupleValues::VALUES_SIZE_MASK)
|
||||
{
|
||||
case TupleValues::VALUES_ARE_ZEROS:
|
||||
i = stop;
|
||||
break;
|
||||
case TupleValues::VALUES_ARE_WORDS:
|
||||
{
|
||||
if (unlikely (p + run_count * HBINT16::static_size > end)) return false;
|
||||
p += skip * HBINT16::static_size;
|
||||
const auto *pp = (const HBINT16 *) p;
|
||||
for (; i < stop; i++)
|
||||
{
|
||||
float v = *pp++ * scalar;
|
||||
if (is_x) points.arrayZ[i].x += v;
|
||||
else points.arrayZ[i].y += v;
|
||||
}
|
||||
p = (const HBUINT8 *) pp;
|
||||
}
|
||||
break;
|
||||
case TupleValues::VALUES_ARE_LONGS:
|
||||
{
|
||||
if (unlikely (p + run_count * HBINT32::static_size > end)) return false;
|
||||
p += skip * HBINT32::static_size;
|
||||
const auto *pp = (const HBINT32 *) p;
|
||||
for (; i < stop; i++)
|
||||
{
|
||||
float v = *pp++ * scalar;
|
||||
if (is_x) points.arrayZ[i].x += v;
|
||||
else points.arrayZ[i].y += v;
|
||||
}
|
||||
p = (const HBUINT8 *) pp;
|
||||
}
|
||||
break;
|
||||
case TupleValues::VALUES_ARE_BYTES:
|
||||
{
|
||||
if (unlikely (p + run_count > end)) return false;
|
||||
p += skip * HBINT8::static_size;
|
||||
const auto *pp = (const HBINT8 *) p;
|
||||
for (; i < stop; i++)
|
||||
{
|
||||
float v = *pp++ * scalar;
|
||||
if (is_x) points.arrayZ[i].x += v;
|
||||
else points.arrayZ[i].y += v;
|
||||
}
|
||||
p = (const HBUINT8 *) pp;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
public:
|
||||
bool apply_deltas_to_points (hb_codepoint_t glyph,
|
||||
hb_array_t<const int> coords,
|
||||
@@ -656,6 +733,9 @@ struct gvar_GVAR
|
||||
shared_indices, &iterator))
|
||||
return true; /* so isn't applied at all */
|
||||
|
||||
bool any_private_points = false;
|
||||
bool private_points_checked = false;
|
||||
|
||||
/* Save original points for inferred delta calculation */
|
||||
auto &orig_points_vec = scratch.orig_points;
|
||||
orig_points_vec.clear (); // Populated lazily
|
||||
@@ -682,6 +762,20 @@ struct gvar_GVAR
|
||||
gvar_cache);
|
||||
|
||||
if (scalar == 0.f) continue;
|
||||
|
||||
if (!private_points_checked)
|
||||
{
|
||||
auto scan = iterator;
|
||||
do
|
||||
{
|
||||
if (scan.current_tuple->has_private_points ())
|
||||
{
|
||||
any_private_points = true;
|
||||
break;
|
||||
}
|
||||
} while (scan.move_to_next ());
|
||||
private_points_checked = true;
|
||||
}
|
||||
const HBUINT8 *p = iterator.get_serialized_data ();
|
||||
unsigned int length = iterator.current_tuple->get_data_size ();
|
||||
if (unlikely (!iterator.var_data_bytes.check_range (p, length)))
|
||||
@@ -706,6 +800,19 @@ struct gvar_GVAR
|
||||
bool apply_to_all = (indices.length == 0);
|
||||
unsigned num_deltas = apply_to_all ? points.length : indices.length;
|
||||
unsigned start_deltas = (apply_to_all && phantom_only && num_deltas >= 4 ? num_deltas - 4 : 0);
|
||||
|
||||
if (apply_to_all && !any_private_points)
|
||||
{
|
||||
#ifdef HB_OPTIMIZE_SIZE
|
||||
if (unlikely (!decompile_deltas_add_to_points (p, points, scalar, end, start_deltas, true))) return false;
|
||||
if (unlikely (!decompile_deltas_add_to_points (p, points, scalar, end, start_deltas, false))) return false;
|
||||
#else
|
||||
if (unlikely (!decompile_deltas_add_to_points<true> (p, points, scalar, end, start_deltas))) return false;
|
||||
if (unlikely (!decompile_deltas_add_to_points<false> (p, points, scalar, end, start_deltas))) return false;
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
|
||||
if (unlikely (!x_deltas.resize_dirty (num_deltas))) return false;
|
||||
if (unlikely (!GlyphVariationData::decompile_deltas (p, x_deltas, end, false, start_deltas))) return false;
|
||||
if (unlikely (!y_deltas.resize_dirty (num_deltas))) return false;
|
||||
@@ -724,8 +831,6 @@ struct gvar_GVAR
|
||||
{
|
||||
for (unsigned int i = phantom_only ? count - 4 : 0; i < count; i++)
|
||||
points.arrayZ[i].translate (deltas.arrayZ[i]);
|
||||
flush = false;
|
||||
|
||||
}
|
||||
hb_memset (deltas.arrayZ + (phantom_only ? count - 4 : 0), 0,
|
||||
(phantom_only ? 4 : count) * sizeof (deltas[0]));
|
||||
|
||||
Vendored
+6
-6
@@ -742,14 +742,14 @@ hb_paint_pop_group (hb_paint_funcs_t *funcs, void *paint_data,
|
||||
|
||||
/**
|
||||
* hb_paint_custom_palette_color:
|
||||
* @funcs: paint functions
|
||||
* @paint_data: associated data passed by the caller
|
||||
* @color_index: color index
|
||||
* @color: (out): fetched color
|
||||
* @funcs: paint functions.
|
||||
* @paint_data: associated data passed by the caller.
|
||||
* @color_index: color index to fetch.
|
||||
* @color: (out): fetched color.
|
||||
*
|
||||
* Gets the custom palette color for @color_index.
|
||||
* Gets the custom palette override color for @color_index.
|
||||
*
|
||||
* Return value: `true` if found, `false` otherwise
|
||||
* Return value: `true` if a custom color is provided, `false` otherwise.
|
||||
*
|
||||
* Since: 7.0.0
|
||||
*/
|
||||
|
||||
Vendored
+19
-18
@@ -684,23 +684,24 @@ typedef void (*hb_paint_pop_group_func_t) (hb_paint_funcs_t *funcs,
|
||||
|
||||
/**
|
||||
* hb_paint_custom_palette_color_func_t:
|
||||
* @funcs: paint functions object
|
||||
* @paint_data: The data accompanying the paint functions in hb_font_paint_glyph()
|
||||
* @color_index: the color index
|
||||
* @color: (out): fetched color
|
||||
* @user_data: User data pointer passed to hb_paint_funcs_set_pop_group_func()
|
||||
* @funcs: paint functions object.
|
||||
* @paint_data: data accompanying the paint functions in hb_font_paint_glyph().
|
||||
* @color_index: color index to fetch.
|
||||
* @color: (out): fetched color.
|
||||
* @user_data: user data pointer passed to hb_paint_funcs_set_custom_palette_color_func().
|
||||
*
|
||||
* A virtual method for the #hb_paint_funcs_t to fetch a color from the custom
|
||||
* color palette.
|
||||
* A virtual method for #hb_paint_funcs_t to fetch a custom palette override
|
||||
* color for @color_index.
|
||||
*
|
||||
* Custom palette colors override the colors from the fonts selected color
|
||||
* palette. It is not necessary to override all palette entries; for entries
|
||||
* that should be taken from the font palette, return `false`.
|
||||
* Custom palette colors override colors from the font's selected color palette.
|
||||
* It is not necessary to override all palette entries; return `false` for
|
||||
* entries that should be taken from the font palette.
|
||||
*
|
||||
* This function might get called multiple times, but the custom palette is
|
||||
* expected to remain unchanged for duration of a hb_font_paint_glyph() call.
|
||||
* This function might be called multiple times, but the custom palette is
|
||||
* expected to remain unchanged for the duration of one
|
||||
* hb_font_paint_glyph() call.
|
||||
*
|
||||
* Return value: `true` if found, `false` otherwise
|
||||
* Return value: `true` if a custom color is provided, `false` otherwise.
|
||||
*
|
||||
* Since: 7.0.0
|
||||
*/
|
||||
@@ -934,12 +935,12 @@ hb_paint_funcs_set_pop_group_func (hb_paint_funcs_t *funcs,
|
||||
|
||||
/**
|
||||
* hb_paint_funcs_set_custom_palette_color_func:
|
||||
* @funcs: A paint functions struct
|
||||
* @func: (closure user_data) (destroy destroy) (scope notified): The custom-palette-color callback
|
||||
* @user_data: Data to pass to @func
|
||||
* @destroy: (nullable): Function to call when @user_data is no longer needed
|
||||
* @funcs: a paint functions struct.
|
||||
* @func: (closure user_data) (destroy destroy) (scope notified): custom-palette-color callback.
|
||||
* @user_data: data to pass to @func.
|
||||
* @destroy: (nullable): function to call when @user_data is no longer needed.
|
||||
*
|
||||
* Sets the custom-palette-color callback on the paint functions struct.
|
||||
* Sets the custom-palette-color callback on @funcs.
|
||||
*
|
||||
* Since: 7.0.0
|
||||
*/
|
||||
|
||||
+1386
File diff suppressed because it is too large
Load Diff
+967
@@ -0,0 +1,967 @@
|
||||
/*
|
||||
* Copyright © 2026 Behdad Esfahbod
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Author(s): Behdad Esfahbod
|
||||
*/
|
||||
|
||||
#include "hb.hh"
|
||||
|
||||
#include "hb-raster-image.hh"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#ifdef HAVE_PNG
|
||||
#include <png.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_PNG
|
||||
struct hb_raster_png_read_blob_t
|
||||
{
|
||||
const uint8_t *data = nullptr;
|
||||
size_t size = 0;
|
||||
size_t offset = 0;
|
||||
};
|
||||
|
||||
static void
|
||||
hb_raster_png_error (png_structp png,
|
||||
png_const_charp msg HB_UNUSED)
|
||||
{
|
||||
png_longjmp (png, 1);
|
||||
}
|
||||
|
||||
static void
|
||||
hb_raster_png_warning (png_structp png HB_UNUSED,
|
||||
png_const_charp msg HB_UNUSED)
|
||||
{}
|
||||
|
||||
static void
|
||||
hb_raster_png_read_blob (png_structp png, png_bytep out, png_size_t length)
|
||||
{
|
||||
hb_raster_png_read_blob_t *r = (hb_raster_png_read_blob_t *) png_get_io_ptr (png);
|
||||
|
||||
if (!r || !r->data || length > r->size - r->offset)
|
||||
png_error (png, "read error");
|
||||
|
||||
hb_memcpy (out, r->data + r->offset, length);
|
||||
r->offset += length;
|
||||
}
|
||||
|
||||
struct hb_raster_png_write_blob_t
|
||||
{
|
||||
hb_vector_t<char> data;
|
||||
};
|
||||
|
||||
static void
|
||||
hb_raster_png_write_blob (png_structp png, png_bytep in, png_size_t length)
|
||||
{
|
||||
hb_raster_png_write_blob_t *w = (hb_raster_png_write_blob_t *) png_get_io_ptr (png);
|
||||
if (!w)
|
||||
png_error (png, "write error");
|
||||
|
||||
unsigned old_length = w->data.length;
|
||||
if (!w->data.resize_dirty ((int) (old_length + length)))
|
||||
png_error (png, "write error");
|
||||
|
||||
hb_memcpy (w->data.arrayZ + old_length, in, length);
|
||||
}
|
||||
|
||||
static void
|
||||
hb_raster_png_flush_blob (png_structp png HB_UNUSED)
|
||||
{}
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Image compositing
|
||||
*/
|
||||
|
||||
/* Unpack premultiplied pixel to float RGBA [0,1]. */
|
||||
static inline void
|
||||
unpack_to_float (uint32_t px, float &r, float &g, float &b, float &a)
|
||||
{
|
||||
b = (px & 0xFF) / 255.f;
|
||||
g = ((px >> 8) & 0xFF) / 255.f;
|
||||
r = ((px >> 16) & 0xFF) / 255.f;
|
||||
a = (px >> 24) / 255.f;
|
||||
}
|
||||
|
||||
/* Pack float RGBA [0,1] premultiplied back to uint32_t. */
|
||||
static inline uint32_t
|
||||
pack_from_float (float r, float g, float b, float a)
|
||||
{
|
||||
return hb_raster_pack_pixel ((uint8_t) (hb_clamp (b, 0.f, 1.f) * 255.f + 0.5f),
|
||||
(uint8_t) (hb_clamp (g, 0.f, 1.f) * 255.f + 0.5f),
|
||||
(uint8_t) (hb_clamp (r, 0.f, 1.f) * 255.f + 0.5f),
|
||||
(uint8_t) (hb_clamp (a, 0.f, 1.f) * 255.f + 0.5f));
|
||||
}
|
||||
|
||||
/* Separable blend mode functions: operate on unpremultiplied [0,1] channels. */
|
||||
static inline float
|
||||
blend_multiply (float sc, float dc) { return sc * dc; }
|
||||
static inline float
|
||||
blend_screen (float sc, float dc) { return sc + dc - sc * dc; }
|
||||
static inline float
|
||||
blend_overlay (float sc, float dc)
|
||||
{ return dc <= 0.5f ? 2.f * sc * dc : 1.f - 2.f * (1.f - sc) * (1.f - dc); }
|
||||
static inline float
|
||||
blend_darken (float sc, float dc) { return hb_min (sc, dc); }
|
||||
static inline float
|
||||
blend_lighten (float sc, float dc) { return hb_max (sc, dc); }
|
||||
static inline float
|
||||
blend_color_dodge (float sc, float dc)
|
||||
{
|
||||
if (dc <= 0.f) return 0.f;
|
||||
if (sc >= 1.f) return 1.f;
|
||||
return hb_min (1.f, dc / (1.f - sc));
|
||||
}
|
||||
static inline float
|
||||
blend_color_burn (float sc, float dc)
|
||||
{
|
||||
if (dc >= 1.f) return 1.f;
|
||||
if (sc <= 0.f) return 0.f;
|
||||
return 1.f - hb_min (1.f, (1.f - dc) / sc);
|
||||
}
|
||||
static inline float
|
||||
blend_hard_light (float sc, float dc)
|
||||
{ return sc <= 0.5f ? 2.f * sc * dc : 1.f - 2.f * (1.f - sc) * (1.f - dc); }
|
||||
static inline float
|
||||
blend_soft_light (float sc, float dc)
|
||||
{
|
||||
if (sc <= 0.5f)
|
||||
return dc - (1.f - 2.f * sc) * dc * (1.f - dc);
|
||||
float d = (dc <= 0.25f) ? ((16.f * dc - 12.f) * dc + 4.f) * dc
|
||||
: sqrtf (dc);
|
||||
return dc + (2.f * sc - 1.f) * (d - dc);
|
||||
}
|
||||
static inline float
|
||||
blend_difference (float sc, float dc) { return fabsf (sc - dc); }
|
||||
static inline float
|
||||
blend_exclusion (float sc, float dc) { return sc + dc - 2.f * sc * dc; }
|
||||
|
||||
/* Apply a separable blend mode per-pixel.
|
||||
* Both src and dst are premultiplied BGRA32. */
|
||||
static inline uint32_t
|
||||
apply_separable_blend (uint32_t src, uint32_t dst,
|
||||
float (*blend_fn)(float, float))
|
||||
{
|
||||
float sr, sg, sb, sa;
|
||||
float dr, dg, db, da;
|
||||
unpack_to_float (src, sr, sg, sb, sa);
|
||||
unpack_to_float (dst, dr, dg, db, da);
|
||||
|
||||
float usr = sa > 0.f ? sr / sa : 0.f;
|
||||
float usg = sa > 0.f ? sg / sa : 0.f;
|
||||
float usb = sa > 0.f ? sb / sa : 0.f;
|
||||
float udr = da > 0.f ? dr / da : 0.f;
|
||||
float udg = da > 0.f ? dg / da : 0.f;
|
||||
float udb = da > 0.f ? db / da : 0.f;
|
||||
|
||||
float br = blend_fn (usr, udr);
|
||||
float bg = blend_fn (usg, udg);
|
||||
float bb = blend_fn (usb, udb);
|
||||
|
||||
float ra = sa + da - sa * da;
|
||||
float rr = sa * da * br + sa * (1.f - da) * usr + (1.f - sa) * da * udr;
|
||||
float rg = sa * da * bg + sa * (1.f - da) * usg + (1.f - sa) * da * udg;
|
||||
float rb = sa * da * bb + sa * (1.f - da) * usb + (1.f - sa) * da * udb;
|
||||
|
||||
return pack_from_float (rr, rg, rb, ra);
|
||||
}
|
||||
|
||||
/* HSL helpers */
|
||||
static inline float
|
||||
hsl_luminosity (float r, float g, float b)
|
||||
{ return 0.299f * r + 0.587f * g + 0.114f * b; }
|
||||
|
||||
static inline float
|
||||
hsl_saturation (float r, float g, float b)
|
||||
{ return hb_max (hb_max (r, g), b) - hb_min (hb_min (r, g), b); }
|
||||
|
||||
static inline void
|
||||
hsl_clip_color (float &r, float &g, float &b)
|
||||
{
|
||||
float l = hsl_luminosity (r, g, b);
|
||||
float mn = hb_min (hb_min (r, g), b);
|
||||
float mx = hb_max (hb_max (r, g), b);
|
||||
if (mn < 0.f)
|
||||
{
|
||||
float d = l - mn;
|
||||
if (d > 0.f) { r = l + (r - l) * l / d; g = l + (g - l) * l / d; b = l + (b - l) * l / d; }
|
||||
}
|
||||
if (mx > 1.f)
|
||||
{
|
||||
float d = mx - l;
|
||||
if (d > 0.f) { r = l + (r - l) * (1.f - l) / d; g = l + (g - l) * (1.f - l) / d; b = l + (b - l) * (1.f - l) / d; }
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
hsl_set_luminosity (float &r, float &g, float &b, float l)
|
||||
{
|
||||
float d = l - hsl_luminosity (r, g, b);
|
||||
r += d; g += d; b += d;
|
||||
hsl_clip_color (r, g, b);
|
||||
}
|
||||
|
||||
static inline void
|
||||
hsl_set_saturation_inner (float &mn, float &mid, float &mx, float s)
|
||||
{
|
||||
if (mx > mn)
|
||||
{
|
||||
mid = (mid - mn) * s / (mx - mn);
|
||||
mx = s;
|
||||
}
|
||||
else
|
||||
mid = mx = 0.f;
|
||||
mn = 0.f;
|
||||
}
|
||||
|
||||
static inline void
|
||||
hsl_set_saturation (float &r, float &g, float &b, float s)
|
||||
{
|
||||
if (r <= g)
|
||||
{
|
||||
if (g <= b) hsl_set_saturation_inner (r, g, b, s);
|
||||
else if (r <= b) hsl_set_saturation_inner (r, b, g, s);
|
||||
else hsl_set_saturation_inner (b, r, g, s);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (r <= b) hsl_set_saturation_inner (g, r, b, s);
|
||||
else if (g <= b) hsl_set_saturation_inner (g, b, r, s);
|
||||
else hsl_set_saturation_inner (b, g, r, s);
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
apply_hsl_blend (uint32_t src, uint32_t dst,
|
||||
hb_paint_composite_mode_t mode)
|
||||
{
|
||||
float sr, sg, sb, sa;
|
||||
float dr, dg, db, da;
|
||||
unpack_to_float (src, sr, sg, sb, sa);
|
||||
unpack_to_float (dst, dr, dg, db, da);
|
||||
|
||||
float usr = sa > 0.f ? sr / sa : 0.f;
|
||||
float usg = sa > 0.f ? sg / sa : 0.f;
|
||||
float usb = sa > 0.f ? sb / sa : 0.f;
|
||||
float udr = da > 0.f ? dr / da : 0.f;
|
||||
float udg = da > 0.f ? dg / da : 0.f;
|
||||
float udb = da > 0.f ? db / da : 0.f;
|
||||
|
||||
float br = udr, bg = udg, bb = udb;
|
||||
|
||||
if (mode == HB_PAINT_COMPOSITE_MODE_HSL_HUE)
|
||||
{
|
||||
br = usr; bg = usg; bb = usb;
|
||||
hsl_set_saturation (br, bg, bb, hsl_saturation (udr, udg, udb));
|
||||
hsl_set_luminosity (br, bg, bb, hsl_luminosity (udr, udg, udb));
|
||||
}
|
||||
else if (mode == HB_PAINT_COMPOSITE_MODE_HSL_SATURATION)
|
||||
{
|
||||
br = udr; bg = udg; bb = udb;
|
||||
hsl_set_saturation (br, bg, bb, hsl_saturation (usr, usg, usb));
|
||||
hsl_set_luminosity (br, bg, bb, hsl_luminosity (udr, udg, udb));
|
||||
}
|
||||
else if (mode == HB_PAINT_COMPOSITE_MODE_HSL_COLOR)
|
||||
{
|
||||
br = usr; bg = usg; bb = usb;
|
||||
hsl_set_luminosity (br, bg, bb, hsl_luminosity (udr, udg, udb));
|
||||
}
|
||||
else /* HSL_LUMINOSITY */
|
||||
{
|
||||
br = udr; bg = udg; bb = udb;
|
||||
hsl_set_luminosity (br, bg, bb, hsl_luminosity (usr, usg, usb));
|
||||
}
|
||||
|
||||
float ra = sa + da - sa * da;
|
||||
float rr = sa * da * br + sa * (1.f - da) * usr + (1.f - sa) * da * udr;
|
||||
float rg = sa * da * bg + sa * (1.f - da) * usg + (1.f - sa) * da * udg;
|
||||
float rb = sa * da * bb + sa * (1.f - da) * usb + (1.f - sa) * da * udb;
|
||||
|
||||
return pack_from_float (rr, rg, rb, ra);
|
||||
}
|
||||
|
||||
/* Composite per-pixel with full blend mode support. */
|
||||
static inline uint32_t
|
||||
composite_pixel (uint32_t src, uint32_t dst,
|
||||
hb_paint_composite_mode_t mode)
|
||||
{
|
||||
uint8_t sa = (uint8_t) (src >> 24);
|
||||
uint8_t da = (uint8_t) (dst >> 24);
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case HB_PAINT_COMPOSITE_MODE_CLEAR:
|
||||
return 0;
|
||||
case HB_PAINT_COMPOSITE_MODE_SRC:
|
||||
return src;
|
||||
case HB_PAINT_COMPOSITE_MODE_DEST:
|
||||
return dst;
|
||||
case HB_PAINT_COMPOSITE_MODE_SRC_OVER:
|
||||
return hb_raster_src_over (src, dst);
|
||||
case HB_PAINT_COMPOSITE_MODE_DEST_OVER:
|
||||
return hb_raster_src_over (dst, src);
|
||||
case HB_PAINT_COMPOSITE_MODE_SRC_IN:
|
||||
return hb_raster_alpha_mul (src, da);
|
||||
case HB_PAINT_COMPOSITE_MODE_DEST_IN:
|
||||
return hb_raster_alpha_mul (dst, sa);
|
||||
case HB_PAINT_COMPOSITE_MODE_SRC_OUT:
|
||||
return hb_raster_alpha_mul (src, 255 - da);
|
||||
case HB_PAINT_COMPOSITE_MODE_DEST_OUT:
|
||||
return hb_raster_alpha_mul (dst, 255 - sa);
|
||||
case HB_PAINT_COMPOSITE_MODE_SRC_ATOP:
|
||||
{
|
||||
/* Fa=Da, Fb=1-Sa */
|
||||
uint32_t a = hb_raster_alpha_mul (src, da);
|
||||
uint32_t b = hb_raster_alpha_mul (dst, 255 - sa);
|
||||
uint8_t rb = (uint8_t) hb_min (255u, (unsigned) (a & 0xFF) + (b & 0xFF));
|
||||
uint8_t rg = (uint8_t) hb_min (255u, (unsigned) ((a >> 8) & 0xFF) + ((b >> 8) & 0xFF));
|
||||
uint8_t rr = (uint8_t) hb_min (255u, (unsigned) ((a >> 16) & 0xFF) + ((b >> 16) & 0xFF));
|
||||
uint8_t ra = (uint8_t) hb_min (255u, (unsigned) (a >> 24) + (b >> 24));
|
||||
return hb_raster_pack_pixel (rb, rg, rr, ra);
|
||||
}
|
||||
case HB_PAINT_COMPOSITE_MODE_DEST_ATOP:
|
||||
{
|
||||
uint32_t a = hb_raster_alpha_mul (dst, sa);
|
||||
uint32_t b = hb_raster_alpha_mul (src, 255 - da);
|
||||
uint8_t rb = (uint8_t) hb_min (255u, (unsigned) (a & 0xFF) + (b & 0xFF));
|
||||
uint8_t rg = (uint8_t) hb_min (255u, (unsigned) ((a >> 8) & 0xFF) + ((b >> 8) & 0xFF));
|
||||
uint8_t rr = (uint8_t) hb_min (255u, (unsigned) ((a >> 16) & 0xFF) + ((b >> 16) & 0xFF));
|
||||
uint8_t ra = (uint8_t) hb_min (255u, (unsigned) (a >> 24) + (b >> 24));
|
||||
return hb_raster_pack_pixel (rb, rg, rr, ra);
|
||||
}
|
||||
case HB_PAINT_COMPOSITE_MODE_XOR:
|
||||
{
|
||||
uint32_t a = hb_raster_alpha_mul (src, 255 - da);
|
||||
uint32_t b = hb_raster_alpha_mul (dst, 255 - sa);
|
||||
uint8_t rb = (uint8_t) hb_min (255u, (unsigned) (a & 0xFF) + (b & 0xFF));
|
||||
uint8_t rg = (uint8_t) hb_min (255u, (unsigned) ((a >> 8) & 0xFF) + ((b >> 8) & 0xFF));
|
||||
uint8_t rr = (uint8_t) hb_min (255u, (unsigned) ((a >> 16) & 0xFF) + ((b >> 16) & 0xFF));
|
||||
uint8_t ra = (uint8_t) hb_min (255u, (unsigned) (a >> 24) + (b >> 24));
|
||||
return hb_raster_pack_pixel (rb, rg, rr, ra);
|
||||
}
|
||||
case HB_PAINT_COMPOSITE_MODE_PLUS:
|
||||
{
|
||||
uint8_t rb = (uint8_t) hb_min (255u, (unsigned) (src & 0xFF) + (dst & 0xFF));
|
||||
uint8_t rg = (uint8_t) hb_min (255u, (unsigned) ((src >> 8) & 0xFF) + ((dst >> 8) & 0xFF));
|
||||
uint8_t rr = (uint8_t) hb_min (255u, (unsigned) ((src >> 16) & 0xFF) + ((dst >> 16) & 0xFF));
|
||||
uint8_t ra = (uint8_t) hb_min (255u, (unsigned) (src >> 24) + (dst >> 24));
|
||||
return hb_raster_pack_pixel (rb, rg, rr, ra);
|
||||
}
|
||||
|
||||
case HB_PAINT_COMPOSITE_MODE_MULTIPLY: return apply_separable_blend (src, dst, blend_multiply);
|
||||
case HB_PAINT_COMPOSITE_MODE_SCREEN: return apply_separable_blend (src, dst, blend_screen);
|
||||
case HB_PAINT_COMPOSITE_MODE_OVERLAY: return apply_separable_blend (src, dst, blend_overlay);
|
||||
case HB_PAINT_COMPOSITE_MODE_DARKEN: return apply_separable_blend (src, dst, blend_darken);
|
||||
case HB_PAINT_COMPOSITE_MODE_LIGHTEN: return apply_separable_blend (src, dst, blend_lighten);
|
||||
case HB_PAINT_COMPOSITE_MODE_COLOR_DODGE: return apply_separable_blend (src, dst, blend_color_dodge);
|
||||
case HB_PAINT_COMPOSITE_MODE_COLOR_BURN: return apply_separable_blend (src, dst, blend_color_burn);
|
||||
case HB_PAINT_COMPOSITE_MODE_HARD_LIGHT: return apply_separable_blend (src, dst, blend_hard_light);
|
||||
case HB_PAINT_COMPOSITE_MODE_SOFT_LIGHT: return apply_separable_blend (src, dst, blend_soft_light);
|
||||
case HB_PAINT_COMPOSITE_MODE_DIFFERENCE: return apply_separable_blend (src, dst, blend_difference);
|
||||
case HB_PAINT_COMPOSITE_MODE_EXCLUSION: return apply_separable_blend (src, dst, blend_exclusion);
|
||||
|
||||
case HB_PAINT_COMPOSITE_MODE_HSL_HUE:
|
||||
case HB_PAINT_COMPOSITE_MODE_HSL_SATURATION:
|
||||
case HB_PAINT_COMPOSITE_MODE_HSL_COLOR:
|
||||
case HB_PAINT_COMPOSITE_MODE_HSL_LUMINOSITY:
|
||||
return apply_hsl_blend (src, dst, mode);
|
||||
|
||||
default:
|
||||
return hb_raster_src_over (src, dst);
|
||||
}
|
||||
}
|
||||
|
||||
/* hb_raster_image_t */
|
||||
|
||||
unsigned
|
||||
hb_raster_image_t::bytes_per_pixel (hb_raster_format_t format)
|
||||
{
|
||||
return format == HB_RASTER_FORMAT_BGRA32 ? 4u : 1u;
|
||||
}
|
||||
|
||||
bool
|
||||
hb_raster_image_t::configure (hb_raster_format_t format,
|
||||
hb_raster_extents_t extents)
|
||||
{
|
||||
if (format != HB_RASTER_FORMAT_A8 &&
|
||||
format != HB_RASTER_FORMAT_BGRA32)
|
||||
format = HB_RASTER_FORMAT_A8;
|
||||
|
||||
unsigned bpp = bytes_per_pixel (format);
|
||||
if (extents.width > UINT_MAX / bpp)
|
||||
return false;
|
||||
|
||||
unsigned min_stride = extents.width * bpp;
|
||||
if (extents.stride == 0 || extents.stride < min_stride)
|
||||
extents.stride = min_stride;
|
||||
|
||||
if (extents.height && extents.stride > (size_t) -1 / extents.height)
|
||||
return false;
|
||||
|
||||
size_t buf_size = (size_t) extents.stride * extents.height;
|
||||
if (buf_size > HB_RASTER_MAX_BUFFER_SIZE)
|
||||
return false;
|
||||
if (unlikely (!buffer.resize_dirty (buf_size)))
|
||||
return false;
|
||||
|
||||
this->format = format;
|
||||
this->extents = extents;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
hb_raster_image_t::deserialize_from_png (hb_blob_t *blob)
|
||||
{
|
||||
#ifndef HAVE_PNG
|
||||
return false;
|
||||
#else
|
||||
if (!blob)
|
||||
return false;
|
||||
|
||||
unsigned blob_len = 0;
|
||||
const uint8_t *blob_data = (const uint8_t *) hb_blob_get_data (blob, &blob_len);
|
||||
if (!blob_data || !blob_len)
|
||||
return false;
|
||||
|
||||
png_structp png = png_create_read_struct (PNG_LIBPNG_VER_STRING, nullptr,
|
||||
hb_raster_png_error,
|
||||
hb_raster_png_warning);
|
||||
if (!png)
|
||||
return false;
|
||||
|
||||
png_infop info = png_create_info_struct (png);
|
||||
if (!info)
|
||||
{
|
||||
png_destroy_read_struct (&png, nullptr, nullptr);
|
||||
return false;
|
||||
}
|
||||
|
||||
hb_raster_png_read_blob_t reader;
|
||||
reader.data = blob_data;
|
||||
reader.size = (size_t) blob_len;
|
||||
reader.offset = 0;
|
||||
hb_vector_t<uint8_t> rgba;
|
||||
hb_vector_t<png_bytep> rows;
|
||||
if (setjmp (png_jmpbuf (png)))
|
||||
{
|
||||
png_destroy_read_struct (&png, &info, nullptr);
|
||||
rgba.fini ();
|
||||
rows.fini ();
|
||||
return false;
|
||||
}
|
||||
|
||||
png_set_read_fn (png, &reader, hb_raster_png_read_blob);
|
||||
png_read_info (png, info);
|
||||
|
||||
png_uint_32 w = 0, h = 0;
|
||||
int bit_depth = 0, color_type = 0;
|
||||
int interlace_type = 0, compression_type = 0, filter_method = 0;
|
||||
png_get_IHDR (png, info, &w, &h, &bit_depth, &color_type,
|
||||
&interlace_type, &compression_type, &filter_method);
|
||||
|
||||
if (!w || !h || w > (png_uint_32) INT_MAX || h > (png_uint_32) INT_MAX)
|
||||
{
|
||||
png_destroy_read_struct (&png, &info, nullptr);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool has_trns = png_get_valid (png, info, PNG_INFO_tRNS);
|
||||
|
||||
if (bit_depth == 16)
|
||||
png_set_strip_16 (png);
|
||||
if (color_type == PNG_COLOR_TYPE_PALETTE)
|
||||
png_set_palette_to_rgb (png);
|
||||
if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
|
||||
png_set_expand_gray_1_2_4_to_8 (png);
|
||||
if (has_trns)
|
||||
png_set_tRNS_to_alpha (png);
|
||||
if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
|
||||
png_set_gray_to_rgb (png);
|
||||
if (!(color_type & PNG_COLOR_MASK_ALPHA) && !has_trns)
|
||||
png_set_add_alpha (png, 0xff, PNG_FILLER_AFTER);
|
||||
if (interlace_type != PNG_INTERLACE_NONE)
|
||||
png_set_interlace_handling (png);
|
||||
|
||||
png_read_update_info (png, info);
|
||||
|
||||
if (png_get_bit_depth (png, info) != 8 || png_get_channels (png, info) != 4)
|
||||
{
|
||||
png_destroy_read_struct (&png, &info, nullptr);
|
||||
return false;
|
||||
}
|
||||
|
||||
png_size_t rowbytes = png_get_rowbytes (png, info);
|
||||
if (rowbytes < (png_size_t) w * 4u)
|
||||
{
|
||||
png_destroy_read_struct (&png, &info, nullptr);
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t rgba_size = (size_t) rowbytes * (size_t) h;
|
||||
if (h && rgba_size / (size_t) h != (size_t) rowbytes)
|
||||
{
|
||||
png_destroy_read_struct (&png, &info, nullptr);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!rgba.resize (rgba_size))
|
||||
{
|
||||
png_destroy_read_struct (&png, &info, nullptr);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!rows.resize ((unsigned) h))
|
||||
{
|
||||
png_destroy_read_struct (&png, &info, nullptr);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (unsigned y = 0; y < (unsigned) h; y++)
|
||||
rows[y] = (png_bytep) (rgba.arrayZ + (size_t) y * (size_t) rowbytes);
|
||||
|
||||
png_read_image (png, rows.arrayZ);
|
||||
png_read_end (png, nullptr);
|
||||
png_destroy_read_struct (&png, &info, nullptr);
|
||||
|
||||
hb_raster_image_t decoded;
|
||||
hb_raster_extents_t decoded_extents = {0, 0, (unsigned) w, (unsigned) h, 0};
|
||||
if (!decoded.configure (HB_RASTER_FORMAT_BGRA32, decoded_extents))
|
||||
return false;
|
||||
|
||||
for (unsigned y = 0; y < (unsigned) h; y++)
|
||||
{
|
||||
hb_packed_t<uint32_t> *dst = (hb_packed_t<uint32_t> *) (decoded.buffer.arrayZ + (size_t) ((unsigned) h - 1 - y) * decoded.extents.stride);
|
||||
const uint8_t *src = rgba.arrayZ + (size_t) y * (size_t) rowbytes;
|
||||
for (unsigned x = 0; x < (unsigned) w; x++)
|
||||
{
|
||||
uint8_t r = src[4 * x + 0];
|
||||
uint8_t g = src[4 * x + 1];
|
||||
uint8_t b = src[4 * x + 2];
|
||||
uint8_t a = src[4 * x + 3];
|
||||
dst[x] = hb_packed_t<uint32_t> ((uint32_t) hb_raster_div255 (b * a)
|
||||
| ((uint32_t) hb_raster_div255 (g * a) << 8)
|
||||
| ((uint32_t) hb_raster_div255 (r * a) << 16)
|
||||
| ((uint32_t) a << 24));
|
||||
}
|
||||
}
|
||||
|
||||
hb_swap (buffer, decoded.buffer);
|
||||
hb_swap (this->extents, decoded.extents);
|
||||
hb_swap (format, decoded.format);
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
hb_blob_t *
|
||||
hb_raster_image_t::serialize_to_png_or_fail () const
|
||||
{
|
||||
#ifndef HAVE_PNG
|
||||
return nullptr;
|
||||
#else
|
||||
if (format != HB_RASTER_FORMAT_BGRA32 || !extents.width || !extents.height)
|
||||
return nullptr;
|
||||
|
||||
png_structp png = png_create_write_struct (PNG_LIBPNG_VER_STRING, nullptr,
|
||||
hb_raster_png_error,
|
||||
hb_raster_png_warning);
|
||||
if (!png)
|
||||
return nullptr;
|
||||
|
||||
png_infop info = png_create_info_struct (png);
|
||||
if (!info)
|
||||
{
|
||||
png_destroy_write_struct (&png, nullptr);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
hb_raster_png_write_blob_t writer;
|
||||
hb_vector_t<uint8_t> rgba;
|
||||
hb_vector_t<png_bytep> rows;
|
||||
if (setjmp (png_jmpbuf (png)))
|
||||
{
|
||||
png_destroy_write_struct (&png, &info);
|
||||
writer.data.fini ();
|
||||
rgba.fini ();
|
||||
rows.fini ();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
png_set_write_fn (png, &writer, hb_raster_png_write_blob, hb_raster_png_flush_blob);
|
||||
png_set_IHDR (png, info,
|
||||
extents.width, extents.height,
|
||||
8, PNG_COLOR_TYPE_RGBA,
|
||||
PNG_INTERLACE_NONE,
|
||||
PNG_COMPRESSION_TYPE_DEFAULT,
|
||||
PNG_FILTER_TYPE_DEFAULT);
|
||||
png_write_info (png, info);
|
||||
|
||||
size_t rowbytes = (size_t) extents.width * 4u;
|
||||
size_t rgba_size = rowbytes * (size_t) extents.height;
|
||||
if (extents.height && rgba_size / (size_t) extents.height != rowbytes)
|
||||
{
|
||||
png_destroy_write_struct (&png, &info);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!rgba.resize (rgba_size))
|
||||
{
|
||||
png_destroy_write_struct (&png, &info);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!rows.resize (extents.height))
|
||||
{
|
||||
png_destroy_write_struct (&png, &info);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (unsigned y = 0; y < extents.height; y++)
|
||||
{
|
||||
uint8_t *dst = rgba.arrayZ + (size_t) y * rowbytes;
|
||||
const uint8_t *src = buffer.arrayZ + (size_t) (extents.height - 1 - y) * extents.stride;
|
||||
|
||||
for (unsigned x = 0; x < extents.width; x++)
|
||||
{
|
||||
uint32_t px;
|
||||
hb_memcpy (&px, src + x * 4, 4);
|
||||
uint8_t b = (uint8_t) (px & 0xFF);
|
||||
uint8_t g = (uint8_t) ((px >> 8) & 0xFF);
|
||||
uint8_t r = (uint8_t) ((px >> 16) & 0xFF);
|
||||
uint8_t a = (uint8_t) (px >> 24);
|
||||
|
||||
dst[4 * x + 3] = a;
|
||||
if (a)
|
||||
{
|
||||
dst[4 * x + 0] = (uint8_t) hb_min (255u, ((unsigned) r * 255u + a / 2u) / a);
|
||||
dst[4 * x + 1] = (uint8_t) hb_min (255u, ((unsigned) g * 255u + a / 2u) / a);
|
||||
dst[4 * x + 2] = (uint8_t) hb_min (255u, ((unsigned) b * 255u + a / 2u) / a);
|
||||
}
|
||||
else
|
||||
dst[4 * x + 0] = dst[4 * x + 1] = dst[4 * x + 2] = 0;
|
||||
}
|
||||
|
||||
rows[y] = (png_bytep) dst;
|
||||
}
|
||||
|
||||
png_write_image (png, rows.arrayZ);
|
||||
png_write_end (png, info);
|
||||
png_destroy_write_struct (&png, &info);
|
||||
|
||||
unsigned length = 0;
|
||||
char *data = writer.data.steal (&length);
|
||||
if (!data && length)
|
||||
return nullptr;
|
||||
|
||||
hb_blob_t *blob = hb_blob_create_or_fail (data, length,
|
||||
HB_MEMORY_MODE_WRITABLE,
|
||||
data, hb_free);
|
||||
if (!blob)
|
||||
hb_free (data);
|
||||
return blob;
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
hb_raster_image_t::clear ()
|
||||
{
|
||||
size_t buf_size = (size_t) extents.stride * extents.height;
|
||||
hb_memset (buffer.arrayZ, 0, buf_size);
|
||||
}
|
||||
|
||||
const uint8_t *
|
||||
hb_raster_image_t::get_buffer () const
|
||||
{
|
||||
return buffer.arrayZ;
|
||||
}
|
||||
|
||||
void
|
||||
hb_raster_image_t::composite_from (const hb_raster_image_t *src,
|
||||
hb_paint_composite_mode_t mode)
|
||||
{
|
||||
unsigned w = extents.width;
|
||||
unsigned h = extents.height;
|
||||
unsigned stride = extents.stride;
|
||||
|
||||
for (unsigned y = 0; y < h; y++)
|
||||
{
|
||||
hb_packed_t<uint32_t> *dp = (hb_packed_t<uint32_t> *) (buffer.arrayZ + y * stride);
|
||||
const hb_packed_t<uint32_t> *sp = (const hb_packed_t<uint32_t> *) (src->buffer.arrayZ + y * stride);
|
||||
for (unsigned x = 0; x < w; x++)
|
||||
dp[x] = hb_packed_t<uint32_t> (composite_pixel ((uint32_t) sp[x], (uint32_t) dp[x], mode));
|
||||
}
|
||||
}
|
||||
|
||||
/* Composite src image onto dst image.
|
||||
* Both images must have the same extents and BGRA32 format. */
|
||||
void
|
||||
hb_raster_image_composite (hb_raster_image_t *dst,
|
||||
const hb_raster_image_t *src,
|
||||
hb_paint_composite_mode_t mode)
|
||||
{
|
||||
dst->composite_from (src, mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* hb_raster_image_create_or_fail:
|
||||
*
|
||||
* Creates a new raster image object.
|
||||
*
|
||||
* Return value: (transfer full):
|
||||
* A newly allocated #hb_raster_image_t with a reference count of 1,
|
||||
* or `NULL` on allocation failure.
|
||||
*
|
||||
* The returned image can be released with hb_raster_image_destroy(), or
|
||||
* transferred for reuse with hb_raster_draw_recycle_image() or
|
||||
* hb_raster_paint_recycle_image().
|
||||
*
|
||||
* Since: 13.0.0
|
||||
**/
|
||||
hb_raster_image_t *
|
||||
hb_raster_image_create_or_fail (void)
|
||||
{
|
||||
return hb_object_create<hb_raster_image_t> ();
|
||||
}
|
||||
|
||||
/**
|
||||
* hb_raster_image_reference: (skip)
|
||||
* @image: a raster image
|
||||
*
|
||||
* Increases the reference count on @image by one.
|
||||
*
|
||||
* This prevents @image from being destroyed until a matching
|
||||
* call to hb_raster_image_destroy() is made.
|
||||
*
|
||||
* Return value: (transfer full):
|
||||
* The referenced #hb_raster_image_t.
|
||||
*
|
||||
* Since: 13.0.0
|
||||
**/
|
||||
hb_raster_image_t *
|
||||
hb_raster_image_reference (hb_raster_image_t *image)
|
||||
{
|
||||
return hb_object_reference (image);
|
||||
}
|
||||
|
||||
/**
|
||||
* hb_raster_image_destroy: (skip)
|
||||
* @image: a raster image
|
||||
*
|
||||
* Decreases the reference count on @image by one. When the
|
||||
* reference count reaches zero, the image and its pixel buffer
|
||||
* are freed.
|
||||
*
|
||||
* Since: 13.0.0
|
||||
**/
|
||||
void
|
||||
hb_raster_image_destroy (hb_raster_image_t *image)
|
||||
{
|
||||
if (!hb_object_destroy (image)) return;
|
||||
hb_free (image);
|
||||
}
|
||||
|
||||
/**
|
||||
* hb_raster_image_set_user_data: (skip)
|
||||
* @image: a raster image
|
||||
* @key: the user-data key
|
||||
* @data: a pointer to the user data
|
||||
* @destroy: (nullable): a callback to call when @data is not needed anymore
|
||||
* @replace: whether to replace an existing data with the same key
|
||||
*
|
||||
* Attaches a user-data key/data pair to the specified raster image.
|
||||
*
|
||||
* Return value: `true` if success, `false` otherwise
|
||||
*
|
||||
* Since: 13.0.0
|
||||
**/
|
||||
hb_bool_t
|
||||
hb_raster_image_set_user_data (hb_raster_image_t *image,
|
||||
hb_user_data_key_t *key,
|
||||
void *data,
|
||||
hb_destroy_func_t destroy,
|
||||
hb_bool_t replace)
|
||||
{
|
||||
return hb_object_set_user_data (image, key, data, destroy, replace);
|
||||
}
|
||||
|
||||
/**
|
||||
* hb_raster_image_get_user_data: (skip)
|
||||
* @image: a raster image
|
||||
* @key: the user-data key
|
||||
*
|
||||
* Fetches the user-data associated with the specified key,
|
||||
* attached to the specified raster image.
|
||||
*
|
||||
* Return value: (transfer none):
|
||||
* A pointer to the user data
|
||||
*
|
||||
* Since: 13.0.0
|
||||
**/
|
||||
void *
|
||||
hb_raster_image_get_user_data (hb_raster_image_t *image,
|
||||
hb_user_data_key_t *key)
|
||||
{
|
||||
return hb_object_get_user_data (image, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* hb_raster_image_configure:
|
||||
* @image: a raster image
|
||||
* @format: the pixel format
|
||||
* @extents: (nullable): desired image extents
|
||||
*
|
||||
* Configures @image format and extents together, resizing backing storage
|
||||
* at most once. This function does not clear pixel contents.
|
||||
*
|
||||
* Passing `NULL` for @extents clears extents and releases the backing
|
||||
* allocation.
|
||||
*
|
||||
* Return value: `true` if configuration succeeds, `false` on allocation
|
||||
* failure
|
||||
*
|
||||
* Since: 13.0.0
|
||||
**/
|
||||
hb_bool_t
|
||||
hb_raster_image_configure (hb_raster_image_t *image,
|
||||
hb_raster_format_t format,
|
||||
const hb_raster_extents_t *extents)
|
||||
{
|
||||
if (unlikely (!extents))
|
||||
{
|
||||
image->extents = {};
|
||||
image->buffer.resize_exact (0);
|
||||
return true;
|
||||
}
|
||||
return image->configure (format, *extents);
|
||||
}
|
||||
|
||||
/**
|
||||
* hb_raster_image_clear:
|
||||
* @image: a raster image
|
||||
*
|
||||
* Clears @image pixels to zero while keeping current extents and format.
|
||||
*
|
||||
* Since: 13.0.0
|
||||
**/
|
||||
void
|
||||
hb_raster_image_clear (hb_raster_image_t *image)
|
||||
{
|
||||
image->clear ();
|
||||
}
|
||||
|
||||
/**
|
||||
* hb_raster_image_get_buffer:
|
||||
* @image: a raster image
|
||||
*
|
||||
* Fetches the raw pixel buffer of @image. The buffer layout is
|
||||
* described by the extents obtained from hb_raster_image_get_extents()
|
||||
* and the format from hb_raster_image_get_format(). Rows are stored
|
||||
* bottom-to-top.
|
||||
*
|
||||
* Return value: (transfer none) (array):
|
||||
* The pixel buffer, or `NULL`
|
||||
*
|
||||
* Since: 13.0.0
|
||||
**/
|
||||
const uint8_t *
|
||||
hb_raster_image_get_buffer (hb_raster_image_t *image)
|
||||
{
|
||||
return image->get_buffer ();
|
||||
}
|
||||
|
||||
/**
|
||||
* hb_raster_image_get_extents:
|
||||
* @image: a raster image
|
||||
* @extents: (out) (nullable): the image extents
|
||||
*
|
||||
* Fetches the pixel-buffer extents of @image.
|
||||
*
|
||||
* Since: 13.0.0
|
||||
**/
|
||||
void
|
||||
hb_raster_image_get_extents (hb_raster_image_t *image,
|
||||
hb_raster_extents_t *extents)
|
||||
{
|
||||
if (extents)
|
||||
*extents = image->extents;
|
||||
}
|
||||
|
||||
/**
|
||||
* hb_raster_image_get_format:
|
||||
* @image: a raster image
|
||||
*
|
||||
* Fetches the pixel format of @image.
|
||||
*
|
||||
* Return value:
|
||||
* The #hb_raster_format_t of the image
|
||||
*
|
||||
* Since: 13.0.0
|
||||
**/
|
||||
hb_raster_format_t
|
||||
hb_raster_image_get_format (hb_raster_image_t *image)
|
||||
{
|
||||
return image->format;
|
||||
}
|
||||
|
||||
/**
|
||||
* hb_raster_image_deserialize_from_png_or_fail:
|
||||
* @image: a raster image
|
||||
* @png: PNG data
|
||||
*
|
||||
* Replaces @image contents by deserializing a PNG blob into a
|
||||
* #HB_RASTER_FORMAT_BGRA32 raster image.
|
||||
*
|
||||
* On success, @image extents are reset to pixel extents with origin
|
||||
* `(0, 0)`. Rows in the resulting image buffer are stored bottom-to-top.
|
||||
* On failure, @image is left unchanged.
|
||||
*
|
||||
* Return value: `true` if deserialization succeeded, `false` otherwise
|
||||
*
|
||||
* Since: 13.1.0
|
||||
**/
|
||||
hb_bool_t
|
||||
hb_raster_image_deserialize_from_png_or_fail (hb_raster_image_t *image,
|
||||
hb_blob_t *png)
|
||||
{
|
||||
return image->deserialize_from_png (png);
|
||||
}
|
||||
|
||||
/**
|
||||
* hb_raster_image_serialize_to_png_or_fail:
|
||||
* @image: a raster image
|
||||
*
|
||||
* Serializes @image to a PNG blob.
|
||||
*
|
||||
* Currently only #HB_RASTER_FORMAT_BGRA32 images are supported.
|
||||
*
|
||||
* Return value: (transfer full):
|
||||
* A newly allocated PNG #hb_blob_t, or `NULL` on failure
|
||||
*
|
||||
* Since: 13.1.0
|
||||
**/
|
||||
hb_blob_t *
|
||||
hb_raster_image_serialize_to_png_or_fail (hb_raster_image_t *image)
|
||||
{
|
||||
return image->serialize_to_png_or_fail ();
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright © 2026 Behdad Esfahbod
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Author(s): Behdad Esfahbod
|
||||
*/
|
||||
|
||||
#ifndef HB_RASTER_IMAGE_HH
|
||||
#define HB_RASTER_IMAGE_HH
|
||||
|
||||
#include "hb.hh"
|
||||
#include "hb-raster-utils.hh"
|
||||
#include "hb-raster.h"
|
||||
#include "hb-object.hh"
|
||||
#include "hb-vector.hh"
|
||||
|
||||
|
||||
/* hb_raster_image_t — pixel artifact */
|
||||
struct hb_raster_image_t
|
||||
{
|
||||
hb_object_header_t header;
|
||||
|
||||
hb_vector_t<uint8_t> buffer;
|
||||
hb_raster_extents_t extents = {};
|
||||
hb_raster_format_t format = HB_RASTER_FORMAT_A8;
|
||||
|
||||
HB_INTERNAL static unsigned bytes_per_pixel (hb_raster_format_t format);
|
||||
HB_INTERNAL bool configure (hb_raster_format_t format, hb_raster_extents_t extents);
|
||||
HB_INTERNAL bool deserialize_from_png (hb_blob_t *png);
|
||||
HB_INTERNAL hb_blob_t *serialize_to_png_or_fail () const;
|
||||
HB_INTERNAL void clear ();
|
||||
HB_INTERNAL const uint8_t *get_buffer () const;
|
||||
HB_INTERNAL void composite_from (const hb_raster_image_t *src,
|
||||
hb_paint_composite_mode_t mode);
|
||||
};
|
||||
|
||||
/* Composite src image onto dst. */
|
||||
HB_INTERNAL void
|
||||
hb_raster_image_composite (hb_raster_image_t *dst,
|
||||
const hb_raster_image_t *src,
|
||||
hb_paint_composite_mode_t mode);
|
||||
|
||||
|
||||
#endif /* HB_RASTER_IMAGE_HH */
|
||||
+2253
File diff suppressed because it is too large
Load Diff
+205
@@ -0,0 +1,205 @@
|
||||
/*
|
||||
* Copyright © 2026 Behdad Esfahbod
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Author(s): Behdad Esfahbod
|
||||
*/
|
||||
|
||||
#ifndef HB_RASTER_PAINT_HH
|
||||
#define HB_RASTER_PAINT_HH
|
||||
|
||||
#include "hb.hh"
|
||||
|
||||
#include "hb-raster-image.hh"
|
||||
#include "hb-geometry.hh"
|
||||
|
||||
/* hb_raster_clip_t — alpha mask for clipping */
|
||||
struct hb_raster_clip_t
|
||||
{
|
||||
hb_vector_t<uint8_t> alpha; /* A8 mask, same extents as root surface */
|
||||
unsigned width = 0;
|
||||
unsigned height = 0;
|
||||
unsigned stride = 0;
|
||||
|
||||
/* Fast path: simple rectangle (no alpha buffer needed) */
|
||||
bool is_rect = true;
|
||||
int rect_x0 = 0, rect_y0 = 0;
|
||||
int rect_x1 = 0, rect_y1 = 0;
|
||||
|
||||
/* Bounding box of non-zero alpha region (valid for both rect and mask) */
|
||||
unsigned min_x = 0, min_y = 0;
|
||||
unsigned max_x = 0, max_y = 0;
|
||||
|
||||
void init_full (unsigned w, unsigned h)
|
||||
{
|
||||
width = w;
|
||||
height = h;
|
||||
stride = (w + 3u) & ~3u;
|
||||
is_rect = true;
|
||||
rect_x0 = 0;
|
||||
rect_y0 = 0;
|
||||
rect_x1 = (int) w;
|
||||
rect_y1 = (int) h;
|
||||
min_x = 0;
|
||||
min_y = 0;
|
||||
max_x = w;
|
||||
max_y = h;
|
||||
}
|
||||
|
||||
void update_bounds_from_rect ()
|
||||
{
|
||||
min_x = (unsigned) hb_max (rect_x0, 0);
|
||||
min_y = (unsigned) hb_max (rect_y0, 0);
|
||||
max_x = (unsigned) hb_max (hb_min (rect_x1, (int) width), 0);
|
||||
max_y = (unsigned) hb_max (hb_min (rect_y1, (int) height), 0);
|
||||
}
|
||||
|
||||
uint8_t get_alpha (unsigned x, unsigned y) const
|
||||
{
|
||||
if (is_rect)
|
||||
return ((int) x >= rect_x0 && (int) x < rect_x1 &&
|
||||
(int) y >= rect_y0 && (int) y < rect_y1) ? 255 : 0;
|
||||
if (x >= width || y >= height) return 0;
|
||||
return alpha[y * stride + x];
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/* hb_raster_paint_t — color glyph paint context */
|
||||
struct hb_raster_paint_t
|
||||
{
|
||||
hb_object_header_t header;
|
||||
|
||||
/* Configuration */
|
||||
hb_transform_t<> base_transform = {1, 0, 0, 1, 0, 0};
|
||||
float x_scale_factor = 1.f;
|
||||
float y_scale_factor = 1.f;
|
||||
hb_raster_extents_t fixed_extents = {};
|
||||
bool has_extents = false;
|
||||
hb_color_t foreground = HB_COLOR (0, 0, 0, 255);
|
||||
hb_map_t *custom_palette = nullptr;
|
||||
|
||||
/* SVG rendering state */
|
||||
hb_codepoint_t svg_glyph = 0;
|
||||
hb_font_t *svg_font = nullptr;
|
||||
unsigned svg_palette = 0;
|
||||
|
||||
/* Stacks */
|
||||
hb_vector_t<hb_transform_t<>> transform_stack;
|
||||
hb_vector_t<hb_raster_clip_t> clip_stack;
|
||||
hb_vector_t<hb_raster_clip_t> clip_cache;
|
||||
hb_vector_t<hb_raster_image_t *> surface_stack;
|
||||
|
||||
/* Cached surface pool (freelist for reuse across push/pop group) */
|
||||
hb_vector_t<hb_raster_image_t *> surface_cache;
|
||||
hb_vector_t<hb_color_stop_t> scratch_color_stops;
|
||||
|
||||
/* Internal rasterizer for clip-to-glyph */
|
||||
hb_raster_draw_t *clip_rdr = nullptr;
|
||||
|
||||
/* Helpers */
|
||||
|
||||
hb_raster_image_t *acquire_surface ()
|
||||
{
|
||||
hb_raster_image_t *img;
|
||||
if (surface_cache.length)
|
||||
img = surface_cache.pop ();
|
||||
else
|
||||
{
|
||||
img = hb_raster_image_create_or_fail ();
|
||||
if (unlikely (!img)) return nullptr;
|
||||
}
|
||||
|
||||
if (unlikely (!img->configure (HB_RASTER_FORMAT_BGRA32, fixed_extents)))
|
||||
{
|
||||
hb_raster_image_destroy (img);
|
||||
return nullptr;
|
||||
}
|
||||
img->clear ();
|
||||
return img;
|
||||
}
|
||||
|
||||
void release_surface (hb_raster_image_t *img)
|
||||
{
|
||||
if (!surface_cache.push_or_fail (img))
|
||||
hb_raster_image_destroy (img);
|
||||
}
|
||||
|
||||
hb_raster_clip_t acquire_clip (unsigned w, unsigned h)
|
||||
{
|
||||
hb_raster_clip_t clip;
|
||||
if (clip_cache.length)
|
||||
clip = clip_cache.pop ();
|
||||
clip.width = w;
|
||||
clip.height = h;
|
||||
clip.stride = (w + 3u) & ~3u;
|
||||
clip.is_rect = false;
|
||||
return clip;
|
||||
}
|
||||
|
||||
void release_clip (hb_raster_clip_t &&clip)
|
||||
{
|
||||
if (clip.alpha.arrayZ)
|
||||
clip_cache.push (std::move (clip));
|
||||
}
|
||||
|
||||
void release_all_clips ()
|
||||
{
|
||||
while (clip_stack.length)
|
||||
release_clip (clip_stack.pop ());
|
||||
}
|
||||
|
||||
hb_raster_image_t *current_surface ()
|
||||
{
|
||||
return surface_stack.length ? surface_stack.tail () : nullptr;
|
||||
}
|
||||
|
||||
hb_raster_clip_t ¤t_clip ()
|
||||
{
|
||||
return clip_stack.tail ();
|
||||
}
|
||||
|
||||
hb_transform_t<> ¤t_transform ()
|
||||
{
|
||||
return transform_stack.tail ();
|
||||
}
|
||||
|
||||
void apply_scale_factor (hb_transform_t<> &t) const
|
||||
{
|
||||
t.xx /= x_scale_factor;
|
||||
t.xy /= x_scale_factor;
|
||||
t.x0 /= x_scale_factor;
|
||||
t.yx /= y_scale_factor;
|
||||
t.yy /= y_scale_factor;
|
||||
t.y0 /= y_scale_factor;
|
||||
}
|
||||
|
||||
hb_transform_t<> current_effective_transform ()
|
||||
{
|
||||
hb_transform_t<> t = current_transform ();
|
||||
apply_scale_factor (t);
|
||||
return t;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif /* HB_RASTER_PAINT_HH */
|
||||
+501
@@ -0,0 +1,501 @@
|
||||
/*
|
||||
* Copyright © 2026 Behdad Esfahbod
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Author(s): Behdad Esfahbod
|
||||
*/
|
||||
|
||||
#ifndef HB_NO_RASTER_SVG
|
||||
|
||||
#include "hb.hh"
|
||||
|
||||
#include "hb-raster-svg-base.hh"
|
||||
#include "hb-raster-svg-parse.hh"
|
||||
|
||||
void
|
||||
svg_parse_style_props (hb_svg_str_t style, hb_svg_style_props_t *out)
|
||||
{
|
||||
if (style.is_null ()) return;
|
||||
const char *p = style.data;
|
||||
const char *end = style.data + style.len;
|
||||
while (p < end)
|
||||
{
|
||||
while (p < end && (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r' || *p == ';'))
|
||||
p++;
|
||||
if (p >= end) break;
|
||||
const char *name_start = p;
|
||||
while (p < end && *p != ':' && *p != ';')
|
||||
p++;
|
||||
const char *name_end = p;
|
||||
while (name_end > name_start &&
|
||||
(name_end[-1] == ' ' || name_end[-1] == '\t' || name_end[-1] == '\n' || name_end[-1] == '\r'))
|
||||
name_end--;
|
||||
if (p >= end || *p != ':')
|
||||
{
|
||||
while (p < end && *p != ';') p++;
|
||||
continue;
|
||||
}
|
||||
p++; /* skip ':' */
|
||||
const char *value_start = p;
|
||||
while (p < end && *p != ';')
|
||||
p++;
|
||||
const char *value_end = p;
|
||||
while (value_start < value_end &&
|
||||
(*value_start == ' ' || *value_start == '\t' || *value_start == '\n' || *value_start == '\r'))
|
||||
value_start++;
|
||||
while (value_end > value_start &&
|
||||
(value_end[-1] == ' ' || value_end[-1] == '\t' || value_end[-1] == '\n' || value_end[-1] == '\r'))
|
||||
value_end--;
|
||||
|
||||
hb_svg_str_t prop_name = {name_start, (unsigned) (name_end - name_start)};
|
||||
hb_svg_str_t prop_value = {value_start, (unsigned) (value_end - value_start)};
|
||||
if (prop_name.len)
|
||||
{
|
||||
switch (hb_svg_ascii_lower (prop_name.data[0]))
|
||||
{
|
||||
case 'c':
|
||||
if (prop_name.len == 9 && prop_name.eq_ascii_ci ("clip-path")) out->clip_path = prop_value;
|
||||
else if (prop_name.len == 5 && prop_name.eq_ascii_ci ("color")) out->color = prop_value;
|
||||
else if (prop_name.len == 2 && prop_name.eq_ascii_ci ("cx")) out->cx = prop_value;
|
||||
else if (prop_name.len == 2 && prop_name.eq_ascii_ci ("cy")) out->cy = prop_value;
|
||||
break;
|
||||
case 'd':
|
||||
if (prop_name.len == 7 && prop_name.eq_ascii_ci ("display")) out->display = prop_value;
|
||||
else if (prop_name.len == 1 && prop_name.eq_ascii_ci ("d")) out->d = prop_value;
|
||||
break;
|
||||
case 'f':
|
||||
if (prop_name.len == 4 && prop_name.eq_ascii_ci ("fill")) out->fill = prop_value;
|
||||
else if (prop_name.len == 12 && prop_name.eq_ascii_ci ("fill-opacity")) out->fill_opacity = prop_value;
|
||||
else if (prop_name.len == 2 && prop_name.eq_ascii_ci ("fx")) out->fx = prop_value;
|
||||
else if (prop_name.len == 2 && prop_name.eq_ascii_ci ("fy")) out->fy = prop_value;
|
||||
else if (prop_name.len == 2 && prop_name.eq_ascii_ci ("fr")) out->fr = prop_value;
|
||||
break;
|
||||
case 'g':
|
||||
if ((prop_name.len == 13 && prop_name.eq_ascii_ci ("gradientunits")) ||
|
||||
(prop_name.len == 14 && prop_name.eq_ascii_ci ("gradient-units"))) out->gradient_units = prop_value;
|
||||
else if ((prop_name.len == 17 && prop_name.eq_ascii_ci ("gradienttransform")) ||
|
||||
(prop_name.len == 18 && prop_name.eq_ascii_ci ("gradient-transform"))) out->gradient_transform = prop_value;
|
||||
break;
|
||||
case 'h':
|
||||
if (prop_name.len == 6 && prop_name.eq_ascii_ci ("height")) out->height = prop_value;
|
||||
break;
|
||||
case 'o':
|
||||
if (prop_name.len == 7 && prop_name.eq_ascii_ci ("opacity")) out->opacity = prop_value;
|
||||
else if (prop_name.len == 6 && prop_name.eq_ascii_ci ("offset")) out->offset = prop_value;
|
||||
break;
|
||||
case 'p':
|
||||
if (prop_name.len == 6 && prop_name.eq_ascii_ci ("points")) out->points = prop_value;
|
||||
break;
|
||||
case 'r':
|
||||
if (prop_name.len == 1 && prop_name.eq_ascii_ci ("r")) out->r = prop_value;
|
||||
else if (prop_name.len == 2 && prop_name.eq_ascii_ci ("rx")) out->rx = prop_value;
|
||||
else if (prop_name.len == 2 && prop_name.eq_ascii_ci ("ry")) out->ry = prop_value;
|
||||
break;
|
||||
case 's':
|
||||
if (prop_name.len == 10 && prop_name.eq_ascii_ci ("stop-color")) out->stop_color = prop_value;
|
||||
else if (prop_name.len == 12 && prop_name.eq_ascii_ci ("stop-opacity")) out->stop_opacity = prop_value;
|
||||
else if ((prop_name.len == 12 && prop_name.eq_ascii_ci ("spreadmethod")) ||
|
||||
(prop_name.len == 13 && prop_name.eq_ascii_ci ("spread-method"))) out->spread_method = prop_value;
|
||||
break;
|
||||
case 't':
|
||||
if (prop_name.len == 9 && prop_name.eq_ascii_ci ("transform")) out->transform = prop_value;
|
||||
break;
|
||||
case 'v':
|
||||
if (prop_name.len == 10 && prop_name.eq_ascii_ci ("visibility")) out->visibility = prop_value;
|
||||
break;
|
||||
case 'w':
|
||||
if (prop_name.len == 5 && prop_name.eq_ascii_ci ("width")) out->width = prop_value;
|
||||
break;
|
||||
case 'x':
|
||||
if (prop_name.len == 1 && prop_name.eq_ascii_ci ("x")) out->x = prop_value;
|
||||
else if (prop_name.len == 2 && prop_name.eq_ascii_ci ("x1")) out->x1 = prop_value;
|
||||
else if (prop_name.len == 2 && prop_name.eq_ascii_ci ("x2")) out->x2 = prop_value;
|
||||
break;
|
||||
case 'y':
|
||||
if (prop_name.len == 1 && prop_name.eq_ascii_ci ("y")) out->y = prop_value;
|
||||
else if (prop_name.len == 2 && prop_name.eq_ascii_ci ("y1")) out->y1 = prop_value;
|
||||
else if (prop_name.len == 2 && prop_name.eq_ascii_ci ("y2")) out->y2 = prop_value;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (p < end && *p == ';') p++;
|
||||
}
|
||||
}
|
||||
|
||||
float
|
||||
svg_parse_number_or_percent (hb_svg_str_t s, bool *is_percent)
|
||||
{
|
||||
if (is_percent) *is_percent = false;
|
||||
s = s.trim ();
|
||||
if (!s.len) return 0.f;
|
||||
if (s.data[s.len - 1] == '%')
|
||||
{
|
||||
if (is_percent) *is_percent = true;
|
||||
hb_svg_str_t n = {s.data, s.len - 1};
|
||||
return n.to_float () / 100.f;
|
||||
}
|
||||
return s.to_float ();
|
||||
}
|
||||
|
||||
hb_svg_str_t
|
||||
hb_raster_svg_find_href_attr (const hb_svg_xml_parser_t &parser)
|
||||
{
|
||||
hb_svg_str_t href = parser.find_attr ("href");
|
||||
if (href.is_null ())
|
||||
href = parser.find_attr ("xlink:href");
|
||||
return href;
|
||||
}
|
||||
|
||||
bool
|
||||
hb_raster_svg_parse_id_ref (hb_svg_str_t s,
|
||||
hb_svg_str_t *out_id,
|
||||
hb_svg_str_t *out_tail)
|
||||
{
|
||||
if (out_id) *out_id = {};
|
||||
if (out_tail) *out_tail = {};
|
||||
s = s.trim ();
|
||||
|
||||
if (s.len && s.data[0] == '#')
|
||||
{
|
||||
hb_svg_str_t id = {s.data + 1, s.len - 1};
|
||||
id = id.trim ();
|
||||
if (!id.len)
|
||||
return false;
|
||||
if (out_id) *out_id = id;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!s.starts_with_ascii_ci ("url("))
|
||||
return false;
|
||||
|
||||
const char *p = s.data + 4;
|
||||
const char *end = s.data + s.len;
|
||||
while (p < end && (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r')) p++;
|
||||
|
||||
const char *q = p;
|
||||
char quote = 0;
|
||||
while (q < end)
|
||||
{
|
||||
char c = *q;
|
||||
if (quote)
|
||||
{
|
||||
if (c == quote) quote = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (c == '"' || c == '\'') quote = c;
|
||||
else if (c == ')') break;
|
||||
}
|
||||
q++;
|
||||
}
|
||||
if (q >= end || *q != ')')
|
||||
return false;
|
||||
|
||||
hb_svg_str_t id = {(const char *) p, (unsigned) (q - p)};
|
||||
id = id.trim ();
|
||||
if (id.len >= 2 &&
|
||||
((id.data[0] == '\'' && id.data[id.len - 1] == '\'') ||
|
||||
(id.data[0] == '"' && id.data[id.len - 1] == '"')))
|
||||
{
|
||||
id.data++;
|
||||
id.len -= 2;
|
||||
}
|
||||
id = id.trim ();
|
||||
if (id.len && id.data[0] == '#')
|
||||
{
|
||||
id.data++;
|
||||
id.len--;
|
||||
}
|
||||
id = id.trim ();
|
||||
if (!id.len)
|
||||
return false;
|
||||
|
||||
if (out_id) *out_id = id;
|
||||
if (out_tail)
|
||||
{
|
||||
const char *tail = q + 1;
|
||||
while (tail < end && (*tail == ' ' || *tail == '\t' || *tail == '\n' || *tail == '\r')) tail++;
|
||||
*out_tail = {(const char *) tail, (unsigned) (end - tail)};
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
hb_raster_svg_parse_local_id_ref (hb_svg_str_t s,
|
||||
hb_svg_str_t *out_id,
|
||||
hb_svg_str_t *out_tail)
|
||||
{
|
||||
if (out_id) *out_id = {};
|
||||
if (out_tail) *out_tail = {};
|
||||
s = s.trim ();
|
||||
|
||||
if (s.len && s.data[0] == '#')
|
||||
{
|
||||
hb_svg_str_t id = {s.data + 1, s.len - 1};
|
||||
id = id.trim ();
|
||||
if (!id.len)
|
||||
return false;
|
||||
if (out_id) *out_id = id;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!s.starts_with_ascii_ci ("url("))
|
||||
return false;
|
||||
|
||||
hb_svg_str_t id;
|
||||
if (!hb_raster_svg_parse_id_ref (s, &id, out_tail))
|
||||
return false;
|
||||
|
||||
const char *p = s.data + 4;
|
||||
const char *end = s.data + s.len;
|
||||
while (p < end && (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r')) p++;
|
||||
if (p < end && (*p == '\'' || *p == '"'))
|
||||
{
|
||||
p++;
|
||||
while (p < end && (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r')) p++;
|
||||
if (p >= end || *p != '#')
|
||||
return false;
|
||||
}
|
||||
else if (p >= end || *p != '#')
|
||||
return false;
|
||||
|
||||
if (out_id) *out_id = id;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
hb_raster_svg_find_element_by_id (const char *doc_start,
|
||||
unsigned doc_len,
|
||||
const OT::SVG::accelerator_t *svg_accel,
|
||||
const OT::SVG::svg_doc_cache_t *doc_cache,
|
||||
hb_svg_str_t id,
|
||||
const char **found)
|
||||
{
|
||||
*found = nullptr;
|
||||
if (!doc_start || !doc_len || !id.len)
|
||||
return false;
|
||||
|
||||
if (doc_cache && svg_accel)
|
||||
{
|
||||
unsigned start = 0, end = 0;
|
||||
OT::SVG::svg_id_span_t key = {id.data, id.len};
|
||||
if (svg_accel->doc_cache_find_id_span (doc_cache, key, &start, &end))
|
||||
{
|
||||
if (start < doc_len && end <= doc_len && start < end)
|
||||
{
|
||||
*found = doc_start + start;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hb_svg_xml_parser_t search (doc_start, doc_len);
|
||||
while (true)
|
||||
{
|
||||
hb_svg_token_type_t tok = search.next ();
|
||||
if (tok == SVG_TOKEN_EOF) break;
|
||||
if (tok != SVG_TOKEN_OPEN_TAG && tok != SVG_TOKEN_SELF_CLOSE_TAG) continue;
|
||||
hb_svg_str_t attr_id = search.find_attr ("id");
|
||||
if (attr_id.len == id.len && 0 == memcmp (attr_id.data, id.data, id.len))
|
||||
{
|
||||
*found = search.tag_start;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
hb_raster_svg_parse_viewbox (hb_svg_str_t viewbox_str,
|
||||
float *x,
|
||||
float *y,
|
||||
float *w,
|
||||
float *h)
|
||||
{
|
||||
if (!viewbox_str.len)
|
||||
return false;
|
||||
hb_svg_float_parser_t vb_fp (viewbox_str);
|
||||
float vb_x = vb_fp.next_float ();
|
||||
float vb_y = vb_fp.next_float ();
|
||||
float vb_w = vb_fp.next_float ();
|
||||
float vb_h = vb_fp.next_float ();
|
||||
if (vb_w <= 0.f || vb_h <= 0.f)
|
||||
return false;
|
||||
if (x) *x = vb_x;
|
||||
if (y) *y = vb_y;
|
||||
if (w) *w = vb_w;
|
||||
if (h) *h = vb_h;
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline float
|
||||
svg_align_offset (hb_svg_str_t align,
|
||||
float leftover,
|
||||
char axis)
|
||||
{
|
||||
if (leftover <= 0.f) return 0.f;
|
||||
if ((axis == 'x' && align.starts_with_ascii_ci ("xMin")) ||
|
||||
(axis == 'y' && (align.eq_ascii_ci ("xMinYMin") ||
|
||||
align.eq_ascii_ci ("xMidYMin") ||
|
||||
align.eq_ascii_ci ("xMaxYMin"))))
|
||||
return 0.f;
|
||||
if ((axis == 'x' && align.starts_with_ascii_ci ("xMax")) ||
|
||||
(axis == 'y' && (align.eq_ascii_ci ("xMinYMax") ||
|
||||
align.eq_ascii_ci ("xMidYMax") ||
|
||||
align.eq_ascii_ci ("xMaxYMax"))))
|
||||
return leftover;
|
||||
return leftover * 0.5f;
|
||||
}
|
||||
|
||||
bool
|
||||
hb_raster_svg_compute_viewbox_transform (float viewport_w,
|
||||
float viewport_h,
|
||||
float vb_x,
|
||||
float vb_y,
|
||||
float vb_w,
|
||||
float vb_h,
|
||||
hb_svg_str_t preserve_aspect_ratio,
|
||||
hb_svg_transform_t *out)
|
||||
{
|
||||
if (!(viewport_w > 0.f && viewport_h > 0.f && vb_w > 0.f && vb_h > 0.f))
|
||||
return false;
|
||||
|
||||
hb_svg_str_t par = preserve_aspect_ratio.trim ();
|
||||
if (par.starts_with_ascii_ci ("defer"))
|
||||
{
|
||||
const char *p = par.data + 5;
|
||||
const char *end = par.data + par.len;
|
||||
while (p < end && (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r')) p++;
|
||||
par = hb_svg_str_t (p, (unsigned) (end - p));
|
||||
}
|
||||
if (!par.len)
|
||||
par = hb_svg_str_t ("xMidYMid meet", 12);
|
||||
|
||||
bool is_none = false;
|
||||
bool is_slice = false;
|
||||
hb_svg_str_t align = par;
|
||||
const char *p = par.data;
|
||||
const char *end = par.data + par.len;
|
||||
while (p < end && *p != ' ' && *p != '\t' && *p != '\n' && *p != '\r') p++;
|
||||
align = hb_svg_str_t (par.data, (unsigned) (p - par.data)).trim ();
|
||||
|
||||
if (par.starts_with_ascii_ci ("none"))
|
||||
is_none = true;
|
||||
else if (align.starts_with_ascii_ci ("x"))
|
||||
{
|
||||
const char *mode = p;
|
||||
while (mode < end && (*mode == ' ' || *mode == '\t' || *mode == '\n' || *mode == '\r')) mode++;
|
||||
if (mode < end && hb_svg_str_t (mode, (unsigned) (end - mode)).starts_with_ascii_ci ("slice"))
|
||||
is_slice = true;
|
||||
}
|
||||
|
||||
hb_svg_transform_t t;
|
||||
if (is_none)
|
||||
{
|
||||
t.xx = viewport_w / vb_w;
|
||||
t.yy = viewport_h / vb_h;
|
||||
t.dx = -vb_x * t.xx;
|
||||
t.dy = -vb_y * t.yy;
|
||||
*out = t;
|
||||
return true;
|
||||
}
|
||||
|
||||
float sx = viewport_w / vb_w;
|
||||
float sy = viewport_h / vb_h;
|
||||
float s = is_slice ? hb_max (sx, sy) : hb_min (sx, sy);
|
||||
float scaled_w = vb_w * s;
|
||||
float scaled_h = vb_h * s;
|
||||
float leftover_x = viewport_w - scaled_w;
|
||||
float leftover_y = viewport_h - scaled_h;
|
||||
|
||||
if (!align.starts_with_ascii_ci ("x"))
|
||||
align = hb_svg_str_t ("xMidYMid", 8);
|
||||
|
||||
t.xx = s;
|
||||
t.yy = s;
|
||||
t.dx = svg_align_offset (align, leftover_x, 'x') - vb_x * s;
|
||||
t.dy = svg_align_offset (align, leftover_y, 'y') - vb_y * s;
|
||||
*out = t;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
hb_raster_svg_compute_use_target_viewbox_transform (hb_svg_xml_parser_t &target_parser,
|
||||
float use_w,
|
||||
float use_h,
|
||||
hb_svg_transform_t *out)
|
||||
{
|
||||
if (!(target_parser.tag_name.eq ("svg") || target_parser.tag_name.eq ("symbol")))
|
||||
return false;
|
||||
|
||||
float viewport_w = use_w;
|
||||
float viewport_h = use_h;
|
||||
hb_svg_style_props_t target_style_props;
|
||||
svg_parse_style_props (target_parser.find_attr ("style"), &target_style_props);
|
||||
if (viewport_w <= 0.f)
|
||||
viewport_w = hb_raster_svg_parse_non_percent_length (svg_pick_attr_or_style (target_parser, target_style_props.width, "width"));
|
||||
if (viewport_h <= 0.f)
|
||||
viewport_h = hb_raster_svg_parse_non_percent_length (svg_pick_attr_or_style (target_parser, target_style_props.height, "height"));
|
||||
|
||||
float vb_x = 0.f, vb_y = 0.f, vb_w = 0.f, vb_h = 0.f;
|
||||
if (!hb_raster_svg_parse_viewbox (target_parser.find_attr ("viewBox"), &vb_x, &vb_y, &vb_w, &vb_h))
|
||||
return false;
|
||||
|
||||
if (!(viewport_w > 0.f && viewport_h > 0.f))
|
||||
{
|
||||
viewport_w = vb_w;
|
||||
viewport_h = vb_h;
|
||||
}
|
||||
|
||||
return hb_raster_svg_compute_viewbox_transform (viewport_w, viewport_h, vb_x, vb_y, vb_w, vb_h,
|
||||
target_parser.find_attr ("preserveAspectRatio"),
|
||||
out);
|
||||
}
|
||||
|
||||
void
|
||||
hb_raster_svg_parse_use_geometry (hb_svg_xml_parser_t &parser,
|
||||
float *x,
|
||||
float *y,
|
||||
float *w,
|
||||
float *h)
|
||||
{
|
||||
hb_svg_style_props_t style_props;
|
||||
svg_parse_style_props (parser.find_attr ("style"), &style_props);
|
||||
|
||||
if (x) *x = hb_raster_svg_parse_non_percent_length (svg_pick_attr_or_style (parser, style_props.x, "x"));
|
||||
if (y) *y = hb_raster_svg_parse_non_percent_length (svg_pick_attr_or_style (parser, style_props.y, "y"));
|
||||
if (w) *w = hb_raster_svg_parse_non_percent_length (svg_pick_attr_or_style (parser, style_props.width, "width"));
|
||||
if (h) *h = hb_raster_svg_parse_non_percent_length (svg_pick_attr_or_style (parser, style_props.height, "height"));
|
||||
}
|
||||
|
||||
float
|
||||
hb_raster_svg_parse_non_percent_length (hb_svg_str_t s)
|
||||
{
|
||||
bool is_percent = false;
|
||||
float v = svg_parse_number_or_percent (s, &is_percent);
|
||||
return is_percent ? 0.f : v;
|
||||
}
|
||||
|
||||
#endif /* !HB_NO_RASTER_SVG */
|
||||
+228
@@ -0,0 +1,228 @@
|
||||
/*
|
||||
* Copyright © 2026 Behdad Esfahbod
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Author(s): Behdad Esfahbod
|
||||
*/
|
||||
|
||||
#ifndef HB_RASTER_SVG_BASE_HH
|
||||
#define HB_RASTER_SVG_BASE_HH
|
||||
|
||||
#include "hb.hh"
|
||||
#include "OT/Color/svg/svg.hh"
|
||||
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static inline char
|
||||
hb_svg_ascii_lower (char c)
|
||||
{
|
||||
if (c >= 'A' && c <= 'Z')
|
||||
return c + ('a' - 'A');
|
||||
return c;
|
||||
}
|
||||
|
||||
struct hb_svg_str_t
|
||||
{
|
||||
const char *data;
|
||||
unsigned len;
|
||||
|
||||
hb_svg_str_t () : data (nullptr), len (0) {}
|
||||
hb_svg_str_t (const char *d, unsigned l) : data (d), len (l) {}
|
||||
|
||||
bool is_null () const { return !data; }
|
||||
|
||||
bool eq (const char *s) const
|
||||
{
|
||||
unsigned slen = (unsigned) strlen (s);
|
||||
return len == slen && memcmp (data, s, len) == 0;
|
||||
}
|
||||
|
||||
bool starts_with (const char *prefix) const
|
||||
{
|
||||
unsigned plen = (unsigned) strlen (prefix);
|
||||
return len >= plen && memcmp (data, prefix, plen) == 0;
|
||||
}
|
||||
|
||||
bool eq_ascii_ci (const char *lit) const
|
||||
{
|
||||
unsigned n = (unsigned) strlen (lit);
|
||||
if (len != n) return false;
|
||||
for (unsigned i = 0; i < n; i++)
|
||||
if (hb_svg_ascii_lower (data[i]) != hb_svg_ascii_lower (lit[i]))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool starts_with_ascii_ci (const char *lit) const
|
||||
{
|
||||
unsigned n = (unsigned) strlen (lit);
|
||||
if (len < n) return false;
|
||||
for (unsigned i = 0; i < n; i++)
|
||||
if (hb_svg_ascii_lower (data[i]) != hb_svg_ascii_lower (lit[i]))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
float to_float () const
|
||||
{
|
||||
if (!data || !len) return 0.f;
|
||||
char buf[64];
|
||||
unsigned n = hb_min (len, (unsigned) sizeof (buf) - 1);
|
||||
memcpy (buf, data, n);
|
||||
buf[n] = '\0';
|
||||
float v = strtof (buf, nullptr);
|
||||
return std::isfinite (v) ? v : 0.f;
|
||||
}
|
||||
|
||||
hb_svg_str_t trim_left () const
|
||||
{
|
||||
const char *p = data;
|
||||
unsigned l = len;
|
||||
while (l && (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r'))
|
||||
{
|
||||
p++;
|
||||
l--;
|
||||
}
|
||||
return {p, l};
|
||||
}
|
||||
|
||||
hb_svg_str_t trim () const
|
||||
{
|
||||
hb_svg_str_t s = trim_left ();
|
||||
while (s.len && (s.data[s.len - 1] == ' ' || s.data[s.len - 1] == '\t' ||
|
||||
s.data[s.len - 1] == '\n' || s.data[s.len - 1] == '\r'))
|
||||
s.len--;
|
||||
return s;
|
||||
}
|
||||
};
|
||||
|
||||
struct hb_svg_style_props_t
|
||||
{
|
||||
hb_svg_str_t fill;
|
||||
hb_svg_str_t fill_opacity;
|
||||
hb_svg_str_t opacity;
|
||||
hb_svg_str_t transform;
|
||||
hb_svg_str_t clip_path;
|
||||
hb_svg_str_t display;
|
||||
hb_svg_str_t color;
|
||||
hb_svg_str_t visibility;
|
||||
hb_svg_str_t offset;
|
||||
hb_svg_str_t stop_color;
|
||||
hb_svg_str_t stop_opacity;
|
||||
hb_svg_str_t spread_method;
|
||||
hb_svg_str_t gradient_units;
|
||||
hb_svg_str_t gradient_transform;
|
||||
hb_svg_str_t x;
|
||||
hb_svg_str_t y;
|
||||
hb_svg_str_t width;
|
||||
hb_svg_str_t height;
|
||||
hb_svg_str_t cx;
|
||||
hb_svg_str_t cy;
|
||||
hb_svg_str_t r;
|
||||
hb_svg_str_t fx;
|
||||
hb_svg_str_t fy;
|
||||
hb_svg_str_t fr;
|
||||
hb_svg_str_t rx;
|
||||
hb_svg_str_t ry;
|
||||
hb_svg_str_t x1;
|
||||
hb_svg_str_t y1;
|
||||
hb_svg_str_t x2;
|
||||
hb_svg_str_t y2;
|
||||
hb_svg_str_t points;
|
||||
hb_svg_str_t d;
|
||||
};
|
||||
|
||||
struct hb_svg_xml_parser_t;
|
||||
struct hb_svg_transform_t;
|
||||
|
||||
HB_INTERNAL void svg_parse_style_props (hb_svg_str_t style, hb_svg_style_props_t *out);
|
||||
HB_INTERNAL float svg_parse_number_or_percent (hb_svg_str_t s, bool *is_percent);
|
||||
HB_INTERNAL hb_svg_str_t hb_raster_svg_find_href_attr (const hb_svg_xml_parser_t &parser);
|
||||
HB_INTERNAL bool hb_raster_svg_parse_id_ref (hb_svg_str_t s,
|
||||
hb_svg_str_t *out_id,
|
||||
hb_svg_str_t *out_tail);
|
||||
HB_INTERNAL bool hb_raster_svg_parse_local_id_ref (hb_svg_str_t s,
|
||||
hb_svg_str_t *out_id,
|
||||
hb_svg_str_t *out_tail);
|
||||
HB_INTERNAL bool hb_raster_svg_find_element_by_id (const char *doc_start,
|
||||
unsigned doc_len,
|
||||
const OT::SVG::accelerator_t *svg_accel,
|
||||
const OT::SVG::svg_doc_cache_t *doc_cache,
|
||||
hb_svg_str_t id,
|
||||
const char **found);
|
||||
HB_INTERNAL bool hb_raster_svg_parse_viewbox (hb_svg_str_t viewbox_str,
|
||||
float *x,
|
||||
float *y,
|
||||
float *w,
|
||||
float *h);
|
||||
HB_INTERNAL bool hb_raster_svg_compute_viewbox_transform (float viewport_w,
|
||||
float viewport_h,
|
||||
float vb_x,
|
||||
float vb_y,
|
||||
float vb_w,
|
||||
float vb_h,
|
||||
hb_svg_str_t preserve_aspect_ratio,
|
||||
hb_svg_transform_t *out);
|
||||
HB_INTERNAL bool hb_raster_svg_compute_use_target_viewbox_transform (hb_svg_xml_parser_t &target_parser,
|
||||
float use_w,
|
||||
float use_h,
|
||||
hb_svg_transform_t *out);
|
||||
HB_INTERNAL void hb_raster_svg_parse_use_geometry (hb_svg_xml_parser_t &parser,
|
||||
float *x,
|
||||
float *y,
|
||||
float *w,
|
||||
float *h);
|
||||
HB_INTERNAL float hb_raster_svg_parse_non_percent_length (hb_svg_str_t s);
|
||||
static inline float
|
||||
svg_parse_float_clamped01 (hb_svg_str_t s)
|
||||
{
|
||||
return hb_clamp (s.to_float (), 0.f, 1.f);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
svg_str_is_inherit (hb_svg_str_t s)
|
||||
{
|
||||
return s.trim ().eq_ascii_ci ("inherit");
|
||||
}
|
||||
|
||||
static inline bool
|
||||
svg_str_is_none (hb_svg_str_t s)
|
||||
{
|
||||
return s.trim ().eq_ascii_ci ("none");
|
||||
}
|
||||
|
||||
static inline bool
|
||||
hb_raster_svg_tag_is_container (hb_svg_str_t tag)
|
||||
{
|
||||
return tag.eq ("g") || tag.eq ("a") || tag.eq ("svg") || tag.eq ("symbol");
|
||||
}
|
||||
|
||||
|
||||
static inline bool
|
||||
hb_raster_svg_tag_is_container_or_use (hb_svg_str_t tag)
|
||||
{
|
||||
return hb_raster_svg_tag_is_container (tag) || tag.eq ("use");
|
||||
}
|
||||
|
||||
#endif /* HB_RASTER_SVG_BASE_HH */
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright © 2026 Behdad Esfahbod
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Author(s): Behdad Esfahbod
|
||||
*/
|
||||
|
||||
#ifndef HB_NO_RASTER_SVG
|
||||
|
||||
#include "hb.hh"
|
||||
|
||||
#include "hb-raster-svg-bbox.hh"
|
||||
|
||||
static void
|
||||
svg_bbox_move_to (hb_draw_funcs_t *dfuncs HB_UNUSED,
|
||||
void *draw_data,
|
||||
hb_draw_state_t *st HB_UNUSED,
|
||||
float to_x, float to_y,
|
||||
void *user_data HB_UNUSED)
|
||||
{
|
||||
((hb_extents_t<> *) draw_data)->add_point (to_x, to_y);
|
||||
}
|
||||
|
||||
static void
|
||||
svg_bbox_line_to (hb_draw_funcs_t *dfuncs HB_UNUSED,
|
||||
void *draw_data,
|
||||
hb_draw_state_t *st HB_UNUSED,
|
||||
float to_x, float to_y,
|
||||
void *user_data HB_UNUSED)
|
||||
{
|
||||
((hb_extents_t<> *) draw_data)->add_point (to_x, to_y);
|
||||
}
|
||||
|
||||
static void
|
||||
svg_bbox_quadratic_to (hb_draw_funcs_t *dfuncs HB_UNUSED,
|
||||
void *draw_data,
|
||||
hb_draw_state_t *st HB_UNUSED,
|
||||
float control_x, float control_y,
|
||||
float to_x, float to_y,
|
||||
void *user_data HB_UNUSED)
|
||||
{
|
||||
hb_extents_t<> *ext = (hb_extents_t<> *) draw_data;
|
||||
ext->add_point (control_x, control_y);
|
||||
ext->add_point (to_x, to_y);
|
||||
}
|
||||
|
||||
static void
|
||||
svg_bbox_cubic_to (hb_draw_funcs_t *dfuncs HB_UNUSED,
|
||||
void *draw_data,
|
||||
hb_draw_state_t *st HB_UNUSED,
|
||||
float control1_x, float control1_y,
|
||||
float control2_x, float control2_y,
|
||||
float to_x, float to_y,
|
||||
void *user_data HB_UNUSED)
|
||||
{
|
||||
hb_extents_t<> *ext = (hb_extents_t<> *) draw_data;
|
||||
ext->add_point (control1_x, control1_y);
|
||||
ext->add_point (control2_x, control2_y);
|
||||
ext->add_point (to_x, to_y);
|
||||
}
|
||||
|
||||
static hb_draw_funcs_t *
|
||||
svg_bbox_draw_funcs ()
|
||||
{
|
||||
static hb_draw_funcs_t *funcs = nullptr;
|
||||
if (unlikely (!funcs))
|
||||
{
|
||||
funcs = hb_draw_funcs_create ();
|
||||
hb_draw_funcs_set_move_to_func (funcs, svg_bbox_move_to, nullptr, nullptr);
|
||||
hb_draw_funcs_set_line_to_func (funcs, svg_bbox_line_to, nullptr, nullptr);
|
||||
hb_draw_funcs_set_quadratic_to_func (funcs, svg_bbox_quadratic_to, nullptr, nullptr);
|
||||
hb_draw_funcs_set_cubic_to_func (funcs, svg_bbox_cubic_to, nullptr, nullptr);
|
||||
hb_draw_funcs_make_immutable (funcs);
|
||||
}
|
||||
return funcs;
|
||||
}
|
||||
|
||||
bool
|
||||
hb_raster_svg_compute_shape_bbox (const hb_svg_shape_emit_data_t &shape,
|
||||
hb_extents_t<> *bbox)
|
||||
{
|
||||
hb_extents_t<> ext;
|
||||
hb_svg_shape_emit_data_t tmp = shape;
|
||||
hb_raster_svg_shape_path_emit (svg_bbox_draw_funcs (), &ext, &tmp);
|
||||
if (ext.is_empty ()) return false;
|
||||
*bbox = ext;
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif /* !HB_NO_RASTER_SVG */
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright © 2026 Behdad Esfahbod
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Author(s): Behdad Esfahbod
|
||||
*/
|
||||
|
||||
#ifndef HB_RASTER_SVG_BBOX_HH
|
||||
#define HB_RASTER_SVG_BBOX_HH
|
||||
|
||||
#include "hb.hh"
|
||||
|
||||
#include "hb-geometry.hh"
|
||||
#include "hb-raster-svg-parse.hh"
|
||||
|
||||
HB_INTERNAL bool
|
||||
hb_raster_svg_compute_shape_bbox (const hb_svg_shape_emit_data_t &shape,
|
||||
hb_extents_t<> *bbox);
|
||||
|
||||
#endif /* HB_RASTER_SVG_BBOX_HH */
|
||||
+528
@@ -0,0 +1,528 @@
|
||||
/*
|
||||
* Copyright © 2026 Behdad Esfahbod
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Author(s): Behdad Esfahbod
|
||||
*/
|
||||
|
||||
#ifndef HB_NO_RASTER_SVG
|
||||
|
||||
#include "hb.hh"
|
||||
|
||||
#include "hb-raster-svg-clip.hh"
|
||||
|
||||
#include "hb-raster.h"
|
||||
#include "hb-raster-paint.hh"
|
||||
#include "hb-raster-svg.hh"
|
||||
#include "hb-raster-svg-base.hh"
|
||||
#include "hb-decycler.hh"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
static inline bool
|
||||
svg_transform_is_identity (const hb_svg_transform_t &t)
|
||||
{
|
||||
return t.xx == 1.f && t.yx == 0.f &&
|
||||
t.xy == 0.f && t.yy == 1.f &&
|
||||
t.dx == 0.f && t.dy == 0.f;
|
||||
}
|
||||
|
||||
static bool
|
||||
svg_parse_element_transform (hb_svg_xml_parser_t &parser,
|
||||
hb_svg_transform_t *out)
|
||||
{
|
||||
hb_svg_style_props_t style_props;
|
||||
svg_parse_style_props (parser.find_attr ("style"), &style_props);
|
||||
hb_svg_str_t transform = svg_pick_attr_or_style (parser, style_props.transform, "transform");
|
||||
if (!transform.len)
|
||||
return false;
|
||||
hb_raster_svg_parse_transform (transform, out);
|
||||
return true;
|
||||
}
|
||||
|
||||
struct hb_svg_clip_collect_context_t
|
||||
{
|
||||
hb_svg_defs_t *defs;
|
||||
hb_svg_clip_path_def_t *clip;
|
||||
const char *doc_start;
|
||||
unsigned doc_len;
|
||||
const OT::SVG::accelerator_t *svg_accel;
|
||||
const OT::SVG::svg_doc_cache_t *doc_cache;
|
||||
hb_decycler_t *use_decycler;
|
||||
bool *had_alloc_failure;
|
||||
};
|
||||
|
||||
static inline void
|
||||
svg_clip_append_shape (hb_svg_clip_collect_context_t *ctx,
|
||||
const hb_svg_shape_emit_data_t &shape,
|
||||
const hb_svg_transform_t &transform)
|
||||
{
|
||||
hb_svg_clip_shape_t clip_shape;
|
||||
clip_shape.shape = shape;
|
||||
if (!svg_transform_is_identity (transform))
|
||||
{
|
||||
clip_shape.has_transform = true;
|
||||
clip_shape.transform = transform;
|
||||
}
|
||||
if (likely (ctx->defs->clip_shapes.push_or_fail (clip_shape)))
|
||||
ctx->clip->shape_count++;
|
||||
else if (ctx->had_alloc_failure)
|
||||
*ctx->had_alloc_failure = true;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
svg_resolve_element_visibility (hb_svg_xml_parser_t &parser,
|
||||
bool parent_visible)
|
||||
{
|
||||
hb_svg_style_props_t style_props;
|
||||
svg_parse_style_props (parser.find_attr ("style"), &style_props);
|
||||
hb_svg_str_t display_str = svg_pick_attr_or_style (parser, style_props.display, "display");
|
||||
hb_svg_str_t visibility_str = svg_pick_attr_or_style (parser, style_props.visibility, "visibility");
|
||||
if (display_str.trim ().eq_ascii_ci ("none"))
|
||||
return false;
|
||||
hb_svg_str_t vis_trim = visibility_str.trim ();
|
||||
if (!vis_trim.len || vis_trim.eq_ascii_ci ("inherit"))
|
||||
return parent_visible;
|
||||
if (vis_trim.eq_ascii_ci ("hidden") ||
|
||||
vis_trim.eq_ascii_ci ("collapse"))
|
||||
return false;
|
||||
if (vis_trim.eq_ascii_ci ("visible"))
|
||||
return true;
|
||||
return parent_visible;
|
||||
}
|
||||
|
||||
static void
|
||||
svg_clip_collect_ref_element (hb_svg_clip_collect_context_t *ctx,
|
||||
hb_svg_xml_parser_t &parser,
|
||||
const hb_svg_transform_t &base_transform,
|
||||
unsigned depth,
|
||||
bool suppress_viewbox_once = false,
|
||||
bool parent_visible = true,
|
||||
bool allow_symbol_once = false);
|
||||
|
||||
static void
|
||||
svg_clip_collect_use_target (hb_svg_clip_collect_context_t *ctx,
|
||||
hb_svg_xml_parser_t &use_parser,
|
||||
const hb_svg_transform_t &base_transform,
|
||||
unsigned depth)
|
||||
{
|
||||
const unsigned SVG_MAX_CLIP_USE_DEPTH = 64;
|
||||
if (depth >= SVG_MAX_CLIP_USE_DEPTH)
|
||||
return;
|
||||
|
||||
hb_svg_str_t href = hb_raster_svg_find_href_attr (use_parser);
|
||||
hb_svg_str_t ref_id;
|
||||
if (!hb_raster_svg_parse_local_id_ref (href, &ref_id, nullptr))
|
||||
return;
|
||||
|
||||
const char *found = nullptr;
|
||||
if (!hb_raster_svg_find_element_by_id (ctx->doc_start, ctx->doc_len,
|
||||
ctx->svg_accel, ctx->doc_cache,
|
||||
ref_id, &found))
|
||||
return;
|
||||
|
||||
hb_decycler_node_t node (*ctx->use_decycler);
|
||||
if (unlikely (!node.visit ((uintptr_t) found)))
|
||||
return;
|
||||
|
||||
hb_svg_transform_t effective = base_transform;
|
||||
float use_x = 0.f, use_y = 0.f, use_w = 0.f, use_h = 0.f;
|
||||
hb_raster_svg_parse_use_geometry (use_parser, &use_x, &use_y, &use_w, &use_h);
|
||||
if (use_x != 0.f || use_y != 0.f)
|
||||
{
|
||||
hb_svg_transform_t tr;
|
||||
tr.dx = use_x;
|
||||
tr.dy = use_y;
|
||||
effective.multiply (tr);
|
||||
}
|
||||
|
||||
unsigned remaining = ctx->doc_len - (unsigned) (found - ctx->doc_start);
|
||||
hb_svg_xml_parser_t ref_parser (found, remaining);
|
||||
hb_svg_token_type_t rt = ref_parser.next ();
|
||||
if (rt != SVG_TOKEN_OPEN_TAG && rt != SVG_TOKEN_SELF_CLOSE_TAG)
|
||||
return;
|
||||
|
||||
bool viewport_mapped = false;
|
||||
hb_svg_transform_t vb_t;
|
||||
if (hb_raster_svg_compute_use_target_viewbox_transform (ref_parser, use_w, use_h, &vb_t))
|
||||
{
|
||||
effective.multiply (vb_t);
|
||||
viewport_mapped = true;
|
||||
}
|
||||
|
||||
bool allow_symbol = ref_parser.tag_name.eq ("symbol");
|
||||
svg_clip_collect_ref_element (ctx, ref_parser, effective, depth + 1,
|
||||
viewport_mapped, true, allow_symbol);
|
||||
}
|
||||
|
||||
static void
|
||||
svg_clip_collect_ref_element (hb_svg_clip_collect_context_t *ctx,
|
||||
hb_svg_xml_parser_t &parser,
|
||||
const hb_svg_transform_t &base_transform,
|
||||
unsigned depth,
|
||||
bool suppress_viewbox_once,
|
||||
bool parent_visible,
|
||||
bool allow_symbol_once)
|
||||
{
|
||||
const unsigned SVG_MAX_CLIP_REF_DEPTH = 64;
|
||||
if (depth >= SVG_MAX_CLIP_REF_DEPTH)
|
||||
{
|
||||
if (!parser.self_closing)
|
||||
svg_skip_subtree (parser);
|
||||
return;
|
||||
}
|
||||
|
||||
bool is_visible = svg_resolve_element_visibility (parser, parent_visible);
|
||||
if (!is_visible)
|
||||
{
|
||||
if (!parser.self_closing)
|
||||
svg_skip_subtree (parser);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Definitions are not directly renderable clip geometry. */
|
||||
if (parser.tag_name.eq ("defs"))
|
||||
{
|
||||
if (!parser.self_closing)
|
||||
svg_skip_subtree (parser);
|
||||
return;
|
||||
}
|
||||
if (parser.tag_name.eq ("symbol") && !allow_symbol_once)
|
||||
{
|
||||
if (!parser.self_closing)
|
||||
svg_skip_subtree (parser);
|
||||
return;
|
||||
}
|
||||
if (parser.tag_name.eq ("symbol"))
|
||||
allow_symbol_once = false;
|
||||
|
||||
hb_svg_transform_t effective = base_transform;
|
||||
hb_svg_style_props_t geom_style_props;
|
||||
svg_parse_style_props (parser.find_attr ("style"), &geom_style_props);
|
||||
hb_svg_transform_t local_t;
|
||||
if (svg_parse_element_transform (parser, &local_t))
|
||||
effective.multiply (local_t);
|
||||
if (parser.tag_name.eq ("svg"))
|
||||
{
|
||||
float svg_x = hb_raster_svg_parse_non_percent_length (svg_pick_attr_or_style (parser, geom_style_props.x, "x"));
|
||||
float svg_y = hb_raster_svg_parse_non_percent_length (svg_pick_attr_or_style (parser, geom_style_props.y, "y"));
|
||||
if (svg_x != 0.f || svg_y != 0.f)
|
||||
{
|
||||
hb_svg_transform_t tr;
|
||||
tr.dx = svg_x;
|
||||
tr.dy = svg_y;
|
||||
effective.multiply (tr);
|
||||
}
|
||||
|
||||
if (!suppress_viewbox_once)
|
||||
{
|
||||
float vb_x = 0.f, vb_y = 0.f, vb_w = 0.f, vb_h = 0.f;
|
||||
if (hb_raster_svg_parse_viewbox (parser.find_attr ("viewBox"),
|
||||
&vb_x, &vb_y, &vb_w, &vb_h))
|
||||
{
|
||||
float viewport_w = hb_raster_svg_parse_non_percent_length (svg_pick_attr_or_style (parser, geom_style_props.width, "width"));
|
||||
float viewport_h = hb_raster_svg_parse_non_percent_length (svg_pick_attr_or_style (parser, geom_style_props.height, "height"));
|
||||
if (!(viewport_w > 0.f && viewport_h > 0.f))
|
||||
{
|
||||
viewport_w = vb_w;
|
||||
viewport_h = vb_h;
|
||||
}
|
||||
hb_svg_transform_t vb_t;
|
||||
if (hb_raster_svg_compute_viewbox_transform (viewport_w, viewport_h,
|
||||
vb_x, vb_y, vb_w, vb_h,
|
||||
parser.find_attr ("preserveAspectRatio"),
|
||||
&vb_t))
|
||||
effective.multiply (vb_t);
|
||||
}
|
||||
suppress_viewbox_once = false;
|
||||
}
|
||||
}
|
||||
|
||||
hb_svg_shape_emit_data_t shape;
|
||||
if (hb_raster_svg_parse_shape_tag (parser, &shape))
|
||||
{
|
||||
svg_clip_append_shape (ctx, shape, effective);
|
||||
if (!parser.self_closing)
|
||||
svg_skip_subtree (parser);
|
||||
return;
|
||||
}
|
||||
|
||||
if (parser.tag_name.eq ("use"))
|
||||
{
|
||||
svg_clip_collect_use_target (ctx, parser, effective, depth + 1);
|
||||
if (!parser.self_closing)
|
||||
svg_skip_subtree (parser);
|
||||
return;
|
||||
}
|
||||
|
||||
bool is_container = hb_raster_svg_tag_is_container (parser.tag_name);
|
||||
if (!is_container || parser.self_closing)
|
||||
{
|
||||
if (!parser.self_closing)
|
||||
svg_skip_subtree (parser);
|
||||
return;
|
||||
}
|
||||
|
||||
int inner_depth = 1;
|
||||
while (inner_depth > 0)
|
||||
{
|
||||
hb_svg_token_type_t tok = parser.next ();
|
||||
if (tok == SVG_TOKEN_EOF) break;
|
||||
if (tok == SVG_TOKEN_CLOSE_TAG)
|
||||
{
|
||||
inner_depth--;
|
||||
continue;
|
||||
}
|
||||
if (tok == SVG_TOKEN_OPEN_TAG || tok == SVG_TOKEN_SELF_CLOSE_TAG)
|
||||
svg_clip_collect_ref_element (ctx, parser, effective, depth + 1, false, is_visible, false);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
hb_raster_svg_process_clip_path_def (hb_svg_defs_t *defs,
|
||||
hb_svg_xml_parser_t &parser,
|
||||
hb_svg_token_type_t tok,
|
||||
const char *doc_start,
|
||||
unsigned doc_len,
|
||||
const OT::SVG::accelerator_t *svg_accel,
|
||||
const OT::SVG::svg_doc_cache_t *doc_cache)
|
||||
{
|
||||
hb_svg_clip_path_def_t clip;
|
||||
hb_svg_str_t id = parser.find_attr ("id");
|
||||
hb_svg_str_t units = parser.find_attr ("clipPathUnits").trim ();
|
||||
if (units.eq_ascii_ci ("objectBoundingBox"))
|
||||
clip.units_user_space = false;
|
||||
else if (units.eq_ascii_ci ("userSpaceOnUse"))
|
||||
clip.units_user_space = true;
|
||||
else
|
||||
clip.units_user_space = true;
|
||||
|
||||
hb_svg_transform_t cp_t;
|
||||
if (svg_parse_element_transform (parser, &cp_t))
|
||||
{
|
||||
clip.has_clip_transform = true;
|
||||
clip.clip_transform = cp_t;
|
||||
}
|
||||
|
||||
clip.first_shape = defs->clip_shapes.length;
|
||||
clip.shape_count = 0;
|
||||
|
||||
if (tok == SVG_TOKEN_OPEN_TAG)
|
||||
{
|
||||
const unsigned SVG_MAX_CLIP_DEPTH = 64;
|
||||
hb_svg_transform_t inherited[SVG_MAX_CLIP_DEPTH];
|
||||
bool inherited_visibility[SVG_MAX_CLIP_DEPTH];
|
||||
inherited[0] = hb_svg_transform_t ();
|
||||
inherited[1] = hb_svg_transform_t ();
|
||||
inherited_visibility[0] = true;
|
||||
inherited_visibility[1] = true;
|
||||
hb_decycler_t use_decycler;
|
||||
|
||||
int cdepth = 1;
|
||||
bool had_alloc_failure = false;
|
||||
hb_svg_clip_collect_context_t collect_ctx = {
|
||||
defs, &clip,
|
||||
doc_start, doc_len, svg_accel, doc_cache,
|
||||
&use_decycler, &had_alloc_failure
|
||||
};
|
||||
while (cdepth > 0)
|
||||
{
|
||||
hb_svg_token_type_t ct = parser.next ();
|
||||
if (ct == SVG_TOKEN_EOF) break;
|
||||
if (ct == SVG_TOKEN_CLOSE_TAG) { cdepth--; continue; }
|
||||
if (ct == SVG_TOKEN_OPEN_TAG || ct == SVG_TOKEN_SELF_CLOSE_TAG)
|
||||
{
|
||||
if (parser.tag_name.eq ("symbol"))
|
||||
{
|
||||
if (ct == SVG_TOKEN_OPEN_TAG)
|
||||
{
|
||||
int skip_depth = 1;
|
||||
while (skip_depth > 0)
|
||||
{
|
||||
hb_svg_token_type_t st = parser.next ();
|
||||
if (st == SVG_TOKEN_EOF) break;
|
||||
if (st == SVG_TOKEN_CLOSE_TAG) skip_depth--;
|
||||
else if (st == SVG_TOKEN_OPEN_TAG) skip_depth++;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (parser.tag_name.eq ("defs"))
|
||||
{
|
||||
if (ct == SVG_TOKEN_OPEN_TAG)
|
||||
{
|
||||
int skip_depth = 1;
|
||||
while (skip_depth > 0)
|
||||
{
|
||||
hb_svg_token_type_t st = parser.next ();
|
||||
if (st == SVG_TOKEN_EOF) break;
|
||||
if (st == SVG_TOKEN_CLOSE_TAG) skip_depth--;
|
||||
else if (st == SVG_TOKEN_OPEN_TAG) skip_depth++;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
bool parent_visible = (unsigned) cdepth < SVG_MAX_CLIP_DEPTH
|
||||
? inherited_visibility[cdepth]
|
||||
: true;
|
||||
bool is_visible = svg_resolve_element_visibility (parser, parent_visible);
|
||||
bool is_hidden = !is_visible;
|
||||
if (is_hidden)
|
||||
{
|
||||
if (ct == SVG_TOKEN_OPEN_TAG)
|
||||
{
|
||||
int skip_depth = 1;
|
||||
while (skip_depth > 0)
|
||||
{
|
||||
hb_svg_token_type_t st = parser.next ();
|
||||
if (st == SVG_TOKEN_EOF) break;
|
||||
if (st == SVG_TOKEN_CLOSE_TAG) skip_depth--;
|
||||
else if (st == SVG_TOKEN_OPEN_TAG) skip_depth++;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
hb_svg_transform_t effective = (unsigned) cdepth < SVG_MAX_CLIP_DEPTH
|
||||
? inherited[cdepth]
|
||||
: hb_svg_transform_t ();
|
||||
|
||||
hb_svg_transform_t local;
|
||||
bool has_local_transform = svg_parse_element_transform (parser, &local);
|
||||
if (has_local_transform)
|
||||
{
|
||||
effective.multiply (local);
|
||||
}
|
||||
|
||||
if (parser.tag_name.eq ("use"))
|
||||
{
|
||||
svg_clip_collect_use_target (&collect_ctx, parser, effective, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
hb_svg_shape_emit_data_t shape;
|
||||
if (hb_raster_svg_parse_shape_tag (parser, &shape))
|
||||
svg_clip_append_shape (&collect_ctx, shape, effective);
|
||||
}
|
||||
|
||||
if (ct == SVG_TOKEN_OPEN_TAG)
|
||||
{
|
||||
if ((unsigned) (cdepth + 1) < SVG_MAX_CLIP_DEPTH)
|
||||
{
|
||||
inherited[cdepth + 1] = effective;
|
||||
inherited_visibility[cdepth + 1] = is_visible;
|
||||
}
|
||||
cdepth++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (had_alloc_failure)
|
||||
id = {};
|
||||
}
|
||||
|
||||
if (id.len)
|
||||
(void) defs->add_clip_path (hb_bytes_t (id.data, id.len), clip);
|
||||
}
|
||||
|
||||
struct hb_svg_clip_emit_data_t
|
||||
{
|
||||
const hb_svg_defs_t *defs;
|
||||
const hb_svg_clip_path_def_t *clip;
|
||||
hb_transform_t<> base_transform;
|
||||
hb_transform_t<> bbox_transform;
|
||||
bool has_bbox_transform = false;
|
||||
};
|
||||
|
||||
static inline hb_transform_t<>
|
||||
svg_to_hb_transform (const hb_svg_transform_t &t)
|
||||
{
|
||||
return hb_transform_t<> (t.xx, t.yx, t.xy, t.yy, t.dx, t.dy);
|
||||
}
|
||||
|
||||
static void
|
||||
svg_clip_path_emit (hb_draw_funcs_t *dfuncs,
|
||||
void *draw_data,
|
||||
void *user_data)
|
||||
{
|
||||
hb_raster_draw_t *rdr = (hb_raster_draw_t *) draw_data;
|
||||
hb_svg_clip_emit_data_t *ed = (hb_svg_clip_emit_data_t *) user_data;
|
||||
const hb_svg_clip_path_def_t *clip = ed->clip;
|
||||
|
||||
for (unsigned i = 0; i < clip->shape_count; i++)
|
||||
{
|
||||
const hb_svg_clip_shape_t &s = ed->defs->clip_shapes[clip->first_shape + i];
|
||||
hb_transform_t<> t = ed->base_transform;
|
||||
if (ed->has_bbox_transform)
|
||||
t.multiply (ed->bbox_transform);
|
||||
if (clip->has_clip_transform)
|
||||
t.multiply (svg_to_hb_transform (clip->clip_transform));
|
||||
if (s.has_transform)
|
||||
t.multiply (svg_to_hb_transform (s.transform));
|
||||
|
||||
hb_raster_draw_set_transform (rdr, t.xx, t.yx, t.xy, t.yy, t.x0, t.y0);
|
||||
|
||||
hb_svg_shape_emit_data_t shape = s.shape;
|
||||
hb_raster_svg_shape_path_emit (dfuncs, draw_data, &shape);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
hb_raster_svg_push_clip_path_ref (hb_raster_paint_t *paint,
|
||||
hb_svg_defs_t *defs,
|
||||
hb_svg_str_t clip_path_str,
|
||||
const hb_extents_t<> *object_bbox)
|
||||
{
|
||||
if (clip_path_str.is_null ()) return false;
|
||||
hb_svg_str_t trimmed = clip_path_str.trim ();
|
||||
if (!trimmed.len || trimmed.eq_ascii_ci ("none")) return false;
|
||||
|
||||
hb_svg_str_t clip_id;
|
||||
if (!hb_raster_svg_parse_local_id_ref (trimmed, &clip_id, nullptr))
|
||||
return false;
|
||||
|
||||
const hb_svg_clip_path_def_t *clip = defs->find_clip_path (hb_bytes_t (clip_id.data, clip_id.len));
|
||||
if (!clip) return false;
|
||||
|
||||
hb_svg_clip_emit_data_t ed;
|
||||
ed.defs = defs;
|
||||
ed.clip = clip;
|
||||
ed.base_transform = paint->current_effective_transform ();
|
||||
|
||||
if (!clip->units_user_space)
|
||||
{
|
||||
if (!object_bbox || object_bbox->is_empty ())
|
||||
return false;
|
||||
float w = object_bbox->xmax - object_bbox->xmin;
|
||||
float h = object_bbox->ymax - object_bbox->ymin;
|
||||
if (!(std::isfinite (w) && std::isfinite (h)) || w <= 0.f || h <= 0.f)
|
||||
return false;
|
||||
ed.has_bbox_transform = true;
|
||||
ed.bbox_transform = hb_transform_t<> (w, 0, 0, h, object_bbox->xmin, object_bbox->ymin);
|
||||
}
|
||||
|
||||
hb_raster_paint_push_clip_path (paint, svg_clip_path_emit, &ed);
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif /* !HB_NO_RASTER_SVG */
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright © 2026 Behdad Esfahbod
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Author(s): Behdad Esfahbod
|
||||
*/
|
||||
|
||||
#ifndef HB_RASTER_SVG_CLIP_HH
|
||||
#define HB_RASTER_SVG_CLIP_HH
|
||||
|
||||
#include "hb.hh"
|
||||
|
||||
#include "OT/Color/svg/svg.hh"
|
||||
#include "hb-paint.hh"
|
||||
#include "hb-raster-svg-defs.hh"
|
||||
|
||||
struct hb_raster_paint_t;
|
||||
|
||||
HB_INTERNAL void
|
||||
hb_raster_svg_process_clip_path_def (hb_svg_defs_t *defs,
|
||||
hb_svg_xml_parser_t &parser,
|
||||
hb_svg_token_type_t tok,
|
||||
const char *doc_start,
|
||||
unsigned doc_len,
|
||||
const OT::SVG::accelerator_t *svg_accel,
|
||||
const OT::SVG::svg_doc_cache_t *doc_cache);
|
||||
|
||||
HB_INTERNAL bool
|
||||
hb_raster_svg_push_clip_path_ref (hb_raster_paint_t *paint,
|
||||
hb_svg_defs_t *defs,
|
||||
hb_svg_str_t clip_path_str,
|
||||
const hb_extents_t<> *object_bbox);
|
||||
|
||||
#endif /* HB_RASTER_SVG_CLIP_HH */
|
||||
+340
@@ -0,0 +1,340 @@
|
||||
/*
|
||||
* Copyright © 2026 Behdad Esfahbod
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Author(s): Behdad Esfahbod
|
||||
*/
|
||||
|
||||
#ifndef HB_NO_RASTER_SVG
|
||||
|
||||
#include "hb.hh"
|
||||
|
||||
#include "hb-raster-svg-color.hh"
|
||||
|
||||
#include "hb-raster.h"
|
||||
#include "hb-raster-svg-base.hh"
|
||||
#include "hb-ot-color.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
struct hb_svg_named_color_t
|
||||
{
|
||||
const char *name;
|
||||
uint32_t rgb; /* 0xRRGGBB */
|
||||
};
|
||||
|
||||
static const hb_svg_named_color_t svg_named_colors[] = {
|
||||
{"aliceblue", 0xF0F8FF},
|
||||
{"antiquewhite", 0xFAEBD7},
|
||||
{"aqua", 0x00FFFF},
|
||||
{"aquamarine", 0x7FFFD4},
|
||||
{"azure", 0xF0FFFF},
|
||||
{"beige", 0xF5F5DC},
|
||||
{"bisque", 0xFFE4C4},
|
||||
{"black", 0x000000},
|
||||
{"blanchedalmond", 0xFFEBCD},
|
||||
{"blue", 0x0000FF},
|
||||
{"blueviolet", 0x8A2BE2},
|
||||
{"brown", 0xA52A2A},
|
||||
{"burlywood", 0xDEB887},
|
||||
{"cadetblue", 0x5F9EA0},
|
||||
{"chartreuse", 0x7FFF00},
|
||||
{"chocolate", 0xD2691E},
|
||||
{"coral", 0xFF7F50},
|
||||
{"cornflowerblue", 0x6495ED},
|
||||
{"cornsilk", 0xFFF8DC},
|
||||
{"crimson", 0xDC143C},
|
||||
{"cyan", 0x00FFFF},
|
||||
{"darkblue", 0x00008B},
|
||||
{"darkcyan", 0x008B8B},
|
||||
{"darkgoldenrod", 0xB8860B},
|
||||
{"darkgray", 0xA9A9A9},
|
||||
{"darkgreen", 0x006400},
|
||||
{"darkgrey", 0xA9A9A9},
|
||||
{"darkkhaki", 0xBDB76B},
|
||||
{"darkmagenta", 0x8B008B},
|
||||
{"darkolivegreen", 0x556B2F},
|
||||
{"darkorange", 0xFF8C00},
|
||||
{"darkorchid", 0x9932CC},
|
||||
{"darkred", 0x8B0000},
|
||||
{"darksalmon", 0xE9967A},
|
||||
{"darkseagreen", 0x8FBC8F},
|
||||
{"darkslateblue", 0x483D8B},
|
||||
{"darkslategray", 0x2F4F4F},
|
||||
{"darkslategrey", 0x2F4F4F},
|
||||
{"darkturquoise", 0x00CED1},
|
||||
{"darkviolet", 0x9400D3},
|
||||
{"deeppink", 0xFF1493},
|
||||
{"deepskyblue", 0x00BFFF},
|
||||
{"dimgray", 0x696969},
|
||||
{"dimgrey", 0x696969},
|
||||
{"dodgerblue", 0x1E90FF},
|
||||
{"firebrick", 0xB22222},
|
||||
{"floralwhite", 0xFFFAF0},
|
||||
{"forestgreen", 0x228B22},
|
||||
{"fuchsia", 0xFF00FF},
|
||||
{"gainsboro", 0xDCDCDC},
|
||||
{"ghostwhite", 0xF8F8FF},
|
||||
{"gold", 0xFFD700},
|
||||
{"goldenrod", 0xDAA520},
|
||||
{"gray", 0x808080},
|
||||
{"green", 0x008000},
|
||||
{"greenyellow", 0xADFF2F},
|
||||
{"grey", 0x808080},
|
||||
{"honeydew", 0xF0FFF0},
|
||||
{"hotpink", 0xFF69B4},
|
||||
{"indianred", 0xCD5C5C},
|
||||
{"indigo", 0x4B0082},
|
||||
{"ivory", 0xFFFFF0},
|
||||
{"khaki", 0xF0E68C},
|
||||
{"lavender", 0xE6E6FA},
|
||||
{"lavenderblush", 0xFFF0F5},
|
||||
{"lawngreen", 0x7CFC00},
|
||||
{"lemonchiffon", 0xFFFACD},
|
||||
{"lightblue", 0xADD8E6},
|
||||
{"lightcoral", 0xF08080},
|
||||
{"lightcyan", 0xE0FFFF},
|
||||
{"lightgoldenrodyellow", 0xFAFAD2},
|
||||
{"lightgray", 0xD3D3D3},
|
||||
{"lightgreen", 0x90EE90},
|
||||
{"lightgrey", 0xD3D3D3},
|
||||
{"lightpink", 0xFFB6C1},
|
||||
{"lightsalmon", 0xFFA07A},
|
||||
{"lightseagreen", 0x20B2AA},
|
||||
{"lightskyblue", 0x87CEFA},
|
||||
{"lightslategray", 0x778899},
|
||||
{"lightslategrey", 0x778899},
|
||||
{"lightsteelblue", 0xB0C4DE},
|
||||
{"lightyellow", 0xFFFFE0},
|
||||
{"lime", 0x00FF00},
|
||||
{"limegreen", 0x32CD32},
|
||||
{"linen", 0xFAF0E6},
|
||||
{"magenta", 0xFF00FF},
|
||||
{"maroon", 0x800000},
|
||||
{"mediumaquamarine", 0x66CDAA},
|
||||
{"mediumblue", 0x0000CD},
|
||||
{"mediumorchid", 0xBA55D3},
|
||||
{"mediumpurple", 0x9370DB},
|
||||
{"mediumseagreen", 0x3CB371},
|
||||
{"mediumslateblue", 0x7B68EE},
|
||||
{"mediumspringgreen", 0x00FA9A},
|
||||
{"mediumturquoise", 0x48D1CC},
|
||||
{"mediumvioletred", 0xC71585},
|
||||
{"midnightblue", 0x191970},
|
||||
{"mintcream", 0xF5FFFA},
|
||||
{"mistyrose", 0xFFE4E1},
|
||||
{"moccasin", 0xFFE4B5},
|
||||
{"navajowhite", 0xFFDEAD},
|
||||
{"navy", 0x000080},
|
||||
{"oldlace", 0xFDF5E6},
|
||||
{"olive", 0x808000},
|
||||
{"olivedrab", 0x6B8E23},
|
||||
{"orange", 0xFFA500},
|
||||
{"orangered", 0xFF4500},
|
||||
{"orchid", 0xDA70D6},
|
||||
{"palegoldenrod", 0xEEE8AA},
|
||||
{"palegreen", 0x98FB98},
|
||||
{"paleturquoise", 0xAFEEEE},
|
||||
{"palevioletred", 0xDB7093},
|
||||
{"papayawhip", 0xFFEFD5},
|
||||
{"peachpuff", 0xFFDAB9},
|
||||
{"peru", 0xCD853F},
|
||||
{"pink", 0xFFC0CB},
|
||||
{"plum", 0xDDA0DD},
|
||||
{"powderblue", 0xB0E0E6},
|
||||
{"purple", 0x800080},
|
||||
{"rebeccapurple", 0x663399},
|
||||
{"red", 0xFF0000},
|
||||
{"rosybrown", 0xBC8F8F},
|
||||
{"royalblue", 0x4169E1},
|
||||
{"saddlebrown", 0x8B4513},
|
||||
{"salmon", 0xFA8072},
|
||||
{"sandybrown", 0xF4A460},
|
||||
{"seagreen", 0x2E8B57},
|
||||
{"seashell", 0xFFF5EE},
|
||||
{"sienna", 0xA0522D},
|
||||
{"silver", 0xC0C0C0},
|
||||
{"skyblue", 0x87CEEB},
|
||||
{"slateblue", 0x6A5ACD},
|
||||
{"slategray", 0x708090},
|
||||
{"slategrey", 0x708090},
|
||||
{"snow", 0xFFFAFA},
|
||||
{"springgreen", 0x00FF7F},
|
||||
{"steelblue", 0x4682B4},
|
||||
{"tan", 0xD2B48C},
|
||||
{"teal", 0x008080},
|
||||
{"thistle", 0xD8BFD8},
|
||||
{"tomato", 0xFF6347},
|
||||
{"turquoise", 0x40E0D0},
|
||||
{"violet", 0xEE82EE},
|
||||
{"wheat", 0xF5DEB3},
|
||||
{"white", 0xFFFFFF},
|
||||
{"whitesmoke", 0xF5F5F5},
|
||||
{"yellow", 0xFFFF00},
|
||||
{"yellowgreen", 0x9ACD32},
|
||||
};
|
||||
|
||||
static int
|
||||
hexval (char c)
|
||||
{
|
||||
if (c >= '0' && c <= '9') return c - '0';
|
||||
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
|
||||
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Parse SVG color value; returns HB_COLOR with alpha = 255.
|
||||
* Sets *is_none if "none". */
|
||||
hb_color_t
|
||||
hb_raster_svg_parse_color (hb_svg_str_t s,
|
||||
hb_paint_funcs_t *pfuncs,
|
||||
void *paint_data,
|
||||
hb_color_t foreground,
|
||||
hb_face_t *face,
|
||||
unsigned palette,
|
||||
bool *is_none)
|
||||
{
|
||||
*is_none = false;
|
||||
s = s.trim ();
|
||||
if (!s.len) { *is_none = true; return HB_COLOR (0, 0, 0, 0); }
|
||||
|
||||
if (s.eq_ascii_ci ("none") || s.eq_ascii_ci ("transparent"))
|
||||
{
|
||||
*is_none = true;
|
||||
return HB_COLOR (0, 0, 0, 0);
|
||||
}
|
||||
|
||||
if (s.eq_ascii_ci ("currentColor"))
|
||||
return foreground;
|
||||
|
||||
/* var(--colorN) → CPAL palette color */
|
||||
if (s.starts_with_ascii_ci ("var("))
|
||||
{
|
||||
const char *p = s.data + 4;
|
||||
const char *e = s.data + s.len;
|
||||
while (p < e && *p == ' ') p++;
|
||||
if (p + 7 < e && p[0] == '-' && p[1] == '-' && p[2] == 'c' &&
|
||||
p[3] == 'o' && p[4] == 'l' && p[5] == 'o' && p[6] == 'r')
|
||||
{
|
||||
p += 7;
|
||||
unsigned color_index = 0;
|
||||
while (p < e && *p >= '0' && *p <= '9')
|
||||
color_index = color_index * 10 + (*p++ - '0');
|
||||
|
||||
hb_color_t palette_color;
|
||||
if (hb_paint_custom_palette_color (pfuncs, paint_data, color_index, &palette_color))
|
||||
return palette_color;
|
||||
|
||||
unsigned count = 1;
|
||||
hb_ot_color_palette_get_colors (face, palette, color_index, &count, &palette_color);
|
||||
if (count)
|
||||
return palette_color;
|
||||
}
|
||||
|
||||
/* Fallback value after comma: var(--colorN, fallback) */
|
||||
p = s.data + 4;
|
||||
while (p < e && *p != ',') p++;
|
||||
if (p < e)
|
||||
{
|
||||
p++;
|
||||
while (p < e && *p == ' ') p++;
|
||||
const char *val_start = p;
|
||||
/* Find closing paren */
|
||||
while (e > val_start && *(e - 1) != ')') e--;
|
||||
if (e > val_start) e--;
|
||||
hb_svg_str_t fallback = {val_start, (unsigned) (e - val_start)};
|
||||
return hb_raster_svg_parse_color (fallback, pfuncs, paint_data, foreground, face, palette, is_none);
|
||||
}
|
||||
|
||||
return foreground;
|
||||
}
|
||||
|
||||
/* #RGB or #RRGGBB */
|
||||
if (s.data[0] == '#')
|
||||
{
|
||||
if (s.len == 4) /* #RGB */
|
||||
{
|
||||
int r = hexval (s.data[1]);
|
||||
int g = hexval (s.data[2]);
|
||||
int b = hexval (s.data[3]);
|
||||
if (r < 0 || g < 0 || b < 0) return HB_COLOR (0, 0, 0, 255);
|
||||
return HB_COLOR (b * 17, g * 17, r * 17, 255);
|
||||
}
|
||||
if (s.len == 7) /* #RRGGBB */
|
||||
{
|
||||
int r = hexval (s.data[1]) * 16 + hexval (s.data[2]);
|
||||
int g = hexval (s.data[3]) * 16 + hexval (s.data[4]);
|
||||
int b = hexval (s.data[5]) * 16 + hexval (s.data[6]);
|
||||
return HB_COLOR (b, g, r, 255);
|
||||
}
|
||||
return HB_COLOR (0, 0, 0, 255);
|
||||
}
|
||||
|
||||
/* rgb(r, g, b) or rgb(r%, g%, b%) */
|
||||
if (s.starts_with ("rgb"))
|
||||
{
|
||||
const char *p = s.data + 3;
|
||||
const char *e = s.data + s.len;
|
||||
while (p < e && *p != '(') p++;
|
||||
if (p < e) p++;
|
||||
|
||||
auto read_component = [&] () -> int {
|
||||
while (p < e && (*p == ' ' || *p == ',')) p++;
|
||||
char buf[32];
|
||||
unsigned n = 0;
|
||||
while (p < e && n < sizeof (buf) - 1 && ((*p >= '0' && *p <= '9') || *p == '.' || *p == '-'))
|
||||
buf[n++] = *p++;
|
||||
bool is_pct = (p < e && *p == '%');
|
||||
if (is_pct) p++;
|
||||
buf[n] = '\0';
|
||||
float val = strtof (buf, nullptr);
|
||||
if (is_pct) val = val * 255.f / 100.f;
|
||||
return hb_clamp ((int) (val + 0.5f), 0, 255);
|
||||
};
|
||||
|
||||
int r = read_component ();
|
||||
int g = read_component ();
|
||||
int b = read_component ();
|
||||
return HB_COLOR (b, g, r, 255);
|
||||
}
|
||||
|
||||
/* Named colors (case-insensitive comparison) */
|
||||
{
|
||||
char lower[32];
|
||||
unsigned n = hb_min (s.len, (unsigned) sizeof (lower) - 1);
|
||||
for (unsigned i = 0; i < n; i++)
|
||||
lower[i] = (s.data[i] >= 'A' && s.data[i] <= 'Z') ? s.data[i] + 32 : s.data[i];
|
||||
lower[n] = '\0';
|
||||
|
||||
for (unsigned i = 0; i < ARRAY_LENGTH (svg_named_colors); i++)
|
||||
if (strcmp (lower, svg_named_colors[i].name) == 0)
|
||||
{
|
||||
uint32_t rgb = svg_named_colors[i].rgb;
|
||||
return HB_COLOR (rgb & 0xFF, (rgb >> 8) & 0xFF, (rgb >> 16) & 0xFF, 255);
|
||||
}
|
||||
}
|
||||
|
||||
return HB_COLOR (0, 0, 0, 255);
|
||||
}
|
||||
|
||||
#endif /* !HB_NO_RASTER_SVG */
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright © 2026 Behdad Esfahbod
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Author(s): Behdad Esfahbod
|
||||
*/
|
||||
|
||||
#ifndef HB_RASTER_SVG_COLOR_HH
|
||||
#define HB_RASTER_SVG_COLOR_HH
|
||||
|
||||
#include "hb.hh"
|
||||
|
||||
#include "hb-face.hh"
|
||||
#include "hb-raster-svg-base.hh"
|
||||
|
||||
HB_INTERNAL hb_color_t
|
||||
hb_raster_svg_parse_color (hb_svg_str_t s,
|
||||
hb_paint_funcs_t *pfuncs,
|
||||
void *paint_data,
|
||||
hb_color_t foreground,
|
||||
hb_face_t *face,
|
||||
unsigned palette,
|
||||
bool *is_none);
|
||||
|
||||
#endif /* HB_RASTER_SVG_COLOR_HH */
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright © 2026 Behdad Esfahbod
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Author(s): Behdad Esfahbod
|
||||
*/
|
||||
|
||||
#ifndef HB_RASTER_SVG_CONTEXT_HH
|
||||
#define HB_RASTER_SVG_CONTEXT_HH
|
||||
|
||||
#include "hb.hh"
|
||||
|
||||
#include "OT/Color/svg/svg.hh"
|
||||
#include "hb-decycler.hh"
|
||||
#include "hb-raster-paint.hh"
|
||||
#include "hb-raster-svg-base.hh"
|
||||
#include "hb-raster-svg-defs.hh"
|
||||
|
||||
struct hb_svg_fill_context_t
|
||||
{
|
||||
hb_raster_paint_t *paint;
|
||||
hb_paint_funcs_t *pfuncs;
|
||||
hb_font_t *font;
|
||||
unsigned palette;
|
||||
hb_svg_defs_t *defs;
|
||||
};
|
||||
|
||||
struct hb_svg_use_context_t
|
||||
{
|
||||
hb_raster_paint_t *paint;
|
||||
hb_paint_funcs_t *pfuncs;
|
||||
|
||||
const char *doc_start;
|
||||
unsigned doc_len;
|
||||
const OT::SVG::accelerator_t *svg_accel;
|
||||
const OT::SVG::svg_doc_cache_t *doc_cache;
|
||||
hb_decycler_t *use_decycler;
|
||||
};
|
||||
|
||||
struct hb_svg_render_context_t
|
||||
{
|
||||
hb_raster_paint_t *paint;
|
||||
hb_paint_funcs_t *pfuncs;
|
||||
hb_font_t *font;
|
||||
unsigned palette;
|
||||
hb_color_t foreground;
|
||||
hb_svg_defs_t defs;
|
||||
int depth = 0;
|
||||
|
||||
const char *doc_start;
|
||||
unsigned doc_len;
|
||||
const OT::SVG::accelerator_t *svg_accel = nullptr;
|
||||
const OT::SVG::svg_doc_cache_t *doc_cache = nullptr;
|
||||
hb_decycler_t use_decycler;
|
||||
bool suppress_viewbox_once = false;
|
||||
bool allow_symbol_render_once = false;
|
||||
|
||||
void push_transform (float xx, float yx, float xy, float yy, float dx, float dy)
|
||||
{
|
||||
hb_paint_push_transform (pfuncs, paint, xx, yx, xy, yy, dx, dy);
|
||||
}
|
||||
void pop_transform ()
|
||||
{
|
||||
hb_paint_pop_transform (pfuncs, paint);
|
||||
}
|
||||
void push_group ()
|
||||
{
|
||||
hb_paint_push_group (pfuncs, paint);
|
||||
}
|
||||
void pop_group (hb_paint_composite_mode_t mode)
|
||||
{
|
||||
hb_paint_pop_group (pfuncs, paint, mode);
|
||||
}
|
||||
void paint_color (hb_color_t color)
|
||||
{
|
||||
hb_paint_color (pfuncs, paint, false, color);
|
||||
}
|
||||
void pop_clip ()
|
||||
{
|
||||
hb_paint_pop_clip (pfuncs, paint);
|
||||
}
|
||||
};
|
||||
|
||||
struct hb_svg_cascade_t
|
||||
{
|
||||
hb_svg_str_t fill;
|
||||
float fill_opacity = 1.f;
|
||||
hb_svg_str_t clip_path;
|
||||
hb_color_t color = HB_COLOR (0, 0, 0, 255);
|
||||
bool visibility = true;
|
||||
float opacity = 1.f;
|
||||
};
|
||||
|
||||
#endif /* HB_RASTER_SVG_CONTEXT_HH */
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright © 2026 Behdad Esfahbod
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Author(s): Behdad Esfahbod
|
||||
*/
|
||||
|
||||
#ifndef HB_NO_RASTER_SVG
|
||||
|
||||
#include "hb.hh"
|
||||
|
||||
#include "hb-raster-svg-defs-scan.hh"
|
||||
|
||||
#include "hb-raster-svg-clip.hh"
|
||||
#include "hb-raster-svg-gradient.hh"
|
||||
|
||||
static inline bool
|
||||
hb_raster_svg_process_defs_child_tag (const hb_svg_defs_scan_context_t *ctx,
|
||||
hb_svg_xml_parser_t &parser,
|
||||
hb_svg_token_type_t tok)
|
||||
{
|
||||
if (parser.tag_name.eq ("linearGradient"))
|
||||
{
|
||||
hb_raster_svg_process_gradient_def (ctx->defs, parser, tok, SVG_GRADIENT_LINEAR,
|
||||
ctx->pfuncs, ctx->paint_data,
|
||||
ctx->foreground, ctx->face,
|
||||
ctx->palette);
|
||||
return true;
|
||||
}
|
||||
if (parser.tag_name.eq ("radialGradient"))
|
||||
{
|
||||
hb_raster_svg_process_gradient_def (ctx->defs, parser, tok, SVG_GRADIENT_RADIAL,
|
||||
ctx->pfuncs, ctx->paint_data,
|
||||
ctx->foreground, ctx->face,
|
||||
ctx->palette);
|
||||
return true;
|
||||
}
|
||||
if (parser.tag_name.eq ("clipPath"))
|
||||
{
|
||||
hb_raster_svg_process_clip_path_def (ctx->defs, parser, tok,
|
||||
ctx->doc_start, ctx->doc_len,
|
||||
ctx->svg_accel, ctx->doc_cache);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
hb_raster_svg_process_defs_element (const hb_svg_defs_scan_context_t *ctx,
|
||||
hb_svg_xml_parser_t &parser)
|
||||
{
|
||||
int depth = 1;
|
||||
|
||||
while (depth > 0)
|
||||
{
|
||||
hb_svg_token_type_t tok = parser.next ();
|
||||
if (tok == SVG_TOKEN_EOF) break;
|
||||
|
||||
if (tok == SVG_TOKEN_CLOSE_TAG)
|
||||
{
|
||||
depth--;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tok == SVG_TOKEN_OPEN_TAG || tok == SVG_TOKEN_SELF_CLOSE_TAG)
|
||||
{
|
||||
if (!hb_raster_svg_process_defs_child_tag (ctx, parser, tok) &&
|
||||
tok == SVG_TOKEN_OPEN_TAG)
|
||||
depth++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
hb_raster_svg_collect_defs (const hb_svg_defs_scan_context_t *ctx,
|
||||
const char *data,
|
||||
unsigned data_len)
|
||||
{
|
||||
hb_svg_xml_parser_t parser (data, data_len);
|
||||
while (true)
|
||||
{
|
||||
hb_svg_token_type_t tok = parser.next ();
|
||||
if (tok == SVG_TOKEN_EOF) break;
|
||||
if (tok == SVG_TOKEN_OPEN_TAG || tok == SVG_TOKEN_SELF_CLOSE_TAG)
|
||||
hb_raster_svg_process_defs_child_tag (ctx, parser, tok);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* !HB_NO_RASTER_SVG */
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright © 2026 Behdad Esfahbod
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Author(s): Behdad Esfahbod
|
||||
*/
|
||||
|
||||
#ifndef HB_RASTER_SVG_DEFS_SCAN_HH
|
||||
#define HB_RASTER_SVG_DEFS_SCAN_HH
|
||||
|
||||
#include "hb.hh"
|
||||
|
||||
#include "OT/Color/svg/svg.hh"
|
||||
#include "hb-raster-svg-defs.hh"
|
||||
|
||||
struct hb_svg_defs_scan_context_t
|
||||
{
|
||||
hb_svg_defs_t *defs;
|
||||
hb_paint_funcs_t *pfuncs;
|
||||
void *paint_data;
|
||||
hb_color_t foreground;
|
||||
hb_face_t *face;
|
||||
unsigned palette;
|
||||
const char *doc_start;
|
||||
unsigned doc_len;
|
||||
const OT::SVG::accelerator_t *svg_accel;
|
||||
const OT::SVG::svg_doc_cache_t *doc_cache;
|
||||
};
|
||||
|
||||
HB_INTERNAL void
|
||||
hb_raster_svg_process_defs_element (const hb_svg_defs_scan_context_t *ctx,
|
||||
hb_svg_xml_parser_t &parser);
|
||||
|
||||
HB_INTERNAL void
|
||||
hb_raster_svg_collect_defs (const hb_svg_defs_scan_context_t *ctx,
|
||||
const char *data,
|
||||
unsigned data_len);
|
||||
|
||||
#endif /* HB_RASTER_SVG_DEFS_SCAN_HH */
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright © 2026 Behdad Esfahbod
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Author(s): Behdad Esfahbod
|
||||
*/
|
||||
|
||||
#ifndef HB_NO_RASTER_SVG
|
||||
|
||||
#include "hb.hh"
|
||||
|
||||
#include "hb-raster-svg-defs.hh"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
hb_svg_defs_t::~hb_svg_defs_t ()
|
||||
{
|
||||
for (unsigned i = 0; i < owned_id_strings.length; i++)
|
||||
hb_free (owned_id_strings.arrayZ[i]);
|
||||
}
|
||||
|
||||
bool
|
||||
hb_svg_defs_t::add_id_mapping (hb_hashmap_t<hb_bytes_t, unsigned> *map,
|
||||
hb_bytes_t id,
|
||||
unsigned idx)
|
||||
{
|
||||
if (!id.length)
|
||||
return false;
|
||||
if (map->has (id))
|
||||
return true;
|
||||
|
||||
unsigned n = (unsigned) id.length;
|
||||
char *owned = (char *) hb_malloc (n + 1);
|
||||
if (unlikely (!owned))
|
||||
return false;
|
||||
hb_memcpy (owned, id.arrayZ, n);
|
||||
owned[n] = '\0';
|
||||
if (unlikely (!owned_id_strings.push_or_fail (owned)))
|
||||
{
|
||||
hb_free (owned);
|
||||
return false;
|
||||
}
|
||||
|
||||
hb_bytes_t owned_key = hb_bytes_t (owned, n);
|
||||
if (unlikely (!map->set (owned_key, idx)))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
hb_svg_defs_t::add_gradient (hb_bytes_t id, const hb_svg_gradient_t &grad)
|
||||
{
|
||||
unsigned idx = gradients.length;
|
||||
gradients.push (grad);
|
||||
if (unlikely (gradients.in_error ()))
|
||||
return false;
|
||||
|
||||
if (unlikely (!add_id_mapping (&gradient_by_id, id, idx)))
|
||||
{
|
||||
gradients.pop ();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const hb_svg_gradient_t *
|
||||
hb_svg_defs_t::find_gradient (hb_bytes_t id) const
|
||||
{
|
||||
if (!id.length) return nullptr;
|
||||
unsigned *idx = nullptr;
|
||||
if (gradient_by_id.has (id, &idx))
|
||||
return &gradients[*idx];
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
hb_svg_defs_t::add_clip_path (hb_bytes_t id, const hb_svg_clip_path_def_t &clip)
|
||||
{
|
||||
unsigned idx = clip_paths.length;
|
||||
clip_paths.push (clip);
|
||||
if (unlikely (clip_paths.in_error ()))
|
||||
return false;
|
||||
|
||||
if (unlikely (!add_id_mapping (&clip_path_by_id, id, idx)))
|
||||
{
|
||||
clip_paths.pop ();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const hb_svg_clip_path_def_t *
|
||||
hb_svg_defs_t::find_clip_path (hb_bytes_t id) const
|
||||
{
|
||||
if (!id.length) return nullptr;
|
||||
unsigned *idx = nullptr;
|
||||
if (clip_path_by_id.has (id, &idx))
|
||||
return &clip_paths[*idx];
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#endif /* !HB_NO_RASTER_SVG */
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright © 2026 Behdad Esfahbod
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Author(s): Behdad Esfahbod
|
||||
*/
|
||||
|
||||
#ifndef HB_RASTER_SVG_DEFS_HH
|
||||
#define HB_RASTER_SVG_DEFS_HH
|
||||
|
||||
#include "hb.hh"
|
||||
|
||||
#include "hb-map.hh"
|
||||
#include "hb-raster-svg-parse.hh"
|
||||
|
||||
enum hb_svg_gradient_type_t
|
||||
{
|
||||
SVG_GRADIENT_LINEAR,
|
||||
SVG_GRADIENT_RADIAL
|
||||
};
|
||||
|
||||
struct hb_svg_gradient_stop_t
|
||||
{
|
||||
float offset;
|
||||
hb_color_t color;
|
||||
bool is_current_color = false;
|
||||
};
|
||||
|
||||
struct hb_svg_gradient_t
|
||||
{
|
||||
hb_svg_gradient_type_t type;
|
||||
|
||||
hb_paint_extend_t spread = HB_PAINT_EXTEND_PAD;
|
||||
bool has_spread = false;
|
||||
hb_svg_transform_t gradient_transform;
|
||||
bool has_gradient_transform = false;
|
||||
bool units_user_space = false;
|
||||
bool has_units_user_space = false;
|
||||
|
||||
float x1 = 0, y1 = 0, x2 = 1, y2 = 0;
|
||||
bool has_x1 = false, has_y1 = false, has_x2 = false, has_y2 = false;
|
||||
|
||||
float cx = 0.5f, cy = 0.5f, r = 0.5f;
|
||||
float fx = -1.f, fy = -1.f, fr = 0.f;
|
||||
bool has_cx = false, has_cy = false, has_r = false;
|
||||
bool has_fx = false, has_fy = false, has_fr = false;
|
||||
|
||||
hb_vector_t<hb_svg_gradient_stop_t> stops;
|
||||
|
||||
hb_bytes_t href_id = {};
|
||||
};
|
||||
|
||||
struct hb_svg_clip_path_def_t
|
||||
{
|
||||
unsigned first_shape = 0;
|
||||
unsigned shape_count = 0;
|
||||
hb_svg_transform_t clip_transform;
|
||||
bool has_clip_transform = false;
|
||||
bool units_user_space = true;
|
||||
};
|
||||
|
||||
struct hb_svg_clip_shape_t
|
||||
{
|
||||
hb_svg_shape_emit_data_t shape;
|
||||
hb_svg_transform_t transform;
|
||||
bool has_transform = false;
|
||||
};
|
||||
|
||||
struct hb_svg_defs_t
|
||||
{
|
||||
hb_vector_t<hb_svg_gradient_t> gradients;
|
||||
hb_vector_t<hb_svg_clip_shape_t> clip_shapes;
|
||||
hb_vector_t<hb_svg_clip_path_def_t> clip_paths;
|
||||
hb_hashmap_t<hb_bytes_t, unsigned> gradient_by_id;
|
||||
hb_hashmap_t<hb_bytes_t, unsigned> clip_path_by_id;
|
||||
hb_vector_t<char *> owned_id_strings;
|
||||
|
||||
HB_INTERNAL ~hb_svg_defs_t ();
|
||||
|
||||
HB_INTERNAL bool add_id_mapping (hb_hashmap_t<hb_bytes_t, unsigned> *map,
|
||||
hb_bytes_t id,
|
||||
unsigned idx);
|
||||
HB_INTERNAL bool add_gradient (hb_bytes_t id, const hb_svg_gradient_t &grad);
|
||||
HB_INTERNAL const hb_svg_gradient_t *find_gradient (hb_bytes_t id) const;
|
||||
HB_INTERNAL bool add_clip_path (hb_bytes_t id, const hb_svg_clip_path_def_t &clip);
|
||||
HB_INTERNAL const hb_svg_clip_path_def_t *find_clip_path (hb_bytes_t id) const;
|
||||
};
|
||||
|
||||
#endif /* HB_RASTER_SVG_DEFS_HH */
|
||||
+300
@@ -0,0 +1,300 @@
|
||||
/*
|
||||
* Copyright © 2026 Behdad Esfahbod
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Author(s): Behdad Esfahbod
|
||||
*/
|
||||
|
||||
#ifndef HB_NO_RASTER_SVG
|
||||
|
||||
#include "hb.hh"
|
||||
|
||||
#include "hb-raster-svg-fill.hh"
|
||||
|
||||
#include "hb-raster-svg-base.hh"
|
||||
#include "hb-raster-svg-color.hh"
|
||||
#include "hb-decycler.hh"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
struct hb_svg_color_line_data_t
|
||||
{
|
||||
const hb_svg_gradient_t *grad;
|
||||
hb_paint_extend_t extend;
|
||||
float alpha_scale;
|
||||
hb_color_t current_color;
|
||||
};
|
||||
|
||||
static unsigned
|
||||
svg_color_line_get_stops (hb_color_line_t *color_line HB_UNUSED,
|
||||
void *color_line_data,
|
||||
unsigned int start,
|
||||
unsigned int *count,
|
||||
hb_color_stop_t *color_stops,
|
||||
void *user_data HB_UNUSED)
|
||||
{
|
||||
hb_svg_color_line_data_t *cl = (hb_svg_color_line_data_t *) color_line_data;
|
||||
const hb_svg_gradient_t *grad = cl->grad;
|
||||
unsigned total = grad->stops.length;
|
||||
|
||||
if (count)
|
||||
{
|
||||
unsigned n = hb_min (*count, total > start ? total - start : 0u);
|
||||
for (unsigned i = 0; i < n; i++)
|
||||
{
|
||||
const hb_svg_gradient_stop_t &stop = grad->stops[start + i];
|
||||
color_stops[i].offset = stop.offset;
|
||||
color_stops[i].is_foreground = false;
|
||||
hb_color_t c = stop.is_current_color ? cl->current_color : stop.color;
|
||||
if (stop.is_current_color)
|
||||
{
|
||||
uint8_t a = (uint8_t) hb_raster_div255 (hb_color_get_alpha (c) * hb_color_get_alpha (stop.color));
|
||||
c = HB_COLOR (hb_color_get_blue (c),
|
||||
hb_color_get_green (c),
|
||||
hb_color_get_red (c),
|
||||
a);
|
||||
}
|
||||
if (cl->alpha_scale < 1.f)
|
||||
{
|
||||
uint8_t a = (uint8_t) hb_clamp ((int) (hb_color_get_alpha (c) * cl->alpha_scale + 0.5f), 0, 255);
|
||||
c = HB_COLOR (hb_color_get_blue (c),
|
||||
hb_color_get_green (c),
|
||||
hb_color_get_red (c),
|
||||
a);
|
||||
}
|
||||
color_stops[i].color = c;
|
||||
}
|
||||
*count = n;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
static hb_paint_extend_t
|
||||
svg_color_line_get_extend (hb_color_line_t *color_line HB_UNUSED,
|
||||
void *color_line_data,
|
||||
void *user_data HB_UNUSED)
|
||||
{
|
||||
hb_svg_color_line_data_t *cl = (hb_svg_color_line_data_t *) color_line_data;
|
||||
return cl->extend;
|
||||
}
|
||||
|
||||
static bool
|
||||
svg_parse_paint_url_with_fallback (hb_svg_str_t s,
|
||||
hb_svg_str_t *out_id,
|
||||
hb_svg_str_t *fallback)
|
||||
{
|
||||
if (out_id) *out_id = {};
|
||||
hb_svg_str_t id;
|
||||
if (!hb_raster_svg_parse_local_id_ref (s, &id, fallback))
|
||||
return false;
|
||||
if (!s.trim ().starts_with_ascii_ci ("url("))
|
||||
return false;
|
||||
if (out_id) *out_id = id;
|
||||
return id.len > 0;
|
||||
}
|
||||
|
||||
void
|
||||
hb_raster_svg_emit_fill (const hb_svg_fill_context_t *ctx,
|
||||
hb_svg_str_t fill_str,
|
||||
float fill_opacity,
|
||||
const hb_extents_t<> *object_bbox,
|
||||
hb_color_t current_color)
|
||||
{
|
||||
bool is_none = false;
|
||||
|
||||
hb_svg_str_t url_id;
|
||||
hb_svg_str_t fallback_paint;
|
||||
bool has_url_paint = svg_parse_paint_url_with_fallback (fill_str, &url_id, &fallback_paint);
|
||||
|
||||
const hb_svg_gradient_t *grad = has_url_paint ? ctx->defs->find_gradient (hb_bytes_t (url_id.data, url_id.len)) : nullptr;
|
||||
if (has_url_paint && !grad)
|
||||
{
|
||||
if (fallback_paint.len)
|
||||
hb_raster_svg_emit_fill (ctx, fallback_paint, fill_opacity, object_bbox, current_color);
|
||||
return;
|
||||
}
|
||||
if (grad)
|
||||
{
|
||||
const unsigned SVG_MAX_GRADIENT_REF_DEPTH = 1024;
|
||||
hb_vector_t<const hb_svg_gradient_t *> chain;
|
||||
hb_decycler_t decycler;
|
||||
const hb_svg_gradient_t *cur = grad;
|
||||
while (cur)
|
||||
{
|
||||
if (chain.length >= SVG_MAX_GRADIENT_REF_DEPTH)
|
||||
break;
|
||||
|
||||
hb_decycler_node_t node (decycler);
|
||||
if (unlikely (!node.visit ((uintptr_t) cur)))
|
||||
break;
|
||||
|
||||
if (unlikely (!chain.push_or_fail (cur)))
|
||||
{
|
||||
if (fallback_paint.len)
|
||||
hb_raster_svg_emit_fill (ctx, fallback_paint, fill_opacity, object_bbox, current_color);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!cur->href_id.length) break;
|
||||
const hb_svg_gradient_t *next = ctx->defs->find_gradient (cur->href_id);
|
||||
if (!next) break;
|
||||
cur = next;
|
||||
}
|
||||
if (!chain.length)
|
||||
{
|
||||
if (fallback_paint.len)
|
||||
hb_raster_svg_emit_fill (ctx, fallback_paint, fill_opacity, object_bbox, current_color);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!chain.length) {
|
||||
if (fallback_paint.len)
|
||||
hb_raster_svg_emit_fill (ctx, fallback_paint, fill_opacity, object_bbox, current_color);
|
||||
return;
|
||||
}
|
||||
hb_svg_gradient_t effective = *chain.arrayZ[chain.length - 1];
|
||||
for (int i = (int) chain.length - 2; i >= 0; i--)
|
||||
{
|
||||
const hb_svg_gradient_t *g = chain.arrayZ[i];
|
||||
effective.type = g->type;
|
||||
if (g->stops.length) effective.stops = g->stops;
|
||||
if (g->has_spread) { effective.spread = g->spread; effective.has_spread = true; }
|
||||
if (g->has_units_user_space)
|
||||
{
|
||||
effective.units_user_space = g->units_user_space;
|
||||
effective.has_units_user_space = true;
|
||||
}
|
||||
if (g->has_gradient_transform)
|
||||
{
|
||||
effective.gradient_transform = g->gradient_transform;
|
||||
effective.has_gradient_transform = true;
|
||||
}
|
||||
if (g->has_x1) { effective.x1 = g->x1; effective.has_x1 = true; }
|
||||
if (g->has_y1) { effective.y1 = g->y1; effective.has_y1 = true; }
|
||||
if (g->has_x2) { effective.x2 = g->x2; effective.has_x2 = true; }
|
||||
if (g->has_y2) { effective.y2 = g->y2; effective.has_y2 = true; }
|
||||
if (g->has_cx) { effective.cx = g->cx; effective.has_cx = true; }
|
||||
if (g->has_cy) { effective.cy = g->cy; effective.has_cy = true; }
|
||||
if (g->has_r) { effective.r = g->r; effective.has_r = true; }
|
||||
if (g->has_fx) { effective.fx = g->fx; effective.has_fx = true; }
|
||||
if (g->has_fy) { effective.fy = g->fy; effective.has_fy = true; }
|
||||
if (g->has_fr) { effective.fr = g->fr; effective.has_fr = true; }
|
||||
}
|
||||
if (!effective.stops.length)
|
||||
{
|
||||
if (fallback_paint.len)
|
||||
hb_raster_svg_emit_fill (ctx, fallback_paint, fill_opacity, object_bbox, current_color);
|
||||
return;
|
||||
}
|
||||
|
||||
hb_svg_color_line_data_t cl_data;
|
||||
cl_data.grad = &effective;
|
||||
cl_data.extend = effective.spread;
|
||||
cl_data.alpha_scale = hb_clamp (fill_opacity, 0.f, 1.f);
|
||||
cl_data.current_color = current_color;
|
||||
|
||||
hb_color_line_t cl = {
|
||||
&cl_data,
|
||||
svg_color_line_get_stops, nullptr,
|
||||
svg_color_line_get_extend, nullptr
|
||||
};
|
||||
|
||||
bool has_bbox_transform = !effective.units_user_space && object_bbox && !object_bbox->is_empty ();
|
||||
if (has_bbox_transform)
|
||||
{
|
||||
float w = object_bbox->xmax - object_bbox->xmin;
|
||||
float h = object_bbox->ymax - object_bbox->ymin;
|
||||
if (std::isfinite (w) && std::isfinite (h) && w > 0.f && h > 0.f)
|
||||
hb_paint_push_transform (ctx->pfuncs, ctx->paint, w, 0, 0, h, object_bbox->xmin, object_bbox->ymin);
|
||||
else
|
||||
has_bbox_transform = false;
|
||||
}
|
||||
|
||||
if (effective.has_gradient_transform)
|
||||
hb_paint_push_transform (ctx->pfuncs, ctx->paint,
|
||||
effective.gradient_transform.xx,
|
||||
effective.gradient_transform.yx,
|
||||
effective.gradient_transform.xy,
|
||||
effective.gradient_transform.yy,
|
||||
effective.gradient_transform.dx,
|
||||
effective.gradient_transform.dy);
|
||||
|
||||
if (effective.type == SVG_GRADIENT_LINEAR)
|
||||
{
|
||||
hb_paint_linear_gradient (ctx->pfuncs, ctx->paint, &cl,
|
||||
effective.x1, effective.y1,
|
||||
effective.x2, effective.y2,
|
||||
effective.x2 - (effective.y2 - effective.y1),
|
||||
effective.y2 + (effective.x2 - effective.x1));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!std::isfinite (effective.r) || effective.r < 0.f)
|
||||
{
|
||||
if (effective.has_gradient_transform)
|
||||
hb_paint_pop_transform (ctx->pfuncs, ctx->paint);
|
||||
if (has_bbox_transform)
|
||||
hb_paint_pop_transform (ctx->pfuncs, ctx->paint);
|
||||
if (fallback_paint.len)
|
||||
hb_raster_svg_emit_fill (ctx, fallback_paint, fill_opacity, object_bbox, current_color);
|
||||
return;
|
||||
}
|
||||
|
||||
float fx = effective.has_fx ? effective.fx : effective.cx;
|
||||
float fy = effective.has_fy ? effective.fy : effective.cy;
|
||||
float fr = effective.has_fr ? effective.fr : 0.f;
|
||||
if (!std::isfinite (fr) || fr < 0.f)
|
||||
fr = 0.f;
|
||||
fr = hb_min (fr, effective.r);
|
||||
|
||||
hb_paint_radial_gradient (ctx->pfuncs, ctx->paint, &cl,
|
||||
fx, fy, fr,
|
||||
effective.cx, effective.cy, effective.r);
|
||||
}
|
||||
|
||||
if (effective.has_gradient_transform)
|
||||
hb_paint_pop_transform (ctx->pfuncs, ctx->paint);
|
||||
if (has_bbox_transform)
|
||||
hb_paint_pop_transform (ctx->pfuncs, ctx->paint);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
hb_color_t color = hb_raster_svg_parse_color (fill_str,
|
||||
ctx->pfuncs,
|
||||
ctx->paint,
|
||||
current_color,
|
||||
hb_font_get_face (ctx->font),
|
||||
ctx->palette,
|
||||
&is_none);
|
||||
if (is_none) return;
|
||||
|
||||
if (fill_opacity < 1.f)
|
||||
color = HB_COLOR (hb_color_get_blue (color),
|
||||
hb_color_get_green (color),
|
||||
hb_color_get_red (color),
|
||||
(uint8_t) (hb_color_get_alpha (color) * fill_opacity + 0.5f));
|
||||
|
||||
hb_paint_color (ctx->pfuncs, ctx->paint, false, color);
|
||||
}
|
||||
|
||||
#endif /* !HB_NO_RASTER_SVG */
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright © 2026 Behdad Esfahbod
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Author(s): Behdad Esfahbod
|
||||
*/
|
||||
|
||||
#ifndef HB_RASTER_SVG_FILL_HH
|
||||
#define HB_RASTER_SVG_FILL_HH
|
||||
|
||||
#include "hb.hh"
|
||||
|
||||
#include "hb-raster-svg-context.hh"
|
||||
|
||||
HB_INTERNAL void
|
||||
hb_raster_svg_emit_fill (const hb_svg_fill_context_t *ctx,
|
||||
hb_svg_str_t fill_str,
|
||||
float fill_opacity,
|
||||
const hb_extents_t<> *object_bbox,
|
||||
hb_color_t current_color);
|
||||
|
||||
#endif /* HB_RASTER_SVG_FILL_HH */
|
||||
+245
@@ -0,0 +1,245 @@
|
||||
/*
|
||||
* Copyright © 2026 Behdad Esfahbod
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Author(s): Behdad Esfahbod
|
||||
*/
|
||||
|
||||
#ifndef HB_NO_RASTER_SVG
|
||||
|
||||
#include "hb.hh"
|
||||
|
||||
#include "hb-raster-svg-gradient.hh"
|
||||
|
||||
#include "hb-raster-svg-base.hh"
|
||||
|
||||
static bool
|
||||
svg_parse_gradient_stop (hb_svg_xml_parser_t &parser,
|
||||
hb_svg_gradient_t &grad,
|
||||
hb_paint_funcs_t *pfuncs,
|
||||
void *paint_data,
|
||||
hb_color_t foreground,
|
||||
hb_face_t *face,
|
||||
unsigned palette)
|
||||
{
|
||||
const unsigned SVG_MAX_GRADIENT_STOPS = 1024;
|
||||
if (grad.stops.length >= SVG_MAX_GRADIENT_STOPS)
|
||||
return true;
|
||||
|
||||
hb_svg_attr_view_t attrs (parser);
|
||||
hb_svg_str_t style = attrs.get ("style");
|
||||
hb_svg_style_props_t style_props;
|
||||
svg_parse_style_props (style, &style_props);
|
||||
hb_svg_str_t offset_str = svg_pick_attr_or_style (parser, style_props.offset, "offset");
|
||||
hb_svg_str_t color_str = svg_pick_attr_or_style (parser, style_props.stop_color, "stop-color");
|
||||
hb_svg_str_t opacity_str = svg_pick_attr_or_style (parser, style_props.stop_opacity, "stop-opacity");
|
||||
hb_svg_str_t display_str = svg_pick_attr_or_style (parser, style_props.display, "display");
|
||||
hb_svg_str_t visibility_str = svg_pick_attr_or_style (parser, style_props.visibility, "visibility");
|
||||
|
||||
if (display_str.trim ().eq_ascii_ci ("none"))
|
||||
return true;
|
||||
hb_svg_str_t visibility_trim = visibility_str.trim ();
|
||||
if (visibility_trim.eq_ascii_ci ("hidden") ||
|
||||
visibility_trim.eq_ascii_ci ("collapse"))
|
||||
return true;
|
||||
|
||||
float offset = 0;
|
||||
if (offset_str.len)
|
||||
offset = hb_clamp (svg_parse_number_or_percent (offset_str, nullptr), 0.f, 1.f);
|
||||
if (grad.stops.length)
|
||||
offset = hb_max (offset, grad.stops.arrayZ[grad.stops.length - 1].offset);
|
||||
|
||||
bool is_none = false;
|
||||
hb_color_t color = HB_COLOR (0, 0, 0, 255);
|
||||
bool is_current_color = false;
|
||||
if (color_str.len && !svg_str_is_inherit (color_str))
|
||||
{
|
||||
is_current_color = color_str.trim ().eq_ascii_ci ("currentColor");
|
||||
color = hb_raster_svg_parse_color (color_str, pfuncs, paint_data, foreground, face, palette, &is_none);
|
||||
}
|
||||
|
||||
if (opacity_str.len && !svg_str_is_inherit (opacity_str))
|
||||
{
|
||||
float opacity = svg_parse_float_clamped01 (opacity_str);
|
||||
color = HB_COLOR (hb_color_get_blue (color),
|
||||
hb_color_get_green (color),
|
||||
hb_color_get_red (color),
|
||||
(uint8_t) (hb_color_get_alpha (color) * opacity + 0.5f));
|
||||
}
|
||||
|
||||
hb_svg_gradient_stop_t stop;
|
||||
stop.offset = offset;
|
||||
stop.color = color;
|
||||
stop.is_current_color = is_current_color;
|
||||
return grad.stops.push_or_fail (stop);
|
||||
}
|
||||
|
||||
static void
|
||||
svg_parse_gradient_attrs (hb_svg_xml_parser_t &parser,
|
||||
hb_svg_gradient_t &grad)
|
||||
{
|
||||
hb_svg_style_props_t style_props;
|
||||
svg_parse_style_props (parser.find_attr ("style"), &style_props);
|
||||
|
||||
hb_svg_str_t spread_str = svg_pick_attr_or_style (parser, style_props.spread_method, "spreadMethod").trim ();
|
||||
if (spread_str.eq_ascii_ci ("reflect"))
|
||||
{
|
||||
grad.spread = HB_PAINT_EXTEND_REFLECT;
|
||||
grad.has_spread = true;
|
||||
}
|
||||
else if (spread_str.eq_ascii_ci ("repeat"))
|
||||
{
|
||||
grad.spread = HB_PAINT_EXTEND_REPEAT;
|
||||
grad.has_spread = true;
|
||||
}
|
||||
else if (spread_str.eq_ascii_ci ("pad"))
|
||||
{
|
||||
grad.spread = HB_PAINT_EXTEND_PAD;
|
||||
grad.has_spread = true;
|
||||
}
|
||||
|
||||
hb_svg_str_t units_str = svg_pick_attr_or_style (parser, style_props.gradient_units, "gradientUnits").trim ();
|
||||
if (units_str.eq_ascii_ci ("userSpaceOnUse"))
|
||||
{
|
||||
grad.units_user_space = true;
|
||||
grad.has_units_user_space = true;
|
||||
}
|
||||
else if (units_str.eq_ascii_ci ("objectBoundingBox"))
|
||||
{
|
||||
grad.units_user_space = false;
|
||||
grad.has_units_user_space = true;
|
||||
}
|
||||
|
||||
hb_svg_str_t transform_str = svg_pick_attr_or_style (parser, style_props.gradient_transform, "gradientTransform");
|
||||
if (transform_str.len)
|
||||
{
|
||||
grad.has_gradient_transform = true;
|
||||
hb_raster_svg_parse_transform (transform_str, &grad.gradient_transform);
|
||||
}
|
||||
|
||||
hb_svg_str_t href = hb_raster_svg_find_href_attr (parser);
|
||||
if (href.len)
|
||||
{
|
||||
hb_svg_str_t href_id;
|
||||
if (hb_raster_svg_parse_local_id_ref (href, &href_id, nullptr))
|
||||
grad.href_id = hb_bytes_t (href_id.data, href_id.len);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
svg_parse_gradient_geometry_attrs (hb_svg_xml_parser_t &parser,
|
||||
hb_svg_gradient_t &grad)
|
||||
{
|
||||
hb_svg_style_props_t style_props;
|
||||
svg_parse_style_props (parser.find_attr ("style"), &style_props);
|
||||
if (grad.type == SVG_GRADIENT_LINEAR)
|
||||
{
|
||||
hb_svg_str_t x1_str = svg_pick_attr_or_style (parser, style_props.x1, "x1");
|
||||
hb_svg_str_t y1_str = svg_pick_attr_or_style (parser, style_props.y1, "y1");
|
||||
hb_svg_str_t x2_str = svg_pick_attr_or_style (parser, style_props.x2, "x2");
|
||||
hb_svg_str_t y2_str = svg_pick_attr_or_style (parser, style_props.y2, "y2");
|
||||
if (x1_str.len) { grad.x1 = svg_parse_number_or_percent (x1_str, nullptr); grad.has_x1 = true; }
|
||||
if (y1_str.len) { grad.y1 = svg_parse_number_or_percent (y1_str, nullptr); grad.has_y1 = true; }
|
||||
if (x2_str.len) { grad.x2 = svg_parse_number_or_percent (x2_str, nullptr); grad.has_x2 = true; }
|
||||
if (y2_str.len) { grad.y2 = svg_parse_number_or_percent (y2_str, nullptr); grad.has_y2 = true; }
|
||||
|
||||
if (!grad.has_x2)
|
||||
grad.x2 = 1.f;
|
||||
}
|
||||
else
|
||||
{
|
||||
hb_svg_str_t cx_str = svg_pick_attr_or_style (parser, style_props.cx, "cx");
|
||||
hb_svg_str_t cy_str = svg_pick_attr_or_style (parser, style_props.cy, "cy");
|
||||
hb_svg_str_t r_str = svg_pick_attr_or_style (parser, style_props.r, "r");
|
||||
hb_svg_str_t fx_str = svg_pick_attr_or_style (parser, style_props.fx, "fx");
|
||||
hb_svg_str_t fy_str = svg_pick_attr_or_style (parser, style_props.fy, "fy");
|
||||
hb_svg_str_t fr_str = svg_pick_attr_or_style (parser, style_props.fr, "fr");
|
||||
|
||||
if (cx_str.len) { grad.cx = svg_parse_number_or_percent (cx_str, nullptr); grad.has_cx = true; }
|
||||
if (cy_str.len) { grad.cy = svg_parse_number_or_percent (cy_str, nullptr); grad.has_cy = true; }
|
||||
if (r_str.len) { grad.r = svg_parse_number_or_percent (r_str, nullptr); grad.has_r = true; }
|
||||
if (fx_str.len) { grad.fx = svg_parse_number_or_percent (fx_str, nullptr); grad.has_fx = true; }
|
||||
if (fy_str.len) { grad.fy = svg_parse_number_or_percent (fy_str, nullptr); grad.has_fy = true; }
|
||||
if (fr_str.len) { grad.fr = svg_parse_number_or_percent (fr_str, nullptr); grad.has_fr = true; }
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
svg_parse_gradient_children (hb_svg_defs_t *defs,
|
||||
hb_svg_xml_parser_t &parser,
|
||||
hb_svg_gradient_t &grad,
|
||||
hb_svg_str_t *id,
|
||||
hb_paint_funcs_t *pfuncs,
|
||||
void *paint_data,
|
||||
hb_color_t foreground,
|
||||
hb_face_t *face,
|
||||
unsigned palette)
|
||||
{
|
||||
int gdepth = 1;
|
||||
bool had_alloc_failure = false;
|
||||
while (gdepth > 0)
|
||||
{
|
||||
hb_svg_token_type_t gt = parser.next ();
|
||||
if (gt == SVG_TOKEN_EOF) break;
|
||||
if (gt == SVG_TOKEN_CLOSE_TAG) { gdepth--; continue; }
|
||||
if ((gt == SVG_TOKEN_OPEN_TAG || gt == SVG_TOKEN_SELF_CLOSE_TAG) &&
|
||||
parser.tag_name.eq ("stop"))
|
||||
if (unlikely (!svg_parse_gradient_stop (parser, grad,
|
||||
pfuncs, paint_data,
|
||||
foreground, face,
|
||||
palette)))
|
||||
had_alloc_failure = true;
|
||||
if (gt == SVG_TOKEN_OPEN_TAG && !parser.tag_name.eq ("stop"))
|
||||
gdepth++;
|
||||
}
|
||||
if (had_alloc_failure || defs->gradients.in_error ())
|
||||
*id = {};
|
||||
}
|
||||
|
||||
void
|
||||
hb_raster_svg_process_gradient_def (hb_svg_defs_t *defs,
|
||||
hb_svg_xml_parser_t &parser,
|
||||
hb_svg_token_type_t tok,
|
||||
hb_svg_gradient_type_t type,
|
||||
hb_paint_funcs_t *pfuncs,
|
||||
void *paint_data,
|
||||
hb_color_t foreground,
|
||||
hb_face_t *face,
|
||||
unsigned palette)
|
||||
{
|
||||
hb_svg_gradient_t grad;
|
||||
grad.type = type;
|
||||
svg_parse_gradient_geometry_attrs (parser, grad);
|
||||
svg_parse_gradient_attrs (parser, grad);
|
||||
|
||||
hb_svg_str_t id = parser.find_attr ("id");
|
||||
if (tok == SVG_TOKEN_OPEN_TAG)
|
||||
svg_parse_gradient_children (defs, parser, grad, &id,
|
||||
pfuncs, paint_data,
|
||||
foreground, face,
|
||||
palette);
|
||||
|
||||
if (id.len)
|
||||
(void) defs->add_gradient (hb_bytes_t (id.data, id.len), grad);
|
||||
}
|
||||
|
||||
#endif /* !HB_NO_RASTER_SVG */
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright © 2026 Behdad Esfahbod
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Author(s): Behdad Esfahbod
|
||||
*/
|
||||
|
||||
#ifndef HB_RASTER_SVG_GRADIENT_HH
|
||||
#define HB_RASTER_SVG_GRADIENT_HH
|
||||
|
||||
#include "hb.hh"
|
||||
|
||||
#include "hb-raster-svg-color.hh"
|
||||
#include "hb-raster-svg-defs.hh"
|
||||
|
||||
HB_INTERNAL void
|
||||
hb_raster_svg_process_gradient_def (hb_svg_defs_t *defs,
|
||||
hb_svg_xml_parser_t &parser,
|
||||
hb_svg_token_type_t tok,
|
||||
hb_svg_gradient_type_t type,
|
||||
hb_paint_funcs_t *pfuncs,
|
||||
void *paint_data,
|
||||
hb_color_t foreground,
|
||||
hb_face_t *face,
|
||||
unsigned palette);
|
||||
|
||||
#endif /* HB_RASTER_SVG_GRADIENT_HH */
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user