Compare commits

..

11 Commits

Author SHA1 Message Date
Chlumsky e3ced1e362 Cubic curve distance optimization 2025-06-20 16:50:26 +02:00
Chlumsky 9e7901aa7c Partial revert of cubic curve distance 2025-06-20 16:40:02 +02:00
Chlumsky 15833154b3 Cubic curve distance improvement 2025-06-20 13:03:17 +02:00
Chlumsky 54535fc7c0 Corner deconvergence polarity fix 2025-06-19 18:07:27 +02:00
Chlumsky 6574da1310 Version 1.12.1 2025-05-31 19:48:24 +02:00
Chlumsky 03889564a5 Minor installation adjustments #216 2025-03-09 11:31:02 +01:00
Chlumsky 84bfb2a402 Fix MTSDF error correction call in CLI mode with -scanline enabled #219 2025-02-10 20:00:05 +01:00
Chlumsky 5a88b0c2b9 Minor warning fixes 2024-09-28 13:12:20 +02:00
Chlumsky 755c45b444 Shapeless error correction no transform overload 2024-06-02 11:30:55 +02:00
Chlumsky 5ec8cef4c2 Improved error correction consistency for inverse Y-axis 2024-06-01 16:05:25 +02:00
Chlumsky cddd6c7308 Error correction fix for inverse Y-axis 2024-05-31 19:23:59 +02:00
22 changed files with 260 additions and 36 deletions

View File

@ -1,4 +1,10 @@
### Version 1.12.1 (2025-05-31)
- Fixed a bug applying error correction incorrectly if shape's Y-axis is inverted (mainly affected SVG input)
- Fixed error correction not being applied in the standalone executable in MTSDF mode with the `-scanline` option (default in non-Skia builds)
- Minor CMake adjustments and warning fixes
## Version 1.12 (2024-05-18)
- Added the possibility to specify asymmetrical distance range (`-arange`, `-apxrange`)

View File

@ -8,6 +8,8 @@ option(MSDFGEN_USE_VCPKG "Use vcpkg package manager to link project dependencies
option(MSDFGEN_USE_OPENMP "Build with OpenMP support for multithreaded code" OFF)
option(MSDFGEN_USE_CPP11 "Build with C++11 enabled" ON)
option(MSDFGEN_USE_SKIA "Build with the Skia library" ON)
option(MSDFGEN_DISABLE_SVG "Disable SVG support" OFF)
option(MSDFGEN_DISABLE_PNG "Disable PNG support" OFF)
option(MSDFGEN_INSTALL "Generate installation target" OFF)
option(MSDFGEN_DYNAMIC_RUNTIME "Link dynamic runtime library instead of static" OFF)
option(BUILD_SHARED_LIBS "Generate dynamic library files instead of static" OFF)
@ -259,6 +261,11 @@ if(MSDFGEN_INSTALL)
endif()
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/msdfgen-config.h.in" msdfgen-config.h)
if (NOT MSDFGEN_INSTALL_NO_GLOBAL_INCLUDE)
write_file("${CMAKE_CURRENT_BINARY_DIR}/msdfgen.h" "\n#pragma once\n\n#include \"msdfgen/msdfgen.h\"")
write_file("${CMAKE_CURRENT_BINARY_DIR}/msdfgen-ext.h" "\n#pragma once\n\n#include \"msdfgen/msdfgen-ext.h\"")
endif()
write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/msdfgenConfigVersion.cmake"
VERSION ${PROJECT_VERSION}
@ -288,6 +295,9 @@ if(MSDFGEN_INSTALL)
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/msdfgen/core
)
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/msdfgen.h" DESTINATION include/msdfgen)
if (NOT MSDFGEN_INSTALL_NO_GLOBAL_INCLUDE)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/msdfgen.h" DESTINATION include)
endif()
if(MSVC AND BUILD_SHARED_LIBS)
install(FILES $<TARGET_PDB_FILE:msdfgen-core> DESTINATION ${CMAKE_INSTALL_BINDIR} OPTIONAL)
endif()
@ -301,6 +311,9 @@ if(MSDFGEN_INSTALL)
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/msdfgen/ext
)
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/msdfgen-ext.h" DESTINATION include/msdfgen)
if (NOT MSDFGEN_INSTALL_NO_GLOBAL_INCLUDE)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/msdfgen-ext.h" DESTINATION include)
endif()
if(MSVC AND BUILD_SHARED_LIBS)
install(FILES $<TARGET_PDB_FILE:msdfgen-ext> DESTINATION ${CMAKE_INSTALL_BINDIR} OPTIONAL)
endif()

