msdfgen: Update to 1.12

This commit is contained in:
Rémi Verschelde 2025-01-09 21:16:08 +01:00
parent 24d74510e5
commit c97c7b73e6
41 changed files with 1038 additions and 363 deletions

View File

@ -469,7 +469,7 @@ License: BSD-2-clause
Files: ./thirdparty/msdfgen/ Files: ./thirdparty/msdfgen/
Comment: Multi-channel signed distance field generator Comment: Multi-channel signed distance field generator
Copyright: 2016-2022, Viktor Chlumsky Copyright: 2014-2024, Viktor Chlumsky
License: Expat License: Expat
Files: ./thirdparty/nvapi/nvapi_minimal.h Files: ./thirdparty/nvapi/nvapi_minimal.h

View File

@ -16,6 +16,7 @@ if env["builtin_msdfgen"]:
thirdparty_dir = "#thirdparty/msdfgen/" thirdparty_dir = "#thirdparty/msdfgen/"
thirdparty_sources = [ thirdparty_sources = [
"core/Contour.cpp", "core/Contour.cpp",
"core/DistanceMapping.cpp",
"core/EdgeHolder.cpp", "core/EdgeHolder.cpp",
"core/MSDFErrorCorrection.cpp", "core/MSDFErrorCorrection.cpp",
"core/Projection.cpp", "core/Projection.cpp",
@ -26,10 +27,15 @@ if env["builtin_msdfgen"]:
"core/edge-segments.cpp", "core/edge-segments.cpp",
"core/edge-selectors.cpp", "core/edge-selectors.cpp",
"core/equation-solver.cpp", "core/equation-solver.cpp",
# "core/export-svg.cpp",
"core/msdf-error-correction.cpp", "core/msdf-error-correction.cpp",
"core/msdfgen.cpp", "core/msdfgen.cpp",
"core/rasterization.cpp", "core/rasterization.cpp",
"core/render-sdf.cpp", "core/render-sdf.cpp",
# "core/save-bmp.cpp",
# "core/save-fl32.cpp",
# "core/save-rgba.cpp",
# "core/save-tiff.cpp",
"core/sdf-error-estimation.cpp", "core/sdf-error-estimation.cpp",
"core/shape-description.cpp", "core/shape-description.cpp",
] ]

View File

@ -123,6 +123,7 @@ if env["msdfgen_enabled"] and env["freetype_enabled"]:
thirdparty_msdfgen_dir = "../../../thirdparty/msdfgen/" thirdparty_msdfgen_dir = "../../../thirdparty/msdfgen/"
thirdparty_msdfgen_sources = [ thirdparty_msdfgen_sources = [
"core/Contour.cpp", "core/Contour.cpp",
"core/DistanceMapping.cpp",
"core/EdgeHolder.cpp", "core/EdgeHolder.cpp",
"core/MSDFErrorCorrection.cpp", "core/MSDFErrorCorrection.cpp",
"core/Projection.cpp", "core/Projection.cpp",
@ -133,10 +134,15 @@ if env["msdfgen_enabled"] and env["freetype_enabled"]:
"core/edge-segments.cpp", "core/edge-segments.cpp",
"core/edge-selectors.cpp", "core/edge-selectors.cpp",
"core/equation-solver.cpp", "core/equation-solver.cpp",
# "core/export-svg.cpp",
"core/msdf-error-correction.cpp", "core/msdf-error-correction.cpp",
"core/msdfgen.cpp", "core/msdfgen.cpp",
"core/rasterization.cpp", "core/rasterization.cpp",
"core/render-sdf.cpp", "core/render-sdf.cpp",
# "core/save-bmp.cpp",
# "core/save-fl32.cpp",
# "core/save-rgba.cpp",
# "core/save-tiff.cpp",
"core/sdf-error-estimation.cpp", "core/sdf-error-estimation.cpp",
"core/shape-description.cpp", "core/shape-description.cpp",
] ]

View File

@ -69,6 +69,7 @@ using namespace godot;
#ifdef _MSC_VER #ifdef _MSC_VER
#pragma warning(disable : 4458) #pragma warning(disable : 4458)
#endif #endif
#include <core/EdgeHolder.h>
#include <core/ShapeDistanceFinder.h> #include <core/ShapeDistanceFinder.h>
#include <core/contour-combiners.h> #include <core/contour-combiners.h>
#include <core/edge-selectors.h> #include <core/edge-selectors.h>

View File

@ -118,6 +118,7 @@ if env["msdfgen_enabled"] and env["freetype_enabled"]:
thirdparty_msdfgen_dir = "../../../thirdparty/msdfgen/" thirdparty_msdfgen_dir = "../../../thirdparty/msdfgen/"
thirdparty_msdfgen_sources = [ thirdparty_msdfgen_sources = [
"core/Contour.cpp", "core/Contour.cpp",
"core/DistanceMapping.cpp",
"core/EdgeHolder.cpp", "core/EdgeHolder.cpp",
"core/MSDFErrorCorrection.cpp", "core/MSDFErrorCorrection.cpp",
"core/Projection.cpp", "core/Projection.cpp",
@ -128,10 +129,15 @@ if env["msdfgen_enabled"] and env["freetype_enabled"]:
"core/edge-segments.cpp", "core/edge-segments.cpp",
"core/edge-selectors.cpp", "core/edge-selectors.cpp",
"core/equation-solver.cpp", "core/equation-solver.cpp",
# "core/export-svg.cpp",
"core/msdf-error-correction.cpp", "core/msdf-error-correction.cpp",
"core/msdfgen.cpp", "core/msdfgen.cpp",
"core/rasterization.cpp", "core/rasterization.cpp",
"core/render-sdf.cpp", "core/render-sdf.cpp",
# "core/save-bmp.cpp",
# "core/save-fl32.cpp",
# "core/save-rgba.cpp",
# "core/save-tiff.cpp",
"core/sdf-error-estimation.cpp", "core/sdf-error-estimation.cpp",
"core/shape-description.cpp", "core/shape-description.cpp",
] ]

View File

@ -64,6 +64,7 @@ using namespace godot;
#ifdef _MSC_VER #ifdef _MSC_VER
#pragma warning(disable : 4458) #pragma warning(disable : 4458)
#endif #endif
#include <core/EdgeHolder.h>
#include <core/ShapeDistanceFinder.h> #include <core/ShapeDistanceFinder.h>
#include <core/contour-combiners.h> #include <core/contour-combiners.h>
#include <core/edge-selectors.h> #include <core/edge-selectors.h>

View File

@ -763,7 +763,7 @@ Collection of single-file libraries used in Godot components.
## msdfgen ## msdfgen
- Upstream: https://github.com/Chlumsky/msdfgen - Upstream: https://github.com/Chlumsky/msdfgen
- Version: 1.11 (f12d7ca00091a632a289865b85c3f2e0bfc6542d, 2023) - Version: 1.12 (85e8b3d47b3d1a42e4a5ebda0a24fb1cc2e669e0, 2024)
- License: MIT - License: MIT
Files extracted from the upstream source: Files extracted from the upstream source:

View File

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

View File

@ -0,0 +1,27 @@
#include "DistanceMapping.h"
namespace msdfgen {
DistanceMapping DistanceMapping::inverse(Range range) {
double rangeWidth = range.upper-range.lower;
return DistanceMapping(rangeWidth, range.lower/(rangeWidth ? rangeWidth : 1));
}
DistanceMapping::DistanceMapping() : scale(1), translate(0) { }
DistanceMapping::DistanceMapping(Range range) : scale(1/(range.upper-range.lower)), translate(-range.lower) { }
double DistanceMapping::operator()(double d) const {
return scale*(d+translate);
}
double DistanceMapping::operator()(Delta d) const {
return scale*d.value;
}
DistanceMapping DistanceMapping::inverse() const {
return DistanceMapping(1/scale, -scale*translate);
}
}

View File

@ -0,0 +1,36 @@
#pragma once
#include "Range.hpp"
namespace msdfgen {
/// Linear transformation of signed distance values.
class DistanceMapping {
public:
/// Explicitly designates value as distance delta rather than an absolute distance.
class Delta {
public:
double value;
inline explicit Delta(double distanceDelta) : value(distanceDelta) { }
inline operator double() const { return value; }
};
static DistanceMapping inverse(Range range);
DistanceMapping();
DistanceMapping(Range range);
double operator()(double d) const;
double operator()(Delta d) const;
DistanceMapping inverse() const;
private:
double scale;
double translate;
inline DistanceMapping(double scale, double translate) : scale(scale), translate(translate) { }
};
}

View File

@ -9,16 +9,6 @@ void EdgeHolder::swap(EdgeHolder &a, EdgeHolder &b) {
b.edgeSegment = tmp; b.edgeSegment = tmp;
} }
EdgeHolder::EdgeHolder() : edgeSegment(NULL) { }
EdgeHolder::EdgeHolder(EdgeSegment *segment) : edgeSegment(segment) { }
EdgeHolder::EdgeHolder(Point2 p0, Point2 p1, EdgeColor edgeColor) : edgeSegment(new LinearSegment(p0, p1, edgeColor)) { }
EdgeHolder::EdgeHolder(Point2 p0, Point2 p1, Point2 p2, EdgeColor edgeColor) : edgeSegment(new QuadraticSegment(p0, p1, p2, edgeColor)) { }
EdgeHolder::EdgeHolder(Point2 p0, Point2 p1, Point2 p2, Point2 p3, EdgeColor edgeColor) : edgeSegment(new CubicSegment(p0, p1, p2, p3, edgeColor)) { }
EdgeHolder::EdgeHolder(const EdgeHolder &orig) : edgeSegment(orig.edgeSegment ? orig.edgeSegment->clone() : NULL) { } EdgeHolder::EdgeHolder(const EdgeHolder &orig) : edgeSegment(orig.edgeSegment ? orig.edgeSegment->clone() : NULL) { }
#ifdef MSDFGEN_USE_CPP11 #ifdef MSDFGEN_USE_CPP11

View File

@ -12,11 +12,11 @@ public:
/// Swaps the edges held by a and b. /// Swaps the edges held by a and b.
static void swap(EdgeHolder &a, EdgeHolder &b); static void swap(EdgeHolder &a, EdgeHolder &b);
EdgeHolder(); inline EdgeHolder() : edgeSegment() { }
EdgeHolder(EdgeSegment *segment); inline EdgeHolder(EdgeSegment *segment) : edgeSegment(segment) { }
EdgeHolder(Point2 p0, Point2 p1, EdgeColor edgeColor = WHITE); inline EdgeHolder(Point2 p0, Point2 p1, EdgeColor edgeColor = WHITE) : edgeSegment(EdgeSegment::create(p0, p1, edgeColor)) { }
EdgeHolder(Point2 p0, Point2 p1, Point2 p2, EdgeColor edgeColor = WHITE); inline EdgeHolder(Point2 p0, Point2 p1, Point2 p2, EdgeColor edgeColor = WHITE) : edgeSegment(EdgeSegment::create(p0, p1, p2, edgeColor)) { }
EdgeHolder(Point2 p0, Point2 p1, Point2 p2, Point2 p3, EdgeColor edgeColor = WHITE); inline EdgeHolder(Point2 p0, Point2 p1, Point2 p2, Point2 p3, EdgeColor edgeColor = WHITE) : edgeSegment(EdgeSegment::create(p0, p1, p2, p3, edgeColor)) { }
EdgeHolder(const EdgeHolder &orig); EdgeHolder(const EdgeHolder &orig);
#ifdef MSDFGEN_USE_CPP11 #ifdef MSDFGEN_USE_CPP11
EdgeHolder(EdgeHolder &&orig); EdgeHolder(EdgeHolder &&orig);

View File

