First Commit

This commit is contained in:
2025-11-18 14:18:26 -07:00
parent 33eb6e3707
commit 27277ec342
6106 changed files with 3571167 additions and 0 deletions

291
3rdparty/plutosvg/source/plutosvg-ft.h vendored Normal file
View File

@@ -0,0 +1,291 @@
/*
* Copyright (c) 2020-2025 Samuel Ugochukwu <sammycageagle@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* @brief FreeType hooks for rendering SVG glyphs with PlutoSVG.
*
* This file implements the integration layer between FreeTypes SVG module (typically
* named "ot-svg") and PlutoSVG. It defines the necessary functions to initialize and
* free the SVG rendering state, render an SVG glyph into a glyph slot, load (and cache)
* SVG documents, and pre-configure glyph slots with appropriate metrics and transforms.
*
* These functions are aggregated into the `plutosvg_ft_hooks` structure.
*
* Usage example:
* @code
* #include <plutosvg-ft.h>
*
* #include <ft2build.h>
* #include FT_FREETYPE_H
* #include FT_MODULE_H
*
* int main(void)
* {
* FT_Library library;
* if (FT_Init_FreeType(&library))
* return -1;
*
* if (FT_Property_Set(library, "ot-svg", "svg-hooks", &plutosvg_ft_hooks))
* return -1;
*
* // ... your code ...
*
* FT_Done_FreeType(library);
* return 0;
* }
* @endcode
*/
#ifndef PLUTOSVG_FT_H
#define PLUTOSVG_FT_H
#include "plutosvg.h"
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <ft2build.h>
#include FT_OTSVG_H
#include FT_COLOR_H
typedef struct {
plutosvg_document_t* document;
const FT_Byte* data;
FT_ULong length;
} plutosvg_ft_document_entry_t;
#define PLUTOSVG_FT_MAX_DOCS 16
typedef struct {
plutosvg_document_t* document;
plutovg_matrix_t matrix;
plutovg_rect_t extents;
plutosvg_ft_document_entry_t entries[PLUTOSVG_FT_MAX_DOCS];
FT_ULong num_entries;
} plutosvg_ft_state_t;
static FT_Error plutosvg_ft_init(FT_Pointer* ft_state)
{
plutosvg_ft_state_t* state = (plutosvg_ft_state_t*)malloc(sizeof(plutosvg_ft_state_t));
memset(state, 0, sizeof(plutosvg_ft_state_t));
*ft_state = state;
return FT_Err_Ok;
}
static void plutosvg_ft_free(FT_Pointer* ft_state)
{
plutosvg_ft_state_t* state = (plutosvg_ft_state_t*)(*ft_state);
for(FT_ULong i = 0; i < state->num_entries; ++i)
plutosvg_document_destroy(state->entries[i].document);
free(state);
}
#define PLUTOSVG_FT_PALETTE_INDEX 0
static bool plutosvg_ft_palette_func(void* closure, const char* name, int length, plutovg_color_t* color)
{
FT_Face ft_face = (FT_Face)(closure);
if(length < 5 || strncmp(name, "color", 5) != 0)
return false;
FT_Palette_Data ft_palette_data;
if(FT_Palette_Data_Get(ft_face, &ft_palette_data))
return false;
FT_Color* ft_palette = NULL;
if(FT_Palette_Select(ft_face, PLUTOSVG_FT_PALETTE_INDEX, &ft_palette)) {
return false;
}
FT_Int index = 0;
for(int i = 5; i < length; ++i) {
const char ch = name[i];
if(ch < '0' || ch > '9')
return false;
index = index * 10 + ch - '0';
}
if(index >= ft_palette_data.num_palette_entries)
return false;
FT_Color* ft_color = ft_palette + index;
color->r = ft_color->red / 255.f;
color->g = ft_color->green / 255.f;
color->b = ft_color->blue / 255.f;
color->a = ft_color->alpha / 255.f;
return true;
}
static FT_Error plutosvg_ft_render(FT_GlyphSlot ft_slot, FT_Pointer* ft_state)
{
plutosvg_ft_state_t* state = (plutosvg_ft_state_t*)(*ft_state);
if(state->document == NULL)
return FT_Err_Invalid_SVG_Document;
plutovg_surface_t* surface = plutovg_surface_create_for_data(ft_slot->bitmap.buffer, ft_slot->bitmap.width, ft_slot->bitmap.rows, ft_slot->bitmap.pitch);
plutovg_canvas_t* canvas = plutovg_canvas_create(surface);
FT_SVG_Document ft_document = (FT_SVG_Document)ft_slot->other;
FT_UShort start_glyph_id = ft_document->start_glyph_id;
FT_UShort end_glyph_id = ft_document->end_glyph_id;
char buffer[64];
char* id = NULL;
if(start_glyph_id < end_glyph_id) {
sprintf(buffer, "glyph%u", ft_slot->glyph_index);
id = buffer;
}
plutovg_canvas_translate(canvas, -state->extents.x, -state->extents.y);
plutovg_canvas_transform(canvas, &state->matrix);
plutosvg_document_render(state->document, id, canvas, NULL, plutosvg_ft_palette_func, ft_slot->face);
ft_slot->bitmap.pixel_mode = FT_PIXEL_MODE_BGRA;
ft_slot->bitmap.num_grays = 256;
ft_slot->format = FT_GLYPH_FORMAT_BITMAP;
plutovg_canvas_destroy(canvas);
plutovg_surface_destroy(surface);
state->document = NULL;
return FT_Err_Ok;
}
static plutosvg_document_t* plutosvg_ft_document_load(plutosvg_ft_state_t* state, const FT_Byte* data, FT_ULong length, FT_UShort units_per_EM)
{
for(FT_ULong i = 0; i < state->num_entries; ++i) {
if(data == state->entries[i].data && length == state->entries[i].length) {
plutosvg_ft_document_entry_t entry = state->entries[i];
memmove(&state->entries[1], &state->entries[0], i * sizeof(plutosvg_ft_document_entry_t));
state->entries[0] = entry;
return entry.document;
}
}
plutosvg_document_t* document = plutosvg_document_load_from_data((const char*)data, length, units_per_EM, units_per_EM, NULL, NULL);
if(document == NULL)
return NULL;
if(state->num_entries == PLUTOSVG_FT_MAX_DOCS) {
state->num_entries--;
plutosvg_document_destroy(state->entries[state->num_entries].document);
}
memmove(&state->entries[1], &state->entries[0], state->num_entries * sizeof(plutosvg_ft_document_entry_t));
state->entries[0].document = document;
state->entries[0].data = data;
state->entries[0].length = length;
state->num_entries++;
return document;
}
static FT_Error plutosvg_ft_preset_slot(FT_GlyphSlot ft_slot, FT_Bool ft_cache, FT_Pointer* ft_state)
{
plutosvg_ft_state_t* state = (plutosvg_ft_state_t*)(*ft_state);
FT_SVG_Document ft_document = (FT_SVG_Document)ft_slot->other;
FT_Size_Metrics ft_metrics = ft_document->metrics;
FT_UShort start_glyph_id = ft_document->start_glyph_id;
FT_UShort end_glyph_id = ft_document->end_glyph_id;
plutosvg_document_t* document = plutosvg_ft_document_load(state, ft_document->svg_document, ft_document->svg_document_length, ft_document->units_per_EM);
if(document == NULL) {
return FT_Err_Invalid_SVG_Document;
}
float document_width = plutosvg_document_get_width(document);
float document_height = plutosvg_document_get_height(document);
plutovg_matrix_t transform = {
(float)ft_document->transform.xx / (1 << 16),
-(float)ft_document->transform.xy / (1 << 16),
-(float)ft_document->transform.yx / (1 << 16),
(float)ft_document->transform.yy / (1 << 16),
(float)ft_document->delta.x / 64 * document_width / ft_metrics.x_ppem,
-(float)ft_document->delta.y / 64 * document_height / ft_metrics.y_ppem
};
float x_svg_to_out = ft_metrics.x_ppem / document_width;
float y_svg_to_out = ft_metrics.y_ppem / document_height;
plutovg_matrix_t matrix;
plutovg_matrix_init_scale(&matrix, x_svg_to_out, y_svg_to_out);
plutovg_matrix_multiply(&matrix, &transform, &matrix);
char buffer[64];
char* id = NULL;
if(start_glyph_id < end_glyph_id) {
sprintf(buffer, "glyph%u", ft_slot->glyph_index);
id = buffer;
}
plutovg_rect_t extents;
if(!plutosvg_document_extents(document, id, &extents)) {
return FT_Err_Invalid_SVG_Document;
}
plutovg_matrix_map_rect(&matrix, &extents, &extents);
ft_slot->bitmap_left = (FT_Int)extents.x;
ft_slot->bitmap_top = (FT_Int)-extents.y;
ft_slot->bitmap.rows = (unsigned int)ceilf(extents.h);
ft_slot->bitmap.width = (unsigned int)ceilf(extents.w);
ft_slot->bitmap.pitch = (int)ft_slot->bitmap.width * 4;
ft_slot->bitmap.pixel_mode = FT_PIXEL_MODE_BGRA;
float metrics_width = extents.w;
float metrics_height = extents.h;
float horiBearingX = extents.x;
float horiBearingY = -extents.y;
float vertBearingX = ft_slot->metrics.horiBearingX / 64.f - ft_slot->metrics.horiAdvance / 64.f / 2;
float vertBearingY = (ft_slot->metrics.vertAdvance / 64.f - ft_slot->metrics.height / 64.f) / 2;
ft_slot->metrics.width = (FT_Pos)roundf(metrics_width * 64);
ft_slot->metrics.height = (FT_Pos)roundf(metrics_height * 64);
ft_slot->metrics.horiBearingX = (FT_Pos)(horiBearingX * 64);
ft_slot->metrics.horiBearingY = (FT_Pos)(horiBearingY * 64);
ft_slot->metrics.vertBearingX = (FT_Pos)(vertBearingX * 64);
ft_slot->metrics.vertBearingY = (FT_Pos)(vertBearingY * 64);
if(ft_slot->metrics.vertAdvance == 0)
ft_slot->metrics.vertAdvance = (FT_Pos)(metrics_height * 1.2f * 64);
if(ft_cache) {
state->document = document;
state->extents = extents;
state->matrix = matrix;
}
return FT_Err_Ok;
}
/**
* @brief FreeType SVG renderer hooks.
*
* This structure is passed to FreeType via FT_Property_Set to delegate SVG glyph
* rendering to PlutoSVG.
*/
static SVG_RendererHooks plutosvg_ft_hooks = {
(SVG_Lib_Init_Func)plutosvg_ft_init,
(SVG_Lib_Free_Func)plutosvg_ft_free,
(SVG_Lib_Render_Func)plutosvg_ft_render,
(SVG_Lib_Preset_Slot_Func)plutosvg_ft_preset_slot
};
#endif // PLUTOSVG_FT_H