mirror of
				https://github.com/godotengine/godot.git
				synced 2025-10-31 13:41:03 +00:00 
			
		
		
		
	 4c3f7d1290
			
		
	
	
		4c3f7d1290
		
	
	
	
	
		
			
			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.
		
			
				
	
	
		
			261 lines
		
	
	
	
		
			9.7 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			261 lines
		
	
	
	
		
			9.7 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| 
 | |
| #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;
 | |
| }
 | |
| 
 | |
| }
 |