diff options
author | bruvzg <7645683+bruvzg@users.noreply.github.com> | 2020-12-27 15:30:33 +0200 |
---|---|---|
committer | bruvzg <7645683+bruvzg@users.noreply.github.com> | 2021-08-27 15:43:18 +0300 |
commit | 4c3f7d1290311456519ca2416590c7e62465b7f2 (patch) | |
tree | d8b2373cb7bcb4b7aff47d81e35f36a0b9bf993c /thirdparty/msdfgen/core/edge-selectors.cpp | |
parent | 00268e37a0e40630ce76b5144cb272c4cc73a672 (diff) |
Makes FontData importable resource.
Adds multi-channel SDF font texture generation and rendering support.
Adds per-font oversampling support.
Adds FontData import plugins (for dynamic fonts, BMFonts and monospaced image fonts), font texture cache pre-generation and loading.
Adds BMFont binary format and outline support.
Diffstat (limited to 'thirdparty/msdfgen/core/edge-selectors.cpp')
-rw-r--r-- | thirdparty/msdfgen/core/edge-selectors.cpp | 261 |
1 files changed, 261 insertions, 0 deletions
diff --git a/thirdparty/msdfgen/core/edge-selectors.cpp b/thirdparty/msdfgen/core/edge-selectors.cpp new file mode 100644 index 0000000000..aee78847fb --- /dev/null +++ b/thirdparty/msdfgen/core/edge-selectors.cpp @@ -0,0 +1,261 @@ + +#include "edge-selectors.h" + +#include "arithmetics.hpp" + +namespace msdfgen { + +#define DISTANCE_DELTA_FACTOR 1.001 + +TrueDistanceSelector::EdgeCache::EdgeCache() : absDistance(0) { } + +void TrueDistanceSelector::reset(const Point2 &p) { + double delta = DISTANCE_DELTA_FACTOR*(p-this->p).length(); + minDistance.distance += nonZeroSign(minDistance.distance)*delta; + this->p = p; +} + +void TrueDistanceSelector::addEdge(EdgeCache &cache, const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge) { + double delta = DISTANCE_DELTA_FACTOR*(p-cache.point).length(); + if (cache.absDistance-delta <= fabs(minDistance.distance)) { + double dummy; + SignedDistance distance = edge->signedDistance(p, dummy); + if (distance < minDistance) + minDistance = distance; + cache.point = p; + cache.absDistance = fabs(distance.distance); + } +} + +void TrueDistanceSelector::merge(const TrueDistanceSelector &other) { + if (other.minDistance < minDistance) + minDistance = other.minDistance; +} + +TrueDistanceSelector::DistanceType TrueDistanceSelector::distance() const { + return minDistance.distance; +} + +PseudoDistanceSelectorBase::EdgeCache::EdgeCache() : absDistance(0), aDomainDistance(0), bDomainDistance(0), aPseudoDistance(0), bPseudoDistance(0) { } + +bool PseudoDistanceSelectorBase::getPseudoDistance(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; + return true; + } + } + return false; +} + +PseudoDistanceSelectorBase::PseudoDistanceSelectorBase() : minNegativePseudoDistance(-fabs(minTrueDistance.distance)), minPositivePseudoDistance(fabs(minTrueDistance.distance)), nearEdge(NULL), nearEdgeParam(0) { } + +void PseudoDistanceSelectorBase::reset(double delta) { + minTrueDistance.distance += nonZeroSign(minTrueDistance.distance)*delta; + minNegativePseudoDistance = -fabs(minTrueDistance.distance); + minPositivePseudoDistance = fabs(minTrueDistance.distance); + nearEdge = NULL; + nearEdgeParam = 0; +} + +bool PseudoDistanceSelectorBase::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.bDomainDistance > 0 && (cache.bPseudoDistance < 0 ? + cache.bPseudoDistance+delta >= minNegativePseudoDistance : + cache.bPseudoDistance-delta <= minPositivePseudoDistance + )) + ); +} + +void PseudoDistanceSelectorBase::addEdgeTrueDistance(const EdgeSegment *edge, const SignedDistance &distance, double param) { + if (distance < minTrueDistance) { + minTrueDistance = distance; + nearEdge = edge; + nearEdgeParam = param; + } +} + +void PseudoDistanceSelectorBase::addEdgePseudoDistance(double distance) { + if (distance <= 0 && distance > minNegativePseudoDistance) + minNegativePseudoDistance = distance; + if (distance >= 0 && distance < minPositivePseudoDistance) + minPositivePseudoDistance = distance; +} + +void PseudoDistanceSelectorBase::merge(const PseudoDistanceSelectorBase &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; +} + +double PseudoDistanceSelectorBase::computeDistance(const Point2 &p) const { + double minDistance = minTrueDistance.distance < 0 ? minNegativePseudoDistance : minPositivePseudoDistance; + if (nearEdge) { + SignedDistance distance = minTrueDistance; + nearEdge->distanceToPseudoDistance(distance, p, nearEdgeParam); + if (fabs(distance.distance) < fabs(minDistance)) + minDistance = distance.distance; + } + return minDistance; +} + +SignedDistance PseudoDistanceSelectorBase::trueDistance() const { + return minTrueDistance; +} + +void PseudoDistanceSelector::reset(const Point2 &p) { + double delta = DISTANCE_DELTA_FACTOR*(p-this->p).length(); + PseudoDistanceSelectorBase::reset(delta); + this->p = p; +} + +void PseudoDistanceSelector::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); + addEdgeTrueDistance(edge, distance, param); + cache.point = p; + cache.absDistance = fabs(distance.distance); + + Vector2 ap = p-edge->point(0); + Vector2 bp = p-edge->point(1); + Vector2 aDir = edge->direction(0).normalize(true); + Vector2 bDir = edge->direction(1).normalize(true); + Vector2 prevDir = prevEdge->direction(1).normalize(true); + Vector2 nextDir = nextEdge->direction(0).normalize(true); + double add = dotProduct(ap, (prevDir+aDir).normalize(true)); + 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 (bdd > 0) { + double pd = distance.distance; + if (getPseudoDistance(pd, bp, bDir)) + addEdgePseudoDistance(pd); + cache.bPseudoDistance = pd; + } + cache.aDomainDistance = add; + cache.bDomainDistance = bdd; + } +} + +PseudoDistanceSelector::DistanceType PseudoDistanceSelector::distance() const { + return computeDistance(p); +} + +void MultiDistanceSelector::reset(const Point2 &p) { + double delta = DISTANCE_DELTA_FACTOR*(p-this->p).length(); + r.reset(delta); + g.reset(delta); + b.reset(delta); + this->p = p; +} + +void MultiDistanceSelector::addEdge(EdgeCache &cache, const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge) { + if ( + (edge->color&RED && r.isEdgeRelevant(cache, edge, p)) || + (edge->color&GREEN && g.isEdgeRelevant(cache, edge, p)) || + (edge->color&BLUE && b.isEdgeRelevant(cache, edge, p)) + ) { + double param; + SignedDistance distance = edge->signedDistance(p, param); + if (edge->color&RED) + r.addEdgeTrueDistance(edge, distance, param); + if (edge->color&GREEN) + g.addEdgeTrueDistance(edge, distance, param); + if (edge->color&BLUE) + b.addEdgeTrueDistance(edge, distance, param); + cache.point = p; + cache.absDistance = fabs(distance.distance); + + Vector2 ap = p-edge->point(0); + Vector2 bp = p-edge->point(1); + Vector2 aDir = edge->direction(0).normalize(true); + Vector2 bDir = edge->direction(1).normalize(true); + Vector2 prevDir = prevEdge->direction(1).normalize(true); + Vector2 nextDir = nextEdge->direction(0).normalize(true); + double add = dotProduct(ap, (prevDir+aDir).normalize(true)); + double bdd = -dotProduct(bp, (bDir+nextDir).normalize(true)); + if (add > 0) { + double pd = distance.distance; + if (PseudoDistanceSelectorBase::getPseudoDistance(pd, ap, -aDir)) { + pd = -pd; + if (edge->color&RED) + r.addEdgePseudoDistance(pd); + if (edge->color&GREEN) + g.addEdgePseudoDistance(pd); + if (edge->color&BLUE) + b.addEdgePseudoDistance(pd); + } + cache.aPseudoDistance = pd; + } + if (bdd > 0) { + double pd = distance.distance; + if (PseudoDistanceSelectorBase::getPseudoDistance(pd, bp, bDir)) { + if (edge->color&RED) + r.addEdgePseudoDistance(pd); + if (edge->color&GREEN) + g.addEdgePseudoDistance(pd); + if (edge->color&BLUE) + b.addEdgePseudoDistance(pd); + } + cache.bPseudoDistance = pd; + } + cache.aDomainDistance = add; + cache.bDomainDistance = bdd; + } +} + +void MultiDistanceSelector::merge(const MultiDistanceSelector &other) { + r.merge(other.r); + g.merge(other.g); + b.merge(other.b); +} + +MultiDistanceSelector::DistanceType MultiDistanceSelector::distance() const { + MultiDistance multiDistance; + multiDistance.r = r.computeDistance(p); + multiDistance.g = g.computeDistance(p); + multiDistance.b = b.computeDistance(p); + return multiDistance; +} + +SignedDistance MultiDistanceSelector::trueDistance() const { + SignedDistance distance = r.trueDistance(); + if (g.trueDistance() < distance) + distance = g.trueDistance(); + if (b.trueDistance() < distance) + distance = b.trueDistance(); + return distance; +} + +MultiAndTrueDistanceSelector::DistanceType MultiAndTrueDistanceSelector::distance() const { + MultiDistance multiDistance = MultiDistanceSelector::distance(); + MultiAndTrueDistance mtd; + mtd.r = multiDistance.r; + mtd.g = multiDistance.g; + mtd.b = multiDistance.b; + mtd.a = trueDistance().distance; + return mtd; +} + +} |