@ -74,7 +74,7 @@ public:
// Compute the evaluated distance (interpolated median) before and after error correction, as well as the exact shape distance. // Compute the evaluated distance (interpolated median) before and after error correction, as well as the exact shape distance.
float oldPSD = median(oldMSD[0], oldMSD[1], oldMSD[2]); float oldPSD = median(oldMSD[0], oldMSD[1], oldMSD[2]);
float newPSD = median(newMSD[0], newMSD[1], newMSD[2]); float newPSD = median(newMSD[0], newMSD[1], newMSD[2]);
float refPSD = float(parent->invRange*parent->distanceFinder.distance(parent->shapeCoord+tVector*parent->texelSize)+.5); float refPSD = float(parent->distanceMapping(parent->distanceFinder.distance(parent->shapeCoord+tVector*parent->texelSize)));
// Compare the differences of the exact distance and the before and after distances. // Compare the differences of the exact distance and the before and after distances.
return parent->minImproveRatio*fabsf(newPSD-refPSD) < double(fabsf(oldPSD-refPSD)); return parent->minImproveRatio*fabsf(newPSD-refPSD) < double(fabsf(oldPSD-refPSD));
} }
@ -87,24 +87,23 @@ public:
Point2 shapeCoord, sdfCoord; Point2 shapeCoord, sdfCoord;
const float *msd; const float *msd;
bool protectedFlag; bool protectedFlag;
inline ShapeDistanceChecker(const BitmapConstRef<float, N> &sdf, const Shape &shape, const Projection &projection, double invRange, double minImproveRatio) : distanceFinder(shape), sdf(sdf), invRange(invRange), minImproveRatio(minImproveRatio) { 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)); texelSize = projection.unprojectVector(Vector2(1));
} }
inline ArtifactClassifier classifier(const Vector2 &direction, double span) { inline ArtifactClassifier classifier(const Vector2 &direction, double span) {
return ArtifactClassifier(this, direction, span); return ArtifactClassifier(this, direction, span);
} }
private: private:
ShapeDistanceFinder<ContourCombiner<PseudoDistanceSelector> > distanceFinder; ShapeDistanceFinder<ContourCombiner<PerpendicularDistanceSelector> > distanceFinder;
BitmapConstRef<float, N> sdf; BitmapConstRef<float, N> sdf;
double invRange; DistanceMapping distanceMapping;
Vector2 texelSize; Vector2 texelSize;
double minImproveRatio; double minImproveRatio;
}; };
MSDFErrorCorrection::MSDFErrorCorrection() { } MSDFErrorCorrection::MSDFErrorCorrection() { }
MSDFErrorCorrection::MSDFErrorCorrection(const BitmapRef<byte, 1> &stencil, const Projection &projection, double range) : stencil(stencil), projection(projection) { MSDFErrorCorrection::MSDFErrorCorrection(const BitmapRef<byte, 1> &stencil, const SDFTransformation &transformation) : stencil(stencil), transformation(transformation) {
invRange = 1/range;
minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio; minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio;
minImproveRatio = ErrorCorrectionConfig::defaultMinImproveRatio; minImproveRatio = ErrorCorrectionConfig::defaultMinImproveRatio;
memset(stencil.pixels, 0, sizeof(byte)*stencil.width*stencil.height); memset(stencil.pixels, 0, sizeof(byte)*stencil.width*stencil.height);
@ -127,7 +126,7 @@ void MSDFErrorCorrection::protectCorners(const Shape &shape) {
// If the color changes from prevEdge to edge, this is a corner. // If the color changes from prevEdge to edge, this is a corner.
if (!(commonColor&(commonColor-1))) { if (!(commonColor&(commonColor-1))) {
// Find the four texels that envelop the corner and mark them as protected. // Find the four texels that envelop the corner and mark them as protected.
Point2 p = projection.project((*edge)->point(0)); Point2 p = transformation.project((*edge)->point(0));
if (shape.inverseYAxis) if (shape.inverseYAxis)
p.y = stencil.height-p.y; p.y = stencil.height-p.y;
int l = (int) floor(p.x-.5); int l = (int) floor(p.x-.5);
@ -191,7 +190,7 @@ template <int N>
void MSDFErrorCorrection::protectEdges(const BitmapConstRef<float, N> &sdf) { void MSDFErrorCorrection::protectEdges(const BitmapConstRef<float, N> &sdf) {
float radius; float radius;
// Horizontal texel pairs // Horizontal texel pairs
radius = float(PROTECTION_RADIUS_TOLERANCE*projection.unprojectVector(Vector2(invRange, 0)).length()); radius = float(PROTECTION_RADIUS_TOLERANCE*transformation.unprojectVector(Vector2(transformation.distanceMapping(DistanceMapping::Delta(1)), 0)).length());
for (int y = 0; y < sdf.height; ++y) { for (int y = 0; y < sdf.height; ++y) {
const float *left = sdf(0, y); const float *left = sdf(0, y);
const float *right = sdf(1, y); const float *right = sdf(1, y);
@ -207,7 +206,7 @@ void MSDFErrorCorrection::protectEdges(const BitmapConstRef<float, N> &sdf) {
} }
} }
// Vertical texel pairs // Vertical texel pairs
radius = float(PROTECTION_RADIUS_TOLERANCE*projection.unprojectVector(Vector2(0, invRange)).length()); radius = float(PROTECTION_RADIUS_TOLERANCE*transformation.unprojectVector(Vector2(0, transformation.distanceMapping(DistanceMapping::Delta(1)))).length());
for (int y = 0; y < sdf.height-1; ++y) { for (int y = 0; y < sdf.height-1; ++y) {
const float *bottom = sdf(0, y); const float *bottom = sdf(0, y);
const float *top = sdf(0, y+1); const float *top = sdf(0, y+1);
@ -223,7 +222,7 @@ void MSDFErrorCorrection::protectEdges(const BitmapConstRef<float, N> &sdf) {
} }
} }
// Diagonal texel pairs // Diagonal texel pairs
radius = float(PROTECTION_RADIUS_TOLERANCE*projection.unprojectVector(Vector2(invRange)).length()); radius = float(PROTECTION_RADIUS_TOLERANCE*transformation.unprojectVector(Vector2(transformation.distanceMapping(DistanceMapping::Delta(1)))).length());
for (int y = 0; y < sdf.height-1; ++y) { for (int y = 0; y < sdf.height-1; ++y) {
const float *lb = sdf(0, y); const float *lb = sdf(0, y);
const float *rb = sdf(1, y); const float *rb = sdf(1, y);
@ -391,9 +390,9 @@ static bool hasDiagonalArtifact(const ArtifactClassifier &artifactClassifier, fl
template <int N> template <int N>
void MSDFErrorCorrection::findErrors(const BitmapConstRef<float, N> &sdf) { void MSDFErrorCorrection::findErrors(const BitmapConstRef<float, N> &sdf) {
// Compute the expected deltas between values of horizontally, vertically, and diagonally adjacent texels. // Compute the expected deltas between values of horizontally, vertically, and diagonally adjacent texels.
double hSpan = minDeviationRatio*projection.unprojectVector(Vector2(invRange, 0)).length(); double hSpan = minDeviationRatio*transformation.unprojectVector(Vector2(transformation.distanceMapping(DistanceMapping::Delta(1)), 0)).length();
double vSpan = minDeviationRatio*projection.unprojectVector(Vector2(0, invRange)).length(); double vSpan = minDeviationRatio*transformation.unprojectVector(Vector2(0, transformation.distanceMapping(DistanceMapping::Delta(1)))).length();
double dSpan = minDeviationRatio*projection.unprojectVector(Vector2(invRange)).length(); double dSpan = minDeviationRatio*transformation.unprojectVector(Vector2(transformation.distanceMapping(DistanceMapping::Delta(1)))).length();
// Inspect all texels. // Inspect all texels.
for (int y = 0; y < sdf.height; ++y) { for (int y = 0; y < sdf.height; ++y) {
for (int x = 0; x < sdf.width; ++x) { for (int x = 0; x < sdf.width; ++x) {
@ -419,14 +418,14 @@ void MSDFErrorCorrection::findErrors(const BitmapConstRef<float, N> &sdf) {
template <template <typename> class ContourCombiner, int N> template <template <typename> class ContourCombiner, int N>
void MSDFErrorCorrection::findErrors(const BitmapConstRef<float, N> &sdf, const Shape &shape) { void MSDFErrorCorrection::findErrors(const BitmapConstRef<float, N> &sdf, const Shape &shape) {
// Compute the expected deltas between values of horizontally, vertically, and diagonally adjacent texels. // Compute the expected deltas between values of horizontally, vertically, and diagonally adjacent texels.
double hSpan = minDeviationRatio*projection.unprojectVector(Vector2(invRange, 0)).length(); double hSpan = minDeviationRatio*transformation.unprojectVector(Vector2(transformation.distanceMapping(DistanceMapping::Delta(1)), 0)).length();
double vSpan = minDeviationRatio*projection.unprojectVector(Vector2(0, invRange)).length(); double vSpan = minDeviationRatio*transformation.unprojectVector(Vector2(0, transformation.distanceMapping(DistanceMapping::Delta(1)))).length();
double dSpan = minDeviationRatio*projection.unprojectVector(Vector2(invRange)).length(); double dSpan = minDeviationRatio*transformation.unprojectVector(Vector2(transformation.distanceMapping(DistanceMapping::Delta(1)))).length();
#ifdef MSDFGEN_USE_OPENMP #ifdef MSDFGEN_USE_OPENMP
#pragma omp parallel #pragma omp parallel
#endif #endif
{ {
ShapeDistanceChecker<ContourCombiner, N> shapeDistanceChecker(sdf, shape, projection, invRange, minImproveRatio); ShapeDistanceChecker<ContourCombiner, N> shapeDistanceChecker(sdf, shape, transformation, transformation.distanceMapping, minImproveRatio);
bool rightToLeft = false; bool rightToLeft = false;
// Inspect all texels. // Inspect all texels.
#ifdef MSDFGEN_USE_OPENMP #ifdef MSDFGEN_USE_OPENMP
@ -439,7 +438,7 @@ void MSDFErrorCorrection::findErrors(const BitmapConstRef<float, N> &sdf, const
if ((*stencil(x, row)&ERROR)) if ((*stencil(x, row)&ERROR))
continue; continue;
const float *c = sdf(x, row); const float *c = sdf(x, row);
shapeDistanceChecker.shapeCoord = projection.unproject(Point2(x+.5, y+.5)); shapeDistanceChecker.shapeCoord = transformation.unproject(Point2(x+.5, y+.5));
shapeDistanceChecker.sdfCoord = Point2(x+.5, row+.5); shapeDistanceChecker.sdfCoord = Point2(x+.5, row+.5);
shapeDistanceChecker.msd = c; shapeDistanceChecker.msd = c;
shapeDistanceChecker.protectedFlag = (*stencil(x, row)&PROTECTED) != 0; shapeDistanceChecker.protectedFlag = (*stencil(x, row)&PROTECTED) != 0;

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
#include "Projection.h" #include "SDFTransformation.h"
#include "Shape.h" #include "Shape.h"
#include "BitmapRef.hpp" #include "BitmapRef.hpp"
@ -20,7 +20,7 @@ public:
}; };
MSDFErrorCorrection(); MSDFErrorCorrection();
explicit MSDFErrorCorrection(const BitmapRef<byte, 1> &stencil, const Projection &projection, double range); explicit MSDFErrorCorrection(const BitmapRef<byte, 1> &stencil, const SDFTransformation &transformation);
/// Sets the minimum ratio between the actual and maximum expected distance delta to be considered an error. /// Sets the minimum ratio between the actual and maximum expected distance delta to be considered an error.
void setMinDeviationRatio(double minDeviationRatio); void setMinDeviationRatio(double minDeviationRatio);
/// Sets the minimum ratio between the pre-correction distance error and the post-correction distance error. /// Sets the minimum ratio between the pre-correction distance error and the post-correction distance error.
@ -46,8 +46,7 @@ public:
private: private:
BitmapRef<byte, 1> stencil; BitmapRef<byte, 1> stencil;
Projection projection; SDFTransformation transformation;
double invRange;
double minDeviationRatio; double minDeviationRatio;
double minImproveRatio; double minImproveRatio;

46
thirdparty/msdfgen/core/Range.hpp vendored Normal file
View File

@ -0,0 +1,46 @@
#pragma once
#include "base.h"
namespace msdfgen {
/**
* Represents the range between two real values.
* For example, the range of representable signed distances.
*/
struct Range {
double lower, upper;
inline Range(double symmetricalWidth = 0) : lower(-.5*symmetricalWidth), upper(.5*symmetricalWidth) { }
inline Range(double lowerBound, double upperBound) : lower(lowerBound), upper(upperBound) { }
inline Range &operator*=(double factor) {
lower *= factor;
upper *= factor;
return *this;
}
inline Range &operator/=(double divisor) {
lower /= divisor;
upper /= divisor;
return *this;
}
inline Range operator*(double factor) const {
return Range(lower*factor, upper*factor);
}
inline Range operator/(double divisor) const {
return Range(lower/divisor, upper/divisor);
}
};
inline Range operator*(double factor, const Range &range) {
return Range(factor*range.lower, factor*range.upper);
}
}

View File

@ -0,0 +1,24 @@
#pragma once
#include "Projection.h"
#include "DistanceMapping.h"
namespace msdfgen {
/**
* Full signed distance field transformation specifies both spatial transformation (Projection)
* as well as distance value transformation (DistanceMapping).
*/
class SDFTransformation : public Projection {
public:
DistanceMapping distanceMapping;
inline SDFTransformation() { }
inline SDFTransformation(const Projection &projection, const DistanceMapping &distanceMapping) : Projection(projection), distanceMapping(distanceMapping) { }
};
}

View File

@ -4,6 +4,8 @@
#include <cstdlib> #include <cstdlib>
#include "arithmetics.hpp" #include "arithmetics.hpp"
#define DECONVERGE_OVERSHOOT 1.11111111111111111 // moves control points slightly more than necessary to account for floating-point errors
namespace msdfgen { namespace msdfgen {
Shape::Shape() : inverseYAxis(false) { } Shape::Shape() : inverseYAxis(false) { }
@ -39,13 +41,23 @@ bool Shape::validate() const {
return true; return true;
} }
static void deconvergeEdge(EdgeHolder &edgeHolder, int param) { static void deconvergeEdge(EdgeHolder &edgeHolder, int param, Vector2 vector) {
switch (edgeHolder->type()) { switch (edgeHolder->type()) {
case (int) QuadraticSegment::EDGE_TYPE: case (int) QuadraticSegment::EDGE_TYPE:
edgeHolder = static_cast<const QuadraticSegment *>(&*edgeHolder)->convertToCubic(); edgeHolder = static_cast<const QuadraticSegment *>(&*edgeHolder)->convertToCubic();
// fallthrough // fallthrough
case (int) CubicSegment::EDGE_TYPE: case (int) CubicSegment::EDGE_TYPE:
static_cast<CubicSegment *>(&*edgeHolder)->deconverge(param, MSDFGEN_DECONVERGENCE_FACTOR); {
Point2 *p = static_cast<CubicSegment *>(&*edgeHolder)->p;
switch (param) {
case 0:
p[1] += (p[1]-p[0]).length()*vector;
break;
case 1:
p[2] += (p[2]-p[3]).length()*vector;
break;
}
}
} }
} }
@ -59,13 +71,19 @@ void Shape::normalize() {
contour->edges.push_back(EdgeHolder(parts[1])); contour->edges.push_back(EdgeHolder(parts[1]));
contour->edges.push_back(EdgeHolder(parts[2])); contour->edges.push_back(EdgeHolder(parts[2]));
} else { } else {
// Push apart convergent edge segments
EdgeHolder *prevEdge = &contour->edges.back(); EdgeHolder *prevEdge = &contour->edges.back();
for (std::vector<EdgeHolder>::iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) { for (std::vector<EdgeHolder>::iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) {
Vector2 prevDir = (*prevEdge)->direction(1).normalize(); Vector2 prevDir = (*prevEdge)->direction(1).normalize();
Vector2 curDir = (*edge)->direction(0).normalize(); Vector2 curDir = (*edge)->direction(0).normalize();
if (dotProduct(prevDir, curDir) < MSDFGEN_CORNER_DOT_EPSILON-1) { if (dotProduct(prevDir, curDir) < MSDFGEN_CORNER_DOT_EPSILON-1) {
deconvergeEdge(*prevEdge, 1); double factor = DECONVERGE_OVERSHOOT*sqrt(1-(MSDFGEN_CORNER_DOT_EPSILON-1)*(MSDFGEN_CORNER_DOT_EPSILON-1))/(MSDFGEN_CORNER_DOT_EPSILON-1);
deconvergeEdge(*edge, 0); 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)
axis = -axis;
deconvergeEdge(*prevEdge, 1, axis.getOrthogonal(true));
deconvergeEdge(*edge, 0, axis.getOrthogonal(false));
} }
prevEdge = &*edge; prevEdge = &*edge;
} }

View File

@ -9,8 +9,6 @@ namespace msdfgen {
// Threshold of the dot product of adjacent edge directions to be considered convergent. // Threshold of the dot product of adjacent edge directions to be considered convergent.
#define MSDFGEN_CORNER_DOT_EPSILON .000001 #define MSDFGEN_CORNER_DOT_EPSILON .000001
// The proportional amount by which a curve's control point will be adjusted to eliminate convergent corners.
#define MSDFGEN_DECONVERGENCE_FACTOR .000001
/// Vector shape representation. /// Vector shape representation.
class Shape { class Shape {

View File

@ -24,8 +24,8 @@ struct Vector2 {
} }
/// Sets individual elements of the vector. /// Sets individual elements of the vector.
inline void set(double x, double y) { inline void set(double newX, double newY) {
this->x = x, this->y = y; x = newX, y = newY;
} }
/// Returns the vector's squared length. /// Returns the vector's squared length.

View File

@ -16,6 +16,13 @@ static void initDistance(MultiDistance &distance) {
distance.b = -DBL_MAX; distance.b = -DBL_MAX;
} }
static void initDistance(MultiAndTrueDistance &distance) {
distance.r = -DBL_MAX;
distance.g = -DBL_MAX;
distance.b = -DBL_MAX;
distance.a = -DBL_MAX;
}
static double resolveDistance(double distance) { static double resolveDistance(double distance) {
return distance; return distance;
} }
@ -43,7 +50,7 @@ typename SimpleContourCombiner<EdgeSelector>::DistanceType SimpleContourCombiner
} }
template class SimpleContourCombiner<TrueDistanceSelector>; template class SimpleContourCombiner<TrueDistanceSelector>;
template class SimpleContourCombiner<PseudoDistanceSelector>; template class SimpleContourCombiner<PerpendicularDistanceSelector>;
template class SimpleContourCombiner<MultiDistanceSelector>; template class SimpleContourCombiner<MultiDistanceSelector>;
template class SimpleContourCombiner<MultiAndTrueDistanceSelector>; template class SimpleContourCombiner<MultiAndTrueDistanceSelector>;
@ -127,7 +134,7 @@ typename OverlappingContourCombiner<EdgeSelector>::DistanceType OverlappingConto
} }
template class OverlappingContourCombiner<TrueDistanceSelector>; template class OverlappingContourCombiner<TrueDistanceSelector>;
template class OverlappingContourCombiner<PseudoDistanceSelector>; template class OverlappingContourCombiner<PerpendicularDistanceSelector>;
template class OverlappingContourCombiner<MultiDistanceSelector>; template class OverlappingContourCombiner<MultiDistanceSelector>;
template class OverlappingContourCombiner<MultiAndTrueDistanceSelector>; template class OverlappingContourCombiner<MultiAndTrueDistanceSelector>;

View File

@ -11,6 +11,15 @@
namespace msdfgen { namespace msdfgen {
/**
* For each position < n, this function will return -1, 0, or 1,
* depending on whether the position is closer to the beginning, middle, or end, respectively.
* It is guaranteed that the output will be balanced in that the total for positions 0 through n-1 will be zero.
*/
static int symmetricalTrichotomy(int position, int n) {
return int(3+2.875*position/(n-1)-1.4375+.5)-3;
}
static bool isCorner(const Vector2 &aDir, const Vector2 &bDir, double crossThreshold) { static bool isCorner(const Vector2 &aDir, const Vector2 &bDir, double crossThreshold) {
return dotProduct(aDir, bDir) <= 0 || fabs(crossProduct(aDir, bDir)) > crossThreshold; return dotProduct(aDir, bDir) <= 0 || fabs(crossProduct(aDir, bDir)) > crossThreshold;
} }
@ -26,30 +35,45 @@ static double estimateEdgeLength(const EdgeSegment *edge) {
return len; return len;
} }
static void switchColor(EdgeColor &color, unsigned long long &seed, EdgeColor banned = BLACK) { static int seedExtract2(unsigned long long &seed) {
EdgeColor combined = EdgeColor(color&banned); int v = int(seed)&1;
if (combined == RED || combined == GREEN || combined == BLUE) {
color = EdgeColor(combined^WHITE);
return;
}
if (color == BLACK || color == WHITE) {
static const EdgeColor start[3] = { CYAN, MAGENTA, YELLOW };
color = start[seed%3];
seed /= 3;
return;
}
int shifted = color<<(1+(seed&1));
color = EdgeColor((shifted|shifted>>3)&WHITE);
seed >>= 1; seed >>= 1;
return v;
}
static int seedExtract3(unsigned long long &seed) {
int v = int(seed%3);
seed /= 3;
return v;
}
static EdgeColor initColor(unsigned long long &seed) {
static const EdgeColor colors[3] = { CYAN, MAGENTA, YELLOW };
return colors[seedExtract3(seed)];
}
static void switchColor(EdgeColor &color, unsigned long long &seed) {
int shifted = color<<(1+seedExtract2(seed));
color = EdgeColor((shifted|shifted>>3)&WHITE);
}
static void switchColor(EdgeColor &color, unsigned long long &seed, EdgeColor banned) {
EdgeColor combined = EdgeColor(color&banned);
if (combined == RED || combined == GREEN || combined == BLUE)
color = EdgeColor(combined^WHITE);
else
switchColor(color, seed);
} }
void edgeColoringSimple(Shape &shape, double angleThreshold, unsigned long long seed) { void edgeColoringSimple(Shape &shape, double angleThreshold, unsigned long long seed) {
double crossThreshold = sin(angleThreshold); double crossThreshold = sin(angleThreshold);
EdgeColor color = initColor(seed);
std::vector<int> corners; std::vector<int> corners;
for (std::vector<Contour>::iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour) { for (std::vector<Contour>::iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour) {
// Identify corners if (contour->edges.empty())
corners.clear(); continue;
if (!contour->edges.empty()) { { // Identify corners
corners.clear();
Vector2 prevDirection = contour->edges.back()->direction(1); Vector2 prevDirection = contour->edges.back()->direction(1);
int index = 0; int index = 0;
for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge, ++index) { for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge, ++index) {
@ -60,19 +84,24 @@ void edgeColoringSimple(Shape &shape, double angleThreshold, unsigned long long
} }
// Smooth contour // Smooth contour
if (corners.empty()) if (corners.empty()) {
switchColor(color, seed);
for (std::vector<EdgeHolder>::iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) for (std::vector<EdgeHolder>::iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge)
(*edge)->color = WHITE; (*edge)->color = color;
}
// "Teardrop" case // "Teardrop" case
else if (corners.size() == 1) { else if (corners.size() == 1) {
EdgeColor colors[3] = { WHITE, WHITE }; EdgeColor colors[3];
switchColor(colors[0], seed); switchColor(color, seed);
switchColor(colors[2] = colors[0], seed); colors[0] = color;
colors[1] = WHITE;
switchColor(color, seed);
colors[2] = color;
int corner = corners[0]; int corner = corners[0];
if (contour->edges.size() >= 3) { if (contour->edges.size() >= 3) {
int m = (int) contour->edges.size(); int m = (int) contour->edges.size();
for (int i = 0; i < m; ++i) for (int i = 0; i < m; ++i)
contour->edges[(corner+i)%m]->color = (colors+1)[int(3+2.875*i/(m-1)-1.4375+.5)-3]; contour->edges[(corner+i)%m]->color = colors[1+symmetricalTrichotomy(i, m)];
} else if (contour->edges.size() >= 1) { } else if (contour->edges.size() >= 1) {
// Less than three edge segments for three colors => edges must be split // Less than three edge segments for three colors => edges must be split
EdgeSegment *parts[7] = { }; EdgeSegment *parts[7] = { };
@ -98,7 +127,6 @@ void edgeColoringSimple(Shape &shape, double angleThreshold, unsigned long long
int spline = 0; int spline = 0;
int start = corners[0]; int start = corners[0];
int m = (int) contour->edges.size(); int m = (int) contour->edges.size();
EdgeColor color = WHITE;
switchColor(color, seed); switchColor(color, seed);
EdgeColor initialColor = color; EdgeColor initialColor = color;
for (int i = 0; i < m; ++i) { for (int i = 0; i < m; ++i) {
@ -123,12 +151,14 @@ struct EdgeColoringInkTrapCorner {
void edgeColoringInkTrap(Shape &shape, double angleThreshold, unsigned long long seed) { void edgeColoringInkTrap(Shape &shape, double angleThreshold, unsigned long long seed) {
typedef EdgeColoringInkTrapCorner Corner; typedef EdgeColoringInkTrapCorner Corner;
double crossThreshold = sin(angleThreshold); double crossThreshold = sin(angleThreshold);
EdgeColor color = initColor(seed);
std::vector<Corner> corners; std::vector<Corner> corners;
for (std::vector<Contour>::iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour) { for (std::vector<Contour>::iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour) {
// Identify corners if (contour->edges.empty())
continue;
double splineLength = 0; double splineLength = 0;
corners.clear(); { // Identify corners
if (!contour->edges.empty()) { corners.clear();
Vector2 prevDirection = contour->edges.back()->direction(1); Vector2 prevDirection = contour->edges.back()->direction(1);
int index = 0; int index = 0;
for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge, ++index) { for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge, ++index) {
@ -143,19 +173,24 @@ void edgeColoringInkTrap(Shape &shape, double angleThreshold, unsigned long long
} }
// Smooth contour // Smooth contour
if (corners.empty()) if (corners.empty()) {
switchColor(color, seed);
for (std::vector<EdgeHolder>::iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) for (std::vector<EdgeHolder>::iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge)
(*edge)->color = WHITE; (*edge)->color = color;
}
// "Teardrop" case // "Teardrop" case
else if (corners.size() == 1) { else if (corners.size() == 1) {
EdgeColor colors[3] = { WHITE, WHITE }; EdgeColor colors[3];
switchColor(colors[0], seed); switchColor(color, seed);
switchColor(colors[2] = colors[0], seed); colors[0] = color;
colors[1] = WHITE;
switchColor(color, seed);
colors[2] = color;
int corner = corners[0].index; int corner = corners[0].index;
if (contour->edges.size() >= 3) { if (contour->edges.size() >= 3) {
int m = (int) contour->edges.size(); int m = (int) contour->edges.size();
for (int i = 0; i < m; ++i) for (int i = 0; i < m; ++i)
contour->edges[(corner+i)%m]->color = (colors+1)[int(3+2.875*i/(m-1)-1.4375+.5)-3]; contour->edges[(corner+i)%m]->color = colors[1+symmetricalTrichotomy(i, m)];
} else if (contour->edges.size() >= 1) { } else if (contour->edges.size() >= 1) {
// Less than three edge segments for three colors => edges must be split // Less than three edge segments for three colors => edges must be split
EdgeSegment *parts[7] = { }; EdgeSegment *parts[7] = { };
@ -191,7 +226,6 @@ void edgeColoringInkTrap(Shape &shape, double angleThreshold, unsigned long long
} }
} }
} }
EdgeColor color = WHITE;
EdgeColor initialColor = BLACK; EdgeColor initialColor = BLACK;
for (int i = 0; i < cornerCount; ++i) { for (int i = 0; i < cornerCount; ++i) {
if (!corners[i].minor) { if (!corners[i].minor) {
@ -271,23 +305,19 @@ static void colorSecondDegreeGraph(int *coloring, const int *const *edgeMatrix,
color = 1; color = 1;
break; break;
case 3: case 3:
color = (int) seed&1; color = seedExtract2(seed); // 0 or 1
seed >>= 1;
break; break;
case 4: case 4:
color = 2; color = 2;
break; break;
case 5: case 5:
color = ((int) seed+1&1)<<1; color = (int) !seedExtract2(seed)<<1; // 2 or 0
seed >>= 1;
break; break;
case 6: case 6:
color = ((int) seed&1)+1; color = seedExtract2(seed)+1; // 1 or 2
seed >>= 1;
break; break;
case 7: case 7:
color = int((seed+i)%3); color = (seedExtract3(seed)+i)%3; // 0 or 1 or 2
seed /= 3;
break; break;
} }
coloring[i] = color; coloring[i] = color;
@ -394,7 +424,7 @@ void edgeColoringByDistance(Shape &shape, double angleThreshold, unsigned long l
for (int i = 0; i < m; ++i) { for (int i = 0; i < m; ++i) {
if (i == m/2) if (i == m/2)
splineStarts.push_back((int) edgeSegments.size()); splineStarts.push_back((int) edgeSegments.size());
if (int(3+2.875*i/(m-1)-1.4375+.5)-3) if (symmetricalTrichotomy(i, m))
edgeSegments.push_back(&*contour->edges[(corner+i)%m]); edgeSegments.push_back(&*contour->edges[(corner+i)%m]);
else else
contour->edges[(corner+i)%m]->color = WHITE; contour->edges[(corner+i)%m]->color = WHITE;

View File

@ -6,15 +6,34 @@
namespace msdfgen { namespace msdfgen {
void EdgeSegment::distanceToPseudoDistance(SignedDistance &distance, Point2 origin, double param) const { EdgeSegment *EdgeSegment::create(Point2 p0, Point2 p1, EdgeColor edgeColor) {
return new LinearSegment(p0, p1, edgeColor);
}
EdgeSegment *EdgeSegment::create(Point2 p0, Point2 p1, Point2 p2, EdgeColor edgeColor) {
if (!crossProduct(p1-p0, p2-p1))
return new LinearSegment(p0, p2, edgeColor);
return new QuadraticSegment(p0, p1, p2, edgeColor);
}
EdgeSegment *EdgeSegment::create(Point2 p0, Point2 p1, Point2 p2, Point2 p3, EdgeColor edgeColor) {
Vector2 p12 = p2-p1;
if (!crossProduct(p1-p0, p12) && !crossProduct(p12, p3-p2))
return new LinearSegment(p0, p3, edgeColor);
if ((p12 = 1.5*p1-.5*p0) == 1.5*p2-.5*p3)
return new QuadraticSegment(p0, p12, p3, edgeColor);
return new CubicSegment(p0, p1, p2, p3, edgeColor);
}
void EdgeSegment::distanceToPerpendicularDistance(SignedDistance &distance, Point2 origin, double param) const {
if (param < 0) { if (param < 0) {
Vector2 dir = direction(0).normalize(); Vector2 dir = direction(0).normalize();
Vector2 aq = origin-point(0); Vector2 aq = origin-point(0);
double ts = dotProduct(aq, dir); double ts = dotProduct(aq, dir);
if (ts < 0) { if (ts < 0) {
double pseudoDistance = crossProduct(aq, dir); double perpendicularDistance = crossProduct(aq, dir);
if (fabs(pseudoDistance) <= fabs(distance.distance)) { if (fabs(perpendicularDistance) <= fabs(distance.distance)) {
distance.distance = pseudoDistance; distance.distance = perpendicularDistance;
distance.dot = 0; distance.dot = 0;
} }
} }
@ -23,9 +42,9 @@ void EdgeSegment::distanceToPseudoDistance(SignedDistance &distance, Point2 orig
Vector2 bq = origin-point(1); Vector2 bq = origin-point(1);
double ts = dotProduct(bq, dir); double ts = dotProduct(bq, dir);
if (ts > 0) { if (ts > 0) {
double pseudoDistance = crossProduct(bq, dir); double perpendicularDistance = crossProduct(bq, dir);
if (fabs(pseudoDistance) <= fabs(distance.distance)) { if (fabs(perpendicularDistance) <= fabs(distance.distance)) {
distance.distance = pseudoDistance; distance.distance = perpendicularDistance;
distance.dot = 0; distance.dot = 0;
} }
} }
@ -38,18 +57,12 @@ LinearSegment::LinearSegment(Point2 p0, Point2 p1, EdgeColor edgeColor) : EdgeSe
} }
QuadraticSegment::QuadraticSegment(Point2 p0, Point2 p1, Point2 p2, EdgeColor edgeColor) : EdgeSegment(edgeColor) { QuadraticSegment::QuadraticSegment(Point2 p0, Point2 p1, Point2 p2, EdgeColor edgeColor) : EdgeSegment(edgeColor) {
if (p1 == p0 || p1 == p2)
p1 = 0.5*(p0+p2);
p[0] = p0; p[0] = p0;
p[1] = p1; p[1] = p1;
p[2] = p2; p[2] = p2;
} }
CubicSegment::CubicSegment(Point2 p0, Point2 p1, Point2 p2, Point2 p3, EdgeColor edgeColor) : EdgeSegment(edgeColor) { CubicSegment::CubicSegment(Point2 p0, Point2 p1, Point2 p2, Point2 p3, EdgeColor edgeColor) : EdgeSegment(edgeColor) {
if ((p1 == p0 || p1 == p3) && (p2 == p0 || p2 == p3)) {
p1 = mix(p0, p3, 1/3.);
p2 = mix(p0, p3, 2/3.);
}
p[0] = p0; p[0] = p0;
p[1] = p1; p[1] = p1;
p[2] = p2; p[2] = p2;
@ -486,43 +499,29 @@ void CubicSegment::moveEndPoint(Point2 to) {
p[3] = to; p[3] = to;
} }
void LinearSegment::splitInThirds(EdgeSegment *&part1, EdgeSegment *&part2, EdgeSegment *&part3) const { void LinearSegment::splitInThirds(EdgeSegment *&part0, EdgeSegment *&part1, EdgeSegment *&part2) const {
part1 = new LinearSegment(p[0], point(1/3.), color); part0 = new LinearSegment(p[0], point(1/3.), color);
part2 = new LinearSegment(point(1/3.), point(2/3.), color); part1 = new LinearSegment(point(1/3.), point(2/3.), color);
part3 = new LinearSegment(point(2/3.), p[1], color); part2 = new LinearSegment(point(2/3.), p[1], color);
} }
void QuadraticSegment::splitInThirds(EdgeSegment *&part1, EdgeSegment *&part2, EdgeSegment *&part3) const { void QuadraticSegment::splitInThirds(EdgeSegment *&part0, EdgeSegment *&part1, EdgeSegment *&part2) const {
part1 = new QuadraticSegment(p[0], mix(p[0], p[1], 1/3.), point(1/3.), color); part0 = new QuadraticSegment(p[0], mix(p[0], p[1], 1/3.), point(1/3.), color);
part2 = new QuadraticSegment(point(1/3.), mix(mix(p[0], p[1], 5/9.), mix(p[1], p[2], 4/9.), .5), point(2/3.), color); part1 = new QuadraticSegment(point(1/3.), mix(mix(p[0], p[1], 5/9.), mix(p[1], p[2], 4/9.), .5), point(2/3.), color);
part3 = new QuadraticSegment(point(2/3.), mix(p[1], p[2], 2/3.), p[2], color); part2 = new QuadraticSegment(point(2/3.), mix(p[1], p[2], 2/3.), p[2], color);
} }
void CubicSegment::splitInThirds(EdgeSegment *&part1, EdgeSegment *&part2, EdgeSegment *&part3) const { void CubicSegment::splitInThirds(EdgeSegment *&part0, EdgeSegment *&part1, EdgeSegment *&part2) const {
part1 = new CubicSegment(p[0], p[0] == p[1] ? p[0] : mix(p[0], p[1], 1/3.), mix(mix(p[0], p[1], 1/3.), mix(p[1], p[2], 1/3.), 1/3.), point(1/3.), color); part0 = new CubicSegment(p[0], p[0] == p[1] ? p[0] : mix(p[0], p[1], 1/3.), mix(mix(p[0], p[1], 1/3.), mix(p[1], p[2], 1/3.), 1/3.), point(1/3.), color);
part2 = new CubicSegment(point(1/3.), part1 = new CubicSegment(point(1/3.),
mix(mix(mix(p[0], p[1], 1/3.), mix(p[1], p[2], 1/3.), 1/3.), mix(mix(p[1], p[2], 1/3.), mix(p[2], p[3], 1/3.), 1/3.), 2/3.), mix(mix(mix(p[0], p[1], 1/3.), mix(p[1], p[2], 1/3.), 1/3.), mix(mix(p[1], p[2], 1/3.), mix(p[2], p[3], 1/3.), 1/3.), 2/3.),
mix(mix(mix(p[0], p[1], 2/3.), mix(p[1], p[2], 2/3.), 2/3.), mix(mix(p[1], p[2], 2/3.), mix(p[2], p[3], 2/3.), 2/3.), 1/3.), mix(mix(mix(p[0], p[1], 2/3.), mix(p[1], p[2], 2/3.), 2/3.), mix(mix(p[1], p[2], 2/3.), mix(p[2], p[3], 2/3.), 2/3.), 1/3.),
point(2/3.), color); point(2/3.), color);
part3 = new CubicSegment(point(2/3.), mix(mix(p[1], p[2], 2/3.), mix(p[2], p[3], 2/3.), 2/3.), p[2] == p[3] ? p[3] : mix(p[2], p[3], 2/3.), p[3], color); part2 = new CubicSegment(point(2/3.), mix(mix(p[1], p[2], 2/3.), mix(p[2], p[3], 2/3.), 2/3.), p[2] == p[3] ? p[3] : mix(p[2], p[3], 2/3.), p[3], color);
} }
EdgeSegment *QuadraticSegment::convertToCubic() const { EdgeSegment *QuadraticSegment::convertToCubic() const {
return new CubicSegment(p[0], mix(p[0], p[1], 2/3.), mix(p[1], p[2], 1/3.), p[2], color); return new CubicSegment(p[0], mix(p[0], p[1], 2/3.), mix(p[1], p[2], 1/3.), p[2], color);
} }
void CubicSegment::deconverge(int param, double amount) {
Vector2 dir = direction(param);
Vector2 normal = dir.getOrthonormal();
double h = dotProduct(directionChange(param)-dir, normal);
switch (param) {
case 0:
p[1] += amount*(dir+sign(h)*sqrt(fabs(h))*normal);
break;
case 1:
p[2] -= amount*(dir-sign(h)*sqrt(fabs(h))*normal);
break;
}
}
} }

View File

@ -17,6 +17,10 @@ class EdgeSegment {
public: public:
EdgeColor color; EdgeColor color;
static EdgeSegment *create(Point2 p0, Point2 p1, EdgeColor edgeColor = WHITE);
static EdgeSegment *create(Point2 p0, Point2 p1, Point2 p2, EdgeColor edgeColor = WHITE);
static EdgeSegment *create(Point2 p0, Point2 p1, Point2 p2, Point2 p3, EdgeColor edgeColor = WHITE);
EdgeSegment(EdgeColor edgeColor = WHITE) : color(edgeColor) { } EdgeSegment(EdgeColor edgeColor = WHITE) : color(edgeColor) { }
virtual ~EdgeSegment() { } virtual ~EdgeSegment() { }
/// Creates a copy of the edge segment. /// Creates a copy of the edge segment.
@ -33,8 +37,8 @@ public:
virtual Vector2 directionChange(double param) const = 0; virtual Vector2 directionChange(double param) const = 0;
/// Returns the minimum signed distance between origin and the edge. /// Returns the minimum signed distance between origin and the edge.
virtual SignedDistance signedDistance(Point2 origin, double &param) const = 0; virtual SignedDistance signedDistance(Point2 origin, double &param) const = 0;
/// Converts a previously retrieved signed distance from origin to pseudo-distance. /// Converts a previously retrieved signed distance from origin to perpendicular distance.
virtual void distanceToPseudoDistance(SignedDistance &distance, Point2 origin, double param) const; virtual void distanceToPerpendicularDistance(SignedDistance &distance, Point2 origin, double param) const;
/// Outputs a list of (at most three) intersections (their X coordinates) with an infinite horizontal scanline at y and returns how many there are. /// Outputs a list of (at most three) intersections (their X coordinates) with an infinite horizontal scanline at y and returns how many there are.
virtual int scanlineIntersections(double x[3], int dy[3], double y) const = 0; virtual int scanlineIntersections(double x[3], int dy[3], double y) const = 0;
/// Adjusts the bounding box to fit the edge segment. /// Adjusts the bounding box to fit the edge segment.
@ -47,7 +51,7 @@ public:
/// Moves the end point of the edge segment. /// Moves the end point of the edge segment.
virtual void moveEndPoint(Point2 to) = 0; virtual void moveEndPoint(Point2 to) = 0;
/// Splits the edge segments into thirds which together represent the original edge. /// Splits the edge segments into thirds which together represent the original edge.
virtual void splitInThirds(EdgeSegment *&part1, EdgeSegment *&part2, EdgeSegment *&part3) const = 0; virtual void splitInThirds(EdgeSegment *&part0, EdgeSegment *&part1, EdgeSegment *&part2) const = 0;
}; };
@ -76,7 +80,7 @@ public:
void reverse(); void reverse();
void moveStartPoint(Point2 to); void moveStartPoint(Point2 to);
void moveEndPoint(Point2 to); void moveEndPoint(Point2 to);
void splitInThirds(EdgeSegment *&part1, EdgeSegment *&part2, EdgeSegment *&part3) const; void splitInThirds(EdgeSegment *&part0, EdgeSegment *&part1, EdgeSegment *&part2) const;
}; };
@ -105,7 +109,7 @@ public:
void reverse(); void reverse();
void moveStartPoint(Point2 to); void moveStartPoint(Point2 to);
void moveEndPoint(Point2 to); void moveEndPoint(Point2 to);
void splitInThirds(EdgeSegment *&part1, EdgeSegment *&part2, EdgeSegment *&part3) const; void splitInThirds(EdgeSegment *&part0, EdgeSegment *&part1, EdgeSegment *&part2) const;
EdgeSegment *convertToCubic() const; EdgeSegment *convertToCubic() const;
@ -135,9 +139,7 @@ public:
void reverse(); void reverse();
void moveStartPoint(Point2 to); void moveStartPoint(Point2 to);
void moveEndPoint(Point2 to); void moveEndPoint(Point2 to);
void splitInThirds(EdgeSegment *&part1, EdgeSegment *&part2, EdgeSegment *&part3) const; void splitInThirds(EdgeSegment *&part0, EdgeSegment *&part1, EdgeSegment *&part2) const;
void deconverge(int param, double amount);
}; };

View File

@ -36,48 +36,48 @@ TrueDistanceSelector::DistanceType TrueDistanceSelector::distance() const {
return minDistance.distance; return minDistance.distance;
} }
PseudoDistanceSelectorBase::EdgeCache::EdgeCache() : absDistance(0), aDomainDistance(0), bDomainDistance(0), aPseudoDistance(0), bPseudoDistance(0) { } PerpendicularDistanceSelectorBase::EdgeCache::EdgeCache() : absDistance(0), aDomainDistance(0), bDomainDistance(0), aPerpendicularDistance(0), bPerpendicularDistance(0) { }
bool PseudoDistanceSelectorBase::getPseudoDistance(double &distance, const Vector2 &ep, const Vector2 &edgeDir) { bool PerpendicularDistanceSelectorBase::getPerpendicularDistance(double &distance, const Vector2 &ep, const Vector2 &edgeDir) {
double ts = dotProduct(ep, edgeDir); double ts = dotProduct(ep, edgeDir);
if (ts > 0) { if (ts > 0) {
double pseudoDistance = crossProduct(ep, edgeDir); double perpendicularDistance = crossProduct(ep, edgeDir);
if (fabs(pseudoDistance) < fabs(distance)) { if (fabs(perpendicularDistance) < fabs(distance)) {
distance = pseudoDistance; distance = perpendicularDistance;
return true; return true;
} }
} }
return false; return false;
} }
PseudoDistanceSelectorBase::PseudoDistanceSelectorBase() : minNegativePseudoDistance(-fabs(minTrueDistance.distance)), minPositivePseudoDistance(fabs(minTrueDistance.distance)), nearEdge(NULL), nearEdgeParam(0) { } PerpendicularDistanceSelectorBase::PerpendicularDistanceSelectorBase() : minNegativePerpendicularDistance(-fabs(minTrueDistance.distance)), minPositivePerpendicularDistance(fabs(minTrueDistance.distance)), nearEdge(NULL), nearEdgeParam(0) { }
void PseudoDistanceSelectorBase::reset(double delta) { void PerpendicularDistanceSelectorBase::reset(double delta) {
minTrueDistance.distance += nonZeroSign(minTrueDistance.distance)*delta; minTrueDistance.distance += nonZeroSign(minTrueDistance.distance)*delta;
minNegativePseudoDistance = -fabs(minTrueDistance.distance); minNegativePerpendicularDistance = -fabs(minTrueDistance.distance);
minPositivePseudoDistance = fabs(minTrueDistance.distance); minPositivePerpendicularDistance = fabs(minTrueDistance.distance);
nearEdge = NULL; nearEdge = NULL;
nearEdgeParam = 0; nearEdgeParam = 0;
} }
bool PseudoDistanceSelectorBase::isEdgeRelevant(const EdgeCache &cache, const EdgeSegment *edge, const Point2 &p) const { bool PerpendicularDistanceSelectorBase::isEdgeRelevant(const EdgeCache &cache, const EdgeSegment *edge, const Point2 &p) const {
double delta = DISTANCE_DELTA_FACTOR*(p-cache.point).length(); double delta = DISTANCE_DELTA_FACTOR*(p-cache.point).length();
return ( return (
cache.absDistance-delta <= fabs(minTrueDistance.distance) || cache.absDistance-delta <= fabs(minTrueDistance.distance) ||
fabs(cache.aDomainDistance) < delta || fabs(cache.aDomainDistance) < delta ||
fabs(cache.bDomainDistance) < delta || fabs(cache.bDomainDistance) < delta ||
(cache.aDomainDistance > 0 && (cache.aPseudoDistance < 0 ? (cache.aDomainDistance > 0 && (cache.aPerpendicularDistance < 0 ?
cache.aPseudoDistance+delta >= minNegativePseudoDistance : cache.aPerpendicularDistance+delta >= minNegativePerpendicularDistance :
cache.aPseudoDistance-delta <= minPositivePseudoDistance cache.aPerpendicularDistance-delta <= minPositivePerpendicularDistance
)) || )) ||
(cache.bDomainDistance > 0 && (cache.bPseudoDistance < 0 ? (cache.bDomainDistance > 0 && (cache.bPerpendicularDistance < 0 ?
cache.bPseudoDistance+delta >= minNegativePseudoDistance : cache.bPerpendicularDistance+delta >= minNegativePerpendicularDistance :
cache.bPseudoDistance-delta <= minPositivePseudoDistance cache.bPerpendicularDistance-delta <= minPositivePerpendicularDistance
)) ))
); );
} }
void PseudoDistanceSelectorBase::addEdgeTrueDistance(const EdgeSegment *edge, const SignedDistance &distance, double param) { void PerpendicularDistanceSelectorBase::addEdgeTrueDistance(const EdgeSegment *edge, const SignedDistance &distance, double param) {
if (distance < minTrueDistance) { if (distance < minTrueDistance) {
minTrueDistance = distance; minTrueDistance = distance;
nearEdge = edge; nearEdge = edge;
@ -85,47 +85,47 @@ void PseudoDistanceSelectorBase::addEdgeTrueDistance(const EdgeSegment *edge, co
} }
} }
void PseudoDistanceSelectorBase::addEdgePseudoDistance(double distance) { void PerpendicularDistanceSelectorBase::addEdgePerpendicularDistance(double distance) {
if (distance <= 0 && distance > minNegativePseudoDistance) if (distance <= 0 && distance > minNegativePerpendicularDistance)
minNegativePseudoDistance = distance; minNegativePerpendicularDistance = distance;
if (distance >= 0 && distance < minPositivePseudoDistance) if (distance >= 0 && distance < minPositivePerpendicularDistance)
minPositivePseudoDistance = distance; minPositivePerpendicularDistance = distance;
} }
void PseudoDistanceSelectorBase::merge(const PseudoDistanceSelectorBase &other) { void PerpendicularDistanceSelectorBase::merge(const PerpendicularDistanceSelectorBase &other) {
if (other.minTrueDistance < minTrueDistance) { if (other.minTrueDistance < minTrueDistance) {
minTrueDistance = other.minTrueDistance; minTrueDistance = other.minTrueDistance;
nearEdge = other.nearEdge; nearEdge = other.nearEdge;
nearEdgeParam = other.nearEdgeParam; nearEdgeParam = other.nearEdgeParam;
} }
if (other.minNegativePseudoDistance > minNegativePseudoDistance) if (other.minNegativePerpendicularDistance > minNegativePerpendicularDistance)
minNegativePseudoDistance = other.minNegativePseudoDistance; minNegativePerpendicularDistance = other.minNegativePerpendicularDistance;
if (other.minPositivePseudoDistance < minPositivePseudoDistance) if (other.minPositivePerpendicularDistance < minPositivePerpendicularDistance)
minPositivePseudoDistance = other.minPositivePseudoDistance; minPositivePerpendicularDistance = other.minPositivePerpendicularDistance;
} }
double PseudoDistanceSelectorBase::computeDistance(const Point2 &p) const { double PerpendicularDistanceSelectorBase::computeDistance(const Point2 &p) const {
double minDistance = minTrueDistance.distance < 0 ? minNegativePseudoDistance : minPositivePseudoDistance; double minDistance = minTrueDistance.distance < 0 ? minNegativePerpendicularDistance : minPositivePerpendicularDistance;
if (nearEdge) { if (nearEdge) {
SignedDistance distance = minTrueDistance; SignedDistance distance = minTrueDistance;
nearEdge->distanceToPseudoDistance(distance, p, nearEdgeParam); nearEdge->distanceToPerpendicularDistance(distance, p, nearEdgeParam);
if (fabs(distance.distance) < fabs(minDistance)) if (fabs(distance.distance) < fabs(minDistance))
minDistance = distance.distance; minDistance = distance.distance;
} }
return minDistance; return minDistance;
} }
SignedDistance PseudoDistanceSelectorBase::trueDistance() const { SignedDistance PerpendicularDistanceSelectorBase::trueDistance() const {
return minTrueDistance; return minTrueDistance;
} }
void PseudoDistanceSelector::reset(const Point2 &p) { void PerpendicularDistanceSelector::reset(const Point2 &p) {
double delta = DISTANCE_DELTA_FACTOR*(p-this->p).length(); double delta = DISTANCE_DELTA_FACTOR*(p-this->p).length();
PseudoDistanceSelectorBase::reset(delta); PerpendicularDistanceSelectorBase::reset(delta);
this->p = p; this->p = p;
} }
void PseudoDistanceSelector::addEdge(EdgeCache &cache, const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge) { void PerpendicularDistanceSelector::addEdge(EdgeCache &cache, const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge) {
if (isEdgeRelevant(cache, edge, p)) { if (isEdgeRelevant(cache, edge, p)) {
double param; double param;
SignedDistance distance = edge->signedDistance(p, param); SignedDistance distance = edge->signedDistance(p, param);
@ -143,22 +143,22 @@ void PseudoDistanceSelector::addEdge(EdgeCache &cache, const EdgeSegment *prevEd
double bdd = -dotProduct(bp, (bDir+nextDir).normalize(true)); double bdd = -dotProduct(bp, (bDir+nextDir).normalize(true));
if (add > 0) { if (add > 0) {
double pd = distance.distance; double pd = distance.distance;
if (getPseudoDistance(pd, ap, -aDir)) if (getPerpendicularDistance(pd, ap, -aDir))
addEdgePseudoDistance(pd = -pd); addEdgePerpendicularDistance(pd = -pd);
cache.aPseudoDistance = pd; cache.aPerpendicularDistance = pd;
} }
if (bdd > 0) { if (bdd > 0) {
double pd = distance.distance; double pd = distance.distance;
if (getPseudoDistance(pd, bp, bDir)) if (getPerpendicularDistance(pd, bp, bDir))
addEdgePseudoDistance(pd); addEdgePerpendicularDistance(pd);
cache.bPseudoDistance = pd; cache.bPerpendicularDistance = pd;
} }
cache.aDomainDistance = add; cache.aDomainDistance = add;
cache.bDomainDistance = bdd; cache.bDomainDistance = bdd;
} }
} }
PseudoDistanceSelector::DistanceType PseudoDistanceSelector::distance() const { PerpendicularDistanceSelector::DistanceType PerpendicularDistanceSelector::distance() const {
return computeDistance(p); return computeDistance(p);
} }
@ -197,28 +197,28 @@ void MultiDistanceSelector::addEdge(EdgeCache &cache, const EdgeSegment *prevEdg
double bdd = -dotProduct(bp, (bDir+nextDir).normalize(true)); double bdd = -dotProduct(bp, (bDir+nextDir).normalize(true));
if (add > 0) { if (add > 0) {
double pd = distance.distance; double pd = distance.distance;
if (PseudoDistanceSelectorBase::getPseudoDistance(pd, ap, -aDir)) { if (PerpendicularDistanceSelectorBase::getPerpendicularDistance(pd, ap, -aDir)) {
pd = -pd; pd = -pd;
if (edge->color&RED) if (edge->color&RED)
r.addEdgePseudoDistance(pd); r.addEdgePerpendicularDistance(pd);
if (edge->color&GREEN) if (edge->color&GREEN)
g.addEdgePseudoDistance(pd); g.addEdgePerpendicularDistance(pd);
if (edge->color&BLUE) if (edge->color&BLUE)
b.addEdgePseudoDistance(pd); b.addEdgePerpendicularDistance(pd);
} }
cache.aPseudoDistance = pd; cache.aPerpendicularDistance = pd;
} }
if (bdd > 0) { if (bdd > 0) {
double pd = distance.distance; double pd = distance.distance;
if (PseudoDistanceSelectorBase::getPseudoDistance(pd, bp, bDir)) { if (PerpendicularDistanceSelectorBase::getPerpendicularDistance(pd, bp, bDir)) {
if (edge->color&RED) if (edge->color&RED)
r.addEdgePseudoDistance(pd); r.addEdgePerpendicularDistance(pd);
if (edge->color&GREEN) if (edge->color&GREEN)
g.addEdgePseudoDistance(pd); g.addEdgePerpendicularDistance(pd);
if (edge->color&BLUE) if (edge->color&BLUE)
b.addEdgePseudoDistance(pd); b.addEdgePerpendicularDistance(pd);
} }
cache.bPseudoDistance = pd; cache.bPerpendicularDistance = pd;
} }
cache.aDomainDistance = add; cache.aDomainDistance = add;
cache.bDomainDistance = bdd; cache.bDomainDistance = bdd;

View File

@ -38,40 +38,40 @@ private:
}; };
class PseudoDistanceSelectorBase { class PerpendicularDistanceSelectorBase {
public: public:
struct EdgeCache { struct EdgeCache {
Point2 point; Point2 point;
double absDistance; double absDistance;
double aDomainDistance, bDomainDistance; double aDomainDistance, bDomainDistance;
double aPseudoDistance, bPseudoDistance; double aPerpendicularDistance, bPerpendicularDistance;
EdgeCache(); EdgeCache();
}; };
static bool getPseudoDistance(double &distance, const Vector2 &ep, const Vector2 &edgeDir); static bool getPerpendicularDistance(double &distance, const Vector2 &ep, const Vector2 &edgeDir);
PseudoDistanceSelectorBase(); PerpendicularDistanceSelectorBase();
void reset(double delta); void reset(double delta);
bool isEdgeRelevant(const EdgeCache &cache, const EdgeSegment *edge, const Point2 &p) const; bool isEdgeRelevant(const EdgeCache &cache, const EdgeSegment *edge, const Point2 &p) const;
void addEdgeTrueDistance(const EdgeSegment *edge, const SignedDistance &distance, double param); void addEdgeTrueDistance(const EdgeSegment *edge, const SignedDistance &distance, double param);
void addEdgePseudoDistance(double distance); void addEdgePerpendicularDistance(double distance);
void merge(const PseudoDistanceSelectorBase &other); void merge(const PerpendicularDistanceSelectorBase &other);
double computeDistance(const Point2 &p) const; double computeDistance(const Point2 &p) const;
SignedDistance trueDistance() const; SignedDistance trueDistance() const;
private: private:
SignedDistance minTrueDistance; SignedDistance minTrueDistance;
double minNegativePseudoDistance; double minNegativePerpendicularDistance;
double minPositivePseudoDistance; double minPositivePerpendicularDistance;
const EdgeSegment *nearEdge; const EdgeSegment *nearEdge;
double nearEdgeParam; double nearEdgeParam;
}; };
/// Selects the nearest edge by its pseudo-distance. /// Selects the nearest edge by its perpendicular distance.
class PseudoDistanceSelector : public PseudoDistanceSelectorBase { class PerpendicularDistanceSelector : public PerpendicularDistanceSelectorBase {
public: public:
typedef double DistanceType; typedef double DistanceType;
@ -85,12 +85,12 @@ private:
}; };
/// Selects the nearest edge for each of the three channels by its pseudo-distance. /// Selects the nearest edge for each of the three channels by its perpendicular distance.
class MultiDistanceSelector { class MultiDistanceSelector {
public: public:
typedef MultiDistance DistanceType; typedef MultiDistance DistanceType;
typedef PseudoDistanceSelectorBase::EdgeCache EdgeCache; typedef PerpendicularDistanceSelectorBase::EdgeCache EdgeCache;
void reset(const Point2 &p); void reset(const Point2 &p);
void addEdge(EdgeCache &cache, const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge); void addEdge(EdgeCache &cache, const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge);
@ -100,11 +100,11 @@ public:
private: private:
Point2 p; Point2 p;
PseudoDistanceSelectorBase r, g, b; PerpendicularDistanceSelectorBase r, g, b;
}; };
/// Selects the nearest edge for each of the three color channels by its pseudo-distance and by true distance for the alpha channel. /// Selects the nearest edge for each of the three color channels by its perpendicular distance and by true distance for the alpha channel.
class MultiAndTrueDistanceSelector : public MultiDistanceSelector { class MultiAndTrueDistanceSelector : public MultiDistanceSelector {
public: public:

79
thirdparty/msdfgen/core/export-svg.cpp vendored Normal file
View File

@ -0,0 +1,79 @@
#include "export-svg.h"
#include <cstdio>
#include "edge-segments.h"
namespace msdfgen {
static void writeSvgCoord(FILE *f, Point2 coord) {
fprintf(f, "%.17g %.17g", coord.x, coord.y);
}
static void writeSvgPathDef(FILE *f, const Shape &shape) {
bool beginning = true;
for (const Contour &c : shape.contours) {
if (c.edges.empty())
continue;
if (beginning)
beginning = false;
else
fputc(' ', f);
fputs("M ", f);
writeSvgCoord(f, c.edges[0]->controlPoints()[0]);
for (const EdgeHolder &e : c.edges) {
const Point2 *cp = e->controlPoints();
switch (e->type()) {
case (int) LinearSegment::EDGE_TYPE:
fputs(" L ", f);
writeSvgCoord(f, cp[1]);
break;
case (int) QuadraticSegment::EDGE_TYPE:
fputs(" Q ", f);
writeSvgCoord(f, cp[1]);
fputc(' ', f);
writeSvgCoord(f, cp[2]);
break;
case (int) CubicSegment::EDGE_TYPE:
fputs(" C ", f);
writeSvgCoord(f, cp[1]);
fputc(' ', f);
writeSvgCoord(f, cp[2]);
fputc(' ', f);
writeSvgCoord(f, cp[3]);
break;
}
}
fputs(" Z", f);
}
}
bool saveSvgShape(const Shape &shape, const char *filename) {
if (FILE *f = fopen(filename, "w")) {
fputs("<svg xmlns=\"http://www.w3.org/2000/svg\"><path", f);
if (!shape.inverseYAxis)
fputs(" transform=\"scale(1 -1)\"", f);
fputs(" d=\"", f);
writeSvgPathDef(f, shape);
fputs("\"/></svg>\n", f);
fclose(f);
return true;
}
return false;
}
bool saveSvgShape(const Shape &shape, const Shape::Bounds &bounds, const char *filename) {
if (FILE *f = fopen(filename, "w")) {
fprintf(f, "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"%.17g %.17g %.17g %.17g\"><path", bounds.l, bounds.b, bounds.r-bounds.l, bounds.t-bounds.b);
if (!shape.inverseYAxis)
fprintf(f, " transform=\"translate(0 %.17g) scale(1 -1)\"", bounds.b+bounds.t);
fputs(" d=\"", f);
writeSvgPathDef(f, shape);
fputs("\"/></svg>\n", f);
fclose(f);
return true;
}
return false;
}
}

11
thirdparty/msdfgen/core/export-svg.h vendored Normal file
View File

@ -0,0 +1,11 @@
#pragma once
#include "Shape.h"
namespace msdfgen {
bool saveSvgShape(const Shape &shape, const char *filename);
bool saveSvgShape(const Shape &shape, const Shape::Bounds &bounds, const char *filename);
}

View File

@ -10,7 +10,7 @@
namespace msdfgen { namespace msdfgen {
template <int N> template <int N>
static void msdfErrorCorrectionInner(const BitmapRef<float, N> &sdf, const Shape &shape, const Projection &projection, double range, const MSDFGeneratorConfig &config) { static void msdfErrorCorrectionInner(const BitmapRef<float, N> &sdf, const Shape &shape, const SDFTransformation &transformation, const MSDFGeneratorConfig &config) {
if (config.errorCorrection.mode == ErrorCorrectionConfig::DISABLED) if (config.errorCorrection.mode == ErrorCorrectionConfig::DISABLED)
return; return;
Bitmap<byte, 1> stencilBuffer; Bitmap<byte, 1> stencilBuffer;
@ -19,7 +19,7 @@ static void msdfErrorCorrectionInner(const BitmapRef<float, N> &sdf, const Shape
BitmapRef<byte, 1> stencil; BitmapRef<byte, 1> stencil;
stencil.pixels = config.errorCorrection.buffer ? config.errorCorrection.buffer : (byte *) stencilBuffer; stencil.pixels = config.errorCorrection.buffer ? config.errorCorrection.buffer : (byte *) stencilBuffer;
stencil.width = sdf.width, stencil.height = sdf.height; stencil.width = sdf.width, stencil.height = sdf.height;
MSDFErrorCorrection ec(stencil, projection, range); MSDFErrorCorrection ec(stencil, transformation);
ec.setMinDeviationRatio(config.errorCorrection.minDeviationRatio); ec.setMinDeviationRatio(config.errorCorrection.minDeviationRatio);
ec.setMinImproveRatio(config.errorCorrection.minImproveRatio); ec.setMinImproveRatio(config.errorCorrection.minImproveRatio);
switch (config.errorCorrection.mode) { switch (config.errorCorrection.mode) {
@ -49,9 +49,9 @@ static void msdfErrorCorrectionInner(const BitmapRef<float, N> &sdf, const Shape
} }
template <int N> template <int N>
static void msdfErrorCorrectionShapeless(const BitmapRef<float, N> &sdf, const Projection &projection, double range, double minDeviationRatio, bool protectAll) { static void msdfErrorCorrectionShapeless(const BitmapRef<float, N> &sdf, const SDFTransformation &transformation, double minDeviationRatio, bool protectAll) {
Bitmap<byte, 1> stencilBuffer(sdf.width, sdf.height); Bitmap<byte, 1> stencilBuffer(sdf.width, sdf.height);
MSDFErrorCorrection ec(stencilBuffer, projection, range); MSDFErrorCorrection ec(stencilBuffer, transformation);
ec.setMinDeviationRatio(minDeviationRatio); ec.setMinDeviationRatio(minDeviationRatio);
if (protectAll) if (protectAll)
ec.protectAll(); ec.protectAll();
@ -59,25 +59,43 @@ static void msdfErrorCorrectionShapeless(const BitmapRef<float, N> &sdf, const P
ec.apply(sdf); ec.apply(sdf);
} }
void msdfErrorCorrection(const BitmapRef<float, 3> &sdf, const Shape &shape, const Projection &projection, double range, const MSDFGeneratorConfig &config) { void msdfErrorCorrection(const BitmapRef<float, 3> &sdf, const Shape &shape, const SDFTransformation &transformation, const MSDFGeneratorConfig &config) {
msdfErrorCorrectionInner(sdf, shape, projection, range, config); msdfErrorCorrectionInner(sdf, shape, transformation, config);
} }
void msdfErrorCorrection(const BitmapRef<float, 4> &sdf, const Shape &shape, const Projection &projection, double range, const MSDFGeneratorConfig &config) { void msdfErrorCorrection(const BitmapRef<float, 4> &sdf, const Shape &shape, const SDFTransformation &transformation, const MSDFGeneratorConfig &config) {
msdfErrorCorrectionInner(sdf, shape, projection, range, config); msdfErrorCorrectionInner(sdf, shape, transformation, config);
}
void msdfErrorCorrection(const BitmapRef<float, 3> &sdf, const Shape &shape, const Projection &projection, Range range, const MSDFGeneratorConfig &config) {
msdfErrorCorrectionInner(sdf, shape, SDFTransformation(projection, range), config);
}
void msdfErrorCorrection(const BitmapRef<float, 4> &sdf, const Shape &shape, const Projection &projection, Range range, const MSDFGeneratorConfig &config) {
msdfErrorCorrectionInner(sdf, shape, SDFTransformation(projection, range), config);
} }
void msdfFastDistanceErrorCorrection(const BitmapRef<float, 3> &sdf, const Projection &projection, double range, double minDeviationRatio) { void msdfFastDistanceErrorCorrection(const BitmapRef<float, 3> &sdf, const SDFTransformation &transformation, double minDeviationRatio) {
msdfErrorCorrectionShapeless(sdf, projection, range, minDeviationRatio, false); msdfErrorCorrectionShapeless(sdf, transformation, minDeviationRatio, false);
} }
void msdfFastDistanceErrorCorrection(const BitmapRef<float, 4> &sdf, const Projection &projection, double range, double minDeviationRatio) { void msdfFastDistanceErrorCorrection(const BitmapRef<float, 4> &sdf, const SDFTransformation &transformation, double minDeviationRatio) {
msdfErrorCorrectionShapeless(sdf, projection, range, minDeviationRatio, false); msdfErrorCorrectionShapeless(sdf, transformation, minDeviationRatio, false);
}
void msdfFastDistanceErrorCorrection(const BitmapRef<float, 3> &sdf, const Projection &projection, Range range, double minDeviationRatio) {
msdfErrorCorrectionShapeless(sdf, SDFTransformation(projection, range), minDeviationRatio, false);
}
void msdfFastDistanceErrorCorrection(const BitmapRef<float, 4> &sdf, const Projection &projection, Range range, double minDeviationRatio) {
msdfErrorCorrectionShapeless(sdf, SDFTransformation(projection, range), minDeviationRatio, false);
} }
void msdfFastEdgeErrorCorrection(const BitmapRef<float, 3> &sdf, const Projection &projection, double range, double minDeviationRatio) { void msdfFastEdgeErrorCorrection(const BitmapRef<float, 3> &sdf, const SDFTransformation &transformation, double minDeviationRatio) {
msdfErrorCorrectionShapeless(sdf, projection, range, minDeviationRatio, true); msdfErrorCorrectionShapeless(sdf, transformation, minDeviationRatio, true);
} }
void msdfFastEdgeErrorCorrection(const BitmapRef<float, 4> &sdf, const Projection &projection, double range, double minDeviationRatio) { void msdfFastEdgeErrorCorrection(const BitmapRef<float, 4> &sdf, const SDFTransformation &transformation, double minDeviationRatio) {
msdfErrorCorrectionShapeless(sdf, projection, range, minDeviationRatio, true); msdfErrorCorrectionShapeless(sdf, transformation, minDeviationRatio, true);
}
void msdfFastEdgeErrorCorrection(const BitmapRef<float, 3> &sdf, const Projection &projection, Range range, double minDeviationRatio) {
msdfErrorCorrectionShapeless(sdf, SDFTransformation(projection, range), minDeviationRatio, true);
}
void msdfFastEdgeErrorCorrection(const BitmapRef<float, 4> &sdf, const Projection &projection, Range range, double minDeviationRatio) {
msdfErrorCorrectionShapeless(sdf, SDFTransformation(projection, range), minDeviationRatio, true);
} }

View File

@ -2,7 +2,9 @@
#pragma once #pragma once
#include "Vector2.hpp" #include "Vector2.hpp"
#include "Range.hpp"
#include "Projection.h" #include "Projection.h"
#include "SDFTransformation.h"
#include "Shape.h" #include "Shape.h"
#include "BitmapRef.hpp" #include "BitmapRef.hpp"
#include "generator-config.h" #include "generator-config.h"
@ -10,16 +12,22 @@
namespace msdfgen { namespace msdfgen {
/// Predicts potential artifacts caused by the interpolation of the MSDF and corrects them by converting nearby texels to single-channel. /// Predicts potential artifacts caused by the interpolation of the MSDF and corrects them by converting nearby texels to single-channel.
void msdfErrorCorrection(const BitmapRef<float, 3> &sdf, const Shape &shape, const Projection &projection, double range, const MSDFGeneratorConfig &config = MSDFGeneratorConfig()); void msdfErrorCorrection(const BitmapRef<float, 3> &sdf, const Shape &shape, const SDFTransformation &transformation, const MSDFGeneratorConfig &config = MSDFGeneratorConfig());
void msdfErrorCorrection(const BitmapRef<float, 4> &sdf, const Shape &shape, const Projection &projection, double range, const MSDFGeneratorConfig &config = MSDFGeneratorConfig()); void msdfErrorCorrection(const BitmapRef<float, 4> &sdf, const Shape &shape, const SDFTransformation &transformation, const MSDFGeneratorConfig &config = MSDFGeneratorConfig());
void msdfErrorCorrection(const BitmapRef<float, 3> &sdf, const Shape &shape, const Projection &projection, Range range, const MSDFGeneratorConfig &config = MSDFGeneratorConfig());
void msdfErrorCorrection(const BitmapRef<float, 4> &sdf, const Shape &shape, const Projection &projection, Range range, const MSDFGeneratorConfig &config = MSDFGeneratorConfig());
/// Applies the simplified error correction to all discontiunous distances (INDISCRIMINATE mode). Does not need shape or translation. /// Applies the simplified error correction to all discontiunous distances (INDISCRIMINATE mode). Does not need shape or translation.
void msdfFastDistanceErrorCorrection(const BitmapRef<float, 3> &sdf, const Projection &projection, double range, double minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio); void msdfFastDistanceErrorCorrection(const BitmapRef<float, 3> &sdf, const SDFTransformation &transformation, double minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio);
void msdfFastDistanceErrorCorrection(const BitmapRef<float, 4> &sdf, const Projection &projection, double range, double minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio); 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);
/// Applies the simplified error correction to edges only (EDGE_ONLY mode). Does not need shape or translation. /// 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 Projection &projection, double range, double minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio); void msdfFastEdgeErrorCorrection(const BitmapRef<float, 3> &sdf, const SDFTransformation &transformation, double minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio);
void msdfFastEdgeErrorCorrection(const BitmapRef<float, 4> &sdf, const Projection &projection, double range, 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);
/// The original version of the error correction algorithm. /// The original version of the error correction algorithm.
void msdfErrorCorrection_legacy(const BitmapRef<float, 3> &output, const Vector2 &threshold); void msdfErrorCorrection_legacy(const BitmapRef<float, 3> &output, const Vector2 &threshold);

View File

@ -13,45 +13,45 @@ class DistancePixelConversion;
template <> template <>
class DistancePixelConversion<double> { class DistancePixelConversion<double> {
double invRange; DistanceMapping mapping;
public: public:
typedef BitmapRef<float, 1> BitmapRefType; typedef BitmapRef<float, 1> BitmapRefType;
inline explicit DistancePixelConversion(double range) : invRange(1/range) { } inline explicit DistancePixelConversion(DistanceMapping mapping) : mapping(mapping) { }
inline void operator()(float *pixels, double distance) const { inline void operator()(float *pixels, double distance) const {
*pixels = float(invRange*distance+.5); *pixels = float(mapping(distance));
} }
}; };
template <> template <>
class DistancePixelConversion<MultiDistance> { class DistancePixelConversion<MultiDistance> {
double invRange; DistanceMapping mapping;
public: public:
typedef BitmapRef<float, 3> BitmapRefType; typedef BitmapRef<float, 3> BitmapRefType;
inline explicit DistancePixelConversion(double range) : invRange(1/range) { } inline explicit DistancePixelConversion(DistanceMapping mapping) : mapping(mapping) { }
inline void operator()(float *pixels, const MultiDistance &distance) const { inline void operator()(float *pixels, const MultiDistance &distance) const {
pixels[0] = float(invRange*distance.r+.5); pixels[0] = float(mapping(distance.r));
pixels[1] = float(invRange*distance.g+.5); pixels[1] = float(mapping(distance.g));
pixels[2] = float(invRange*distance.b+.5); pixels[2] = float(mapping(distance.b));
} }
}; };
template <> template <>
class DistancePixelConversion<MultiAndTrueDistance> { class DistancePixelConversion<MultiAndTrueDistance> {
double invRange; DistanceMapping mapping;
public: public:
typedef BitmapRef<float, 4> BitmapRefType; typedef BitmapRef<float, 4> BitmapRefType;
inline explicit DistancePixelConversion(double range) : invRange(1/range) { } inline explicit DistancePixelConversion(DistanceMapping mapping) : mapping(mapping) { }
inline void operator()(float *pixels, const MultiAndTrueDistance &distance) const { inline void operator()(float *pixels, const MultiAndTrueDistance &distance) const {
pixels[0] = float(invRange*distance.r+.5); pixels[0] = float(mapping(distance.r));
pixels[1] = float(invRange*distance.g+.5); pixels[1] = float(mapping(distance.g));
pixels[2] = float(invRange*distance.b+.5); pixels[2] = float(mapping(distance.b));
pixels[3] = float(invRange*distance.a+.5); pixels[3] = float(mapping(distance.a));
} }
}; };
template <class ContourCombiner> template <class ContourCombiner>
void generateDistanceField(const typename DistancePixelConversion<typename ContourCombiner::DistanceType>::BitmapRefType &output, const Shape &shape, const Projection &projection, double range) { void generateDistanceField(const typename DistancePixelConversion<typename ContourCombiner::DistanceType>::BitmapRefType &output, const Shape &shape, const SDFTransformation &transformation) {
DistancePixelConversion<typename ContourCombiner::DistanceType> distancePixelConversion(range); DistancePixelConversion<typename ContourCombiner::DistanceType> distancePixelConversion(transformation.distanceMapping);
#ifdef MSDFGEN_USE_OPENMP #ifdef MSDFGEN_USE_OPENMP
#pragma omp parallel #pragma omp parallel
#endif #endif
@ -65,7 +65,7 @@ void generateDistanceField(const typename DistancePixelConversion<typename Conto
int row = shape.inverseYAxis ? output.height-y-1 : y; int row = shape.inverseYAxis ? output.height-y-1 : y;
for (int col = 0; col < output.width; ++col) { for (int col = 0; col < output.width; ++col) {
int x = rightToLeft ? output.width-col-1 : col; int x = rightToLeft ? output.width-col-1 : col;
Point2 p = projection.unproject(Point2(x+.5, y+.5)); Point2 p = transformation.unproject(Point2(x+.5, y+.5));
typename ContourCombiner::DistanceType distance = distanceFinder.distance(p); typename ContourCombiner::DistanceType distance = distanceFinder.distance(p);
distancePixelConversion(output(x, row), distance); distancePixelConversion(output(x, row), distance);
} }
@ -74,57 +74,96 @@ void generateDistanceField(const typename DistancePixelConversion<typename Conto
} }
} }
void generateSDF(const BitmapRef<float, 1> &output, const Shape &shape, const Projection &projection, double range, const GeneratorConfig &config) { void generateSDF(const BitmapRef<float, 1> &output, const Shape &shape, const SDFTransformation &transformation, const GeneratorConfig &config) {
if (config.overlapSupport) if (config.overlapSupport)
generateDistanceField<OverlappingContourCombiner<TrueDistanceSelector> >(output, shape, projection, range); generateDistanceField<OverlappingContourCombiner<TrueDistanceSelector> >(output, shape, transformation);
else else
generateDistanceField<SimpleContourCombiner<TrueDistanceSelector> >(output, shape, projection, range); generateDistanceField<SimpleContourCombiner<TrueDistanceSelector> >(output, shape, transformation);
} }
void generatePseudoSDF(const BitmapRef<float, 1> &output, const Shape &shape, const Projection &projection, double range, const GeneratorConfig &config) { void generatePSDF(const BitmapRef<float, 1> &output, const Shape &shape, const SDFTransformation &transformation, const GeneratorConfig &config) {
if (config.overlapSupport) if (config.overlapSupport)
generateDistanceField<OverlappingContourCombiner<PseudoDistanceSelector> >(output, shape, projection, range); generateDistanceField<OverlappingContourCombiner<PerpendicularDistanceSelector> >(output, shape, transformation);
else else
generateDistanceField<SimpleContourCombiner<PseudoDistanceSelector> >(output, shape, projection, range); generateDistanceField<SimpleContourCombiner<PerpendicularDistanceSelector> >(output, shape, transformation);
} }
void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, const Projection &projection, double range, const MSDFGeneratorConfig &config) { void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, const SDFTransformation &transformation, const MSDFGeneratorConfig &config) {
if (config.overlapSupport) if (config.overlapSupport)
generateDistanceField<OverlappingContourCombiner<MultiDistanceSelector> >(output, shape, projection, range); generateDistanceField<OverlappingContourCombiner<MultiDistanceSelector> >(output, shape, transformation);
else else
generateDistanceField<SimpleContourCombiner<MultiDistanceSelector> >(output, shape, projection, range); generateDistanceField<SimpleContourCombiner<MultiDistanceSelector> >(output, shape, transformation);
msdfErrorCorrection(output, shape, projection, range, config); msdfErrorCorrection(output, shape, transformation, config);
} }
void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, const Projection &projection, double range, const MSDFGeneratorConfig &config) { void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, const SDFTransformation &transformation, const MSDFGeneratorConfig &config) {
if (config.overlapSupport) if (config.overlapSupport)
generateDistanceField<OverlappingContourCombiner<MultiAndTrueDistanceSelector> >(output, shape, projection, range); generateDistanceField<OverlappingContourCombiner<MultiAndTrueDistanceSelector> >(output, shape, transformation);
else else
generateDistanceField<SimpleContourCombiner<MultiAndTrueDistanceSelector> >(output, shape, projection, range); generateDistanceField<SimpleContourCombiner<MultiAndTrueDistanceSelector> >(output, shape, transformation);
msdfErrorCorrection(output, shape, projection, range, config); msdfErrorCorrection(output, shape, transformation, config);
}
void generateSDF(const BitmapRef<float, 1> &output, const Shape &shape, const Projection &projection, Range range, const GeneratorConfig &config) {
if (config.overlapSupport)
generateDistanceField<OverlappingContourCombiner<TrueDistanceSelector> >(output, shape, SDFTransformation(projection, range));
else
generateDistanceField<SimpleContourCombiner<TrueDistanceSelector> >(output, shape, SDFTransformation(projection, range));
}
void generatePSDF(const BitmapRef<float, 1> &output, const Shape &shape, const Projection &projection, Range range, const GeneratorConfig &config) {
if (config.overlapSupport)
generateDistanceField<OverlappingContourCombiner<PerpendicularDistanceSelector> >(output, shape, SDFTransformation(projection, range));
else
generateDistanceField<SimpleContourCombiner<PerpendicularDistanceSelector> >(output, shape, SDFTransformation(projection, range));
}
void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, const Projection &projection, Range range, const MSDFGeneratorConfig &config) {
if (config.overlapSupport)
generateDistanceField<OverlappingContourCombiner<MultiDistanceSelector> >(output, shape, SDFTransformation(projection, range));
else
generateDistanceField<SimpleContourCombiner<MultiDistanceSelector> >(output, shape, SDFTransformation(projection, range));
msdfErrorCorrection(output, shape, SDFTransformation(projection, range), config);
}
void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, const Projection &projection, Range range, const MSDFGeneratorConfig &config) {
if (config.overlapSupport)
generateDistanceField<OverlappingContourCombiner<MultiAndTrueDistanceSelector> >(output, shape, SDFTransformation(projection, range));
else
generateDistanceField<SimpleContourCombiner<MultiAndTrueDistanceSelector> >(output, shape, SDFTransformation(projection, range));
msdfErrorCorrection(output, shape, SDFTransformation(projection, range), config);
} }
// Legacy API // Legacy API
void generateSDF(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport) { void generatePseudoSDF(const BitmapRef<float, 1> &output, const Shape &shape, const Projection &projection, Range range, const GeneratorConfig &config) {
generatePSDF(output, shape, SDFTransformation(projection, range), config);
}
void generateSDF(const BitmapRef<float, 1> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport) {
generateSDF(output, shape, Projection(scale, translate), range, GeneratorConfig(overlapSupport)); generateSDF(output, shape, Projection(scale, translate), range, GeneratorConfig(overlapSupport));
} }
void generatePseudoSDF(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport) { void generatePSDF(const BitmapRef<float, 1> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport) {
generatePseudoSDF(output, shape, Projection(scale, translate), range, GeneratorConfig(overlapSupport)); generatePSDF(output, shape, Projection(scale, translate), range, GeneratorConfig(overlapSupport));
} }
void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, const ErrorCorrectionConfig &errorCorrectionConfig, bool overlapSupport) { void generatePseudoSDF(const BitmapRef<float, 1> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport) {
generatePSDF(output, shape, Projection(scale, translate), range, GeneratorConfig(overlapSupport));
}
void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate, const ErrorCorrectionConfig &errorCorrectionConfig, bool overlapSupport) {
generateMSDF(output, shape, Projection(scale, translate), range, MSDFGeneratorConfig(overlapSupport, errorCorrectionConfig)); generateMSDF(output, shape, Projection(scale, translate), range, MSDFGeneratorConfig(overlapSupport, errorCorrectionConfig));
} }
void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, const ErrorCorrectionConfig &errorCorrectionConfig, bool overlapSupport) { void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate, const ErrorCorrectionConfig &errorCorrectionConfig, bool overlapSupport) {
generateMTSDF(output, shape, Projection(scale, translate), range, MSDFGeneratorConfig(overlapSupport, errorCorrectionConfig)); generateMTSDF(output, shape, Projection(scale, translate), range, MSDFGeneratorConfig(overlapSupport, errorCorrectionConfig));
} }
// Legacy version // Legacy version
void generateSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate) { void generateSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate) {
DistanceMapping distanceMapping(range);
#ifdef MSDFGEN_USE_OPENMP #ifdef MSDFGEN_USE_OPENMP
#pragma omp parallel for #pragma omp parallel for
#endif #endif
@ -140,12 +179,13 @@ void generateSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape, d
if (distance < minDistance) if (distance < minDistance)
minDistance = distance; minDistance = distance;
} }
*output(x, row) = float(minDistance.distance/range+.5); *output(x, row) = float(distanceMapping(minDistance.distance));
} }
} }
} }
void generatePseudoSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate) { void generatePSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate) {
DistanceMapping distanceMapping(range);
#ifdef MSDFGEN_USE_OPENMP #ifdef MSDFGEN_USE_OPENMP
#pragma omp parallel for #pragma omp parallel for
#endif #endif
@ -167,13 +207,18 @@ void generatePseudoSDF_legacy(const BitmapRef<float, 1> &output, const Shape &sh
} }
} }
if (nearEdge) if (nearEdge)
(*nearEdge)->distanceToPseudoDistance(minDistance, p, nearParam); (*nearEdge)->distanceToPerpendicularDistance(minDistance, p, nearParam);
*output(x, row) = float(minDistance.distance/range+.5); *output(x, row) = float(distanceMapping(minDistance.distance));
} }
} }
} }
void generateMSDF_legacy(const BitmapRef<float, 3> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, ErrorCorrectionConfig errorCorrectionConfig) { void generatePseudoSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate) {
generatePSDF_legacy(output, shape, range, scale, translate);
}
void generateMSDF_legacy(const BitmapRef<float, 3> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate, ErrorCorrectionConfig errorCorrectionConfig) {
DistanceMapping distanceMapping(range);
#ifdef MSDFGEN_USE_OPENMP #ifdef MSDFGEN_USE_OPENMP
#pragma omp parallel for #pragma omp parallel for
#endif #endif
@ -212,14 +257,14 @@ void generateMSDF_legacy(const BitmapRef<float, 3> &output, const Shape &shape,
} }
if (r.nearEdge) if (r.nearEdge)
(*r.nearEdge)->distanceToPseudoDistance(r.minDistance, p, r.nearParam); (*r.nearEdge)->distanceToPerpendicularDistance(r.minDistance, p, r.nearParam);
if (g.nearEdge) if (g.nearEdge)
(*g.nearEdge)->distanceToPseudoDistance(g.minDistance, p, g.nearParam); (*g.nearEdge)->distanceToPerpendicularDistance(g.minDistance, p, g.nearParam);
if (b.nearEdge) if (b.nearEdge)
(*b.nearEdge)->distanceToPseudoDistance(b.minDistance, p, b.nearParam); (*b.nearEdge)->distanceToPerpendicularDistance(b.minDistance, p, b.nearParam);
output(x, row)[0] = float(r.minDistance.distance/range+.5); output(x, row)[0] = float(distanceMapping(r.minDistance.distance));
output(x, row)[1] = float(g.minDistance.distance/range+.5); output(x, row)[1] = float(distanceMapping(g.minDistance.distance));
output(x, row)[2] = float(b.minDistance.distance/range+.5); output(x, row)[2] = float(distanceMapping(b.minDistance.distance));
} }
} }
@ -227,7 +272,8 @@ void generateMSDF_legacy(const BitmapRef<float, 3> &output, const Shape &shape,
msdfErrorCorrection(output, shape, Projection(scale, translate), range, MSDFGeneratorConfig(false, errorCorrectionConfig)); msdfErrorCorrection(output, shape, Projection(scale, translate), range, MSDFGeneratorConfig(false, errorCorrectionConfig));
} }
void generateMTSDF_legacy(const BitmapRef<float, 4> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, ErrorCorrectionConfig errorCorrectionConfig) { void generateMTSDF_legacy(const BitmapRef<float, 4> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate, ErrorCorrectionConfig errorCorrectionConfig) {
DistanceMapping distanceMapping(range);
#ifdef MSDFGEN_USE_OPENMP #ifdef MSDFGEN_USE_OPENMP
#pragma omp parallel for #pragma omp parallel for
#endif #endif
@ -269,15 +315,15 @@ void generateMTSDF_legacy(const BitmapRef<float, 4> &output, const Shape &shape,
} }
if (r.nearEdge) if (r.nearEdge)
(*r.nearEdge)->distanceToPseudoDistance(r.minDistance, p, r.nearParam); (*r.nearEdge)->distanceToPerpendicularDistance(r.minDistance, p, r.nearParam);
if (g.nearEdge) if (g.nearEdge)
(*g.nearEdge)->distanceToPseudoDistance(g.minDistance, p, g.nearParam); (*g.nearEdge)->distanceToPerpendicularDistance(g.minDistance, p, g.nearParam);
if (b.nearEdge) if (b.nearEdge)
(*b.nearEdge)->distanceToPseudoDistance(b.minDistance, p, b.nearParam); (*b.nearEdge)->distanceToPerpendicularDistance(b.minDistance, p, b.nearParam);
output(x, row)[0] = float(r.minDistance.distance/range+.5); output(x, row)[0] = float(distanceMapping(r.minDistance.distance));
output(x, row)[1] = float(g.minDistance.distance/range+.5); output(x, row)[1] = float(distanceMapping(g.minDistance.distance));
output(x, row)[2] = float(b.minDistance.distance/range+.5); output(x, row)[2] = float(distanceMapping(b.minDistance.distance));
output(x, row)[3] = float(minDistance.distance/range+.5); output(x, row)[3] = float(distanceMapping(minDistance.distance));
} }
} }

View File

@ -6,7 +6,7 @@
namespace msdfgen { namespace msdfgen {
inline byte pixelFloatToByte(float x) { inline byte pixelFloatToByte(float x) {
return byte(clamp(256.f*x, 255.f)); return byte(~int(255.5f-255.f*clamp(x)));
} }
inline float pixelByteToFloat(byte x) { inline float pixelByteToFloat(byte x) {

View File

@ -2,89 +2,175 @@
#include "render-sdf.h" #include "render-sdf.h"
#include "arithmetics.hpp" #include "arithmetics.hpp"
#include "DistanceMapping.h"
#include "pixel-conversion.hpp" #include "pixel-conversion.hpp"
#include "bitmap-interpolation.hpp" #include "bitmap-interpolation.hpp"
namespace msdfgen { namespace msdfgen {
static float distVal(float dist, double pxRange, float midValue) { static float distVal(float dist, DistanceMapping mapping) {
if (!pxRange) return (float) clamp(mapping(dist)+.5);
return (float) (dist > midValue);
return (float) clamp((dist-midValue)*pxRange+.5);
} }
void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 1> &sdf, double pxRange, float midValue) { void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 1> &sdf, Range sdfPxRange, float sdThreshold) {
Vector2 scale((double) sdf.width/output.width, (double) sdf.height/output.height); Vector2 scale((double) sdf.width/output.width, (double) sdf.height/output.height);
pxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height); if (sdfPxRange.lower == sdfPxRange.upper) {
for (int y = 0; y < output.height; ++y) for (int y = 0; y < output.height; ++y) {
for (int x = 0; x < output.width; ++x) { for (int x = 0; x < output.width; ++x) {
float sd; float sd;
interpolate(&sd, sdf, scale*Point2(x+.5, y+.5)); interpolate(&sd, sdf, scale*Point2(x+.5, y+.5));
*output(x, y) = distVal(sd, pxRange, midValue); *output(x, y) = float(sd >= sdThreshold);
}
} }
} else {
sdfPxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height);
DistanceMapping distanceMapping = DistanceMapping::inverse(sdfPxRange);
float sdBias = .5f-sdThreshold;
for (int y = 0; y < output.height; ++y) {
for (int x = 0; x < output.width; ++x) {
float sd;
interpolate(&sd, sdf, scale*Point2(x+.5, y+.5));
*output(x, y) = distVal(sd+sdBias, distanceMapping);
}
}
}
} }
void renderSDF(const BitmapRef<float, 3> &output, const BitmapConstRef<float, 1> &sdf, double pxRange, float midValue) { void renderSDF(const BitmapRef<float, 3> &output, const BitmapConstRef<float, 1> &sdf, Range sdfPxRange, float sdThreshold) {
Vector2 scale((double) sdf.width/output.width, (double) sdf.height/output.height); Vector2 scale((double) sdf.width/output.width, (double) sdf.height/output.height);
pxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height); if (sdfPxRange.lower == sdfPxRange.upper) {
for (int y = 0; y < output.height; ++y) for (int y = 0; y < output.height; ++y) {
for (int x = 0; x < output.width; ++x) { for (int x = 0; x < output.width; ++x) {
float sd; float sd;
interpolate(&sd, sdf, scale*Point2(x+.5, y+.5)); interpolate(&sd, sdf, scale*Point2(x+.5, y+.5));
float v = distVal(sd, pxRange, midValue); float v = float(sd >= sdThreshold);
output(x, y)[0] = v; output(x, y)[0] = v;
output(x, y)[1] = v; output(x, y)[1] = v;
output(x, y)[2] = v; output(x, y)[2] = v;
}
} }
} else {
sdfPxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height);
DistanceMapping distanceMapping = DistanceMapping::inverse(sdfPxRange);
float sdBias = .5f-sdThreshold;
for (int y = 0; y < output.height; ++y) {
for (int x = 0; x < output.width; ++x) {
float sd;
interpolate(&sd, sdf, scale*Point2(x+.5, y+.5));
float v = distVal(sd+sdBias, distanceMapping);
output(x, y)[0] = v;
output(x, y)[1] = v;
output(x, y)[2] = v;
}
}
}
} }
void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 3> &sdf, double pxRange, float midValue) { void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 3> &sdf, Range sdfPxRange, float sdThreshold) {
Vector2 scale((double) sdf.width/output.width, (double) sdf.height/output.height); Vector2 scale((double) sdf.width/output.width, (double) sdf.height/output.height);
pxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height); if (sdfPxRange.lower == sdfPxRange.upper) {
for (int y = 0; y < output.height; ++y) for (int y = 0; y < output.height; ++y) {
for (int x = 0; x < output.width; ++x) { for (int x = 0; x < output.width; ++x) {
float sd[3]; float sd[3];
interpolate(sd, sdf, scale*Point2(x+.5, y+.5)); interpolate(sd, sdf, scale*Point2(x+.5, y+.5));
*output(x, y) = distVal(median(sd[0], sd[1], sd[2]), pxRange, midValue); *output(x, y) = float(median(sd[0], sd[1], sd[2]) >= sdThreshold);
}
} }
} else {
sdfPxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height);
DistanceMapping distanceMapping = DistanceMapping::inverse(sdfPxRange);
float sdBias = .5f-sdThreshold;
for (int y = 0; y < output.height; ++y) {
for (int x = 0; x < output.width; ++x) {
float sd[3];
interpolate(sd, sdf, scale*Point2(x+.5, y+.5));
*output(x, y) = distVal(median(sd[0], sd[1], sd[2])+sdBias, distanceMapping);
}
}
}
} }
void renderSDF(const BitmapRef<float, 3> &output, const BitmapConstRef<float, 3> &sdf, double pxRange, float midValue) { void renderSDF(const BitmapRef<float, 3> &output, const BitmapConstRef<float, 3> &sdf, Range sdfPxRange, float sdThreshold) {
Vector2 scale((double) sdf.width/output.width, (double) sdf.height/output.height); Vector2 scale((double) sdf.width/output.width, (double) sdf.height/output.height);
pxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height); if (sdfPxRange.lower == sdfPxRange.upper) {
for (int y = 0; y < output.height; ++y) for (int y = 0; y < output.height; ++y) {
for (int x = 0; x < output.width; ++x) { for (int x = 0; x < output.width; ++x) {
float sd[3]; float sd[3];
interpolate(sd, sdf, scale*Point2(x+.5, y+.5)); interpolate(sd, sdf, scale*Point2(x+.5, y+.5));
output(x, y)[0] = distVal(sd[0], pxRange, midValue); output(x, y)[0] = float(sd[0] >= sdThreshold);
output(x, y)[1] = distVal(sd[1], pxRange, midValue); output(x, y)[1] = float(sd[1] >= sdThreshold);
output(x, y)[2] = distVal(sd[2], pxRange, midValue); output(x, y)[2] = float(sd[2] >= sdThreshold);
}
} }
} else {
sdfPxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height);
DistanceMapping distanceMapping = DistanceMapping::inverse(sdfPxRange);
float sdBias = .5f-sdThreshold;
for (int y = 0; y < output.height; ++y) {
for (int x = 0; x < output.width; ++x) {
float sd[3];
interpolate(sd, sdf, scale*Point2(x+.5, y+.5));
output(x, y)[0] = distVal(sd[0]+sdBias, distanceMapping);
output(x, y)[1] = distVal(sd[1]+sdBias, distanceMapping);
output(x, y)[2] = distVal(sd[2]+sdBias, distanceMapping);
}
}
}
} }
void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 4> &sdf, double pxRange, float midValue) { void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 4> &sdf, Range sdfPxRange, float sdThreshold) {
Vector2 scale((double) sdf.width/output.width, (double) sdf.height/output.height); Vector2 scale((double) sdf.width/output.width, (double) sdf.height/output.height);
pxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height); if (sdfPxRange.lower == sdfPxRange.upper) {
for (int y = 0; y < output.height; ++y) for (int y = 0; y < output.height; ++y) {
for (int x = 0; x < output.width; ++x) { for (int x = 0; x < output.width; ++x) {
float sd[4]; float sd[4];
interpolate(sd, sdf, scale*Point2(x+.5, y+.5)); interpolate(sd, sdf, scale*Point2(x+.5, y+.5));
*output(x, y) = distVal(median(sd[0], sd[1], sd[2]), pxRange, midValue); *output(x, y) = float(median(sd[0], sd[1], sd[2]) >= sdThreshold);
}
} }
} else {
sdfPxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height);
DistanceMapping distanceMapping = DistanceMapping::inverse(sdfPxRange);
float sdBias = .5f-sdThreshold;
for (int y = 0; y < output.height; ++y) {
for (int x = 0; x < output.width; ++x) {
float sd[4];
interpolate(sd, sdf, scale*Point2(x+.5, y+.5));
*output(x, y) = distVal(median(sd[0], sd[1], sd[2])+sdBias, distanceMapping);
}
}
}
} }
void renderSDF(const BitmapRef<float, 4> &output, const BitmapConstRef<float, 4> &sdf, double pxRange, float midValue) { void renderSDF(const BitmapRef<float, 4> &output, const BitmapConstRef<float, 4> &sdf, Range sdfPxRange, float sdThreshold) {
Vector2 scale((double) sdf.width/output.width, (double) sdf.height/output.height); Vector2 scale((double) sdf.width/output.width, (double) sdf.height/output.height);
pxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height); if (sdfPxRange.lower == sdfPxRange.upper) {
for (int y = 0; y < output.height; ++y) for (int y = 0; y < output.height; ++y) {
for (int x = 0; x < output.width; ++x) { for (int x = 0; x < output.width; ++x) {
float sd[4]; float sd[4];
interpolate(sd, sdf, scale*Point2(x+.5, y+.5)); interpolate(sd, sdf, scale*Point2(x+.5, y+.5));
output(x, y)[0] = distVal(sd[0], pxRange, midValue); output(x, y)[0] = float(sd[0] >= sdThreshold);
output(x, y)[1] = distVal(sd[1], pxRange, midValue); output(x, y)[1] = float(sd[1] >= sdThreshold);
output(x, y)[2] = distVal(sd[2], pxRange, midValue); output(x, y)[2] = float(sd[2] >= sdThreshold);
output(x, y)[3] = distVal(sd[3], pxRange, midValue); output(x, y)[3] = float(sd[3] >= sdThreshold);
}
} }
} else {
sdfPxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height);
DistanceMapping distanceMapping = DistanceMapping::inverse(sdfPxRange);
float sdBias = .5f-sdThreshold;
for (int y = 0; y < output.height; ++y) {
for (int x = 0; x < output.width; ++x) {
float sd[4];
interpolate(sd, sdf, scale*Point2(x+.5, y+.5));
output(x, y)[0] = distVal(sd[0]+sdBias, distanceMapping);
output(x, y)[1] = distVal(sd[1]+sdBias, distanceMapping);
output(x, y)[2] = distVal(sd[2]+sdBias, distanceMapping);
output(x, y)[3] = distVal(sd[3]+sdBias, distanceMapping);
}
}
}
} }
void simulate8bit(const BitmapRef<float, 1> &bitmap) { void simulate8bit(const BitmapRef<float, 1> &bitmap) {

View File

@ -2,17 +2,18 @@
#pragma once #pragma once
#include "Vector2.hpp" #include "Vector2.hpp"
#include "Range.hpp"
#include "BitmapRef.hpp" #include "BitmapRef.hpp"
namespace msdfgen { namespace msdfgen {
/// Reconstructs the shape's appearance into output from the distance field sdf. /// Reconstructs the shape's appearance into output from the distance field sdf.
void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 1> &sdf, double pxRange = 0, float midValue = .5f); void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 1> &sdf, Range sdfPxRange = 0, float sdThreshold = .5f);
void renderSDF(const BitmapRef<float, 3> &output, const BitmapConstRef<float, 1> &sdf, double pxRange = 0, float midValue = .5f); void renderSDF(const BitmapRef<float, 3> &output, const BitmapConstRef<float, 1> &sdf, Range sdfPxRange = 0, float sdThreshold = .5f);
void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 3> &sdf, double pxRange = 0, float midValue = .5f); void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 3> &sdf, Range sdfPxRange = 0, float sdThreshold = .5f);
void renderSDF(const BitmapRef<float, 3> &output, const BitmapConstRef<float, 3> &sdf, double pxRange = 0, float midValue = .5f); void renderSDF(const BitmapRef<float, 3> &output, const BitmapConstRef<float, 3> &sdf, Range sdfPxRange = 0, float sdThreshold = .5f);
void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 4> &sdf, double pxRange = 0, float midValue = .5f); void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 4> &sdf, Range sdfPxRange = 0, float sdThreshold = .5f);
void renderSDF(const BitmapRef<float, 4> &output, const BitmapConstRef<float, 4> &sdf, double pxRange = 0, float midValue = .5f); void renderSDF(const BitmapRef<float, 4> &output, const BitmapConstRef<float, 4> &sdf, Range sdfPxRange = 0, float sdThreshold = .5f);
/// Snaps the values of the floating-point bitmaps into one of the 256 values representable in a standard 8-bit bitmap. /// Snaps the values of the floating-point bitmaps into one of the 256 values representable in a standard 8-bit bitmap.
void simulate8bit(const BitmapRef<float, 1> &bitmap); void simulate8bit(const BitmapRef<float, 1> &bitmap);

