diff --git a/3rdparty/harfbuzz/CMakeLists.txt b/3rdparty/harfbuzz/CMakeLists.txt
new file mode 100644
index 0000000000..1d87fa0263
--- /dev/null
+++ b/3rdparty/harfbuzz/CMakeLists.txt
@@ -0,0 +1,70 @@
+# ----------------------------------------------------------------------------
+# CMake file for the bundled HarfBuzz text-shaping library. See root CMakeLists.txt
+#
+# OpenCV vendors a minimal subset of HarfBuzz (core + HB_HAS_RASTER) as a
+# normal static library: each .cc is a separate translation unit. The subset
+# is produced by hb_extract.py (see that script to update HarfBuzz).
+# ----------------------------------------------------------------------------
+project(${HARFBUZZ_LIBRARY} CXX)
+
+ocv_include_directories("${CMAKE_CURRENT_SOURCE_DIR}/src")
+
+# Every .cc copied by hb_extract.py is a real translation unit (the script
+# never copies non-TU .cc files), so a plain recursive glob is exactly right.
+file(GLOB_RECURSE lib_srcs src/*.cc)
+file(GLOB_RECURSE lib_hdrs src/*.h src/*.hh)
+
+# HarfBuzz is built with HB_TINY for minimum footprint. Because we request the
+# software rasterizer (HB_HAS_RASTER, defined below), HB_TINY does NOT disable
+# the draw/paint/color APIs nor CFF (PostScript/.otf
+# outlines) -- see hb-config.hh: HB_NO_DRAW/COLOR/PAINT and the TINY->HB_NO_CFF
+# rule only trigger when no HB_HAS_* backend is requested. So CFF (CJK/Indic),
+# COLR/CPAL color and our rasterizer all survive HB_TINY.
+#
+# Two features HB_TINY (via HB_LEAN) would otherwise drop are restored through
+# HarfBuzz's config-override hook (hb-opencv-config.hh): thread-safety (HB_NO_MT)
+# and variable fonts (HB_NO_VAR). No platform backends (CoreText/DirectWrite/...).
+
+add_library(${HARFBUZZ_LIBRARY} STATIC ${OPENCV_3RDPARTY_EXCLUDE_FROM_ALL} ${lib_srcs} ${lib_hdrs})
+
+target_compile_definitions(${HARFBUZZ_LIBRARY} PRIVATE
+ HB_TINY
+ HB_HAS_RASTER # request the software rasterizer: keeps draw/paint/color + CFF under HB_TINY
+ "HB_CONFIG_OVERRIDE_H=\"hb-opencv-config.hh\"")
+
+set_target_properties(${HARFBUZZ_LIBRARY} PROPERTIES
+ CXX_STANDARD 17
+ CXX_STANDARD_REQUIRED ON
+)
+
+ocv_warnings_disable(CMAKE_CXX_FLAGS
+ -Wunused-variable -Wunused-function -Wunused-parameter
+ -Wunused-but-set-variable # clang15/gcc
+ -Wshadow
+ -Wmissing-declarations # gcc
+ -Wmissing-prototypes # clang
+ -Wimplicit-fallthrough
+ -Wextra-semi # clang
+ -Wdeprecated-copy # gcc/clang
+ -Wsuggest-override
+ -Wcast-function-type
+ -Wclass-memaccess # gcc
+ -Wexpansion-to-defined
+)
+ocv_warnings_disable(CMAKE_CXX_FLAGS /wd4244 /wd4267 /wd4127 /wd4146 /wd4456 /wd4459) # MSVC
+
+set_target_properties(${HARFBUZZ_LIBRARY}
+ PROPERTIES OUTPUT_NAME ${HARFBUZZ_LIBRARY}
+ DEBUG_POSTFIX "${OPENCV_DEBUG_POSTFIX}"
+ COMPILE_PDB_NAME ${HARFBUZZ_LIBRARY}
+ COMPILE_PDB_NAME_DEBUG "${HARFBUZZ_LIBRARY}${OPENCV_DEBUG_POSTFIX}"
+ ARCHIVE_OUTPUT_DIRECTORY ${3P_LIBRARY_OUTPUT_PATH}
+ )
+
+if(ENABLE_SOLUTION_FOLDERS)
+ set_target_properties(${HARFBUZZ_LIBRARY} PROPERTIES FOLDER "3rdparty")
+endif()
+
+if(NOT BUILD_SHARED_LIBS)
+ ocv_install_target(${HARFBUZZ_LIBRARY} EXPORT OpenCVModules ARCHIVE DESTINATION ${OPENCV_3P_LIB_INSTALL_PATH} COMPONENT dev OPTIONAL)
+endif()
diff --git a/3rdparty/harfbuzz/COPYING b/3rdparty/harfbuzz/COPYING
new file mode 100644
index 0000000000..1dd917e9f2
--- /dev/null
+++ b/3rdparty/harfbuzz/COPYING
@@ -0,0 +1,42 @@
+HarfBuzz is licensed under the so-called "Old MIT" license. Details follow.
+For parts of HarfBuzz that are licensed under different licenses see individual
+files names COPYING in subdirectories where applicable.
+
+Copyright © 2010-2022 Google, Inc.
+Copyright © 2015-2020 Ebrahim Byagowi
+Copyright © 2019,2020 Facebook, Inc.
+Copyright © 2012,2015 Mozilla Foundation
+Copyright © 2011 Codethink Limited
+Copyright © 2008,2010 Nokia Corporation and/or its subsidiary(-ies)
+Copyright © 2009 Keith Stribley
+Copyright © 2011 Martin Hosken and SIL International
+Copyright © 2007 Chris Wilson
+Copyright © 2005,2006,2020,2021,2022,2023 Behdad Esfahbod
+Copyright © 2004,2007,2008,2009,2010,2013,2021,2022,2023 Red Hat, Inc.
+Copyright © 1998-2005 David Turner and Werner Lemberg
+Copyright © 2016 Igalia S.L.
+Copyright © 2022 Matthias Clasen
+Copyright © 2018,2021 Khaled Hosny
+Copyright © 2018,2019,2020 Adobe, Inc
+Copyright © 2013-2015 Alexei Podtelezhnikov
+
+For full copyright notices consult the individual files in the package.
+
+
+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.
diff --git a/3rdparty/harfbuzz/README.md b/3rdparty/harfbuzz/README.md
new file mode 100644
index 0000000000..eadd274f35
--- /dev/null
+++ b/3rdparty/harfbuzz/README.md
@@ -0,0 +1,220 @@
+# HarfBuzz
+
+
+
+
+
+[](https://github.com/harfbuzz/harfbuzz/actions/workflows/linux.yml)
+[](https://github.com/harfbuzz/harfbuzz/actions/workflows/macos.yml)
+[](https://github.com/harfbuzz/harfbuzz/actions/workflows/msvc.yml)
+[](https://oss-fuzz-build-logs.storage.googleapis.com/index.html#harfbuzz)
+[](https://scan.coverity.com/projects/harfbuzz)
+[](https://repology.org/project/harfbuzz/versions)
+[](https://securityscorecards.dev/viewer/?uri=github.com/harfbuzz/harfbuzz)
+
+
+
+HarfBuzz started as a text shaping engine but has grown into a
+full font platform — the `ffmpeg` of text shaping. It primarily
+supports [OpenType][1], but also [Apple Advanced Typography][2].
+
+HarfBuzz shapes the majority of text on modern screens.
+
+HarfBuzz is optimized for robustness, correctness, and performance
+— in that order. Achieve all.
+
+**[Try it live at harfbuzz-world.cc](https://harfbuzz-world.cc/)** — an interactive playground for shaping, subsetting, rasterization, vector output, and GPU rendering, all running in your browser.
+
+Here is a quick map of its components:
+
+### Core libraries
+
+| Library | Description |
+|---------|-------------|
+| **libharfbuzz** | Text shaping, draw API, paint API. Highly configurable (see [CONFIG.md](CONFIG.md)). Optional integration backends compiled in: hb-ft (FreeType), hb-coretext (macOS), hb-uniscribe (Windows), hb-directwrite (Windows), hb-gdi (Windows), hb-glib, hb-graphite2. |
+| **libharfbuzz-subset** | Font subsetting and variable-font instancing. |
+
+### Auxiliary libraries
+
+| Library | Description |
+|---------|-------------|
+| **libharfbuzz-icu** | ICU Unicode integration. |
+| **libharfbuzz-cairo** | Cairo rendering integration. |
+| **libharfbuzz-gobject** | GObject/GI bindings. |
+
+### Experimental libraries
+
+| Library | Description |
+|---------|-------------|
+| **libharfbuzz-raster** | Glyph rasterization to bitmaps, including color fonts. Uses hb-draw and hb-paint. |
+| **libharfbuzz-vector** | Glyph output to vector formats (currently SVG), including color fonts. Uses hb-draw and hb-paint. |
+| **libharfbuzz-gpu** | Encodes glyph outlines for GPU rasterization (Slug algorithm). Provides shader sources in GLSL, WGSL, MSL, and HLSL. [Live demo.](https://harfbuzz.github.io/hb-gpu-demo/) |
+
+Notable missing feature: font hinting (including autohinting)
+is not implemented. For hinted rasterization, use FreeType or
+Skrifa.
+
+For simplified builds, amalgamated sources are available:
+`harfbuzz.cc` (just libharfbuzz), `harfbuzz-subset.cc` (just
+libharfbuzz-subset), or `harfbuzz-world.cc` (everything, driven
+by a custom `hb-features.h`). For a live in-browser playground
+plus a worked example of the world.cc single-file build, see
+[harfbuzz-world.cc][26].
+
+### Command-line tools
+
+| Tool | Description |
+|------|-------------|
+| **hb-shape** | Shape text and display glyph output. |
+| **hb-view** | Render shaped text to an image. |
+| **hb-subset** | Subset and optimize fonts. |
+| **hb-info** | Display font metadata. |
+| **hb-raster** | Render glyphs to bitmap images. |
+| **hb-vector** | Render glyphs to vector formats (SVG). |
+| **hb-gpu** | Interactive GPU text rendering. |
+
+The canonical source tree and bug trackers are available on [github][4].
+Both development and user support discussion around HarfBuzz happen on
+[github][4] as well.
+
+For license information, see [COPYING](COPYING).
+
+## API stability
+
+The API that comes with `hb.h` will not change incompatibly. Other, peripheral,
+headers are more likely to go through minor modifications, but again, we do our
+best to never change API in an incompatible way. We will never break the ABI.
+
+The API and ABI are stable even across major version number jumps. In fact,
+current HarfBuzz is API/ABI compatible all the way back to the 0.9.x series.
+If one day we need to break the API/ABI, that would be called a new library.
+
+As such, we bump the major version number only when we add major new features,
+the minor version when there is new API, and the micro version when there
+are bug fixes.
+
+## Documentation
+
+For user manual as well as API documentation, check: https://harfbuzz.github.io
+
+## Download
+
+Tarball releases and Win32/Win64 binary bundles are available on the
+[github releases][3] page.
+
+## Development
+
+For build information, see [BUILD.md](BUILD.md).
+
+For custom configurations, see [CONFIG.md](CONFIG.md).
+
+For testing and profiling, see [TESTING.md](TESTING.md).
+
+For using with Python, see [README.python.md](README.python.md). There is also [uharfbuzz](https://github.com/harfbuzz/uharfbuzz).
+
+For cross-compiling to Windows from Linux or macOS, see [README.mingw.md](README.mingw.md).
+
+To report bugs or submit patches please use [github][4] issues and pull-requests.
+
+### Developer documents
+
+To get a better idea of where HarfBuzz stands in the text rendering stack you
+may want to read [State of Text Rendering 2024][6].
+Here are a few presentation slides about HarfBuzz over the years:
+
+- 2026 – [HarfBuzz at 20!][25]
+- 2016 – [Ten Years of HarfBuzz][20]
+- 2014 – [Unicode, OpenType, and HarfBuzz: Closing the Circle][7]
+- 2012 – [HarfBuzz, The Free and Open Text Shaping Engine][8]
+- 2009 – [HarfBuzz: the Free and Open Shaping Engine][9]
+
+More presentations and papers are available on [behdad][11]'s website.
+In particular, the following _studies_ are relevant to HarfBuzz development:
+
+- 2025 – [AAT layout caches][24]
+- 2025 – [OpenType Layout lookup caches][23]
+- 2025 – [Introducing HarfRust][22]
+- 2025 – [Subsetting][21]
+- 2025 – [Caching][12]
+- 2025 – [`hb-decycler`][13]
+- 2022 – [`hb-iter`][14]
+- 2022 – [A C library written in C++][15]
+- 2022 – [The case of the slow `hb-ft` `>h_advance` function][18]
+- 2022 – [PackTab: A static integer table packer][16]
+- 2020 – [HarfBuzz OT+AAT "Unishaper"][19]
+- 2014 – [Building the Indic Shaper][17]
+- 2012 – [Memory Consumption][10]
+
+
+## Name
+
+HarfBuzz /hærfˈbɒːz/
+
+From Persian حرف (*Harf*: letter) and باز (*Buzz*: open).
+Transliteration of the Persian calque for *OpenType*.
+
+As a noun: *The* Open Source *text shaping* engine.
+
+As an adjective: Insincerely talkative; glib. A nod to the
+GNOME project where HarfBuzz originates from.
+
+The logo shows حرفباز in the IranNastaliq font, on a Damascus
+steel background.
+
+> Background: Originally there was this font format called TrueType. People and
+> companies started calling their type engines all things ending in Type:
+> FreeType, CoolType, ClearType, etc. And then came OpenType, which is the
+> successor of TrueType. So, for my OpenType implementation, I decided to stick
+> with the concept but use the Persian translation. Which is fitting given that
+> Persian is written in the Arabic script, and OpenType is an extension of
+> TrueType that adds support for complex script rendering, and HarfBuzz is an
+> implementation of OpenType text shaping.
+
+## Users
+
+HarfBuzz is used in Android, Chrome, ChromeOS, Firefox, Flutter, GNOME, GTK+, KDE,
+Qt, LibreOffice, OpenJDK, XeTeX, Adobe Photoshop, Illustrator, InDesign,
+Microsoft Edge, Amazon Kindle, PlayStation, Godot Engine, Unreal Engine,
+Figma, Canva, QuarkXPress, Scribus, smart TVs,
+car displays, and many other places.
+
+
+
+
+
+
+
+## Distribution
+
+
+ Packaging status of HarfBuzz
+
+[](https://repology.org/project/harfbuzz/versions)
+
+
+
+[1]: https://docs.microsoft.com/en-us/typography/opentype/spec/
+[2]: https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6AATIntro.html
+[3]: https://github.com/harfbuzz/harfbuzz/releases
+[4]: https://github.com/harfbuzz/harfbuzz
+[6]: https://behdad.org/text2024
+[7]: https://docs.google.com/presentation/d/1x97pfbB1gbD53Yhz6-_yBUozQMVJ_5yMqqR_D-R7b7I/preview
+[8]: https://docs.google.com/presentation/d/1ySTZaXP5XKFg0OpmHZM00v5b17GSr3ojnzJekl4U8qI/preview
+[9]: https://behdad.org/doc/harfbuzz2009-slides.pdf
+[10]: https://docs.google.com/document/d/12jfNpQJzeVIAxoUSpk7KziyINAa1msbGliyXqguS86M/preview
+[11]: https://behdad.org/
+[12]: https://docs.google.com/document/d/1_VgObf6Je0J8byMLsi7HCQHnKo2emGnx_ib_sHo-bt4/preview
+[13]: https://docs.google.com/document/d/1Y-u08l9YhObRVObETZt1k8f_5lQdOix9TRH3zEXaoAw/preview
+[14]: https://docs.google.com/document/d/1o-xvxCbgMe9JYFHLVnPjk01ZY_8Cj0vB9-KTI1d0nyk/preview
+[15]: https://docs.google.com/document/d/18hI56KJpvXtwWbc9QSaz9zzhJwIMnrJ-zkAaKS-W-8k/preview
+[16]: https://docs.google.com/document/d/1Xq3owVt61HVkJqbLFHl73il6pcTy6PdPJJ7bSouQiQw/preview
+[17]: https://docs.google.com/document/d/1wMPwVNBvsIriamcyBO5aNs7Cdr8lmbwLJ8GmZBAswF4/preview
+[18]: https://docs.google.com/document/d/1wskYbA-czBt57oH9gEuGf3sWbTx7bfOiEIcDs36-heo/preview
+[19]: https://prezi.com/view/THNPJGFVDUCWoM20syev/
+[20]: https://behdad.org/doc/harfbuzz10years-slides.pdf
+[21]: https://docs.google.com/document/d/1_vZrt97OorJ0jA1YzJ29LRcGr3YGrNJANdOABjVZGEs/preview
+[22]: https://docs.google.com/document/d/1aH_waagdEM5UhslQxCeFEb82ECBhPlZjy5_MwLNLBYo/preview
+[23]: https://docs.google.com/document/d/1hRd5oYQJLrt0JuwWhEJWi7wh_9rbaIJkX6IR9DW7rZQ/preview
+[24]: https://docs.google.com/document/d/1a3K6fHjsiWW36vSzwJwCwEBOgznunKs80PSpBbpfHiA/preview
+[25]: https://docs.google.com/presentation/d/1o9Exz1c-Lr-dJjA8dcBn_Vl_Y37cupmFzmclMjBE_Bc/view
+[26]: https://harfbuzz-world.cc/
diff --git a/3rdparty/harfbuzz/hb_extract.py b/3rdparty/harfbuzz/hb_extract.py
new file mode 100644
index 0000000000..c7e1b10f45
--- /dev/null
+++ b/3rdparty/harfbuzz/hb_extract.py
@@ -0,0 +1,386 @@
+#!/usr/bin/env python3
+"""
+hb_extract.py -- copy the minimal subset of HarfBuzz sources used by OpenCV.
+
+OpenCV vendors HarfBuzz as a normal static library: every .cc is a separate
+translation unit (no unity / amalgamation build). This script reproduces the
+exact src/ tree that OpenCV ships, from a pristine HarfBuzz checkout.
+
+It does three things beyond a naive copy, all to keep the vendored tree small
+and buildable with OpenCV's configuration (HB_TINY + HB_HAS_RASTER, see
+CMakeLists.txt):
+
+ 1. Copies the translation units that upstream `harfbuzz-world.cc` would
+ compile for the requested HB_HAS_* sections -- EXCEPT the ones that build
+ to an empty object under our config (see EMPTY_TUS). It never emits
+ `harfbuzz-world.cc` itself (we compile each .cc directly), and never
+ copies non-TU .cc files found under src/OT|graph (only real TUs from the
+ parsed world.cc lists are copied), so CMake's file(GLOB_RECURSE src/*.cc)
+ picks up exactly the right set.
+
+ 2. Prunes headers down to the set actually reachable by #include from the
+ copied TUs (+ the public headers OpenCV includes). This automatically
+ drops the large amount of HarfBuzz src/ that we never compile -- GPU and
+ WASM backends, the subset/repacker graph, platform backends (CoreText/
+ DirectWrite/GDI/Uniscribe/Graphite2/GObject/Cairo), the vector-paint
+ backend, and headers used only by the skipped empty TUs -- without a
+ hardcoded blocklist.
+
+ 3. Generates `hb-opencv-config.hh`, the config-override header CMakeLists.txt
+ points HB_CONFIG_OVERRIDE_H at. It re-enables thread-safety (HB_NO_MT) and
+ variable fonts (HB_NO_VAR) that HB_TINY would otherwise switch off. (It
+ does NOT generate upstream's hb-features.h: nothing we compile includes
+ it.)
+
+Usage (to refresh this very directory, just point it at a harfbuzz checkout):
+ python hb_extract.py path/to/harfbuzz
+
+ The defaults already produce the OpenCV subset: output '.', features
+ HB_HAS_RASTER, and it also copies README.md + COPYING. Override only if needed:
+ -o DIR output root (default '.')
+ -f FLAGS comma-separated HB_HAS_* (default HB_HAS_RASTER)
+ -a FILES extra files (default README.md,COPYING)
+ --list-flags print available HB_HAS_* flags and exit
+
+-a paths are relative to the harfbuzz repo root (parent of src/):
+ -a README.md -> copied to /README.md
+ -a COPYING -> copied to /COPYING
+ -a src/hb-raster.h -> copied to /src/hb-raster.h
+"""
+
+import argparse
+import posixpath
+import re
+import shutil
+import sys
+from pathlib import Path
+
+
+# Translation units that compile to an EMPTY object (libtool: "has no symbols")
+# under OpenCV's HarfBuzz configuration (HB_TINY + HB_HAS_RASTER => AAT, legacy
+# fallback shaping, math, meta, name, buffer (de)serialize/verify and the style
+# API are all compiled out). They contribute nothing, so we do not vendor them.
+# Re-derive this list (from the libtool warnings of a clean libharfbuzz build)
+# if the HarfBuzz configuration in CMakeLists.txt changes.
+EMPTY_TUS = {
+ "hb-aat-layout.cc",
+ "hb-aat-map.cc",
+ "hb-buffer-serialize.cc",
+ "hb-buffer-verify.cc",
+ "hb-fallback-shape.cc",
+ "hb-ot-math.cc",
+ "hb-ot-meta.cc",
+ "hb-ot-name.cc",
+ "hb-raster.cc",
+ "hb-style.cc",
+}
+
+# Public headers OpenCV's text engine (drawing_text.cpp) includes directly.
+# Used together with the copied TUs as the roots of the header-reachability
+# prune.
+SEED_PUBLIC_HEADERS = ("hb.h", "hb-ot.h", "hb-raster.h")
+
+# Generated config-override header. CMakeLists.txt builds HarfBuzz with HB_TINY
+# and HB_CONFIG_OVERRIDE_H="hb-opencv-config.hh"; this file is included by
+# hb-config.hh after HB_TINY/HB_LEAN expand (so the #undef takes effect) but
+# before the option-closure derives dependent macros.
+OPENCV_CONFIG_HH = '''\
+/*
+ * OpenCV-specific HarfBuzz configuration override. GENERATED by hb_extract.py.
+ *
+ * HarfBuzz is built with HB_TINY (see 3rdparty/harfbuzz/CMakeLists.txt) for the
+ * smallest possible footprint. HB_TINY pulls in HB_LEAN + HB_MINI, which would
+ * disable two features that OpenCV's text engine relies on. We restore them
+ * here.
+ *
+ * This file is included by hb-config.hh via HB_CONFIG_OVERRIDE_H, i.e. AFTER
+ * HB_TINY/HB_LEAN/HB_MINI have expanded their macros (so the #undef below takes
+ * effect) but BEFORE the "closure of options" derives dependent macros (e.g.
+ * HB_NO_VAR_COMPOSITES from HB_NO_VAR). That ordering is what makes a plain
+ * #undef sufficient.
+ *
+ * HB_NO_MT - keep thread-safety. hb_font_t instances live inside cv::FontFace
+ * objects, NOT in the thread_local FontRenderEngine, so a single
+ * FontFace (and its hb_font_t) can be shared across threads; its
+ * reference counting must stay atomic.
+ *
+ * HB_NO_VAR - keep variable-font support: the 'wght' axis used for synthetic
+ * weights and named-instance/axis queries (hb_font_set_variations,
+ * hb_ot_var_*). Variable fonts are a primary reason OpenCV adopted
+ * HarfBuzz, so this must remain enabled.
+ */
+
+#undef HB_NO_MT
+#undef HB_NO_VAR
+'''
+
+OPENCV_CONFIG_NAME = "hb-opencv-config.hh"
+
+
+def parse_world_cc(path):
+ """Return (core_files, sections) parsed from harfbuzz-world.cc.
+
+ core_files -- list of .cc paths (relative to src/) in the unconditional
+ "Core library" section.
+ sections -- dict HB_HAS_XXX -> list of .cc paths from that #ifdef block.
+ """
+ lines = path.read_text(encoding="utf-8").splitlines()
+ include_re = re.compile(r'^\s*#include\s+"([^"]+\.cc)"')
+
+ core_files = []
+ sections = {}
+
+ STATE_PREAMBLE, STATE_CORE, STATE_IFDEF = "preamble", "core", "ifdef"
+ state = STATE_PREAMBLE
+ current_flag = None
+ ifdef_depth = 0
+
+ for line in lines:
+ if state == STATE_PREAMBLE:
+ if "/* Core library." in line:
+ state = STATE_CORE
+
+ elif state == STATE_CORE:
+ m = include_re.match(line)
+ if m:
+ core_files.append(m.group(1))
+ else:
+ m = re.match(r"^\s*#ifdef\s+(HB_HAS_\w+)", line)
+ if m:
+ current_flag = m.group(1)
+ sections[current_flag] = []
+ state = STATE_IFDEF
+ ifdef_depth = 1
+
+ elif state == STATE_IFDEF:
+ if re.match(r"^\s*#if", line):
+ ifdef_depth += 1
+ elif re.match(r"^\s*#endif", line):
+ ifdef_depth -= 1
+ if ifdef_depth == 0:
+ state = STATE_CORE
+ current_flag = None
+ else:
+ m = include_re.match(line)
+ if m and current_flag is not None:
+ sections[current_flag].append(m.group(1))
+
+ return core_files, sections
+
+
+def copy_files(file_list, src_dir, out_src_dir, label=""):
+ skipped = []
+ for rel in file_list:
+ if Path(rel).name in EMPTY_TUS:
+ skipped.append(rel)
+ continue
+ src = src_dir / rel
+ dst = out_src_dir / rel
+ dst.parent.mkdir(parents=True, exist_ok=True)
+ if not src.exists():
+ print(f" [WARN] not found: {src}", file=sys.stderr)
+ continue
+ shutil.copy2(src, dst)
+ print(f" [{label or 'core'}] src/{rel}")
+ for rel in skipped:
+ print(f" [skip-empty] src/{rel}")
+
+
+def copy_headers(src_dir, out_src_dir, public_h=True):
+ """Copy all candidate headers: src/*.hh, src/*.h (public API), and *.h/*.hh
+ from src/OT/** and src/graph/**. The unreferenced ones are removed later by
+ prune_unreferenced_headers().
+
+ NOTE: .cc files under OT/ and graph/ are intentionally NOT copied here --
+ the only ones we need are real translation units, copied via the parsed
+ world.cc lists. Copying e.g. src/graph/test-classdef-graph.cc would make
+ CMake's file(GLOB_RECURSE src/*.cc) try to compile a non-TU file.
+ """
+ count = 0
+
+ def _copy(p):
+ nonlocal count
+ dst = out_src_dir / p.relative_to(src_dir)
+ dst.parent.mkdir(parents=True, exist_ok=True)
+ shutil.copy2(p, dst)
+ count += 1
+
+ for hdr in src_dir.glob("*.hh"):
+ _copy(hdr)
+
+ for subdir in ("OT", "graph"):
+ d = src_dir / subdir
+ if not d.exists():
+ continue
+ for hdr in d.rglob("*"):
+ if hdr.is_file() and hdr.suffix in (".h", ".hh"):
+ _copy(hdr)
+
+ if public_h:
+ for hdr in src_dir.glob("*.h"):
+ _copy(hdr)
+
+ return count
+
+
+def prune_unreferenced_headers(out_src_dir):
+ """Delete every header not reachable by #include from the copied TUs and the
+ public seed headers, then drop any directory left empty.
+
+ HarfBuzz's amalgamated src/ ships many headers we never compile (GPU/WASM
+ backends, subset/repacker graph, platform backends, vector-paint, and the
+ headers used only by the empty TUs we skip). Reachability removes them all
+ without a hardcoded list. The edge regex also treats any quoted "...h"/
+ "...hh" literal as an include, which covers the `#include HB_STRING_ARRAY_LIST`
+ macro-indirection trick (e.g. hb-ot-cff1-std-str.hh, hb-ot-post-macroman.hh).
+ """
+ files = {p.relative_to(out_src_dir).as_posix()
+ for p in out_src_dir.rglob("*") if p.is_file()}
+ headers = {f for f in files if f.endswith((".h", ".hh"))}
+ edge_re = re.compile(r'#\s*include\s+["<]([^">]+)[">]|"([^"]+\.hh?)"')
+
+ def resolve(inc, including):
+ for cand in (posixpath.normpath(posixpath.join(posixpath.dirname(including), inc)),
+ posixpath.normpath(inc)):
+ if cand in files:
+ return cand
+ return None
+
+ seeds = {f for f in files if f.endswith(".cc")}
+ seeds |= {h for h in SEED_PUBLIC_HEADERS if h in files}
+
+ seen, stack = set(), list(seeds)
+ while stack:
+ cur = stack.pop()
+ if cur in seen:
+ continue
+ seen.add(cur)
+ try:
+ txt = (out_src_dir / cur).read_text(encoding="utf-8", errors="ignore")
+ except OSError:
+ continue
+ for m in edge_re.finditer(txt):
+ inc = m.group(1) or m.group(2)
+ r = resolve(inc, cur)
+ if r and r not in seen:
+ stack.append(r)
+
+ needed = {f for f in seen if f.endswith((".h", ".hh"))}
+ for h in sorted(headers - needed):
+ (out_src_dir / h).unlink()
+ print(f" [prune] src/{h}")
+
+ # remove directories left empty by pruning (deepest first)
+ for d in sorted((p for p in out_src_dir.rglob("*") if p.is_dir()),
+ key=lambda p: len(p.parts), reverse=True):
+ try:
+ d.rmdir()
+ except OSError:
+ pass
+
+ return len(headers - needed), len(needed)
+
+
+def write_opencv_config_h(out_src_dir):
+ """Generate hb-opencv-config.hh (the HB_CONFIG_OVERRIDE_H header)."""
+ (out_src_dir / OPENCV_CONFIG_NAME).write_text(OPENCV_CONFIG_HH, encoding="utf-8")
+ print(f" [gen] src/{OPENCV_CONFIG_NAME}")
+
+
+def main():
+ parser = argparse.ArgumentParser(
+ description="Copy the OpenCV subset of HarfBuzz sources.",
+ formatter_class=argparse.RawDescriptionHelpFormatter,
+ epilog="""
+examples:
+ python hb_extract.py ~/work/harfbuzz -o . -f HB_HAS_RASTER -a README.md,COPYING
+ python hb_extract.py ~/work/harfbuzz --list-flags
+ """,
+ )
+ parser.add_argument("harfbuzz_dir", metavar="harfbuzz-dir",
+ help="path to the harfbuzz repo root (contains src/)")
+ parser.add_argument("-o", "--output", metavar="DIR", default=".",
+ help="output root directory; sources go into DIR/src/ "
+ "(default: current directory)")
+ parser.add_argument("-f", "--features", metavar="FLAGS", default="HB_HAS_RASTER",
+ help="comma-separated HB_HAS_* flags (default: HB_HAS_RASTER)")
+ parser.add_argument("-a", "--add", metavar="FILES", default="README.md,COPYING",
+ help="comma-separated files relative to the harfbuzz repo "
+ "root (default: README.md,COPYING)")
+ parser.add_argument("--no-public-headers", action="store_true",
+ help="skip copying public src/*.h API headers "
+ "(OT/, graph/, *.hh are always copied)")
+ parser.add_argument("--list-flags", action="store_true",
+ help="list available HB_HAS_* flags and exit")
+ args = parser.parse_args()
+
+ repo_dir = Path(args.harfbuzz_dir).resolve()
+ world_cc_path = repo_dir / "src" / "harfbuzz-world.cc"
+ if not world_cc_path.exists():
+ print(f"error: {world_cc_path} not found", file=sys.stderr)
+ sys.exit(1)
+
+ src_dir = world_cc_path.parent
+ core_files, sections = parse_world_cc(world_cc_path)
+
+ if args.list_flags:
+ for flag, files in sorted(sections.items()):
+ print(f" {flag:<22} ({len(files)} .cc files)")
+ return
+
+ enabled_flags = [f.strip() for f in args.features.split(",") if f.strip()]
+ extra_files = [f.strip() for f in args.add.split(",") if f.strip()]
+
+ out_dir = Path(args.output).resolve()
+ out_src_dir = out_dir / "src"
+
+ # Start from a clean src/ so removed-upstream files do not linger.
+ if out_src_dir.exists():
+ shutil.rmtree(out_src_dir)
+ out_src_dir.mkdir(parents=True, exist_ok=True)
+
+ print(f"\nsource : {repo_dir}")
+ print(f"output : {out_dir}")
+ print(f"flags : {enabled_flags}\n")
+
+ print("core:")
+ copy_files(core_files, src_dir, out_src_dir)
+
+ for flag in enabled_flags:
+ flag_files = sections.get(flag)
+ if not flag_files:
+ print(f"\n[{flag}]: no .cc files (flag not found in harfbuzz-world.cc)")
+ continue
+ print(f"\n[{flag}]:")
+ copy_files(flag_files, src_dir, out_src_dir, label=flag)
+
+ if extra_files:
+ print("\nextra:")
+ for rel in extra_files:
+ src = repo_dir / rel
+ dst = out_dir / rel
+ dst.parent.mkdir(parents=True, exist_ok=True)
+ if not src.exists():
+ print(f" [WARN] not found: {src}", file=sys.stderr)
+ continue
+ shutil.copy2(src, dst)
+ print(f" [add] {rel}")
+
+ n = copy_headers(src_dir, out_src_dir,
+ public_h=not args.no_public_headers)
+ print(f"\nheaders: {n} files copied")
+
+ print("\nprune (unreferenced headers):")
+ removed, kept = prune_unreferenced_headers(out_src_dir)
+ print(f" removed {removed}, kept {kept}")
+
+ print("\ngenerated:")
+ write_opencv_config_h(out_src_dir)
+
+ all_cc = sorted(out_src_dir.rglob("*.cc"))
+ all_h = list(out_src_dir.rglob("*.h")) + list(out_src_dir.rglob("*.hh"))
+ print(f"\ndone: {len(all_cc)} .cc | {len(all_h)} headers -> {out_dir}")
+
+
+if __name__ == "__main__":
+ main()
diff --git a/3rdparty/harfbuzz/src/OT/Color/CBDT/CBDT.hh b/3rdparty/harfbuzz/src/OT/Color/CBDT/CBDT.hh
new file mode 100644
index 0000000000..2d8f1da52d
--- /dev/null
+++ b/3rdparty/harfbuzz/src/OT/Color/CBDT/CBDT.hh
@@ -0,0 +1,1033 @@
+/*
+ * Copyright © 2016 Google, Inc.
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Seigo Nonaka, Calder Kitagawa
+ */
+
+#ifndef OT_COLOR_CBDT_CBDT_HH
+#define OT_COLOR_CBDT_CBDT_HH
+
+#include "../../../hb-open-type.hh"
+#include "../../../hb-paint.hh"
+
+/*
+ * CBLC -- Color Bitmap Location
+ * https://docs.microsoft.com/en-us/typography/opentype/spec/cblc
+ * https://docs.microsoft.com/en-us/typography/opentype/spec/eblc
+ * CBDT -- Color Bitmap Data
+ * https://docs.microsoft.com/en-us/typography/opentype/spec/cbdt
+ * https://docs.microsoft.com/en-us/typography/opentype/spec/ebdt
+ */
+#define HB_OT_TAG_CBLC HB_TAG('C','B','L','C')
+#define HB_OT_TAG_CBDT HB_TAG('C','B','D','T')
+
+
+namespace OT {
+
+struct cblc_bitmap_size_subset_context_t
+{
+ const char *cbdt;
+ unsigned int cbdt_length;
+ hb_vector_t *cbdt_prime;
+ unsigned int size; /* INOUT
+ * Input: old size of IndexSubtable
+ * Output: new size of IndexSubtable
+ */
+ unsigned int num_tables; /* INOUT
+ * Input: old number of subtables.
+ * Output: new number of subtables.
+ */
+ hb_codepoint_t start_glyph; /* OUT */
+ hb_codepoint_t end_glyph; /* OUT */
+};
+
+static inline bool
+_copy_data_to_cbdt (hb_vector_t *cbdt_prime,
+ const void *data,
+ unsigned length)
+{
+ unsigned int new_len = cbdt_prime->length + length;
+ if (unlikely (!cbdt_prime->alloc (new_len))) return false;
+ hb_memcpy (cbdt_prime->arrayZ + cbdt_prime->length, data, length);
+ cbdt_prime->length = new_len;
+ return true;
+}
+
+struct SmallGlyphMetrics
+{
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this));
+ }
+
+ void get_extents (hb_font_t *font, hb_glyph_extents_t *extents, bool scale) const
+ {
+ extents->x_bearing = bearingX;
+ extents->y_bearing = bearingY;
+ extents->width = width;
+ extents->height = -static_cast (height);
+
+ if (scale)
+ font->scale_glyph_extents (extents);
+ }
+
+ HBUINT8 height;
+ HBUINT8 width;
+ HBINT8 bearingX;
+ HBINT8 bearingY;
+ HBUINT8 advance;
+ public:
+ DEFINE_SIZE_STATIC (5);
+};
+
+struct BigGlyphMetrics : SmallGlyphMetrics
+{
+ HBINT8 vertBearingX;
+ HBINT8 vertBearingY;
+ HBUINT8 vertAdvance;
+ public:
+ DEFINE_SIZE_STATIC (8);
+};
+
+struct SBitLineMetrics
+{
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this));
+ }
+
+ HBINT8 ascender;
+ HBINT8 decender;
+ HBUINT8 widthMax;
+ HBINT8 caretSlopeNumerator;
+ HBINT8 caretSlopeDenominator;
+ HBINT8 caretOffset;
+ HBINT8 minOriginSB;
+ HBINT8 minAdvanceSB;
+ HBINT8 maxBeforeBL;
+ HBINT8 minAfterBL;
+ HBINT8 padding1;
+ HBINT8 padding2;
+ public:
+ DEFINE_SIZE_STATIC (12);
+};
+
+
+/*
+ * Index Subtables.
+ */
+
+struct IndexSubtableHeader
+{
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this));
+ }
+
+ HBUINT16 indexFormat;
+ HBUINT16 imageFormat;
+ HBUINT32 imageDataOffset;
+ public:
+ DEFINE_SIZE_STATIC (8);
+};
+
+template
+struct IndexSubtableFormat1Or3
+{
+ bool sanitize (hb_sanitize_context_t *c, unsigned int glyph_count) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this) &&
+ offsetArrayZ.sanitize (c, glyph_count + 1));
+ }
+
+ bool get_image_data (unsigned int idx,
+ unsigned int *offset,
+ unsigned int *length) const
+ {
+ if (unlikely (offsetArrayZ[idx + 1] <= offsetArrayZ[idx]))
+ return false;
+
+ *offset = header.imageDataOffset + offsetArrayZ[idx];
+ *length = offsetArrayZ[idx + 1] - offsetArrayZ[idx];
+ return true;
+ }
+
+ bool add_offset (hb_serialize_context_t *c,
+ unsigned int offset,
+ unsigned int *size /* OUT (accumulated) */)
+ {
+ TRACE_SERIALIZE (this);
+ Offset embedded_offset;
+ embedded_offset = offset;
+ *size += sizeof (OffsetType);
+ auto *o = c->embed (embedded_offset);
+ return_trace ((bool) o);
+ }
+
+ IndexSubtableHeader header;
+ UnsizedArrayOf>
+ offsetArrayZ;
+ public:
+ DEFINE_SIZE_ARRAY (8, offsetArrayZ);
+};
+
+struct IndexSubtableFormat1 : IndexSubtableFormat1Or3 {};
+struct IndexSubtableFormat3 : IndexSubtableFormat1Or3 {};
+
+struct IndexSubtable
+{
+ bool sanitize (hb_sanitize_context_t *c, unsigned int glyph_count) const
+ {
+ TRACE_SANITIZE (this);
+ if (!u.header.sanitize (c)) return_trace (false);
+ hb_barrier ();
+ switch (u.header.indexFormat)
+ {
+ case 1: return_trace (u.format1.sanitize (c, glyph_count));
+ case 3: return_trace (u.format3.sanitize (c, glyph_count));
+ default:return_trace (true);
+ }
+ }
+
+ bool
+ finish_subtable (hb_serialize_context_t *c,
+ unsigned int cbdt_prime_len,
+ unsigned int num_glyphs,
+ unsigned int *size /* OUT (accumulated) */)
+ {
+ TRACE_SERIALIZE (this);
+
+ unsigned int local_offset = cbdt_prime_len - u.header.imageDataOffset;
+ switch (u.header.indexFormat)
+ {
+ case 1: return_trace (u.format1.add_offset (c, local_offset, size));
+ case 3: {
+ if (!u.format3.add_offset (c, local_offset, size))
+ return_trace (false);
+ if (!(num_glyphs & 0x01)) // Pad to 32-bit alignment if needed.
+ return_trace (u.format3.add_offset (c, 0, size));
+ return_trace (true);
+ }
+ // TODO: implement 2, 4, 5.
+ case 2: case 4: // No-op.
+ case 5: // Pad to 32-bit aligned.
+ default: return_trace (false);
+ }
+ }
+
+ bool
+ fill_missing_glyphs (hb_serialize_context_t *c,
+ unsigned int cbdt_prime_len,
+ unsigned int num_missing,
+ unsigned int *size /* OUT (accumulated) */,
+ unsigned int *num_glyphs /* OUT (accumulated) */)
+ {
+ TRACE_SERIALIZE (this);
+
+ unsigned int local_offset = cbdt_prime_len - u.header.imageDataOffset;
+ switch (u.header.indexFormat)
+ {
+ case 1: {
+ for (unsigned int i = 0; i < num_missing; i++)
+ {
+ if (unlikely (!u.format1.add_offset (c, local_offset, size)))
+ return_trace (false);
+ *num_glyphs += 1;
+ }
+ return_trace (true);
+ }
+ case 3: {
+ for (unsigned int i = 0; i < num_missing; i++)
+ {
+ if (unlikely (!u.format3.add_offset (c, local_offset, size)))
+ return_trace (false);
+ *num_glyphs += 1;
+ }
+ return_trace (true);
+ }
+ // TODO: implement 2, 4, 5.
+ case 2: // Add empty space in cbdt_prime?.
+ case 4: case 5: // No-op as sparse is supported.
+ default: return_trace (false);
+ }
+ }
+
+ bool
+ copy_glyph_at_idx (hb_serialize_context_t *c, unsigned int idx,
+ const char *cbdt, unsigned int cbdt_length,
+ hb_vector_t *cbdt_prime /* INOUT */,
+ IndexSubtable *subtable_prime /* INOUT */,
+ unsigned int *size /* OUT (accumulated) */) const
+ {
+ TRACE_SERIALIZE (this);
+
+ unsigned int offset, length, format;
+ if (unlikely (!get_image_data (idx, &offset, &length, &format))) return_trace (false);
+ if (unlikely (offset > cbdt_length || cbdt_length - offset < length)) return_trace (false);
+
+ auto *header_prime = subtable_prime->get_header ();
+ unsigned int new_local_offset = cbdt_prime->length - (unsigned int) header_prime->imageDataOffset;
+ if (unlikely (!_copy_data_to_cbdt (cbdt_prime, cbdt + offset, length))) return_trace (false);
+
+ return_trace (subtable_prime->add_offset (c, new_local_offset, size));
+ }
+
+ bool
+ add_offset (hb_serialize_context_t *c, unsigned int local_offset,
+ unsigned int *size /* OUT (accumulated) */)
+ {
+ TRACE_SERIALIZE (this);
+ switch (u.header.indexFormat)
+ {
+ case 1: return_trace (u.format1.add_offset (c, local_offset, size));
+ case 3: return_trace (u.format3.add_offset (c, local_offset, size));
+ // TODO: Implement tables 2, 4, 5
+ case 2: // Should be a no-op.
+ case 4: case 5: // Handle sparse cases.
+ default: return_trace (false);
+ }
+ }
+
+ bool get_extents (hb_glyph_extents_t *extents HB_UNUSED, bool scale HB_UNUSED) const
+ {
+ switch (u.header.indexFormat)
+ {
+ case 2: case 5: /* TODO */
+ case 1: case 3: case 4: /* Variable-metrics formats do not have metrics here. */
+ default:return (false);
+ }
+ }
+
+ bool
+ get_image_data (unsigned int idx, unsigned int *offset,
+ unsigned int *length, unsigned int *format) const
+ {
+ *format = u.header.imageFormat;
+ switch (u.header.indexFormat)
+ {
+ case 1: return u.format1.get_image_data (idx, offset, length);
+ case 3: return u.format3.get_image_data (idx, offset, length);
+ default: return false;
+ }
+ }
+
+ const IndexSubtableHeader* get_header () const { return &u.header; }
+
+ void populate_header (unsigned index_format,
+ unsigned image_format,
+ unsigned int image_data_offset,
+ unsigned int *size)
+ {
+ u.header.indexFormat = index_format;
+ u.header.imageFormat = image_format;
+ u.header.imageDataOffset = image_data_offset;
+ switch (u.header.indexFormat)
+ {
+ case 1: *size += IndexSubtableFormat1::min_size; break;
+ case 3: *size += IndexSubtableFormat3::min_size; break;
+ }
+ }
+
+ protected:
+ union {
+ IndexSubtableHeader header;
+ IndexSubtableFormat1 format1;
+ IndexSubtableFormat3 format3;
+ /* TODO: Format 2, 4, 5. */
+ } u;
+ public:
+ DEFINE_SIZE_UNION (8, header);
+};
+
+struct IndexSubtableRecord
+{
+ /* XXX Remove this and fix by not inserting it into vector. */
+ IndexSubtableRecord& operator = (const IndexSubtableRecord &o)
+ {
+ firstGlyphIndex = o.firstGlyphIndex;
+ lastGlyphIndex = o.lastGlyphIndex;
+ offsetToSubtable = (unsigned) o.offsetToSubtable;
+ assert (offsetToSubtable.is_null ());
+ return *this;
+ }
+
+ bool sanitize (hb_sanitize_context_t *c, const void *base) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this) &&
+ hb_barrier () &&
+ firstGlyphIndex <= lastGlyphIndex &&
+ offsetToSubtable.sanitize (c, base, lastGlyphIndex - firstGlyphIndex + 1));
+ }
+
+ const IndexSubtable* get_subtable (const void *base) const
+ {
+ return &(base+offsetToSubtable);
+ }
+
+ bool add_new_subtable (hb_subset_context_t* c,
+ cblc_bitmap_size_subset_context_t *bitmap_size_context,
+ IndexSubtableRecord *record,
+ const hb_vector_t> *lookup, /* IN */
+ const void *base,
+ unsigned int *start /* INOUT */) const
+ {
+ TRACE_SERIALIZE (this);
+
+ auto *subtable = c->serializer->start_embed ();
+ if (unlikely (!c->serializer->extend_min (subtable))) return_trace (false);
+
+ auto *old_subtable = get_subtable (base);
+ auto *old_header = old_subtable->get_header ();
+
+ subtable->populate_header (old_header->indexFormat,
+ old_header->imageFormat,
+ bitmap_size_context->cbdt_prime->length,
+ &bitmap_size_context->size);
+
+ unsigned int num_glyphs = 0;
+ bool early_exit = false;
+ for (unsigned int i = *start; i < lookup->length; i++)
+ {
+ hb_codepoint_t new_gid = (*lookup)[i].first;
+ const IndexSubtableRecord *next_record = (*lookup)[i].second;
+ const IndexSubtable *next_subtable = next_record->get_subtable (base);
+ auto *next_header = next_subtable->get_header ();
+ if (next_header != old_header)
+ {
+ *start = i;
+ early_exit = true;
+ break;
+ }
+ unsigned int num_missing = record->add_glyph_for_subset (new_gid);
+ if (unlikely (!subtable->fill_missing_glyphs (c->serializer,
+ bitmap_size_context->cbdt_prime->length,
+ num_missing,
+ &bitmap_size_context->size,
+ &num_glyphs)))
+ return_trace (false);
+
+ hb_codepoint_t old_gid = 0;
+ c->plan->old_gid_for_new_gid (new_gid, &old_gid);
+ if (old_gid < next_record->firstGlyphIndex)
+ return_trace (false);
+
+ unsigned int old_idx = (unsigned int) old_gid - next_record->firstGlyphIndex;
+ if (unlikely (!next_subtable->copy_glyph_at_idx (c->serializer,
+ old_idx,
+ bitmap_size_context->cbdt,
+ bitmap_size_context->cbdt_length,
+ bitmap_size_context->cbdt_prime,
+ subtable,
+ &bitmap_size_context->size)))
+ return_trace (false);
+ num_glyphs += 1;
+ }
+ if (!early_exit)
+ *start = lookup->length;
+ if (unlikely (!subtable->finish_subtable (c->serializer,
+ bitmap_size_context->cbdt_prime->length,
+ num_glyphs,
+ &bitmap_size_context->size)))
+ return_trace (false);
+ return_trace (true);
+ }
+
+ bool add_new_record (hb_subset_context_t *c,
+ cblc_bitmap_size_subset_context_t *bitmap_size_context,
+ const hb_vector_t> *lookup, /* IN */
+ const void *base,
+ unsigned int *start, /* INOUT */
+ hb_vector_t* records /* INOUT */) const
+ {
+ TRACE_SERIALIZE (this);
+ auto snap = c->serializer->snapshot ();
+ unsigned int old_size = bitmap_size_context->size;
+ unsigned int old_cbdt_prime_length = bitmap_size_context->cbdt_prime->length;
+
+ // Set to invalid state to indicate filling glyphs is not yet started.
+ if (unlikely (!c->serializer->check_success (records->resize (records->length + 1))))
+ return_trace (false);
+
+ records->tail ().firstGlyphIndex = 1;
+ records->tail ().lastGlyphIndex = 0;
+ bitmap_size_context->size += IndexSubtableRecord::min_size;
+
+ c->serializer->push ();
+
+ if (unlikely (!add_new_subtable (c, bitmap_size_context, &(records->tail ()), lookup, base, start)))
+ {
+ c->serializer->pop_discard ();
+ c->serializer->revert (snap);
+ bitmap_size_context->cbdt_prime->shrink (old_cbdt_prime_length);
+ bitmap_size_context->size = old_size;
+ records->resize (records->length - 1);
+ return_trace (false);
+ }
+
+ bitmap_size_context->num_tables += 1;
+ return_trace (true);
+ }
+
+ unsigned int add_glyph_for_subset (hb_codepoint_t gid)
+ {
+ if (firstGlyphIndex > lastGlyphIndex)
+ {
+ firstGlyphIndex = gid;
+ lastGlyphIndex = gid;
+ return 0;
+ }
+ // TODO maybe assert? this shouldn't occur.
+ if (lastGlyphIndex > gid)
+ return 0;
+ unsigned int num_missing = (unsigned int) (gid - lastGlyphIndex - 1);
+ lastGlyphIndex = gid;
+ return num_missing;
+ }
+
+ bool get_extents (hb_glyph_extents_t *extents, const void *base, bool scale) const
+ { return (base+offsetToSubtable).get_extents (extents, scale); }
+
+ bool get_image_data (unsigned int gid,
+ const void *base,
+ unsigned int *offset,
+ unsigned int *length,
+ unsigned int *format) const
+ {
+ if (gid < firstGlyphIndex || gid > lastGlyphIndex) return false;
+ return (base+offsetToSubtable).get_image_data (gid - firstGlyphIndex,
+ offset, length, format);
+ }
+
+ HBGlyphID16 firstGlyphIndex;
+ HBGlyphID16 lastGlyphIndex;
+ Offset32To offsetToSubtable;
+ public:
+ DEFINE_SIZE_STATIC (8);
+};
+
+struct IndexSubtableArray
+{
+ friend struct CBDT;
+
+ bool sanitize (hb_sanitize_context_t *c, unsigned int count) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (indexSubtablesZ.sanitize (c, count, this));
+ }
+
+ void
+ build_lookup (hb_subset_context_t *c, cblc_bitmap_size_subset_context_t *bitmap_size_context,
+ hb_vector_t> *lookup /* OUT */) const
+ {
+ bool start_glyph_is_set = false;
+ unsigned num_glyphs = c->plan->num_output_glyphs ();
+ for (hb_codepoint_t new_gid = 0; new_gid < num_glyphs; new_gid++)
+ {
+ hb_codepoint_t old_gid;
+ if (unlikely (!c->plan->old_gid_for_new_gid (new_gid, &old_gid))) continue;
+
+ const IndexSubtableRecord* record = find_table (old_gid, bitmap_size_context->num_tables);
+ if (unlikely (!record)) continue;
+
+ // Don't add gaps to the lookup. The best way to determine if a glyph is a
+ // gap is that it has no image data.
+ unsigned int offset, length, format;
+ if (unlikely (!record->get_image_data (old_gid, this, &offset, &length, &format))) continue;
+
+ lookup->push (hb_pair_t (new_gid, record));
+
+ if (!start_glyph_is_set)
+ {
+ bitmap_size_context->start_glyph = new_gid;
+ start_glyph_is_set = true;
+ }
+
+ bitmap_size_context->end_glyph = new_gid;
+ }
+ }
+
+ bool
+ subset (hb_subset_context_t *c,
+ cblc_bitmap_size_subset_context_t *bitmap_size_context) const
+ {
+ TRACE_SUBSET (this);
+
+ hb_vector_t> lookup;
+ build_lookup (c, bitmap_size_context, &lookup);
+ if (unlikely (!c->serializer->propagate_error (lookup)))
+ return false;
+
+ bitmap_size_context->size = 0;
+ bitmap_size_context->num_tables = 0;
+ hb_vector_t records;
+ for (unsigned int start = 0; start < lookup.length;)
+ {
+ if (unlikely (!lookup[start].second->add_new_record (c, bitmap_size_context, &lookup, this, &start, &records)))
+ {
+ // Discard any leftover pushes to the serializer from successful records.
+ for (unsigned int i = 0; i < records.length; i++)
+ c->serializer->pop_discard ();
+ return_trace (false);
+ }
+ }
+
+ /* Workaround to ensure offset ordering is from least to greatest when
+ * resolving links. */
+ hb_vector_t objidxs;
+ for (unsigned int i = 0; i < records.length; i++)
+ objidxs.push (c->serializer->pop_pack ());
+ for (unsigned int i = 0; i < records.length; i++)
+ {
+ IndexSubtableRecord* record = c->serializer->embed (records[i]);
+ if (unlikely (!record)) return_trace (false);
+ c->serializer->add_link (record->offsetToSubtable, objidxs[records.length - 1 - i]);
+ }
+ return_trace (true);
+ }
+
+ public:
+ const IndexSubtableRecord* find_table (hb_codepoint_t glyph, unsigned int numTables) const
+ {
+ for (unsigned int i = 0; i < numTables; ++i)
+ {
+ unsigned int firstGlyphIndex = indexSubtablesZ[i].firstGlyphIndex;
+ unsigned int lastGlyphIndex = indexSubtablesZ[i].lastGlyphIndex;
+ if (firstGlyphIndex <= glyph && glyph <= lastGlyphIndex)
+ return &indexSubtablesZ[i];
+ }
+ return nullptr;
+ }
+
+ protected:
+ UnsizedArrayOf indexSubtablesZ;
+};
+
+struct BitmapSizeTable
+{
+ friend struct CBLC;
+ friend struct CBDT;
+
+ bool sanitize (hb_sanitize_context_t *c, const void *base) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this) &&
+ hb_barrier () &&
+ indexSubtableArrayOffset.sanitize (c, base, numberOfIndexSubtables) &&
+ horizontal.sanitize (c) &&
+ vertical.sanitize (c));
+ }
+
+ const IndexSubtableRecord *
+ find_table (hb_codepoint_t glyph, const void *base, const void **out_base) const
+ {
+ *out_base = &(base+indexSubtableArrayOffset);
+ return (base+indexSubtableArrayOffset).find_table (glyph, numberOfIndexSubtables);
+ }
+
+ bool
+ subset (hb_subset_context_t *c, const void *base,
+ const char *cbdt, unsigned int cbdt_length,
+ hb_vector_t *cbdt_prime /* INOUT */) const
+ {
+ TRACE_SUBSET (this);
+ auto *out_table = c->serializer->embed (this);
+ if (unlikely (!out_table)) return_trace (false);
+
+ cblc_bitmap_size_subset_context_t bitmap_size_context;
+ bitmap_size_context.cbdt = cbdt;
+ bitmap_size_context.cbdt_length = cbdt_length;
+ bitmap_size_context.cbdt_prime = cbdt_prime;
+ bitmap_size_context.size = indexTablesSize;
+ bitmap_size_context.num_tables = numberOfIndexSubtables;
+ bitmap_size_context.start_glyph = 1;
+ bitmap_size_context.end_glyph = 0;
+
+ if (!out_table->indexSubtableArrayOffset.serialize_subset (c,
+ indexSubtableArrayOffset,
+ base,
+ &bitmap_size_context))
+ return_trace (false);
+ if (!bitmap_size_context.size ||
+ !bitmap_size_context.num_tables ||
+ bitmap_size_context.start_glyph > bitmap_size_context.end_glyph)
+ return_trace (false);
+
+ out_table->indexTablesSize = bitmap_size_context.size;
+ out_table->numberOfIndexSubtables = bitmap_size_context.num_tables;
+ out_table->startGlyphIndex = bitmap_size_context.start_glyph;
+ out_table->endGlyphIndex = bitmap_size_context.end_glyph;
+ return_trace (true);
+ }
+
+ protected:
+ NNOffset32To
+ indexSubtableArrayOffset;
+ HBUINT32 indexTablesSize;
+ HBUINT32 numberOfIndexSubtables;
+ HBUINT32 colorRef;
+ SBitLineMetrics horizontal;
+ SBitLineMetrics vertical;
+ HBGlyphID16 startGlyphIndex;
+ HBGlyphID16 endGlyphIndex;
+ HBUINT8 ppemX;
+ HBUINT8 ppemY;
+ HBUINT8 bitDepth;
+ HBINT8 flags;
+ public:
+ DEFINE_SIZE_STATIC (48);
+};
+
+
+/*
+ * Glyph Bitmap Data Formats.
+ */
+
+struct GlyphBitmapDataFormat17
+{
+ SmallGlyphMetrics glyphMetrics;
+ Array32Of data;
+ public:
+ DEFINE_SIZE_ARRAY (9, data);
+};
+
+struct GlyphBitmapDataFormat18
+{
+ BigGlyphMetrics glyphMetrics;
+ Array32Of data;
+ public:
+ DEFINE_SIZE_ARRAY (12, data);
+};
+
+struct GlyphBitmapDataFormat19
+{
+ Array32Of data;
+ public:
+ DEFINE_SIZE_ARRAY (4, data);
+};
+
+struct CBLC
+{
+ friend struct CBDT;
+
+ static constexpr hb_tag_t tableTag = HB_OT_TAG_CBLC;
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this) &&
+ hb_barrier () &&
+ likely (version.major == 2 || version.major == 3) &&
+ hb_barrier () &&
+ sizeTables.sanitize (c, this));
+ }
+
+ static bool
+ sink_cbdt (hb_subset_context_t *c, hb_vector_t* cbdt_prime)
+ {
+ hb_blob_t *cbdt_prime_blob = hb_blob_create (cbdt_prime->arrayZ,
+ cbdt_prime->length,
+ HB_MEMORY_MODE_WRITABLE,
+ cbdt_prime->arrayZ,
+ hb_free);
+ cbdt_prime->init (); // Leak arrayZ to the blob.
+ bool ret = c->plan->add_table (HB_OT_TAG_CBDT, cbdt_prime_blob);
+ hb_blob_destroy (cbdt_prime_blob);
+ return ret;
+ }
+
+ bool
+ subset_size_table (hb_subset_context_t *c, const BitmapSizeTable& table,
+ const char *cbdt /* IN */, unsigned int cbdt_length,
+ CBLC *cblc_prime /* INOUT */, hb_vector_t *cbdt_prime /* INOUT */) const
+ {
+ TRACE_SUBSET (this);
+ cblc_prime->sizeTables.len++;
+
+ auto snap = c->serializer->snapshot ();
+ auto cbdt_prime_len = cbdt_prime->length;
+
+ if (!table.subset (c, this, cbdt, cbdt_length, cbdt_prime))
+ {
+ cblc_prime->sizeTables.len--;
+ c->serializer->revert (snap);
+ cbdt_prime->shrink (cbdt_prime_len);
+ return_trace (false);
+ }
+ return_trace (true);
+ }
+
+ // Implemented in cc file as it depends on definition of CBDT.
+ HB_INTERNAL bool subset (hb_subset_context_t *c) const;
+
+ protected:
+ const BitmapSizeTable &choose_strike (hb_font_t *font) const
+ {
+ unsigned count = sizeTables.len;
+ if (unlikely (!count))
+ return Null (BitmapSizeTable);
+
+ unsigned int requested_ppem = hb_max (font->x_ppem, font->y_ppem);
+ if (!requested_ppem)
+ requested_ppem = 1<<30; /* Choose largest strike. */
+ unsigned int best_i = 0;
+ unsigned int best_ppem = hb_max (sizeTables[0].ppemX, sizeTables[0].ppemY);
+
+ for (unsigned int i = 1; i < count; i++)
+ {
+ unsigned int ppem = hb_max (sizeTables[i].ppemX, sizeTables[i].ppemY);
+ if ((requested_ppem <= ppem && ppem < best_ppem) ||
+ (requested_ppem > best_ppem && ppem > best_ppem))
+ {
+ best_i = i;
+ best_ppem = ppem;
+ }
+ }
+
+ return sizeTables[best_i];
+ }
+
+ protected:
+ FixedVersion<> version;
+ Array32Of sizeTables;
+ public:
+ DEFINE_SIZE_ARRAY (8, sizeTables);
+};
+
+struct CBDT
+{
+ static constexpr hb_tag_t tableTag = HB_OT_TAG_CBDT;
+
+ struct accelerator_t
+ {
+ accelerator_t (hb_face_t *face)
+ {
+ this->cblc = hb_sanitize_context_t ().reference_table (face);
+ this->cbdt = hb_sanitize_context_t ().reference_table (face);
+
+ upem = hb_face_get_upem (face);
+ }
+ ~accelerator_t ()
+ {
+ this->cblc.destroy ();
+ this->cbdt.destroy ();
+ }
+
+ bool
+ get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents, bool scale = true) const
+ {
+ const void *base;
+ const BitmapSizeTable &strike = this->cblc->choose_strike (font);
+ const IndexSubtableRecord *subtable_record = strike.find_table (glyph, cblc, &base);
+ if (!subtable_record || !strike.ppemX || !strike.ppemY)
+ return false;
+
+ if (subtable_record->get_extents (extents, base, scale))
+ return true;
+
+ unsigned int image_offset = 0, image_length = 0, image_format = 0;
+ if (!subtable_record->get_image_data (glyph, base, &image_offset, &image_length, &image_format))
+ return false;
+
+ unsigned int cbdt_len = cbdt.get_length ();
+ if (unlikely (image_offset > cbdt_len || cbdt_len - image_offset < image_length))
+ return false;
+
+ switch (image_format)
+ {
+ case 17: {
+ if (unlikely (image_length < GlyphBitmapDataFormat17::min_size))
+ return false;
+ auto &glyphFormat17 = StructAtOffset (this->cbdt, image_offset);
+ glyphFormat17.glyphMetrics.get_extents (font, extents, scale);
+ break;
+ }
+ case 18: {
+ if (unlikely (image_length < GlyphBitmapDataFormat18::min_size))
+ return false;
+ auto &glyphFormat18 = StructAtOffset (this->cbdt, image_offset);
+ glyphFormat18.glyphMetrics.get_extents (font, extents, scale);
+ break;
+ }
+ default: return false; /* TODO: Support other image formats. */
+ }
+
+ /* Convert to font units. */
+ if (scale)
+ {
+ float x_scale = upem / (float) strike.ppemX;
+ float y_scale = upem / (float) strike.ppemY;
+ extents->x_bearing = roundf (extents->x_bearing * x_scale);
+ extents->y_bearing = roundf (extents->y_bearing * y_scale);
+ extents->width = roundf (extents->width * x_scale);
+ extents->height = roundf (extents->height * y_scale);
+ }
+
+ return true;
+ }
+
+ hb_blob_t*
+ reference_png (hb_font_t *font, hb_codepoint_t glyph) const
+ {
+ const void *base;
+ const BitmapSizeTable &strike = this->cblc->choose_strike (font);
+ const IndexSubtableRecord *subtable_record = strike.find_table (glyph, cblc, &base);
+ if (!subtable_record || !strike.ppemX || !strike.ppemY)
+ return hb_blob_get_empty ();
+
+ unsigned int image_offset = 0, image_length = 0, image_format = 0;
+ if (!subtable_record->get_image_data (glyph, base, &image_offset, &image_length, &image_format))
+ return hb_blob_get_empty ();
+
+ unsigned int cbdt_len = cbdt.get_length ();
+ if (unlikely (image_offset > cbdt_len || cbdt_len - image_offset < image_length))
+ return hb_blob_get_empty ();
+
+ switch (image_format)
+ {
+ case 17:
+ {
+ if (unlikely (image_length < GlyphBitmapDataFormat17::min_size))
+ return hb_blob_get_empty ();
+ auto &glyphFormat17 = StructAtOffset (this->cbdt, image_offset);
+ return hb_blob_create_sub_blob (cbdt.get_blob (),
+ image_offset + GlyphBitmapDataFormat17::min_size,
+ glyphFormat17.data.len);
+ }
+ case 18:
+ {
+ if (unlikely (image_length < GlyphBitmapDataFormat18::min_size))
+ return hb_blob_get_empty ();
+ auto &glyphFormat18 = StructAtOffset (this->cbdt, image_offset);
+ return hb_blob_create_sub_blob (cbdt.get_blob (),
+ image_offset + GlyphBitmapDataFormat18::min_size,
+ glyphFormat18.data.len);
+ }
+ case 19:
+ {
+ if (unlikely (image_length < GlyphBitmapDataFormat19::min_size))
+ return hb_blob_get_empty ();
+ auto &glyphFormat19 = StructAtOffset (this->cbdt, image_offset);
+ return hb_blob_create_sub_blob (cbdt.get_blob (),
+ image_offset + GlyphBitmapDataFormat19::min_size,
+ glyphFormat19.data.len);
+ }
+ default: return hb_blob_get_empty (); /* TODO: Support other image formats. */
+ }
+ }
+
+ bool has_data () const { return cbdt->version.major; }
+
+ bool paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data) const
+ {
+ if (!has_data ()) return false;
+
+ hb_glyph_extents_t extents;
+ hb_glyph_extents_t pixel_extents;
+ if (unlikely (!font->get_glyph_extents (glyph, &extents, false)))
+ return false;
+
+ if (unlikely (!get_extents (font, glyph, &pixel_extents, false)))
+ return false;
+
+ hb_blob_t *blob = reference_png (font, glyph);
+ if (unlikely (hb_blob_is_immutable (blob)))
+ return false;
+
+ bool ret = funcs->image (data,
+ blob,
+ pixel_extents.width, -pixel_extents.height,
+ HB_PAINT_IMAGE_FORMAT_PNG,
+ 0.f,
+ &extents);
+
+ hb_blob_destroy (blob);
+
+ return ret;
+ }
+
+ private:
+ hb_blob_ptr_t cblc;
+ hb_blob_ptr_t cbdt;
+
+ unsigned int upem;
+ };
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this) &&
+ hb_barrier () &&
+ likely (version.major == 2 || version.major == 3));
+ }
+
+ protected:
+ FixedVersion<> version;
+ UnsizedArrayOf dataZ;
+ public:
+ DEFINE_SIZE_ARRAY (4, dataZ);
+};
+
+inline bool
+CBLC::subset (hb_subset_context_t *c) const
+{
+ TRACE_SUBSET (this);
+
+ // Use a vector as a secondary buffer as the tables need to be built in parallel.
+ hb_vector_t cbdt_prime;
+
+ auto *cblc_prime = c->serializer->start_embed ();
+ if (unlikely (!c->serializer->extend_min (cblc_prime))) return_trace (false);
+ cblc_prime->version = version;
+
+ hb_blob_t* cbdt_blob = hb_sanitize_context_t ().reference_table (c->plan->source);
+ unsigned int cbdt_length;
+ CBDT* cbdt = (CBDT *) hb_blob_get_data (cbdt_blob, &cbdt_length);
+ if (unlikely (cbdt_length < CBDT::min_size))
+ {
+ hb_blob_destroy (cbdt_blob);
+ return_trace (false);
+ }
+ _copy_data_to_cbdt (&cbdt_prime, cbdt, CBDT::min_size);
+
+ for (const BitmapSizeTable& table : + sizeTables.iter ())
+ subset_size_table (c, table, (const char *) cbdt, cbdt_length, cblc_prime, &cbdt_prime);
+
+ hb_blob_destroy (cbdt_blob);
+
+ return_trace (CBLC::sink_cbdt (c, &cbdt_prime));
+}
+
+struct CBDT_accelerator_t : CBDT::accelerator_t {
+ CBDT_accelerator_t (hb_face_t *face) : CBDT::accelerator_t (face) {}
+};
+
+
+} /* namespace OT */
+
+#endif /* OT_COLOR_CBDT_CBDT_HH */
diff --git a/3rdparty/harfbuzz/src/OT/Color/COLR/COLR.hh b/3rdparty/harfbuzz/src/OT/Color/COLR/COLR.hh
new file mode 100644
index 0000000000..4b8afb4070
--- /dev/null
+++ b/3rdparty/harfbuzz/src/OT/Color/COLR/COLR.hh
@@ -0,0 +1,2861 @@
+/*
+ * Copyright © 2018 Ebrahim Byagowi
+ * Copyright © 2020 Google, Inc.
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Calder Kitagawa
+ */
+
+#ifndef OT_COLOR_COLR_COLR_HH
+#define OT_COLOR_COLR_COLR_HH
+
+#include "../../../hb.hh"
+#include "../../../hb-decycler.hh"
+#include "../../../hb-open-type.hh"
+#include "../../../hb-ot-var-common.hh"
+#include "../../../hb-paint.hh"
+#include "../../../hb-paint-bounded.hh"
+#include "../../../hb-paint-extents.hh"
+
+#include "../CPAL/CPAL.hh"
+
+/*
+ * COLR -- Color
+ * https://docs.microsoft.com/en-us/typography/opentype/spec/colr
+ */
+#define HB_OT_TAG_COLR HB_TAG('C','O','L','R')
+
+namespace OT {
+struct hb_paint_context_t;
+}
+
+struct hb_colr_scratch_t
+{
+ hb_paint_bounded_context_t paint_bounded;
+ hb_paint_extents_context_t paint_extents;
+};
+
+namespace OT {
+
+struct COLR;
+
+struct Paint;
+
+struct hb_paint_context_t :
+ hb_dispatch_context_t
+{
+ const char *get_name () { return "PAINT"; }
+ template
+ return_t dispatch (const T &obj) { obj.paint_glyph (this); return hb_empty_t (); }
+ static return_t default_return_value () { return hb_empty_t (); }
+
+ const COLR* get_colr_table () const
+ { return reinterpret_cast (base); }
+
+public:
+ const void *base;
+ hb_paint_funcs_t *funcs;
+ void *data;
+ hb_font_t *font;
+ hb_array_t palette;
+ hb_color_t foreground;
+ ItemVarStoreInstancer &instancer;
+ hb_decycler_t glyphs_decycler;
+ hb_decycler_t layers_decycler;
+ int depth_left = HB_MAX_NESTING_LEVEL;
+ int edge_count = HB_MAX_GRAPH_EDGE_COUNT;
+
+ hb_paint_context_t (const void *base_,
+ hb_paint_funcs_t *funcs_,
+ void *data_,
+ hb_font_t *font_,
+ unsigned int palette_,
+ hb_color_t foreground_,
+ ItemVarStoreInstancer &instancer_) :
+ base (base_),
+ funcs (funcs_),
+ data (data_),
+ font (font_),
+ palette (
+#ifndef HB_NO_COLOR
+ // https://github.com/harfbuzz/harfbuzz/issues/5116
+ font->face->table.CPAL->get_palette_colors (palette_ < font->face->table.CPAL->get_palette_count () ? palette_ : 0)
+#endif
+ ),
+ foreground (foreground_),
+ instancer (instancer_)
+ {
+ if (font->is_synthetic)
+ {
+ font = hb_font_create_sub_font (font);
+ hb_font_set_synthetic_bold (font, 0, 0, true);
+ hb_font_set_synthetic_slant (font, 0);
+ }
+ else
+ hb_font_reference (font);
+ }
+
+ ~hb_paint_context_t ()
+ {
+ hb_font_destroy (font);
+ }
+
+ static unsigned
+ color_alpha (hb_color_t color, float alpha)
+ {
+ return roundf (hb_color_get_alpha (color) * alpha);
+ }
+
+ hb_color_t get_color (unsigned int color_index, float alpha, hb_bool_t *is_foreground)
+ {
+ hb_color_t color = foreground;
+
+ *is_foreground = true;
+
+ if (color_index != 0xffff)
+ {
+ if (!funcs->custom_palette_color (data, color_index, &color))
+ color = palette[color_index];
+
+ *is_foreground = false;
+ }
+
+ return HB_COLOR (hb_color_get_blue (color),
+ hb_color_get_green (color),
+ hb_color_get_red (color),
+ color_alpha (color, alpha));
+ }
+
+ inline void recurse (const Paint &paint);
+};
+
+struct hb_colrv1_closure_context_t :
+ hb_dispatch_context_t
+{
+ template
+ return_t dispatch (const T &obj)
+ {
+ if (unlikely (nesting_level_left == 0))
+ return hb_empty_t ();
+
+ if (paint_visited (&obj))
+ return hb_empty_t ();
+
+ nesting_level_left--;
+ obj.closurev1 (this);
+ nesting_level_left++;
+ return hb_empty_t ();
+ }
+ static return_t default_return_value () { return hb_empty_t (); }
+
+ bool paint_visited (const void *paint)
+ {
+ hb_codepoint_t delta = (hb_codepoint_t) ((uintptr_t) paint - (uintptr_t) base);
+ if (visited_paint.in_error() || visited_paint.has (delta))
+ return true;
+
+ visited_paint.add (delta);
+ return false;
+ }
+
+ const COLR* get_colr_table () const
+ { return reinterpret_cast (base); }
+
+ void add_glyph (unsigned glyph_id)
+ { glyphs->add (glyph_id); }
+
+ void add_layer_indices (unsigned first_layer_index, unsigned num_of_layers)
+ {
+ if (num_of_layers == 0) return;
+ layer_indices->add_range (first_layer_index, first_layer_index + num_of_layers - 1);
+ }
+
+ void add_palette_index (unsigned palette_index)
+ { palette_indices->add (palette_index); }
+
+ void add_var_idxes (unsigned first_var_idx, unsigned num_idxes)
+ {
+ if (!num_idxes || first_var_idx == VarIdx::NO_VARIATION) return;
+ variation_indices->add_range (first_var_idx, first_var_idx + num_idxes - 1);
+ }
+
+ public:
+ const void *base;
+ hb_set_t visited_paint;
+ hb_set_t *glyphs;
+ hb_set_t *layer_indices;
+ hb_set_t *palette_indices;
+ hb_set_t *variation_indices;
+ unsigned num_var_idxes;
+ unsigned nesting_level_left;
+
+ hb_colrv1_closure_context_t (const void *base_,
+ hb_set_t *glyphs_,
+ hb_set_t *layer_indices_,
+ hb_set_t *palette_indices_,
+ hb_set_t *variation_indices_,
+ unsigned num_var_idxes_ = 1,
+ unsigned nesting_level_left_ = HB_MAX_NESTING_LEVEL) :
+ base (base_),
+ glyphs (glyphs_),
+ layer_indices (layer_indices_),
+ palette_indices (palette_indices_),
+ variation_indices (variation_indices_),
+ num_var_idxes (num_var_idxes_),
+ nesting_level_left (nesting_level_left_)
+ {}
+};
+
+struct LayerRecord
+{
+ operator hb_ot_color_layer_t () const { return {glyphId, colorIdx}; }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this));
+ }
+
+ public:
+ HBGlyphID16 glyphId; /* Glyph ID of layer glyph */
+ Index colorIdx; /* Index value to use with a
+ * selected color palette.
+ * An index value of 0xFFFF
+ * is a special case indicating
+ * that the text foreground
+ * color (defined by a
+ * higher-level client) should
+ * be used and shall not be
+ * treated as actual index
+ * into CPAL ColorRecord array. */
+ public:
+ DEFINE_SIZE_STATIC (4);
+};
+
+struct BaseGlyphRecord
+{
+ int cmp (hb_codepoint_t g) const
+ { return g < glyphId ? -1 : g > glyphId ? 1 : 0; }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this));
+ }
+
+ public:
+ HBGlyphID16 glyphId; /* Glyph ID of reference glyph */
+ HBUINT16 firstLayerIdx; /* Index (from beginning of
+ * the Layer Records) to the
+ * layer record. There will be
+ * numLayers consecutive entries
+ * for this base glyph. */
+ HBUINT16 numLayers; /* Number of color layers
+ * associated with this glyph */
+ public:
+ DEFINE_SIZE_STATIC (6);
+};
+
+template
+struct Variable
+{
+ static constexpr bool is_variable = true;
+
+ Variable* copy (hb_serialize_context_t *c) const
+ {
+ TRACE_SERIALIZE (this);
+ return_trace (c->embed (this));
+ }
+
+ void closurev1 (hb_colrv1_closure_context_t* c) const
+ {
+ c->num_var_idxes = 0;
+ // update c->num_var_idxes during value closure
+ value.closurev1 (c);
+ c->add_var_idxes (varIdxBase, c->num_var_idxes);
+ }
+
+ bool subset (hb_subset_context_t *c,
+ const ItemVarStoreInstancer &instancer) const
+ {
+ TRACE_SUBSET (this);
+ if (!value.subset (c, instancer, varIdxBase)) return_trace (false);
+ if (c->plan->all_axes_pinned)
+ return_trace (true);
+
+ VarIdx new_varidx;
+ new_varidx = varIdxBase;
+ if (varIdxBase != VarIdx::NO_VARIATION)
+ {
+ hb_pair_t *new_varidx_delta;
+ if (!c->plan->colrv1_variation_idx_delta_map.has (varIdxBase, &new_varidx_delta))
+ return_trace (false);
+
+ new_varidx = hb_first (*new_varidx_delta);
+ }
+
+ return_trace (c->serializer->embed (new_varidx));
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this) && value.sanitize (c));
+ }
+
+ void paint_glyph (hb_paint_context_t *c) const
+ {
+ TRACE_PAINT (this);
+ value.paint_glyph (c, varIdxBase);
+ }
+
+ void get_color_stop (hb_paint_context_t *c,
+ hb_color_stop_t *stop,
+ const ItemVarStoreInstancer &instancer) const
+ {
+ value.get_color_stop (c, stop, varIdxBase, instancer);
+ }
+
+ hb_paint_extend_t get_extend () const
+ {
+ return value.get_extend ();
+ }
+
+ protected:
+ T value;
+ public:
+ VarIdx varIdxBase;
+ public:
+ DEFINE_SIZE_MIN (VarIdx::static_size + T::min_size);
+};
+
+template
+struct NoVariable
+{
+ static constexpr bool is_variable = false;
+
+ static constexpr uint32_t varIdxBase = VarIdx::NO_VARIATION;
+
+ NoVariable* copy (hb_serialize_context_t *c) const
+ {
+ TRACE_SERIALIZE (this);
+ return_trace (c->embed (this));
+ }
+
+ void closurev1 (hb_colrv1_closure_context_t* c) const
+ { value.closurev1 (c); }
+
+ bool subset (hb_subset_context_t *c,
+ const ItemVarStoreInstancer &instancer) const
+ {
+ TRACE_SUBSET (this);
+ return_trace (value.subset (c, instancer, varIdxBase));
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this) && value.sanitize (c));
+ }
+
+ void paint_glyph (hb_paint_context_t *c) const
+ {
+ TRACE_PAINT (this);
+ value.paint_glyph (c, varIdxBase);
+ }
+
+ void get_color_stop (hb_paint_context_t *c,
+ hb_color_stop_t *stop,
+ const ItemVarStoreInstancer &instancer) const
+ {
+ value.get_color_stop (c, stop, VarIdx::NO_VARIATION, instancer);
+ }
+
+ hb_paint_extend_t get_extend () const
+ {
+ return value.get_extend ();
+ }
+
+ T value;
+ public:
+ DEFINE_SIZE_MIN (T::min_size);
+};
+
+// Color structures
+
+struct ColorStop
+{
+ void closurev1 (hb_colrv1_closure_context_t* c) const
+ {
+ c->add_palette_index (paletteIndex);
+ c->num_var_idxes = 2;
+ }
+
+ bool subset (hb_subset_context_t *c,
+ const ItemVarStoreInstancer &instancer,
+ uint32_t varIdxBase) const
+ {
+ TRACE_SUBSET (this);
+ auto *out = c->serializer->embed (*this);
+ if (unlikely (!out)) return_trace (false);
+
+ if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
+ {
+ out->stopOffset.set_float (stopOffset.to_float(instancer (varIdxBase, 0)));
+ out->alpha.set_float (alpha.to_float (instancer (varIdxBase, 1)));
+ }
+
+ return_trace (c->serializer->check_assign (out->paletteIndex, c->plan->colr_palettes.get (paletteIndex),
+ HB_SERIALIZE_ERROR_INT_OVERFLOW));
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this));
+ }
+
+ void get_color_stop (hb_paint_context_t *c,
+ hb_color_stop_t *out,
+ uint32_t varIdx,
+ const ItemVarStoreInstancer &instancer) const
+ {
+ out->offset = stopOffset.to_float(instancer (varIdx, 0));
+ out->color = c->get_color (paletteIndex,
+ alpha.to_float (instancer (varIdx, 1)),
+ &out->is_foreground);
+ }
+
+ F2DOT14 stopOffset;
+ HBUINT16 paletteIndex;
+ F2DOT14 alpha;
+ public:
+ DEFINE_SIZE_STATIC (2 + 2 * F2DOT14::static_size);
+};
+
+struct Extend : HBUINT8
+{
+ enum {
+ EXTEND_PAD = 0,
+ EXTEND_REPEAT = 1,
+ EXTEND_REFLECT = 2,
+ };
+ public:
+ DEFINE_SIZE_STATIC (1);
+};
+
+template class Var>
+struct ColorLine
+{
+ void closurev1 (hb_colrv1_closure_context_t* c) const
+ {
+ for (const auto &stop : stops.iter ())
+ stop.closurev1 (c);
+ }
+
+ bool subset (hb_subset_context_t *c,
+ const ItemVarStoreInstancer &instancer) const
+ {
+ TRACE_SUBSET (this);
+ auto *out = c->serializer->start_embed (this);
+ if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
+
+ if (!c->serializer->check_assign (out->extend, extend, HB_SERIALIZE_ERROR_INT_OVERFLOW)) return_trace (false);
+ if (!c->serializer->check_assign (out->stops.len, stops.len, HB_SERIALIZE_ERROR_ARRAY_OVERFLOW)) return_trace (false);
+
+ for (const auto& stop : stops.iter ())
+ {
+ if (!stop.subset (c, instancer)) return_trace (false);
+ }
+ return_trace (true);
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this) &&
+ stops.sanitize (c));
+ }
+
+ /* get up to count stops from start */
+ unsigned int
+ get_color_stops (hb_paint_context_t *c,
+ unsigned int start,
+ unsigned int *count,
+ hb_color_stop_t *color_stops,
+ const ItemVarStoreInstancer &instancer) const
+ {
+ unsigned int len = stops.len;
+
+ if (count && color_stops)
+ {
+ unsigned int i;
+ for (i = 0; i < *count && start + i < len; i++)
+ stops[start + i].get_color_stop (c, &color_stops[i], instancer);
+ *count = i;
+ }
+
+ return len;
+ }
+
+ HB_INTERNAL static unsigned int static_get_color_stops (hb_color_line_t *color_line,
+ void *color_line_data,
+ unsigned int start,
+ unsigned int *count,
+ hb_color_stop_t *color_stops,
+ void *user_data)
+ {
+ const ColorLine *thiz = (const ColorLine *) color_line_data;
+ hb_paint_context_t *c = (hb_paint_context_t *) user_data;
+ return thiz->get_color_stops (c, start, count, color_stops, c->instancer);
+ }
+
+ hb_paint_extend_t get_extend () const
+ {
+ switch ((unsigned int) extend)
+ {
+ case Extend::EXTEND_REPEAT: return HB_PAINT_EXTEND_REPEAT;
+ case Extend::EXTEND_REFLECT: return HB_PAINT_EXTEND_REFLECT;
+ case Extend::EXTEND_PAD:
+ default: return HB_PAINT_EXTEND_PAD;
+ }
+ }
+
+ HB_INTERNAL static hb_paint_extend_t static_get_extend (hb_color_line_t *color_line,
+ void *color_line_data,
+ void *user_data)
+ {
+ const ColorLine *thiz = (const ColorLine *) color_line_data;
+ return thiz->get_extend ();
+ }
+
+ Extend extend;
+ Array16Of> stops;
+ public:
+ DEFINE_SIZE_ARRAY_SIZED (3, stops);
+};
+
+// Composition modes
+
+// Compositing modes are taken from https://www.w3.org/TR/compositing-1/
+// NOTE: a brief audit of major implementations suggests most support most
+// or all of the specified modes.
+struct CompositeMode : HBUINT8
+{
+ enum {
+ // Porter-Duff modes
+ // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators
+ COMPOSITE_CLEAR = 0, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_clear
+ COMPOSITE_SRC = 1, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_src
+ COMPOSITE_DEST = 2, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_dst
+ COMPOSITE_SRC_OVER = 3, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_srcover
+ COMPOSITE_DEST_OVER = 4, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_dstover
+ COMPOSITE_SRC_IN = 5, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_srcin
+ COMPOSITE_DEST_IN = 6, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_dstin
+ COMPOSITE_SRC_OUT = 7, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_srcout
+ COMPOSITE_DEST_OUT = 8, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_dstout
+ COMPOSITE_SRC_ATOP = 9, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_srcatop
+ COMPOSITE_DEST_ATOP = 10, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_dstatop
+ COMPOSITE_XOR = 11, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_xor
+ COMPOSITE_PLUS = 12, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_plus
+
+ // Blend modes
+ // https://www.w3.org/TR/compositing-1/#blending
+ COMPOSITE_SCREEN = 13, // https://www.w3.org/TR/compositing-1/#blendingscreen
+ COMPOSITE_OVERLAY = 14, // https://www.w3.org/TR/compositing-1/#blendingoverlay
+ COMPOSITE_DARKEN = 15, // https://www.w3.org/TR/compositing-1/#blendingdarken
+ COMPOSITE_LIGHTEN = 16, // https://www.w3.org/TR/compositing-1/#blendinglighten
+ COMPOSITE_COLOR_DODGE = 17, // https://www.w3.org/TR/compositing-1/#blendingcolordodge
+ COMPOSITE_COLOR_BURN = 18, // https://www.w3.org/TR/compositing-1/#blendingcolorburn
+ COMPOSITE_HARD_LIGHT = 19, // https://www.w3.org/TR/compositing-1/#blendinghardlight
+ COMPOSITE_SOFT_LIGHT = 20, // https://www.w3.org/TR/compositing-1/#blendingsoftlight
+ COMPOSITE_DIFFERENCE = 21, // https://www.w3.org/TR/compositing-1/#blendingdifference
+ COMPOSITE_EXCLUSION = 22, // https://www.w3.org/TR/compositing-1/#blendingexclusion
+ COMPOSITE_MULTIPLY = 23, // https://www.w3.org/TR/compositing-1/#blendingmultiply
+
+ // Modes that, uniquely, do not operate on components
+ // https://www.w3.org/TR/compositing-1/#blendingnonseparable
+ COMPOSITE_HSL_HUE = 24, // https://www.w3.org/TR/compositing-1/#blendinghue
+ COMPOSITE_HSL_SATURATION = 25, // https://www.w3.org/TR/compositing-1/#blendingsaturation
+ COMPOSITE_HSL_COLOR = 26, // https://www.w3.org/TR/compositing-1/#blendingcolor
+ COMPOSITE_HSL_LUMINOSITY = 27, // https://www.w3.org/TR/compositing-1/#blendingluminosity
+ };
+ public:
+ DEFINE_SIZE_STATIC (1);
+};
+
+struct Affine2x3
+{
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this));
+ }
+
+ void closurev1 (hb_colrv1_closure_context_t* c) const
+ { c->num_var_idxes = 6; }
+
+ bool subset (hb_subset_context_t *c,
+ const ItemVarStoreInstancer &instancer,
+ uint32_t varIdxBase) const
+ {
+ TRACE_SUBSET (this);
+ auto *out = c->serializer->embed (*this);
+ if (unlikely (!out)) return_trace (false);
+ if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
+ {
+ out->xx.set_float (xx.to_float(instancer (varIdxBase, 0)));
+ out->yx.set_float (yx.to_float(instancer (varIdxBase, 1)));
+ out->xy.set_float (xy.to_float(instancer (varIdxBase, 2)));
+ out->yy.set_float (yy.to_float(instancer (varIdxBase, 3)));
+ out->dx.set_float (dx.to_float(instancer (varIdxBase, 4)));
+ out->dy.set_float (dy.to_float(instancer (varIdxBase, 5)));
+ }
+ return_trace (true);
+ }
+
+ void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
+ {
+ TRACE_PAINT (this);
+ c->funcs->push_transform (c->data,
+ xx.to_float (c->instancer (varIdxBase, 0)),
+ yx.to_float (c->instancer (varIdxBase, 1)),
+ xy.to_float (c->instancer (varIdxBase, 2)),
+ yy.to_float (c->instancer (varIdxBase, 3)),
+ dx.to_float (c->instancer (varIdxBase, 4)),
+ dy.to_float (c->instancer (varIdxBase, 5)));
+ }
+
+ F16DOT16 xx;
+ F16DOT16 yx;
+ F16DOT16 xy;
+ F16DOT16 yy;
+ F16DOT16 dx;
+ F16DOT16 dy;
+ public:
+ DEFINE_SIZE_STATIC (6 * F16DOT16::static_size);
+};
+
+struct PaintColrLayers
+{
+ void closurev1 (hb_colrv1_closure_context_t* c) const;
+
+ bool subset (hb_subset_context_t *c,
+ const ItemVarStoreInstancer &instancer HB_UNUSED) const
+ {
+ TRACE_SUBSET (this);
+ auto *out = c->serializer->embed (this);
+ if (unlikely (!out)) return_trace (false);
+
+ uint32_t first_layer_index = numLayers ? c->plan->colrv1_layers.get (firstLayerIndex) : 0;
+ return_trace (c->serializer->check_assign (out->firstLayerIndex, first_layer_index,
+ HB_SERIALIZE_ERROR_INT_OVERFLOW));
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this));
+ }
+
+ inline void paint_glyph (hb_paint_context_t *c) const;
+
+ HBUINT8 format; /* format = 1 */
+ HBUINT8 numLayers;
+ HBUINT32 firstLayerIndex; /* index into COLRv1::layerList */
+ public:
+ DEFINE_SIZE_STATIC (6);
+};
+
+struct PaintSolid
+{
+ void closurev1 (hb_colrv1_closure_context_t* c) const
+ {
+ c->add_palette_index (paletteIndex);
+ c->num_var_idxes = 1;
+ }
+
+ bool subset (hb_subset_context_t *c,
+ const ItemVarStoreInstancer &instancer,
+ uint32_t varIdxBase) const
+ {
+ TRACE_SUBSET (this);
+ auto *out = c->serializer->embed (*this);
+ if (unlikely (!out)) return_trace (false);
+
+ if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
+ out->alpha.set_float (alpha.to_float (instancer (varIdxBase, 0)));
+
+ if (format == 3 && c->plan->all_axes_pinned)
+ out->format = 2;
+
+ return_trace (c->serializer->check_assign (out->paletteIndex, c->plan->colr_palettes.get (paletteIndex),
+ HB_SERIALIZE_ERROR_INT_OVERFLOW));
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this));
+ }
+
+ void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
+ {
+ TRACE_PAINT (this);
+ hb_bool_t is_foreground;
+ hb_color_t color;
+
+ color = c->get_color (paletteIndex,
+ alpha.to_float (c->instancer (varIdxBase, 0)),
+ &is_foreground);
+ c->funcs->color (c->data, is_foreground, color);
+ }
+
+ HBUINT8 format; /* format = 2(noVar) or 3(Var)*/
+ HBUINT16 paletteIndex;
+ F2DOT14 alpha;
+ public:
+ DEFINE_SIZE_STATIC (3 + F2DOT14::static_size);
+};
+
+template class Var>
+struct PaintLinearGradient
+{
+ void closurev1 (hb_colrv1_closure_context_t* c) const
+ {
+ (this+colorLine).closurev1 (c);
+ c->num_var_idxes = 6;
+ }
+
+ bool subset (hb_subset_context_t *c,
+ const ItemVarStoreInstancer &instancer,
+ uint32_t varIdxBase) const
+ {
+ TRACE_SUBSET (this);
+ auto *out = c->serializer->embed (this);
+ if (unlikely (!out)) return_trace (false);
+
+ if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
+ {
+ out->x0 = x0 + (int) roundf (instancer (varIdxBase, 0));
+ out->y0 = y0 + (int) roundf (instancer (varIdxBase, 1));
+ out->x1 = x1 + (int) roundf (instancer (varIdxBase, 2));
+ out->y1 = y1 + (int) roundf (instancer (varIdxBase, 3));
+ out->x2 = x2 + (int) roundf (instancer (varIdxBase, 4));
+ out->y2 = y2 + (int) roundf (instancer (varIdxBase, 5));
+ }
+
+ if (format == 5 && c->plan->all_axes_pinned)
+ out->format = 4;
+
+ return_trace (out->colorLine.serialize_subset (c, colorLine, this, instancer));
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this) && colorLine.sanitize (c, this));
+ }
+
+ void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
+ {
+ TRACE_PAINT (this);
+ hb_color_line_t cl = {
+ (void *) &(this+colorLine),
+ (this+colorLine).static_get_color_stops, c,
+ (this+colorLine).static_get_extend, nullptr
+ };
+
+ c->funcs->linear_gradient (c->data, &cl,
+ x0 + c->instancer (varIdxBase, 0),
+ y0 + c->instancer (varIdxBase, 1),
+ x1 + c->instancer (varIdxBase, 2),
+ y1 + c->instancer (varIdxBase, 3),
+ x2 + c->instancer (varIdxBase, 4),
+ y2 + c->instancer (varIdxBase, 5));
+ }
+
+ HBUINT8 format; /* format = 4(noVar) or 5 (Var) */
+ Offset24To> colorLine; /* Offset (from beginning of PaintLinearGradient
+ * table) to ColorLine subtable. */
+ FWORD x0;
+ FWORD y0;
+ FWORD x1;
+ FWORD y1;
+ FWORD x2;
+ FWORD y2;
+ public:
+ DEFINE_SIZE_STATIC (4 + 6 * FWORD::static_size);
+};
+
+template class Var>
+struct PaintRadialGradient
+{
+ void closurev1 (hb_colrv1_closure_context_t* c) const
+ {
+ (this+colorLine).closurev1 (c);
+ c->num_var_idxes = 6;
+ }
+
+ bool subset (hb_subset_context_t *c,
+ const ItemVarStoreInstancer &instancer,
+ uint32_t varIdxBase) const
+ {
+ TRACE_SUBSET (this);
+ auto *out = c->serializer->embed (this);
+ if (unlikely (!out)) return_trace (false);
+
+ if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
+ {
+ out->x0 = x0 + (int) roundf (instancer (varIdxBase, 0));
+ out->y0 = y0 + (int) roundf (instancer (varIdxBase, 1));
+ out->radius0 = radius0 + (unsigned) roundf (instancer (varIdxBase, 2));
+ out->x1 = x1 + (int) roundf (instancer (varIdxBase, 3));
+ out->y1 = y1 + (int) roundf (instancer (varIdxBase, 4));
+ out->radius1 = radius1 + (unsigned) roundf (instancer (varIdxBase, 5));
+ }
+
+ if (format == 7 && c->plan->all_axes_pinned)
+ out->format = 6;
+
+ return_trace (out->colorLine.serialize_subset (c, colorLine, this, instancer));
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this) && colorLine.sanitize (c, this));
+ }
+
+ void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
+ {
+ TRACE_PAINT (this);
+ hb_color_line_t cl = {
+ (void *) &(this+colorLine),
+ (this+colorLine).static_get_color_stops, c,
+ (this+colorLine).static_get_extend, nullptr
+ };
+
+ c->funcs->radial_gradient (c->data, &cl,
+ x0 + c->instancer (varIdxBase, 0),
+ y0 + c->instancer (varIdxBase, 1),
+ radius0 + c->instancer (varIdxBase, 2),
+ x1 + c->instancer (varIdxBase, 3),
+ y1 + c->instancer (varIdxBase, 4),
+ radius1 + c->instancer (varIdxBase, 5));
+ }
+
+ HBUINT8 format; /* format = 6(noVar) or 7 (Var) */
+ Offset24To> colorLine; /* Offset (from beginning of PaintRadialGradient
+ * table) to ColorLine subtable. */
+ FWORD x0;
+ FWORD y0;
+ UFWORD radius0;
+ FWORD x1;
+ FWORD y1;
+ UFWORD radius1;
+ public:
+ DEFINE_SIZE_STATIC (4 + 6 * FWORD::static_size);
+};
+
+template class Var>
+struct PaintSweepGradient
+{
+ void closurev1 (hb_colrv1_closure_context_t* c) const
+ {
+ (this+colorLine).closurev1 (c);
+ c->num_var_idxes = 4;
+ }
+
+ bool subset (hb_subset_context_t *c,
+ const ItemVarStoreInstancer &instancer,
+ uint32_t varIdxBase) const
+ {
+ TRACE_SUBSET (this);
+ auto *out = c->serializer->embed (this);
+ if (unlikely (!out)) return_trace (false);
+
+ if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
+ {
+ out->centerX = centerX + (int) roundf (instancer (varIdxBase, 0));
+ out->centerY = centerY + (int) roundf (instancer (varIdxBase, 1));
+ out->startAngle.set_float (startAngle.to_float (instancer (varIdxBase, 2)));
+ out->endAngle.set_float (endAngle.to_float (instancer (varIdxBase, 3)));
+ }
+
+ if (format == 9 && c->plan->all_axes_pinned)
+ out->format = 8;
+
+ return_trace (out->colorLine.serialize_subset (c, colorLine, this, instancer));
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this) && colorLine.sanitize (c, this));
+ }
+
+ void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
+ {
+ TRACE_PAINT (this);
+ hb_color_line_t cl = {
+ (void *) &(this+colorLine),
+ (this+colorLine).static_get_color_stops, c,
+ (this+colorLine).static_get_extend, nullptr
+ };
+
+ c->funcs->sweep_gradient (c->data, &cl,
+ centerX + c->instancer (varIdxBase, 0),
+ centerY + c->instancer (varIdxBase, 1),
+ (startAngle.to_float (c->instancer (varIdxBase, 2)) + 1) * HB_PI,
+ (endAngle.to_float (c->instancer (varIdxBase, 3)) + 1) * HB_PI);
+ }
+
+ HBUINT8 format; /* format = 8(noVar) or 9 (Var) */
+ Offset24To> colorLine; /* Offset (from beginning of PaintSweepGradient
+ * table) to ColorLine subtable. */
+ FWORD centerX;
+ FWORD centerY;
+ F2DOT14 startAngle;
+ F2DOT14 endAngle;
+ public:
+ DEFINE_SIZE_STATIC (4 + 2 * FWORD::static_size + 2 * F2DOT14::static_size);
+};
+
+// Paint a non-COLR glyph, filled as indicated by paint.
+struct PaintGlyph
+{
+ void closurev1 (hb_colrv1_closure_context_t* c) const;
+
+ bool subset (hb_subset_context_t *c,
+ const ItemVarStoreInstancer &instancer) const
+ {
+ TRACE_SUBSET (this);
+ auto *out = c->serializer->embed (this);
+ if (unlikely (!out)) return_trace (false);
+
+ if (! c->serializer->check_assign (out->gid, c->plan->glyph_map->get (gid),
+ HB_SERIALIZE_ERROR_INT_OVERFLOW))
+ return_trace (false);
+
+ return_trace (out->paint.serialize_subset (c, paint, this, instancer));
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this) && paint.sanitize (c, this));
+ }
+
+ void paint_glyph (hb_paint_context_t *c) const
+ {
+ TRACE_PAINT (this);
+ c->funcs->push_inverse_font_transform (c->data, c->font);
+ c->funcs->push_clip_glyph (c->data, gid, c->font);
+ c->funcs->push_font_transform (c->data, c->font);
+ c->recurse (this+paint);
+ c->funcs->pop_transform (c->data);
+ c->funcs->pop_clip (c->data);
+ c->funcs->pop_transform (c->data);
+ }
+
+ HBUINT8 format; /* format = 10 */
+ Offset24To paint; /* Offset (from beginning of PaintGlyph table) to Paint subtable. */
+ HBUINT16 gid;
+ public:
+ DEFINE_SIZE_STATIC (6);
+};
+
+struct PaintColrGlyph
+{
+ void closurev1 (hb_colrv1_closure_context_t* c) const;
+
+ bool subset (hb_subset_context_t *c,
+ const ItemVarStoreInstancer &instancer HB_UNUSED) const
+ {
+ TRACE_SUBSET (this);
+ auto *out = c->serializer->embed (this);
+ if (unlikely (!out)) return_trace (false);
+
+ return_trace (c->serializer->check_assign (out->gid, c->plan->glyph_map->get (gid),
+ HB_SERIALIZE_ERROR_INT_OVERFLOW));
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this));
+ }
+
+ inline void paint_glyph (hb_paint_context_t *c) const;
+
+ HBUINT8 format; /* format = 11 */
+ HBUINT16 gid;
+ public:
+ DEFINE_SIZE_STATIC (3);
+};
+
+template class Var>
+struct PaintTransform
+{
+ HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
+
+ bool subset (hb_subset_context_t *c,
+ const ItemVarStoreInstancer &instancer) const
+ {
+ TRACE_SUBSET (this);
+ auto *out = c->serializer->embed (this);
+ if (unlikely (!out)) return_trace (false);
+ if (!out->transform.serialize_subset (c, transform, this, instancer)) return_trace (false);
+ if (format == 13 && c->plan->all_axes_pinned)
+ out->format = 12;
+ return_trace (out->src.serialize_subset (c, src, this, instancer));
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this) &&
+ src.sanitize (c, this) &&
+ transform.sanitize (c, this));
+ }
+
+ void paint_glyph (hb_paint_context_t *c) const
+ {
+ TRACE_PAINT (this);
+ (this+transform).paint_glyph (c); // This does a push_transform()
+ c->recurse (this+src);
+ c->funcs->pop_transform (c->data);
+ }
+
+ HBUINT8 format; /* format = 12(noVar) or 13 (Var) */
+ Offset24To src; /* Offset (from beginning of PaintTransform table) to Paint subtable. */
+ Offset24To> transform;
+ public:
+ DEFINE_SIZE_STATIC (7);
+};
+
+struct PaintTranslate
+{
+ HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
+
+ bool subset (hb_subset_context_t *c,
+ const ItemVarStoreInstancer &instancer,
+ uint32_t varIdxBase) const
+ {
+ TRACE_SUBSET (this);
+ auto *out = c->serializer->embed (this);
+ if (unlikely (!out)) return_trace (false);
+
+ if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
+ {
+ out->dx = dx + (int) roundf (instancer (varIdxBase, 0));
+ out->dy = dy + (int) roundf (instancer (varIdxBase, 1));
+ }
+
+ if (format == 15 && c->plan->all_axes_pinned)
+ out->format = 14;
+
+ return_trace (out->src.serialize_subset (c, src, this, instancer));
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this) && src.sanitize (c, this));
+ }
+
+ void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
+ {
+ TRACE_PAINT (this);
+ float ddx = dx + c->instancer (varIdxBase, 0);
+ float ddy = dy + c->instancer (varIdxBase, 1);
+
+ c->funcs->push_translate (c->data, ddx, ddy);
+ c->recurse (this+src);
+ c->funcs->pop_transform (c->data);
+ }
+
+ HBUINT8 format; /* format = 14(noVar) or 15 (Var) */
+ Offset24To src; /* Offset (from beginning of PaintTranslate table) to Paint subtable. */
+ FWORD dx;
+ FWORD dy;
+ public:
+ DEFINE_SIZE_STATIC (4 + 2 * FWORD::static_size);
+};
+
+struct PaintScale
+{
+ HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
+
+ bool subset (hb_subset_context_t *c,
+ const ItemVarStoreInstancer &instancer,
+ uint32_t varIdxBase) const
+ {
+ TRACE_SUBSET (this);
+ auto *out = c->serializer->embed (this);
+ if (unlikely (!out)) return_trace (false);
+
+ if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
+ {
+ out->scaleX.set_float (scaleX.to_float (instancer (varIdxBase, 0)));
+ out->scaleY.set_float (scaleY.to_float (instancer (varIdxBase, 1)));
+ }
+
+ if (format == 17 && c->plan->all_axes_pinned)
+ out->format = 16;
+
+ return_trace (out->src.serialize_subset (c, src, this, instancer));
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this) && src.sanitize (c, this));
+ }
+
+ void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
+ {
+ TRACE_PAINT (this);
+ float sx = scaleX.to_float (c->instancer (varIdxBase, 0));
+ float sy = scaleY.to_float (c->instancer (varIdxBase, 1));
+
+ c->funcs->push_scale (c->data, sx, sy);
+ c->recurse (this+src);
+ c->funcs->pop_transform (c->data);
+ }
+
+ HBUINT8 format; /* format = 16 (noVar) or 17(Var) */
+ Offset24To src; /* Offset (from beginning of PaintScale table) to Paint subtable. */
+ F2DOT14 scaleX;
+ F2DOT14 scaleY;
+ public:
+ DEFINE_SIZE_STATIC (4 + 2 * F2DOT14::static_size);
+};
+
+struct PaintScaleAroundCenter
+{
+ HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
+
+ bool subset (hb_subset_context_t *c,
+ const ItemVarStoreInstancer &instancer,
+ uint32_t varIdxBase) const
+ {
+ TRACE_SUBSET (this);
+ auto *out = c->serializer->embed (this);
+ if (unlikely (!out)) return_trace (false);
+
+ if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
+ {
+ out->scaleX.set_float (scaleX.to_float (instancer (varIdxBase, 0)));
+ out->scaleY.set_float (scaleY.to_float (instancer (varIdxBase, 1)));
+ out->centerX = centerX + (int) roundf (instancer (varIdxBase, 2));
+ out->centerY = centerY + (int) roundf (instancer (varIdxBase, 3));
+ }
+
+ if (format == 19 && c->plan->all_axes_pinned)
+ out->format = 18;
+
+ return_trace (out->src.serialize_subset (c, src, this, instancer));
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this) && src.sanitize (c, this));
+ }
+
+ void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
+ {
+ TRACE_PAINT (this);
+ float sx = scaleX.to_float (c->instancer (varIdxBase, 0));
+ float sy = scaleY.to_float (c->instancer (varIdxBase, 1));
+ float tCenterX = centerX + c->instancer (varIdxBase, 2);
+ float tCenterY = centerY + c->instancer (varIdxBase, 3);
+
+ c->funcs->push_scale_around_center (c->data, sx, sy, tCenterX, tCenterY);
+ c->recurse (this+src);
+ c->funcs->pop_transform (c->data);
+ }
+
+ HBUINT8 format; /* format = 18 (noVar) or 19(Var) */
+ Offset24To src; /* Offset (from beginning of PaintScaleAroundCenter table) to Paint subtable. */
+ F2DOT14 scaleX;
+ F2DOT14 scaleY;
+ FWORD centerX;
+ FWORD centerY;
+ public:
+ DEFINE_SIZE_STATIC (4 + 2 * F2DOT14::static_size + 2 * FWORD::static_size);
+};
+
+struct PaintScaleUniform
+{
+ HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
+
+ bool subset (hb_subset_context_t *c,
+ const ItemVarStoreInstancer &instancer,
+ uint32_t varIdxBase) const
+ {
+ TRACE_SUBSET (this);
+ auto *out = c->serializer->embed (this);
+ if (unlikely (!out)) return_trace (false);
+
+ if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
+ out->scale.set_float (scale.to_float (instancer (varIdxBase, 0)));
+
+ if (format == 21 && c->plan->all_axes_pinned)
+ out->format = 20;
+
+ return_trace (out->src.serialize_subset (c, src, this, instancer));
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this) && src.sanitize (c, this));
+ }
+
+ void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
+ {
+ TRACE_PAINT (this);
+ float s = scale.to_float (c->instancer (varIdxBase, 0));
+
+ c->funcs->push_scale (c->data, s, s);
+ c->recurse (this+src);
+ c->funcs->pop_transform (c->data);
+ }
+
+ HBUINT8 format; /* format = 20 (noVar) or 21(Var) */
+ Offset24To src; /* Offset (from beginning of PaintScaleUniform table) to Paint subtable. */
+ F2DOT14 scale;
+ public:
+ DEFINE_SIZE_STATIC (4 + F2DOT14::static_size);
+};
+
+struct PaintScaleUniformAroundCenter
+{
+ HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
+
+ bool subset (hb_subset_context_t *c,
+ const ItemVarStoreInstancer &instancer,
+ uint32_t varIdxBase) const
+ {
+ TRACE_SUBSET (this);
+ auto *out = c->serializer->embed (this);
+ if (unlikely (!out)) return_trace (false);
+
+ if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
+ {
+ out->scale.set_float (scale.to_float (instancer (varIdxBase, 0)));
+ out->centerX = centerX + (int) roundf (instancer (varIdxBase, 1));
+ out->centerY = centerY + (int) roundf (instancer (varIdxBase, 2));
+ }
+
+ if (format == 23 && c->plan->all_axes_pinned)
+ out->format = 22;
+
+ return_trace (out->src.serialize_subset (c, src, this, instancer));
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this) && src.sanitize (c, this));
+ }
+
+ void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
+ {
+ TRACE_PAINT (this);
+ float s = scale.to_float (c->instancer (varIdxBase, 0));
+ float tCenterX = centerX + c->instancer (varIdxBase, 1);
+ float tCenterY = centerY + c->instancer (varIdxBase, 2);
+
+ c->funcs->push_scale_around_center (c->data, s, s, tCenterX, tCenterY);
+ c->recurse (this+src);
+ c->funcs->pop_transform (c->data);
+ }
+
+ HBUINT8 format; /* format = 22 (noVar) or 23(Var) */
+ Offset24To src; /* Offset (from beginning of PaintScaleUniformAroundCenter table) to Paint subtable. */
+ F2DOT14 scale;
+ FWORD centerX;
+ FWORD centerY;
+ public:
+ DEFINE_SIZE_STATIC (4 + F2DOT14::static_size + 2 * FWORD::static_size);
+};
+
+struct PaintRotate
+{
+ HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
+
+ bool subset (hb_subset_context_t *c,
+ const ItemVarStoreInstancer &instancer,
+ uint32_t varIdxBase) const
+ {
+ TRACE_SUBSET (this);
+ auto *out = c->serializer->embed (this);
+ if (unlikely (!out)) return_trace (false);
+
+ if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
+ out->angle.set_float (angle.to_float (instancer (varIdxBase, 0)));
+
+ if (format == 25 && c->plan->all_axes_pinned)
+ out->format = 24;
+
+ return_trace (out->src.serialize_subset (c, src, this, instancer));
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this) && src.sanitize (c, this));
+ }
+
+ void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
+ {
+ TRACE_PAINT (this);
+ float a = angle.to_float (c->instancer (varIdxBase, 0));
+
+ c->funcs->push_rotate (c->data, a);
+ c->recurse (this+src);
+ c->funcs->pop_transform (c->data);
+ }
+
+ HBUINT8 format; /* format = 24 (noVar) or 25(Var) */
+ Offset24To src; /* Offset (from beginning of PaintRotate table) to Paint subtable. */
+ F2DOT14 angle;
+ public:
+ DEFINE_SIZE_STATIC (4 + F2DOT14::static_size);
+};
+
+struct PaintRotateAroundCenter
+{
+ HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
+
+ bool subset (hb_subset_context_t *c,
+ const ItemVarStoreInstancer &instancer,
+ uint32_t varIdxBase) const
+ {
+ TRACE_SUBSET (this);
+ auto *out = c->serializer->embed (this);
+ if (unlikely (!out)) return_trace (false);
+
+ if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
+ {
+ out->angle.set_float (angle.to_float (instancer (varIdxBase, 0)));
+ out->centerX = centerX + (int) roundf (instancer (varIdxBase, 1));
+ out->centerY = centerY + (int) roundf (instancer (varIdxBase, 2));
+ }
+
+ if (format ==27 && c->plan->all_axes_pinned)
+ out->format = 26;
+
+ return_trace (out->src.serialize_subset (c, src, this, instancer));
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this) && src.sanitize (c, this));
+ }
+
+ void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
+ {
+ TRACE_PAINT (this);
+ float a = angle.to_float (c->instancer (varIdxBase, 0));
+ float tCenterX = centerX + c->instancer (varIdxBase, 1);
+ float tCenterY = centerY + c->instancer (varIdxBase, 2);
+
+ c->funcs->push_rotate_around_center (c->data, a, tCenterX, tCenterY);
+ c->recurse (this+src);
+ c->funcs->pop_transform (c->data);
+ }
+
+ HBUINT8 format; /* format = 26 (noVar) or 27(Var) */
+ Offset24To src; /* Offset (from beginning of PaintRotateAroundCenter table) to Paint subtable. */
+ F2DOT14 angle;
+ FWORD centerX;
+ FWORD centerY;
+ public:
+ DEFINE_SIZE_STATIC (4 + F2DOT14::static_size + 2 * FWORD::static_size);
+};
+
+struct PaintSkew
+{
+ HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
+
+ bool subset (hb_subset_context_t *c,
+ const ItemVarStoreInstancer &instancer,
+ uint32_t varIdxBase) const
+ {
+ TRACE_SUBSET (this);
+ auto *out = c->serializer->embed (this);
+ if (unlikely (!out)) return_trace (false);
+
+ if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
+ {
+ out->xSkewAngle.set_float (xSkewAngle.to_float (instancer (varIdxBase, 0)));
+ out->ySkewAngle.set_float (ySkewAngle.to_float (instancer (varIdxBase, 1)));
+ }
+
+ if (format == 29 && c->plan->all_axes_pinned)
+ out->format = 28;
+
+ return_trace (out->src.serialize_subset (c, src, this, instancer));
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this) && src.sanitize (c, this));
+ }
+
+ void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
+ {
+ TRACE_PAINT (this);
+ float sx = xSkewAngle.to_float(c->instancer (varIdxBase, 0));
+ float sy = ySkewAngle.to_float(c->instancer (varIdxBase, 1));
+
+ c->funcs->push_skew (c->data, sx, sy);
+ c->recurse (this+src);
+ c->funcs->pop_transform (c->data);
+ }
+
+ HBUINT8 format; /* format = 28(noVar) or 29 (Var) */
+ Offset24To src; /* Offset (from beginning of PaintSkew table) to Paint subtable. */
+ F2DOT14 xSkewAngle;
+ F2DOT14 ySkewAngle;
+ public:
+ DEFINE_SIZE_STATIC (4 + 2 * F2DOT14::static_size);
+};
+
+struct PaintSkewAroundCenter
+{
+ HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
+
+ bool subset (hb_subset_context_t *c,
+ const ItemVarStoreInstancer &instancer,
+ uint32_t varIdxBase) const
+ {
+ TRACE_SUBSET (this);
+ auto *out = c->serializer->embed (this);
+ if (unlikely (!out)) return_trace (false);
+
+ if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
+ {
+ out->xSkewAngle.set_float (xSkewAngle.to_float (instancer (varIdxBase, 0)));
+ out->ySkewAngle.set_float (ySkewAngle.to_float (instancer (varIdxBase, 1)));
+ out->centerX = centerX + (int) roundf (instancer (varIdxBase, 2));
+ out->centerY = centerY + (int) roundf (instancer (varIdxBase, 3));
+ }
+
+ if (format == 31 && c->plan->all_axes_pinned)
+ out->format = 30;
+
+ return_trace (out->src.serialize_subset (c, src, this, instancer));
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this) && src.sanitize (c, this));
+ }
+
+ void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
+ {
+ TRACE_PAINT (this);
+ float sx = xSkewAngle.to_float(c->instancer (varIdxBase, 0));
+ float sy = ySkewAngle.to_float(c->instancer (varIdxBase, 1));
+ float tCenterX = centerX + c->instancer (varIdxBase, 2);
+ float tCenterY = centerY + c->instancer (varIdxBase, 3);
+
+ c->funcs->push_skew_around_center (c->data, sx, sy, tCenterX, tCenterY);
+ c->recurse (this+src);
+ c->funcs->pop_transform (c->data);
+ }
+
+ HBUINT8 format; /* format = 30(noVar) or 31 (Var) */
+ Offset24To src; /* Offset (from beginning of PaintSkewAroundCenter table) to Paint subtable. */
+ F2DOT14 xSkewAngle;
+ F2DOT14 ySkewAngle;
+ FWORD centerX;
+ FWORD centerY;
+ public:
+ DEFINE_SIZE_STATIC (4 + 2 * F2DOT14::static_size + 2 * FWORD::static_size);
+};
+
+struct PaintComposite
+{
+ void closurev1 (hb_colrv1_closure_context_t* c) const;
+
+ bool subset (hb_subset_context_t *c,
+ const ItemVarStoreInstancer &instancer) const
+ {
+ TRACE_SUBSET (this);
+ auto *out = c->serializer->embed (this);
+ if (unlikely (!out)) return_trace (false);
+
+ bool ret = false;
+ ret |= out->src.serialize_subset (c, src, this, instancer);
+ ret |= out->backdrop.serialize_subset (c, backdrop, this, instancer);
+ return_trace (ret);
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this) &&
+ c->check_ops (this->min_size) && // PainComposite can get exponential
+ src.sanitize (c, this) &&
+ backdrop.sanitize (c, this));
+ }
+
+ void paint_glyph (hb_paint_context_t *c) const
+ {
+ TRACE_PAINT (this);
+ c->funcs->push_group_for (c->data, HB_PAINT_COMPOSITE_MODE_SRC_OVER);
+ c->recurse (this+backdrop);
+ c->funcs->push_group_for (c->data, (hb_paint_composite_mode_t) (int) mode);
+ c->recurse (this+src);
+ c->funcs->pop_group (c->data, (hb_paint_composite_mode_t) (int) mode);
+ c->funcs->pop_group (c->data, HB_PAINT_COMPOSITE_MODE_SRC_OVER);
+ }
+
+ HBUINT8 format; /* format = 32 */
+ Offset24To src; /* Offset (from beginning of PaintComposite table) to source Paint subtable. */
+ CompositeMode mode; /* If mode is unrecognized use COMPOSITE_CLEAR */
+ Offset24To backdrop; /* Offset (from beginning of PaintComposite table) to backdrop Paint subtable. */
+ public:
+ DEFINE_SIZE_STATIC (8);
+};
+
+struct ClipBoxData
+{
+ int xMin, yMin, xMax, yMax;
+};
+
+struct ClipBoxFormat1
+{
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this));
+ }
+
+ void get_clip_box (ClipBoxData &clip_box, const ItemVarStoreInstancer &instancer HB_UNUSED) const
+ {
+ clip_box.xMin = xMin;
+ clip_box.yMin = yMin;
+ clip_box.xMax = xMax;
+ clip_box.yMax = yMax;
+ }
+
+ bool subset (hb_subset_context_t *c,
+ const ItemVarStoreInstancer &instancer,
+ uint32_t varIdxBase) const
+ {
+ TRACE_SUBSET (this);
+ auto *out = c->serializer->embed (*this);
+ if (unlikely (!out)) return_trace (false);
+
+ if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
+ {
+ out->xMin = xMin + (int) roundf (instancer (varIdxBase, 0));
+ out->yMin = yMin + (int) roundf (instancer (varIdxBase, 1));
+ out->xMax = xMax + (int) roundf (instancer (varIdxBase, 2));
+ out->yMax = yMax + (int) roundf (instancer (varIdxBase, 3));
+ }
+
+ if (format == 2 && c->plan->all_axes_pinned)
+ out->format = 1;
+
+ return_trace (true);
+ }
+
+ public:
+ HBUINT8 format; /* format = 1(noVar) or 2(Var)*/
+ FWORD xMin;
+ FWORD yMin;
+ FWORD xMax;
+ FWORD yMax;
+ public:
+ DEFINE_SIZE_STATIC (1 + 4 * FWORD::static_size);
+};
+
+struct ClipBoxFormat2 : Variable
+{
+ void get_clip_box (ClipBoxData &clip_box, const ItemVarStoreInstancer &instancer) const
+ {
+ value.get_clip_box(clip_box, instancer);
+ if (instancer)
+ {
+ clip_box.xMin += roundf (instancer (varIdxBase, 0));
+ clip_box.yMin += roundf (instancer (varIdxBase, 1));
+ clip_box.xMax += roundf (instancer (varIdxBase, 2));
+ clip_box.yMax += roundf (instancer (varIdxBase, 3));
+ }
+ }
+
+ void closurev1 (hb_colrv1_closure_context_t* c) const
+ { c->variation_indices->add_range (varIdxBase, varIdxBase + 3); }
+};
+
+struct ClipBox
+{
+ bool subset (hb_subset_context_t *c,
+ const ItemVarStoreInstancer &instancer) const
+ {
+ TRACE_SUBSET (this);
+ switch (u.format.v) {
+ case 1: return_trace (u.format1.subset (c, instancer, VarIdx::NO_VARIATION));
+ case 2: return_trace (u.format2.subset (c, instancer));
+ default:return_trace (c->default_return_value ());
+ }
+ }
+
+ void closurev1 (hb_colrv1_closure_context_t* c) const
+ {
+ switch (u.format.v) {
+ case 2: u.format2.closurev1 (c); return;
+ default:return;
+ }
+ }
+
+ template
+ typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
+ {
+ if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value ();
+ TRACE_DISPATCH (this, u.format.v);
+ switch (u.format.v) {
+ case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...));
+ case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...));
+ default:return_trace (c->default_return_value ());
+ }
+ }
+
+ bool get_extents (hb_glyph_extents_t *extents,
+ const ItemVarStoreInstancer &instancer) const
+ {
+ ClipBoxData clip_box;
+ switch (u.format.v) {
+ case 1:
+ u.format1.get_clip_box (clip_box, instancer);
+ break;
+ case 2:
+ u.format2.get_clip_box (clip_box, instancer);
+ break;
+ default:
+ return false;
+ }
+
+ extents->x_bearing = clip_box.xMin;
+ extents->y_bearing = clip_box.yMax;
+ extents->width = clip_box.xMax - clip_box.xMin;
+ extents->height = clip_box.yMin - clip_box.yMax;
+ return true;
+ }
+
+ protected:
+ union {
+ struct { HBUINT8 v; } format; /* Format identifier */
+ ClipBoxFormat1 format1;
+ ClipBoxFormat2 format2;
+ } u;
+};
+
+struct ClipRecord
+{
+ int cmp (hb_codepoint_t g) const
+ { return g < startGlyphID ? -1 : g <= endGlyphID ? 0 : +1; }
+
+ void closurev1 (hb_colrv1_closure_context_t* c, const void *base) const
+ {
+ if (!c->glyphs->intersects (startGlyphID, endGlyphID)) return;
+ (base+clipBox).closurev1 (c);
+ }
+
+ bool subset (hb_subset_context_t *c,
+ const void *base,
+ const ItemVarStoreInstancer &instancer) const
+ {
+ TRACE_SUBSET (this);
+ auto *out = c->serializer->embed (*this);
+ if (unlikely (!out)) return_trace (false);
+
+ return_trace (out->clipBox.serialize_subset (c, clipBox, base, instancer));
+ }
+
+ bool sanitize (hb_sanitize_context_t *c, const void *base) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this) && clipBox.sanitize (c, base));
+ }
+
+ bool get_extents (hb_glyph_extents_t *extents,
+ const void *base,
+ const ItemVarStoreInstancer &instancer) const
+ {
+ return (base+clipBox).get_extents (extents, instancer);
+ }
+
+ public:
+ HBUINT16 startGlyphID; // first gid clip applies to
+ HBUINT16 endGlyphID; // last gid clip applies to, inclusive
+ Offset24To clipBox; // Box or VarBox
+ public:
+ DEFINE_SIZE_STATIC (7);
+};
+DECLARE_NULL_NAMESPACE_BYTES (OT, ClipRecord);
+
+struct ClipList
+{
+ unsigned serialize_clip_records (hb_subset_context_t *c,
+ const ItemVarStoreInstancer &instancer,
+ const hb_set_t& gids,
+ const hb_map_t& gid_offset_map) const
+ {
+ TRACE_SERIALIZE (this);
+ if (gids.is_empty () ||
+ gid_offset_map.get_population () != gids.get_population ())
+ return_trace (0);
+
+ unsigned count = 0;
+
+ hb_codepoint_t start_gid= gids.get_min ();
+ hb_codepoint_t prev_gid = start_gid;
+
+ unsigned offset = gid_offset_map.get (start_gid);
+ unsigned prev_offset = offset;
+ for (const hb_codepoint_t _ : gids.iter ())
+ {
+ if (_ == start_gid) continue;
+
+ offset = gid_offset_map.get (_);
+ if (_ == prev_gid + 1 && offset == prev_offset)
+ {
+ prev_gid = _;
+ continue;
+ }
+
+ ClipRecord record;
+ record.startGlyphID = start_gid;
+ record.endGlyphID = prev_gid;
+ record.clipBox = prev_offset;
+
+ if (!record.subset (c, this, instancer)) return_trace (0);
+ count++;
+
+ start_gid = _;
+ prev_gid = _;
+ prev_offset = offset;
+ }
+
+ //last one
+ {
+ ClipRecord record;
+ record.startGlyphID = start_gid;
+ record.endGlyphID = prev_gid;
+ record.clipBox = prev_offset;
+ if (!record.subset (c, this, instancer)) return_trace (0);
+ count++;
+ }
+ return_trace (count);
+ }
+
+ bool subset (hb_subset_context_t *c,
+ const ItemVarStoreInstancer &instancer) const
+ {
+ TRACE_SUBSET (this);
+ auto *out = c->serializer->start_embed (*this);
+ if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
+ if (!c->serializer->check_assign (out->format, format, HB_SERIALIZE_ERROR_INT_OVERFLOW)) return_trace (false);
+
+ const hb_set_t& glyphset = c->plan->_glyphset_colred;
+ const hb_map_t &glyph_map = *c->plan->glyph_map;
+
+ hb_map_t new_gid_offset_map;
+ hb_set_t new_gids;
+ for (const ClipRecord& record : clips.iter ())
+ {
+ unsigned start_gid = record.startGlyphID;
+ unsigned end_gid = record.endGlyphID;
+ for (unsigned gid = start_gid; gid <= end_gid; gid++)
+ {
+ if (!glyphset.has (gid) || !glyph_map.has (gid)) continue;
+ unsigned new_gid = glyph_map.get (gid);
+ new_gid_offset_map.set (new_gid, record.clipBox);
+ new_gids.add (new_gid);
+ }
+ }
+
+ unsigned count = serialize_clip_records (c, instancer, new_gids, new_gid_offset_map);
+ if (!count) return_trace (false);
+ return_trace (c->serializer->check_assign (out->clips.len, count, HB_SERIALIZE_ERROR_INT_OVERFLOW));
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ // TODO Make a formatted struct!
+ return_trace (c->check_struct (this) && clips.sanitize (c, this));
+ }
+
+ bool
+ get_extents (hb_codepoint_t gid,
+ hb_glyph_extents_t *extents,
+ const ItemVarStoreInstancer &instancer) const
+ {
+ auto *rec = clips.as_array ().bsearch (gid);
+ if (rec)
+ return rec->get_extents (extents, this, instancer);
+ return false;
+ }
+
+ HBUINT8 format; // Set to 1.
+ SortedArray32Of clips; // Clip records, sorted by startGlyphID
+ public:
+ DEFINE_SIZE_ARRAY_SIZED (5, clips);
+};
+
+struct Paint
+{
+
+ template
+ bool sanitize (hb_sanitize_context_t *c, Ts&&... ds) const
+ {
+ TRACE_SANITIZE (this);
+
+ if (unlikely (!c->check_start_recursion (HB_MAX_NESTING_LEVEL)))
+ return_trace (c->no_dispatch_return_value ());
+
+ return_trace (c->end_recursion (this->dispatch (c, std::forward (ds)...)));
+ }
+
+ template
+ typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
+ {
+ if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value ();
+ TRACE_DISPATCH (this, u.format.v);
+ switch (u.format.v) {
+ case 1: return_trace (c->dispatch (u.paintformat1, std::forward (ds)...));
+ case 2: return_trace (c->dispatch (u.paintformat2, std::forward (ds)...));
+ case 3: return_trace (c->dispatch (u.paintformat3, std::forward (ds)...));
+ case 4: return_trace (c->dispatch (u.paintformat4, std::forward (ds)...));
+ case 5: return_trace (c->dispatch (u.paintformat5, std::forward (ds)...));
+ case 6: return_trace (c->dispatch (u.paintformat6, std::forward (ds)...));
+ case 7: return_trace (c->dispatch (u.paintformat7, std::forward (ds)...));
+ case 8: return_trace (c->dispatch (u.paintformat8, std::forward (ds)...));
+ case 9: return_trace (c->dispatch (u.paintformat9, std::forward (ds)...));
+ case 10: return_trace (c->dispatch (u.paintformat10, std::forward (ds)...));
+ case 11: return_trace (c->dispatch (u.paintformat11, std::forward (ds)...));
+ case 12: return_trace (c->dispatch (u.paintformat12, std::forward (ds)...));
+ case 13: return_trace (c->dispatch (u.paintformat13, std::forward (ds)...));
+ case 14: return_trace (c->dispatch (u.paintformat14, std::forward (ds)...));
+ case 15: return_trace (c->dispatch (u.paintformat15, std::forward (ds)...));
+ case 16: return_trace (c->dispatch (u.paintformat16, std::forward (ds)...));
+ case 17: return_trace (c->dispatch (u.paintformat17, std::forward (ds)...));
+ case 18: return_trace (c->dispatch (u.paintformat18, std::forward (ds)...));
+ case 19: return_trace (c->dispatch (u.paintformat19, std::forward (ds)...));
+ case 20: return_trace (c->dispatch (u.paintformat20, std::forward (ds)...));
+ case 21: return_trace (c->dispatch (u.paintformat21, std::forward (ds)...));
+ case 22: return_trace (c->dispatch (u.paintformat22, std::forward (ds)...));
+ case 23: return_trace (c->dispatch (u.paintformat23, std::forward (ds)...));
+ case 24: return_trace (c->dispatch (u.paintformat24, std::forward (ds)...));
+ case 25: return_trace (c->dispatch (u.paintformat25, std::forward (ds)...));
+ case 26: return_trace (c->dispatch (u.paintformat26, std::forward (ds)...));
+ case 27: return_trace (c->dispatch (u.paintformat27, std::forward (ds)...));
+ case 28: return_trace (c->dispatch (u.paintformat28, std::forward (ds)...));
+ case 29: return_trace (c->dispatch (u.paintformat29, std::forward (ds)...));
+ case 30: return_trace (c->dispatch (u.paintformat30, std::forward (ds)...));
+ case 31: return_trace (c->dispatch (u.paintformat31, std::forward (ds)...));
+ case 32: return_trace (c->dispatch (u.paintformat32, std::forward (ds)...));
+ default:return_trace (c->default_return_value ());
+ }
+ }
+
+ protected:
+ union {
+ struct { HBUINT8 v; } format;
+ PaintColrLayers paintformat1;
+ NoVariable paintformat2;
+ Variable paintformat3;
+ NoVariable> paintformat4;
+ Variable> paintformat5;
+ NoVariable> paintformat6;
+ Variable> paintformat7;
+ NoVariable> paintformat8;
+ Variable> paintformat9;
+ PaintGlyph paintformat10;
+ PaintColrGlyph paintformat11;
+ PaintTransform paintformat12;
+ PaintTransform paintformat13;
+ NoVariable paintformat14;
+ Variable paintformat15;
+ NoVariable paintformat16;
+ Variable paintformat17;
+ NoVariable paintformat18;
+ Variable paintformat19;
+ NoVariable paintformat20;
+ Variable paintformat21;
+ NoVariable paintformat22;
+ Variable paintformat23;
+ NoVariable paintformat24;
+ Variable paintformat25;
+ NoVariable paintformat26;
+ Variable paintformat27;
+ NoVariable paintformat28;
+ Variable paintformat29;
+ NoVariable paintformat30;
+ Variable paintformat31;
+ PaintComposite paintformat32;
+ } u;
+ public:
+ DEFINE_SIZE_MIN (2);
+};
+
+struct BaseGlyphPaintRecord
+{
+ int cmp (hb_codepoint_t g) const
+ { return g < glyphId ? -1 : g > glyphId ? 1 : 0; }
+
+ bool serialize (hb_serialize_context_t *s, const hb_map_t* glyph_map,
+ const void* src_base, hb_subset_context_t *c,
+ const ItemVarStoreInstancer &instancer) const
+ {
+ TRACE_SERIALIZE (this);
+ auto *out = s->embed (this);
+ if (unlikely (!out)) return_trace (false);
+ if (!s->check_assign (out->glyphId, glyph_map->get (glyphId),
+ HB_SERIALIZE_ERROR_INT_OVERFLOW))
+ return_trace (false);
+
+ return_trace (out->paint.serialize_subset (c, paint, src_base, instancer));
+ }
+
+ bool sanitize (hb_sanitize_context_t *c, const void *base) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (likely (c->check_struct (this) && paint.sanitize (c, base)));
+ }
+
+ public:
+ HBGlyphID16 glyphId; /* Glyph ID of reference glyph */
+ Offset32To paint; /* Offset (from beginning of BaseGlyphPaintRecord array) to Paint,
+ * Typically PaintColrLayers */
+ public:
+ DEFINE_SIZE_STATIC (6);
+};
+
+struct BaseGlyphList : SortedArray32Of
+{
+ bool subset (hb_subset_context_t *c,
+ const ItemVarStoreInstancer &instancer) const
+ {
+ TRACE_SUBSET (this);
+ auto *out = c->serializer->start_embed (this);
+ if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
+ const hb_set_t* glyphset = &c->plan->_glyphset_colred;
+
+ for (const auto& _ : as_array ())
+ {
+ unsigned gid = _.glyphId;
+ if (!glyphset->has (gid)) continue;
+
+ if (_.serialize (c->serializer, c->plan->glyph_map, this, c, instancer)) out->len++;
+ else return_trace (false);
+ }
+
+ return_trace (out->len != 0);
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (SortedArray32Of::sanitize (c, this));
+ }
+};
+
+struct LayerList : Array32OfOffset32To
+{
+ const Paint& get_paint (unsigned i) const
+ { return this+(*this)[i]; }
+
+ bool subset (hb_subset_context_t *c,
+ const ItemVarStoreInstancer &instancer) const
+ {
+ TRACE_SUBSET (this);
+ auto *out = c->serializer->start_embed (this);
+ if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
+
+ bool ret = false;
+ for (const auto& _ : + hb_enumerate (*this)
+ | hb_filter (c->plan->colrv1_layers, hb_first))
+
+ {
+ auto *o = out->serialize_append (c->serializer);
+ if (unlikely (!o)) return_trace (false);
+ ret |= o->serialize_subset (c, _.second, this, instancer);
+ }
+ return_trace (ret);
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (Array32OfOffset32To::sanitize (c, this));
+ }
+};
+
+struct delta_set_index_map_subset_plan_t
+{
+ unsigned get_inner_bit_count () const { return inner_bit_count; }
+ unsigned get_width () const { return ((outer_bit_count + inner_bit_count + 7) / 8); }
+ hb_array_t get_output_map () const { return output_map.as_array (); }
+
+ delta_set_index_map_subset_plan_t (const hb_map_t &new_deltaset_idx_varidx_map)
+ {
+ map_count = 0;
+ outer_bit_count = 0;
+ inner_bit_count = 1;
+ output_map.init ();
+
+ /* search backwards */
+ unsigned count = new_deltaset_idx_varidx_map.get_population ();
+ if (!count) return;
+
+ unsigned last_idx = (unsigned)-1;
+ unsigned last_varidx = (unsigned)-1;
+
+ for (unsigned i = count; i; i--)
+ {
+ unsigned delta_set_idx = i - 1;
+ unsigned var_idx = new_deltaset_idx_varidx_map.get (delta_set_idx);
+ if (i == count)
+ {
+ last_idx = delta_set_idx;
+ last_varidx = var_idx;
+ continue;
+ }
+ if (var_idx != last_varidx)
+ break;
+ last_idx = delta_set_idx;
+ }
+
+ map_count = last_idx + 1;
+ }
+
+ bool remap (const hb_map_t &new_deltaset_idx_varidx_map)
+ {
+ /* recalculate bit_count */
+ outer_bit_count = 1;
+ inner_bit_count = 1;
+
+ if (unlikely (!output_map.resize_dirty (map_count))) return false;
+
+ for (unsigned idx = 0; idx < map_count; idx++)
+ {
+ uint32_t *var_idx;
+ if (!new_deltaset_idx_varidx_map.has (idx, &var_idx)) return false;
+ output_map.arrayZ[idx] = *var_idx;
+
+ unsigned outer = (*var_idx) >> 16;
+ unsigned bit_count = (outer == 0) ? 1 : hb_bit_storage (outer);
+ outer_bit_count = hb_max (bit_count, outer_bit_count);
+
+ unsigned inner = (*var_idx) & 0xFFFF;
+ bit_count = (inner == 0) ? 1 : hb_bit_storage (inner);
+ inner_bit_count = hb_max (bit_count, inner_bit_count);
+ }
+ return true;
+ }
+
+ private:
+ unsigned map_count;
+ unsigned outer_bit_count;
+ unsigned inner_bit_count;
+ hb_vector_t output_map;
+};
+
+struct COLR
+{
+ static constexpr hb_tag_t tableTag = HB_OT_TAG_COLR;
+
+ bool has_data () const { return has_v0_data () || version; }
+
+ bool has_v0_data () const { return numBaseGlyphs; }
+ bool has_v1_data () const
+ {
+ if (version < 1)
+ return false;
+ hb_barrier ();
+
+ return (this+baseGlyphList).len > 0;
+ }
+
+ unsigned int get_glyph_layers (hb_codepoint_t glyph,
+ unsigned int start_offset,
+ unsigned int *count, /* IN/OUT. May be NULL. */
+ hb_ot_color_layer_t *layers /* OUT. May be NULL. */) const
+ {
+ const BaseGlyphRecord &record = (this+baseGlyphsZ).bsearch (numBaseGlyphs, glyph);
+
+ hb_array_t all_layers = (this+layersZ).as_array (numLayers);
+ hb_array_t glyph_layers = all_layers.sub_array (record.firstLayerIdx,
+ record.numLayers);
+ if (count)
+ {
+ + glyph_layers.sub_array (start_offset, count)
+ | hb_sink (hb_array (layers, *count))
+ ;
+ }
+ return glyph_layers.length;
+ }
+
+ struct accelerator_t
+ {
+ accelerator_t (hb_face_t *face)
+ { colr = hb_sanitize_context_t ().reference_table (face); }
+
+ ~accelerator_t ()
+ {
+ auto *scratch = cached_scratch.get_relaxed ();
+ if (scratch)
+ {
+ scratch->~hb_colr_scratch_t ();
+ hb_free (scratch);
+ }
+
+ colr.destroy ();
+ }
+
+
+ bool has_data () const { return colr->has_data (); }
+
+#ifndef HB_NO_PAINT
+ bool
+ get_extents (hb_font_t *font,
+ hb_codepoint_t glyph,
+ hb_glyph_extents_t *extents) const
+ {
+ if (unlikely (!has_data ())) return false;
+
+ hb_colr_scratch_t *scratch = acquire_scratch ();
+ if (unlikely (!scratch)) return true;
+ bool ret = colr->get_extents (font, glyph, extents, *scratch);
+ release_scratch (scratch);
+ return ret;
+ }
+
+ bool paint_glyph (hb_font_t *font,
+ hb_codepoint_t glyph,
+ hb_paint_funcs_t *funcs, void *data,
+ unsigned int palette_index,
+ hb_color_t foreground,
+ bool clip = true) const
+ {
+ if (unlikely (!has_data ())) return false;
+
+ hb_colr_scratch_t *scratch = acquire_scratch ();
+ if (unlikely (!scratch)) return true;
+ bool ret = colr->paint_glyph (font, glyph, funcs, data, palette_index, foreground, clip, *scratch);
+ release_scratch (scratch);
+ return ret;
+ }
+#endif
+
+ bool is_valid () { return colr.get_blob ()->length; }
+
+ void closure_glyphs (hb_codepoint_t glyph,
+ hb_set_t *related_ids /* OUT */) const
+ { colr->closure_glyphs (glyph, related_ids); }
+
+ void closure_V0palette_indices (const hb_set_t *glyphs,
+ hb_set_t *palettes /* OUT */) const
+ { colr->closure_V0palette_indices (glyphs, palettes); }
+
+ void closure_forV1 (hb_set_t *glyphset,
+ hb_set_t *layer_indices,
+ hb_set_t *palette_indices,
+ hb_set_t *variation_indices,
+ hb_set_t *delta_set_indices) const
+ { colr->closure_forV1 (glyphset, layer_indices, palette_indices, variation_indices, delta_set_indices); }
+
+ bool has_var_store () const
+ { return colr->has_var_store (); }
+
+ const ItemVariationStore &get_var_store () const
+ { return colr->get_var_store (); }
+ const ItemVariationStore *get_var_store_ptr () const
+ { return colr->get_var_store_ptr (); }
+
+ bool has_delta_set_index_map () const
+ { return colr->has_delta_set_index_map (); }
+
+ const DeltaSetIndexMap &get_delta_set_index_map () const
+ { return colr->get_delta_set_index_map (); }
+ const DeltaSetIndexMap *get_delta_set_index_map_ptr () const
+ { return colr->get_delta_set_index_map_ptr (); }
+
+ private:
+
+ hb_colr_scratch_t *acquire_scratch () const
+ {
+ hb_colr_scratch_t *scratch = cached_scratch.get_acquire ();
+
+ if (!scratch || unlikely (!cached_scratch.cmpexch (scratch, nullptr)))
+ {
+ scratch = (hb_colr_scratch_t *) hb_calloc (1, sizeof (hb_colr_scratch_t));
+ if (unlikely (!scratch))
+ return nullptr;
+ }
+
+ return scratch;
+ }
+ void release_scratch (hb_colr_scratch_t *scratch) const
+ {
+ if (!cached_scratch.cmpexch (nullptr, scratch))
+ {
+ scratch->~hb_colr_scratch_t ();
+ hb_free (scratch);
+ }
+ }
+
+ public:
+ hb_blob_ptr_t colr;
+ private:
+ mutable hb_atomic_t cached_scratch;
+ };
+
+ void closure_glyphs (hb_codepoint_t glyph,
+ hb_set_t *related_ids /* OUT */) const
+ {
+ const BaseGlyphRecord *record = get_base_glyph_record (glyph);
+ if (!record) return;
+
+ auto glyph_layers = (this+layersZ).as_array (numLayers).sub_array (record->firstLayerIdx,
+ record->numLayers);
+ if (!glyph_layers.length) return;
+ related_ids->add_array (&glyph_layers[0].glyphId, glyph_layers.length, LayerRecord::min_size);
+ }
+
+ void closure_V0palette_indices (const hb_set_t *glyphs,
+ hb_set_t *palettes /* OUT */) const
+ {
+ if (!numBaseGlyphs || !numLayers) return;
+ hb_array_t baseGlyphs = (this+baseGlyphsZ).as_array (numBaseGlyphs);
+ hb_array_t all_layers = (this+layersZ).as_array (numLayers);
+
+ for (const BaseGlyphRecord record : baseGlyphs)
+ {
+ if (!glyphs->has (record.glyphId)) continue;
+ hb_array_t glyph_layers = all_layers.sub_array (record.firstLayerIdx,
+ record.numLayers);
+ for (const LayerRecord layer : glyph_layers)
+ palettes->add (layer.colorIdx);
+ }
+ }
+
+ void closure_forV1 (hb_set_t *glyphset,
+ hb_set_t *layer_indices,
+ hb_set_t *palette_indices,
+ hb_set_t *variation_indices,
+ hb_set_t *delta_set_indices) const
+ {
+ if (version < 1) return;
+ hb_barrier ();
+
+ hb_set_t visited_glyphs;
+
+ hb_colrv1_closure_context_t c (this, &visited_glyphs, layer_indices, palette_indices, variation_indices);
+ const BaseGlyphList &baseglyph_paintrecords = this+baseGlyphList;
+
+ for (const BaseGlyphPaintRecord &baseglyph_paintrecord: baseglyph_paintrecords.iter ())
+ {
+ unsigned gid = baseglyph_paintrecord.glyphId;
+ if (!glyphset->has (gid)) continue;
+
+ const Paint &paint = &baseglyph_paintrecords+baseglyph_paintrecord.paint;
+ paint.dispatch (&c);
+ }
+ hb_set_union (glyphset, &visited_glyphs);
+
+ const ClipList &cliplist = this+clipList;
+ c.glyphs = glyphset;
+ for (const ClipRecord &clip_record : cliplist.clips.iter())
+ clip_record.closurev1 (&c, &cliplist);
+
+ // if a DeltaSetIndexMap is included, collected variation indices are
+ // actually delta set indices, we need to map them into variation indices
+ if (has_delta_set_index_map ())
+ {
+ const DeltaSetIndexMap &var_idx_map = this+varIdxMap;
+ delta_set_indices->set (*variation_indices);
+ variation_indices->clear ();
+ for (unsigned delta_set_idx : *delta_set_indices)
+ variation_indices->add (var_idx_map.map (delta_set_idx));
+ }
+ }
+
+ const LayerList& get_layerList () const
+ { return (this+layerList); }
+
+ const BaseGlyphList& get_baseglyphList () const
+ { return (this+baseGlyphList); }
+
+ bool has_var_store () const
+ { return version >= 1 && hb_barrier () && varStore != 0; }
+
+ bool has_delta_set_index_map () const
+ { return version >= 1 && hb_barrier () && varIdxMap != 0; }
+
+ bool has_clip_list () const
+ { return version >= 1 && hb_barrier () && clipList != 0; }
+
+ const DeltaSetIndexMap &get_delta_set_index_map () const
+ { return has_delta_set_index_map () && hb_barrier () ? this+varIdxMap : Null (DeltaSetIndexMap); }
+ const DeltaSetIndexMap *get_delta_set_index_map_ptr () const
+ { return has_delta_set_index_map () && hb_barrier () ? &(this+varIdxMap) : nullptr; }
+
+ const ItemVariationStore &get_var_store () const
+ { return has_var_store () && hb_barrier () ? this+varStore : Null (ItemVariationStore); }
+ const ItemVariationStore *get_var_store_ptr () const
+ { return has_var_store () && hb_barrier () ? &(this+varStore) : nullptr; }
+
+ const ClipList &get_clip_list () const
+ { return has_clip_list () && hb_barrier () ? this+clipList : Null (ClipList); }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this) &&
+ hb_barrier () &&
+ (this+baseGlyphsZ).sanitize (c, numBaseGlyphs) &&
+ (this+layersZ).sanitize (c, numLayers) &&
+ (version == 0 ||
+ (hb_barrier () &&
+ baseGlyphList.sanitize (c, this) &&
+ layerList.sanitize (c, this) &&
+ clipList.sanitize (c, this) &&
+ varIdxMap.sanitize (c, this) &&
+ varStore.sanitize (c, this))));
+ }
+
+ template
+ bool serialize_V0 (hb_serialize_context_t *c,
+ unsigned version,
+ BaseIterator base_it,
+ LayerIterator layer_it)
+ {
+ TRACE_SERIALIZE (this);
+ if (unlikely (base_it.len () != layer_it.len ()))
+ return_trace (false);
+
+ this->version = version;
+ numLayers = 0;
+ numBaseGlyphs = base_it.len ();
+ if (numBaseGlyphs == 0)
+ {
+ baseGlyphsZ = 0;
+ layersZ = 0;
+ return_trace (true);
+ }
+
+ c->push ();
+ for (const hb_item_type _ : + base_it.iter ())
+ {
+ auto* record = c->embed (_);
+ if (unlikely (!record)) return_trace (false);
+ record->firstLayerIdx = numLayers;
+ numLayers += record->numLayers;
+ }
+ c->add_link (baseGlyphsZ, c->pop_pack ());
+
+ c->push ();
+ for (const hb_item_type& _ : + layer_it.iter ())
+ _.as_array ().copy (c);
+
+ c->add_link (layersZ, c->pop_pack ());
+
+ return_trace (true);
+ }
+
+ const BaseGlyphRecord* get_base_glyph_record (hb_codepoint_t gid) const
+ {
+ const BaseGlyphRecord* record = &(this+baseGlyphsZ).bsearch (numBaseGlyphs, (unsigned int) gid);
+ if (record == &Null (BaseGlyphRecord) ||
+ (record && (hb_codepoint_t) record->glyphId != gid))
+ record = nullptr;
+ return record;
+ }
+
+ const BaseGlyphPaintRecord* get_base_glyph_paintrecord (hb_codepoint_t gid) const
+ {
+ const BaseGlyphList &list = this+baseGlyphList;
+ unsigned int i;
+ if (!list.bfind ((unsigned) gid, &i))
+ return nullptr;
+ return &list[i];
+ }
+
+ bool downgrade_to_V0 (const hb_set_t &glyphset) const
+ {
+ //no more COLRv1 glyphs, downgrade to version 0
+ for (const BaseGlyphPaintRecord& _ : get_baseglyphList ())
+ if (glyphset.has (_.glyphId))
+ return false;
+
+ return true;
+ }
+
+ bool subset_varstore (hb_subset_context_t *c,
+ COLR* out /* OUT */) const
+ {
+ TRACE_SUBSET (this);
+ if (!varStore || c->plan->all_axes_pinned ||
+ !c->plan->colrv1_variation_idx_delta_map)
+ return_trace (true);
+
+ const ItemVariationStore& var_store = this+varStore;
+ if (c->plan->normalized_coords)
+ {
+ item_variations_t item_vars;
+ /* turn off varstore optimization when varIdxMap is null, so we maintain
+ * original var_idx sequence */
+ bool optimize = (varIdxMap != 0) ? true : false;
+ if (!item_vars.instantiate (var_store, c->plan,
+ optimize, /* optimization */
+ optimize, /* use_no_variation_idx = false */
+ c->plan->colrv1_varstore_inner_maps.as_array ()))
+ return_trace (false);
+
+ /* do not serialize varStore if there's no variation data after
+ * instancing: region_list or var_data is empty */
+ if (item_vars.get_region_list () &&
+ item_vars.get_vardata_encodings () &&
+ !out->varStore.serialize_serialize (c->serializer,
+ item_vars.has_long_word (),
+ c->plan->axis_tags,
+ item_vars.get_region_list (),
+ item_vars.get_vardata_encodings ()))
+ return_trace (false);
+
+ /* if varstore is optimized, update colrv1_new_deltaset_idx_varidx_map in
+ * subset plan.
+ * If varstore is empty after instancing, varidx_map would be empty and
+ * all var_idxes will be updated to VarIdx::NO_VARIATION */
+ if (optimize)
+ {
+ const hb_map_t &varidx_map = item_vars.get_varidx_map ();
+ for (auto _ : c->plan->colrv1_new_deltaset_idx_varidx_map.iter_ref ())
+ {
+ uint32_t varidx = _.second;
+ uint32_t *new_varidx;
+ if (varidx_map.has (varidx, &new_varidx))
+ _.second = *new_varidx;
+ else
+ _.second = VarIdx::NO_VARIATION;
+ }
+ }
+ }
+ else
+ {
+ if (unlikely (!out->varStore.serialize_serialize (c->serializer,
+ &var_store,
+ c->plan->colrv1_varstore_inner_maps.as_array ())))
+ return_trace (false);
+ }
+
+ return_trace (true);
+ }
+
+ bool subset_delta_set_index_map (hb_subset_context_t *c,
+ COLR* out /* OUT */) const
+ {
+ TRACE_SUBSET (this);
+ if (!varIdxMap || c->plan->all_axes_pinned ||
+ !c->plan->colrv1_new_deltaset_idx_varidx_map)
+ return_trace (true);
+
+ const hb_map_t &deltaset_idx_varidx_map = c->plan->colrv1_new_deltaset_idx_varidx_map;
+ delta_set_index_map_subset_plan_t index_map_plan (deltaset_idx_varidx_map);
+
+ if (unlikely (!index_map_plan.remap (deltaset_idx_varidx_map)))
+ return_trace (false);
+
+ return_trace (out->varIdxMap.serialize_serialize (c->serializer, index_map_plan));
+ }
+
+ bool subset (hb_subset_context_t *c) const
+ {
+ TRACE_SUBSET (this);
+ const hb_map_t &reverse_glyph_map = *c->plan->reverse_glyph_map;
+ const hb_set_t& glyphset = c->plan->_glyphset_colred;
+
+ auto base_it =
+ + hb_range (c->plan->num_output_glyphs ())
+ | hb_filter ([&](hb_codepoint_t new_gid)
+ {
+ hb_codepoint_t old_gid = reverse_glyph_map.get (new_gid);
+ if (glyphset.has (old_gid)) return true;
+ return false;
+ })
+ | hb_map_retains_sorting ([&](hb_codepoint_t new_gid)
+ {
+ hb_codepoint_t old_gid = reverse_glyph_map.get (new_gid);
+
+ const BaseGlyphRecord* old_record = get_base_glyph_record (old_gid);
+ if (unlikely (!old_record))
+ return hb_pair_t (false, Null (BaseGlyphRecord));
+ BaseGlyphRecord new_record = {};
+ new_record.glyphId = new_gid;
+ new_record.numLayers = old_record->numLayers;
+ return hb_pair_t (true, new_record);
+ })
+ | hb_filter (hb_first)
+ | hb_map_retains_sorting (hb_second)
+ ;
+
+ auto layer_it =
+ + hb_range (c->plan->num_output_glyphs ())
+ | hb_map (reverse_glyph_map)
+ | hb_filter (glyphset)
+ | hb_map_retains_sorting ([&](hb_codepoint_t old_gid)
+ {
+ const BaseGlyphRecord* old_record = get_base_glyph_record (old_gid);
+ hb_vector_t out_layers;
+
+ if (unlikely (!old_record ||
+ old_record->firstLayerIdx >= numLayers ||
+ old_record->firstLayerIdx + old_record->numLayers > numLayers))
+ return hb_pair_t> (false, out_layers);
+
+ auto layers = (this+layersZ).as_array (numLayers).sub_array (old_record->firstLayerIdx,
+ old_record->numLayers);
+ out_layers.resize (layers.length);
+ for (unsigned int i = 0; i < layers.length; i++) {
+ out_layers[i] = layers[i];
+ hb_codepoint_t new_gid = 0;
+ if (unlikely (!c->plan->new_gid_for_old_gid (out_layers[i].glyphId, &new_gid)))
+ return hb_pair_t> (false, out_layers);
+ out_layers[i].glyphId = new_gid;
+ out_layers[i].colorIdx = c->plan->colr_palettes.get (layers[i].colorIdx);
+ }
+
+ return hb_pair_t> (true, out_layers);
+ })
+ | hb_filter (hb_first)
+ | hb_map_retains_sorting (hb_second)
+ ;
+
+ if (version == 0 && (!base_it || !layer_it))
+ return_trace (false);
+
+ auto *colr_prime = c->serializer->start_embed ();
+ if (unlikely (!c->serializer->extend_min (colr_prime))) return_trace (false);
+
+ if (version == 0 || downgrade_to_V0 (glyphset))
+ return_trace (colr_prime->serialize_V0 (c->serializer, 0, base_it, layer_it));
+
+ hb_barrier ();
+
+ //start version 1
+ if (!c->serializer->allocate_size (5 * HBUINT32::static_size)) return_trace (false);
+ if (!colr_prime->serialize_V0 (c->serializer, version, base_it, layer_it)) return_trace (false);
+
+ /* subset ItemVariationStore first, cause varidx_map needs to be updated
+ * after instancing */
+ if (!subset_varstore (c, colr_prime)) return_trace (false);
+
+ ItemVarStoreInstancer instancer (get_var_store_ptr (),
+ get_delta_set_index_map_ptr (),
+ c->plan->normalized_coords.as_array ());
+
+ if (!colr_prime->baseGlyphList.serialize_subset (c, baseGlyphList, this, instancer))
+ return_trace (false);
+
+ colr_prime->layerList.serialize_subset (c, layerList, this, instancer);
+ colr_prime->clipList.serialize_subset (c, clipList, this, instancer);
+
+ return_trace (subset_delta_set_index_map (c, colr_prime));
+ }
+
+ const Paint *get_base_glyph_paint (hb_codepoint_t glyph) const
+ {
+ const BaseGlyphList &baseglyph_paintrecords = this+baseGlyphList;
+ const BaseGlyphPaintRecord* record = get_base_glyph_paintrecord (glyph);
+ if (record)
+ {
+ const Paint &paint = &baseglyph_paintrecords+record->paint;
+ return &paint;
+ }
+ else
+ return nullptr;
+ }
+
+#ifndef HB_NO_PAINT
+ bool
+ get_extents (hb_font_t *font,
+ hb_codepoint_t glyph,
+ hb_glyph_extents_t *extents,
+ hb_colr_scratch_t &scratch) const
+ {
+
+ ItemVarStoreInstancer instancer (get_var_store_ptr (),
+ get_delta_set_index_map_ptr (),
+ hb_array (font->coords, font->num_coords));
+
+ if (get_clip (glyph, extents, instancer))
+ {
+ font->scale_glyph_extents (extents);
+ return true;
+ }
+
+ auto *extents_funcs = hb_paint_extents_get_funcs ();
+ scratch.paint_extents.clear ();
+ bool ret = paint_glyph (font, glyph, extents_funcs, &scratch.paint_extents, 0, HB_COLOR(0,0,0,0), true, scratch);
+
+ auto e = scratch.paint_extents.get_extents ();
+ if (e.is_void ())
+ {
+ extents->x_bearing = 0;
+ extents->y_bearing = 0;
+ extents->width = 0;
+ extents->height = 0;
+ }
+ else
+ {
+ extents->x_bearing = e.xmin;
+ extents->y_bearing = e.ymax;
+ extents->width = e.xmax - e.xmin;
+ extents->height = e.ymin - e.ymax;
+ }
+
+ return ret;
+ }
+#endif
+
+ bool
+ has_paint_for_glyph (hb_codepoint_t glyph) const
+ {
+ if (version >= 1)
+ {
+ hb_barrier ();
+
+ const Paint *paint = get_base_glyph_paint (glyph);
+
+ return paint != nullptr;
+ }
+
+ return false;
+ }
+
+ bool get_clip (hb_codepoint_t glyph,
+ hb_glyph_extents_t *extents,
+ const ItemVarStoreInstancer instancer) const
+ {
+ return get_clip_list ().get_extents (glyph,
+ extents,
+ instancer);
+ }
+
+#ifndef HB_NO_PAINT
+ bool
+ paint_glyph (hb_font_t *font,
+ hb_codepoint_t glyph,
+ hb_paint_funcs_t *funcs, void *data,
+ unsigned int palette_index, hb_color_t foreground,
+ bool clip,
+ hb_colr_scratch_t &scratch) const
+ {
+ ItemVarStoreInstancer instancer (get_var_store_ptr (),
+ get_delta_set_index_map_ptr (),
+ hb_array (font->coords,
+ font->has_nonzero_coords ? font->num_coords : 0));
+ hb_paint_context_t c (this, funcs, data, font, palette_index, foreground, instancer);
+
+ hb_decycler_node_t node (c.glyphs_decycler);
+ node.visit (glyph);
+
+ if (version >= 1)
+ {
+ hb_barrier ();
+
+ const Paint *paint = get_base_glyph_paint (glyph);
+ if (paint)
+ {
+ // COLRv1 glyph
+
+ bool is_bounded = true;
+ hb_glyph_extents_t extents = {};
+ if (clip)
+ {
+ if (!get_clip (glyph, &extents, instancer))
+ {
+ clip = false;
+ is_bounded = false;
+ }
+
+ if (!is_bounded)
+ {
+ auto *bounded_funcs = hb_paint_bounded_get_funcs ();
+ scratch.paint_bounded.clear ();
+
+ paint_glyph (font, glyph,
+ bounded_funcs, &scratch.paint_bounded,
+ palette_index, foreground,
+ false,
+ scratch);
+
+ is_bounded = scratch.paint_bounded.is_bounded ();
+ }
+ }
+
+ c.funcs->push_font_transform (c.data, font);
+
+ if (clip)
+ c.funcs->push_clip_rectangle (c.data,
+ extents.x_bearing,
+ extents.y_bearing + extents.height,
+ extents.x_bearing + extents.width,
+ extents.y_bearing);
+
+ if (is_bounded)
+ c.recurse (*paint);
+
+ if (clip)
+ c.funcs->pop_clip (c.data);
+
+ c.funcs->pop_transform (c.data);
+
+ return true;
+ }
+ }
+
+ const BaseGlyphRecord *record = get_base_glyph_record (glyph);
+ if (record && ((hb_codepoint_t) record->glyphId == glyph))
+ {
+ // COLRv0 glyph
+ for (const auto &r : (this+layersZ).as_array (numLayers)
+ .sub_array (record->firstLayerIdx, record->numLayers))
+ {
+ hb_bool_t is_foreground;
+ hb_color_t color = c.get_color (r.colorIdx, 1., &is_foreground);
+ c.funcs->push_clip_glyph (c.data, r.glyphId, c.font);
+ c.funcs->color (c.data, is_foreground, color);
+ c.funcs->pop_clip (c.data);
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+#endif
+
+ protected:
+ HBUINT16 version; /* Table version number (starts at 0). */
+ HBUINT16 numBaseGlyphs; /* Number of Base Glyph Records. */
+ NNOffset32To>
+ baseGlyphsZ; /* Offset to Base Glyph records. */
+ NNOffset32To>
+ layersZ; /* Offset to Layer Records. */
+ HBUINT16 numLayers; /* Number of Layer Records. */
+ // Version-1 additions
+ Offset32To baseGlyphList;
+ Offset32To layerList;
+ Offset32To clipList; // Offset to ClipList table (may be NULL)
+ Offset32To varIdxMap; // Offset to DeltaSetIndexMap table (may be NULL)
+ Offset32To varStore;
+ public:
+ DEFINE_SIZE_MIN (14);
+};
+
+struct COLR_accelerator_t : COLR::accelerator_t {
+ COLR_accelerator_t (hb_face_t *face) : COLR::accelerator_t (face) {}
+};
+
+void
+hb_paint_context_t::recurse (const Paint &paint)
+{
+ if (unlikely (depth_left <= 0 || edge_count <= 0)) return;
+ depth_left--;
+ edge_count--;
+ paint.dispatch (this);
+ depth_left++;
+}
+
+void PaintColrLayers::paint_glyph (hb_paint_context_t *c) const
+{
+ TRACE_PAINT (this);
+ const LayerList &paint_offset_lists = c->get_colr_table ()->get_layerList ();
+ hb_decycler_node_t node (c->layers_decycler);
+ for (unsigned i = firstLayerIndex; i < firstLayerIndex + numLayers; i++)
+ {
+ if (unlikely (!node.visit (i)))
+ return;
+
+ const Paint &paint = paint_offset_lists.get_paint (i);
+ c->recurse (paint);
+ }
+}
+
+void PaintColrGlyph::paint_glyph (hb_paint_context_t *c) const
+{
+ TRACE_PAINT (this);
+
+ hb_decycler_node_t node (c->glyphs_decycler);
+ if (unlikely (!node.visit (gid)))
+ return;
+
+ c->funcs->push_inverse_font_transform (c->data, c->font);
+ if (c->funcs->color_glyph (c->data, gid, c->font))
+ {
+ c->funcs->pop_transform (c->data);
+ return;
+ }
+ c->funcs->pop_transform (c->data);
+
+ const COLR *colr_table = c->get_colr_table ();
+ const Paint *paint = colr_table->get_base_glyph_paint (gid);
+
+ hb_glyph_extents_t extents = {0};
+ bool has_clip_box = colr_table->get_clip (gid, &extents, c->instancer);
+
+ if (has_clip_box)
+ c->funcs->push_clip_rectangle (c->data,
+ extents.x_bearing,
+ extents.y_bearing + extents.height,
+ extents.x_bearing + extents.width,
+ extents.y_bearing);
+
+ if (paint)
+ c->recurse (*paint);
+
+ if (has_clip_box)
+ c->funcs->pop_clip (c->data);
+}
+
+} /* namespace OT */
+
+#endif /* OT_COLOR_COLR_COLR_HH */
diff --git a/3rdparty/harfbuzz/src/OT/Color/CPAL/CPAL.hh b/3rdparty/harfbuzz/src/OT/Color/CPAL/CPAL.hh
new file mode 100644
index 0000000000..9364fd9395
--- /dev/null
+++ b/3rdparty/harfbuzz/src/OT/Color/CPAL/CPAL.hh
@@ -0,0 +1,367 @@
+/*
+ * Copyright © 2016 Google, Inc.
+ * Copyright © 2018 Ebrahim Byagowi
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Sascha Brawer
+ */
+
+#ifndef OT_COLOR_CPAL_CPAL_HH
+#define OT_COLOR_CPAL_CPAL_HH
+
+#include "../../../hb-open-type.hh"
+#include "../../../hb-ot-color.h"
+#include "../../../hb-ot-name.h"
+
+
+/*
+ * CPAL -- Color Palette
+ * https://docs.microsoft.com/en-us/typography/opentype/spec/cpal
+ */
+#define HB_OT_TAG_CPAL HB_TAG('C','P','A','L')
+
+namespace OT {
+
+
+struct CPALV1Tail
+{
+ friend struct CPAL;
+
+ private:
+ hb_ot_color_palette_flags_t get_palette_flags (const void *base,
+ unsigned int palette_index,
+ unsigned int palette_count) const
+ {
+ if (!paletteFlagsZ) return HB_OT_COLOR_PALETTE_FLAG_DEFAULT;
+ return (hb_ot_color_palette_flags_t) (uint32_t)
+ (base+paletteFlagsZ).as_array (palette_count)[palette_index];
+ }
+
+ hb_ot_name_id_t get_palette_name_id (const void *base,
+ unsigned int palette_index,
+ unsigned int palette_count) const
+ {
+ if (!paletteLabelsZ) return HB_OT_NAME_ID_INVALID;
+ return (base+paletteLabelsZ).as_array (palette_count)[palette_index];
+ }
+
+ hb_ot_name_id_t get_color_name_id (const void *base,
+ unsigned int color_index,
+ unsigned int color_count) const
+ {
+ if (!colorLabelsZ) return HB_OT_NAME_ID_INVALID;
+ return (base+colorLabelsZ).as_array (color_count)[color_index];
+ }
+
+ public:
+ void collect_name_ids (const void *base,
+ unsigned palette_count,
+ unsigned color_count,
+ const hb_map_t *color_index_map,
+ hb_set_t *nameids_to_retain /* OUT */) const
+ {
+ if (paletteLabelsZ)
+ {
+ + (base+paletteLabelsZ).as_array (palette_count)
+ | hb_sink (nameids_to_retain)
+ ;
+ }
+
+ if (colorLabelsZ)
+ {
+ const hb_array_t colorLabels = (base+colorLabelsZ).as_array (color_count);
+ for (unsigned i = 0; i < color_count; i++)
+ {
+ if (!color_index_map->has (i)) continue;
+ nameids_to_retain->add (colorLabels[i]);
+ }
+ }
+ }
+
+ bool serialize (hb_serialize_context_t *c,
+ unsigned palette_count,
+ unsigned color_count,
+ const void *base,
+ const hb_map_t *color_index_map) const
+ {
+ TRACE_SERIALIZE (this);
+ auto *out = c->allocate_size (static_size);
+ if (unlikely (!out)) return_trace (false);
+
+ out->paletteFlagsZ = 0;
+ if (paletteFlagsZ)
+ out->paletteFlagsZ.serialize_copy (c, paletteFlagsZ, base, 0, hb_serialize_context_t::Head, palette_count);
+
+ out->paletteLabelsZ = 0;
+ if (paletteLabelsZ)
+ out->paletteLabelsZ.serialize_copy (c, paletteLabelsZ, base, 0, hb_serialize_context_t::Head, palette_count);
+
+ const hb_array_t colorLabels = (base+colorLabelsZ).as_array (color_count);
+ if (colorLabelsZ)
+ {
+ c->push ();
+ for (unsigned i = 0; i < color_count; i++)
+ {
+ if (!color_index_map->has (i)) continue;
+ if (!c->copy (colorLabels[i]))
+ {
+ c->pop_discard ();
+ return_trace (false);
+ }
+ }
+ c->add_link (out->colorLabelsZ, c->pop_pack ());
+ }
+ return_trace (true);
+ }
+
+ bool sanitize (hb_sanitize_context_t *c,
+ const void *base,
+ unsigned int palette_count,
+ unsigned int color_count) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this) &&
+ (!paletteFlagsZ || (base+paletteFlagsZ).sanitize (c, palette_count)) &&
+ (!paletteLabelsZ || (base+paletteLabelsZ).sanitize (c, palette_count)) &&
+ (!colorLabelsZ || (base+colorLabelsZ).sanitize (c, color_count)));
+ }
+
+ protected:
+ // TODO(garretrieger): these offsets can hold nulls so we should not be using non-null offsets
+ // here. Currently they are needed since UnsizedArrayOf doesn't define null_size
+ NNOffset32To>
+ paletteFlagsZ; /* Offset from the beginning of CPAL table to
+ * the Palette Type Array. Set to 0 if no array
+ * is provided. */
+ NNOffset32To>
+ paletteLabelsZ; /* Offset from the beginning of CPAL table to
+ * the palette labels array. Set to 0 if no
+ * array is provided. */
+ NNOffset32To>
+ colorLabelsZ; /* Offset from the beginning of CPAL table to
+ * the color labels array. Set to 0
+ * if no array is provided. */
+ public:
+ DEFINE_SIZE_STATIC (12);
+};
+
+typedef HBUINT32 BGRAColor;
+
+struct CPAL
+{
+ static constexpr hb_tag_t tableTag = HB_OT_TAG_CPAL;
+
+ bool has_data () const { return numPalettes; }
+
+ size_t get_size () const
+ { return min_size + numPalettes * sizeof (colorRecordIndicesZ[0]); }
+
+ unsigned int get_palette_count () const { return numPalettes; }
+ unsigned int get_color_count () const { return numColors; }
+
+ hb_ot_color_palette_flags_t get_palette_flags (unsigned int palette_index) const
+ { return v1 ().get_palette_flags (this, palette_index, numPalettes); }
+
+ hb_ot_name_id_t get_palette_name_id (unsigned int palette_index) const
+ { return v1 ().get_palette_name_id (this, palette_index, numPalettes); }
+
+ hb_ot_name_id_t get_color_name_id (unsigned int color_index) const
+ { return v1 ().get_color_name_id (this, color_index, numColors); }
+
+ hb_array_t get_palette_colors (unsigned int palette_index) const
+ {
+ if (unlikely (palette_index >= numPalettes))
+ return hb_array_t ();
+ unsigned int start_index = colorRecordIndicesZ[palette_index];
+ hb_array_t all_colors ((this+colorRecordsZ).arrayZ, numColorRecords);
+ return all_colors.sub_array (start_index, numColors);
+ }
+ unsigned int get_palette_colors (unsigned int palette_index,
+ unsigned int start_offset,
+ unsigned int *color_count, /* IN/OUT. May be NULL. */
+ hb_color_t *colors /* OUT. May be NULL. */) const
+ {
+ if (unlikely (palette_index >= numPalettes))
+ {
+ if (color_count) *color_count = 0;
+ return 0;
+ }
+ unsigned int start_index = colorRecordIndicesZ[palette_index];
+ hb_array_t all_colors ((this+colorRecordsZ).arrayZ, numColorRecords);
+ hb_array_t palette_colors = all_colors.sub_array (start_index,
+ numColors);
+ if (color_count)
+ {
+ + palette_colors.sub_array (start_offset, color_count)
+ | hb_sink (hb_array (colors, *color_count))
+ ;
+ }
+ return numColors;
+ }
+
+ void collect_name_ids (const hb_map_t *color_index_map,
+ hb_set_t *nameids_to_retain /* OUT */) const
+ {
+ if (version == 1)
+ {
+ hb_barrier ();
+ v1 ().collect_name_ids (this, numPalettes, numColors, color_index_map, nameids_to_retain);
+ }
+ }
+
+ private:
+ const CPALV1Tail& v1 () const
+ {
+ if (version == 0) return Null (CPALV1Tail);
+ hb_barrier ();
+ return StructAfter (*this);
+ }
+
+ public:
+ bool serialize (hb_serialize_context_t *c,
+ const hb_array_t &color_record_indices,
+ const hb_array_t &color_records,
+ const hb_vector_t& first_color_index_for_layer,
+ const hb_map_t& first_color_to_layer_index,
+ const hb_set_t &retained_color_indices) const
+ {
+ TRACE_SERIALIZE (this);
+
+ // TODO(grieger): limit total final size.
+
+ for (const auto idx : color_record_indices)
+ {
+ hb_codepoint_t layer_index = first_color_to_layer_index[idx];
+
+ HBUINT16 new_idx;
+ new_idx = layer_index * retained_color_indices.get_population ();
+ if (!c->copy (new_idx)) return_trace (false);
+ }
+
+ c->push ();
+ for (unsigned first_color_index : first_color_index_for_layer)
+ {
+ for (hb_codepoint_t color_index : retained_color_indices)
+ {
+ if (!c->copy (color_records[first_color_index + color_index]))
+ {
+ c->pop_discard ();
+ return_trace (false);
+ }
+ }
+ }
+
+ c->add_link (colorRecordsZ, c->pop_pack ());
+ return_trace (true);
+ }
+
+ bool subset (hb_subset_context_t *c) const
+ {
+ TRACE_SUBSET (this);
+ if (!numPalettes) return_trace (false);
+
+ const hb_map_t *color_index_map = &c->plan->colr_palettes;
+ if (color_index_map->is_empty ()) return_trace (false);
+
+ hb_set_t retained_color_indices;
+ for (const auto _ : color_index_map->keys ())
+ {
+ if (_ == 0xFFFF) continue;
+ retained_color_indices.add (_);
+ }
+ if (retained_color_indices.is_empty ()) return_trace (false);
+
+ auto *out = c->serializer->start_embed (*this);
+ if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
+
+
+ out->version = version;
+ out->numColors = retained_color_indices.get_population ();
+ out->numPalettes = numPalettes;
+
+ hb_vector_t first_color_index_for_layer;
+ hb_map_t first_color_to_layer_index;
+
+ const hb_array_t colorRecordIndices = colorRecordIndicesZ.as_array (numPalettes);
+ for (const auto first_color_record_idx : colorRecordIndices)
+ {
+ if (first_color_to_layer_index.has (first_color_record_idx)) continue;
+
+ first_color_index_for_layer.push (first_color_record_idx);
+ if (unlikely (!c->serializer->propagate_error (first_color_index_for_layer))) return_trace (false);
+ first_color_to_layer_index.set (first_color_record_idx,
+ first_color_index_for_layer.length - 1);
+ }
+
+ out->numColorRecords = first_color_index_for_layer.length
+ * retained_color_indices.get_population ();
+
+ const hb_array_t color_records = (this+colorRecordsZ).as_array (numColorRecords);
+ if (!out->serialize (c->serializer,
+ colorRecordIndices,
+ color_records,
+ first_color_index_for_layer,
+ first_color_to_layer_index,
+ retained_color_indices))
+ return_trace (false);
+
+ if (version == 1)
+ {
+ hb_barrier ();
+ return_trace (v1 ().serialize (c->serializer, numPalettes, numColors, this, color_index_map));
+ }
+
+ return_trace (true);
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this) &&
+ hb_barrier () &&
+ (this+colorRecordsZ).sanitize (c, numColorRecords) &&
+ colorRecordIndicesZ.sanitize (c, numPalettes) &&
+ (version == 0 || v1 ().sanitize (c, this, numPalettes, numColors)));
+ }
+
+ protected:
+ HBUINT16 version; /* Table version number */
+ /* Version 0 */
+ HBUINT16 numColors; /* Number of colors in each palette. */
+ HBUINT16 numPalettes; /* Number of palettes in the table. */
+ HBUINT16 numColorRecords; /* Total number of color records, combined for
+ * all palettes. */
+ NNOffset32To>
+ colorRecordsZ; /* Offset from the beginning of CPAL table to
+ * the first ColorRecord. */
+ UnsizedArrayOf
+ colorRecordIndicesZ; /* Index of each palette’s first color record in
+ * the combined color record array. */
+/*CPALV1Tail v1;*/
+ public:
+ DEFINE_SIZE_ARRAY (12, colorRecordIndicesZ);
+};
+
+} /* namespace OT */
+
+
+#endif /* OT_COLOR_CPAL_CPAL_HH */
diff --git a/3rdparty/harfbuzz/src/OT/Color/sbix/sbix.hh b/3rdparty/harfbuzz/src/OT/Color/sbix/sbix.hh
new file mode 100644
index 0000000000..b5760053a3
--- /dev/null
+++ b/3rdparty/harfbuzz/src/OT/Color/sbix/sbix.hh
@@ -0,0 +1,449 @@
+/*
+ * Copyright © 2018 Ebrahim Byagowi
+ * Copyright © 2020 Google, Inc.
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Calder Kitagawa
+ */
+
+#ifndef OT_COLOR_SBIX_SBIX_HH
+#define OT_COLOR_SBIX_SBIX_HH
+
+#include "../../../hb-open-type.hh"
+#include "../../../hb-paint.hh"
+
+/*
+ * sbix -- Standard Bitmap Graphics
+ * https://docs.microsoft.com/en-us/typography/opentype/spec/sbix
+ * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6sbix.html
+ */
+#define HB_OT_TAG_sbix HB_TAG('s','b','i','x')
+
+
+namespace OT {
+
+
+struct SBIXGlyph
+{
+ SBIXGlyph* copy (hb_serialize_context_t *c, unsigned int data_length) const
+ {
+ TRACE_SERIALIZE (this);
+ SBIXGlyph* new_glyph = c->start_embed ();
+ if (unlikely (!c->extend_min (new_glyph))) return_trace (nullptr);
+
+ new_glyph->xOffset = xOffset;
+ new_glyph->yOffset = yOffset;
+ new_glyph->graphicType = graphicType;
+ data.copy (c, data_length);
+ return_trace (new_glyph);
+ }
+
+ HBINT16 xOffset; /* The horizontal (x-axis) offset from the left
+ * edge of the graphic to the glyph’s origin.
+ * That is, the x-coordinate of the point on the
+ * baseline at the left edge of the glyph. */
+ HBINT16 yOffset; /* The vertical (y-axis) offset from the bottom
+ * edge of the graphic to the glyph’s origin.
+ * That is, the y-coordinate of the point on the
+ * baseline at the left edge of the glyph. */
+ Tag graphicType; /* Indicates the format of the embedded graphic
+ * data: one of 'jpg ', 'png ' or 'tiff', or the
+ * special format 'dupe'. */
+ UnsizedArrayOf
+ data; /* The actual embedded graphic data. The total
+ * length is inferred from sequential entries in
+ * the glyphDataOffsets array and the fixed size
+ * (8 bytes) of the preceding fields. */
+ public:
+ DEFINE_SIZE_ARRAY (8, data);
+};
+
+struct SBIXStrike
+{
+ static size_t get_size (unsigned num_glyphs)
+ { return min_size + num_glyphs * HBUINT32::static_size; }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this) &&
+ imageOffsetsZ.sanitize_shallow (c, c->get_num_glyphs () + 1));
+ }
+
+ hb_blob_t *get_glyph_blob (unsigned int glyph_id,
+ hb_blob_t *sbix_blob,
+ hb_tag_t file_type,
+ int *x_offset,
+ int *y_offset,
+ unsigned int num_glyphs,
+ unsigned int *strike_ppem) const
+ {
+ if (unlikely (!ppem)) return hb_blob_get_empty (); /* To get Null() object out of the way. */
+
+ unsigned int retry_count = 8;
+ unsigned int sbix_len = sbix_blob->length;
+ unsigned int strike_offset = (const char *) this - (const char *) sbix_blob->data;
+ assert (strike_offset < sbix_len);
+
+ retry:
+ if (unlikely (glyph_id >= num_glyphs ||
+ imageOffsetsZ[glyph_id + 1] <= imageOffsetsZ[glyph_id] ||
+ imageOffsetsZ[glyph_id + 1] - imageOffsetsZ[glyph_id] <= SBIXGlyph::min_size ||
+ (unsigned int) imageOffsetsZ[glyph_id + 1] > sbix_len - strike_offset))
+ return hb_blob_get_empty ();
+
+ unsigned int glyph_offset = strike_offset + (unsigned int) imageOffsetsZ[glyph_id] + SBIXGlyph::min_size;
+ unsigned int glyph_length = imageOffsetsZ[glyph_id + 1] - imageOffsetsZ[glyph_id] - SBIXGlyph::min_size;
+
+ const SBIXGlyph *glyph = &(this+imageOffsetsZ[glyph_id]);
+
+ if (glyph->graphicType == HB_TAG ('d','u','p','e'))
+ {
+ if (glyph_length >= 2)
+ {
+ glyph_id = *((HBUINT16 *) &glyph->data);
+ if (retry_count--)
+ goto retry;
+ }
+ return hb_blob_get_empty ();
+ }
+
+ if (unlikely (file_type != glyph->graphicType))
+ return hb_blob_get_empty ();
+
+ if (strike_ppem) *strike_ppem = ppem;
+ if (x_offset) *x_offset = glyph->xOffset;
+ if (y_offset) *y_offset = glyph->yOffset;
+ return hb_blob_create_sub_blob (sbix_blob, glyph_offset, glyph_length);
+ }
+
+ bool subset (hb_subset_context_t *c, unsigned int available_len) const
+ {
+ TRACE_SUBSET (this);
+ unsigned int num_output_glyphs = c->plan->num_output_glyphs ();
+
+ auto* out = c->serializer->start_embed ();
+ auto snap = c->serializer->snapshot ();
+ if (unlikely (!c->serializer->extend (out, num_output_glyphs + 1))) return_trace (false);
+ out->ppem = ppem;
+ out->resolution = resolution;
+ HBUINT32 head;
+ head = get_size (num_output_glyphs + 1);
+
+ bool has_glyphs = false;
+ for (unsigned new_gid = 0; new_gid < num_output_glyphs; new_gid++)
+ {
+ hb_codepoint_t old_gid;
+ if (!c->plan->old_gid_for_new_gid (new_gid, &old_gid) ||
+ unlikely (imageOffsetsZ[old_gid].is_null () ||
+ imageOffsetsZ[old_gid + 1].is_null () ||
+ imageOffsetsZ[old_gid + 1] <= imageOffsetsZ[old_gid] ||
+ imageOffsetsZ[old_gid + 1] - imageOffsetsZ[old_gid] <= SBIXGlyph::min_size) ||
+ (unsigned int) imageOffsetsZ[old_gid + 1] > available_len)
+ {
+ out->imageOffsetsZ[new_gid] = head;
+ continue;
+ }
+ has_glyphs = true;
+ unsigned int delta = imageOffsetsZ[old_gid + 1] - imageOffsetsZ[old_gid];
+ unsigned int glyph_data_length = delta - SBIXGlyph::min_size;
+ if (!(this+imageOffsetsZ[old_gid]).copy (c->serializer, glyph_data_length))
+ return_trace (false);
+ out->imageOffsetsZ[new_gid] = head;
+ head += delta;
+ }
+ if (has_glyphs)
+ out->imageOffsetsZ[num_output_glyphs] = head;
+ else
+ c->serializer->revert (snap);
+ return_trace (has_glyphs);
+ }
+
+ public:
+ HBUINT16 ppem; /* The PPEM size for which this strike was designed. */
+ HBUINT16 resolution; /* The device pixel density (in PPI) for which this
+ * strike was designed. (E.g., 96 PPI, 192 PPI.) */
+ protected:
+ UnsizedArrayOf>
+ imageOffsetsZ; /* Offset from the beginning of the strike data header
+ * to bitmap data for an individual glyph ID. */
+ public:
+ DEFINE_SIZE_ARRAY (4, imageOffsetsZ);
+};
+
+struct sbix
+{
+ static constexpr hb_tag_t tableTag = HB_OT_TAG_sbix;
+
+ bool has_data () const { return version; }
+
+ const SBIXStrike &get_strike (unsigned int i) const { return this+strikes[i]; }
+
+ struct accelerator_t
+ {
+ accelerator_t (hb_face_t *face)
+ {
+ table = hb_sanitize_context_t ().reference_table (face);
+ num_glyphs = face->get_num_glyphs ();
+ }
+ ~accelerator_t () { table.destroy (); }
+
+ bool has_data () const { return table->has_data (); }
+
+ bool get_extents (hb_font_t *font,
+ hb_codepoint_t glyph,
+ hb_glyph_extents_t *extents,
+ bool scale = true) const
+ {
+ /* We only support PNG right now, and following function checks type. */
+ return get_png_extents (font, glyph, extents, scale);
+ }
+
+ hb_blob_t *reference_png (hb_font_t *font,
+ hb_codepoint_t glyph_id,
+ int *x_offset,
+ int *y_offset,
+ unsigned int *available_ppem) const
+ {
+ return choose_strike (font).get_glyph_blob (glyph_id, table.get_blob (),
+ HB_TAG ('p','n','g',' '),
+ x_offset, y_offset,
+ num_glyphs, available_ppem);
+ }
+
+ bool paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data) const
+ {
+ if (!has_data ())
+ return false;
+
+ int x_offset = 0, y_offset = 0;
+ unsigned int strike_ppem = 0;
+ hb_glyph_extents_t extents;
+ hb_glyph_extents_t pixel_extents;
+
+ if (!font->get_glyph_extents (glyph, &extents, false))
+ return false;
+
+ if (unlikely (!get_extents (font, glyph, &pixel_extents, false)))
+ return false;
+
+ hb_blob_t *blob = reference_png (font, glyph, &x_offset, &y_offset, &strike_ppem);
+ if (hb_blob_is_immutable (blob))
+ return false;
+
+ bool ret = funcs->image (data,
+ blob,
+ pixel_extents.width, -pixel_extents.height,
+ HB_PAINT_IMAGE_FORMAT_PNG,
+ 0.f,
+ &extents);
+
+ hb_blob_destroy (blob);
+
+ return ret;
+ }
+
+ private:
+
+ const SBIXStrike &choose_strike (hb_font_t *font) const
+ {
+ unsigned count = table->strikes.len;
+ if (unlikely (!count))
+ return Null (SBIXStrike);
+
+ unsigned int requested_ppem = hb_max (font->x_ppem, font->y_ppem);
+ if (!requested_ppem)
+ requested_ppem = 1<<30; /* Choose largest strike. */
+ /* TODO Add DPI sensitivity as well? */
+ unsigned int best_i = 0;
+ unsigned int best_ppem = table->get_strike (0).ppem;
+
+ for (unsigned int i = 1; i < count; i++)
+ {
+ unsigned int ppem = (table->get_strike (i)).ppem;
+ if ((requested_ppem <= ppem && ppem < best_ppem) ||
+ (requested_ppem > best_ppem && ppem > best_ppem))
+ {
+ best_i = i;
+ best_ppem = ppem;
+ }
+ }
+
+ return table->get_strike (best_i);
+ }
+
+ struct PNGHeader
+ {
+ HBUINT8 signature[8];
+ struct
+ {
+ struct
+ {
+ HBUINT32 length;
+ Tag type;
+ } header;
+ HBUINT32 width;
+ HBUINT32 height;
+ HBUINT8 bitDepth;
+ HBUINT8 colorType;
+ HBUINT8 compressionMethod;
+ HBUINT8 filterMethod;
+ HBUINT8 interlaceMethod;
+ } IHDR;
+
+ public:
+ DEFINE_SIZE_STATIC (29);
+ };
+
+ bool get_png_extents (hb_font_t *font,
+ hb_codepoint_t glyph,
+ hb_glyph_extents_t *extents,
+ bool scale = true) const
+ {
+ /* Following code is safe to call even without data.
+ * But faster to short-circuit. */
+ if (!has_data ())
+ return false;
+
+ int x_offset = 0, y_offset = 0;
+ unsigned int strike_ppem = 0;
+ hb_blob_t *blob = reference_png (font, glyph, &x_offset, &y_offset, &strike_ppem);
+
+ const PNGHeader &png = *blob->as();
+
+ if (png.IHDR.height >= 65536 || png.IHDR.width >= 65536)
+ {
+ hb_blob_destroy (blob);
+ return false;
+ }
+
+ extents->x_bearing = x_offset;
+ extents->y_bearing = png.IHDR.height + y_offset;
+ extents->width = png.IHDR.width;
+ extents->height = -1 * png.IHDR.height;
+
+ /* Convert to font units. */
+ if (strike_ppem && scale)
+ {
+ float scale = font->face->get_upem () / (float) strike_ppem;
+ extents->x_bearing = roundf (extents->x_bearing * scale);
+ extents->y_bearing = roundf (extents->y_bearing * scale);
+ extents->width = roundf (extents->width * scale);
+ extents->height = roundf (extents->height * scale);
+ }
+
+ if (scale)
+ font->scale_glyph_extents (extents);
+
+ hb_blob_destroy (blob);
+
+ return strike_ppem;
+ }
+
+ private:
+ hb_blob_ptr_t table;
+
+ unsigned int num_glyphs;
+ };
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (likely (c->check_struct (this) &&
+ hb_barrier () &&
+ version >= 1 &&
+ strikes.sanitize (c, this)));
+ }
+
+ bool
+ add_strike (hb_subset_context_t *c, unsigned i) const
+ {
+ if (strikes[i].is_null () || c->source_blob->length < (unsigned) strikes[i])
+ return false;
+
+ return (this+strikes[i]).subset (c, c->source_blob->length - (unsigned) strikes[i]);
+ }
+
+ bool serialize_strike_offsets (hb_subset_context_t *c) const
+ {
+ TRACE_SERIALIZE (this);
+
+ auto *out = c->serializer->start_embed> ();
+ if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
+
+ hb_vector_t*> new_strikes;
+ hb_vector_t objidxs;
+ for (int i = strikes.len - 1; i >= 0; --i)
+ {
+ auto* o = out->serialize_append (c->serializer);
+ if (unlikely (!o)) return_trace (false);
+ *o = 0;
+ auto snap = c->serializer->snapshot ();
+ c->serializer->push ();
+ bool ret = add_strike (c, i);
+ if (!ret)
+ {
+ c->serializer->pop_discard ();
+ out->pop ();
+ c->serializer->revert (snap);
+ }
+ else
+ {
+ objidxs.push (c->serializer->pop_pack ());
+ new_strikes.push (o);
+ }
+ }
+ for (unsigned int i = 0; i < new_strikes.length; ++i)
+ c->serializer->add_link (*new_strikes[i], objidxs[new_strikes.length - 1 - i]);
+
+ return_trace (true);
+ }
+
+ bool subset (hb_subset_context_t* c) const
+ {
+ TRACE_SUBSET (this);
+
+ if (unlikely (!c->serializer->embed (this->version))) return_trace (false);
+ if (unlikely (!c->serializer->embed (this->flags))) return_trace (false);
+
+ return_trace (serialize_strike_offsets (c));
+ }
+
+ protected:
+ HBUINT16 version; /* Table version number — set to 1 */
+ HBUINT16 flags; /* Bit 0: Set to 1. Bit 1: Draw outlines.
+ * Bits 2 to 15: reserved (set to 0). */
+ Array32OfOffset32To
+ strikes; /* Offsets from the beginning of the 'sbix'
+ * table to data for each individual bitmap strike. */
+ public:
+ DEFINE_SIZE_ARRAY (8, strikes);
+};
+
+struct sbix_accelerator_t : sbix::accelerator_t {
+ sbix_accelerator_t (hb_face_t *face) : sbix::accelerator_t (face) {}
+};
+
+
+} /* namespace OT */
+
+#endif /* OT_COLOR_SBIX_SBIX_HH */
diff --git a/3rdparty/harfbuzz/src/OT/Color/svg/svg.hh b/3rdparty/harfbuzz/src/OT/Color/svg/svg.hh
new file mode 100644
index 0000000000..b89894c64a
--- /dev/null
+++ b/3rdparty/harfbuzz/src/OT/Color/svg/svg.hh
@@ -0,0 +1,829 @@
+/*
+ * Copyright © 2018 Ebrahim Byagowi
+ *
+ * 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.
+ */
+
+#ifndef OT_COLOR_SVG_SVG_HH
+#define OT_COLOR_SVG_SVG_HH
+
+#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
+#include
+
+/*
+ * SVG -- SVG (Scalable Vector Graphics)
+ * https://docs.microsoft.com/en-us/typography/opentype/spec/svg
+ */
+
+#define HB_OT_TAG_SVG HB_TAG('S','V','G',' ')
+
+
+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,
+ index_offset + (unsigned int) svgDoc,
+ svgDocLength);
+ }
+
+ bool sanitize (hb_sanitize_context_t *c, const void *base) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this) &&
+ hb_barrier () &&
+ svgDoc.sanitize (c, base, svgDocLength));
+ }
+
+ protected:
+ HBUINT16 startGlyphID; /* The first glyph ID in the range described by
+ * this index entry. */
+ HBUINT16 endGlyphID; /* The last glyph ID in the range described by
+ * this index entry. Must be >= startGlyphID. */
+ NNOffset32To>
+ svgDoc; /* Offset from the beginning of the SVG Document Index
+ * to an SVG document. Must be non-zero. */
+ HBUINT32 svgDocLength; /* Length of the SVG document.
+ * Must be non-zero. */
+ public:
+ DEFINE_SIZE_STATIC (12);
+};
+
+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 defs_entries;
+ hb_codepoint_t start_glyph = HB_CODEPOINT_INVALID;
+ hb_codepoint_t end_glyph = HB_CODEPOINT_INVALID;
+ hb_vector_t> glyph_spans;
+ hb_hashmap_t> id_spans;
+ };
+
+ bool has_data () const { return svgDocEntries; }
+
+ struct accelerator_t
+ {
+ accelerator_t (hb_face_t *face);
+ ~accelerator_t ();
+
+ hb_blob_t *reference_blob_for_glyph (hb_codepoint_t glyph_id) const
+ {
+ return table->get_glyph_entry (glyph_id).reference_blob (table.get_blob (),
+ 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 *
+ 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 ())
+ return false;
+
+ hb_blob_t *blob = reference_blob_for_glyph (glyph);
+
+ if (blob == hb_blob_get_empty ())
+ return false;
+
+ bool ret = funcs->image (data,
+ blob,
+ 0, 0,
+ HB_PAINT_IMAGE_FORMAT_SVG,
+ 0.f,
+ nullptr);
+
+ hb_blob_destroy (blob);
+
+ return ret;
+ }
+
+ 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 table;
+ mutable hb_vector_t> doc_caches;
+ public:
+ DEFINE_SIZE_STATIC (sizeof (hb_blob_ptr_t) +
+ sizeof (hb_vector_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);
+ return_trace (likely (c->check_struct (this) &&
+ (this+svgDocEntries).sanitize_shallow (c)));
+ }
+
+ protected:
+ HBUINT16 version; /* Table version (starting at 0). */
+ Offset32To>
+ svgDocEntries; /* Offset (relative to the start of the SVG table) to the
+ * SVG Documents Index. Must be non-zero. */
+ /* Array of SVG Document Index Entries. */
+ HBUINT32 reserved; /* Set to 0. */
+ public:
+ 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 *defs_entries,
+ hb_vector_t *glyph_spans,
+ hb_vector_t *id_entries)
+{
+ open_elem_t stack[MAX_DEPTH] = {};
+ unsigned depth = 0;
+ if (unlikely (!defs_entries->alloc (256) ||
+ !glyph_spans->alloc (256) ||
+ !id_entries->alloc (256)))
+ return false;
+
+ unsigned defs_depth = 0;
+ unsigned i = 0;
+ while (i < len)
+ {
+ if (svg[i] != '<')
+ {
+ i++;
+ continue;
+ }
+
+ if (i + 4 <= len && !memcmp (svg + i, "", 3);
+ if (cend < 0) return false;
+ i = (unsigned) cend + 3;
+ continue;
+ }
+ if (i + 9 <= len && !memcmp (svg + i, "", 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)
+ {
+ if (unlikely (!id_entries->push_or_fail (id_entry_t {e.id, (uint32_t) e.start, (uint32_t) end})))
+ return false;
+
+ if (e.in_defs_content)
+ {
+ if (unlikely (!defs_entries->push_or_fail ()))
+ return false;
+ auto &slot = defs_entries->tail ();
+ slot.id = e.id;
+ slot.start = e.start;
+ slot.end = end;
+ }
+
+ hb_codepoint_t gid;
+ if (parse_glyph_id_span (e.id, &gid))
+ {
+ if (unlikely (!glyph_spans->push_or_fail (glyph_entry_t {gid, (uint32_t) e.start, (uint32_t) end})))
+ return false;
+ }
+ }
+
+ if (e.is_defs && defs_depth)
+ defs_depth--;
+
+ i = end;
+ continue;
+ }
+
+ SVG::svg_id_span_t id = {};
+ 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 = {};
+ e.start = i;
+ e.id = id;
+ e.in_defs_content = defs_depth > 0;
+ e.is_defs = is_defs;
+
+ if (self_closing)
+ {
+ unsigned end = gt + 1;
+ if (e.id.len)
+ {
+ if (unlikely (!id_entries->push_or_fail (id_entry_t {e.id, (uint32_t) e.start, (uint32_t) end})))
+ return false;
+
+ if (e.in_defs_content)
+ {
+ if (unlikely (!defs_entries->push_or_fail ()))
+ return false;
+ auto &slot = defs_entries->tail ();
+ slot.id = e.id;
+ slot.start = e.start;
+ slot.end = end;
+ }
+
+ hb_codepoint_t gid;
+ if (parse_glyph_id_span (e.id, &gid))
+ {
+ if (unlikely (!glyph_spans->push_or_fail (glyph_entry_t {gid, (uint32_t) e.start, (uint32_t) end})))
+ return false;
+ }
+ }
+ }
+ 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 (face);
+ doc_caches.init ();
+ unsigned doc_count = table->get_document_count ();
+ if (doc_count && unlikely (!doc_caches.resize (doc_count)))
+ doc_caches.clear ();
+ 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 (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 (span.start, span.end);
+ }
+
+ for (unsigned i = 0; i < id_entries.length; i++)
+ {
+ const auto &e = id_entries.arrayZ[i];
+ hb_pair_t *out = nullptr;
+ if (doc->id_spans.has (e.id, &out))
+ continue;
+ if (unlikely (!doc->id_spans.set (e.id, hb_pair_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::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 (unlikely (span.first > span.second || span.second > doc->len))
+ 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 *span = nullptr;
+ if (!doc->id_spans.has (id, &span))
+ return false;
+ if (unlikely (span->first > span->second || span->second > doc->len))
+ 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) {}
+};
+
+} /* namespace OT */
+
+
+#endif /* OT_COLOR_SVG_SVG_HH */
diff --git a/3rdparty/harfbuzz/src/OT/Layout/Common/Coverage.hh b/3rdparty/harfbuzz/src/OT/Layout/Common/Coverage.hh
new file mode 100644
index 0000000000..93ca1b2f7a
--- /dev/null
+++ b/3rdparty/harfbuzz/src/OT/Layout/Common/Coverage.hh
@@ -0,0 +1,394 @@
+/*
+ * Copyright © 2007,2008,2009 Red Hat, Inc.
+ * Copyright © 2010,2012 Google, Inc.
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ * Google Author(s): Behdad Esfahbod, Garret Rieger
+ */
+
+#ifndef OT_LAYOUT_COMMON_COVERAGE_HH
+#define OT_LAYOUT_COMMON_COVERAGE_HH
+
+#include "../types.hh"
+#include "CoverageFormat1.hh"
+#include "CoverageFormat2.hh"
+
+namespace OT {
+namespace Layout {
+namespace Common {
+
+template
+static inline void Coverage_serialize (hb_serialize_context_t *c,
+ Iterator it);
+
+struct Coverage
+{
+
+ protected:
+ union {
+ struct { HBUINT16 v; } format; /* Format identifier */
+ CoverageFormat1_3 format1;
+ CoverageFormat2_4 format2;
+#ifndef HB_NO_BEYOND_64K
+ CoverageFormat1_3format3;
+ CoverageFormat2_4format4;
+#endif
+ } u;
+ public:
+ DEFINE_SIZE_UNION (2, format.v);
+
+#ifndef HB_OPTIMIZE_SIZE
+ HB_ALWAYS_INLINE
+#endif
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ if (!u.format.v.sanitize (c)) return_trace (false);
+ hb_barrier ();
+ switch (u.format.v)
+ {
+ case 1: return_trace (u.format1.sanitize (c));
+ case 2: return_trace (u.format2.sanitize (c));
+#ifndef HB_NO_BEYOND_64K
+ case 3: return_trace (u.format3.sanitize (c));
+ case 4: return_trace (u.format4.sanitize (c));
+#endif
+ default:return_trace (true);
+ }
+ }
+
+ /* Has interface. */
+ unsigned operator [] (hb_codepoint_t k) const { return get (k); }
+ bool has (hb_codepoint_t k) const { return (*this)[k] != NOT_COVERED; }
+ /* Predicate. */
+ bool operator () (hb_codepoint_t k) const { return has (k); }
+
+ unsigned int get (hb_codepoint_t k) const { return get_coverage (k); }
+ unsigned int get_coverage (hb_codepoint_t glyph_id) const
+ {
+ switch (u.format.v) {
+ case 1: return u.format1.get_coverage (glyph_id);
+ case 2: return u.format2.get_coverage (glyph_id);
+#ifndef HB_NO_BEYOND_64K
+ case 3: return u.format3.get_coverage (glyph_id);
+ case 4: return u.format4.get_coverage (glyph_id);
+#endif
+ default:return NOT_COVERED;
+ }
+ }
+ unsigned int get_coverage (hb_codepoint_t glyph_id,
+ hb_ot_layout_mapping_cache_t *cache) const
+ {
+ unsigned coverage;
+ if (cache && cache->get (glyph_id, &coverage)) return coverage < cache->MAX_VALUE ? coverage : NOT_COVERED;
+ coverage = get_coverage (glyph_id);
+ if (cache) {
+ if (coverage == NOT_COVERED)
+ cache->set_unchecked (glyph_id, cache->MAX_VALUE);
+ else if (likely (coverage < cache->MAX_VALUE))
+ cache->set_unchecked (glyph_id, coverage);
+ }
+ return coverage;
+ }
+
+ unsigned int get_coverage_binary (hb_codepoint_t glyph_id,
+ hb_ot_layout_binary_cache_t *cache) const
+ {
+ unsigned coverage;
+ if (cache && cache->get (glyph_id, &coverage)) return coverage < cache->MAX_VALUE ? coverage : NOT_COVERED;
+ coverage = get_coverage (glyph_id);
+ if (cache) {
+ if (coverage == NOT_COVERED)
+ cache->set_unchecked (glyph_id, cache->MAX_VALUE);
+ else
+ cache->set_unchecked (glyph_id, 0);
+ }
+ return coverage;
+ }
+
+ unsigned get_population () const
+ {
+ switch (u.format.v) {
+ case 1: return u.format1.get_population ();
+ case 2: return u.format2.get_population ();
+#ifndef HB_NO_BEYOND_64K
+ case 3: return u.format3.get_population ();
+ case 4: return u.format4.get_population ();
+#endif
+ default:return NOT_COVERED;
+ }
+ }
+
+ template
+ bool serialize (hb_serialize_context_t *c, Iterator glyphs)
+ {
+ TRACE_SERIALIZE (this);
+ if (unlikely (!c->extend_min (this))) return_trace (false);
+
+ unsigned count = hb_len (glyphs);
+ unsigned num_ranges = 0;
+ hb_codepoint_t last = (hb_codepoint_t) -2;
+ hb_codepoint_t max = 0;
+ bool unsorted = false;
+ for (auto g: glyphs)
+ {
+ if (last != (hb_codepoint_t) -2 && g < last)
+ unsorted = true;
+ if (last + 1 != g)
+ num_ranges++;
+ last = g;
+ if (g > max) max = g;
+ }
+ u.format.v = !unsorted && count <= num_ranges * 3 ? 1 : 2;
+
+#ifndef HB_NO_BEYOND_64K
+ if (max > 0xFFFFu)
+ u.format.v += 2;
+ if (unlikely (max > 0xFFFFFFu))
+#else
+ if (unlikely (max > 0xFFFFu))
+#endif
+ {
+ c->check_success (false, HB_SERIALIZE_ERROR_INT_OVERFLOW);
+ return_trace (false);
+ }
+
+ switch (u.format.v)
+ {
+ case 1: return_trace (u.format1.serialize (c, glyphs));
+ case 2: return_trace (u.format2.serialize (c, glyphs));
+#ifndef HB_NO_BEYOND_64K
+ case 3: return_trace (u.format3.serialize (c, glyphs));
+ case 4: return_trace (u.format4.serialize (c, glyphs));
+#endif
+ default:return_trace (false);
+ }
+ }
+
+ bool subset (hb_subset_context_t *c) const
+ {
+ TRACE_SUBSET (this);
+ auto it =
+ + iter ()
+ | hb_take (c->plan->source->get_num_glyphs ())
+ | hb_map_retains_sorting (c->plan->glyph_map_gsub)
+ | hb_filter ([] (hb_codepoint_t glyph) { return glyph != HB_MAP_VALUE_INVALID; })
+ ;
+
+ // Cache the iterator result as it will be iterated multiple times
+ // by the serialize code below.
+ hb_sorted_vector_t glyphs (it);
+ Coverage_serialize (c->serializer, glyphs.iter ());
+ return_trace (bool (glyphs));
+ }
+
+ bool intersects (const hb_set_t *glyphs) const
+ {
+ switch (u.format.v)
+ {
+ case 1: return u.format1.intersects (glyphs);
+ case 2: return u.format2.intersects (glyphs);
+#ifndef HB_NO_BEYOND_64K
+ case 3: return u.format3.intersects (glyphs);
+ case 4: return u.format4.intersects (glyphs);
+#endif
+ default:return false;
+ }
+ }
+ bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const
+ {
+ switch (u.format.v)
+ {
+ case 1: return u.format1.intersects_coverage (glyphs, index);
+ case 2: return u.format2.intersects_coverage (glyphs, index);
+#ifndef HB_NO_BEYOND_64K
+ case 3: return u.format3.intersects_coverage (glyphs, index);
+ case 4: return u.format4.intersects_coverage (glyphs, index);
+#endif
+ default:return false;
+ }
+ }
+
+ unsigned cost () const
+ {
+ switch (u.format.v) {
+ case 1: hb_barrier (); return u.format1.cost ();
+ case 2: hb_barrier (); return u.format2.cost ();
+#ifndef HB_NO_BEYOND_64K
+ case 3: hb_barrier (); return u.format3.cost ();
+ case 4: hb_barrier (); return u.format4.cost ();
+#endif
+ default:return 0u;
+ }
+ }
+
+ /* Might return false if array looks unsorted.
+ * Used for faster rejection of corrupt data. */
+ template
+ bool collect_coverage (set_t *glyphs) const
+ {
+ switch (u.format.v)
+ {
+ case 1: return u.format1.collect_coverage (glyphs);
+ case 2: return u.format2.collect_coverage (glyphs);
+#ifndef HB_NO_BEYOND_64K
+ case 3: return u.format3.collect_coverage (glyphs);
+ case 4: return u.format4.collect_coverage (glyphs);
+#endif
+ default:return false;
+ }
+ }
+
+ template
+ void intersect_set (const hb_set_t &glyphs, IterableOut&& intersect_glyphs) const
+ {
+ switch (u.format.v)
+ {
+ case 1: return u.format1.intersect_set (glyphs, intersect_glyphs);
+ case 2: return u.format2.intersect_set (glyphs, intersect_glyphs);
+#ifndef HB_NO_BEYOND_64K
+ case 3: return u.format3.intersect_set (glyphs, intersect_glyphs);
+ case 4: return u.format4.intersect_set (glyphs, intersect_glyphs);
+#endif
+ default:return ;
+ }
+ }
+
+ struct iter_t : hb_iter_with_fallback_t
+ {
+ static constexpr bool is_sorted_iterator = true;
+ iter_t (const Coverage &c_ = Null (Coverage))
+ {
+ hb_memset (this, 0, sizeof (*this));
+ format = c_.u.format.v;
+ switch (format)
+ {
+ case 1: u.format1.init (c_.u.format1); return;
+ case 2: u.format2.init (c_.u.format2); return;
+#ifndef HB_NO_BEYOND_64K
+ case 3: u.format3.init (c_.u.format3); return;
+ case 4: u.format4.init (c_.u.format4); return;
+#endif
+ default: return;
+ }
+ }
+ bool __more__ () const
+ {
+ switch (format)
+ {
+ case 1: return u.format1.__more__ ();
+ case 2: return u.format2.__more__ ();
+#ifndef HB_NO_BEYOND_64K
+ case 3: return u.format3.__more__ ();
+ case 4: return u.format4.__more__ ();
+#endif
+ default:return false;
+ }
+ }
+ void __next__ ()
+ {
+ switch (format)
+ {
+ case 1: u.format1.__next__ (); break;
+ case 2: u.format2.__next__ (); break;
+#ifndef HB_NO_BEYOND_64K
+ case 3: u.format3.__next__ (); break;
+ case 4: u.format4.__next__ (); break;
+#endif
+ default: break;
+ }
+ }
+ typedef hb_codepoint_t __item_t__;
+ __item_t__ __item__ () const { return get_glyph (); }
+
+ hb_codepoint_t get_glyph () const
+ {
+ switch (format)
+ {
+ case 1: return u.format1.get_glyph ();
+ case 2: return u.format2.get_glyph ();
+#ifndef HB_NO_BEYOND_64K
+ case 3: return u.format3.get_glyph ();
+ case 4: return u.format4.get_glyph ();
+#endif
+ default:return 0;
+ }
+ }
+ bool operator != (const iter_t& o) const
+ {
+ if (unlikely (format != o.format)) return true;
+ switch (format)
+ {
+ case 1: return u.format1 != o.u.format1;
+ case 2: return u.format2 != o.u.format2;
+#ifndef HB_NO_BEYOND_64K
+ case 3: return u.format3 != o.u.format3;
+ case 4: return u.format4 != o.u.format4;
+#endif
+ default:return false;
+ }
+ }
+ iter_t __end__ () const
+ {
+ iter_t it;
+ it.format = format;
+ switch (format)
+ {
+ case 1: it.u.format1 = u.format1.__end__ (); break;
+ case 2: it.u.format2 = u.format2.__end__ (); break;
+#ifndef HB_NO_BEYOND_64K
+ case 3: it.u.format3 = u.format3.__end__ (); break;
+ case 4: it.u.format4 = u.format4.__end__ (); break;
+#endif
+ default: break;
+ }
+ return it;
+ }
+
+ private:
+ unsigned int format;
+ union {
+#ifndef HB_NO_BEYOND_64K
+ CoverageFormat2_4::iter_t format4; /* Put this one first since it's larger; helps shut up compiler. */
+ CoverageFormat1_3::iter_t format3;
+#endif
+ CoverageFormat2_4::iter_t format2; /* Put this one first since it's larger; helps shut up compiler. */
+ CoverageFormat1_3::iter_t format1;
+ } u;
+ };
+ iter_t iter () const { return iter_t (*this); }
+};
+
+template
+static inline void
+Coverage_serialize (hb_serialize_context_t *c,
+ Iterator it)
+{ c->start_embed ()->serialize (c, it); }
+
+}
+}
+}
+
+#endif // #ifndef OT_LAYOUT_COMMON_COVERAGE_HH
diff --git a/3rdparty/harfbuzz/src/OT/Layout/Common/CoverageFormat1.hh b/3rdparty/harfbuzz/src/OT/Layout/Common/CoverageFormat1.hh
new file mode 100644
index 0000000000..823e45073d
--- /dev/null
+++ b/3rdparty/harfbuzz/src/OT/Layout/Common/CoverageFormat1.hh
@@ -0,0 +1,135 @@
+/*
+ * Copyright © 2007,2008,2009 Red Hat, Inc.
+ * Copyright © 2010,2012 Google, Inc.
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ * Google Author(s): Behdad Esfahbod, Garret Rieger
+ */
+
+
+#ifndef OT_LAYOUT_COMMON_COVERAGEFORMAT1_HH
+#define OT_LAYOUT_COMMON_COVERAGEFORMAT1_HH
+
+namespace OT {
+namespace Layout {
+namespace Common {
+
+#define NOT_COVERED ((unsigned int) -1)
+
+template