diff --git a/skia-adventure/ft2-demos-all-4-svg-hooks-skia-m116.diff b/skia-adventure/ft2-demos-all-4-svg-hooks-skia-m116.diff new file mode 100644 index 0000000..5e53759 --- /dev/null +++ b/skia-adventure/ft2-demos-all-4-svg-hooks-skia-m116.diff @@ -0,0 +1,964 @@ +diff --git a/Makefile b/Makefile +index cded506..0843d78 100644 +--- a/Makefile ++++ b/Makefile +@@ -145,10 +148,15 @@ else + $(TOP_DIR)/include \ + $(SRC_DIR) + ++ # Skia definitely want c++, and clang++ does not like -std=99 ++ CC=g++ + COMPILE = $(CC) $(ANSIFLAGS) \ + $(INCLUDES:%=$I%) \ + $(CFLAGS) \ +- $(FT_DEMO_CFLAGS) ++ $(FT_DEMO_CFLAGS) -I$(TOP_DIR_2)/svg-native-viewer/svgnative/include/ \ ++ -I$(TOP_DIR_2)/skia/include/core/ \ ++ -I$(TOP_DIR_2)/skia/ -DHAVE_SKIA ++ + + # Enable C99 for gcc to avoid warnings. + # Note that clang++ aborts with an error if we use `-std=C99', +@@ -186,8 +194,12 @@ else + # `FT_DEMO_LDFLAGS` has been set in `unix-cc.mk`, too. + override CC = $(CCraw) + LINK_CMD = $(LIBTOOL) --mode=link $(CC) \ ++ -L$(TOP_DIR_2)/svg-native-viewer/svgnative/build/linux/ -lSVGNativeViewerLib -lexpat \ ++ -L$(TOP_DIR_2)/skia/out/Shared/ -lsvg -lskia -lskshaper -lskunicode -lharfbuzz -lGL -lfontconfig -ljpeg \ + $(subst /,$(COMPILER_SEP),$(LDFLAGS)) + LINK_LIBS = $(subst /,$(COMPILER_SEP),$(FTLIB) $(EFENCE)) \ ++ -L$(TOP_DIR_2)/svg-native-viewer/svgnative/build/linux/ -lSVGNativeViewerLib -lexpat \ ++ -L$(TOP_DIR_2)/skia/out/Shared/ -lsvg -lskia -lskshaper -lskunicode -lharfbuzz -lGL -lfontconfig -ljpeg \ + $(FT_DEMO_LDFLAGS) + else + LINK_CMD = $(CC) $(subst /,$(COMPILER_SEP),$(LDFLAGS)) +@@ -469,9 +481,30 @@ else + $(OBJ_DIR_2)/rsvg-port.$(SO): $(SRC_DIR)/rsvg-port.c $(SRC_DIR)/rsvg-port.h + $(COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $<) + ++ $(OBJ_DIR_2)/skia-port.$(SO): $(SRC_DIR)/skia-port.c $(SRC_DIR)/skia-port.h ++ $(COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $<) ++ ++ $(OBJ_DIR_2)/rsvg-port-alt.$(SO): $(SRC_DIR)/rsvg-port.c $(SRC_DIR)/rsvg-port.h ++ $(COMPILE) -DUSE_ADOBE=1 $T$(subst /,$(COMPILER_SEP),$@ $<) ++ ++ $(OBJ_DIR_2)/skia-port-alt.$(SO): $(SRC_DIR)/skia-port.c $(SRC_DIR)/skia-port.h ++ $(COMPILE) -DUSE_ADOBE=1 $T$(subst /,$(COMPILER_SEP),$@ $<) ++ ++ $(OBJ_DIR_2)/skia-colrv1-stub.$(SO): $(SRC_DIR)/skia-colrv1-stub.cpp $(SRC_DIR)/skia-colrv1-stub.h ++ $(COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $<) ++ ++ $(OBJ_DIR_2)/skia-src-ports-SkFontHost_FreeType_colrv1.$(SO): $(SRC_DIR)/skia-src-ports-SkFontHost_FreeType_colrv1.cpp ++ $(COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $<) ++ ++ $(OBJ_DIR_2)/skia-src-ports-SkFontHost_FreeType_common_colrv1.$(SO): $(SRC_DIR)/skia-src-ports-SkFontHost_FreeType_common_colrv1.cpp ++ $(COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $<) ++ + FTCOMMON_OBJ := $(OBJ_DIR_2)/ftcommon.$(SO) \ + $(OBJ_DIR_2)/ftpngout.$(SO) \ +- $(OBJ_DIR_2)/rsvg-port.$(SO) ++ $(OBJ_DIR_2)/rsvg-port.$(SO) \ ++ $(OBJ_DIR_2)/skia-port.$(SO) \ ++ $(OBJ_DIR_2)/rsvg-port-alt.$(SO) \ ++ $(OBJ_DIR_2)/skia-port-alt.$(SO) + + + #################################################################### +diff --git a/README.Adobe-SVG-Native-Cairo b/README.Adobe-SVG-Native-Cairo +new file mode 100644 +index 0000000..896b3a0 +--- /dev/null ++++ b/README.Adobe-SVG-Native-Cairo +@@ -0,0 +1,42 @@ ++CC=c++ ./configure ++ ++Building svg-native-viewer: ++=========================== ++ ++build static library with (without "-DSHARED=ON" on cmake): ++ ++git submodule update --init ++cd svgnative/ ++ cmake -Bbuild/linux -H. -DLIB_ONLY=ON -DPLATFORM_XML=ON -DCAIRO=ON \ ++ -DSKIA=ON -DSKIA_LIBRARY_PATH=`pwd`/../../skia/out/Shared/libskia.so \ ++ -DSKIA_SOURCE_DIR=`pwd`/../../skia/ -DCMAKE_CXX_FLAGS="-I`pwd`/include/svgnative/" -DCMAKE_POSITION_INDEPENDENT_CODE=ON ++ cd build/linux/ ++ make ++ ++Apply small change: ++ ++diff --git a/svgnative/include/svgnative/SVGRenderer.h b/svgnative/include/svgnative/SVGRenderer.h ++index aa09bb4..1745093 100644 ++--- a/svgnative/include/svgnative/SVGRenderer.h +++++ b/svgnative/include/svgnative/SVGRenderer.h ++@@ -24,6 +24,7 @@ governing permissions and limitations under the License. ++ #include ++ #include ++ #include +++#include ++ ++ namespace SVGNative ++ { ++--- ++ ++This works with: ++ ++commit ab9ea1d48b0ff055c2fb063ae4c68edafce5b7c5 (origin/main, origin/HEAD) ++Author: tushar ++Date: Thu Mar 23 17:07:26 2023 +0530 ++ ++ update only boost submodule ++ ++--- ++ ++Then just build ft2-demo as normal. +diff --git a/meson.build b/meson.build +index 14409a3..a1d488f 100644 +--- a/meson.build ++++ b/meson.build +@@ -83,6 +83,8 @@ ftcommon_lib = static_library('ftcommon', + 'src/ftpngout.c', + 'src/rsvg-port.c', + 'src/rsvg-port.h', ++ 'src/skia-port.c', ++ 'src/skia-port.h', + ], + c_args: ftcommon_lib_c_args, + dependencies: [libpng_dep, librsvg_dep, libfreetype2_dep], +diff --git a/src/ftcommon.c b/src/ftcommon.c +index 74553aa..017e0a4 100644 +--- a/src/ftcommon.c ++++ b/src/ftcommon.c +@@ -29,6 +29,7 @@ + #include "strbuf.h" + #include "ftcommon.h" + #include "rsvg-port.h" ++#include "skia-port.h" + + #include + #include +@@ -395,6 +396,8 @@ + + handle->use_sbits_cache = 1; + ++ handle->use_skia = 0; ++ + /* string_init */ + memset( handle->string, 0, sizeof ( TGlyph ) * MAX_GLYPHS ); + handle->string_length = 0; +@@ -444,6 +447,35 @@ + } + + ++ void ++ FTDemo_Set_SVG_Hooks( FTDemo_Handle* handle ) ++ { ++ /* The use of an external SVG rendering library is optional. */ ++ switch ( handle->use_skia ) ++ { ++ case 1: ++ (void)FT_Property_Set( handle->library, ++ "ot-svg", "svg-hooks", &skia_hooks ); ++ break; ++ ++ case 2: ++ (void)FT_Property_Set( handle->library, ++ "ot-svg", "svg-hooks", &skia_hooks2 ); ++ break; ++ ++ case 3: ++ (void)FT_Property_Set( handle->library, ++ "ot-svg", "svg-hooks", &rsvg_hooks2 ); ++ break; ++ ++ default: ++ (void)FT_Property_Set( handle->library, ++ "ot-svg", "svg-hooks", &rsvg_hooks ); ++ break; ++ } ++ } ++ ++ + void + FTDemo_Version( FTDemo_Handle* handle, + FT_String str[64] ) +diff --git a/src/ftcommon.h b/src/ftcommon.h +index 3ae11bb..c5f8d6b 100644 +--- a/src/ftcommon.h ++++ b/src/ftcommon.h +@@ -223,6 +223,8 @@ + int lcd_mode; /* mono, aa, light, vrgb, ... */ + int preload; /* force font file preloading */ + ++ int use_skia; /* use_skia for render hooks */ ++ + /* don't touch the following fields! */ + + /* used for string rendering */ +@@ -244,6 +246,11 @@ + FTDemo_Done( FTDemo_Handle* handle ); + + ++ /* set svg hooks */ ++ void ++ FTDemo_Set_SVG_Hooks( FTDemo_Handle* handle ); ++ ++ + /* append version information */ + void + FTDemo_Version( FTDemo_Handle* handle, +diff --git a/src/ftgrid.c b/src/ftgrid.c +index bff99b0..d544693 100644 +--- a/src/ftgrid.c ++++ b/src/ftgrid.c +@@ -1734,7 +1776,7 @@ + + while ( 1 ) + { +- option = getopt( *argc, *argv, "a:d:e:f:k:nr:v" ); ++ option = getopt( *argc, *argv, "a:d:e:f:k:nr:s:v" ); + + if ( option == -1 ) + break; +@@ -1792,6 +1834,11 @@ + usage( execname ); + break; + ++ case 's': ++ handle->use_skia = atoi( optarg ); ++ FTDemo_Set_SVG_Hooks( handle ); ++ break; ++ + case 'v': + { + FT_String str[64] = "ftgrid (FreeType) "; +diff --git a/src/ftview.c b/src/ftview.c +index 88edbec..ee6a937 100644 +--- a/src/ftview.c ++++ b/src/ftview.c +@@ -1721,7 +1721,7 @@ + + while ( 1 ) + { +- option = getopt( *argc, *argv, "d:e:f:k:L:l:m:pr:v" ); ++ option = getopt( *argc, *argv, "d:e:f:k:L:l:m:pr:s:v" ); + + if ( option == -1 ) + break; +@@ -1810,6 +1810,12 @@ + usage( execname ); + break; + ++ case 's': ++ handle->use_skia = atoi( optarg ); ++ FTDemo_Set_SVG_Hooks( handle ); ++ break; ++ ++ + case 'v': + { + FT_String str[64] = "ftview (FreeType) "; +diff --git a/src/rsvg-port.c b/src/rsvg-port.c +index 7a8bb9a..9f1c190 100644 +--- a/src/rsvg-port.c ++++ b/src/rsvg-port.c +@@ -22,7 +22,12 @@ + #ifdef HAVE_LIBRSVG + + #include ++#ifdef USE_ADOBE ++#include "svgnative/SVGDocument.h" ++#include "svgnative/ports/cairo/CairoSVGRenderer.h" ++#else + #include ++#endif + #include + #include + +@@ -30,6 +35,7 @@ + #include + + #include "rsvg-port.h" ++#include + + + /* +@@ -39,12 +45,17 @@ + * useful to cache some of the results obtained by one hook function that + * the other one might use. + */ +- FT_Error ++ static FT_Error + rsvg_port_init( FT_Pointer *state ) + { + /* allocate the memory upon initialization */ + *state = malloc( sizeof( Rsvg_Port_StateRec ) ); /* XXX error handling */ + ++#ifdef USE_ADOBE ++ fprintf( stderr, "Using Adobe Cairo\n" ); ++#else ++ fprintf( stderr, "Using Librsvg Cairo\n" ); ++#endif + return FT_Err_Ok; + } + +@@ -52,7 +63,7 @@ + /* + * Deallocate the state structure. + */ +- void ++ static void + rsvg_port_free( FT_Pointer *state ) + { + free( *state ); +@@ -65,7 +76,7 @@ + * simply use the recording surface by playing it back against the + * surface. + */ +- FT_Error ++ static FT_Error + rsvg_port_render( FT_GlyphSlot slot, + FT_Pointer *_state ) + { +@@ -145,7 +156,7 @@ + * necessary for appropriate memory allocation, as well as ultimately + * compositing the glyph later on by client applications. + */ +- FT_Error ++ static FT_Error + rsvg_port_preset_slot( FT_GlyphSlot slot, + FT_Bool cache, + FT_Pointer *_state ) +@@ -160,6 +171,7 @@ + FT_UShort end_glyph_id = document->end_glyph_id; + FT_UShort start_glyph_id = document->start_glyph_id; + ++#ifndef USE_ADOBE /* Rsvg */ + /* Librsvg variables. */ + GError *gerror = NULL; + gboolean ret; +@@ -173,6 +185,7 @@ + RsvgLength out_height; + RsvgRectangle out_viewbox; + RsvgDimensionData dimension_svg; ++#endif + + cairo_t *rec_cr; + cairo_matrix_t transform_matrix; +@@ -207,15 +220,23 @@ + state = &state_dummy; + + /* Form an `RsvgHandle` by loading the SVG document. */ ++#ifndef USE_ADOBE /* Rsvg */ + handle = rsvg_handle_new_from_data( document->svg_document, + document->svg_document_length, + &gerror ); ++#else ++ auto renderer = std::make_shared(); ++ char *s = strndup((char *)document->svg_document, ++ document->svg_document_length); // careful here, svg documents could have NULLs - see svg-native-viewer#164 ++ auto handle = std::unique_ptr(SVGNative::SVGDocument::CreateSVGDocument(s, renderer)); ++#endif + if ( handle == NULL ) + { + error = FT_Err_Invalid_SVG_Document; + goto CleanLibrsvg; + } + ++#ifndef USE_ADOBE /* Rsvg */ + /* Get attributes like `viewBox` and `width`/`height`. */ + rsvg_handle_get_intrinsic_dimensions( handle, + &out_has_width, +@@ -274,6 +295,10 @@ + /* Scale factors from SVG coordinates to the needed output size. */ + x_svg_to_out = (double)metrics.x_ppem / dimension_svg.width; + y_svg_to_out = (double)metrics.y_ppem / dimension_svg.height; ++#else ++ x_svg_to_out = (double)metrics.x_ppem / units_per_EM; ++ y_svg_to_out = (double)metrics.y_ppem / units_per_EM; ++#endif + + /* + * Create a cairo recording surface. This is done for two reasons. +@@ -302,9 +327,18 @@ + yy = (double)document->transform.yy / ( 1 << 16 ); + + x0 = (double)document->delta.x / 64 * ++#ifndef USE_ADOBE /* Rsvg */ + dimension_svg.width / metrics.x_ppem; ++#else ++ handle->Width() / metrics.x_ppem; ++#endif + y0 = -(double)document->delta.y / 64 * ++#ifndef USE_ADOBE /* Rsvg */ + dimension_svg.height / metrics.y_ppem; ++#else ++ handle->Height() / metrics.y_ppem; ++#endif ++ + + /* Cairo stores both transformation and translation in one matrix. */ + transform_matrix.xx = xx; +@@ -320,21 +354,34 @@ + /* Set up a transformation matrix. */ + cairo_transform( rec_cr, &transform_matrix ); + ++#ifdef USE_ADOBE ++ renderer->SetCairo(rec_cr); ++#endif + /* If the document contains only one glyph, `start_glyph_id` and */ + /* `end_glyph_id` have the same value. Otherwise `end_glyph_id` */ + /* is larger. */ + if ( start_glyph_id < end_glyph_id ) + { + /* Render only the element with its ID equal to `glyph`. */ ++#ifndef USE_ADOBE /* Rsvg */ + sprintf( str, "#glyph%u", slot->glyph_index ); + id = str; ++#else ++ sprintf( str, "glyph%u", slot->glyph_index ); ++ id = str; ++ handle->Render(id, handle->Width(), handle->Height()); ++#endif + } + else + { ++#ifdef USE_ADOBE ++ handle->Render(handle->Width(), handle->Height()); ++#endif + /* NULL = Render the whole document */ + id = NULL; + } + ++#ifndef USE_ADOBE /* Rsvg */ + #if LIBRSVG_CHECK_VERSION( 2, 52, 0 ) + { + RsvgRectangle viewport = +@@ -361,6 +408,7 @@ + error = FT_Err_Invalid_SVG_Document; + goto CleanCairo; + } ++#endif + + /* Get the bounding box of the drawing. */ + cairo_recording_surface_ink_extents( state->rec_surface, &x, &y, +@@ -428,13 +476,19 @@ + + CleanLibrsvg: + /* Destroy the handle. */ ++#ifndef USE_ADOBE /* Rsvg */ + g_object_unref( handle ); ++#endif + + return error; + } + + ++#ifdef USE_ADOBE ++ SVG_RendererHooks rsvg_hooks2 = { ++#else + SVG_RendererHooks rsvg_hooks = { ++#endif + (SVG_Lib_Init_Func)rsvg_port_init, + (SVG_Lib_Free_Func)rsvg_port_free, + (SVG_Lib_Render_Func)rsvg_port_render, +diff --git a/src/rsvg-port.h b/src/rsvg-port.h +index 073fd5e..d44830a 100644 +--- a/src/rsvg-port.h ++++ b/src/rsvg-port.h +@@ -25,7 +25,9 @@ + #ifdef HAVE_LIBRSVG + + #include ++#ifndef USE_ADOBE /* Rsvg */ + #include ++#endif + #include + + +@@ -45,25 +47,10 @@ + + typedef struct Rsvg_Port_StateRec_* Rsvg_Port_State; + +- +- FT_Error +- rsvg_port_init( FT_Pointer *state ); +- +- void +- rsvg_port_free( FT_Pointer *state ); +- +- FT_Error +- rsvg_port_render( FT_GlyphSlot slot, +- FT_Pointer *state ); +- +- FT_Error +- rsvg_port_preset_slot( FT_GlyphSlot slot, +- FT_Bool cache, +- FT_Pointer *state ); +- + #endif /* HAVE_LIBRSVG */ + + ++ extern SVG_RendererHooks rsvg_hooks2; + extern SVG_RendererHooks rsvg_hooks; + + #endif /* RSVG_PORT_H */ +diff --git a/src/skia-port.c b/src/skia-port.c +new file mode 100644 +index 0000000..f4c9a64 +--- /dev/null ++++ b/src/skia-port.c +@@ -0,0 +1,390 @@ ++/**************************************************************************** ++ * ++ * skia-port.c ++ * ++ * Skia-based hook functions for OT-SVG rendering in FreeType ++ * (implementation). ++ * ++ * Copyright (C) 2022-2023 by ++ * Hin-Tak Leung, based on rsvg-port.c ++ * ++ * This file is part of the FreeType project, and may only be used, ++ * modified, and distributed under the terms of the FreeType project ++ * license, LICENSE.TXT. By continuing to use, modify, or distribute ++ * this file you indicate that you have read the license and ++ * understand and accept it fully. ++ * ++ */ ++ ++/* ++ * Main reference, this landed in Skia m103: ++ * https://github.com/google/skia/commit/9cbadcd9280dc139af2f4d41d25a6c9a750e0302.patch ++ ++ * From 9cbadcd9280dc139af2f4d41d25a6c9a750e0302 Mon Sep 17 00:00:00 2001 ++ * From: Ben Wagner ++ * Date: Wed, 20 Apr 2022 17:52:50 -0400 ++ * Subject: [PATCH] Add optional OT-SVG support to FreeType ++ ++ * In particular, ++ src/ports/SkFontHost_FreeType_common.cpp: ++ SkScalerContext_FreeType_Base::drawSVGGlyph() ++ src/ports/SkFontHost_FreeType.cpp: ++ SkScalerContext_FreeType::generateMetrics() ++*/ ++#include ++#include ++ ++#ifdef HAVE_SKIA ++ ++#ifdef USE_ADOBE ++#include "svgnative/SVGDocument.h" ++#include "svgnative/ports/skia/SkiaSVGRenderer.h" ++#else ++#include "modules/svg/include/SkSVGDOM.h" ++#include "modules/svg/include/SkSVGNode.h" ++#include "modules/svg/include/SkSVGRenderContext.h" // SkSVGPresentationContext ++#endif ++#include "include/core/SkBitmap.h" ++#include "include/core/SkCanvas.h" ++#include "include/core/SkMatrix.h" ++#include "include/core/SkStream.h" ++#include "include/private/base/SkFixed.h" // SkFixedToFloat(x) ++#include ++#include ++ ++#include ++#include ++ ++#include "skia-port.h" ++#include ++ ++ /* ++ * The init hook is called when the first OT-SVG glyph is rendered. All ++ * we do is to allocate an internal state structure and set the pointer in ++ * `library->svg_renderer_state`. This state structure becomes very ++ * useful to cache some of the results obtained by one hook function that ++ * the other one might use. ++ */ ++ static FT_Error ++ skia_port_init( FT_Pointer *state ) ++ { ++ /* allocate the memory upon initialization */ ++ *state = calloc( sizeof( Skia_Port_StateRec ), 1 ); /* XXX error handling */ ++ // Skia pointers are reference-counted, so ++ // malloc seems to be buggy and calloc is needed here. ++ // Unlike rsvg. ++ ++#ifdef USE_ADOBE ++ fprintf( stderr, "Using Adobe Skia\n" ); ++#else ++ fprintf( stderr, "Using Skia m103+\n" ); ++#endif ++ return FT_Err_Ok; ++ } ++ ++ ++ /* ++ * Deallocate the state structure. ++ */ ++ static void ++ skia_port_free( FT_Pointer *state ) ++ { ++ free( *state ); ++ } ++ ++ ++ /* ++ * The render hook. The job of this hook is to simply render the glyph in ++ * the buffer that has been allocated on the FreeType side. Here we ++ * simply use the recording surface by playing it back against the ++ * surface. ++ */ ++ static FT_Error ++ skia_port_render( FT_GlyphSlot slot, ++ FT_Pointer *_state ) ++ { ++ FT_Error error = FT_Err_Ok; ++ ++ Skia_Port_State state; ++ ++ state = *(Skia_Port_State*)_state; ++ ++ /* Create a SkBitmap to store the rendered image. However, */ ++ /* don't allocate memory; instead use the space already provided in */ ++ /* `slot->bitmap.buffer`. */ ++ SkBitmap dstBitmap; ++ dstBitmap.setInfo(SkImageInfo::Make(slot->bitmap.width, slot->bitmap.rows, ++ kBGRA_8888_SkColorType, // Not kN32 - FT_Bitmap are platform-neutral, FT_PIXEL_MODE_BGRA blue channel comes first in memory. ++ kPremul_SkAlphaType), // FT_PIXEL_MODE_BGRA is pre-multiplied and in the sRGB colorspace ++ slot->bitmap.pitch); ++ dstBitmap.setPixels(slot->bitmap.buffer); ++ ++ SkCanvas canvas(dstBitmap); ++ ++ canvas.clear(SK_ColorTRANSPARENT); ++ ++ /* Set a translate transform that translates the points in such a way */ ++ /* that we get a tight rendering with least redundant white spac. */ ++ canvas.translate( -state->x, -state->y ); /* floating values in skia */ ++ ++ /* Replay from state->picture. This saves us from parsing the */ ++ /* document again and redoing what was already done in the preset */ ++ /* hook. */ ++ canvas.drawPicture( state->picture ); ++ ++ slot->bitmap.pixel_mode = FT_PIXEL_MODE_BGRA; ++ slot->bitmap.num_grays = 256; ++ slot->format = FT_GLYPH_FORMAT_BITMAP; ++ ++ /* Clean up everything. */ ++ state->picture.reset(); ++ ++ return error; ++ } ++ ++ ++ /* ++ * This hook is called at two different locations. Firstly, it is called ++ * when presetting the glyphslot when `FT_Load_Glyph` is called. ++ * Secondly, it is called right before the render hook is called. When ++ * `cache` is false, it is the former, when `cache` is true, it is the ++ * latter. ++ * ++ * The job of this function is to preset the slot setting the width, ++ * height, pitch, `bitmap.left`, and `bitmap.top`. These are all ++ * necessary for appropriate memory allocation, as well as ultimately ++ * compositing the glyph later on by client applications. ++ */ ++ static FT_Error ++ skia_port_preset_slot( FT_GlyphSlot slot, ++ FT_Bool cache, ++ FT_Pointer *_state ) ++ { ++ /* FreeType variables. */ ++ FT_Error error = FT_Err_Ok; ++ ++ FT_SVG_Document document = (FT_SVG_Document)slot->other; ++ ++ FT_UShort units_per_EM = document->units_per_EM; ++ FT_UShort end_glyph_id = document->end_glyph_id; ++ FT_UShort start_glyph_id = document->start_glyph_id; ++ ++ SkCanvas *recordingCanvas; ++ SkMatrix m; ++ ++ /* Rendering port's state. */ ++ Skia_Port_State state; ++ Skia_Port_StateRec state_dummy; ++ ++ /* General variables. */ ++ double x, y; ++ double width, height; ++ ++ float metrics_width, metrics_height; ++ float horiBearingX, horiBearingY; ++ float vertBearingX, vertBearingY; ++ float tmpf; ++ ++ /* If `cache` is `TRUE` we store calculations in the actual port */ ++ /* state variable, otherwise we just create a dummy variable and */ ++ /* store there. This saves us from too many 'if' statements. */ ++ if ( cache ) ++ state = *(Skia_Port_State*)_state; ++ else ++ state = &state_dummy; ++ ++ /* Form an `sk_sp` by loading the SVG document. */ ++#ifdef USE_ADOBE ++ auto renderer = std::make_shared(); ++ char *s = strndup((char *)document->svg_document, ++ document->svg_document_length); // careful here, svg documents could have NULLs - see svg-native-viewer#164 ++ auto svg = std::unique_ptr(SVGNative::SVGDocument::CreateSVGDocument(s, renderer)); ++#else ++ SkMemoryStream svgmem(document->svg_document, ++ document->svg_document_length, false /*not copying */); ++ sk_sp svg = SkSVGDOM::MakeFromStream(svgmem); ++ ++ //svg->getRoot()->intrinsicSize(); ++ if (svg->containerSize().isEmpty()) { ++ SkSize size = SkSize::Make(units_per_EM, units_per_EM); ++ svg->setContainerSize(size); ++ } ++#endif ++ // Do we care about the viewBox attribute? It is auto I think, anyway. ++ ++ /* ++ * Create a SkPictureRecorder. This is done for two reasons. ++ * Firstly, it is required to get the bounding box of the final drawing ++ * so we can use an appropriate translate transform to get a tight ++ * rendering. Secondly, if `cache` is true, we can save this surface ++ * and later replay it against an image surface for the final rendering. ++ * This saves us from loading and parsing the document again. ++ */ ++ SkPictureRecorder recorder; ++ ++ SkRect infiniteRect = SkRect::MakeLTRB(-SK_ScalarInfinity, -SK_ScalarInfinity, ++ SK_ScalarInfinity, SK_ScalarInfinity); ++ sk_sp bboxh = SkRTreeFactory()(); ++ ++ recordingCanvas = recorder.beginRecording(infiniteRect, bboxh); ++ ++ /* ++ * Borrow heavily from: ++ * src/ports/SkFontHost_FreeType_common.cpp: ++ * SkScalerContext_FreeType_Base::drawSVGGlyph() ++ */ ++ SkASSERT(slot->format == FT_GLYPH_FORMAT_SVG); ++ ++ /* ++ * We need to take into account any transformations applied. The end ++ * user who applied the transformation doesn't know the internal details ++ * of the SVG document. Thus, we expect that the end user should just ++ * write the transformation as if the glyph is a traditional one. We ++ * then do some maths on this to get the equivalent transformation in ++ * SVG coordinates. ++ */ ++ FT_Matrix ftMatrix = document->transform; ++ FT_Vector ftOffset = document->delta; ++ ++ /* Skia stores both transformation and translation in one matrix. */ ++ m.setAll( ++ SkFixedToFloat(ftMatrix.xx), -SkFixedToFloat(ftMatrix.xy), SkFixedToFloat(ftOffset.x), ++ -SkFixedToFloat(ftMatrix.yx), SkFixedToFloat(ftMatrix.yy), -SkFixedToFloat(ftOffset.y), ++ 0 , 0 , 1 ); ++ ++ /* Set up a scale transformation to scale up the document to the */ ++ /* required output size. */ ++ m.postScale(SkFixedToFloat(document->metrics.x_scale) / 64.0f, ++ SkFixedToFloat(document->metrics.y_scale) / 64.0f); ++ ++ /* Set up a transformation matrix. */ ++ recordingCanvas->concat(m); ++ ++#ifdef USE_ADOBE ++ renderer->SetSkCanvas(recordingCanvas); ++#endif ++ /* If the document contains only one glyph, `start_glyph_id` and */ ++ /* `end_glyph_id` have the same value. Otherwise `end_glyph_id` */ ++ /* is larger. */ ++ if ( start_glyph_id < end_glyph_id ) ++ { ++#ifndef USE_ADOBE /* Skia svg */ ++ SkSVGPresentationContext pctx; ++#endif ++ char id[32]; ++ /* Render only the element with its ID equal to `glyph`. */ ++ sprintf( id, "glyph%u", slot->glyph_index ); ++ ++ /* ++ * Unlike Rsvg, Skia's renderNode() takes an extra ++ * SkSVGPresentationContext argument, which sets foreground ++ * colors, palettes, etc, and does not take NULL to render ++ * whole. In the case of OT-SVG, there is no extra ++ * Context, so leaving it as default is fine. ++ */ ++#ifdef USE_ADOBE ++ svg->Render(id, svg->Width(), svg->Height()); ++#else ++ svg->renderNode(recordingCanvas, pctx, id); ++#endif ++ } ++ else ++ { ++ /* Render the whole document */ ++#ifdef USE_ADOBE ++ svg->Render(svg->Width(), svg->Height()); ++#else ++ svg->render(recordingCanvas); ++#endif ++ } ++ ++ /* Get the bounding box of the drawing. */ ++ state->picture = recorder.finishRecordingAsPicture(); ++ SkRect bounds = state->picture->cullRect(); ++ SkASSERT(bounds.isFinite()); ++ ++ width = ceil(bounds.right()) - floor(bounds.left()); //bounds.width(); // fRight minus fLeft ++ height = ceil(bounds.bottom()) - floor(bounds.top()); //bounds.height(); // fBottom minus fTop ++ x = floor(bounds.left()); ++ y = floor(bounds.top()); ++ ++ /* We store the bounding box's `x` and `y` values so that the render */ ++ /* hook can apply a translation to get a tight rendering. */ ++ state->x = x; /* truncation is consistent in being floor() for negative values, to match cast to (FT_Int) below. */ ++ state->y = y; ++ /* rsvg actually always returns floats in integral values, but floor() x,y and ceil height/width */ ++ /* we don't want to copy the floor behavior, actually */ ++ ++ /* Preset the values. */ ++ slot->bitmap_left = (FT_Int) state->x; ++ slot->bitmap_top = (FT_Int)-state->y; ++ ++ slot->bitmap.rows = height ; ++ slot->bitmap.width = width ; ++ ++ slot->bitmap.pitch = (int)slot->bitmap.width * 4; ++ ++ slot->bitmap.pixel_mode = FT_PIXEL_MODE_BGRA; ++ ++ /* Compute all the bearings and set them correctly. The outline is */ ++ /* scaled already, we just need to use the bounding box. */ ++ metrics_width = (float)width; ++ metrics_height = (float)height; ++ ++ horiBearingX = (float) state->x; ++ horiBearingY = (float)-state->y; ++ ++ vertBearingX = slot->metrics.horiBearingX / 64.0f - ++ slot->metrics.horiAdvance / 64.0f / 2; ++ vertBearingY = ( slot->metrics.vertAdvance / 64.0f - ++ slot->metrics.height / 64.0f ) / 2; /* XXX parentheses correct? */ ++ ++ /* Do conversion in two steps to avoid 'bad function cast' warning. */ ++ tmpf = roundf( metrics_width * 64 ); ++ slot->metrics.width = (FT_Pos)tmpf; ++ tmpf = roundf( metrics_height * 64 ); ++ slot->metrics.height = (FT_Pos)tmpf; ++ ++ slot->metrics.horiBearingX = (FT_Pos)( horiBearingX * 64 ); /* XXX rounding? */ ++ slot->metrics.horiBearingY = (FT_Pos)( horiBearingY * 64 ); ++ slot->metrics.vertBearingX = (FT_Pos)( vertBearingX * 64 ); ++ slot->metrics.vertBearingY = (FT_Pos)( vertBearingY * 64 ); ++ ++ if ( slot->metrics.vertAdvance == 0 ) ++ slot->metrics.vertAdvance = (FT_Pos)( metrics_height * 1.2f * 64 ); ++ ++ /* If a render call is to follow, just destroy the canvas for the */ ++ /* SkPictureRecorder since no more drawing will be done on it. */ ++ /* However, keep the Picture itself for use by the render hook. */ ++ // TRUE defined in /usr/include/glib-2.0/glib/gmacros.h ++ // TRUE/FALSE not in c++, defined in include/freetype/internal/ftobjs.h (not used) ++ // but defined indirectly in librsvg. Its header to refer to it (from glib). ++ if ( !cache ) ++ { ++ /* We don't have to do this; just being pedantic */ ++ state->picture.reset(); ++ } ++ ++ return error; ++ } ++ ++ ++#ifdef USE_ADOBE ++ SVG_RendererHooks skia_hooks2 = { ++#else ++ SVG_RendererHooks skia_hooks = { ++#endif ++ (SVG_Lib_Init_Func)skia_port_init, ++ (SVG_Lib_Free_Func)skia_port_free, ++ (SVG_Lib_Render_Func)skia_port_render, ++ (SVG_Lib_Preset_Slot_Func)skia_port_preset_slot ++ }; ++ ++#else /* !HAVE_SKIA */ ++ ++ SVG_RendererHooks skia_hooks = { NULL, NULL, NULL, NULL }; ++ ++#endif /* !HAVE_SKIA */ ++ ++ ++/* End */ +diff --git a/src/skia-port.h b/src/skia-port.h +new file mode 100644 +index 0000000..6a9187d +--- /dev/null ++++ b/src/skia-port.h +@@ -0,0 +1,57 @@ ++/**************************************************************************** ++ * ++ * skia-port.h ++ * ++ * Skia based hook functions for OT-SVG rendering in FreeType ++ * (headers). ++ * ++ * Copyright (C) 2022-2023 by ++ * Hin-Tak Leung, based on rsvg-port.h ++ * ++ * This file is part of the FreeType project, and may only be used, ++ * modified, and distributed under the terms of the FreeType project ++ * license, LICENSE.TXT. By continuing to use, modify, or distribute ++ * this file you indicate that you have read the license and ++ * understand and accept it fully. ++ * ++ */ ++ ++#ifndef SKIA_PORT_H ++#define SKIA_PORT_H ++ ++#include ++#include ++ ++#ifdef HAVE_SKIA ++ ++#include "include/core/SkPictureRecorder.h" ++#include ++ ++ ++ /* ++ * Different hook functions can access persisting data by creating a state ++ * structure and putting its address in `library->svg_renderer_state`. ++ * Functions can then store and retrieve data from this structure. ++ */ ++ typedef struct Skia_Port_StateRec_ ++ { ++ sk_sp picture; /* sk_sp is short for "Shared pointer class to wrap classes that support a ref()/unref() interface."!*/ ++ ++ double x; ++ double y; ++ ++ } Skia_Port_StateRec; ++ ++ typedef struct Skia_Port_StateRec_* Skia_Port_State; ++ ++ ++#endif /* HAVE_SKAI */ ++ ++ ++ extern SVG_RendererHooks skia_hooks2; ++ extern SVG_RendererHooks skia_hooks; ++ ++#endif /* SKIA_PORT_H */ ++ ++ ++/* End */