View File

@ -8,10 +8,12 @@
#ifdef MSDFGEN_USE_CPP11 #ifdef MSDFGEN_USE_CPP11
#include <cstdint> #include <cstdint>
#else #else
namespace msdfgen {
typedef int int32_t; typedef int int32_t;
typedef unsigned uint32_t; typedef unsigned uint32_t;
typedef unsigned short uint16_t; typedef unsigned short uint16_t;
typedef unsigned char uint8_t; typedef unsigned char uint8_t;
}
#endif #endif
#include "pixel-conversion.hpp" #include "pixel-conversion.hpp"

39
thirdparty/msdfgen/core/save-fl32.cpp vendored Normal file
View File

@ -0,0 +1,39 @@
#include "save-fl32.h"
#include <cstdio>
namespace msdfgen {
// Requires byte reversal for floats on big-endian platform
#ifndef __BIG_ENDIAN__
template <int N>
bool saveFl32(const BitmapConstRef<float, N> &bitmap, const char *filename) {
if (FILE *f = fopen(filename, "wb")) {
byte header[16] = { byte('F'), byte('L'), byte('3'), byte('2') };
header[4] = byte(bitmap.height);
header[5] = byte(bitmap.height>>8);
header[6] = byte(bitmap.height>>16);
header[7] = byte(bitmap.height>>24);
header[8] = byte(bitmap.width);
header[9] = byte(bitmap.width>>8);
header[10] = byte(bitmap.width>>16);
header[11] = byte(bitmap.width>>24);
header[12] = byte(N);
fwrite(header, 1, 16, f);
fwrite(bitmap.pixels, sizeof(float), N*bitmap.width*bitmap.height, f);
fclose(f);
return true;
}
return false;
}
template bool saveFl32(const BitmapConstRef<float, 1> &bitmap, const char *filename);
template bool saveFl32(const BitmapConstRef<float, 2> &bitmap, const char *filename);
template bool saveFl32(const BitmapConstRef<float, 3> &bitmap, const char *filename);
template bool saveFl32(const BitmapConstRef<float, 4> &bitmap, const char *filename);
#endif
}

