mirror of https://github.com/Chlumsky/msdfgen.git
Compare commits
11 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
e3ced1e362 | |
|
|
9e7901aa7c | |
|
|
15833154b3 | |
|
|
54535fc7c0 | |
|
|
6574da1310 | |
|
|
03889564a5 | |
|
|
84bfb2a402 | |
|
|
5a88b0c2b9 | |
|
|
755c45b444 | |
|
|
5ec8cef4c2 | |
|
|
cddd6c7308 |
|
|
@ -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`)
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
20
README.md
20
README.md
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
@ -199,9 +199,9 @@ SignedDistance QuadraticSegment::signedDistance(Point2 origin, double ¶m) 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 ¶m) 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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
|
||||
#ifndef _CRT_SECURE_NO_WARNINGS
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
#include "save-bmp.h"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
|
||||
#ifndef _CRT_SECURE_NO_WARNINGS
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
#include "save-tiff.h"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
|
||||
#ifndef _CRT_SECURE_NO_WARNINGS
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
#include "shape-description.h"
|
||||
|
||||
#include <cstdlib>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
|
||||
#ifndef _CRT_SECURE_NO_WARNINGS
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
#include "save-png.h"
|
||||
|
||||
#include <cstdio>
|
||||
|
|
|
|||
9
main.cpp
9
main.cpp
|
|
@ -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:;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
Loading…
Reference in New Issue