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/
Comment: Multi-channel signed distance field generator
Copyright: 2016-2022, Viktor Chlumsky
Copyright: 2014-2024, Viktor Chlumsky
License: Expat
Files: ./thirdparty/nvapi/nvapi_minimal.h

View File

@ -16,6 +16,7 @@ if env["builtin_msdfgen"]:
thirdparty_dir = "#thirdparty/msdfgen/"
thirdparty_sources = [
"core/Contour.cpp",
"core/DistanceMapping.cpp",
"core/EdgeHolder.cpp",
"core/MSDFErrorCorrection.cpp",
"core/Projection.cpp",
@ -26,10 +27,15 @@ if env["builtin_msdfgen"]:
"core/edge-segments.cpp",
"core/edge-selectors.cpp",
"core/equation-solver.cpp",
# "core/export-svg.cpp",
"core/msdf-error-correction.cpp",
"core/msdfgen.cpp",
"core/rasterization.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/shape-description.cpp",
]

View File

@ -123,6 +123,7 @@ if env["msdfgen_enabled"] and env["freetype_enabled"]:
thirdparty_msdfgen_dir = "../../../thirdparty/msdfgen/"
thirdparty_msdfgen_sources = [
"core/Contour.cpp",
"core/DistanceMapping.cpp",
"core/EdgeHolder.cpp",
"core/MSDFErrorCorrection.cpp",
"core/Projection.cpp",
@ -133,10 +134,15 @@ if env["msdfgen_enabled"] and env["freetype_enabled"]:
"core/edge-segments.cpp",
"core/edge-selectors.cpp",
"core/equation-solver.cpp",
# "core/export-svg.cpp",
"core/msdf-error-correction.cpp",
"core/msdfgen.cpp",
"core/rasterization.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/shape-description.cpp",
]

View File

@ -69,6 +69,7 @@ using namespace godot;
#ifdef _MSC_VER
#pragma warning(disable : 4458)
#endif
#include <core/EdgeHolder.h>
#include <core/ShapeDistanceFinder.h>
#include <core/contour-combiners.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_sources = [
"core/Contour.cpp",
"core/DistanceMapping.cpp",
"core/EdgeHolder.cpp",
"core/MSDFErrorCorrection.cpp",
"core/Projection.cpp",
@ -128,10 +129,15 @@ if env["msdfgen_enabled"] and env["freetype_enabled"]:
"core/edge-segments.cpp",
"core/edge-selectors.cpp",
"core/equation-solver.cpp",
# "core/export-svg.cpp",
"core/msdf-error-correction.cpp",
"core/msdfgen.cpp",
"core/rasterization.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/shape-description.cpp",
]

View File

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

View File

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

View File

@ -1,6 +1,6 @@
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
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;
}
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) { }
#ifdef MSDFGEN_USE_CPP11

View File