12
thirdparty/msdfgen/core/save-fl32.h vendored Normal file
View File

@ -0,0 +1,12 @@
#pragma once
#include "BitmapRef.hpp"
namespace msdfgen {
/// Saves the bitmap as an uncompressed floating-point FL32 file, which can be decoded trivially.
template <int N>
bool saveFl32(const BitmapConstRef<float, N> &bitmap, const char *filename);
}

133
thirdparty/msdfgen/core/save-rgba.cpp vendored Normal file
View File

@ -0,0 +1,133 @@
#include "save-rgba.h"
#include <cstdio>
#include "pixel-conversion.hpp"
namespace msdfgen {
class RgbaFileOutput {
FILE *file;
public:
RgbaFileOutput(const char *filename, unsigned width, unsigned height) {
if ((file = fopen(filename, "wb"))) {
byte header[12] = { byte('R'), byte('G'), byte('B'), byte('A') };
header[4] = byte(width>>24);
header[5] = byte(width>>16);
header[6] = byte(width>>8);
header[7] = byte(width);
header[8] = byte(height>>24);
header[9] = byte(height>>16);
header[10] = byte(height>>8);
header[11] = byte(height);
fwrite(header, 1, 12, file);
}
}
~RgbaFileOutput() {
if (file)
fclose(file);
}
void writePixel(const byte rgba[4]) {
fwrite(rgba, 1, 4, file);
}
operator FILE *() {
return file;
}
};
bool saveRgba(const BitmapConstRef<byte, 1> &bitmap, const char *filename) {
RgbaFileOutput output(filename, bitmap.width, bitmap.height);
if (output) {
byte rgba[4] = { byte(0), byte(0), byte(0), byte(0xff) };
for (int y = bitmap.height; y--;) {
for (const byte *p = bitmap(0, y), *end = p+bitmap.width; p < end; ++p) {
rgba[0] = rgba[1] = rgba[2] = *p;
output.writePixel(rgba);
}
}
return true;
}
return false;
}
bool saveRgba(const BitmapConstRef<byte, 3> &bitmap, const char *filename) {
RgbaFileOutput output(filename, bitmap.width, bitmap.height);
if (output) {
byte rgba[4] = { byte(0), byte(0), byte(0), byte(0xff) };
for (int y = bitmap.height; y--;) {
for (const byte *p = bitmap(0, y), *end = p+3*bitmap.width; p < end; p += 3) {
rgba[0] = p[0], rgba[1] = p[1], rgba[2] = p[2];
output.writePixel(rgba);
}
}
return true;
}
return false;
}
bool saveRgba(const BitmapConstRef<byte, 4> &bitmap, const char *filename) {
RgbaFileOutput output(filename, bitmap.width, bitmap.height);
if (output) {
for (int y = bitmap.height; y--;)
fwrite(bitmap(0, y), 1, 4*bitmap.width, output);
return true;
}
return false;
}
bool saveRgba(const BitmapConstRef<float, 1> &bitmap, const char *filename) {
RgbaFileOutput output(filename, bitmap.width, bitmap.height);
if (output) {
byte rgba[4] = { byte(0), byte(0), byte(0), byte(0xff) };
for (int y = bitmap.height; y--;) {
for (const float *p = bitmap(0, y), *end = p+bitmap.width; p < end; ++p) {
rgba[0] = rgba[1] = rgba[2] = pixelFloatToByte(*p);
output.writePixel(rgba);
}
}
return true;
}
return false;
}
bool saveRgba(const BitmapConstRef<float, 3> &bitmap, const char *filename) {
RgbaFileOutput output(filename, bitmap.width, bitmap.height);
if (output) {
byte rgba[4] = { byte(0), byte(0), byte(0), byte(0xff) };
for (int y = bitmap.height; y--;) {
for (const float *p = bitmap(0, y), *end = p+3*bitmap.width; p < end; p += 3) {
rgba[0] = pixelFloatToByte(p[0]);
rgba[1] = pixelFloatToByte(p[1]);
rgba[2] = pixelFloatToByte(p[2]);
output.writePixel(rgba);
}
}
return true;
}
return false;
}
bool saveRgba(const BitmapConstRef<float, 4> &bitmap, const char *filename) {
RgbaFileOutput output(filename, bitmap.width, bitmap.height);
if (output) {
byte rgba[4];
for (int y = bitmap.height; y--;) {
for (const float *p = bitmap(0, y), *end = p+4*bitmap.width; p < end; p += 4) {
rgba[0] = pixelFloatToByte(p[0]);
rgba[1] = pixelFloatToByte(p[1]);
rgba[2] = pixelFloatToByte(p[2]);
rgba[3] = pixelFloatToByte(p[3]);
output.writePixel(rgba);
}
}
return true;
}
return false;
}
}