View File

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2014 - 2024 Viktor Chlumsky
Copyright (c) 2014 - 2025 Viktor Chlumsky
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -113,25 +113,23 @@ in order to generate a distance field. Please note that all classes and function
Example:
```c++
#include "msdfgen.h"
#include "msdfgen-ext.h"
#include <msdfgen.h>
#include <msdfgen-ext.h>
using namespace msdfgen;
int main() {
FreetypeHandle *ft = initializeFreetype();
if (ft) {
FontHandle *font = loadFont(ft, "C:\\Windows\\Fonts\\arialbd.ttf");
if (font) {
if (FreetypeHandle *ft = initializeFreetype()) {
if (FontHandle *font = loadFont(ft, "C:\\Windows\\Fonts\\arialbd.ttf")) {
Shape shape;
if (loadGlyph(shape, font, 'A')) {
if (loadGlyph(shape, font, 'A', FONT_SCALING_EM_NORMALIZED)) {
shape.normalize();
// max. angle
edgeColoringSimple(shape, 3.0);
// output width, height
Bitmap<float, 3> msdf(32, 32);
// scale, translation
SDFTransformation t(Projection(1.0, Vector2(4.0, 4.0)), Range(4.0));
// scale, translation (in em's)
SDFTransformation t(Projection(32.0, Vector2(0.125, 0.125)), Range(0.125));
generateMSDF(msdf, shape, t);
savePng(msdf, "output.png");
}
@ -141,7 +139,6 @@ int main() {
}
return 0;
}
```
## Using a multi-channel distance field
@ -150,6 +147,9 @@ Using a multi-channel distance field generated by this program is similarly simp
The only additional operation is computing the **median** of the three channels inside the fragment shader,
right after sampling the distance field. This signed distance value can then be used the same way as usual.
**Important:** Make sure to interpret the MSDF color channels in linear space just like the alpha channel and not as sRGB,
even if the image format (PNG, BMP) may suggest otherwise.
The following is an example GLSL fragment shader with anti-aliasing:
```glsl

View File

@ -6,11 +6,17 @@ set(MSDFGEN_USE_VCPKG @MSDFGEN_USE_VCPKG@)
set(MSDFGEN_USE_OPENMP @MSDFGEN_USE_OPENMP@)
set(MSDFGEN_USE_SKIA @MSDFGEN_USE_SKIA@)
set(MSDFGEN_STANDALONE_AVAILABLE @MSDFGEN_BUILD_STANDALONE@)
set(MSDFGEN_DISABLE_SVG @MSDFGEN_DISABLE_SVG@)
set(MSDFGEN_DISABLE_PNG @MSDFGEN_DISABLE_PNG@)
if(NOT MSDFGEN_CORE_ONLY)
find_dependency(Freetype REQUIRED)
find_dependency(tinyxml2 REQUIRED)
find_dependency(PNG REQUIRED)
if(NOT MSDFGEN_DISABLE_SVG)
find_dependency(tinyxml2 REQUIRED)
endif()
if(NOT MSDFGEN_DISABLE_PNG)
find_dependency(PNG REQUIRED)
endif()
endif()
if(MSDFGEN_USE_SKIA)
find_dependency(Threads REQUIRED)
@ -30,12 +36,16 @@ if(MSDFGEN_CORE_ONLY)
if(${CMAKE_VERSION} VERSION_LESS "3.18.0")
set_target_properties(msdfgen::msdfgen-core PROPERTIES IMPORTED_GLOBAL TRUE)
endif()
add_library(msdfgen::msdfgen ALIAS msdfgen::msdfgen-core)
if(NOT TARGET msdfgen::msdfgen)
add_library(msdfgen::msdfgen ALIAS msdfgen::msdfgen-core)
endif()
else()
if(${CMAKE_VERSION} VERSION_LESS "3.18.0")
set_target_properties(msdfgen::msdfgen-full PROPERTIES IMPORTED_GLOBAL TRUE)
endif()
add_library(msdfgen::msdfgen ALIAS msdfgen::msdfgen-full)
if(NOT TARGET msdfgen::msdfgen)
add_library(msdfgen::msdfgen ALIAS msdfgen::msdfgen-full)
endif()
endif()
if(MSDFGEN_STANDALONE_AVAILABLE)

View File

@ -89,6 +89,8 @@ public:
bool protectedFlag;
inline ShapeDistanceChecker(const BitmapConstRef<float, N> &sdf, const Shape &shape, const Projection &projection, DistanceMapping distanceMapping, double minImproveRatio) : distanceFinder(shape), sdf(sdf), distanceMapping(distanceMapping), minImproveRatio(minImproveRatio) {
texelSize = projection.unprojectVector(Vector2(1));
if (shape.inverseYAxis)
texelSize.y = -texelSize.y;
}
inline ArtifactClassifier classifier(const Vector2 &direction, double span) {
return ArtifactClassifier(this, direction, span);
@ -127,10 +129,10 @@ void MSDFErrorCorrection::protectCorners(const Shape &shape) {
if (!(commonColor&(commonColor-1))) {
// Find the four texels that envelop the corner and mark them as protected.
Point2 p = transformation.project((*edge)->point(0));
if (shape.inverseYAxis)
p.y = stencil.height-p.y;
int l = (int) floor(p.x-.5);
int b = (int) floor(p.y-.5);
if (shape.inverseYAxis)
b = stencil.height-b-2;
int r = l+1;
int t = b+1;
// Check that the positions are within bounds.

View File

@ -3,6 +3,7 @@
#include <cstdlib>
#include "arithmetics.hpp"
#include "convergent-curve-ordering.h"
#define DECONVERGE_OVERSHOOT 1.11111111111111111 // moves control points slightly more than necessary to account for floating-point errors
@ -79,8 +80,7 @@ void Shape::normalize() {
if (dotProduct(prevDir, curDir) < MSDFGEN_CORNER_DOT_EPSILON-1) {
double factor = DECONVERGE_OVERSHOOT*sqrt(1-(MSDFGEN_CORNER_DOT_EPSILON-1)*(MSDFGEN_CORNER_DOT_EPSILON-1))/(MSDFGEN_CORNER_DOT_EPSILON-1);
Vector2 axis = factor*(curDir-prevDir).normalize();
// Determine curve ordering using third-order derivative (t = 0) of crossProduct((*prevEdge)->point(1-t)-p0, (*edge)->point(t)-p0) where p0 is the corner (*edge)->point(0)
if (crossProduct((*prevEdge)->directionChange(1), (*edge)->direction(0))+crossProduct((*edge)->directionChange(0), (*prevEdge)->direction(1)) < 0)
if (convergentCurveOrdering(*prevEdge, *edge) < 0)
axis = -axis;
deconvergeEdge(*prevEdge, 1, axis.getOrthogonal(true));
deconvergeEdge(*edge, 0, axis.getOrthogonal(false));

View File

@ -0,0 +1,140 @@
#include "convergent-curve-ordering.h"
#include "arithmetics.hpp"
#include "Vector2.hpp"
/*
* For non-degenerate curves A(t), B(t) (ones where all control points are distinct) both originating at P = A(0) = B(0) = *corner,
* we are computing the limit of
*
* sign(crossProduct( A(t / |A'(0)|) - P, B(t / |B'(0)|) - P ))
*
* for t -> 0 from 1. Of note is that the curves' parameter has to be normed by the first derivative at P,
* which ensures that the limit approaches P at the same rate along both curves - omitting this was the main error of earlier versions of deconverge.
*
* For degenerate cubic curves (ones where the first control point equals the origin point), the denominator |A'(0)| is zero,
* so to address that, we approach with the square root of t and use the derivative of A(sqrt(t)), which at t = 0 equals A''(0)/2
* Therefore, in these cases, we replace one factor of the cross product with A(sqrt(2*t / |A''(0)|)) - P
*
* The cross product results in a polynomial (in respect to t or t^2 in the degenerate case),
* the limit of sign of which at zero can be determined by the lowest order non-zero derivative,
* which equals to the sign of the first non-zero polynomial coefficient in the order of increasing exponents.
*
* The polynomial's constant and linear terms are zero, so the first derivative is definitely zero as well.
* The second derivative is assumed to be zero (or near zero) due to the curves being convergent - this is an input requirement
* (otherwise the correct result is the sign of the cross product of their directions at t = 0).
* Therefore, we skip the first and second derivatives.
*/
namespace msdfgen {
static void simplifyDegenerateCurve(Point2 *controlPoints, int &order) {
if (order == 3 && (controlPoints[1] == controlPoints[0] || controlPoints[1] == controlPoints[3]) && (controlPoints[2] == controlPoints[0] || controlPoints[2] == controlPoints[3])) {
controlPoints[1] = controlPoints[3];
order = 1;
}
if (order == 2 && (controlPoints[1] == controlPoints[0] || controlPoints[1] == controlPoints[2])) {
controlPoints[1] = controlPoints[2];
order = 1;
}
if (order == 1 && controlPoints[0] == controlPoints[1])
order = 0;
}
int convergentCurveOrdering(const Point2 *corner, int controlPointsBefore, int controlPointsAfter) {
if (!(controlPointsBefore > 0 && controlPointsAfter > 0))
return 0;
Vector2 a1, a2, a3, b1, b2, b3;
a1 = *(corner-1)-*corner;
b1 = *(corner+1)-*corner;
if (controlPointsBefore >= 2)
a2 = *(corner-2)-*(corner-1)-a1;
if (controlPointsAfter >= 2)
b2 = *(corner+2)-*(corner+1)-b1;
if (controlPointsBefore >= 3) {
a3 = *(corner-3)-*(corner-2)-(*(corner-2)-*(corner-1))-a2;
a2 *= 3;
}
if (controlPointsAfter >= 3) {
b3 = *(corner+3)-*(corner+2)-(*(corner+2)-*(corner+1))-b2;
b2 *= 3;
}
a1 *= controlPointsBefore;
b1 *= controlPointsAfter;
// Non-degenerate case
if (a1 && b1) {
double as = a1.length();
double bs = b1.length();
// Third derivative
if (double d = as*crossProduct(a1, b2) + bs*crossProduct(a2, b1))
return sign(d);
// Fourth derivative
if (double d = as*as*crossProduct(a1, b3) + as*bs*crossProduct(a2, b2) + bs*bs*crossProduct(a3, b1))
return sign(d);
// Fifth derivative
if (double d = as*crossProduct(a2, b3) + bs*crossProduct(a3, b2))
return sign(d);
// Sixth derivative
return sign(crossProduct(a3, b3));
}
// Degenerate curve after corner (control point after corner equals corner)
int s = 1;
if (a1) { // !b1
// Swap aN <-> bN and handle in if (b1)
b1 = a1;
a1 = b2, b2 = a2, a2 = a1;
a1 = b3, b3 = a3, a3 = a1;
s = -1; // make sure to also flip output
}
// Degenerate curve before corner (control point before corner equals corner)
if (b1) { // !a1
// Two-and-a-half-th derivative
if (double d = crossProduct(a3, b1))
return s*sign(d);
// Third derivative
if (double d = crossProduct(a2, b2))
return s*sign(d);
// Three-and-a-half-th derivative
if (double d = crossProduct(a3, b2))
return s*sign(d);
// Fourth derivative
if (double d = crossProduct(a2, b3))
return s*sign(d);
// Four-and-a-half-th derivative
return s*sign(crossProduct(a3, b3));
}
// Degenerate curves on both sides of the corner (control point before and after corner equals corner)
{ // !a1 && !b1
// Two-and-a-half-th derivative
if (double d = sqrt(a2.length())*crossProduct(a2, b3) + sqrt(b2.length())*crossProduct(a3, b2))
return sign(d);
// Third derivative
return sign(crossProduct(a3, b3));
}
}
int convergentCurveOrdering(const EdgeSegment *a, const EdgeSegment *b) {
Point2 controlPoints[12];
Point2 *corner = controlPoints+4;
Point2 *aCpTmp = controlPoints+8;
int aOrder = int(a->type());
int bOrder = int(b->type());
if (!(aOrder >= 1 && aOrder <= 3 && bOrder >= 1 && bOrder <= 3)) {
// Not implemented - only linear, quadratic, and cubic curves supported
return 0;
}
for (int i = 0; i <= aOrder; ++i)
aCpTmp[i] = a->controlPoints()[i];
for (int i = 0; i <= bOrder; ++i)
corner[i] = b->controlPoints()[i];
if (aCpTmp[aOrder] != *corner)
return 0;
simplifyDegenerateCurve(aCpTmp, aOrder);
simplifyDegenerateCurve(corner, bOrder);
for (int i = 0; i < aOrder; ++i)
corner[i-aOrder] = aCpTmp[i];
return convergentCurveOrdering(corner, aOrder, bOrder);
}
}

View File

@ -0,0 +1,11 @@
#pragma once
#include "edge-segments.h"
namespace msdfgen {
/// For curves a, b converging at P = a->point(1) = b->point(0) with the same (opposite) direction, determines the relative ordering in which they exit P (i.e. whether a is to the left or right of b at the smallest positive radius around P)
int convergentCurveOrdering(const EdgeSegment *a, const EdgeSegment *b);
}

View File

@ -199,9 +199,9 @@ SignedDistance QuadraticSegment::signedDistance(Point2 origin, double &param) co
double minDistance = nonZeroSign(crossProduct(epDir, qa))*qa.length(); // distance from A
param = -dotProduct(qa, epDir)/dotProduct(epDir, epDir);
{
epDir = direction(1);
double distance = (p[2]-origin).length(); // distance from B
if (distance < fabs(minDistance)) {
epDir = direction(1);
minDistance = nonZeroSign(crossProduct(epDir, p[2]-origin))*distance;
param = dotProduct(origin-p[1], epDir)/dotProduct(epDir, epDir);
}
@ -235,25 +235,31 @@ SignedDistance CubicSegment::signedDistance(Point2 origin, double &param) const
double minDistance = nonZeroSign(crossProduct(epDir, qa))*qa.length(); // distance from A
param = -dotProduct(qa, epDir)/dotProduct(epDir, epDir);
{
epDir = direction(1);
double distance = (p[3]-origin).length(); // distance from B
if (distance < fabs(minDistance)) {
epDir = direction(1);
minDistance = nonZeroSign(crossProduct(epDir, p[3]-origin))*distance;
param = dotProduct(epDir-(p[3]-origin), epDir)/dotProduct(epDir, epDir);
}
}
// Iterative minimum distance search
for (int i = 0; i <= MSDFGEN_CUBIC_SEARCH_STARTS; ++i) {
double t = (double) i/MSDFGEN_CUBIC_SEARCH_STARTS;
double t = 1./MSDFGEN_CUBIC_SEARCH_STARTS*i;
Vector2 qe = qa+3*t*ab+3*t*t*br+t*t*t*as;
for (int step = 0; step < MSDFGEN_CUBIC_SEARCH_STEPS; ++step) {
// Improve t
Vector2 d1 = 3*ab+6*t*br+3*t*t*as;
Vector2 d2 = 6*br+6*t*as;
t -= dotProduct(qe, d1)/(dotProduct(d1, d1)+dotProduct(qe, d2));
if (t <= 0 || t >= 1)
break;
qe = qa+3*t*ab+3*t*t*br+t*t*t*as;
Vector2 d1 = 3*ab+6*t*br+3*t*t*as;
Vector2 d2 = 6*br+6*t*as;
double improvedT = t-dotProduct(qe, d1)/(dotProduct(d1, d1)+dotProduct(qe, d2));
if (improvedT > 0 && improvedT < 1) {
int remainingSteps = MSDFGEN_CUBIC_SEARCH_STEPS;
do {
t = improvedT;
qe = qa+3*t*ab+3*t*t*br+t*t*t*as;
d1 = 3*ab+6*t*br+3*t*t*as;
if (!--remainingSteps)
break;
d2 = 6*br+6*t*as;
improvedT = t-dotProduct(qe, d1)/(dotProduct(d1, d1)+dotProduct(qe, d2));
} while (improvedT > 0 && improvedT < 1);
double distance = qe.length();
if (distance < fabs(minDistance)) {
minDistance = nonZeroSign(crossProduct(d1, qe))*distance;

View File

@ -84,6 +84,12 @@ void msdfFastDistanceErrorCorrection(const BitmapRef<float, 3> &sdf, const Proje
void msdfFastDistanceErrorCorrection(const BitmapRef<float, 4> &sdf, const Projection &projection, Range range, double minDeviationRatio) {
msdfErrorCorrectionShapeless(sdf, SDFTransformation(projection, range), minDeviationRatio, false);
}
void msdfFastDistanceErrorCorrection(const BitmapRef<float, 3> &sdf, Range pxRange, double minDeviationRatio) {
msdfErrorCorrectionShapeless(sdf, SDFTransformation(Projection(), pxRange), minDeviationRatio, false);
}
void msdfFastDistanceErrorCorrection(const BitmapRef<float, 4> &sdf, Range pxRange, double minDeviationRatio) {
msdfErrorCorrectionShapeless(sdf, SDFTransformation(Projection(), pxRange), minDeviationRatio, false);
}
void msdfFastEdgeErrorCorrection(const BitmapRef<float, 3> &sdf, const SDFTransformation &transformation, double minDeviationRatio) {
msdfErrorCorrectionShapeless(sdf, transformation, minDeviationRatio, true);
@ -97,6 +103,12 @@ void msdfFastEdgeErrorCorrection(const BitmapRef<float, 3> &sdf, const Projectio
void msdfFastEdgeErrorCorrection(const BitmapRef<float, 4> &sdf, const Projection &projection, Range range, double minDeviationRatio) {
msdfErrorCorrectionShapeless(sdf, SDFTransformation(projection, range), minDeviationRatio, true);
}
void msdfFastEdgeErrorCorrection(const BitmapRef<float, 3> &sdf, Range pxRange, double minDeviationRatio) {
msdfErrorCorrectionShapeless(sdf, SDFTransformation(Projection(), pxRange), minDeviationRatio, true);
}
void msdfFastEdgeErrorCorrection(const BitmapRef<float, 4> &sdf, Range pxRange, double minDeviationRatio) {
msdfErrorCorrectionShapeless(sdf, SDFTransformation(Projection(), pxRange), minDeviationRatio, true);
}
// Legacy version

View File

@ -22,12 +22,16 @@ void msdfFastDistanceErrorCorrection(const BitmapRef<float, 3> &sdf, const SDFTr
void msdfFastDistanceErrorCorrection(const BitmapRef<float, 4> &sdf, const SDFTransformation &transformation, double minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio);
void msdfFastDistanceErrorCorrection(const BitmapRef<float, 3> &sdf, const Projection &projection, Range range, double minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio);
void msdfFastDistanceErrorCorrection(const BitmapRef<float, 4> &sdf, const Projection &projection, Range range, double minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio);
void msdfFastDistanceErrorCorrection(const BitmapRef<float, 3> &sdf, Range pxRange, double minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio);
void msdfFastDistanceErrorCorrection(const BitmapRef<float, 4> &sdf, Range pxRange, double minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio);
/// Applies the simplified error correction to edges only (EDGE_ONLY mode). Does not need shape or translation.
void msdfFastEdgeErrorCorrection(const BitmapRef<float, 3> &sdf, const SDFTransformation &transformation, double minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio);
void msdfFastEdgeErrorCorrection(const BitmapRef<float, 4> &sdf, const SDFTransformation &transformation, double minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio);
void msdfFastEdgeErrorCorrection(const BitmapRef<float, 3> &sdf, const Projection &projection, Range range, double minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio);
void msdfFastEdgeErrorCorrection(const BitmapRef<float, 4> &sdf, const Projection &projection, Range range, double minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio);
void msdfFastEdgeErrorCorrection(const BitmapRef<float, 3> &sdf, Range pxRange, double minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio);
void msdfFastEdgeErrorCorrection(const BitmapRef<float, 4> &sdf, Range pxRange, double minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio);
/// The original version of the error correction algorithm.
void msdfErrorCorrection_legacy(const BitmapRef<float, 3> &output, const Vector2 &threshold);

View File

@ -33,7 +33,7 @@ void distanceSignCorrection(const BitmapRef<float, 1> &sdf, const Shape &shape,
template <int N>
static void multiDistanceSignCorrection(const BitmapRef<float, N> &sdf, const Shape &shape, const Projection &projection, FillRule fillRule) {
int w = sdf.width, h = sdf.height;
if (!(w*h))
if (!(w && h))
return;
Scanline scanline;
bool ambiguous = false;

View File

@ -1,5 +1,7 @@
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif
#include "save-bmp.h"

View File

@ -1,5 +1,7 @@
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif
#include "save-tiff.h"

View File

@ -1,5 +1,8 @@
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif
#include "shape-description.h"
#include <cstdlib>

View File

@ -1,6 +1,11 @@
#ifndef _USE_MATH_DEFINES
#define _USE_MATH_DEFINES
#endif
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif
#include "import-svg.h"
#ifndef MSDFGEN_DISABLE_SVG

View File

@ -1,5 +1,8 @@
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif
#include "save-png.h"
#include <cstdio>

View File

@ -2,14 +2,19 @@
/*
* MULTI-CHANNEL SIGNED DISTANCE FIELD GENERATOR - standalone console program
* --------------------------------------------------------------------------
* A utility by Viktor Chlumsky, (c) 2014 - 2024
* A utility by Viktor Chlumsky, (c) 2014 - 2025
*
*/
#ifdef MSDFGEN_STANDALONE
#ifndef _USE_MATH_DEFINES
#define _USE_MATH_DEFINES
#endif
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif
#include <cstdlib>
#include <cstdio>
#include <cmath>
@ -1285,7 +1290,7 @@ int main(int argc, const char *const *argv) {
break;
case MULTI_AND_TRUE:
distanceSignCorrection(mtsdf, shape, transformation, fillRule);
msdfErrorCorrection(msdf, shape, transformation, postErrorCorrectionConfig);
msdfErrorCorrection(mtsdf, shape, transformation, postErrorCorrectionConfig);
break;
default:;
}

View File

@ -4,7 +4,7 @@
/*
* MULTI-CHANNEL SIGNED DISTANCE FIELD GENERATOR
* ---------------------------------------------
* A utility by Viktor Chlumsky, (c) 2014 - 2024
* A utility by Viktor Chlumsky, (c) 2014 - 2025
*
* The extension module provides ways to easily load input and save output using popular formats.
*

View File

@ -4,7 +4,7 @@
/*
* MULTI-CHANNEL SIGNED DISTANCE FIELD GENERATOR
* ---------------------------------------------
* A utility by Viktor Chlumsky, (c) 2014 - 2024
* A utility by Viktor Chlumsky, (c) 2014 - 2025
*
* The technique used to generate multi-channel distance fields in this code
* has been developed by Viktor Chlumsky in 2014 for his master's thesis,

View File

@ -1,7 +1,7 @@
{
"$schema": "https://raw.githubusercontent.com/microsoft/vcpkg-tool/master/docs/vcpkg.schema.json",
"name": "msdfgen",
"version": "1.12.0",
"version": "1.12.1",
"default-features": [
"extensions",
"geometry-preprocessing",