@ -12,11 +12,11 @@ public:
/// Swaps the edges held by a and b.
static void swap(EdgeHolder &a, EdgeHolder &b);
EdgeHolder();
EdgeHolder(EdgeSegment *segment);
EdgeHolder(Point2 p0, Point2 p1, EdgeColor edgeColor = WHITE);
EdgeHolder(Point2 p0, Point2 p1, Point2 p2, EdgeColor edgeColor = WHITE);
EdgeHolder(Point2 p0, Point2 p1, Point2 p2, Point2 p3, EdgeColor edgeColor = WHITE);
inline EdgeHolder() : edgeSegment() { }
inline EdgeHolder(EdgeSegment *segment) : edgeSegment(segment) { }
inline EdgeHolder(Point2 p0, Point2 p1, EdgeColor edgeColor = WHITE) : edgeSegment(EdgeSegment::create(p0, p1, edgeColor)) { }
inline EdgeHolder(Point2 p0, Point2 p1, Point2 p2, EdgeColor edgeColor = WHITE) : edgeSegment(EdgeSegment::create(p0, p1, p2, edgeColor)) { }
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);
#ifdef MSDFGEN_USE_CPP11
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.
float oldPSD = median(oldMSD[0], oldMSD[1], oldMSD[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.
return parent->minImproveRatio*fabsf(newPSD-refPSD) < double(fabsf(oldPSD-refPSD));
}
@ -87,24 +87,23 @@ public:
Point2 shapeCoord, sdfCoord;
const float *msd;
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));
}
inline ArtifactClassifier classifier(const Vector2 &direction, double span) {
return ArtifactClassifier(this, direction, span);
}
private:
ShapeDistanceFinder<ContourCombiner<PseudoDistanceSelector> > distanceFinder;
ShapeDistanceFinder<ContourCombiner<PerpendicularDistanceSelector> > distanceFinder;
BitmapConstRef<float, N> sdf;
double invRange;
DistanceMapping distanceMapping;
Vector2 texelSize;
double minImproveRatio;
};
MSDFErrorCorrection::MSDFErrorCorrection() { }
MSDFErrorCorrection::MSDFErrorCorrection(const BitmapRef<byte, 1> &stencil, const Projection &projection, double range) : stencil(stencil), projection(projection) {
invRange = 1/range;
MSDFErrorCorrection::MSDFErrorCorrection(const BitmapRef<byte, 1> &stencil, const SDFTransformation &transformation) : stencil(stencil), transformation(transformation) {
minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio;
minImproveRatio = ErrorCorrectionConfig::defaultMinImproveRatio;
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 (!(commonColor&(commonColor-1))) {
// 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)
p.y = stencil.height-p.y;
int l = (int) floor(p.x-.5);
@ -191,7 +190,7 @@ template <int N>
void MSDFErrorCorrection::protectEdges(const BitmapConstRef<float, N> &sdf) {
float radius;
// 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) {
const float *left = sdf(0, y);
const float *right = sdf(1, y);
@ -207,7 +206,7 @@ void MSDFErrorCorrection::protectEdges(const BitmapConstRef<float, N> &sdf) {
}
}
// 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) {
const float *bottom = sdf(0, y);
const float *top = sdf(0, y+1);
@ -223,7 +222,7 @@ void MSDFErrorCorrection::protectEdges(const BitmapConstRef<float, N> &sdf) {
}
}
// 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) {
const float *lb = sdf(0, y);
const float *rb = sdf(1, y);
@ -391,9 +390,9 @@ static bool hasDiagonalArtifact(const ArtifactClassifier &artifactClassifier, fl
template <int N>
void MSDFErrorCorrection::findErrors(const BitmapConstRef<float, N> &sdf) {
// Compute the expected deltas between values of horizontally, vertically, and diagonally adjacent texels.
double hSpan = minDeviationRatio*projection.unprojectVector(Vector2(invRange, 0)).length();
double vSpan = minDeviationRatio*projection.unprojectVector(Vector2(0, invRange)).length();
double dSpan = minDeviationRatio*projection.unprojectVector(Vector2(invRange)).length();
double hSpan = minDeviationRatio*transformation.unprojectVector(Vector2(transformation.distanceMapping(DistanceMapping::Delta(1)), 0)).length();
double vSpan = minDeviationRatio*transformation.unprojectVector(Vector2(0, transformation.distanceMapping(DistanceMapping::Delta(1)))).length();
double dSpan = minDeviationRatio*transformation.unprojectVector(Vector2(transformation.distanceMapping(DistanceMapping::Delta(1)))).length();
// Inspect all texels.
for (int y = 0; y < sdf.height; ++y) {
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>
void MSDFErrorCorrection::findErrors(const BitmapConstRef<float, N> &sdf, const Shape &shape) {
// Compute the expected deltas between values of horizontally, vertically, and diagonally adjacent texels.
double hSpan = minDeviationRatio*projection.unprojectVector(Vector2(invRange, 0)).length();
double vSpan = minDeviationRatio*projection.unprojectVector(Vector2(0, invRange)).length();
double dSpan = minDeviationRatio*projection.unprojectVector(Vector2(invRange)).length();
double hSpan = minDeviationRatio*transformation.unprojectVector(Vector2(transformation.distanceMapping(DistanceMapping::Delta(1)), 0)).length();
double vSpan = minDeviationRatio*transformation.unprojectVector(Vector2(0, transformation.distanceMapping(DistanceMapping::Delta(1)))).length();
double dSpan = minDeviationRatio*transformation.unprojectVector(Vector2(transformation.distanceMapping(DistanceMapping::Delta(1)))).length();
#ifdef MSDFGEN_USE_OPENMP
#pragma omp parallel
#endif
{
ShapeDistanceChecker<ContourCombiner, N> shapeDistanceChecker(sdf, shape, projection, invRange, minImproveRatio);
ShapeDistanceChecker<ContourCombiner, N> shapeDistanceChecker(sdf, shape, transformation, transformation.distanceMapping, minImproveRatio);
bool rightToLeft = false;
// Inspect all texels.
#ifdef MSDFGEN_USE_OPENMP
@ -439,7 +438,7 @@ void MSDFErrorCorrection::findErrors(const BitmapConstRef<float, N> &sdf, const
if ((*stencil(x, row)&ERROR))
continue;
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.msd = c;
shapeDistanceChecker.protectedFlag = (*stencil(x, row)&PROTECTED) != 0;

View File

@ -1,7 +1,7 @@
#pragma once
#include "Projection.h"
#include "SDFTransformation.h"
#include "Shape.h"
#include "BitmapRef.hpp"
@ -20,7 +20,7 @@ public:
};
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.
void setMinDeviationRatio(double minDeviationRatio);
/// Sets the minimum ratio between the pre-correction distance error and the post-correction distance error.
@ -46,8 +46,7 @@ public:
private:
BitmapRef<byte, 1> stencil;
Projection projection;
double invRange;
SDFTransformation transformation;
double minDeviationRatio;
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 "arithmetics.hpp"
#define DECONVERGE_OVERSHOOT 1.11111111111111111 // moves control points slightly more than necessary to account for floating-point errors
namespace msdfgen {
Shape::Shape() : inverseYAxis(false) { }
@ -39,13 +41,23 @@ bool Shape::validate() const {
return true;
}
static void deconvergeEdge(EdgeHolder &edgeHolder, int param) {
static void deconvergeEdge(EdgeHolder &edgeHolder, int param, Vector2 vector) {
switch (edgeHolder->type()) {
case (int) QuadraticSegment::EDGE_TYPE:
edgeHolder = static_cast<const QuadraticSegment *>(&*edgeHolder)->convertToCubic();
// fallthrough
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[2]));
} else {
// Push apart convergent edge segments
EdgeHolder *prevEdge = &contour->edges.back();
for (std::vector<EdgeHolder>::iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) {
Vector2 prevDir = (*prevEdge)->direction(1).normalize();
Vector2 curDir = (*edge)->direction(0).normalize();
if (dotProduct(prevDir, curDir) < MSDFGEN_CORNER_DOT_EPSILON-1) {
deconvergeEdge(*prevEdge, 1);
deconvergeEdge(*edge, 0);
double factor = DECONVERGE_OVERSHOOT*sqrt(1-(MSDFGEN_CORNER_DOT_EPSILON-1)*(MSDFGEN_CORNER_DOT_EPSILON-1))/(MSDFGEN_CORNER_DOT_EPSILON-1);
Vector2 axis = factor*(curDir-prevDir).normalize();
// Determine curve ordering using third-order derivative (t = 0) of crossProduct((*prevEdge)->point(1-t)-p0, (*edge)->point(t)-p0) where p0 is the corner (*edge)->point(0)
if (crossProduct((*prevEdge)->directionChange(1), (*edge)->direction(0))+crossProduct((*edge)->directionChange(0), (*prevEdge)->direction(1)) < 0)
axis = -axis;
deconvergeEdge(*prevEdge, 1, axis.getOrthogonal(true));
deconvergeEdge(*edge, 0, axis.getOrthogonal(false));
}
prevEdge = &*edge;
}

View File

@ -9,8 +9,6 @@ namespace msdfgen {
// Threshold of the dot product of adjacent edge directions to be considered convergent.
#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.
class Shape {

View File

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

View File

@ -16,6 +16,13 @@ static void initDistance(MultiDistance &distance) {
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) {
return distance;
}
@ -43,7 +50,7 @@ typename SimpleContourCombiner<EdgeSelector>::DistanceType SimpleContourCombiner
}
template class SimpleContourCombiner<TrueDistanceSelector>;
template class SimpleContourCombiner<PseudoDistanceSelector>;
template class SimpleContourCombiner<PerpendicularDistanceSelector>;
template class SimpleContourCombiner<MultiDistanceSelector>;
template class SimpleContourCombiner<MultiAndTrueDistanceSelector>;
@ -127,7 +134,7 @@ typename OverlappingContourCombiner<EdgeSelector>::DistanceType OverlappingConto
}
template class OverlappingContourCombiner<TrueDistanceSelector>;
template class OverlappingContourCombiner<PseudoDistanceSelector>;
template class OverlappingContourCombiner<PerpendicularDistanceSelector>;
template class OverlappingContourCombiner<MultiDistanceSelector>;
template class OverlappingContourCombiner<MultiAndTrueDistanceSelector>;

View File

@ -11,6 +11,15 @@
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) {
return dotProduct(aDir, bDir) <= 0 || fabs(crossProduct(aDir, bDir)) > crossThreshold;
}
@ -26,30 +35,45 @@ static double estimateEdgeLength(const EdgeSegment *edge) {
return len;
}
static void switchColor(EdgeColor &color, unsigned long long &seed, EdgeColor banned = BLACK) {
EdgeColor combined = EdgeColor(color&banned);
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);
static int seedExtract2(unsigned long long &seed) {
int v = int(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) {
double crossThreshold = sin(angleThreshold);
EdgeColor color = initColor(seed);
std::vector<int> corners;
for (std::vector<Contour>::iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour) {
// Identify corners
corners.clear();
if (!contour->edges.empty()) {
if (contour->edges.empty())
continue;
{ // Identify corners
corners.clear();
Vector2 prevDirection = contour->edges.back()->direction(1);
int index = 0;
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
if (corners.empty())
if (corners.empty()) {
switchColor(color, seed);
for (std::vector<EdgeHolder>::iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge)
(*edge)->color = WHITE;
(*edge)->color = color;
}
// "Teardrop" case
else if (corners.size() == 1) {
EdgeColor colors[3] = { WHITE, WHITE };
switchColor(colors[0], seed);
switchColor(colors[2] = colors[0], seed);
EdgeColor colors[3];
switchColor(color, seed);
colors[0] = color;
colors[1] = WHITE;
switchColor(color, seed);
colors[2] = color;
int corner = corners[0];
if (contour->edges.size() >= 3) {
int m = (int) contour->edges.size();
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) {
// Less than three edge segments for three colors => edges must be split
EdgeSegment *parts[7] = { };
@ -98,7 +127,6 @@ void edgeColoringSimple(Shape &shape, double angleThreshold, unsigned long long
int spline = 0;
int start = corners[0];
int m = (int) contour->edges.size();
EdgeColor color = WHITE;
switchColor(color, seed);
EdgeColor initialColor = color;
for (int i = 0; i < m; ++i) {
@ -123,12 +151,14 @@ struct EdgeColoringInkTrapCorner {
void edgeColoringInkTrap(Shape &shape, double angleThreshold, unsigned long long seed) {
typedef EdgeColoringInkTrapCorner Corner;
double crossThreshold = sin(angleThreshold);
EdgeColor color = initColor(seed);
std::vector<Corner> corners;
for (std::vector<Contour>::iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour) {
// Identify corners
if (contour->edges.empty())
continue;
double splineLength = 0;
corners.clear();
if (!contour->edges.empty()) {
{ // Identify corners
corners.clear();
Vector2 prevDirection = contour->edges.back()->direction(1);
int index = 0;
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
if (corners.empty())
if (corners.empty()) {
switchColor(color, seed);
for (std::vector<EdgeHolder>::iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge)
(*edge)->color = WHITE;
(*edge)->color = color;
}
// "Teardrop" case
else if (corners.size() == 1) {
EdgeColor colors[3] = { WHITE, WHITE };
switchColor(colors[0], seed);
switchColor(colors[2] = colors[0], seed);
EdgeColor colors[3];
switchColor(color, seed);
colors[0] = color;
colors[1] = WHITE;
switchColor(color, seed);
colors[2] = color;
int corner = corners[0].index;
if (contour->edges.size() >= 3) {
int m = (int) contour->edges.size();
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) {
// Less than three edge segments for three colors => edges must be split
EdgeSegment *parts[7] = { };
@ -191,7 +226,6 @@ void edgeColoringInkTrap(Shape &shape, double angleThreshold, unsigned long long
}
}
}
EdgeColor color = WHITE;
EdgeColor initialColor = BLACK;
for (int i = 0; i < cornerCount; ++i) {
if (!corners[i].minor) {
@ -271,23 +305,19 @@ static void colorSecondDegreeGraph(int *coloring, const int *const *edgeMatrix,
color = 1;
break;
case 3:
color = (int) seed&1;
seed >>= 1;
color = seedExtract2(seed); // 0 or 1
break;
case 4:
color = 2;
break;
case 5:
color = ((int) seed+1&1)<<1;
seed >>= 1;
color = (int) !seedExtract2(seed)<<1; // 2 or 0
break;
case 6:
color = ((int) seed&1)+1;
seed >>= 1;
color = seedExtract2(seed)+1; // 1 or 2
break;
case 7:
color = int((seed+i)%3);
seed /= 3;
color = (seedExtract3(seed)+i)%3; // 0 or 1 or 2
break;
}
coloring[i] = color;
@ -394,7 +424,7 @@ void edgeColoringByDistance(Shape &shape, double angleThreshold, unsigned long l
for (int i = 0; i < m; ++i) {
if (i == m/2)
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]);
else
contour->edges[(corner+i)%m]->color = WHITE;

View File

@ -6,15 +6,34 @@
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) {
Vector2 dir = direction(0).normalize();
Vector2 aq = origin-point(0);
double ts = dotProduct(aq, dir);
if (ts < 0) {
double pseudoDistance = crossProduct(aq, dir);
if (fabs(pseudoDistance) <= fabs(distance.distance)) {
distance.distance = pseudoDistance;
double perpendicularDistance = crossProduct(aq, dir);
if (fabs(perpendicularDistance) <= fabs(distance.distance)) {
distance.distance = perpendicularDistance;
distance.dot = 0;
}
}
@ -23,9 +42,9 @@ void EdgeSegment::distanceToPseudoDistance(SignedDistance &distance, Point2 orig
Vector2 bq = origin-point(1);
double ts = dotProduct(bq, dir);
if (ts > 0) {
double pseudoDistance = crossProduct(bq, dir);
if (fabs(pseudoDistance) <= fabs(distance.distance)) {
distance.distance = pseudoDistance;
double perpendicularDistance = crossProduct(bq, dir);
if (fabs(perpendicularDistance) <= fabs(distance.distance)) {
distance.distance = perpendicularDistance;
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) {
if (p1 == p0 || p1 == p2)
p1 = 0.5*(p0+p2);
p[0] = p0;
p[1] = p1;
p[2] = p2;
}
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[1] = p1;
p[2] = p2;
@ -486,43 +499,29 @@ void CubicSegment::moveEndPoint(Point2 to) {
p[3] = to;
}
void LinearSegment::splitInThirds(EdgeSegment *&part1, EdgeSegment *&part2, EdgeSegment *&part3) const {
part1 = new LinearSegment(p[0], point(1/3.), color);
part2 = new LinearSegment(point(1/3.), point(2/3.), color);
part3 = new LinearSegment(point(2/3.), p[1], color);
void LinearSegment::splitInThirds(EdgeSegment *&part0, EdgeSegment *&part1, EdgeSegment *&part2) const {
part0 = new LinearSegment(p[0], point(1/3.), color);
part1 = new LinearSegment(point(1/3.), point(2/3.), color);
part2 = new LinearSegment(point(2/3.), p[1], color);
}
void QuadraticSegment::splitInThirds(EdgeSegment *&part1, EdgeSegment *&part2, EdgeSegment *&part3) const {
part1 = 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);
part3 = new QuadraticSegment(point(2/3.), mix(p[1], p[2], 2/3.), p[2], color);
void QuadraticSegment::splitInThirds(EdgeSegment *&part0, EdgeSegment *&part1, EdgeSegment *&part2) const {
part0 = new QuadraticSegment(p[0], mix(p[0], p[1], 1/3.), point(1/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);
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 {
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);
part2 = new CubicSegment(point(1/3.),
void CubicSegment::splitInThirds(EdgeSegment *&part0, EdgeSegment *&part1, EdgeSegment *&part2) const {
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);
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], 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);
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 {
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:
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) { }
virtual ~EdgeSegment() { }
/// Creates a copy of the edge segment.
@ -33,8 +37,8 @@ public:
virtual Vector2 directionChange(double param) const = 0;
/// Returns the minimum signed distance between origin and the edge.
virtual SignedDistance signedDistance(Point2 origin, double &param) const = 0;
/// Converts a previously retrieved signed distance from origin to pseudo-distance.
virtual void distanceToPseudoDistance(SignedDistance &distance, Point2 origin, double param) const;
/// Converts a previously retrieved signed distance from origin to perpendicular distance.
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.
virtual int scanlineIntersections(double x[3], int dy[3], double y) const = 0;
/// Adjusts the bounding box to fit the edge segment.
@ -47,7 +51,7 @@ public:
/// Moves the end point of the edge segment.
virtual void moveEndPoint(Point2 to) = 0;
/// 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 moveStartPoint(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 moveStartPoint(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;
@ -135,9 +139,7 @@ public:
void reverse();
void moveStartPoint(Point2 to);
void moveEndPoint(Point2 to);
void splitInThirds(EdgeSegment *&part1, EdgeSegment *&part2, EdgeSegment *&part3) const;
void deconverge(int param, double amount);
void splitInThirds(EdgeSegment *&part0, EdgeSegment *&part1, EdgeSegment *&part2) const;
};

View File

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

View File

@ -38,40 +38,40 @@ private:
};
class PseudoDistanceSelectorBase {
class PerpendicularDistanceSelectorBase {
public:
struct EdgeCache {
Point2 point;
double absDistance;
double aDomainDistance, bDomainDistance;
double aPseudoDistance, bPseudoDistance;
double aPerpendicularDistance, bPerpendicularDistance;
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);
bool isEdgeRelevant(const EdgeCache &cache, const EdgeSegment *edge, const Point2 &p) const;
void addEdgeTrueDistance(const EdgeSegment *edge, const SignedDistance &distance, double param);
void addEdgePseudoDistance(double distance);
void merge(const PseudoDistanceSelectorBase &other);
void addEdgePerpendicularDistance(double distance);
void merge(const PerpendicularDistanceSelectorBase &other);
double computeDistance(const Point2 &p) const;
SignedDistance trueDistance() const;
private:
SignedDistance minTrueDistance;
double minNegativePseudoDistance;
double minPositivePseudoDistance;
double minNegativePerpendicularDistance;
double minPositivePerpendicularDistance;
const EdgeSegment *nearEdge;
double nearEdgeParam;
};
/// Selects the nearest edge by its pseudo-distance.
class PseudoDistanceSelector : public PseudoDistanceSelectorBase {
/// Selects the nearest edge by its perpendicular distance.
class PerpendicularDistanceSelector : public PerpendicularDistanceSelectorBase {
public:
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 {
public:
typedef MultiDistance DistanceType;
typedef PseudoDistanceSelectorBase::EdgeCache EdgeCache;
typedef PerpendicularDistanceSelectorBase::EdgeCache EdgeCache;
void reset(const Point2 &p);
void addEdge(EdgeCache &cache, const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge);
@ -100,11 +100,11 @@ public:
private:
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 {
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 {
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)
return;
Bitmap<byte, 1> stencilBuffer;
@ -19,7 +19,7 @@ static void msdfErrorCorrectionInner(const BitmapRef<float, N> &sdf, const Shape
BitmapRef<byte, 1> stencil;
stencil.pixels = config.errorCorrection.buffer ? config.errorCorrection.buffer : (byte *) stencilBuffer;
stencil.width = sdf.width, stencil.height = sdf.height;
MSDFErrorCorrection ec(stencil, projection, range);
MSDFErrorCorrection ec(stencil, transformation);
ec.setMinDeviationRatio(config.errorCorrection.minDeviationRatio);
ec.setMinImproveRatio(config.errorCorrection.minImproveRatio);
switch (config.errorCorrection.mode) {
@ -49,9 +49,9 @@ static void msdfErrorCorrectionInner(const BitmapRef<float, N> &sdf, const Shape
}
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);
MSDFErrorCorrection ec(stencilBuffer, projection, range);
MSDFErrorCorrection ec(stencilBuffer, transformation);
ec.setMinDeviationRatio(minDeviationRatio);
if (protectAll)
ec.protectAll();
@ -59,25 +59,43 @@ static void msdfErrorCorrectionShapeless(const BitmapRef<float, N> &sdf, const P
ec.apply(sdf);
}
void msdfErrorCorrection(const BitmapRef<float, 3> &sdf, const Shape &shape, const Projection &projection, double range, const MSDFGeneratorConfig &config) {
msdfErrorCorrectionInner(sdf, shape, projection, range, config);
void msdfErrorCorrection(const BitmapRef<float, 3> &sdf, const Shape &shape, const SDFTransformation &transformation, const MSDFGeneratorConfig &config) {
msdfErrorCorrectionInner(sdf, shape, transformation, config);
}
void msdfErrorCorrection(const BitmapRef<float, 4> &sdf, const Shape &shape, const Projection &projection, double range, const MSDFGeneratorConfig &config) {
msdfErrorCorrectionInner(sdf, shape, projection, range, config);
void msdfErrorCorrection(const BitmapRef<float, 4> &sdf, const Shape &shape, const SDFTransformation &transformation, const MSDFGeneratorConfig &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) {
msdfErrorCorrectionShapeless(sdf, projection, range, minDeviationRatio, false);
void msdfFastDistanceErrorCorrection(const BitmapRef<float, 3> &sdf, const SDFTransformation &transformation, double minDeviationRatio) {
msdfErrorCorrectionShapeless(sdf, transformation, minDeviationRatio, false);
}
void msdfFastDistanceErrorCorrection(const BitmapRef<float, 4> &sdf, const Projection &projection, double range, double minDeviationRatio) {
msdfErrorCorrectionShapeless(sdf, projection, range, minDeviationRatio, false);
void msdfFastDistanceErrorCorrection(const BitmapRef<float, 4> &sdf, const SDFTransformation &transformation, double minDeviationRatio) {
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) {
msdfErrorCorrectionShapeless(sdf, projection, range, minDeviationRatio, true);
void msdfFastEdgeErrorCorrection(const BitmapRef<float, 3> &sdf, const SDFTransformation &transformation, double minDeviationRatio) {
msdfErrorCorrectionShapeless(sdf, transformation, minDeviationRatio, true);
}
void msdfFastEdgeErrorCorrection(const BitmapRef<float, 4> &sdf, const Projection &projection, double range, double minDeviationRatio) {
msdfErrorCorrectionShapeless(sdf, projection, range, minDeviationRatio, true);
void msdfFastEdgeErrorCorrection(const BitmapRef<float, 4> &sdf, const SDFTransformation &transformation, double minDeviationRatio) {
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
#include "Vector2.hpp"
#include "Range.hpp"
#include "Projection.h"
#include "SDFTransformation.h"
#include "Shape.h"
#include "BitmapRef.hpp"
#include "generator-config.h"
@ -10,16 +12,22 @@
namespace msdfgen {
/// 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, 4> &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 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.
void msdfFastDistanceErrorCorrection(const BitmapRef<float, 3> &sdf, const Projection &projection, double range, 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, 3> &sdf, const SDFTransformation &transformation, 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.
void msdfFastEdgeErrorCorrection(const BitmapRef<float, 3> &sdf, const Projection &projection, double range, 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, 3> &sdf, const SDFTransformation &transformation, double minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio);
void msdfFastEdgeErrorCorrection(const BitmapRef<float, 4> &sdf, const SDFTransformation &transformation, double minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio);
void msdfFastEdgeErrorCorrection(const BitmapRef<float, 3> &sdf, const Projection &projection, Range range, double minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio);
void msdfFastEdgeErrorCorrection(const BitmapRef<float, 4> &sdf, const Projection &projection, Range range, double minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio);
/// The original version of the error correction algorithm.
void msdfErrorCorrection_legacy(const BitmapRef<float, 3> &output, const Vector2 &threshold);

View File

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

View File

@ -6,7 +6,7 @@
namespace msdfgen {
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) {

View File

@ -2,89 +2,175 @@
#include "render-sdf.h"
#include "arithmetics.hpp"
#include "DistanceMapping.h"
#include "pixel-conversion.hpp"
#include "bitmap-interpolation.hpp"
namespace msdfgen {
static float distVal(float dist, double pxRange, float midValue) {
if (!pxRange)
return (float) (dist > midValue);
return (float) clamp((dist-midValue)*pxRange+.5);
static float distVal(float dist, DistanceMapping mapping) {
return (float) clamp(mapping(dist)+.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);
pxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height);
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, pxRange, midValue);
if (sdfPxRange.lower == sdfPxRange.upper) {
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) = 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);
pxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height);
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, pxRange, midValue);
output(x, y)[0] = v;
output(x, y)[1] = v;
output(x, y)[2] = v;
if (sdfPxRange.lower == sdfPxRange.upper) {
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 = float(sd >= sdThreshold);
output(x, y)[0] = v;
output(x, y)[1] = 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);
pxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height);
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]), pxRange, midValue);
if (sdfPxRange.lower == sdfPxRange.upper) {
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) = 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);
pxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height);
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], pxRange, midValue);
output(x, y)[1] = distVal(sd[1], pxRange, midValue);
output(x, y)[2] = distVal(sd[2], pxRange, midValue);
if (sdfPxRange.lower == sdfPxRange.upper) {
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] = float(sd[0] >= sdThreshold);
output(x, y)[1] = float(sd[1] >= sdThreshold);
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);
pxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height);
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]), pxRange, midValue);
if (sdfPxRange.lower == sdfPxRange.upper) {
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) = 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);
pxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height);
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], pxRange, midValue);
output(x, y)[1] = distVal(sd[1], pxRange, midValue);
output(x, y)[2] = distVal(sd[2], pxRange, midValue);
output(x, y)[3] = distVal(sd[3], pxRange, midValue);
if (sdfPxRange.lower == sdfPxRange.upper) {
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] = float(sd[0] >= sdThreshold);
output(x, y)[1] = float(sd[1] >= sdThreshold);
output(x, y)[2] = float(sd[2] >= sdThreshold);
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) {

View File

@ -2,17 +2,18 @@
#pragma once
#include "Vector2.hpp"
#include "Range.hpp"
#include "BitmapRef.hpp"
namespace msdfgen {
/// 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, 3> &output, const BitmapConstRef<float, 1> &sdf, double pxRange = 0, float midValue = .5f);
void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 3> &sdf, double pxRange = 0, float midValue = .5f);
void renderSDF(const BitmapRef<float, 3> &output, const BitmapConstRef<float, 3> &sdf, double pxRange = 0, float midValue = .5f);
void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 4> &sdf, double pxRange = 0, float midValue = .5f);
void renderSDF(const BitmapRef<float, 4> &output, const BitmapConstRef<float, 4> &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, Range sdfPxRange = 0, float sdThreshold = .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, Range sdfPxRange = 0, float sdThreshold = .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, 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.
void simulate8bit(const BitmapRef<float, 1> &bitmap);

View File

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

View File

@ -2,6 +2,8 @@
#define _CRT_SECURE_NO_WARNINGS
#include "shape-description.h"
#include <cstdlib>
namespace msdfgen {
int readCharF(FILE *input) {
@ -25,14 +27,25 @@ int readCharS(const char **input) {
}
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 read = 0;
int result = sscanf(*input, "%lf,%lf%n", &coord.x, &coord.y, &read);
*input += read;
return result;
char *end = NULL;
coord.x = strtod(*input, &end);
if (end <= *input)
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) {

View File

@ -4,7 +4,7 @@
/*
* 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
* has been developed by Viktor Chlumsky in 2014 for his master's thesis,
@ -18,7 +18,10 @@
#include "core/base.h"
#include "core/arithmetics.hpp"
#include "core/Vector2.hpp"
#include "core/Range.hpp"
#include "core/Projection.h"
#include "core/DistanceMapping.h"
#include "core/SDFTransformation.h"
#include "core/Scanline.h"
#include "core/Shape.h"
#include "core/BitmapRef.hpp"
@ -33,32 +36,43 @@
#include "core/sdf-error-estimation.h"
#include "core/save-bmp.h"
#include "core/save-tiff.h"
#include "core/save-rgba.h"
#include "core/save-fl32.h"
#include "core/shape-description.h"
#include "core/export-svg.h"
namespace msdfgen {
/// 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.
void generatePseudoSDF(const BitmapRef<float, 1> &output, const Shape &shape, const Projection &projection, double range, const GeneratorConfig &config = GeneratorConfig());
/// Generates a single-channel signed perpendicular distance field.
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)
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.
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
void generateSDF(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport = true);
void generatePseudoSDF(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport = true);
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 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 generateSDF(const BitmapRef<float, 1> &output, const Shape &shape, const Projection &projection, Range range, const GeneratorConfig &config = GeneratorConfig());
void generatePSDF(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, const Projection &projection, Range range, const GeneratorConfig &config = GeneratorConfig());
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.
void generateSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape, double 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 generateMSDF_legacy(const BitmapRef<float, 3> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, ErrorCorrectionConfig errorCorrectionConfig = ErrorCorrectionConfig());
void generateMTSDF_legacy(const BitmapRef<float, 4> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, ErrorCorrectionConfig errorCorrectionConfig = ErrorCorrectionConfig());
void generateSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape, Range 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 generatePseudoSDF_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, 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());
}