16
thirdparty/msdfgen/core/save-rgba.h vendored Normal file
View File

@ -0,0 +1,16 @@
#pragma once
#include "BitmapRef.hpp"
namespace msdfgen {
/// Saves the bitmap as a simple RGBA file, which can be decoded trivially.
bool saveRgba(const BitmapConstRef<byte, 1> &bitmap, const char *filename);
bool saveRgba(const BitmapConstRef<byte, 3> &bitmap, const char *filename);
bool saveRgba(const BitmapConstRef<byte, 4> &bitmap, const char *filename);
bool saveRgba(const BitmapConstRef<float, 1> &bitmap, const char *filename);
bool saveRgba(const BitmapConstRef<float, 3> &bitmap, const char *filename);
bool saveRgba(const BitmapConstRef<float, 4> &bitmap, const char *filename);
}

View File

@ -8,10 +8,12 @@
#ifdef MSDFGEN_USE_CPP11 #ifdef MSDFGEN_USE_CPP11
#include <cstdint> #include <cstdint>
#else #else
namespace msdfgen {
typedef int int32_t; typedef int int32_t;
typedef unsigned uint32_t; typedef unsigned uint32_t;
typedef unsigned short uint16_t; typedef unsigned short uint16_t;
typedef unsigned char uint8_t; typedef unsigned char uint8_t;
}
#endif #endif
namespace msdfgen { namespace msdfgen {

View File

@ -2,6 +2,8 @@
#define _CRT_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_WARNINGS
#include "shape-description.h" #include "shape-description.h"
#include <cstdlib>
namespace msdfgen { namespace msdfgen {
int readCharF(FILE *input) { int readCharF(FILE *input) {
@ -25,14 +27,25 @@ int readCharS(const char **input) {
} }
int readCoordF(FILE *input, Point2 &coord) { int readCoordF(FILE *input, Point2 &coord) {
return fscanf(input, "%lf,%lf", &coord.x, &coord.y); return fscanf(input, "%lf , %lf", &coord.x, &coord.y);
} }
int readCoordS(const char **input, Point2 &coord) { int readCoordS(const char **input, Point2 &coord) {
int read = 0; char *end = NULL;
int result = sscanf(*input, "%lf,%lf%n", &coord.x, &coord.y, &read); coord.x = strtod(*input, &end);
*input += read; if (end <= *input)
return result; return 0;
*input = end;
while (**input == ' ' || **input == '\t' || **input == '\n' || **input == '\r')
++*input;
if (**input != ',')
return 1;
++*input;
coord.y = strtod(*input, &end);
if (end <= *input)
return 1;
*input = end;
return 2;
} }
static bool writeCoord(FILE *output, Point2 coord) { static bool writeCoord(FILE *output, Point2 coord) {

View File

@ -4,7 +4,7 @@
/* /*
* MULTI-CHANNEL SIGNED DISTANCE FIELD GENERATOR * MULTI-CHANNEL SIGNED DISTANCE FIELD GENERATOR
* --------------------------------------------- * ---------------------------------------------
* A utility by Viktor Chlumsky, (c) 2014 - 2023 * A utility by Viktor Chlumsky, (c) 2014 - 2024
* *
* The technique used to generate multi-channel distance fields in this code * 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, * has been developed by Viktor Chlumsky in 2014 for his master's thesis,
@ -18,7 +18,10 @@
#include "core/base.h" #include "core/base.h"
#include "core/arithmetics.hpp" #include "core/arithmetics.hpp"
#include "core/Vector2.hpp" #include "core/Vector2.hpp"
#include "core/Range.hpp"
#include "core/Projection.h" #include "core/Projection.h"
#include "core/DistanceMapping.h"
#include "core/SDFTransformation.h"
#include "core/Scanline.h" #include "core/Scanline.h"
#include "core/Shape.h" #include "core/Shape.h"
#include "core/BitmapRef.hpp" #include "core/BitmapRef.hpp"
@ -33,32 +36,43 @@
#include "core/sdf-error-estimation.h" #include "core/sdf-error-estimation.h"
#include "core/save-bmp.h" #include "core/save-bmp.h"
#include "core/save-tiff.h" #include "core/save-tiff.h"
#include "core/save-rgba.h"
#include "core/save-fl32.h"
#include "core/shape-description.h" #include "core/shape-description.h"
#include "core/export-svg.h"
namespace msdfgen { namespace msdfgen {
/// Generates a conventional single-channel signed distance field. /// Generates a conventional single-channel signed distance field.
void generateSDF(const BitmapRef<float, 1> &output, const Shape &shape, const Projection &projection, double range, const GeneratorConfig &config = GeneratorConfig()); void generateSDF(const BitmapRef<float, 1> &output, const Shape &shape, const SDFTransformation &transformation, const GeneratorConfig &config = GeneratorConfig());
/// Generates a single-channel signed pseudo-distance field. /// Generates a single-channel signed perpendicular distance field.
void generatePseudoSDF(const BitmapRef<float, 1> &output, const Shape &shape, const Projection &projection, double range, const GeneratorConfig &config = GeneratorConfig()); void generatePSDF(const BitmapRef<float, 1> &output, const Shape &shape, const SDFTransformation &transformation, const GeneratorConfig &config = GeneratorConfig());
/// Generates a multi-channel signed distance field. Edge colors must be assigned first! (See edgeColoringSimple) /// Generates a multi-channel signed distance field. Edge colors must be assigned first! (See edgeColoringSimple)
void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, const Projection &projection, double range, const MSDFGeneratorConfig &config = MSDFGeneratorConfig()); void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, const SDFTransformation &transformation, const MSDFGeneratorConfig &config = MSDFGeneratorConfig());
/// Generates a multi-channel signed distance field with true distance in the alpha channel. Edge colors must be assigned first. /// Generates a multi-channel signed distance field with true distance in the alpha channel. Edge colors must be assigned first.
void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, const Projection &projection, double range, const MSDFGeneratorConfig &config = MSDFGeneratorConfig()); void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, const SDFTransformation &transformation, const MSDFGeneratorConfig &config = MSDFGeneratorConfig());
// Old version of the function API's kept for backwards compatibility // Old version of the function API's kept for backwards compatibility
void generateSDF(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport = true); void generateSDF(const BitmapRef<float, 1> &output, const Shape &shape, const Projection &projection, Range range, const GeneratorConfig &config = GeneratorConfig());
void generatePseudoSDF(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport = true); void generatePSDF(const BitmapRef<float, 1> &output, const Shape &shape, const Projection &projection, Range range, const GeneratorConfig &config = GeneratorConfig());
void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, const ErrorCorrectionConfig &errorCorrectionConfig = ErrorCorrectionConfig(), bool overlapSupport = true); void generatePseudoSDF(const BitmapRef<float, 1> &output, const Shape &shape, const Projection &projection, Range range, const GeneratorConfig &config = GeneratorConfig());
void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, const ErrorCorrectionConfig &errorCorrectionConfig = ErrorCorrectionConfig(), bool overlapSupport = true); void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, const Projection &projection, Range range, const MSDFGeneratorConfig &config = MSDFGeneratorConfig());
void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, const Projection &projection, Range range, const MSDFGeneratorConfig &config = MSDFGeneratorConfig());
void generateSDF(const BitmapRef<float, 1> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport = true);
void generatePSDF(const BitmapRef<float, 1> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport = true);
void generatePseudoSDF(const BitmapRef<float, 1> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport = true);
void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate, const ErrorCorrectionConfig &errorCorrectionConfig = ErrorCorrectionConfig(), bool overlapSupport = true);
void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate, const ErrorCorrectionConfig &errorCorrectionConfig = ErrorCorrectionConfig(), bool overlapSupport = true);
// Original simpler versions of the previous functions, which work well under normal circumstances, but cannot deal with overlapping contours. // Original simpler versions of the previous functions, which work well under normal circumstances, but cannot deal with overlapping contours.
void generateSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate); void generateSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate);
void generatePseudoSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate); void generatePSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate);
void generateMSDF_legacy(const BitmapRef<float, 3> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, ErrorCorrectionConfig errorCorrectionConfig = ErrorCorrectionConfig()); void generatePseudoSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate);
void generateMTSDF_legacy(const BitmapRef<float, 4> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, ErrorCorrectionConfig errorCorrectionConfig = ErrorCorrectionConfig()); void generateMSDF_legacy(const BitmapRef<float, 3> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate, ErrorCorrectionConfig errorCorrectionConfig = ErrorCorrectionConfig());
void generateMTSDF_legacy(const BitmapRef<float, 4> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate, ErrorCorrectionConfig errorCorrectionConfig = ErrorCorrectionConfig());
} }