diff options
Diffstat (limited to 'dep/recastnavigation')
| -rw-r--r-- | dep/recastnavigation/Detour/CMakeLists.txt | 13 | ||||
| -rw-r--r-- | dep/recastnavigation/Detour/DetourObstacleAvoidance.cpp | 96 | ||||
| -rw-r--r-- | dep/recastnavigation/Detour/DetourObstacleAvoidance.h | 72 | ||||
| -rw-r--r-- | dep/recastnavigation/Detour/Include/DetourAlloc.h (renamed from dep/recastnavigation/Detour/DetourAlloc.h) | 0 | ||||
| -rw-r--r-- | dep/recastnavigation/Detour/Include/DetourAssert.h (renamed from dep/recastnavigation/Detour/DetourAssert.h) | 0 | ||||
| -rw-r--r-- | dep/recastnavigation/Detour/Include/DetourCommon.h (renamed from dep/recastnavigation/Detour/DetourCommon.h) | 5 | ||||
| -rw-r--r-- | dep/recastnavigation/Detour/Include/DetourNavMesh.h (renamed from dep/recastnavigation/Detour/DetourNavMesh.h) | 23 | ||||
| -rw-r--r-- | dep/recastnavigation/Detour/Include/DetourNavMeshBuilder.h (renamed from dep/recastnavigation/Detour/DetourNavMeshBuilder.h) | 0 | ||||
| -rw-r--r-- | dep/recastnavigation/Detour/Include/DetourNavMeshQuery.h (renamed from dep/recastnavigation/Detour/DetourNavMeshQuery.h) | 14 | ||||
| -rw-r--r-- | dep/recastnavigation/Detour/Include/DetourNode.h (renamed from dep/recastnavigation/Detour/DetourNode.h) | 0 | ||||
| -rw-r--r-- | dep/recastnavigation/Detour/Include/DetourStatus.h (renamed from dep/recastnavigation/Detour/DetourStatus.h) | 0 | ||||
| -rw-r--r-- | dep/recastnavigation/Detour/Source/DetourAlloc.cpp (renamed from dep/recastnavigation/Detour/DetourAlloc.cpp) | 0 | ||||
| -rw-r--r-- | dep/recastnavigation/Detour/Source/DetourCommon.cpp (renamed from dep/recastnavigation/Detour/DetourCommon.cpp) | 0 | ||||
| -rw-r--r-- | dep/recastnavigation/Detour/Source/DetourNavMesh.cpp (renamed from dep/recastnavigation/Detour/DetourNavMesh.cpp) | 44 | ||||
| -rw-r--r-- | dep/recastnavigation/Detour/Source/DetourNavMeshBuilder.cpp (renamed from dep/recastnavigation/Detour/DetourNavMeshBuilder.cpp) | 0 | ||||
| -rw-r--r-- | dep/recastnavigation/Detour/Source/DetourNavMeshQuery.cpp (renamed from dep/recastnavigation/Detour/DetourNavMeshQuery.cpp) | 117 | ||||
| -rw-r--r-- | dep/recastnavigation/Detour/Source/DetourNode.cpp (renamed from dep/recastnavigation/Detour/DetourNode.cpp) | 0 | ||||
| -rw-r--r-- | dep/recastnavigation/Readme.txt | 76 | ||||
| -rw-r--r-- | dep/recastnavigation/Recast/CMakeLists.txt | 22 | ||||
| -rw-r--r-- | dep/recastnavigation/Recast/Include/Recast.h (renamed from dep/recastnavigation/Recast/Recast.h) | 7 | ||||
| -rw-r--r-- | dep/recastnavigation/Recast/Include/RecastAlloc.h (renamed from dep/recastnavigation/Recast/RecastAlloc.h) | 0 | ||||
| -rw-r--r-- | dep/recastnavigation/Recast/Include/RecastAssert.h (renamed from dep/recastnavigation/Recast/RecastAssert.h) | 0 | ||||
| -rw-r--r-- | dep/recastnavigation/Recast/Source/Recast.cpp (renamed from dep/recastnavigation/Recast/Recast.cpp) | 20 | ||||
| -rw-r--r-- | dep/recastnavigation/Recast/Source/RecastAlloc.cpp (renamed from dep/recastnavigation/Recast/RecastAlloc.cpp) | 0 | ||||
| -rw-r--r-- | dep/recastnavigation/Recast/Source/RecastArea.cpp (renamed from dep/recastnavigation/Recast/RecastArea.cpp) | 0 | ||||
| -rw-r--r-- | dep/recastnavigation/Recast/Source/RecastContour.cpp (renamed from dep/recastnavigation/Recast/RecastContour.cpp) | 0 | ||||
| -rw-r--r-- | dep/recastnavigation/Recast/Source/RecastFilter.cpp (renamed from dep/recastnavigation/Recast/RecastFilter.cpp) | 0 | ||||
| -rw-r--r-- | dep/recastnavigation/Recast/Source/RecastLayers.cpp (renamed from dep/recastnavigation/Recast/RecastLayers.cpp) | 4 | ||||
| -rw-r--r-- | dep/recastnavigation/Recast/Source/RecastMesh.cpp (renamed from dep/recastnavigation/Recast/RecastMesh.cpp) | 47 | ||||
| -rw-r--r-- | dep/recastnavigation/Recast/Source/RecastMeshDetail.cpp (renamed from dep/recastnavigation/Recast/RecastMeshDetail.cpp) | 102 | ||||
| -rw-r--r-- | dep/recastnavigation/Recast/Source/RecastRasterization.cpp (renamed from dep/recastnavigation/Recast/RecastRasterization.cpp) | 113 | ||||
| -rw-r--r-- | dep/recastnavigation/Recast/Source/RecastRegion.cpp (renamed from dep/recastnavigation/Recast/RecastRegion.cpp) | 141 | ||||
| -rw-r--r-- | dep/recastnavigation/TODO.txt | 20 | ||||
| -rw-r--r-- | dep/recastnavigation/recast_hotfix1.diff | 13 | ||||
| -rw-r--r-- | dep/recastnavigation/recastnavigation.diff | 4066 |
35 files changed, 4614 insertions, 401 deletions
diff --git a/dep/recastnavigation/Detour/CMakeLists.txt b/dep/recastnavigation/Detour/CMakeLists.txt index b7c0853efc4..5f3542e96b9 100644 --- a/dep/recastnavigation/Detour/CMakeLists.txt +++ b/dep/recastnavigation/Detour/CMakeLists.txt @@ -9,13 +9,14 @@ # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. set(Detour_STAT_SRCS - DetourAlloc.cpp - DetourCommon.cpp - DetourNavMesh.cpp - DetourNavMeshBuilder.cpp - DetourNavMeshQuery.cpp - DetourNode.cpp + Source/DetourAlloc.cpp + Source/DetourCommon.cpp + Source/DetourNavMesh.cpp + Source/DetourNavMeshBuilder.cpp + Source/DetourNavMeshQuery.cpp + Source/DetourNode.cpp ) +include_directories(Include) if(WIN32) include_directories( diff --git a/dep/recastnavigation/Detour/DetourObstacleAvoidance.cpp b/dep/recastnavigation/Detour/DetourObstacleAvoidance.cpp index a255c9b3fd1..d3f90b7ab17 100644 --- a/dep/recastnavigation/Detour/DetourObstacleAvoidance.cpp +++ b/dep/recastnavigation/Detour/DetourObstacleAvoidance.cpp @@ -25,6 +25,7 @@ #include <float.h> #include <new> +static const float DT_PI = 3.14159265f; static int sweepCircleCircle(const float* c0, const float r0, const float* v, const float* c1, const float r1, @@ -206,12 +207,6 @@ void dtFreeObstacleAvoidanceQuery(dtObstacleAvoidanceQuery* ptr) dtObstacleAvoidanceQuery::dtObstacleAvoidanceQuery() : - m_velBias(0.0f), - m_weightDesVel(0.0f), - m_weightCurVel(0.0f), - m_weightSide(0.0f), - m_weightToi(0.0f), - m_horizTime(0.0f), m_maxCircles(0), m_circles(0), m_ncircles(0), @@ -318,11 +313,11 @@ void dtObstacleAvoidanceQuery::prepare(const float* pos, const float* dvel) float dtObstacleAvoidanceQuery::processSample(const float* vcand, const float cs, const float* pos, const float rad, - const float vmax, const float* vel, const float* dvel, + const float* vel, const float* dvel, dtObstacleAvoidanceDebugData* debug) { // Find min time of impact and exit amongst all obstacles. - float tmin = m_horizTime; + float tmin = m_params.horizTime; float side = 0; int nside = 0; @@ -395,11 +390,10 @@ float dtObstacleAvoidanceQuery::processSample(const float* vcand, const float cs if (nside) side /= nside; - const float ivmax = 1.0f / vmax; - const float vpen = m_weightDesVel * (dtVdist2D(vcand, dvel) * ivmax); - const float vcpen = m_weightCurVel * (dtVdist2D(vcand, vel) * ivmax); - const float spen = m_weightSide * side; - const float tpen = m_weightToi * (1.0f/(0.1f+tmin / m_horizTime)); + const float vpen = m_params.weightDesVel * (dtVdist2D(vcand, dvel) * m_invVmax); + const float vcpen = m_params.weightCurVel * (dtVdist2D(vcand, vel) * m_invVmax); + const float spen = m_params.weightSide * side; + const float tpen = m_params.weightToi * (1.0f/(0.1f+tmin*m_invHorizTime)); const float penalty = vpen + vcpen + spen + tpen; @@ -410,28 +404,34 @@ float dtObstacleAvoidanceQuery::processSample(const float* vcand, const float cs return penalty; } -void dtObstacleAvoidanceQuery::sampleVelocityGrid(const float* pos, const float rad, const float vmax, - const float* vel, const float* dvel, - float* nvel, const int gsize, - dtObstacleAvoidanceDebugData* debug) +int dtObstacleAvoidanceQuery::sampleVelocityGrid(const float* pos, const float rad, const float vmax, + const float* vel, const float* dvel, float* nvel, + const dtObstacleAvoidanceParams* params, + dtObstacleAvoidanceDebugData* debug) { prepare(pos, dvel); + memcpy(&m_params, params, sizeof(dtObstacleAvoidanceParams)); + m_invHorizTime = 1.0f / m_params.horizTime; + m_vmax = vmax; + m_invVmax = 1.0f / vmax; + dtVset(nvel, 0,0,0); if (debug) debug->reset(); - const float cvx = dvel[0] * m_velBias; - const float cvz = dvel[2] * m_velBias; - const float cs = vmax * 2 * (1 - m_velBias) / (float)(gsize-1); - const float half = (gsize-1)*cs*0.5f; + const float cvx = dvel[0] * m_params.velBias; + const float cvz = dvel[2] * m_params.velBias; + const float cs = vmax * 2 * (1 - m_params.velBias) / (float)(m_params.gridSize-1); + const float half = (m_params.gridSize-1)*cs*0.5f; float minPenalty = FLT_MAX; + int ns = 0; - for (int y = 0; y < gsize; ++y) + for (int y = 0; y < m_params.gridSize; ++y) { - for (int x = 0; x < gsize; ++x) + for (int x = 0; x < m_params.gridSize; ++x) { float vcand[3]; vcand[0] = cvx + x*cs - half; @@ -440,7 +440,8 @@ void dtObstacleAvoidanceQuery::sampleVelocityGrid(const float* pos, const float if (dtSqr(vcand[0])+dtSqr(vcand[2]) > dtSqr(vmax+cs/2)) continue; - const float penalty = processSample(vcand, cs, pos,rad,vmax,vel,dvel, debug); + const float penalty = processSample(vcand, cs, pos,rad,vel,dvel, debug); + ns++; if (penalty < minPenalty) { minPenalty = penalty; @@ -448,31 +449,38 @@ void dtObstacleAvoidanceQuery::sampleVelocityGrid(const float* pos, const float } } } + + return ns; } -static const float DT_PI = 3.14159265f; - -void dtObstacleAvoidanceQuery::sampleVelocityAdaptive(const float* pos, const float rad, const float vmax, - const float* vel, const float* dvel, float* nvel, - const int ndivs, const int nrings, const int depth, - dtObstacleAvoidanceDebugData* debug) +int dtObstacleAvoidanceQuery::sampleVelocityAdaptive(const float* pos, const float rad, const float vmax, + const float* vel, const float* dvel, float* nvel, + const dtObstacleAvoidanceParams* params, + dtObstacleAvoidanceDebugData* debug) { prepare(pos, dvel); + memcpy(&m_params, params, sizeof(dtObstacleAvoidanceParams)); + m_invHorizTime = 1.0f / m_params.horizTime; + m_vmax = vmax; + m_invVmax = 1.0f / vmax; + dtVset(nvel, 0,0,0); if (debug) debug->reset(); - + // Build sampling pattern aligned to desired velocity. - static const int MAX_PATTERN_DIVS = 32; - static const int MAX_PATTERN_RINGS = 4; - float pat[(MAX_PATTERN_DIVS*MAX_PATTERN_RINGS+1)*2]; + float pat[(DT_MAX_PATTERN_DIVS*DT_MAX_PATTERN_RINGS+1)*2]; int npat = 0; - const int nd = dtClamp(ndivs, 1, MAX_PATTERN_DIVS); - const int nr = dtClamp(nrings, 1, MAX_PATTERN_RINGS); + const int ndivs = (int)m_params.adaptiveDivs; + const int nrings= (int)m_params.adaptiveRings; + const int depth = (int)m_params.adaptiveDepth; + + const int nd = dtClamp(ndivs, 1, DT_MAX_PATTERN_DIVS); + const int nr = dtClamp(nrings, 1, DT_MAX_PATTERN_RINGS); const float da = (1.0f/nd) * DT_PI*2; const float dang = atan2f(dvel[2], dvel[0]); @@ -483,21 +491,22 @@ void dtObstacleAvoidanceQuery::sampleVelocityAdaptive(const float* pos, const fl for (int j = 0; j < nr; ++j) { - const float rad = (float)(nr-j)/(float)nr; + const float r = (float)(nr-j)/(float)nr; float a = dang + (j&1)*0.5f*da; for (int i = 0; i < nd; ++i) { - pat[npat*2+0] = cosf(a)*rad; - pat[npat*2+1] = sinf(a)*rad; + pat[npat*2+0] = cosf(a)*r; + pat[npat*2+1] = sinf(a)*r; npat++; a += da; } } // Start sampling. - float cr = vmax * (1.0f-m_velBias); + float cr = vmax * (1.0f - m_params.velBias); float res[3]; - dtVset(res, dvel[0] * m_velBias, 0, dvel[2] * m_velBias); + dtVset(res, dvel[0] * m_params.velBias, 0, dvel[2] * m_params.velBias); + int ns = 0; for (int k = 0; k < depth; ++k) { @@ -514,7 +523,8 @@ void dtObstacleAvoidanceQuery::sampleVelocityAdaptive(const float* pos, const fl if (dtSqr(vcand[0])+dtSqr(vcand[2]) > dtSqr(vmax+0.001f)) continue; - const float penalty = processSample(vcand,cr/10, pos,rad,vmax,vel,dvel, debug); + const float penalty = processSample(vcand,cr/10, pos,rad,vel,dvel, debug); + ns++; if (penalty < minPenalty) { minPenalty = penalty; @@ -528,5 +538,7 @@ void dtObstacleAvoidanceQuery::sampleVelocityAdaptive(const float* pos, const fl } dtVcopy(nvel, res); + + return ns; } diff --git a/dep/recastnavigation/Detour/DetourObstacleAvoidance.h b/dep/recastnavigation/Detour/DetourObstacleAvoidance.h index 4a7187a7998..8ff6211e867 100644 --- a/dep/recastnavigation/Detour/DetourObstacleAvoidance.h +++ b/dep/recastnavigation/Detour/DetourObstacleAvoidance.h @@ -21,21 +21,19 @@ struct dtObstacleCircle { - float p[3]; // Position of the obstacle - float vel[3]; // Velocity of the obstacle - float dvel[3]; // Velocity of the obstacle - float rad; // Radius of the obstacle - float dp[3], np[3]; // Use for side selection during sampling. + float p[3]; ///< Position of the obstacle + float vel[3]; ///< Velocity of the obstacle + float dvel[3]; ///< Velocity of the obstacle + float rad; ///< Radius of the obstacle + float dp[3], np[3]; ///< Use for side selection during sampling. }; struct dtObstacleSegment { - float p[3], q[3]; // End points of the obstacle segment + float p[3], q[3]; ///< End points of the obstacle segment bool touch; }; -static const int RVO_SAMPLE_RAD = 15; -static const int MAX_RVO_SAMPLES = (RVO_SAMPLE_RAD*2+1)*(RVO_SAMPLE_RAD*2+1) + 100; class dtObstacleAvoidanceDebugData { @@ -75,6 +73,23 @@ dtObstacleAvoidanceDebugData* dtAllocObstacleAvoidanceDebugData(); void dtFreeObstacleAvoidanceDebugData(dtObstacleAvoidanceDebugData* ptr); +static const int DT_MAX_PATTERN_DIVS = 32; ///< Max numver of adaptive divs. +static const int DT_MAX_PATTERN_RINGS = 4; ///< Max number of adaptive rings. + +struct dtObstacleAvoidanceParams +{ + float velBias; + float weightDesVel; + float weightCurVel; + float weightSide; + float weightToi; + float horizTime; + unsigned char gridSize; ///< grid + unsigned char adaptiveDivs; ///< adaptive + unsigned char adaptiveRings; ///< adaptive + unsigned char adaptiveDepth; ///< adaptive +}; + class dtObstacleAvoidanceQuery { public: @@ -90,22 +105,15 @@ public: void addSegment(const float* p, const float* q); - inline void setVelocitySelectionBias(float v) { m_velBias = v; } - inline void setDesiredVelocityWeight(float w) { m_weightDesVel = w; } - inline void setCurrentVelocityWeight(float w) { m_weightCurVel = w; } - inline void setPreferredSideWeight(float w) { m_weightSide = w; } - inline void setCollisionTimeWeight(float w) { m_weightToi = w; } - inline void setTimeHorizon(float t) { m_horizTime = t; } - - void sampleVelocityGrid(const float* pos, const float rad, const float vmax, - const float* vel, const float* dvel, float* nvel, - const int gsize, - dtObstacleAvoidanceDebugData* debug = 0); - - void sampleVelocityAdaptive(const float* pos, const float rad, const float vmax, - const float* vel, const float* dvel, float* nvel, - const int ndivs, const int nrings, const int depth, - dtObstacleAvoidanceDebugData* debug = 0); + int sampleVelocityGrid(const float* pos, const float rad, const float vmax, + const float* vel, const float* dvel, float* nvel, + const dtObstacleAvoidanceParams* params, + dtObstacleAvoidanceDebugData* debug = 0); + + int sampleVelocityAdaptive(const float* pos, const float rad, const float vmax, + const float* vel, const float* dvel, float* nvel, + const dtObstacleAvoidanceParams* params, + dtObstacleAvoidanceDebugData* debug = 0); inline int getObstacleCircleCount() const { return m_ncircles; } const dtObstacleCircle* getObstacleCircle(const int i) { return &m_circles[i]; } @@ -119,19 +127,17 @@ private: float processSample(const float* vcand, const float cs, const float* pos, const float rad, - const float vmax, const float* vel, const float* dvel, + const float* vel, const float* dvel, dtObstacleAvoidanceDebugData* debug); dtObstacleCircle* insertCircle(const float dist); dtObstacleSegment* insertSegment(const float dist); - float m_velBias; - float m_weightDesVel; - float m_weightCurVel; - float m_weightSide; - float m_weightToi; - float m_horizTime; - + dtObstacleAvoidanceParams m_params; + float m_invHorizTime; + float m_vmax; + float m_invVmax; + int m_maxCircles; dtObstacleCircle* m_circles; int m_ncircles; @@ -145,4 +151,4 @@ dtObstacleAvoidanceQuery* dtAllocObstacleAvoidanceQuery(); void dtFreeObstacleAvoidanceQuery(dtObstacleAvoidanceQuery* ptr); -#endif // DETOUROBSTACLEAVOIDANCE_H
\ No newline at end of file +#endif // DETOUROBSTACLEAVOIDANCE_H diff --git a/dep/recastnavigation/Detour/DetourAlloc.h b/dep/recastnavigation/Detour/Include/DetourAlloc.h index e814b62a716..e814b62a716 100644 --- a/dep/recastnavigation/Detour/DetourAlloc.h +++ b/dep/recastnavigation/Detour/Include/DetourAlloc.h diff --git a/dep/recastnavigation/Detour/DetourAssert.h b/dep/recastnavigation/Detour/Include/DetourAssert.h index 3cf652288fa..3cf652288fa 100644 --- a/dep/recastnavigation/Detour/DetourAssert.h +++ b/dep/recastnavigation/Detour/Include/DetourAssert.h diff --git a/dep/recastnavigation/Detour/DetourCommon.h b/dep/recastnavigation/Detour/Include/DetourCommon.h index ed7c5149db9..0888614ea9b 100644 --- a/dep/recastnavigation/Detour/DetourCommon.h +++ b/dep/recastnavigation/Detour/Include/DetourCommon.h @@ -32,6 +32,11 @@ feature to find minor members. /// @name General helper functions /// @{ +/// Used to ignore a function parameter. VS complains about unused parameters +/// and this silences the warning. +/// @param [in] _ Unused parameter +template<class T> void dtIgnoreUnused(const T&) { } + /// Swaps the values of the two parameters. /// @param[in,out] a Value A /// @param[in,out] b Value B diff --git a/dep/recastnavigation/Detour/DetourNavMesh.h b/dep/recastnavigation/Detour/Include/DetourNavMesh.h index c094e4134d5..cdd473f1aff 100644 --- a/dep/recastnavigation/Detour/DetourNavMesh.h +++ b/dep/recastnavigation/Detour/Include/DetourNavMesh.h @@ -25,7 +25,8 @@ // Edited by TC #if defined(WIN32) && !defined(__MINGW32__) -typedef unsigned __int64 uint64; +/// Do not rename back to uint64. Otherwise mac complains about typedef redefinition +typedef unsigned __int64 uint64_d; #else #include <stdint.h> #ifndef uint64_t @@ -33,7 +34,8 @@ typedef unsigned __int64 uint64; #include <linux/types.h> #endif #endif -typedef uint64_t uint64; +/// Do not rename back to uint64. Otherwise mac complains about typedef redefinition +typedef uint64_t uint64_d; #endif // Note: If you want to use 64-bit refs, change the types of both dtPolyRef & dtTileRef. @@ -42,17 +44,13 @@ typedef uint64_t uint64; // Edited by TC // We cannot have over 31 bits for either tile nor poly // without changing polyCount to use 64bits too. -static const int STATIC_SALT_BITS = 12; -static const int STATIC_TILE_BITS = 21; -static const int STATIC_POLY_BITS = 31; - /// A handle to a polygon within a navigation mesh tile. /// @ingroup detour -typedef uint64 dtPolyRef; // Edited by TC +typedef uint64_d dtPolyRef; // Edited by TC /// A handle to a tile within a navigation mesh. /// @ingroup detour -typedef uint64 dtTileRef; // Edited by TC +typedef uint64_d dtTileRef; // Edited by TC /// The maximum number of vertices per navigation polygon. /// @ingroup detour @@ -92,6 +90,12 @@ static const unsigned int DT_OFFMESH_CON_BIDIR = 1; /// @ingroup detour static const int DT_MAX_AREAS = 64; +static const int STATIC_SALT_BITS = 12; +static const int STATIC_TILE_BITS = 21; +static const int STATIC_POLY_BITS = 31; +// we cannot have over 31 bits for either tile nor poly +// without changing polyCount to use 64bits too. + /// Tile flags used for various functions and fields. /// For an example, see dtNavMesh::addTile(). enum dtTileFlags @@ -583,8 +587,7 @@ private: dtPolyRef findNearestPolyInTile(const dtMeshTile* tile, const float* center, const float* extents, float* nearestPt) const; /// Returns closest point on polygon. - void closestPointOnPolyInTile(const dtMeshTile* tile, unsigned int ip, - const float* pos, float* closest) const; + void closestPointOnPoly(dtPolyRef ref, const float* pos, float* closest, bool* posOverPoly) const; dtNavMeshParams m_params; ///< Current initialization params. TODO: do not store this info twice. float m_orig[3]; ///< Origin of the tile (0,0) diff --git a/dep/recastnavigation/Detour/DetourNavMeshBuilder.h b/dep/recastnavigation/Detour/Include/DetourNavMeshBuilder.h index c80d1717630..c80d1717630 100644 --- a/dep/recastnavigation/Detour/DetourNavMeshBuilder.h +++ b/dep/recastnavigation/Detour/Include/DetourNavMeshBuilder.h diff --git a/dep/recastnavigation/Detour/DetourNavMeshQuery.h b/dep/recastnavigation/Detour/Include/DetourNavMeshQuery.h index d431bf177bd..4a5112c9eb9 100644 --- a/dep/recastnavigation/Detour/DetourNavMeshQuery.h +++ b/dep/recastnavigation/Detour/Include/DetourNavMeshQuery.h @@ -200,8 +200,8 @@ public: /// Finalizes and returns the results of an incomplete sliced path query, returning the path to the furthest /// polygon on the existing path that was visited during the search. - /// @param[out] existing An array of polygon references for the existing path. - /// @param[out] existingSize The number of polygon in the @p existing array. + /// @param[in] existing An array of polygon references for the existing path. + /// @param[in] existingSize The number of polygon in the @p existing array. /// @param[out] path An ordered list of polygon references representing the path. (Start to end.) /// [(polyRef) * @p pathCount] /// @param[out] pathCount The number of polygons returned in the @p path array. @@ -378,8 +378,9 @@ public: /// @param[in] ref The reference id of the polygon. /// @param[in] pos The position to check. [(x, y, z)] /// @param[out] closest The closest point on the polygon. [(x, y, z)] + /// @param[out] posOverPoly True of the position is over the polygon. /// @returns The status flags for the query. - dtStatus closestPointOnPoly(dtPolyRef ref, const float* pos, float* closest) const; + dtStatus closestPointOnPoly(dtPolyRef ref, const float* pos, float* closest, bool* posOverPoly) const; /// Returns a point on the boundary closest to the source point if the source point is outside the /// polygon's xz-bounds. @@ -428,12 +429,7 @@ private: /// Queries polygons within a tile. int queryPolygonsInTile(const dtMeshTile* tile, const float* qmin, const float* qmax, const dtQueryFilter* filter, dtPolyRef* polys, const int maxPolys) const; - /// Find nearest polygon within a tile. - dtPolyRef findNearestPolyInTile(const dtMeshTile* tile, const float* center, const float* extents, - const dtQueryFilter* filter, float* nearestPt) const; - /// Returns closest point on polygon. - void closestPointOnPolyInTile(const dtMeshTile* tile, const dtPoly* poly, const float* pos, float* closest) const; - + /// Returns portal points between two polygons. dtStatus getPortalPoints(dtPolyRef from, dtPolyRef to, float* left, float* right, unsigned char& fromType, unsigned char& toType) const; diff --git a/dep/recastnavigation/Detour/DetourNode.h b/dep/recastnavigation/Detour/Include/DetourNode.h index b68c922d038..b68c922d038 100644 --- a/dep/recastnavigation/Detour/DetourNode.h +++ b/dep/recastnavigation/Detour/Include/DetourNode.h diff --git a/dep/recastnavigation/Detour/DetourStatus.h b/dep/recastnavigation/Detour/Include/DetourStatus.h index af822c4a92d..af822c4a92d 100644 --- a/dep/recastnavigation/Detour/DetourStatus.h +++ b/dep/recastnavigation/Detour/Include/DetourStatus.h diff --git a/dep/recastnavigation/Detour/DetourAlloc.cpp b/dep/recastnavigation/Detour/Source/DetourAlloc.cpp index 5f671df5bdb..5f671df5bdb 100644 --- a/dep/recastnavigation/Detour/DetourAlloc.cpp +++ b/dep/recastnavigation/Detour/Source/DetourAlloc.cpp diff --git a/dep/recastnavigation/Detour/DetourCommon.cpp b/dep/recastnavigation/Detour/Source/DetourCommon.cpp index b5700f5930b..b5700f5930b 100644 --- a/dep/recastnavigation/Detour/DetourCommon.cpp +++ b/dep/recastnavigation/Detour/Source/DetourCommon.cpp diff --git a/dep/recastnavigation/Detour/DetourNavMesh.cpp b/dep/recastnavigation/Detour/Source/DetourNavMesh.cpp index 6b8e2d9d649..51740509950 100644 --- a/dep/recastnavigation/Detour/DetourNavMesh.cpp +++ b/dep/recastnavigation/Detour/Source/DetourNavMesh.cpp @@ -609,10 +609,12 @@ void dtNavMesh::baseOffMeshLinks(dtMeshTile* tile) } } -void dtNavMesh::closestPointOnPolyInTile(const dtMeshTile* tile, unsigned int ip, - const float* pos, float* closest) const +void dtNavMesh::closestPointOnPoly(dtPolyRef ref, const float* pos, float* closest, bool* posOverPoly) const { - const dtPoly* poly = &tile->polys[ip]; + const dtMeshTile* tile = 0; + const dtPoly* poly = 0; + getTileAndPolyByRefUnsafe(ref, &tile, &poly); + // Off-mesh connections don't have detail polygons. if (poly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION) { @@ -622,11 +624,14 @@ void dtNavMesh::closestPointOnPolyInTile(const dtMeshTile* tile, unsigned int ip const float d1 = dtVdist(pos, v1); const float u = d0 / (d0+d1); dtVlerp(closest, v0, v1, u); + if (posOverPoly) + *posOverPoly = false; return; } + const unsigned int ip = (unsigned int)(poly - tile->polys); const dtPolyDetail* pd = &tile->detailMeshes[ip]; - + // Clamp point to be inside the polygon. float verts[DT_VERTS_PER_POLYGON*3]; float edged[DT_VERTS_PER_POLYGON]; @@ -652,6 +657,14 @@ void dtNavMesh::closestPointOnPolyInTile(const dtMeshTile* tile, unsigned int ip const float* va = &verts[imin*3]; const float* vb = &verts[((imin+1)%nv)*3]; dtVlerp(closest, va, vb, edget[imin]); + + if (posOverPoly) + *posOverPoly = false; + } + else + { + if (posOverPoly) + *posOverPoly = true; } // Find height at the location. @@ -694,12 +707,27 @@ dtPolyRef dtNavMesh::findNearestPolyInTile(const dtMeshTile* tile, { dtPolyRef ref = polys[i]; float closestPtPoly[3]; - closestPointOnPolyInTile(tile, decodePolyIdPoly(ref), center, closestPtPoly); - float d = dtVdistSqr(center, closestPtPoly); + float diff[3]; + bool posOverPoly = false; + float d = 0; + closestPointOnPoly(ref, center, closestPtPoly, &posOverPoly); + + // If a point is directly over a polygon and closer than + // climb height, favor that instead of straight line nearest point. + dtVsub(diff, center, closestPtPoly); + if (posOverPoly) + { + d = dtAbs(diff[1]) - tile->header->walkableClimb; + d = d > 0 ? d*d : 0; + } + else + { + d = dtVlenSqr(diff); + } + if (d < nearestDistanceSqr) { - if (nearestPt) - dtVcopy(nearestPt, closestPtPoly); + dtVcopy(nearestPt, closestPtPoly); nearestDistanceSqr = d; nearest = ref; } diff --git a/dep/recastnavigation/Detour/DetourNavMeshBuilder.cpp b/dep/recastnavigation/Detour/Source/DetourNavMeshBuilder.cpp index 9d8471b96a1..9d8471b96a1 100644 --- a/dep/recastnavigation/Detour/DetourNavMeshBuilder.cpp +++ b/dep/recastnavigation/Detour/Source/DetourNavMeshBuilder.cpp diff --git a/dep/recastnavigation/Detour/DetourNavMeshQuery.cpp b/dep/recastnavigation/Detour/Source/DetourNavMeshQuery.cpp index e6557cf707e..f1709dfd4cf 100644 --- a/dep/recastnavigation/Detour/DetourNavMeshQuery.cpp +++ b/dep/recastnavigation/Detour/Source/DetourNavMeshQuery.cpp @@ -502,7 +502,7 @@ dtStatus dtNavMeshQuery::findRandomPointAroundCircle(dtPolyRef startRef, const f /// /// See closestPointOnPolyBoundary() for a limited but faster option. /// -dtStatus dtNavMeshQuery::closestPointOnPoly(dtPolyRef ref, const float* pos, float* closest) const +dtStatus dtNavMeshQuery::closestPointOnPoly(dtPolyRef ref, const float* pos, float* closest, bool* posOverPoly) const { dtAssert(m_nav); const dtMeshTile* tile = 0; @@ -511,19 +511,7 @@ dtStatus dtNavMeshQuery::closestPointOnPoly(dtPolyRef ref, const float* pos, flo return DT_FAILURE | DT_INVALID_PARAM; if (!tile) return DT_FAILURE | DT_INVALID_PARAM; - - // Edited by TC - if (poly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION) - return DT_FAILURE; - - closestPointOnPolyInTile(tile, poly, pos, closest); - return DT_SUCCESS; -} - -void dtNavMeshQuery::closestPointOnPolyInTile(const dtMeshTile* tile, const dtPoly* poly, - const float* pos, float* closest) const -{ // Off-mesh connections don't have detail polygons. if (poly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION) { @@ -533,7 +521,9 @@ void dtNavMeshQuery::closestPointOnPolyInTile(const dtMeshTile* tile, const dtPo const float d1 = dtVdist(pos, v1); const float u = d0 / (d0+d1); dtVlerp(closest, v0, v1, u); - return; + if (posOverPoly) + *posOverPoly = false; + return DT_SUCCESS; } const unsigned int ip = (unsigned int)(poly - tile->polys); @@ -564,6 +554,14 @@ void dtNavMeshQuery::closestPointOnPolyInTile(const dtMeshTile* tile, const dtPo const float* va = &verts[imin*3]; const float* vb = &verts[((imin+1)%nv)*3]; dtVlerp(closest, va, vb, edget[imin]); + + if (posOverPoly) + *posOverPoly = false; + } + else + { + if (posOverPoly) + *posOverPoly = true; } // Find height at the location. @@ -585,30 +583,8 @@ void dtNavMeshQuery::closestPointOnPolyInTile(const dtMeshTile* tile, const dtPo break; } } - -/* float closestDistSqr = FLT_MAX; - for (int j = 0; j < pd->triCount; ++j) - { - const unsigned char* t = &tile->detailTris[(pd->triBase+j)*4]; - const float* v[3]; - for (int k = 0; k < 3; ++k) - { - if (t[k] < poly->vertCount) - v[k] = &tile->verts[poly->verts[t[k]]*3]; - else - v[k] = &tile->detailVerts[(pd->vertBase+(t[k]-poly->vertCount))*3]; - } - - float pt[3]; - dtClosestPtPointTriangle(pt, pos, v[0], v[1], v[2]); - float d = dtVdistSqr(pos, pt); - - if (d < closestDistSqr) - { - dtVcopy(closest, pt); - closestDistSqr = d; - } - }*/ + + return DT_SUCCESS; } /// @par @@ -687,8 +663,8 @@ dtStatus dtNavMeshQuery::getPolyHeight(dtPolyRef ref, const float* pos, float* h { const float* v0 = &tile->verts[poly->verts[0]*3]; const float* v1 = &tile->verts[poly->verts[1]*3]; - const float d0 = dtVdist(pos, v0); - const float d1 = dtVdist(pos, v1); + const float d0 = dtVdist2D(pos, v0); + const float d1 = dtVdist2D(pos, v1); const float u = d0 / (d0+d1); if (height) *height = v0[1] + (v1[1] - v0[1]) * u; @@ -752,8 +728,27 @@ dtStatus dtNavMeshQuery::findNearestPoly(const float* center, const float* exten { dtPolyRef ref = polys[i]; float closestPtPoly[3]; - closestPointOnPoly(ref, center, closestPtPoly); - float d = dtVdistSqr(center, closestPtPoly); + float diff[3]; + bool posOverPoly = false; + float d = 0; + closestPointOnPoly(ref, center, closestPtPoly, &posOverPoly); + + // If a point is directly over a polygon and closer than + // climb height, favor that instead of straight line nearest point. + dtVsub(diff, center, closestPtPoly); + if (posOverPoly) + { + const dtMeshTile* tile = 0; + const dtPoly* poly = 0; + m_nav->getTileAndPolyByRefUnsafe(polys[i], &tile, &poly); + d = dtAbs(diff[1]) - tile->header->walkableClimb; + d = d > 0 ? d*d : 0; + } + else + { + d = dtVlenSqr(diff); + } + if (d < nearestDistanceSqr) { if (nearestPt) @@ -769,42 +764,6 @@ dtStatus dtNavMeshQuery::findNearestPoly(const float* center, const float* exten return DT_SUCCESS; } -dtPolyRef dtNavMeshQuery::findNearestPolyInTile(const dtMeshTile* tile, const float* center, const float* extents, - const dtQueryFilter* filter, float* nearestPt) const -{ - dtAssert(m_nav); - - float bmin[3], bmax[3]; - dtVsub(bmin, center, extents); - dtVadd(bmax, center, extents); - - // Get nearby polygons from proximity grid. - dtPolyRef polys[128]; - int polyCount = queryPolygonsInTile(tile, bmin, bmax, filter, polys, 128); - - // Find nearest polygon amongst the nearby polygons. - dtPolyRef nearest = 0; - float nearestDistanceSqr = FLT_MAX; - for (int i = 0; i < polyCount; ++i) - { - dtPolyRef ref = polys[i]; - const dtPoly* poly = &tile->polys[m_nav->decodePolyIdPoly(ref)]; - float closestPtPoly[3]; - closestPointOnPolyInTile(tile, poly, center, closestPtPoly); - - float d = dtVdistSqr(center, closestPtPoly); - if (d < nearestDistanceSqr) - { - if (nearestPt) - dtVcopy(nearestPt, closestPtPoly); - nearestDistanceSqr = d; - nearest = ref; - } - } - - return nearest; -} - int dtNavMeshQuery::queryPolygonsInTile(const dtMeshTile* tile, const float* qmin, const float* qmax, const dtQueryFilter* filter, dtPolyRef* polys, const int maxPolys) const @@ -3347,7 +3306,7 @@ dtStatus dtNavMeshQuery::findDistanceToWall(dtPolyRef startRef, const float* cen dtVsub(hitNormal, centerPos, hitPos); dtVnormalize(hitNormal); - *hitDist = dtSqrt(radiusSqr); + *hitDist = sqrtf(radiusSqr); return status; } diff --git a/dep/recastnavigation/Detour/DetourNode.cpp b/dep/recastnavigation/Detour/Source/DetourNode.cpp index 4c8215e20d0..4c8215e20d0 100644 --- a/dep/recastnavigation/Detour/DetourNode.cpp +++ b/dep/recastnavigation/Detour/Source/DetourNode.cpp diff --git a/dep/recastnavigation/Readme.txt b/dep/recastnavigation/Readme.txt index 0c2f7b1675f..1383b01d582 100644 --- a/dep/recastnavigation/Readme.txt +++ b/dep/recastnavigation/Readme.txt @@ -32,9 +32,6 @@ the regions as simple polygons. The toolset code is located in the Recast folder and demo application using the Recast toolset is located in the RecastDemo folder. -The project files with this distribution can be compiled with Microsoft Visual C++ 2008 -(you can download it for free) and XCode 3.1. - Detour @@ -43,78 +40,7 @@ Recast is accompanied with Detour, path-finding and spatial reasoning toolkit. Y Detour offers simple static navigation mesh which is suitable for many simple cases, as well as tiled navigation mesh which allows you to plug in and out pieces of the mesh. The tiled mesh allows to create systems where you stream new navigation data in and out as the player progresses the level, or you may regenerate tiles as the world changes. -Latest code available at http://code.google.com/p/recastnavigation/ - - --- - -Release Notes - ----------------- -* Recast 1.4 - Released August 24th, 2009 - -- Added detail height mesh generation (RecastDetailMesh.cpp) for single, - tiled statmeshes as well as tilemesh. -- Added feature to contour tracing which detects extra vertices along - tile edges which should be removed later. -- Changed the tiled stat mesh preprocess, so that it first generated - polymeshes per tile and finally combines them. -- Fixed bug in the GUI code where invisible buttons could be pressed. - ----------------- -* Recast 1.31 - Released July 24th, 2009 - -- Better cost and heuristic functions. -- Fixed tile navmesh raycast on tile borders. - ----------------- -* Recast 1.3 - Released July 14th, 2009 - -- Added dtTileNavMesh which allows to dynamically add and remove navmesh pieces at runtime. -- Renamed stat navmesh types to dtStat* (i.e. dtPoly is now dtStatPoly). -- Moved common code used by tile and stat navmesh to DetourNode.h/cpp and DetourCommon.h/cpp. -- Refactores the demo code. - ----------------- -* Recast 1.2 - Released June 17th, 2009 - -- Added tiled mesh generation. The tiled generation allows to generate navigation for - much larger worlds, it removes some of the artifacts that comes from distance fields - in open areas, and allows later streaming and dynamic runtime generation -- Improved and added some debug draw modes -- API change: The helper function rcBuildNavMesh does not exists anymore, - had to change few internal things to cope with the tiled processing, - similar API functionality will be added later once the tiled process matures -- The demo is getting way too complicated, need to split demos -- Fixed several filtering functions so that the mesh is tighter to the geometry, - sometimes there could be up error up to tow voxel units close to walls, - now it should be just one. - ----------------- -* Recast 1.1 - Released April 11th, 2009 - -This is the first release of Detour. - ----------------- -* Recast 1.0 - Released March 29th, 2009 - -This is the first release of Recast. - -The process is not always as robust as I would wish. The watershed phase sometimes swallows tiny islands -which are close to edges. These droppings are handled in rcBuildContours, but the code is not -particularly robust either. - -Another non-robust case is when portal contours (contours shared between two regions) are always -assumed to be straight. That can lead to overlapping contours specially when the level has -large open areas. - - +Latest code available at https://github.com/memononen/recastnavigation Mikko Mononen memon@inside.org diff --git a/dep/recastnavigation/Recast/CMakeLists.txt b/dep/recastnavigation/Recast/CMakeLists.txt index 09f20b4ed2f..f4869bf8773 100644 --- a/dep/recastnavigation/Recast/CMakeLists.txt +++ b/dep/recastnavigation/Recast/CMakeLists.txt @@ -9,18 +9,20 @@ # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. set(Recast_STAT_SRCS - Recast.cpp - RecastAlloc.cpp - RecastArea.cpp - RecastContour.cpp - RecastFilter.cpp - RecastLayers.cpp - RecastMesh.cpp - RecastMeshDetail.cpp - RecastRasterization.cpp - RecastRegion.cpp + Source/Recast.cpp + Source/RecastAlloc.cpp + Source/RecastArea.cpp + Source/RecastContour.cpp + Source/RecastFilter.cpp + Source/RecastLayers.cpp + Source/RecastMesh.cpp + Source/RecastMeshDetail.cpp + Source/RecastRasterization.cpp + Source/RecastRegion.cpp ) +include_directories(Include) + if(WIN32) include_directories( ${CMAKE_SOURCE_DIR}/dep/zlib diff --git a/dep/recastnavigation/Recast/Recast.h b/dep/recastnavigation/Recast/Include/Recast.h index fb36aa4c5cf..3f4ae96d1c9 100644 --- a/dep/recastnavigation/Recast/Recast.h +++ b/dep/recastnavigation/Recast/Include/Recast.h @@ -219,7 +219,7 @@ struct rcConfig int maxEdgeLen; /// The maximum distance a simplfied contour's border edges should deviate - /// the original raw contour. [Limit: >=0] [Units: wu] + /// the original raw contour. [Limit: >=0] [Units: vx] float maxSimplificationError; /// The minimum number of cells allowed to form isolated island areas. [Limit: >=0] [Units: vx] @@ -549,6 +549,11 @@ static const int RC_NOT_CONNECTED = 0x3f; /// @name General helper functions /// @{ +/// Used to ignore a function parameter. VS complains about unused parameters +/// and this silences the warning. +/// @param [in] _ Unused parameter +template<class T> void rcIgnoreUnused(const T&) { } + /// Swaps the values of the two parameters. /// @param[in,out] a Value A /// @param[in,out] b Value B diff --git a/dep/recastnavigation/Recast/RecastAlloc.h b/dep/recastnavigation/Recast/Include/RecastAlloc.h index 438be9ea56b..438be9ea56b 100644 --- a/dep/recastnavigation/Recast/RecastAlloc.h +++ b/dep/recastnavigation/Recast/Include/RecastAlloc.h diff --git a/dep/recastnavigation/Recast/RecastAssert.h b/dep/recastnavigation/Recast/Include/RecastAssert.h index 2aca0d9a14f..2aca0d9a14f 100644 --- a/dep/recastnavigation/Recast/RecastAssert.h +++ b/dep/recastnavigation/Recast/Include/RecastAssert.h diff --git a/dep/recastnavigation/Recast/Recast.cpp b/dep/recastnavigation/Recast/Source/Recast.cpp index 803daac3bcf..b9d86036c3f 100644 --- a/dep/recastnavigation/Recast/Recast.cpp +++ b/dep/recastnavigation/Recast/Source/Recast.cpp @@ -208,12 +208,11 @@ void rcCalcGridSize(const float* bmin, const float* bmax, float cs, int* w, int* /// See the #rcConfig documentation for more information on the configuration parameters. /// /// @see rcAllocHeightfield, rcHeightfield -bool rcCreateHeightfield(rcContext* /*ctx*/, rcHeightfield& hf, int width, int height, +bool rcCreateHeightfield(rcContext* ctx, rcHeightfield& hf, int width, int height, const float* bmin, const float* bmax, float cs, float ch) { - // TODO: VC complains about unref formal variable, figure out a way to handle this better. -// rcAssert(ctx); + rcIgnoreUnused(ctx); hf.width = width; hf.height = height; @@ -245,13 +244,12 @@ static void calcTriNormal(const float* v0, const float* v1, const float* v2, flo /// See the #rcConfig documentation for more information on the configuration parameters. /// /// @see rcHeightfield, rcClearUnwalkableTriangles, rcRasterizeTriangles -void rcMarkWalkableTriangles(rcContext* /*ctx*/, const float walkableSlopeAngle, +void rcMarkWalkableTriangles(rcContext* ctx, const float walkableSlopeAngle, const float* verts, int /*nv*/, const int* tris, int nt, unsigned char* areas) { - // TODO: VC complains about unref formal variable, figure out a way to handle this better. -// rcAssert(ctx); + rcIgnoreUnused(ctx); const float walkableThr = cosf(walkableSlopeAngle/180.0f*RC_PI); @@ -275,13 +273,12 @@ void rcMarkWalkableTriangles(rcContext* /*ctx*/, const float walkableSlopeAngle, /// See the #rcConfig documentation for more information on the configuration parameters. /// /// @see rcHeightfield, rcClearUnwalkableTriangles, rcRasterizeTriangles -void rcClearUnwalkableTriangles(rcContext* /*ctx*/, const float walkableSlopeAngle, +void rcClearUnwalkableTriangles(rcContext* ctx, const float walkableSlopeAngle, const float* verts, int /*nv*/, const int* tris, int nt, unsigned char* areas) { - // TODO: VC complains about unref formal variable, figure out a way to handle this better. -// rcAssert(ctx); + rcIgnoreUnused(ctx); const float walkableThr = cosf(walkableSlopeAngle/180.0f*RC_PI); @@ -297,10 +294,9 @@ void rcClearUnwalkableTriangles(rcContext* /*ctx*/, const float walkableSlopeAng } } -int rcGetHeightFieldSpanCount(rcContext* /*ctx*/, rcHeightfield& hf) +int rcGetHeightFieldSpanCount(rcContext* ctx, rcHeightfield& hf) { - // TODO: VC complains about unref formal variable, figure out a way to handle this better. -// rcAssert(ctx); + rcIgnoreUnused(ctx); const int w = hf.width; const int h = hf.height; diff --git a/dep/recastnavigation/Recast/RecastAlloc.cpp b/dep/recastnavigation/Recast/Source/RecastAlloc.cpp index b5ec1516146..b5ec1516146 100644 --- a/dep/recastnavigation/Recast/RecastAlloc.cpp +++ b/dep/recastnavigation/Recast/Source/RecastAlloc.cpp diff --git a/dep/recastnavigation/Recast/RecastArea.cpp b/dep/recastnavigation/Recast/Source/RecastArea.cpp index 1a338cd9b8c..1a338cd9b8c 100644 --- a/dep/recastnavigation/Recast/RecastArea.cpp +++ b/dep/recastnavigation/Recast/Source/RecastArea.cpp diff --git a/dep/recastnavigation/Recast/RecastContour.cpp b/dep/recastnavigation/Recast/Source/RecastContour.cpp index 5c324bcedfe..5c324bcedfe 100644 --- a/dep/recastnavigation/Recast/RecastContour.cpp +++ b/dep/recastnavigation/Recast/Source/RecastContour.cpp diff --git a/dep/recastnavigation/Recast/RecastFilter.cpp b/dep/recastnavigation/Recast/Source/RecastFilter.cpp index bf985c362c9..bf985c362c9 100644 --- a/dep/recastnavigation/Recast/RecastFilter.cpp +++ b/dep/recastnavigation/Recast/Source/RecastFilter.cpp diff --git a/dep/recastnavigation/Recast/RecastLayers.cpp b/dep/recastnavigation/Recast/Source/RecastLayers.cpp index 5ea6cb79d16..204f72e8cb2 100644 --- a/dep/recastnavigation/Recast/RecastLayers.cpp +++ b/dep/recastnavigation/Recast/Source/RecastLayers.cpp @@ -325,7 +325,7 @@ bool rcBuildHeightfieldLayers(rcContext* ctx, rcCompactHeightfield& chf, continue; // Skip if the height range would become too large. const int ymin = rcMin(root.ymin, regn.ymin); - const int ymax = rcMax(root.ymax, regn.ymax); // Edited by TC + const int ymax = rcMax(root.ymax, regn.ymax); if ((ymax - ymin) >= 255) continue; @@ -373,7 +373,7 @@ bool rcBuildHeightfieldLayers(rcContext* ctx, rcCompactHeightfield& chf, continue; // Skip if the height range would become too large. const int ymin = rcMin(ri.ymin, rj.ymin); - const int ymax = rcMax(ri.ymax, rj.ymax); // Edited by TC + const int ymax = rcMax(ri.ymax, rj.ymax); if ((ymax - ymin) >= 255) continue; diff --git a/dep/recastnavigation/Recast/RecastMesh.cpp b/dep/recastnavigation/Recast/Source/RecastMesh.cpp index 13aad2af01c..8af609b79fb 100644 --- a/dep/recastnavigation/Recast/RecastMesh.cpp +++ b/dep/recastnavigation/Recast/Source/RecastMesh.cpp @@ -661,7 +661,8 @@ static bool removeVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned short } // Remove the polygon. unsigned short* p2 = &mesh.polys[(mesh.npolys-1)*nvp*2]; - memcpy(p,p2,sizeof(unsigned short)*nvp); + if (p != p2) + memcpy(p,p2,sizeof(unsigned short)*nvp); memset(p+nvp,0xff,sizeof(unsigned short)*nvp); mesh.regs[i] = mesh.regs[mesh.npolys-1]; mesh.areas[i] = mesh.areas[mesh.npolys-1]; @@ -861,7 +862,9 @@ static bool removeVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned short unsigned short* pa = &polys[bestPa*nvp]; unsigned short* pb = &polys[bestPb*nvp]; mergePolys(pa, pb, bestEa, bestEb, tmpPoly, nvp); - memcpy(pb, &polys[(npolys-1)*nvp], sizeof(unsigned short)*nvp); + unsigned short* last = &polys[(npolys-1)*nvp]; + if (pb != last) + memcpy(pb, last, sizeof(unsigned short)*nvp); pregs[bestPb] = pregs[npolys-1]; pareas[bestPb] = pareas[npolys-1]; npolys--; @@ -1105,7 +1108,9 @@ bool rcBuildPolyMesh(rcContext* ctx, rcContourSet& cset, const int nvp, rcPolyMe unsigned short* pa = &polys[bestPa*nvp]; unsigned short* pb = &polys[bestPb*nvp]; mergePolys(pa, pb, bestEa, bestEb, tmpPoly, nvp); - memcpy(pb, &polys[(npolys-1)*nvp], sizeof(unsigned short)*nvp); + unsigned short* lastPoly = &polys[(npolys-1)*nvp]; + if (pb != lastPoly) + memcpy(pb, lastPoly, sizeof(unsigned short)*nvp); npolys--; } else @@ -1319,6 +1324,12 @@ bool rcMergePolyMeshes(rcContext* ctx, rcPolyMesh** meshes, const int nmeshes, r const unsigned short ox = (unsigned short)floorf((pmesh->bmin[0]-mesh.bmin[0])/mesh.cs+0.5f); const unsigned short oz = (unsigned short)floorf((pmesh->bmin[2]-mesh.bmin[2])/mesh.cs+0.5f); + bool isMinX = (ox == 0); + bool isMinZ = (oz == 0); + bool isMaxX = ((unsigned short)floorf((mesh.bmax[0] - pmesh->bmax[0]) / mesh.cs + 0.5f)) == 0; + bool isMaxZ = ((unsigned short)floorf((mesh.bmax[2] - pmesh->bmax[2]) / mesh.cs + 0.5f)) == 0; + bool isOnBorder = (isMinX || isMinZ || isMaxX || isMaxZ); + for (int j = 0; j < pmesh->nverts; ++j) { unsigned short* v = &pmesh->verts[j*3]; @@ -1339,6 +1350,36 @@ bool rcMergePolyMeshes(rcContext* ctx, rcPolyMesh** meshes, const int nmeshes, r if (src[k] == RC_MESH_NULL_IDX) break; tgt[k] = vremap[src[k]]; } + + if (isOnBorder) + { + for (int k = mesh.nvp; k < mesh.nvp * 2; ++k) + { + if (src[k] & 0x8000 && src[k] != 0xffff) + { + unsigned short dir = src[k] & 0xf; + switch (dir) + { + case 0: // Portal x- + if (isMinX) + tgt[k] = src[k]; + break; + case 1: // Portal z+ + if (isMaxZ) + tgt[k] = src[k]; + break; + case 2: // Portal x+ + if (isMaxX) + tgt[k] = src[k]; + break; + case 3: // Portal z- + if (isMinZ) + tgt[k] = src[k]; + break; + } + } + } + } } } diff --git a/dep/recastnavigation/Recast/RecastMeshDetail.cpp b/dep/recastnavigation/Recast/Source/RecastMeshDetail.cpp index f49d67400c2..8325b883707 100644 --- a/dep/recastnavigation/Recast/RecastMeshDetail.cpp +++ b/dep/recastnavigation/Recast/Source/RecastMeshDetail.cpp @@ -200,8 +200,8 @@ static unsigned short getHeight(const float fx, const float fy, const float fz, { int ix = (int)floorf(fx*ics + 0.01f); int iz = (int)floorf(fz*ics + 0.01f); - ix = rcClamp(ix-hp.xmin, 0, hp.width); - iz = rcClamp(iz-hp.ymin, 0, hp.height); + ix = rcClamp(ix-hp.xmin, 0, hp.width - 1); + iz = rcClamp(iz-hp.ymin, 0, hp.height - 1); unsigned short h = hp.data[ix+iz*hp.width]; if (h == RC_UNSET_HEIGHT) { @@ -554,7 +554,7 @@ static bool buildPolyDetail(rcContext* ctx, const float* in, const int nin, float dx = vi[0] - vj[0]; float dy = vi[1] - vj[1]; float dz = vi[2] - vj[2]; - float d = rcSqrt(dx*dx + dz*dz); + float d = sqrtf(dx*dx + dz*dz); int nn = 1 + (int)floorf(d/sampleDist); if (nn >= MAX_VERTS_PER_EDGE) nn = MAX_VERTS_PER_EDGE-1; if (nverts+nn >= MAX_VERTS) @@ -741,7 +741,8 @@ static bool buildPolyDetail(rcContext* ctx, const float* in, const int nin, return true; } -static void getHeightData(const rcCompactHeightfield& chf, + +static void getHeightDataSeedsFromVertices(const rcCompactHeightfield& chf, const unsigned short* poly, const int npoly, const unsigned short* verts, const int bs, rcHeightPatch& hp, rcIntArray& stack) @@ -869,8 +870,83 @@ static void getHeightData(const rcCompactHeightfield& chf, int idx = cx-hp.xmin+(cy-hp.ymin)*hp.width; const rcCompactSpan& cs = chf.spans[ci]; hp.data[idx] = cs.y; + + // getHeightData seeds are given in coordinates with borders + stack[i+0] += bs; + stack[i+1] += bs; } +} + + + +static void getHeightData(const rcCompactHeightfield& chf, + const unsigned short* poly, const int npoly, + const unsigned short* verts, const int bs, + rcHeightPatch& hp, rcIntArray& stack, + int region) +{ + // Note: Reads to the compact heightfield are offset by border size (bs) + // since border size offset is already removed from the polymesh vertices. + + stack.resize(0); + memset(hp.data, 0xff, sizeof(unsigned short)*hp.width*hp.height); + + bool empty = true; + + // Copy the height from the same region, and mark region borders + // as seed points to fill the rest. + for (int hy = 0; hy < hp.height; hy++) + { + int y = hp.ymin + hy + bs; + for (int hx = 0; hx < hp.width; hx++) + { + int x = hp.xmin + hx + bs; + const rcCompactCell& c = chf.cells[x+y*chf.width]; + for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) + { + const rcCompactSpan& s = chf.spans[i]; + if (s.reg == region) + { + // Store height + hp.data[hx + hy*hp.width] = s.y; + empty = false; + + // If any of the neighbours is not in same region, + // add the current location as flood fill start + bool border = false; + for (int dir = 0; dir < 4; ++dir) + { + if (rcGetCon(s, dir) != RC_NOT_CONNECTED) + { + const int ax = x + rcGetDirOffsetX(dir); + const int ay = y + rcGetDirOffsetY(dir); + const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(s, dir); + const rcCompactSpan& as = chf.spans[ai]; + if (as.reg != region) + { + border = true; + break; + } + } + } + if (border) + { + stack.push(x); + stack.push(y); + stack.push(i); + } + break; + } + } + } + } + + // if the polygon does not contian any points from the current region (rare, but happens) + // then use the cells closest to the polygon vertices as seeds to fill the height field + if (empty) + getHeightDataSeedsFromVertices(chf, poly, npoly, verts, bs, hp, stack); + static const int RETRACT_SIZE = 256; int head = 0; @@ -895,26 +971,25 @@ static void getHeightData(const rcCompactHeightfield& chf, const int ax = cx + rcGetDirOffsetX(dir); const int ay = cy + rcGetDirOffsetY(dir); + const int hx = ax - hp.xmin - bs; + const int hy = ay - hp.ymin - bs; - if (ax < hp.xmin || ax >= (hp.xmin+hp.width) || - ay < hp.ymin || ay >= (hp.ymin+hp.height)) + if (hx < 0 || hx >= hp.width || hy < 0 || hy >= hp.height) continue; - if (hp.data[ax-hp.xmin+(ay-hp.ymin)*hp.width] != RC_UNSET_HEIGHT) + if (hp.data[hx + hy*hp.width] != RC_UNSET_HEIGHT) continue; - const int ai = (int)chf.cells[(ax+bs)+(ay+bs)*chf.width].index + rcGetCon(cs, dir); - + const int ai = (int)chf.cells[ax + ay*chf.width].index + rcGetCon(cs, dir); const rcCompactSpan& as = chf.spans[ai]; - int idx = ax-hp.xmin+(ay-hp.ymin)*hp.width; - hp.data[idx] = as.y; + + hp.data[hx + hy*hp.width] = as.y; stack.push(ax); stack.push(ay); stack.push(ai); } } - } static unsigned char getEdgeFlags(const float* va, const float* vb, @@ -1072,7 +1147,7 @@ bool rcBuildPolyMeshDetail(rcContext* ctx, const rcPolyMesh& mesh, const rcCompa hp.ymin = bounds[i*4+2]; hp.width = bounds[i*4+1]-bounds[i*4+0]; hp.height = bounds[i*4+3]-bounds[i*4+2]; - getHeightData(chf, p, npoly, mesh.verts, borderSize, hp, stack); + getHeightData(chf, p, npoly, mesh.verts, borderSize, hp, stack, mesh.regs[i]); // Build detail mesh. int nverts = 0; @@ -1242,4 +1317,3 @@ bool rcMergePolyMeshDetails(rcContext* ctx, rcPolyMeshDetail** meshes, const int return true; } - diff --git a/dep/recastnavigation/Recast/RecastRasterization.cpp b/dep/recastnavigation/Recast/Source/RecastRasterization.cpp index d2bb7c98f18..45a7d35bf3e 100644 --- a/dep/recastnavigation/Recast/RecastRasterization.cpp +++ b/dep/recastnavigation/Recast/Source/RecastRasterization.cpp @@ -95,7 +95,7 @@ static void addSpan(rcHeightfield& hf, const int x, const int y, s->area = area; s->next = 0; - // Empty cell, add he first span. + // Empty cell, add the first span. if (!hf.spans[idx]) { hf.spans[idx] = s; @@ -169,36 +169,64 @@ void rcAddSpan(rcContext* /*ctx*/, rcHeightfield& hf, const int x, const int y, addSpan(hf, x,y, smin, smax, area, flagMergeThr); } -static int clipPoly(const float* in, int n, float* out, float pnx, float pnz, float pd) +// divides a convex polygons into two convex polygons on both sides of a line +static void dividePoly(const float* in, int nin, + float* out1, int* nout1, + float* out2, int* nout2, + float x, int axis) { float d[12]; - for (int i = 0; i < n; ++i) - d[i] = pnx*in[i*3+0] + pnz*in[i*3+2] + pd; - - int m = 0; - for (int i = 0, j = n-1; i < n; j=i, ++i) + for (int i = 0; i < nin; ++i) + d[i] = x - in[i*3+axis]; + + int m = 0, n = 0; + for (int i = 0, j = nin-1; i < nin; j=i, ++i) { bool ina = d[j] >= 0; bool inb = d[i] >= 0; if (ina != inb) { float s = d[j] / (d[j] - d[i]); - out[m*3+0] = in[j*3+0] + (in[i*3+0] - in[j*3+0])*s; - out[m*3+1] = in[j*3+1] + (in[i*3+1] - in[j*3+1])*s; - out[m*3+2] = in[j*3+2] + (in[i*3+2] - in[j*3+2])*s; + out1[m*3+0] = in[j*3+0] + (in[i*3+0] - in[j*3+0])*s; + out1[m*3+1] = in[j*3+1] + (in[i*3+1] - in[j*3+1])*s; + out1[m*3+2] = in[j*3+2] + (in[i*3+2] - in[j*3+2])*s; + rcVcopy(out2 + n*3, out1 + m*3); m++; + n++; + // add the i'th point to the right polygon. Do NOT add points that are on the dividing line + // since these were already added above + if (d[i] > 0) + { + rcVcopy(out1 + m*3, in + i*3); + m++; + } + else if (d[i] < 0) + { + rcVcopy(out2 + n*3, in + i*3); + n++; + } } - if (inb) + else // same side { - out[m*3+0] = in[i*3+0]; - out[m*3+1] = in[i*3+1]; - out[m*3+2] = in[i*3+2]; - m++; + // add the i'th point to the right polygon. Addition is done even for points on the dividing line + if (d[i] >= 0) + { + rcVcopy(out1 + m*3, in + i*3); + m++; + if (d[i] != 0) + continue; + } + rcVcopy(out2 + n*3, in + i*3); + n++; } } - return m; + + *nout1 = m; + *nout2 = n; } + + static void rasterizeTri(const float* v0, const float* v1, const float* v2, const unsigned char area, rcHeightfield& hf, const float* bmin, const float* bmax, @@ -222,48 +250,57 @@ static void rasterizeTri(const float* v0, const float* v1, const float* v2, if (!overlapBounds(bmin, bmax, tmin, tmax)) return; - // Calculate the footpring of the triangle on the grid. - int x0 = (int)((tmin[0] - bmin[0])*ics); + // Calculate the footprint of the triangle on the grid's y-axis int y0 = (int)((tmin[2] - bmin[2])*ics); - int x1 = (int)((tmax[0] - bmin[0])*ics); int y1 = (int)((tmax[2] - bmin[2])*ics); - x0 = rcClamp(x0, 0, w-1); y0 = rcClamp(y0, 0, h-1); - x1 = rcClamp(x1, 0, w-1); y1 = rcClamp(y1, 0, h-1); // Clip the triangle into all grid cells it touches. - float in[7*3], out[7*3], inrow[7*3]; + float buf[7*3*4]; + float *in = buf, *inrow = buf+7*3, *p1 = inrow+7*3, *p2 = p1+7*3; + + rcVcopy(&in[0], v0); + rcVcopy(&in[1*3], v1); + rcVcopy(&in[2*3], v2); + int nvrow, nvIn = 3; for (int y = y0; y <= y1; ++y) { - // Clip polygon to row. - rcVcopy(&in[0], v0); - rcVcopy(&in[1*3], v1); - rcVcopy(&in[2*3], v2); - int nvrow = 3; + // Clip polygon to row. Store the remaining polygon as well const float cz = bmin[2] + y*cs; - nvrow = clipPoly(in, nvrow, out, 0, 1, -cz); - if (nvrow < 3) continue; - nvrow = clipPoly(out, nvrow, inrow, 0, -1, cz+cs); + dividePoly(in, nvIn, inrow, &nvrow, p1, &nvIn, cz+cs, 2); + rcSwap(in, p1); if (nvrow < 3) continue; + // find the horizontal bounds in the row + float minX = inrow[0], maxX = inrow[0]; + for (int i=1; i<nvrow; ++i) + { + if (minX > inrow[i*3]) minX = inrow[i*3]; + if (maxX < inrow[i*3]) maxX = inrow[i*3]; + } + int x0 = (int)((minX - bmin[0])*ics); + int x1 = (int)((maxX - bmin[0])*ics); + x0 = rcClamp(x0, 0, w-1); + x1 = rcClamp(x1, 0, w-1); + + int nv, nv2 = nvrow; + for (int x = x0; x <= x1; ++x) { - // Clip polygon to column. - int nv = nvrow; + // Clip polygon to column. store the remaining polygon as well const float cx = bmin[0] + x*cs; - nv = clipPoly(inrow, nv, out, 1, 0, -cx); - if (nv < 3) continue; - nv = clipPoly(out, nv, in, -1, 0, cx+cs); + dividePoly(inrow, nv2, p1, &nv, p2, &nv2, cx+cs, 0); + rcSwap(inrow, p2); if (nv < 3) continue; // Calculate min and max of the span. - float smin = in[1], smax = in[1]; + float smin = p1[1], smax = p1[1]; for (int i = 1; i < nv; ++i) { - smin = rcMin(smin, in[i*3+1]); - smax = rcMax(smax, in[i*3+1]); + smin = rcMin(smin, p1[i*3+1]); + smax = rcMax(smax, p1[i*3+1]); } smin -= bmin[1]; smax -= bmin[1]; diff --git a/dep/recastnavigation/Recast/RecastRegion.cpp b/dep/recastnavigation/Recast/Source/RecastRegion.cpp index 76e631cc5fb..589fac29203 100644 --- a/dep/recastnavigation/Recast/RecastRegion.cpp +++ b/dep/recastnavigation/Recast/Source/RecastRegion.cpp @@ -286,7 +286,10 @@ static bool floodRegion(int x, int y, int i, if (nr & RC_BORDER_REG) // Do not take borders into account. continue; if (nr != 0 && nr != r) + { ar = nr; + break; + } const rcCompactSpan& as = chf.spans[ai]; @@ -300,7 +303,10 @@ static bool floodRegion(int x, int y, int i, continue; unsigned short nr2 = srcReg[ai2]; if (nr2 != 0 && nr2 != r) + { ar = nr2; + break; + } } } } @@ -340,30 +346,44 @@ static unsigned short* expandRegions(int maxIter, unsigned short level, rcCompactHeightfield& chf, unsigned short* srcReg, unsigned short* srcDist, unsigned short* dstReg, unsigned short* dstDist, - rcIntArray& stack) + rcIntArray& stack, + bool fillStack) { const int w = chf.width; const int h = chf.height; - // Find cells revealed by the raised level. - stack.resize(0); - for (int y = 0; y < h; ++y) + if (fillStack) { - for (int x = 0; x < w; ++x) + // Find cells revealed by the raised level. + stack.resize(0); + for (int y = 0; y < h; ++y) { - const rcCompactCell& c = chf.cells[x+y*w]; - for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) + for (int x = 0; x < w; ++x) { - if (chf.dist[i] >= level && srcReg[i] == 0 && chf.areas[i] != RC_NULL_AREA) + const rcCompactCell& c = chf.cells[x+y*w]; + for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) { - stack.push(x); - stack.push(y); - stack.push(i); + if (chf.dist[i] >= level && srcReg[i] == 0 && chf.areas[i] != RC_NULL_AREA) + { + stack.push(x); + stack.push(y); + stack.push(i); + } } } } } - + else // use cells in the input stack + { + // mark all cells which already have a region + for (int j=0; j<stack.size(); j+=3) + { + int i = stack[j+2]; + if (srcReg[i] != 0) + stack[j+2] = -1; + } + } + int iter = 0; while (stack.size() > 0) { @@ -434,6 +454,61 @@ static unsigned short* expandRegions(int maxIter, unsigned short level, } + +static void sortCellsByLevel(unsigned short startLevel, + rcCompactHeightfield& chf, + unsigned short* srcReg, + unsigned int nbStacks, rcIntArray* stacks, + unsigned short loglevelsPerStack) // the levels per stack (2 in our case) as a bit shift +{ + const int w = chf.width; + const int h = chf.height; + startLevel = startLevel >> loglevelsPerStack; + + for (unsigned int j=0; j<nbStacks; ++j) + stacks[j].resize(0); + + // put all cells in the level range into the appropriate stacks + for (int y = 0; y < h; ++y) + { + for (int x = 0; x < w; ++x) + { + const rcCompactCell& c = chf.cells[x+y*w]; + for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) + { + if (chf.areas[i] == RC_NULL_AREA || srcReg[i] != 0) + continue; + + int level = chf.dist[i] >> loglevelsPerStack; + int sId = startLevel - level; + if (sId >= (int)nbStacks) + continue; + if (sId < 0) + sId = 0; + + stacks[sId].push(x); + stacks[sId].push(y); + stacks[sId].push(i); + } + } + } +} + + +static void appendStacks(rcIntArray& srcStack, rcIntArray& dstStack, + unsigned short* srcReg) +{ + for (int j=0; j<srcStack.size(); j+=3) + { + int i = srcStack[j+2]; + if ((i < 0) || (srcReg[i] != 0)) + continue; + dstStack.push(srcStack[j]); + dstStack.push(srcStack[j+1]); + dstStack.push(srcStack[j+2]); + } +} + struct rcRegion { inline rcRegion(unsigned short i) : @@ -1236,7 +1311,13 @@ bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf, } ctx->startTimer(RC_TIMER_BUILD_REGIONS_WATERSHED); - + + const int LOG_NB_STACKS = 3; + const int NB_STACKS = 1 << LOG_NB_STACKS; + rcIntArray lvlStacks[NB_STACKS]; + for (int i=0; i<NB_STACKS; ++i) + lvlStacks[i].resize(1024); + rcIntArray stack(1024); rcIntArray visited(1024); @@ -1271,14 +1352,25 @@ bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf, chf.borderSize = borderSize; } + int sId = -1; while (level > 0) { level = level >= 2 ? level-2 : 0; - + sId = (sId+1) & (NB_STACKS-1); + +// ctx->startTimer(RC_TIMER_DIVIDE_TO_LEVELS); + + if (sId == 0) + sortCellsByLevel(level, chf, srcReg, NB_STACKS, lvlStacks, 1); + else + appendStacks(lvlStacks[sId-1], lvlStacks[sId], srcReg); // copy left overs from last level + +// ctx->stopTimer(RC_TIMER_DIVIDE_TO_LEVELS); + ctx->startTimer(RC_TIMER_BUILD_REGIONS_EXPAND); // Expand current regions until no empty connected cells found. - if (expandRegions(expandIters, level, chf, srcReg, srcDist, dstReg, dstDist, stack) != srcReg) + if (expandRegions(expandIters, level, chf, srcReg, srcDist, dstReg, dstDist, lvlStacks[sId], false) != srcReg) { rcSwap(srcReg, dstReg); rcSwap(srcDist, dstDist); @@ -1289,18 +1381,15 @@ bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf, ctx->startTimer(RC_TIMER_BUILD_REGIONS_FLOOD); // Mark new regions with IDs. - for (int y = 0; y < h; ++y) + for (int j=0; j<lvlStacks[sId].size(); j+=3) { - for (int x = 0; x < w; ++x) + int x = lvlStacks[sId][j]; + int y = lvlStacks[sId][j+1]; + int i = lvlStacks[sId][j+2]; + if (i >= 0 && srcReg[i] == 0) { - const rcCompactCell& c = chf.cells[x+y*w]; - for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) - { - if (chf.dist[i] < level || srcReg[i] != 0 || chf.areas[i] == RC_NULL_AREA) - continue; - if (floodRegion(x, y, i, level, regionId, chf, srcReg, srcDist, stack)) - regionId++; - } + if (floodRegion(x, y, i, level, regionId, chf, srcReg, srcDist, stack)) + regionId++; } } @@ -1308,7 +1397,7 @@ bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf, } // Expand current regions until no empty connected cells found. - if (expandRegions(expandIters*8, 0, chf, srcReg, srcDist, dstReg, dstDist, stack) != srcReg) + if (expandRegions(expandIters*8, 0, chf, srcReg, srcDist, dstReg, dstDist, stack, true) != srcReg) { rcSwap(srcReg, dstReg); rcSwap(srcDist, dstDist); diff --git a/dep/recastnavigation/TODO.txt b/dep/recastnavigation/TODO.txt deleted file mode 100644 index b911c0e4720..00000000000 --- a/dep/recastnavigation/TODO.txt +++ /dev/null @@ -1,20 +0,0 @@ -TODO/Roadmap - -Summer/Autumn 2009 - -- Off mesh links (jump links) -- Area annotations -- Embed extra data per polygon -- Height conforming navmesh - - -Autumn/Winter 2009/2010 - -- Detour path following -- More dynamic example with tile navmesh -- Faster small tile process - - -More info at http://digestingduck.blogspot.com/2009/07/recast-and-detour-roadmap.html - -- diff --git a/dep/recastnavigation/recast_hotfix1.diff b/dep/recastnavigation/recast_hotfix1.diff deleted file mode 100644 index d370b0a68c8..00000000000 --- a/dep/recastnavigation/recast_hotfix1.diff +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/dep/recastnavigation/Detour/DetourNavMesh.h b/dep/recastnavigation/Detour/DetourNavMesh.h -index 52d2c50..99e30c7 100644 ---- a/dep/recastnavigation/Detour/DetourNavMesh.h -+++ b/dep/recastnavigation/Detour/DetourNavMesh.h -@@ -21,7 +21,7 @@ - - #include "DetourAlloc.h" - --#ifdef WIN32 -+#if defined(WIN32) && !defined(__MINGW32__) - typedef unsigned __int64 uint64; - #else - #include <stdint.h> diff --git a/dep/recastnavigation/recastnavigation.diff b/dep/recastnavigation/recastnavigation.diff new file mode 100644 index 00000000000..68e976c955e --- /dev/null +++ b/dep/recastnavigation/recastnavigation.diff @@ -0,0 +1,4066 @@ + Detour/Include/DetourNavMesh.h | 76 +- + Detour/Include/DetourNavMeshQuery.h | 0 + Detour/Include/DetourNode.h | 0 + Detour/Source/DetourCommon.cpp | 4 +- + Detour/Source/DetourNavMesh.cpp | 32 +- + Detour/Source/DetourNavMeshBuilder.cpp | 6 +- + Detour/Source/DetourNavMeshQuery.cpp | 9 +- + Detour/Source/DetourNode.cpp | 29 +- + DetourCrowd/Include/DetourObstacleAvoidance.h | 0 + DetourCrowd/Source/DetourObstacleAvoidance.cpp | 6 +- + Recast/Include/Recast.h | 8 +- + Recast/Source/RecastContour.cpp | 0 + Recast/Source/RecastLayers.cpp | 0 + Recast/Source/RecastMesh.cpp | 0 + Recast/Source/RecastMeshDetail.cpp | 0 + Recast/Source/RecastRegion.cpp | 0 + RecastDemo/Contrib/stb_truetype.h | 3612 ++++++++++++------------ + RecastDemo/Include/NavMeshPruneTool.h | 0 + 18 files changed, 1865 insertions(+), 1917 deletions(-) + +diff --git a/Detour/Include/DetourNavMesh.h b/Detour/Include/DetourNavMesh.h +index 95a63e4..cdd473f 100644 +--- a/Detour/Include/DetourNavMesh.h ++++ b/Detour/Include/DetourNavMesh.h +@@ -22,39 +22,35 @@ + #include "DetourAlloc.h" + #include "DetourStatus.h" + +-// Undefine (or define in a build cofnig) the following line to use 64bit polyref. +-// Generally not needed, useful for very large worlds. +-// Note: tiles build using 32bit refs are not compatible with 64bit refs! +-//#define DT_POLYREF64 1 +- +-#ifdef DT_POLYREF64 +-// TODO: figure out a multiplatform version of uint64_t +-// - maybe: https://code.google.com/p/msinttypes/ +-// - or: http://www.azillionmonkeys.com/qed/pstdint.h ++ ++// Edited by TC ++#if defined(WIN32) && !defined(__MINGW32__) ++/// Do not rename back to uint64. Otherwise mac complains about typedef redefinition ++typedef unsigned __int64 uint64_d; ++#else + #include <stdint.h> ++#ifndef uint64_t ++#ifdef __linux__ ++#include <linux/types.h> ++#endif + #endif ++/// Do not rename back to uint64. Otherwise mac complains about typedef redefinition ++typedef uint64_t uint64_d; ++#endif + + // Note: If you want to use 64-bit refs, change the types of both dtPolyRef & dtTileRef. + // It is also recommended that you change dtHashRef() to a proper 64-bit hash. + ++// Edited by TC ++// We cannot have over 31 bits for either tile nor poly ++// without changing polyCount to use 64bits too. + /// A handle to a polygon within a navigation mesh tile. + /// @ingroup detour +-#ifdef DT_POLYREF64 +-static const unsigned int DT_SALT_BITS = 16; +-static const unsigned int DT_TILE_BITS = 28; +-static const unsigned int DT_POLY_BITS = 20; +-typedef uint64_t dtPolyRef; +-#else +-typedef unsigned int dtPolyRef; +-#endif ++typedef uint64_d dtPolyRef; // Edited by TC + + /// A handle to a tile within a navigation mesh. + /// @ingroup detour +-#ifdef DT_POLYREF64 +-typedef uint64_t dtTileRef; +-#else +-typedef unsigned int dtTileRef; +-#endif ++typedef uint64_d dtTileRef; // Edited by TC + + /// The maximum number of vertices per navigation polygon. + /// @ingroup detour +@@ -94,6 +90,12 @@ static const unsigned int DT_OFFMESH_CON_BIDIR = 1; + /// @ingroup detour + static const int DT_MAX_AREAS = 64; + ++static const int STATIC_SALT_BITS = 12; ++static const int STATIC_TILE_BITS = 21; ++static const int STATIC_POLY_BITS = 31; ++// we cannot have over 31 bits for either tile nor poly ++// without changing polyCount to use 64bits too. ++ + /// Tile flags used for various functions and fields. + /// For an example, see dtNavMesh::addTile(). + enum dtTileFlags +@@ -492,11 +494,7 @@ public: + /// @param[in] ip The index of the polygon within the tile. + inline dtPolyRef encodePolyId(unsigned int salt, unsigned int it, unsigned int ip) const + { +-#ifdef DT_POLYREF64 +- return ((dtPolyRef)salt << (DT_POLY_BITS+DT_TILE_BITS)) | ((dtPolyRef)it << DT_POLY_BITS) | (dtPolyRef)ip; +-#else + return ((dtPolyRef)salt << (m_polyBits+m_tileBits)) | ((dtPolyRef)it << m_polyBits) | (dtPolyRef)ip; +-#endif + } + + /// Decodes a standard polygon reference. +@@ -508,21 +506,12 @@ public: + /// @see #encodePolyId + inline void decodePolyId(dtPolyRef ref, unsigned int& salt, unsigned int& it, unsigned int& ip) const + { +-#ifdef DT_POLYREF64 +- const dtPolyRef saltMask = ((dtPolyRef)1<<DT_SALT_BITS)-1; +- const dtPolyRef tileMask = ((dtPolyRef)1<<DT_TILE_BITS)-1; +- const dtPolyRef polyMask = ((dtPolyRef)1<<DT_POLY_BITS)-1; +- salt = (unsigned int)((ref >> (DT_POLY_BITS+DT_TILE_BITS)) & saltMask); +- it = (unsigned int)((ref >> DT_POLY_BITS) & tileMask); +- ip = (unsigned int)(ref & polyMask); +-#else + const dtPolyRef saltMask = ((dtPolyRef)1<<m_saltBits)-1; + const dtPolyRef tileMask = ((dtPolyRef)1<<m_tileBits)-1; + const dtPolyRef polyMask = ((dtPolyRef)1<<m_polyBits)-1; + salt = (unsigned int)((ref >> (m_polyBits+m_tileBits)) & saltMask); + it = (unsigned int)((ref >> m_polyBits) & tileMask); + ip = (unsigned int)(ref & polyMask); +-#endif + } + + /// Extracts a tile's salt value from the specified polygon reference. +@@ -531,13 +520,8 @@ public: + /// @see #encodePolyId + inline unsigned int decodePolyIdSalt(dtPolyRef ref) const + { +-#ifdef DT_POLYREF64 +- const dtPolyRef saltMask = ((dtPolyRef)1<<DT_SALT_BITS)-1; +- return (unsigned int)((ref >> (DT_POLY_BITS+DT_TILE_BITS)) & saltMask); +-#else + const dtPolyRef saltMask = ((dtPolyRef)1<<m_saltBits)-1; + return (unsigned int)((ref >> (m_polyBits+m_tileBits)) & saltMask); +-#endif + } + + /// Extracts the tile's index from the specified polygon reference. +@@ -546,13 +530,8 @@ public: + /// @see #encodePolyId + inline unsigned int decodePolyIdTile(dtPolyRef ref) const + { +-#ifdef DT_POLYREF64 +- const dtPolyRef tileMask = ((dtPolyRef)1<<DT_TILE_BITS)-1; +- return (unsigned int)((ref >> DT_POLY_BITS) & tileMask); +-#else + const dtPolyRef tileMask = ((dtPolyRef)1<<m_tileBits)-1; + return (unsigned int)((ref >> m_polyBits) & tileMask); +-#endif + } + + /// Extracts the polygon's index (within its tile) from the specified polygon reference. +@@ -561,13 +540,8 @@ public: + /// @see #encodePolyId + inline unsigned int decodePolyIdPoly(dtPolyRef ref) const + { +-#ifdef DT_POLYREF64 +- const dtPolyRef polyMask = ((dtPolyRef)1<<DT_POLY_BITS)-1; +- return (unsigned int)(ref & polyMask); +-#else + const dtPolyRef polyMask = ((dtPolyRef)1<<m_polyBits)-1; + return (unsigned int)(ref & polyMask); +-#endif + } + + /// @} +@@ -626,11 +600,9 @@ private: + dtMeshTile* m_nextFree; ///< Freelist of tiles. + dtMeshTile* m_tiles; ///< List of tiles. + +-#ifndef DT_POLYREF64 + unsigned int m_saltBits; ///< Number of salt bits in the tile ID. + unsigned int m_tileBits; ///< Number of tile bits in the tile ID. + unsigned int m_polyBits; ///< Number of poly bits in the tile ID. +-#endif + }; + + /// Allocates a navigation mesh object using the Detour allocator. +diff --git a/Detour/Source/DetourCommon.cpp b/Detour/Source/DetourCommon.cpp +index a98d8c8..b5700f5 100644 +--- a/Detour/Source/DetourCommon.cpp ++++ b/Detour/Source/DetourCommon.cpp +@@ -16,14 +16,14 @@ + // 3. This notice may not be removed or altered from any source distribution. + // + ++#include <math.h> + #include "DetourCommon.h" +-#include "DetourMath.h" + + ////////////////////////////////////////////////////////////////////////////////////////// + + float dtSqrt(float x) + { +- return dtMathSqrtf(x); ++ return sqrtf(x); + } + + void dtClosestPtPointTriangle(float* closest, const float* p, +diff --git a/Detour/Source/DetourNavMesh.cpp b/Detour/Source/DetourNavMesh.cpp +index 9d627be..5174050 100644 +--- a/Detour/Source/DetourNavMesh.cpp ++++ b/Detour/Source/DetourNavMesh.cpp +@@ -16,13 +16,13 @@ + // 3. This notice may not be removed or altered from any source distribution. + // + ++#include <math.h> + #include <float.h> + #include <string.h> + #include <stdio.h> + #include "DetourNavMesh.h" + #include "DetourNode.h" + #include "DetourCommon.h" +-#include "DetourMath.h" + #include "DetourAlloc.h" + #include "DetourAssert.h" + #include <new> +@@ -193,13 +193,11 @@ dtNavMesh::dtNavMesh() : + m_tileLutMask(0), + m_posLookup(0), + m_nextFree(0), +- m_tiles(0) ++ m_tiles(0), ++ m_saltBits(0), ++ m_tileBits(0), ++ m_polyBits(0) + { +-#ifndef DT_POLYREF64 +- m_saltBits = 0; +- m_tileBits = 0; +- m_polyBits = 0; +-#endif + memset(&m_params, 0, sizeof(dtNavMeshParams)); + m_orig[0] = 0; + m_orig[1] = 0; +@@ -250,17 +248,11 @@ dtStatus dtNavMesh::init(const dtNavMeshParams* params) + m_nextFree = &m_tiles[i]; + } + +- // Init ID generator values. +-#ifndef DT_POLYREF64 +- m_tileBits = dtIlog2(dtNextPow2((unsigned int)params->maxTiles)); +- m_polyBits = dtIlog2(dtNextPow2((unsigned int)params->maxPolys)); +- // Only allow 31 salt bits, since the salt mask is calculated using 32bit uint and it will overflow. +- m_saltBits = dtMin((unsigned int)31, 32 - m_tileBits - m_polyBits); +- +- if (m_saltBits < 10) +- return DT_FAILURE | DT_INVALID_PARAM; +-#endif +- ++ // Edited by TC ++ m_tileBits = STATIC_TILE_BITS; ++ m_polyBits = STATIC_POLY_BITS; ++ m_saltBits = STATIC_SALT_BITS; ++ + return DT_SUCCESS; + } + +@@ -1242,11 +1234,7 @@ dtStatus dtNavMesh::removeTile(dtTileRef ref, unsigned char** data, int* dataSiz + tile->offMeshCons = 0; + + // Update salt, salt should never be zero. +-#ifdef DT_POLYREF64 +- tile->salt = (tile->salt+1) & ((1<<DT_SALT_BITS)-1); +-#else + tile->salt = (tile->salt+1) & ((1<<m_saltBits)-1); +-#endif + if (tile->salt == 0) + tile->salt++; + +diff --git a/Detour/Source/DetourNavMeshBuilder.cpp b/Detour/Source/DetourNavMeshBuilder.cpp +index 1bf271b..9d8471b 100644 +--- a/Detour/Source/DetourNavMeshBuilder.cpp ++++ b/Detour/Source/DetourNavMeshBuilder.cpp +@@ -16,13 +16,13 @@ + // 3. This notice may not be removed or altered from any source distribution. + // + ++#include <math.h> + #include <stdio.h> + #include <stdlib.h> + #include <string.h> + #include <float.h> + #include "DetourNavMesh.h" + #include "DetourCommon.h" +-#include "DetourMath.h" + #include "DetourNavMeshBuilder.h" + #include "DetourAlloc.h" + #include "DetourAssert.h" +@@ -202,8 +202,8 @@ static int createBVTree(const unsigned short* verts, const int /*nverts*/, + if (z > it.bmax[2]) it.bmax[2] = z; + } + // Remap y +- it.bmin[1] = (unsigned short)dtMathFloorf((float)it.bmin[1]*ch/cs); +- it.bmax[1] = (unsigned short)dtMathCeilf((float)it.bmax[1]*ch/cs); ++ it.bmin[1] = (unsigned short)floorf((float)it.bmin[1]*ch/cs); ++ it.bmax[1] = (unsigned short)ceilf((float)it.bmax[1]*ch/cs); + } + + int curNode = 0; +diff --git a/Detour/Source/DetourNavMeshQuery.cpp b/Detour/Source/DetourNavMeshQuery.cpp +index 2e30464..f1709df 100644 +--- a/Detour/Source/DetourNavMeshQuery.cpp ++++ b/Detour/Source/DetourNavMeshQuery.cpp +@@ -16,13 +16,13 @@ + // 3. This notice may not be removed or altered from any source distribution. + // + ++#include <math.h> + #include <float.h> + #include <string.h> + #include "DetourNavMeshQuery.h" + #include "DetourNavMesh.h" + #include "DetourNode.h" + #include "DetourCommon.h" +-#include "DetourMath.h" + #include "DetourAlloc.h" + #include "DetourAssert.h" + #include <new> +@@ -99,8 +99,9 @@ inline float dtQueryFilter::getCost(const float* pa, const float* pb, + return dtVdist(pa, pb) * m_areaCost[curPoly->getArea()]; + } + #endif +- +-static const float H_SCALE = 0.999f; // Search heuristic scale. ++ ++// Edited by TC ++static const float H_SCALE = 2.0f; // Search heuristic scale. + + + dtNavMeshQuery* dtAllocNavMeshQuery() +@@ -3305,7 +3306,7 @@ dtStatus dtNavMeshQuery::findDistanceToWall(dtPolyRef startRef, const float* cen + dtVsub(hitNormal, centerPos, hitPos); + dtVnormalize(hitNormal); + +- *hitDist = dtMathSqrtf(radiusSqr); ++ *hitDist = sqrtf(radiusSqr); + + return status; + } +diff --git a/Detour/Source/DetourNode.cpp b/Detour/Source/DetourNode.cpp +index 57cb206..4c8215e 100644 +--- a/Detour/Source/DetourNode.cpp ++++ b/Detour/Source/DetourNode.cpp +@@ -22,30 +22,17 @@ + #include "DetourCommon.h" + #include <string.h> + +-#ifdef DT_POLYREF64 +-// From Thomas Wang, https://gist.github.com/badboy/6267743 + inline unsigned int dtHashRef(dtPolyRef a) + { +- a = (~a) + (a << 18); // a = (a << 18) - a - 1; +- a = a ^ (a >> 31); +- a = a * 21; // a = (a + (a << 2)) + (a << 4); +- a = a ^ (a >> 11); +- a = a + (a << 6); +- a = a ^ (a >> 22); +- return (unsigned int)a; ++ // Edited by TC ++ a = (~a) + (a << 18); ++ a = a ^ (a >> 31); ++ a = a * 21; ++ a = a ^ (a >> 11); ++ a = a + (a << 6); ++ a = a ^ (a >> 22); ++ return (unsigned int)a; + } +-#else +-inline unsigned int dtHashRef(dtPolyRef a) +-{ +- a += ~(a<<15); +- a ^= (a>>10); +- a += (a<<3); +- a ^= (a>>6); +- a += ~(a<<11); +- a ^= (a>>16); +- return (unsigned int)a; +-} +-#endif + + ////////////////////////////////////////////////////////////////////////////////////////// + dtNodePool::dtNodePool(int maxNodes, int hashSize) : +diff --git a/DetourCrowd/Source/DetourObstacleAvoidance.cpp b/DetourCrowd/Source/DetourObstacleAvoidance.cpp +index 0fad9ef..d3f90b7 100644 +--- a/DetourCrowd/Source/DetourObstacleAvoidance.cpp ++++ b/DetourCrowd/Source/DetourObstacleAvoidance.cpp +@@ -18,10 +18,10 @@ + + #include "DetourObstacleAvoidance.h" + #include "DetourCommon.h" +-#include "DetourMath.h" + #include "DetourAlloc.h" + #include "DetourAssert.h" + #include <string.h> ++#include <math.h> + #include <float.h> + #include <new> + +@@ -58,7 +58,7 @@ static int isectRaySeg(const float* ap, const float* u, + dtVsub(v,bq,bp); + dtVsub(w,ap,bp); + float d = dtVperp2D(u,v); +- if (dtMathFabs(d) < 1e-6f) return 0; ++ if (fabsf(d) < 1e-6f) return 0; + d = 1.0f/d; + t = dtVperp2D(v,w) * d; + if (t < 0 || t > 1) return 0; +@@ -482,7 +482,7 @@ int dtObstacleAvoidanceQuery::sampleVelocityAdaptive(const float* pos, const flo + const int nd = dtClamp(ndivs, 1, DT_MAX_PATTERN_DIVS); + const int nr = dtClamp(nrings, 1, DT_MAX_PATTERN_RINGS); + const float da = (1.0f/nd) * DT_PI*2; +- const float dang = dtMathAtan2f(dvel[2], dvel[0]); ++ const float dang = atan2f(dvel[2], dvel[0]); + + // Always add sample at zero + pat[npat*2+0] = 0; +diff --git a/Recast/Include/Recast.h b/Recast/Include/Recast.h +index 336837e..3f4ae96 100644 +--- a/Recast/Include/Recast.h ++++ b/Recast/Include/Recast.h +@@ -243,7 +243,7 @@ struct rcConfig + }; + + /// Defines the number of bits allocated to rcSpan::smin and rcSpan::smax. +-static const int RC_SPAN_HEIGHT_BITS = 13; ++static const int RC_SPAN_HEIGHT_BITS = 16; // EDITED BY TC + /// Defines the maximum value for rcSpan::smin and rcSpan::smax. + static const int RC_SPAN_MAX_HEIGHT = (1<<RC_SPAN_HEIGHT_BITS)-1; + +@@ -255,9 +255,9 @@ static const int RC_SPANS_PER_POOL = 2048; + /// @see rcHeightfield + struct rcSpan + { +- unsigned int smin : 13; ///< The lower limit of the span. [Limit: < #smax] +- unsigned int smax : 13; ///< The upper limit of the span. [Limit: <= #RC_SPAN_MAX_HEIGHT] +- unsigned int area : 6; ///< The area id assigned to the span. ++ unsigned int smin : 16; ///< The lower limit of the span. [Limit: < #smax] ++ unsigned int smax : 16; ///< The upper limit of the span. [Limit: <= #RC_SPAN_MAX_HEIGHT] ++ unsigned char area; ///< The area id assigned to the span. + rcSpan* next; ///< The next span higher up in column. + }; + +diff --git a/RecastDemo/Contrib/stb_truetype.h b/RecastDemo/Contrib/stb_truetype.h +index fd72578..92dc8c2 100644 +--- a/RecastDemo/Contrib/stb_truetype.h ++++ b/RecastDemo/Contrib/stb_truetype.h +@@ -1,1806 +1,1806 @@ +-// stb_truetype.h - v0.3 - public domain - 2009 Sean Barrett / RAD Game Tools +-// +-// This library processes TrueType files: +-// parse files +-// extract glyph metrics +-// extract glyph shapes +-// render glyphs to one-channel bitmaps with antialiasing (box filter) +-// +-// Todo: +-// non-MS cmaps +-// crashproof on bad data +-// hinting +-// subpixel positioning when rendering bitmap +-// cleartype-style AA +-// +-// ADDITIONAL CONTRIBUTORS +-// +-// Mikko Mononen: compound shape support, more cmap formats +-// +-// VERSIONS +-// +-// 0.3 (2009-06-24) cmap fmt=12, compound shapes (MM) +-// userdata, malloc-from-userdata, non-zero fill (STB) +-// 0.2 (2009-03-11) Fix unsigned/signed char warnings +-// 0.1 (2009-03-09) First public release +-// +-// USAGE +-// +-// Include this file in whatever places neeed to refer to it. In ONE C/C++ +-// file, write: +-// #define STB_TRUETYPE_IMPLEMENTATION +-// before the #include of this file. This expands out the actual +-// implementation into that C/C++ file. +-// +-// Look at the header-file sections below for the API, but here's a quick skim: +-// +-// Simple 3D API (don't ship this, but it's fine for tools and quick start, +-// and you can cut and paste from it to move to more advanced) +-// stbtt_BakeFontBitmap() -- bake a font to a bitmap for use as texture +-// stbtt_GetBakedQuad() -- compute quad to draw for a given char +-// +-// "Load" a font file from a memory buffer (you have to keep the buffer loaded) +-// stbtt_InitFont() +-// stbtt_GetFontOffsetForIndex() -- use for TTC font collections +-// +-// Render a unicode codepoint to a bitmap +-// stbtt_GetCodepointBitmap() -- allocates and returns a bitmap +-// stbtt_MakeCodepointBitmap() -- renders into bitmap you provide +-// stbtt_GetCodepointBitmapBox() -- how big the bitmap must be +-// +-// Character advance/positioning +-// stbtt_GetCodepointHMetrics() +-// stbtt_GetFontVMetrics() +-// +-// NOTES +-// +-// The system uses the raw data found in the .ttf file without changing it +-// and without building auxiliary data structures. This is a bit inefficient +-// on little-endian systems (the data is big-endian), but assuming you're +-// caching the bitmaps or glyph shapes this shouldn't be a big deal. +-// +-// It appears to be very hard to programmatically determine what font a +-// given file is in a general way. I provide an API for this, but I don't +-// recommend it. +-// +-// +-// SOURCE STATISTICS (based on v0.3, 1800 LOC) +-// +-// Documentation & header file 350 LOC \___ 500 LOC documentation +-// Sample code 140 LOC / +-// Truetype parsing 580 LOC ---- 600 LOC TrueType +-// Software rasterization 240 LOC \ . +-// Curve tesselation 120 LOC \__ 500 LOC Bitmap creation +-// Bitmap management 70 LOC / +-// Baked bitmap interface 70 LOC / +-// Font name matching & access 150 LOC ---- 150 +-// C runtime library abstraction 60 LOC ---- 60 +- +- +-////////////////////////////////////////////////////////////////////////////// +-////////////////////////////////////////////////////////////////////////////// +-//// +-//// SAMPLE PROGRAMS +-//// +-// +-// Incomplete text-in-3d-api example, which draws quads properly aligned to be lossless +-// +-#if 0 +-#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation +-#include "stb_truetype.h" +- +-char ttf_buffer[1<<20]; +-unsigned char temp_bitmap[512*512]; +- +-stbtt_chardata cdata[96]; // ASCII 32..126 is 95 glyphs +-GLstbtt_uint ftex; +- +-void my_stbtt_initfont(void) +-{ +- fread(ttf_buffer, 1, 1<<20, fopen("c:/windows/fonts/times.ttf", "rb")); +- stbtt_BakeFontBitmap(data,0, 32.0, temp_bitmap,512,512, 32,96, cdata); // no guarantee this fits! +- // can free ttf_buffer at this point +- glGenTextures(1, &ftex); +- glBindTexture(GL_TEXTURE_2D, ftex); +- glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 512,512, 0, GL_ALPHA, GL_UNSIGNED_BYTE, temp_bitmap); +- // can free temp_bitmap at this point +- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); +-} +- +-void my_stbtt_print(float x, float y, char *text) +-{ +- // assume orthographic projection with units = screen pixels, origin at top left +- glBindTexture(GL_TEXTURE_2D, ftex); +- glBegin(GL_QUADS); +- while (*text) { +- if (*text >= 32 && *text < 128) { +- stbtt_aligned_quad q; +- stbtt_GetBakedQuad(cdata, 512,512, *text-32, &x,&y,&q,1);//1=opengl,0=old d3d +- glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y0); +- glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y0); +- glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y1); +- glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y1); +- } +- ++text; +- } +- glEnd(); +-} +-#endif +-// +-// +-////////////////////////////////////////////////////////////////////////////// +-// +-// Complete program (this compiles): get a single bitmap, print as ASCII art +-// +-#if 0 +-#include <stdio.h> +-#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation +-#include "stb_truetype.h" +- +-char ttf_buffer[1<<25]; +- +-int main(int argc, char **argv) +-{ +- stbtt_fontinfo font; +- unsigned char *bitmap; +- int w,h,i,j,c = (argc > 1 ? atoi(argv[1]) : 'a'), s = (argc > 2 ? atoi(argv[2]) : 20); +- +- fread(ttf_buffer, 1, 1<<25, fopen(argc > 3 ? argv[3] : "c:/windows/fonts/arialbd.ttf", "rb")); +- +- stbtt_InitFont(&font, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer,0)); +- bitmap = stbtt_GetCodepointBitmap(&font, 0,stbtt_ScaleForPixelHeight(&font, s), c, &w, &h, 0,0); +- +- for (j=0; j < h; ++j) { +- for (i=0; i < w; ++i) +- putchar(" .:ioVM@"[bitmap[j*w+i]>>5]); +- putchar('\n'); +- } +- return 0; +-} +-#endif +-// +-// Output: +-// +-// .ii. +-// @@@@@@. +-// V@Mio@@o +-// :i. V@V +-// :oM@@M +-// :@@@MM@M +-// @@o o@M +-// :@@. M@M +-// @@@o@@@@ +-// :M@@V:@@. +-// +-////////////////////////////////////////////////////////////////////////////// +-// +-// Complete program: print "Hello World!" banner, with bugs +-// +-#if 0 +-int main(int arg, char **argv) +-{ +- unsigned char screen[20][79]; +- int i,j, pos=0; +- float scale; +- char *text = "Heljo World!"; +- +- fread(buffer, 1, 1000000, fopen("c:/windows/fonts/arialbd.ttf", "rb")); +- stbtt_InitFont(&font, buffer, 0); +- +- scale = stbtt_ScaleForPixelHeight(&font, 16); +- memset(screen, 0, sizeof(screen)); +- +- while (*text) { +- int advance,lsb,x0,y0,x1,y1, newpos, baseline=13; +- stbtt_GetCodepointHMetrics(&font, *text, &advance, &lsb); +- stbtt_GetCodepointBitmapBox(&font, *text, scale,scale, &x0,&y0,&x1,&y1); +- newpos = pos + (int) (lsb * scale) + x0; +- stbtt_MakeCodepointBitmap(&font, &screen[baseline + y0][newpos], x1-x0,y1-y0, 79, scale,scale, *text); +- // note that this stomps the old data, so where character boxes overlap (e.g. 'lj') it's wrong +- // because this API is really for baking character bitmaps into textures +- pos += (int) (advance * scale); +- ++text; +- } +- +- for (j=0; j < 20; ++j) { +- for (i=0; i < 79; ++i) +- putchar(" .:ioVM@"[screen[j][i]>>5]); +- putchar('\n'); +- } +- +- return 0; +-} +-#endif +- +- +-////////////////////////////////////////////////////////////////////////////// +-////////////////////////////////////////////////////////////////////////////// +-//// +-//// INTEGRATION WITH RUNTIME LIBRARIES +-//// +- +-#ifdef STB_TRUETYPE_IMPLEMENTATION +- // #define your own (u)stbtt_int8/16/32 before including to override this +- #ifndef stbtt_uint8 +- typedef unsigned char stbtt_uint8; +- typedef signed char stbtt_int8; +- typedef unsigned short stbtt_uint16; +- typedef signed short stbtt_int16; +- typedef unsigned int stbtt_uint32; +- typedef signed int stbtt_int32; +- #endif +- +- typedef char stbtt__check_size32[sizeof(stbtt_int32)==4 ? 1 : -1]; +- typedef char stbtt__check_size16[sizeof(stbtt_int16)==2 ? 1 : -1]; +- +- // #define your own STBTT_sort() to override this to avoid qsort +- #ifndef STBTT_sort +- #include <stdlib.h> +- #define STBTT_sort(data,num_items,item_size,compare_func) qsort(data,num_items,item_size,compare_func) +- #endif +- +- // #define your own STBTT_ifloor/STBTT_iceil() to avoid math.h +- #ifndef STBTT_ifloor +- #include <math.h> +- #define STBTT_ifloor(x) ((int) floor(x)) +- #define STBTT_iceil(x) ((int) ceil(x)) +- #endif +- +- // #define your own functions "STBTT_malloc" / "STBTT_free" to avoid malloc.h +- #ifndef STBTT_malloc +- #include <malloc.h> +- #define STBTT_malloc(x,u) malloc(x) +- #define STBTT_free(x,u) free(x) +- #endif +- +- #ifndef STBTT_assert +- #include <assert.h> +- #define STBTT_assert(x) assert(x) +- #endif +- +- #ifndef STBTT_strlen +- #include <string.h> +- #define STBTT_strlen(x) strlen(x) +- #endif +- +- #ifndef STBTT_memcpy +- #include <memory.h> +- #define STBTT_memcpy memcpy +- #define STBTT_memset memset +- #endif +-#endif +- +-/////////////////////////////////////////////////////////////////////////////// +-/////////////////////////////////////////////////////////////////////////////// +-//// +-//// INTERFACE +-//// +-//// +- +-#ifndef __STB_INCLUDE_STB_TRUETYPE_H__ +-#define __STB_INCLUDE_STB_TRUETYPE_H__ +- +-#ifdef __cplusplus +-extern "C" { +-#endif +- +-////////////////////////////////////////////////////////////////////////////// +-// +-// TEXTURE BAKING API +-// +-// If you use this API, you only have to call two functions ever. +-// +- +-typedef struct +-{ +- unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap +- float xoff,yoff,xadvance; +-} stbtt_bakedchar; +- +-extern int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) +- float pixel_height, // height of font in pixels +- unsigned char *pixels, int pw, int ph, // bitmap to be filled in +- int first_char, int num_chars, // characters to bake +- stbtt_bakedchar *chardata); // you allocate this, it's num_chars long +-// if return is positive, the first unused row of the bitmap +-// if return is negative, returns the negative of the number of characters that fit +-// if return is 0, no characters fit and no rows were used +-// This uses a very crappy packing. +- +-typedef struct +-{ +- float x0,y0,s0,t0; // top-left +- float x1,y1,s1,t1; // bottom-right +-} stbtt_aligned_quad; +- +-extern void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, // same data as above +- int char_index, // character to display +- float *xpos, float *ypos, // pointers to current position in screen pixel space +- stbtt_aligned_quad *q, // output: quad to draw +- int opengl_fillrule); // true if opengl fill rule; false if DX9 or earlier +-// Call GetBakedQuad with char_index = 'character - first_char', and it +-// creates the quad you need to draw and advances the current position. +-// It's inefficient; you might want to c&p it and optimize it. +- +- +-////////////////////////////////////////////////////////////////////////////// +-// +-// FONT LOADING +-// +-// +- +-extern int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index); +-// Each .ttf file may have more than one font. Each has a sequential index +-// number starting from 0. Call this function to get the font offset for a +-// given index; it returns -1 if the index is out of range. A regular .ttf +-// file will only define one font and it always be at offset 0, so it will +-// return '0' for index 0, and -1 for all other indices. You can just skip +-// this step if you know it's that kind of font. +- +- +-// The following structure is defined publically so you can declare one on +-// the stack or as a global or etc. +-typedef struct +-{ +- void *userdata; +- unsigned char *data; // pointer to .ttf file +- int fontstart; // offset of start of font +- +- int numGlyphs; // number of glyphs, needed for range checking +- +- int loca,head,glyf,hhea,hmtx; // table locations as offset from start of .ttf +- int index_map; // a cmap mapping for our chosen character encoding +- int indexToLocFormat; // format needed to map from glyph index to glyph +-} stbtt_fontinfo; +- +-extern int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset); +-// Given an offset into the file that defines a font, this function builds +-// the necessary cached info for the rest of the system. You must allocate +-// the stbtt_fontinfo yourself, and stbtt_InitFont will fill it out. You don't +-// need to do anything special to free it, because the contents are a pure +-// cache with no additional data structures. Returns 0 on failure. +- +- +-////////////////////////////////////////////////////////////////////////////// +-// +-// CHARACTER TO GLYPH-INDEX CONVERSIOn +- +-int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint); +-// If you're going to perform multiple operations on the same character +-// and you want a speed-up, call this function with the character you're +-// going to process, then use glyph-based functions instead of the +-// codepoint-based functions. +- +- +-////////////////////////////////////////////////////////////////////////////// +-// +-// CHARACTER PROPERTIES +-// +- +-extern float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float pixels); +-// computes a scale factor to produce a font whose "height" is 'pixels' tall. +-// Height is measured as the distance from the highest ascender to the lowest +-// descender; in other words, it's equivalent to calling stbtt_GetFontVMetrics +-// and computing: +-// scale = pixels / (ascent - descent) +-// so if you prefer to measure height by the ascent only, use a similar calculation. +- +-extern void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap); +-// ascent is the coordinate above the baseline the font extends; descent +-// is the coordinate below the baseline the font extends (i.e. it is typically negative) +-// lineGap is the spacing between one row's descent and the next row's ascent... +-// so you should advance the vertical position by "*ascent - *descent + *lineGap" +-// these are expressed in unscaled coordinates +- +-extern void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing); +-// leftSideBearing is the offset from the current horizontal position to the left edge of the character +-// advanceWidth is the offset from the current horizontal position to the next horizontal position +-// these are expressed in unscaled coordinates +- +-extern int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2); +-// an additional amount to add to the 'advance' value between ch1 and ch2 +-// @TODO; for now always returns 0! +- +-extern int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1); +-// Gets the bounding box of the visible part of the glyph, in unscaled coordinates +- +-extern void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing); +-extern int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2); +-extern int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1); +-// as above, but takes one or more glyph indices for greater efficiency +- +- +-////////////////////////////////////////////////////////////////////////////// +-// +-// GLYPH SHAPES (you probably don't need these, but they have to go before +-// the bitmaps for C declaration-order reasons) +-// +- +-#ifndef STBTT_vmove // you can predefine these to use different values (but why?) +- enum { +- STBTT_vmove=1, +- STBTT_vline, +- STBTT_vcurve +- }; +-#endif +- +-#ifndef stbtt_vertex // you can predefine this to use different values +- // (we share this with other code at RAD) +- #define stbtt_vertex_type short // can't use stbtt_int16 because that's not visible in the header file +- typedef struct +- { +- stbtt_vertex_type x,y,cx,cy; +- unsigned char type,padding; +- } stbtt_vertex; +-#endif +- +-extern int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices); +-extern int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **vertices); +-// returns # of vertices and fills *vertices with the pointer to them +-// these are expressed in "unscaled" coordinates +- +-extern void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices); +-// frees the data allocated above +- +-////////////////////////////////////////////////////////////////////////////// +-// +-// BITMAP RENDERING +-// +- +-extern void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata); +-// frees the bitmap allocated below +- +-extern unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff); +-// allocates a large-enough single-channel 8bpp bitmap and renders the +-// specified character/glyph at the specified scale into it, with +-// antialiasing. 0 is no coverage (transparent), 255 is fully covered (opaque). +-// *width & *height are filled out with the width & height of the bitmap, +-// which is stored left-to-right, top-to-bottom. +-// +-// xoff/yoff are the offset it pixel space from the glyph origin to the top-left of the bitmap +- +-extern void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint); +-// the same as above, but you pass in storage for the bitmap in the form +-// of 'output', with row spacing of 'out_stride' bytes. the bitmap is +-// clipped to out_w/out_h bytes. call the next function to get the +-// height and width and positioning info +- +-extern void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); +-// get the bbox of the bitmap centered around the glyph origin; so the +-// bitmap width is ix1-ix0, height is iy1-iy0, and location to place +-// the bitmap top left is (leftSideBearing*scale,iy0). +-// (Note that the bitmap uses y-increases-down, but the shape uses +-// y-increases-up, so CodepointBitmapBox and CodepointBox are inverted.) +- +-extern unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff); +-extern void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); +-extern void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph); +- +-//extern void stbtt_get_true_bbox(stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); +- +-// @TODO: don't expose this structure +-typedef struct +-{ +- int w,h,stride; +- unsigned char *pixels; +-} stbtt__bitmap; +- +-extern void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, int x_off, int y_off, int invert, void *userdata); +- +-////////////////////////////////////////////////////////////////////////////// +-// +-// Finding the right font... +-// +-// You should really just solve this offline, keep your own tables +-// of what font is what, and don't try to get it out of the .ttf file. +-// That's because getting it out of the .ttf file is really hard, because +-// the names in the file can appear in many possible encodings, in many +-// possible languages, and e.g. if you need a case-insensitive comparison, +-// the details of that depend on the encoding & language in a complex way +-// (actually underspecified in truetype, but also gigantic). +-// +-// But you can use the provided functions in two possible ways: +-// stbtt_FindMatchingFont() will use *case-sensitive* comparisons on +-// unicode-encoded names to try to find the font you want; +-// you can run this before calling stbtt_InitFont() +-// +-// stbtt_GetFontNameString() lets you get any of the various strings +-// from the file yourself and do your own comparisons on them. +-// You have to have called stbtt_InitFont() first. +- +- +-extern int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags); +-// returns the offset (not index) of the font that matches, or -1 if none +-// if you use STBTT_MACSTYLE_DONTCARE, use a font name like "Arial Bold". +-// if you use any other flag, use a font name like "Arial"; this checks +-// the 'macStyle' header field; i don't know if fonts set this consistently +-#define STBTT_MACSTYLE_DONTCARE 0 +-#define STBTT_MACSTYLE_BOLD 1 +-#define STBTT_MACSTYLE_ITALIC 2 +-#define STBTT_MACSTYLE_UNDERSCORE 4 +-#define STBTT_MACSTYLE_NONE 8 // <= not same as 0, this makes us check the bitfield is 0 +- +-extern int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2); +-// returns 1/0 whether the first string interpreted as utf8 is identical to +-// the second string interpreted as big-endian utf16... useful for strings from next func +- +-extern char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID); +-// returns the string (which may be big-endian double byte, e.g. for unicode) +-// and puts the length in bytes in *length. +-// +-// some of the values for the IDs are below; for more see the truetype spec: +-// http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html +-// http://www.microsoft.com/typography/otspec/name.htm +- +-enum { // platformID +- STBTT_PLATFORM_ID_UNICODE =0, +- STBTT_PLATFORM_ID_MAC =1, +- STBTT_PLATFORM_ID_ISO =2, +- STBTT_PLATFORM_ID_MICROSOFT =3 +-}; +- +-enum { // encodingID for STBTT_PLATFORM_ID_UNICODE +- STBTT_UNICODE_EID_UNICODE_1_0 =0, +- STBTT_UNICODE_EID_UNICODE_1_1 =1, +- STBTT_UNICODE_EID_ISO_10646 =2, +- STBTT_UNICODE_EID_UNICODE_2_0_BMP=3, +- STBTT_UNICODE_EID_UNICODE_2_0_FULL=4, +-}; +- +-enum { // encodingID for STBTT_PLATFORM_ID_MICROSOFT +- STBTT_MS_EID_SYMBOL =0, +- STBTT_MS_EID_UNICODE_BMP =1, +- STBTT_MS_EID_SHIFTJIS =2, +- STBTT_MS_EID_UNICODE_FULL =10, +-}; +- +-enum { // encodingID for STBTT_PLATFORM_ID_MAC; same as Script Manager codes +- STBTT_MAC_EID_ROMAN =0, STBTT_MAC_EID_ARABIC =4, +- STBTT_MAC_EID_JAPANESE =1, STBTT_MAC_EID_HEBREW =5, +- STBTT_MAC_EID_CHINESE_TRAD =2, STBTT_MAC_EID_GREEK =6, +- STBTT_MAC_EID_KOREAN =3, STBTT_MAC_EID_RUSSIAN =7, +-}; +- +-enum { // languageID for STBTT_PLATFORM_ID_MICROSOFT; same as LCID... +- // problematic because there are e.g. 16 english LCIDs and 16 arabic LCIDs +- STBTT_MS_LANG_ENGLISH =0x0409, STBTT_MS_LANG_ITALIAN =0x0410, +- STBTT_MS_LANG_CHINESE =0x0804, STBTT_MS_LANG_JAPANESE =0x0411, +- STBTT_MS_LANG_DUTCH =0x0413, STBTT_MS_LANG_KOREAN =0x0412, +- STBTT_MS_LANG_FRENCH =0x040c, STBTT_MS_LANG_RUSSIAN =0x0419, +- STBTT_MS_LANG_GERMAN =0x0407, STBTT_MS_LANG_SPANISH =0x0409, +- STBTT_MS_LANG_HEBREW =0x040d, STBTT_MS_LANG_SWEDISH =0x041D, +-}; +- +-enum { // languageID for STBTT_PLATFORM_ID_MAC +- STBTT_MAC_LANG_ENGLISH =0 , STBTT_MAC_LANG_JAPANESE =11, +- STBTT_MAC_LANG_ARABIC =12, STBTT_MAC_LANG_KOREAN =23, +- STBTT_MAC_LANG_DUTCH =4 , STBTT_MAC_LANG_RUSSIAN =32, +- STBTT_MAC_LANG_FRENCH =1 , STBTT_MAC_LANG_SPANISH =6 , +- STBTT_MAC_LANG_GERMAN =2 , STBTT_MAC_LANG_SWEDISH =5 , +- STBTT_MAC_LANG_HEBREW =10, STBTT_MAC_LANG_CHINESE_SIMPLIFIED =33, +- STBTT_MAC_LANG_ITALIAN =3 , STBTT_MAC_LANG_CHINESE_TRAD =19, +-}; +- +-#ifdef __cplusplus +-} +-#endif +- +-#endif // __STB_INCLUDE_STB_TRUETYPE_H__ +- +-/////////////////////////////////////////////////////////////////////////////// +-/////////////////////////////////////////////////////////////////////////////// +-//// +-//// IMPLEMENTATION +-//// +-//// +- +-#ifdef STB_TRUETYPE_IMPLEMENTATION +- +-////////////////////////////////////////////////////////////////////////// +-// +-// accessors to parse data from file +-// +- +-// on platforms that don't allow misaligned reads, if we want to allow +-// truetype fonts that aren't padded to alignment, define ALLOW_UNALIGNED_TRUETYPE +- +-#define ttBYTE(p) (* (stbtt_uint8 *) (p)) +-#define ttCHAR(p) (* (stbtt_int8 *) (p)) +-#define ttFixed(p) ttLONG(p) +- +-#if defined(STB_TRUETYPE_BIGENDIAN) && !defined(ALLOW_UNALIGNED_TRUETYPE) +- +- #define ttUSHORT(p) (* (stbtt_uint16 *) (p)) +- #define ttSHORT(p) (* (stbtt_int16 *) (p)) +- #define ttULONG(p) (* (stbtt_uint32 *) (p)) +- #define ttLONG(p) (* (stbtt_int32 *) (p)) +- +-#else +- +- stbtt_uint16 ttUSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; } +- stbtt_int16 ttSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; } +- stbtt_uint32 ttULONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } +- stbtt_int32 ttLONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } +- +-#endif +- +-#define stbtt_tag4(p,c0,c1,c2,c3) ((p)[0] == (c0) && (p)[1] == (c1) && (p)[2] == (c2) && (p)[3] == (c3)) +-#define stbtt_tag(p,str) stbtt_tag4(p,str[0],str[1],str[2],str[3]) +- +-static int stbtt__isfont(const stbtt_uint8 *font) +-{ +- // check the version number +- if (stbtt_tag(font, "1")) return 1; // TrueType 1 +- if (stbtt_tag(font, "typ1")) return 1; // TrueType with type 1 font -- we don't support this! +- if (stbtt_tag(font, "OTTO")) return 1; // OpenType with CFF +- if (stbtt_tag4(font, 0,1,0,0)) return 1; // OpenType 1.0 +- return 0; +-} +- +-// @OPTIMIZE: binary search +-static stbtt_uint32 stbtt__find_table(stbtt_uint8 *data, stbtt_uint32 fontstart, const char *tag) +-{ +- stbtt_int32 num_tables = ttUSHORT(data+fontstart+4); +- stbtt_uint32 tabledir = fontstart + 12; +- stbtt_int32 i; +- for (i=0; i < num_tables; ++i) { +- stbtt_uint32 loc = tabledir + 16*i; +- if (stbtt_tag(data+loc+0, tag)) +- return ttULONG(data+loc+8); +- } +- return 0; +-} +- +-int stbtt_GetFontOffsetForIndex(const unsigned char *font_collection, int index) +-{ +- // if it's just a font, there's only one valid index +- if (stbtt__isfont(font_collection)) +- return index == 0 ? 0 : -1; +- +- // check if it's a TTC +- if (stbtt_tag(font_collection, "ttcf")) { +- // version 1? +- if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) { +- stbtt_int32 n = ttLONG(font_collection+8); +- if (index >= n) +- return -1; +- return ttULONG(font_collection+12+index*14); +- } +- } +- return -1; +-} +- +-int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data2, int fontstart) +-{ +- stbtt_uint8 *data = (stbtt_uint8 *) data2; +- stbtt_uint32 cmap, t; +- stbtt_int32 i,numTables; +- +- info->data = data; +- info->fontstart = fontstart; +- +- cmap = stbtt__find_table(data, fontstart, "cmap"); +- info->loca = stbtt__find_table(data, fontstart, "loca"); +- info->head = stbtt__find_table(data, fontstart, "head"); +- info->glyf = stbtt__find_table(data, fontstart, "glyf"); +- info->hhea = stbtt__find_table(data, fontstart, "hhea"); +- info->hmtx = stbtt__find_table(data, fontstart, "hmtx"); +- if (!cmap || !info->loca || !info->head || !info->glyf || !info->hhea || !info->hmtx) +- return 0; +- +- t = stbtt__find_table(data, fontstart, "maxp"); +- if (t) +- info->numGlyphs = ttUSHORT(data+t+4); +- else +- info->numGlyphs = 0xffff; +- +- // find a cmap encoding table we understand *now* to avoid searching +- // later. (todo: could make this installable) +- // the same regardless of glyph. +- numTables = ttUSHORT(data + cmap + 2); +- info->index_map = 0; +- for (i=0; i < numTables; ++i) { +- stbtt_uint32 encoding_record = cmap + 4 + 8 * i; +- // find an encoding we understand: +- switch(ttUSHORT(data+encoding_record)) { +- case STBTT_PLATFORM_ID_MICROSOFT: +- switch (ttUSHORT(data+encoding_record+2)) { +- case STBTT_MS_EID_UNICODE_BMP: +- case STBTT_MS_EID_UNICODE_FULL: +- // MS/Unicode +- info->index_map = cmap + ttULONG(data+encoding_record+4); +- break; +- } +- break; +- } +- } +- if (info->index_map == 0) +- return 0; +- +- info->indexToLocFormat = ttUSHORT(data+info->head + 50); +- return 1; +-} +- +-int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint) +-{ +- stbtt_uint8 *data = info->data; +- stbtt_uint32 index_map = info->index_map; +- +- stbtt_uint16 format = ttUSHORT(data + index_map + 0); +- if (format == 0) { // apple byte encoding +- stbtt_int32 bytes = ttUSHORT(data + index_map + 2); +- if (unicode_codepoint < bytes-6) +- return ttBYTE(data + index_map + 6 + unicode_codepoint); +- return 0; +- } else if (format == 6) { +- stbtt_uint32 first = ttUSHORT(data + index_map + 6); +- stbtt_uint32 count = ttUSHORT(data + index_map + 8); +- if ((stbtt_uint32) unicode_codepoint >= first && (stbtt_uint32) unicode_codepoint < first+count) +- return ttUSHORT(data + index_map + 10 + (unicode_codepoint - first)*2); +- return 0; +- } else if (format == 2) { +- STBTT_assert(0); // @TODO: high-byte mapping for japanese/chinese/korean +- return 0; +- } else if (format == 4) { // standard mapping for windows fonts: binary search collection of ranges +- stbtt_uint16 segcount = ttUSHORT(data+index_map+6) >> 1; +- stbtt_uint16 searchRange = ttUSHORT(data+index_map+8) >> 1; +- stbtt_uint16 entrySelector = ttUSHORT(data+index_map+10); +- stbtt_uint16 rangeShift = ttUSHORT(data+index_map+12) >> 1; +- stbtt_uint16 item, offset, start, end; +- +- // do a binary search of the segments +- stbtt_uint32 endCount = index_map + 14; +- stbtt_uint32 search = endCount; +- +- if (unicode_codepoint > 0xffff) +- return 0; +- +- // they lie from endCount .. endCount + segCount +- // but searchRange is the nearest power of two, so... +- if (unicode_codepoint >= ttUSHORT(data + search + rangeShift*2)) +- search += rangeShift*2; +- +- // now decrement to bias correctly to find smallest +- search -= 2; +- while (entrySelector) { +- stbtt_uint16 start, end; +- searchRange >>= 1; +- start = ttUSHORT(data + search + 2 + segcount*2 + 2); +- end = ttUSHORT(data + search + 2); +- start = ttUSHORT(data + search + searchRange*2 + segcount*2 + 2); +- end = ttUSHORT(data + search + searchRange*2); +- if (unicode_codepoint > end) +- search += searchRange*2; +- --entrySelector; +- } +- search += 2; +- +- item = (stbtt_uint16) ((search - endCount) >> 1); +- +- STBTT_assert(unicode_codepoint <= ttUSHORT(data + endCount + 2*item)); +- start = ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item); +- end = ttUSHORT(data + index_map + 14 + 2 + 2*item); +- if (unicode_codepoint < start) +- return 0; +- +- offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item); +- if (offset == 0) +- return unicode_codepoint + ttSHORT(data + index_map + 14 + segcount*4 + 2 + 2*item); +- +- return ttUSHORT(data + offset + (unicode_codepoint-start)*2 + index_map + 14 + segcount*6 + 2 + 2*item); +- } else if (format == 12) { +- stbtt_uint16 ngroups = ttUSHORT(data+index_map+6); +- stbtt_int32 low,high; +- low = 0; high = (stbtt_int32)ngroups; +- // Binary search the right group. +- while (low <= high) { +- stbtt_int32 mid = low + ((high-low) >> 1); // rounds down, so low <= mid < high +- stbtt_uint32 start_char = ttULONG(data+index_map+16+mid*12); +- stbtt_uint32 end_char = ttULONG(data+index_map+16+mid*12+4); +- if ((stbtt_uint32) unicode_codepoint < start_char) +- high = mid-1; +- else if ((stbtt_uint32) unicode_codepoint > end_char) +- low = mid+1; +- else { +- stbtt_uint32 start_glyph = ttULONG(data+index_map+16+mid*12+8); +- return start_glyph + unicode_codepoint-start_char; +- } +- } +- return 0; // not found +- } +- // @TODO +- STBTT_assert(0); +- return 0; +-} +- +-int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices) +-{ +- return stbtt_GetGlyphShape(info, stbtt_FindGlyphIndex(info, unicode_codepoint), vertices); +-} +- +-static void stbtt_setvertex(stbtt_vertex *v, stbtt_uint8 type, stbtt_int16 x, stbtt_int16 y, stbtt_int16 cx, stbtt_int16 cy) +-{ +- v->type = type; +- v->x = x; +- v->y = y; +- v->cx = cx; +- v->cy = cy; +-} +- +-static int stbtt__GetGlyfOffset(const stbtt_fontinfo *info, int glyph_index) +-{ +- int g1,g2; +- +- if (glyph_index >= info->numGlyphs) return -1; // glyph index out of range +- if (info->indexToLocFormat >= 2) return -1; // unknown index->glyph map format +- +- if (info->indexToLocFormat == 0) { +- g1 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2) * 2; +- g2 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2 + 2) * 2; +- } else { +- g1 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4); +- g2 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4 + 4); +- } +- +- return g1==g2 ? -1 : g1; // if length is 0, return -1 +-} +- +-int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1) +-{ +- int g = stbtt__GetGlyfOffset(info, glyph_index); +- if (g < 0) return 0; +- +- if (x0) *x0 = ttSHORT(info->data + g + 2); +- if (y0) *y0 = ttSHORT(info->data + g + 4); +- if (x1) *x1 = ttSHORT(info->data + g + 6); +- if (y1) *y1 = ttSHORT(info->data + g + 8); +- return 1; +-} +- +-int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1) +-{ +- return stbtt_GetGlyphBox(info, stbtt_FindGlyphIndex(info,codepoint), x0,y0,x1,y1); +-} +- +-int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) +-{ +- stbtt_int16 numberOfContours; +- stbtt_uint8 *endPtsOfContours; +- stbtt_uint8 *data = info->data; +- stbtt_vertex *vertices=0; +- int num_vertices=0; +- int g = stbtt__GetGlyfOffset(info, glyph_index); +- +- *pvertices = NULL; +- +- if (g < 0) return 0; +- +- numberOfContours = ttSHORT(data + g); +- +- if (numberOfContours > 0) { +- stbtt_uint8 flags=0,flagcount; +- stbtt_int32 ins, i,j=0,m,n, next_move, was_off=0, off; +- stbtt_int16 x,y,cx,cy,sx,sy; +- stbtt_uint8 *points; +- endPtsOfContours = (data + g + 10); +- ins = ttUSHORT(data + g + 10 + numberOfContours * 2); +- points = data + g + 10 + numberOfContours * 2 + 2 + ins; +- +- n = 1+ttUSHORT(endPtsOfContours + numberOfContours*2-2); +- +- m = n + numberOfContours; // a loose bound on how many vertices we might need +- vertices = (stbtt_vertex *) STBTT_malloc(m * sizeof(vertices[0]), info->userdata); +- if (vertices == 0) +- return 0; +- +- next_move = 0; +- flagcount=0; +- +- // in first pass, we load uninterpreted data into the allocated array +- // above, shifted to the end of the array so we won't overwrite it when +- // we create our final data starting from the front +- +- off = m - n; // starting offset for uninterpreted data, regardless of how m ends up being calculated +- +- // first load flags +- +- for (i=0; i < n; ++i) { +- if (flagcount == 0) { +- flags = *points++; +- if (flags & 8) +- flagcount = *points++; +- } else +- --flagcount; +- vertices[off+i].type = flags; +- } +- +- // now load x coordinates +- x=0; +- for (i=0; i < n; ++i) { +- flags = vertices[off+i].type; +- if (flags & 2) { +- stbtt_int16 dx = *points++; +- x += (flags & 16) ? dx : -dx; // ??? +- } else { +- if (!(flags & 16)) { +- x = x + (stbtt_int16) (points[0]*256 + points[1]); +- points += 2; +- } +- } +- vertices[off+i].x = x; +- } +- +- // now load y coordinates +- y=0; +- for (i=0; i < n; ++i) { +- flags = vertices[off+i].type; +- if (flags & 4) { +- stbtt_int16 dy = *points++; +- y += (flags & 32) ? dy : -dy; // ??? +- } else { +- if (!(flags & 32)) { +- y = y + (stbtt_int16) (points[0]*256 + points[1]); +- points += 2; +- } +- } +- vertices[off+i].y = y; +- } +- +- // now convert them to our format +- num_vertices=0; +- sx = sy = cx = cy = 0; +- for (i=0; i < n; ++i) { +- flags = vertices[off+i].type; +- x = (stbtt_int16) vertices[off+i].x; +- y = (stbtt_int16) vertices[off+i].y; +- if (next_move == i) { +- // when we get to the end, we have to close the shape explicitly +- if (i != 0) { +- if (was_off) +- stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve,sx,sy,cx,cy); +- else +- stbtt_setvertex(&vertices[num_vertices++], STBTT_vline,sx,sy,0,0); +- } +- +- // now start the new one +- stbtt_setvertex(&vertices[num_vertices++], STBTT_vmove,x,y,0,0); +- next_move = 1 + ttUSHORT(endPtsOfContours+j*2); +- ++j; +- was_off = 0; +- sx = x; +- sy = y; +- } else { +- if (!(flags & 1)) { // if it's a curve +- if (was_off) // two off-curve control points in a row means interpolate an on-curve midpoint +- stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+x)>>1, (cy+y)>>1, cx, cy); +- cx = x; +- cy = y; +- was_off = 1; +- } else { +- if (was_off) +- stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, x,y, cx, cy); +- else +- stbtt_setvertex(&vertices[num_vertices++], STBTT_vline, x,y,0,0); +- was_off = 0; +- } +- } +- } +- if (i != 0) { +- if (was_off) +- stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve,sx,sy,cx,cy); +- else +- stbtt_setvertex(&vertices[num_vertices++], STBTT_vline,sx,sy,0,0); +- } +- } else if (numberOfContours == -1) { +- // Compound shapes. +- int more = 1; +- stbtt_uint8 *comp = data + g + 10; +- num_vertices = 0; +- vertices = 0; +- while (more) { +- stbtt_uint16 flags, gidx; +- int comp_num_verts = 0, i; +- stbtt_vertex *comp_verts = 0, *tmp = 0; +- float mtx[6] = {1,0,0,1,0,0}, m, n; +- +- flags = ttSHORT(comp); comp+=2; +- gidx = ttSHORT(comp); comp+=2; +- +- if (flags & 2) { // XY values +- if (flags & 1) { // shorts +- mtx[4] = ttSHORT(comp); comp+=2; +- mtx[5] = ttSHORT(comp); comp+=2; +- } else { +- mtx[4] = ttCHAR(comp); comp+=1; +- mtx[5] = ttCHAR(comp); comp+=1; +- } +- } +- else { +- // @TODO handle matching point +- STBTT_assert(0); +- } +- if (flags & (1<<3)) { // WE_HAVE_A_SCALE +- mtx[0] = mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; +- mtx[1] = mtx[2] = 0; +- } else if (flags & (1<<6)) { // WE_HAVE_AN_X_AND_YSCALE +- mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; +- mtx[1] = mtx[2] = 0; +- mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; +- } else if (flags & (1<<7)) { // WE_HAVE_A_TWO_BY_TWO +- mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; +- mtx[1] = ttSHORT(comp)/16384.0f; comp+=2; +- mtx[2] = ttSHORT(comp)/16384.0f; comp+=2; +- mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; +- } +- +- // Find transformation scales. +- m = (float) sqrt(mtx[0]*mtx[0] + mtx[1]*mtx[1]); +- n = (float) sqrt(mtx[2]*mtx[2] + mtx[3]*mtx[3]); +- +- // Get indexed glyph. +- comp_num_verts = stbtt_GetGlyphShape(info, gidx, &comp_verts); +- if (comp_num_verts > 0) { +- // Transform vertices. +- for (i = 0; i < comp_num_verts; ++i) { +- stbtt_vertex* v = &comp_verts[i]; +- stbtt_vertex_type x,y; +- x=v->x; y=v->y; +- v->x = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); +- v->y = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); +- x=v->cx; y=v->cy; +- v->cx = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); +- v->cy = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); +- } +- // Append vertices. +- tmp = (stbtt_vertex*)STBTT_malloc((num_vertices+comp_num_verts)*sizeof(stbtt_vertex), info->userdata); +- if (!tmp) { +- if (vertices) STBTT_free(vertices, info->userdata); +- if (comp_verts) STBTT_free(comp_verts, info->userdata); +- return 0; +- } +- if (num_vertices > 0) memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex)); +- memcpy(tmp+num_vertices, comp_verts, comp_num_verts*sizeof(stbtt_vertex)); +- if (vertices) STBTT_free(vertices, info->userdata); +- vertices = tmp; +- STBTT_free(comp_verts, info->userdata); +- num_vertices += comp_num_verts; +- } +- // More components ? +- more = flags & (1<<5); +- } +- } else if (numberOfContours < 0) { +- // @TODO other compound variations? +- STBTT_assert(0); +- } else { +- // numberOfCounters == 0, do nothing +- } +- +- *pvertices = vertices; +- return num_vertices; +-} +- +-void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing) +-{ +- stbtt_uint16 numOfLongHorMetrics = ttUSHORT(info->data+info->hhea + 34); +- if (glyph_index < numOfLongHorMetrics) { +- if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*glyph_index); +- if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*glyph_index + 2); +- } else { +- if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*(numOfLongHorMetrics-1)); +- if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*numOfLongHorMetrics + 2*(glyph_index - numOfLongHorMetrics)); +- } +-} +- +-int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo * /*info*/, int /*glyph1*/, int /*glyph2*/) +-{ +- return 0; +-} +- +-int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo * /*info*/, int /*ch1*/, int /*ch2*/) +-{ +- return 0; +-} +- +-void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing) +-{ +- stbtt_GetGlyphHMetrics(info, stbtt_FindGlyphIndex(info,codepoint), advanceWidth, leftSideBearing); +-} +- +-void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap) +-{ +- if (ascent ) *ascent = ttSHORT(info->data+info->hhea + 4); +- if (descent) *descent = ttSHORT(info->data+info->hhea + 6); +- if (lineGap) *lineGap = ttSHORT(info->data+info->hhea + 8); +-} +- +-float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float height) +-{ +- int fheight = ttSHORT(info->data + info->hhea + 4) - ttSHORT(info->data + info->hhea + 6); +- return (float) height / fheight; +-} +- +-void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v) +-{ +- STBTT_free(v, info->userdata); +-} +- +-////////////////////////////////////////////////////////////////////////////// +-// +-// antialiasing software rasterizer +-// +- +-void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) +-{ +- int x0,y0,x1,y1; +- if (!stbtt_GetGlyphBox(font, glyph, &x0,&y0,&x1,&y1)) +- x0=y0=x1=y1=0; // e.g. space character +- // now move to integral bboxes (treating pixels as little squares, what pixels get touched)? +- if (ix0) *ix0 = STBTT_ifloor(x0 * scale_x); +- if (iy0) *iy0 = -STBTT_iceil (y1 * scale_y); +- if (ix1) *ix1 = STBTT_iceil (x1 * scale_x); +- if (iy1) *iy1 = -STBTT_ifloor(y0 * scale_y); +-} +- +-void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) +-{ +- stbtt_GetGlyphBitmapBox(font, stbtt_FindGlyphIndex(font,codepoint), scale_x, scale_y, ix0,iy0,ix1,iy1); +-} +- +-typedef struct stbtt__edge { +- float x0,y0, x1,y1; +- int invert; +-} stbtt__edge; +- +-typedef struct stbtt__active_edge +-{ +- int x,dx; +- float ey; +- struct stbtt__active_edge *next; +- int valid; +-} stbtt__active_edge; +- +-#define FIXSHIFT 10 +-#define FIX (1 << FIXSHIFT) +-#define FIXMASK (FIX-1) +- +-static stbtt__active_edge *new_active(stbtt__edge *e, int off_x, float start_point, void *userdata) +-{ +- stbtt__active_edge *z = (stbtt__active_edge *) STBTT_malloc(sizeof(*z), userdata); // @TODO: make a pool of these!!! +- float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); +- STBTT_assert(e->y0 <= start_point); +- if (!z) return z; +- // round dx down to avoid going too far +- if (dxdy < 0) +- z->dx = -STBTT_ifloor(FIX * -dxdy); +- else +- z->dx = STBTT_ifloor(FIX * dxdy); +- z->x = STBTT_ifloor(FIX * (e->x0 + dxdy * (start_point - e->y0))); +- z->x -= off_x * FIX; +- z->ey = e->y1; +- z->next = 0; +- z->valid = e->invert ? 1 : -1; +- return z; +-} +- +-// note: this routine clips fills that extend off the edges... ideally this +-// wouldn't happen, but it could happen if the truetype glyph bounding boxes +-// are wrong, or if the user supplies a too-small bitmap +-static void stbtt__fill_active_edges(unsigned char *scanline, int len, stbtt__active_edge *e, int max_weight) +-{ +- // non-zero winding fill +- int x0=0, w=0; +- +- while (e) { +- if (w == 0) { +- // if we're currently at zero, we need to record the edge start point +- x0 = e->x; w += e->valid; +- } else { +- int x1 = e->x; w += e->valid; +- // if we went to zero, we need to draw +- if (w == 0) { +- int i = x0 >> FIXSHIFT; +- int j = x1 >> FIXSHIFT; +- +- if (i < len && j >= 0) { +- if (i == j) { +- // x0,x1 are the same pixel, so compute combined coverage +- scanline[i] = scanline[i] + (stbtt_uint8) ((x1 - x0) * max_weight >> FIXSHIFT); +- } else { +- if (i >= 0) // add antialiasing for x0 +- scanline[i] = scanline[i] + (stbtt_uint8) (((FIX - (x0 & FIXMASK)) * max_weight) >> FIXSHIFT); +- else +- i = -1; // clip +- +- if (j < len) // add antialiasing for x1 +- scanline[j] = scanline[j] + (stbtt_uint8) (((x1 & FIXMASK) * max_weight) >> FIXSHIFT); +- else +- j = len; // clip +- +- for (++i; i < j; ++i) // fill pixels between x0 and x1 +- scanline[i] = scanline[i] + (stbtt_uint8) max_weight; +- } +- } +- } +- } +- +- e = e->next; +- } +-} +- +-static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata) +-{ +- stbtt__active_edge *active = NULL; +- int y,j=0; +- int max_weight = (255 / vsubsample); // weight per vertical scanline +- int s; // vertical subsample index +- unsigned char scanline_data[512], *scanline; +- +- if (result->w > 512) +- scanline = (unsigned char *) STBTT_malloc(result->w, userdata); +- else +- scanline = scanline_data; +- +- y = off_y * vsubsample; +- e[n].y0 = (off_y + result->h) * (float) vsubsample + 1; +- +- while (j < result->h) { +- STBTT_memset(scanline, 0, result->w); +- for (s=0; s < vsubsample; ++s) { +- // find center of pixel for this scanline +- float scan_y = y + 0.5f; +- stbtt__active_edge **step = &active; +- +- // update all active edges; +- // remove all active edges that terminate before the center of this scanline +- while (*step) { +- stbtt__active_edge * z = *step; +- if (z->ey <= scan_y) { +- *step = z->next; // delete from list +- STBTT_assert(z->valid); +- z->valid = 0; +- STBTT_free(z, userdata); +- } else { +- z->x += z->dx; // advance to position for current scanline +- step = &((*step)->next); // advance through list +- } +- } +- +- // resort the list if needed +- for(;;) { +- int changed=0; +- step = &active; +- while (*step && (*step)->next) { +- if ((*step)->x > (*step)->next->x) { +- stbtt__active_edge *t = *step; +- stbtt__active_edge *q = t->next; +- +- t->next = q->next; +- q->next = t; +- *step = q; +- changed = 1; +- } +- step = &(*step)->next; +- } +- if (!changed) break; +- } +- +- // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline +- while (e->y0 <= scan_y) { +- if (e->y1 > scan_y) { +- stbtt__active_edge *z = new_active(e, off_x, scan_y, userdata); +- // find insertion point +- if (active == NULL) +- active = z; +- else if (z->x < active->x) { +- // insert at front +- z->next = active; +- active = z; +- } else { +- // find thing to insert AFTER +- stbtt__active_edge *p = active; +- while (p->next && p->next->x < z->x) +- p = p->next; +- // at this point, p->next->x is NOT < z->x +- z->next = p->next; +- p->next = z; +- } +- } +- ++e; +- } +- +- // now process all active edges in XOR fashion +- if (active) +- stbtt__fill_active_edges(scanline, result->w, active, max_weight); +- +- ++y; +- } +- STBTT_memcpy(result->pixels + j * result->stride, scanline, result->w); +- ++j; +- } +- +- while (active) { +- stbtt__active_edge *z = active; +- active = active->next; +- STBTT_free(z, userdata); +- } +- +- if (scanline != scanline_data) +- STBTT_free(scanline, userdata); +-} +- +-static int stbtt__edge_compare(const void *p, const void *q) +-{ +- stbtt__edge *a = (stbtt__edge *) p; +- stbtt__edge *b = (stbtt__edge *) q; +- +- if (a->y0 < b->y0) return -1; +- if (a->y0 > b->y0) return 1; +- return 0; +-} +- +-typedef struct +-{ +- float x,y; +-} stbtt__point; +- +-static void stbtt__rasterize(stbtt__bitmap *result, stbtt__point *pts, int *wcount, int windings, float scale_x, float scale_y, int off_x, int off_y, int invert, void *userdata) +-{ +- float y_scale_inv = invert ? -scale_y : scale_y; +- stbtt__edge *e; +- int n,i,j,k,m; +- int vsubsample = result->h < 8 ? 15 : 5; +- // vsubsample should divide 255 evenly; otherwise we won't reach full opacity +- +- // now we have to blow out the windings into explicit edge lists +- n = 0; +- for (i=0; i < windings; ++i) +- n += wcount[i]; +- +- e = (stbtt__edge *) STBTT_malloc(sizeof(*e) * (n+1), userdata); // add an extra one as a sentinel +- if (e == 0) return; +- n = 0; +- +- m=0; +- for (i=0; i < windings; ++i) { +- stbtt__point *p = pts + m; +- m += wcount[i]; +- j = wcount[i]-1; +- for (k=0; k < wcount[i]; j=k++) { +- int a=k,b=j; +- // skip the edge if horizontal +- if (p[j].y == p[k].y) +- continue; +- // add edge from j to k to the list +- e[n].invert = 0; +- if (invert ? p[j].y > p[k].y : p[j].y < p[k].y) { +- e[n].invert = 1; +- a=j,b=k; +- } +- e[n].x0 = p[a].x * scale_x; +- e[n].y0 = p[a].y * y_scale_inv * vsubsample; +- e[n].x1 = p[b].x * scale_x; +- e[n].y1 = p[b].y * y_scale_inv * vsubsample; +- ++n; +- } +- } +- +- // now sort the edges by their highest point (should snap to integer, and then by x) +- STBTT_sort(e, n, sizeof(e[0]), stbtt__edge_compare); +- +- // now, traverse the scanlines and find the intersections on each scanline, use xor winding rule +- stbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y, userdata); +- +- STBTT_free(e, userdata); +-} +- +-static void stbtt__add_point(stbtt__point *points, int n, float x, float y) +-{ +- if (!points) return; // during first pass, it's unallocated +- points[n].x = x; +- points[n].y = y; +-} +- +-// tesselate until threshhold p is happy... @TODO warped to compensate for non-linear stretching +-static int stbtt__tesselate_curve(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float objspace_flatness_squared, int n) +-{ +- // midpoint +- float mx = (x0 + 2*x1 + x2)/4; +- float my = (y0 + 2*y1 + y2)/4; +- // versus directly drawn line +- float dx = (x0+x2)/2 - mx; +- float dy = (y0+y2)/2 - my; +- if (n > 16) // 65536 segments on one curve better be enough! +- return 1; +- if (dx*dx+dy*dy > objspace_flatness_squared) { // half-pixel error allowed... need to be smaller if AA +- stbtt__tesselate_curve(points, num_points, x0,y0, (x0+x1)/2.0f,(y0+y1)/2.0f, mx,my, objspace_flatness_squared,n+1); +- stbtt__tesselate_curve(points, num_points, mx,my, (x1+x2)/2.0f,(y1+y2)/2.0f, x2,y2, objspace_flatness_squared,n+1); +- } else { +- stbtt__add_point(points, *num_points,x2,y2); +- *num_points = *num_points+1; +- } +- return 1; +-} +- +-// returns number of contours +-stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours, void *userdata) +-{ +- stbtt__point *points=0; +- int num_points=0; +- +- float objspace_flatness_squared = objspace_flatness * objspace_flatness; +- int i,n=0,start=0, pass; +- +- // count how many "moves" there are to get the contour count +- for (i=0; i < num_verts; ++i) +- if (vertices[i].type == STBTT_vmove) +- ++n; +- +- *num_contours = n; +- if (n == 0) return 0; +- +- *contour_lengths = (int *) STBTT_malloc(sizeof(**contour_lengths) * n, userdata); +- +- if (*contour_lengths == 0) { +- *num_contours = 0; +- return 0; +- } +- +- // make two passes through the points so we don't need to realloc +- for (pass=0; pass < 2; ++pass) { +- float x=0,y=0; +- if (pass == 1) { +- points = (stbtt__point *) STBTT_malloc(num_points * sizeof(points[0]), userdata); +- if (points == NULL) goto error; +- } +- num_points = 0; +- n= -1; +- for (i=0; i < num_verts; ++i) { +- switch (vertices[i].type) { +- case STBTT_vmove: +- // start the next contour +- if (n >= 0) +- (*contour_lengths)[n] = num_points - start; +- ++n; +- start = num_points; +- +- x = vertices[i].x, y = vertices[i].y; +- stbtt__add_point(points, num_points++, x,y); +- break; +- case STBTT_vline: +- x = vertices[i].x, y = vertices[i].y; +- stbtt__add_point(points, num_points++, x, y); +- break; +- case STBTT_vcurve: +- stbtt__tesselate_curve(points, &num_points, x,y, +- vertices[i].cx, vertices[i].cy, +- vertices[i].x, vertices[i].y, +- objspace_flatness_squared, 0); +- x = vertices[i].x, y = vertices[i].y; +- break; +- } +- } +- (*contour_lengths)[n] = num_points - start; +- } +- +- return points; +-error: +- STBTT_free(points, userdata); +- STBTT_free(*contour_lengths, userdata); +- *contour_lengths = 0; +- *num_contours = 0; +- return NULL; +-} +- +-void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, int x_off, int y_off, int invert, void *userdata) +-{ +- float scale = scale_x > scale_y ? scale_y : scale_x; +- int winding_count, *winding_lengths; +- stbtt__point *windings = stbtt_FlattenCurves(vertices, num_verts, flatness_in_pixels / scale, &winding_lengths, &winding_count, userdata); +- if (windings) { +- stbtt__rasterize(result, windings, winding_lengths, winding_count, scale_x, scale_y, x_off, y_off, invert, userdata); +- STBTT_free(winding_lengths, userdata); +- STBTT_free(windings, userdata); +- } +-} +- +-void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata) +-{ +- STBTT_free(bitmap, userdata); +-} +- +-unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff) +-{ +- int ix0,iy0,ix1,iy1; +- stbtt__bitmap gbm; +- stbtt_vertex *vertices; +- int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); +- +- if (scale_x == 0) scale_x = scale_y; +- if (scale_y == 0) { +- if (scale_x == 0) return NULL; +- scale_y = scale_x; +- } +- +- stbtt_GetGlyphBitmapBox(info, glyph, scale_x, scale_y, &ix0,&iy0,&ix1,&iy1); +- +- // now we get the size +- gbm.w = (ix1 - ix0); +- gbm.h = (iy1 - iy0); +- gbm.pixels = NULL; // in case we error +- +- if (width ) *width = gbm.w; +- if (height) *height = gbm.h; +- if (xoff ) *xoff = ix0; +- if (yoff ) *yoff = iy0; +- +- if (gbm.w && gbm.h) { +- gbm.pixels = (unsigned char *) STBTT_malloc(gbm.w * gbm.h, info->userdata); +- if (gbm.pixels) { +- gbm.stride = gbm.w; +- +- stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, ix0, iy0, 1, info->userdata); +- } +- } +- STBTT_free(vertices, info->userdata); +- return gbm.pixels; +-} +- +-void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph) +-{ +- int ix0,iy0; +- stbtt_vertex *vertices; +- int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); +- stbtt__bitmap gbm; +- +- stbtt_GetGlyphBitmapBox(info, glyph, scale_x, scale_y, &ix0,&iy0,0,0); +- gbm.pixels = output; +- gbm.w = out_w; +- gbm.h = out_h; +- gbm.stride = out_stride; +- +- if (gbm.w && gbm.h) +- stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, ix0,iy0, 1, info->userdata); +- +- STBTT_free(vertices, info->userdata); +-} +- +-unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff) +-{ +- return stbtt_GetGlyphBitmap(info, scale_x, scale_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff); +-} +- +-void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint) +-{ +- stbtt_MakeGlyphBitmap(info, output, out_w, out_h, out_stride, scale_x, scale_y, stbtt_FindGlyphIndex(info,codepoint)); +-} +- +-////////////////////////////////////////////////////////////////////////////// +-// +-// bitmap baking +-// +-// This is SUPER-SHITTY packing to keep source code small +- +-extern int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) +- float pixel_height, // height of font in pixels +- unsigned char *pixels, int pw, int ph, // bitmap to be filled in +- int first_char, int num_chars, // characters to bake +- stbtt_bakedchar *chardata) +-{ +- float scale; +- int x,y,bottom_y, i; +- stbtt_fontinfo f; +- stbtt_InitFont(&f, data, offset); +- STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels +- x=y=1; +- bottom_y = 1; +- +- scale = stbtt_ScaleForPixelHeight(&f, pixel_height); +- +- for (i=0; i < num_chars; ++i) { +- int advance, lsb, x0,y0,x1,y1,gw,gh; +- int g = stbtt_FindGlyphIndex(&f, first_char + i); +- stbtt_GetGlyphHMetrics(&f, g, &advance, &lsb); +- stbtt_GetGlyphBitmapBox(&f, g, scale,scale, &x0,&y0,&x1,&y1); +- gw = x1-x0; +- gh = y1-y0; +- if (x + gw + 1 >= pw) +- y = bottom_y, x = 1; // advance to next row +- if (y + gh + 1 >= ph) // check if it fits vertically AFTER potentially moving to next row +- return -i; +- STBTT_assert(x+gw < pw); +- STBTT_assert(y+gh < ph); +- stbtt_MakeGlyphBitmap(&f, pixels+x+y*pw, gw,gh,pw, scale,scale, g); +- chardata[i].x0 = (stbtt_int16) x; +- chardata[i].y0 = (stbtt_int16) y; +- chardata[i].x1 = (stbtt_int16) (x + gw); +- chardata[i].y1 = (stbtt_int16) (y + gh); +- chardata[i].xadvance = scale * advance; +- chardata[i].xoff = (float) x0; +- chardata[i].yoff = (float) y0; +- x = x + gw + 2; +- if (y+gh+2 > bottom_y) +- bottom_y = y+gh+2; +- } +- return bottom_y; +-} +- +-void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int opengl_fillrule) +-{ +- float d3d_bias = opengl_fillrule ? 0 : -0.5f; +- float ipw = 1.0f / pw, iph = 1.0f / ph; +- stbtt_bakedchar *b = chardata + char_index; +- int round_x = STBTT_ifloor((*xpos + b->xoff) + 0.5); +- int round_y = STBTT_ifloor((*ypos + b->yoff) + 0.5); +- +- q->x0 = round_x + d3d_bias; +- q->y0 = round_y + d3d_bias; +- q->x1 = round_x + b->x1 - b->x0 + d3d_bias; +- q->y1 = round_y + b->y1 - b->y0 + d3d_bias; +- +- q->s0 = b->x0 * ipw; +- q->t0 = b->y0 * ipw; +- q->s1 = b->x1 * iph; +- q->t1 = b->y1 * iph; +- +- *xpos += b->xadvance; +-} +- +-////////////////////////////////////////////////////////////////////////////// +-// +-// font name matching -- recommended not to use this +-// +- +-// check if a utf8 string contains a prefix which is the utf16 string; if so return length of matching utf8 string +-static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(stbtt_uint8 *s1, stbtt_int32 len1, stbtt_uint8 *s2, stbtt_int32 len2) +-{ +- stbtt_int32 i=0; +- +- // convert utf16 to utf8 and compare the results while converting +- while (len2) { +- stbtt_uint16 ch = s2[0]*256 + s2[1]; +- if (ch < 0x80) { +- if (i >= len1) return -1; +- if (s1[i++] != ch) return -1; +- } else if (ch < 0x800) { +- if (i+1 >= len1) return -1; +- if (s1[i++] != 0xc0 + (ch >> 6)) return -1; +- if (s1[i++] != 0x80 + (ch & 0x3f)) return -1; +- } else if (ch >= 0xd800 && ch < 0xdc00) { +- stbtt_uint32 c; +- stbtt_uint16 ch2 = s2[2]*256 + s2[3]; +- if (i+3 >= len1) return -1; +- c = ((ch - 0xd800) << 10) + (ch2 - 0xdc00) + 0x10000; +- if (s1[i++] != 0xf0 + (c >> 18)) return -1; +- if (s1[i++] != 0x80 + ((c >> 12) & 0x3f)) return -1; +- if (s1[i++] != 0x80 + ((c >> 6) & 0x3f)) return -1; +- if (s1[i++] != 0x80 + ((c ) & 0x3f)) return -1; +- s2 += 2; // plus another 2 below +- len2 -= 2; +- } else if (ch >= 0xdc00 && ch < 0xe000) { +- return -1; +- } else { +- if (i+2 >= len1) return -1; +- if (s1[i++] != 0xe0 + (ch >> 12)) return -1; +- if (s1[i++] != 0x80 + ((ch >> 6) & 0x3f)) return -1; +- if (s1[i++] != 0x80 + ((ch ) & 0x3f)) return -1; +- } +- s2 += 2; +- len2 -= 2; +- } +- return i; +-} +- +-int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2) +-{ +- return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((stbtt_uint8*) s1, len1, (stbtt_uint8*) s2, len2); +-} +- +-// returns results in whatever encoding you request... but note that 2-byte encodings +-// will be BIG-ENDIAN... use stbtt_CompareUTF8toUTF16_bigendian() to compare +-char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID) +-{ +- stbtt_int32 i,count,stringOffset; +- stbtt_uint8 *fc = font->data; +- stbtt_uint32 offset = font->fontstart; +- stbtt_uint32 nm = stbtt__find_table(fc, offset, "name"); +- if (!nm) return NULL; +- +- count = ttUSHORT(fc+nm+2); +- stringOffset = nm + ttUSHORT(fc+nm+4); +- for (i=0; i < count; ++i) { +- stbtt_uint32 loc = nm + 6 + 12 * i; +- if (platformID == ttUSHORT(fc+loc+0) && encodingID == ttUSHORT(fc+loc+2) +- && languageID == ttUSHORT(fc+loc+4) && nameID == ttUSHORT(fc+loc+6)) { +- *length = ttUSHORT(fc+loc+8); +- return (char *) (fc+stringOffset+ttUSHORT(fc+loc+10)); +- } +- } +- return NULL; +-} +- +-static int stbtt__matchpair(stbtt_uint8 *fc, stbtt_uint32 nm, stbtt_uint8 *name, stbtt_int32 nlen, stbtt_int32 target_id, stbtt_int32 next_id) +-{ +- stbtt_int32 i; +- stbtt_int32 count = ttUSHORT(fc+nm+2); +- stbtt_int32 stringOffset = nm + ttUSHORT(fc+nm+4); +- +- for (i=0; i < count; ++i) { +- stbtt_uint32 loc = nm + 6 + 12 * i; +- stbtt_int32 id = ttUSHORT(fc+loc+6); +- if (id == target_id) { +- // find the encoding +- stbtt_int32 platform = ttUSHORT(fc+loc+0), encoding = ttUSHORT(fc+loc+2), language = ttUSHORT(fc+loc+4); +- +- // is this a Unicode encoding? +- if (platform == 0 || (platform == 3 && encoding == 1) || (platform == 3 && encoding == 10)) { +- stbtt_int32 slen = ttUSHORT(fc+loc+8), off = ttUSHORT(fc+loc+10); +- +- // check if there's a prefix match +- stbtt_int32 matchlen = stbtt__CompareUTF8toUTF16_bigendian_prefix(name, nlen, fc+stringOffset+off,slen); +- if (matchlen >= 0) { +- // check for target_id+1 immediately following, with same encoding & language +- if (i+1 < count && ttUSHORT(fc+loc+12+6) == next_id && ttUSHORT(fc+loc+12) == platform && ttUSHORT(fc+loc+12+2) == encoding && ttUSHORT(fc+loc+12+4) == language) { +- stbtt_int32 slen = ttUSHORT(fc+loc+12+8), off = ttUSHORT(fc+loc+12+10); +- if (slen == 0) { +- if (matchlen == nlen) +- return 1; +- } else if (matchlen < nlen && name[matchlen] == ' ') { +- ++matchlen; +- if (stbtt_CompareUTF8toUTF16_bigendian((char*) (name+matchlen), nlen-matchlen, (char*)(fc+stringOffset+off),slen)) +- return 1; +- } +- } else { +- // if nothing immediately following +- if (matchlen == nlen) +- return 1; +- } +- } +- } +- +- // @TODO handle other encodings +- } +- } +- return 0; +-} +- +-static int stbtt__matches(stbtt_uint8 *fc, stbtt_uint32 offset, stbtt_uint8 *name, stbtt_int32 flags) +-{ +- stbtt_int32 nlen = STBTT_strlen((char *) name); +- stbtt_uint32 nm,hd; +- if (!stbtt__isfont(fc+offset)) return 0; +- +- // check italics/bold/underline flags in macStyle... +- if (flags) { +- hd = stbtt__find_table(fc, offset, "head"); +- if ((ttUSHORT(fc+hd+44) & 7) != (flags & 7)) return 0; +- } +- +- nm = stbtt__find_table(fc, offset, "name"); +- if (!nm) return 0; +- +- if (flags) { +- // if we checked the macStyle flags, then just check the family and ignore the subfamily +- if (stbtt__matchpair(fc, nm, name, nlen, 16, -1)) return 1; +- if (stbtt__matchpair(fc, nm, name, nlen, 1, -1)) return 1; +- if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; +- } else { +- if (stbtt__matchpair(fc, nm, name, nlen, 16, 17)) return 1; +- if (stbtt__matchpair(fc, nm, name, nlen, 1, 2)) return 1; +- if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; +- } +- +- return 0; +-} +- +-int stbtt_FindMatchingFont(const unsigned char *font_collection, const char *name_utf8, stbtt_int32 flags) +-{ +- stbtt_int32 i; +- for (i=0;;++i) { +- stbtt_int32 off = stbtt_GetFontOffsetForIndex(font_collection, i); +- if (off < 0) return off; +- if (stbtt__matches((stbtt_uint8 *) font_collection, off, (stbtt_uint8*) name_utf8, flags)) +- return off; +- } +-} +- +-#endif // STB_TRUETYPE_IMPLEMENTATION ++// stb_truetype.h - v0.3 - public domain - 2009 Sean Barrett / RAD Game Tools ++// ++// This library processes TrueType files: ++// parse files ++// extract glyph metrics ++// extract glyph shapes ++// render glyphs to one-channel bitmaps with antialiasing (box filter) ++// ++// Todo: ++// non-MS cmaps ++// crashproof on bad data ++// hinting ++// subpixel positioning when rendering bitmap ++// cleartype-style AA ++// ++// ADDITIONAL CONTRIBUTORS ++// ++// Mikko Mononen: compound shape support, more cmap formats ++// ++// VERSIONS ++// ++// 0.3 (2009-06-24) cmap fmt=12, compound shapes (MM) ++// userdata, malloc-from-userdata, non-zero fill (STB) ++// 0.2 (2009-03-11) Fix unsigned/signed char warnings ++// 0.1 (2009-03-09) First public release ++// ++// USAGE ++// ++// Include this file in whatever places neeed to refer to it. In ONE C/C++ ++// file, write: ++// #define STB_TRUETYPE_IMPLEMENTATION ++// before the #include of this file. This expands out the actual ++// implementation into that C/C++ file. ++// ++// Look at the header-file sections below for the API, but here's a quick skim: ++// ++// Simple 3D API (don't ship this, but it's fine for tools and quick start, ++// and you can cut and paste from it to move to more advanced) ++// stbtt_BakeFontBitmap() -- bake a font to a bitmap for use as texture ++// stbtt_GetBakedQuad() -- compute quad to draw for a given char ++// ++// "Load" a font file from a memory buffer (you have to keep the buffer loaded) ++// stbtt_InitFont() ++// stbtt_GetFontOffsetForIndex() -- use for TTC font collections ++// ++// Render a unicode codepoint to a bitmap ++// stbtt_GetCodepointBitmap() -- allocates and returns a bitmap ++// stbtt_MakeCodepointBitmap() -- renders into bitmap you provide ++// stbtt_GetCodepointBitmapBox() -- how big the bitmap must be ++// ++// Character advance/positioning ++// stbtt_GetCodepointHMetrics() ++// stbtt_GetFontVMetrics() ++// ++// NOTES ++// ++// The system uses the raw data found in the .ttf file without changing it ++// and without building auxiliary data structures. This is a bit inefficient ++// on little-endian systems (the data is big-endian), but assuming you're ++// caching the bitmaps or glyph shapes this shouldn't be a big deal. ++// ++// It appears to be very hard to programmatically determine what font a ++// given file is in a general way. I provide an API for this, but I don't ++// recommend it. ++// ++// ++// SOURCE STATISTICS (based on v0.3, 1800 LOC) ++// ++// Documentation & header file 350 LOC \___ 500 LOC documentation ++// Sample code 140 LOC / ++// Truetype parsing 580 LOC ---- 600 LOC TrueType ++// Software rasterization 240 LOC \ . ++// Curve tesselation 120 LOC \__ 500 LOC Bitmap creation ++// Bitmap management 70 LOC / ++// Baked bitmap interface 70 LOC / ++// Font name matching & access 150 LOC ---- 150 ++// C runtime library abstraction 60 LOC ---- 60 ++ ++ ++////////////////////////////////////////////////////////////////////////////// ++////////////////////////////////////////////////////////////////////////////// ++//// ++//// SAMPLE PROGRAMS ++//// ++// ++// Incomplete text-in-3d-api example, which draws quads properly aligned to be lossless ++// ++#if 0 ++#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation ++#include "stb_truetype.h" ++ ++char ttf_buffer[1<<20]; ++unsigned char temp_bitmap[512*512]; ++ ++stbtt_chardata cdata[96]; // ASCII 32..126 is 95 glyphs ++GLstbtt_uint ftex; ++ ++void my_stbtt_initfont(void) ++{ ++ fread(ttf_buffer, 1, 1<<20, fopen("c:/windows/fonts/times.ttf", "rb")); ++ stbtt_BakeFontBitmap(data,0, 32.0, temp_bitmap,512,512, 32,96, cdata); // no guarantee this fits! ++ // can free ttf_buffer at this point ++ glGenTextures(1, &ftex); ++ glBindTexture(GL_TEXTURE_2D, ftex); ++ glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 512,512, 0, GL_ALPHA, GL_UNSIGNED_BYTE, temp_bitmap); ++ // can free temp_bitmap at this point ++ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); ++} ++ ++void my_stbtt_print(float x, float y, char *text) ++{ ++ // assume orthographic projection with units = screen pixels, origin at top left ++ glBindTexture(GL_TEXTURE_2D, ftex); ++ glBegin(GL_QUADS); ++ while (*text) { ++ if (*text >= 32 && *text < 128) { ++ stbtt_aligned_quad q; ++ stbtt_GetBakedQuad(cdata, 512,512, *text-32, &x,&y,&q,1);//1=opengl,0=old d3d ++ glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y0); ++ glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y0); ++ glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y1); ++ glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y1); ++ } ++ ++text; ++ } ++ glEnd(); ++} ++#endif ++// ++// ++////////////////////////////////////////////////////////////////////////////// ++// ++// Complete program (this compiles): get a single bitmap, print as ASCII art ++// ++#if 0 ++#include <stdio.h> ++#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation ++#include "stb_truetype.h" ++ ++char ttf_buffer[1<<25]; ++ ++int main(int argc, char **argv) ++{ ++ stbtt_fontinfo font; ++ unsigned char *bitmap; ++ int w,h,i,j,c = (argc > 1 ? atoi(argv[1]) : 'a'), s = (argc > 2 ? atoi(argv[2]) : 20); ++ ++ fread(ttf_buffer, 1, 1<<25, fopen(argc > 3 ? argv[3] : "c:/windows/fonts/arialbd.ttf", "rb")); ++ ++ stbtt_InitFont(&font, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer,0)); ++ bitmap = stbtt_GetCodepointBitmap(&font, 0,stbtt_ScaleForPixelHeight(&font, s), c, &w, &h, 0,0); ++ ++ for (j=0; j < h; ++j) { ++ for (i=0; i < w; ++i) ++ putchar(" .:ioVM@"[bitmap[j*w+i]>>5]); ++ putchar('\n'); ++ } ++ return 0; ++} ++#endif ++// ++// Output: ++// ++// .ii. ++// @@@@@@. ++// V@Mio@@o ++// :i. V@V ++// :oM@@M ++// :@@@MM@M ++// @@o o@M ++// :@@. M@M ++// @@@o@@@@ ++// :M@@V:@@. ++// ++////////////////////////////////////////////////////////////////////////////// ++// ++// Complete program: print "Hello World!" banner, with bugs ++// ++#if 0 ++int main(int arg, char **argv) ++{ ++ unsigned char screen[20][79]; ++ int i,j, pos=0; ++ float scale; ++ char *text = "Heljo World!"; ++ ++ fread(buffer, 1, 1000000, fopen("c:/windows/fonts/arialbd.ttf", "rb")); ++ stbtt_InitFont(&font, buffer, 0); ++ ++ scale = stbtt_ScaleForPixelHeight(&font, 16); ++ memset(screen, 0, sizeof(screen)); ++ ++ while (*text) { ++ int advance,lsb,x0,y0,x1,y1, newpos, baseline=13; ++ stbtt_GetCodepointHMetrics(&font, *text, &advance, &lsb); ++ stbtt_GetCodepointBitmapBox(&font, *text, scale,scale, &x0,&y0,&x1,&y1); ++ newpos = pos + (int) (lsb * scale) + x0; ++ stbtt_MakeCodepointBitmap(&font, &screen[baseline + y0][newpos], x1-x0,y1-y0, 79, scale,scale, *text); ++ // note that this stomps the old data, so where character boxes overlap (e.g. 'lj') it's wrong ++ // because this API is really for baking character bitmaps into textures ++ pos += (int) (advance * scale); ++ ++text; ++ } ++ ++ for (j=0; j < 20; ++j) { ++ for (i=0; i < 79; ++i) ++ putchar(" .:ioVM@"[screen[j][i]>>5]); ++ putchar('\n'); ++ } ++ ++ return 0; ++} ++#endif ++ ++ ++////////////////////////////////////////////////////////////////////////////// ++////////////////////////////////////////////////////////////////////////////// ++//// ++//// INTEGRATION WITH RUNTIME LIBRARIES ++//// ++ ++#ifdef STB_TRUETYPE_IMPLEMENTATION ++ // #define your own (u)stbtt_int8/16/32 before including to override this ++ #ifndef stbtt_uint8 ++ typedef unsigned char stbtt_uint8; ++ typedef signed char stbtt_int8; ++ typedef unsigned short stbtt_uint16; ++ typedef signed short stbtt_int16; ++ typedef unsigned int stbtt_uint32; ++ typedef signed int stbtt_int32; ++ #endif ++ ++ typedef char stbtt__check_size32[sizeof(stbtt_int32)==4 ? 1 : -1]; ++ typedef char stbtt__check_size16[sizeof(stbtt_int16)==2 ? 1 : -1]; ++ ++ // #define your own STBTT_sort() to override this to avoid qsort ++ #ifndef STBTT_sort ++ #include <stdlib.h> ++ #define STBTT_sort(data,num_items,item_size,compare_func) qsort(data,num_items,item_size,compare_func) ++ #endif ++ ++ // #define your own STBTT_ifloor/STBTT_iceil() to avoid math.h ++ #ifndef STBTT_ifloor ++ #include <math.h> ++ #define STBTT_ifloor(x) ((int) floor(x)) ++ #define STBTT_iceil(x) ((int) ceil(x)) ++ #endif ++ ++ // #define your own functions "STBTT_malloc" / "STBTT_free" to avoid malloc.h ++ #ifndef STBTT_malloc ++ #include <malloc.h> ++ #define STBTT_malloc(x,u) malloc(x) ++ #define STBTT_free(x,u) free(x) ++ #endif ++ ++ #ifndef STBTT_assert ++ #include <assert.h> ++ #define STBTT_assert(x) assert(x) ++ #endif ++ ++ #ifndef STBTT_strlen ++ #include <string.h> ++ #define STBTT_strlen(x) strlen(x) ++ #endif ++ ++ #ifndef STBTT_memcpy ++ #include <memory.h> ++ #define STBTT_memcpy memcpy ++ #define STBTT_memset memset ++ #endif ++#endif ++ ++/////////////////////////////////////////////////////////////////////////////// ++/////////////////////////////////////////////////////////////////////////////// ++//// ++//// INTERFACE ++//// ++//// ++ ++#ifndef __STB_INCLUDE_STB_TRUETYPE_H__ ++#define __STB_INCLUDE_STB_TRUETYPE_H__ ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++////////////////////////////////////////////////////////////////////////////// ++// ++// TEXTURE BAKING API ++// ++// If you use this API, you only have to call two functions ever. ++// ++ ++typedef struct ++{ ++ unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap ++ float xoff,yoff,xadvance; ++} stbtt_bakedchar; ++ ++extern int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) ++ float pixel_height, // height of font in pixels ++ unsigned char *pixels, int pw, int ph, // bitmap to be filled in ++ int first_char, int num_chars, // characters to bake ++ stbtt_bakedchar *chardata); // you allocate this, it's num_chars long ++// if return is positive, the first unused row of the bitmap ++// if return is negative, returns the negative of the number of characters that fit ++// if return is 0, no characters fit and no rows were used ++// This uses a very crappy packing. ++ ++typedef struct ++{ ++ float x0,y0,s0,t0; // top-left ++ float x1,y1,s1,t1; // bottom-right ++} stbtt_aligned_quad; ++ ++extern void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, // same data as above ++ int char_index, // character to display ++ float *xpos, float *ypos, // pointers to current position in screen pixel space ++ stbtt_aligned_quad *q, // output: quad to draw ++ int opengl_fillrule); // true if opengl fill rule; false if DX9 or earlier ++// Call GetBakedQuad with char_index = 'character - first_char', and it ++// creates the quad you need to draw and advances the current position. ++// It's inefficient; you might want to c&p it and optimize it. ++ ++ ++////////////////////////////////////////////////////////////////////////////// ++// ++// FONT LOADING ++// ++// ++ ++extern int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index); ++// Each .ttf file may have more than one font. Each has a sequential index ++// number starting from 0. Call this function to get the font offset for a ++// given index; it returns -1 if the index is out of range. A regular .ttf ++// file will only define one font and it always be at offset 0, so it will ++// return '0' for index 0, and -1 for all other indices. You can just skip ++// this step if you know it's that kind of font. ++ ++ ++// The following structure is defined publically so you can declare one on ++// the stack or as a global or etc. ++typedef struct ++{ ++ void *userdata; ++ unsigned char *data; // pointer to .ttf file ++ int fontstart; // offset of start of font ++ ++ int numGlyphs; // number of glyphs, needed for range checking ++ ++ int loca,head,glyf,hhea,hmtx; // table locations as offset from start of .ttf ++ int index_map; // a cmap mapping for our chosen character encoding ++ int indexToLocFormat; // format needed to map from glyph index to glyph ++} stbtt_fontinfo; ++ ++extern int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset); ++// Given an offset into the file that defines a font, this function builds ++// the necessary cached info for the rest of the system. You must allocate ++// the stbtt_fontinfo yourself, and stbtt_InitFont will fill it out. You don't ++// need to do anything special to free it, because the contents are a pure ++// cache with no additional data structures. Returns 0 on failure. ++ ++ ++////////////////////////////////////////////////////////////////////////////// ++// ++// CHARACTER TO GLYPH-INDEX CONVERSIOn ++ ++int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint); ++// If you're going to perform multiple operations on the same character ++// and you want a speed-up, call this function with the character you're ++// going to process, then use glyph-based functions instead of the ++// codepoint-based functions. ++ ++ ++////////////////////////////////////////////////////////////////////////////// ++// ++// CHARACTER PROPERTIES ++// ++ ++extern float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float pixels); ++// computes a scale factor to produce a font whose "height" is 'pixels' tall. ++// Height is measured as the distance from the highest ascender to the lowest ++// descender; in other words, it's equivalent to calling stbtt_GetFontVMetrics ++// and computing: ++// scale = pixels / (ascent - descent) ++// so if you prefer to measure height by the ascent only, use a similar calculation. ++ ++extern void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap); ++// ascent is the coordinate above the baseline the font extends; descent ++// is the coordinate below the baseline the font extends (i.e. it is typically negative) ++// lineGap is the spacing between one row's descent and the next row's ascent... ++// so you should advance the vertical position by "*ascent - *descent + *lineGap" ++// these are expressed in unscaled coordinates ++ ++extern void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing); ++// leftSideBearing is the offset from the current horizontal position to the left edge of the character ++// advanceWidth is the offset from the current horizontal position to the next horizontal position ++// these are expressed in unscaled coordinates ++ ++extern int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2); ++// an additional amount to add to the 'advance' value between ch1 and ch2 ++// @TODO; for now always returns 0! ++ ++extern int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1); ++// Gets the bounding box of the visible part of the glyph, in unscaled coordinates ++ ++extern void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing); ++extern int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2); ++extern int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1); ++// as above, but takes one or more glyph indices for greater efficiency ++ ++ ++////////////////////////////////////////////////////////////////////////////// ++// ++// GLYPH SHAPES (you probably don't need these, but they have to go before ++// the bitmaps for C declaration-order reasons) ++// ++ ++#ifndef STBTT_vmove // you can predefine these to use different values (but why?) ++ enum { ++ STBTT_vmove=1, ++ STBTT_vline, ++ STBTT_vcurve ++ }; ++#endif ++ ++#ifndef stbtt_vertex // you can predefine this to use different values ++ // (we share this with other code at RAD) ++ #define stbtt_vertex_type short // can't use stbtt_int16 because that's not visible in the header file ++ typedef struct ++ { ++ stbtt_vertex_type x,y,cx,cy; ++ unsigned char type,padding; ++ } stbtt_vertex; ++#endif ++ ++extern int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices); ++extern int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **vertices); ++// returns # of vertices and fills *vertices with the pointer to them ++// these are expressed in "unscaled" coordinates ++ ++extern void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices); ++// frees the data allocated above ++ ++////////////////////////////////////////////////////////////////////////////// ++// ++// BITMAP RENDERING ++// ++ ++extern void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata); ++// frees the bitmap allocated below ++ ++extern unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff); ++// allocates a large-enough single-channel 8bpp bitmap and renders the ++// specified character/glyph at the specified scale into it, with ++// antialiasing. 0 is no coverage (transparent), 255 is fully covered (opaque). ++// *width & *height are filled out with the width & height of the bitmap, ++// which is stored left-to-right, top-to-bottom. ++// ++// xoff/yoff are the offset it pixel space from the glyph origin to the top-left of the bitmap ++ ++extern void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint); ++// the same as above, but you pass in storage for the bitmap in the form ++// of 'output', with row spacing of 'out_stride' bytes. the bitmap is ++// clipped to out_w/out_h bytes. call the next function to get the ++// height and width and positioning info ++ ++extern void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); ++// get the bbox of the bitmap centered around the glyph origin; so the ++// bitmap width is ix1-ix0, height is iy1-iy0, and location to place ++// the bitmap top left is (leftSideBearing*scale,iy0). ++// (Note that the bitmap uses y-increases-down, but the shape uses ++// y-increases-up, so CodepointBitmapBox and CodepointBox are inverted.) ++ ++extern unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff); ++extern void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); ++extern void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph); ++ ++//extern void stbtt_get_true_bbox(stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); ++ ++// @TODO: don't expose this structure ++typedef struct ++{ ++ int w,h,stride; ++ unsigned char *pixels; ++} stbtt__bitmap; ++ ++extern void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, int x_off, int y_off, int invert, void *userdata); ++ ++////////////////////////////////////////////////////////////////////////////// ++// ++// Finding the right font... ++// ++// You should really just solve this offline, keep your own tables ++// of what font is what, and don't try to get it out of the .ttf file. ++// That's because getting it out of the .ttf file is really hard, because ++// the names in the file can appear in many possible encodings, in many ++// possible languages, and e.g. if you need a case-insensitive comparison, ++// the details of that depend on the encoding & language in a complex way ++// (actually underspecified in truetype, but also gigantic). ++// ++// But you can use the provided functions in two possible ways: ++// stbtt_FindMatchingFont() will use *case-sensitive* comparisons on ++// unicode-encoded names to try to find the font you want; ++// you can run this before calling stbtt_InitFont() ++// ++// stbtt_GetFontNameString() lets you get any of the various strings ++// from the file yourself and do your own comparisons on them. ++// You have to have called stbtt_InitFont() first. ++ ++ ++extern int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags); ++// returns the offset (not index) of the font that matches, or -1 if none ++// if you use STBTT_MACSTYLE_DONTCARE, use a font name like "Arial Bold". ++// if you use any other flag, use a font name like "Arial"; this checks ++// the 'macStyle' header field; i don't know if fonts set this consistently ++#define STBTT_MACSTYLE_DONTCARE 0 ++#define STBTT_MACSTYLE_BOLD 1 ++#define STBTT_MACSTYLE_ITALIC 2 ++#define STBTT_MACSTYLE_UNDERSCORE 4 ++#define STBTT_MACSTYLE_NONE 8 // <= not same as 0, this makes us check the bitfield is 0 ++ ++extern int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2); ++// returns 1/0 whether the first string interpreted as utf8 is identical to ++// the second string interpreted as big-endian utf16... useful for strings from next func ++ ++extern char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID); ++// returns the string (which may be big-endian double byte, e.g. for unicode) ++// and puts the length in bytes in *length. ++// ++// some of the values for the IDs are below; for more see the truetype spec: ++// http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html ++// http://www.microsoft.com/typography/otspec/name.htm ++ ++enum { // platformID ++ STBTT_PLATFORM_ID_UNICODE =0, ++ STBTT_PLATFORM_ID_MAC =1, ++ STBTT_PLATFORM_ID_ISO =2, ++ STBTT_PLATFORM_ID_MICROSOFT =3 ++}; ++ ++enum { // encodingID for STBTT_PLATFORM_ID_UNICODE ++ STBTT_UNICODE_EID_UNICODE_1_0 =0, ++ STBTT_UNICODE_EID_UNICODE_1_1 =1, ++ STBTT_UNICODE_EID_ISO_10646 =2, ++ STBTT_UNICODE_EID_UNICODE_2_0_BMP=3, ++ STBTT_UNICODE_EID_UNICODE_2_0_FULL=4, ++}; ++ ++enum { // encodingID for STBTT_PLATFORM_ID_MICROSOFT ++ STBTT_MS_EID_SYMBOL =0, ++ STBTT_MS_EID_UNICODE_BMP =1, ++ STBTT_MS_EID_SHIFTJIS =2, ++ STBTT_MS_EID_UNICODE_FULL =10, ++}; ++ ++enum { // encodingID for STBTT_PLATFORM_ID_MAC; same as Script Manager codes ++ STBTT_MAC_EID_ROMAN =0, STBTT_MAC_EID_ARABIC =4, ++ STBTT_MAC_EID_JAPANESE =1, STBTT_MAC_EID_HEBREW =5, ++ STBTT_MAC_EID_CHINESE_TRAD =2, STBTT_MAC_EID_GREEK =6, ++ STBTT_MAC_EID_KOREAN =3, STBTT_MAC_EID_RUSSIAN =7, ++}; ++ ++enum { // languageID for STBTT_PLATFORM_ID_MICROSOFT; same as LCID... ++ // problematic because there are e.g. 16 english LCIDs and 16 arabic LCIDs ++ STBTT_MS_LANG_ENGLISH =0x0409, STBTT_MS_LANG_ITALIAN =0x0410, ++ STBTT_MS_LANG_CHINESE =0x0804, STBTT_MS_LANG_JAPANESE =0x0411, ++ STBTT_MS_LANG_DUTCH =0x0413, STBTT_MS_LANG_KOREAN =0x0412, ++ STBTT_MS_LANG_FRENCH =0x040c, STBTT_MS_LANG_RUSSIAN =0x0419, ++ STBTT_MS_LANG_GERMAN =0x0407, STBTT_MS_LANG_SPANISH =0x0409, ++ STBTT_MS_LANG_HEBREW =0x040d, STBTT_MS_LANG_SWEDISH =0x041D, ++}; ++ ++enum { // languageID for STBTT_PLATFORM_ID_MAC ++ STBTT_MAC_LANG_ENGLISH =0 , STBTT_MAC_LANG_JAPANESE =11, ++ STBTT_MAC_LANG_ARABIC =12, STBTT_MAC_LANG_KOREAN =23, ++ STBTT_MAC_LANG_DUTCH =4 , STBTT_MAC_LANG_RUSSIAN =32, ++ STBTT_MAC_LANG_FRENCH =1 , STBTT_MAC_LANG_SPANISH =6 , ++ STBTT_MAC_LANG_GERMAN =2 , STBTT_MAC_LANG_SWEDISH =5 , ++ STBTT_MAC_LANG_HEBREW =10, STBTT_MAC_LANG_CHINESE_SIMPLIFIED =33, ++ STBTT_MAC_LANG_ITALIAN =3 , STBTT_MAC_LANG_CHINESE_TRAD =19, ++}; ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif // __STB_INCLUDE_STB_TRUETYPE_H__ ++ ++/////////////////////////////////////////////////////////////////////////////// ++/////////////////////////////////////////////////////////////////////////////// ++//// ++//// IMPLEMENTATION ++//// ++//// ++ ++#ifdef STB_TRUETYPE_IMPLEMENTATION ++ ++////////////////////////////////////////////////////////////////////////// ++// ++// accessors to parse data from file ++// ++ ++// on platforms that don't allow misaligned reads, if we want to allow ++// truetype fonts that aren't padded to alignment, define ALLOW_UNALIGNED_TRUETYPE ++ ++#define ttBYTE(p) (* (stbtt_uint8 *) (p)) ++#define ttCHAR(p) (* (stbtt_int8 *) (p)) ++#define ttFixed(p) ttLONG(p) ++ ++#if defined(STB_TRUETYPE_BIGENDIAN) && !defined(ALLOW_UNALIGNED_TRUETYPE) ++ ++ #define ttUSHORT(p) (* (stbtt_uint16 *) (p)) ++ #define ttSHORT(p) (* (stbtt_int16 *) (p)) ++ #define ttULONG(p) (* (stbtt_uint32 *) (p)) ++ #define ttLONG(p) (* (stbtt_int32 *) (p)) ++ ++#else ++ ++ stbtt_uint16 ttUSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; } ++ stbtt_int16 ttSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; } ++ stbtt_uint32 ttULONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } ++ stbtt_int32 ttLONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } ++ ++#endif ++ ++#define stbtt_tag4(p,c0,c1,c2,c3) ((p)[0] == (c0) && (p)[1] == (c1) && (p)[2] == (c2) && (p)[3] == (c3)) ++#define stbtt_tag(p,str) stbtt_tag4(p,str[0],str[1],str[2],str[3]) ++ ++static int stbtt__isfont(const stbtt_uint8 *font) ++{ ++ // check the version number ++ if (stbtt_tag(font, "1")) return 1; // TrueType 1 ++ if (stbtt_tag(font, "typ1")) return 1; // TrueType with type 1 font -- we don't support this! ++ if (stbtt_tag(font, "OTTO")) return 1; // OpenType with CFF ++ if (stbtt_tag4(font, 0,1,0,0)) return 1; // OpenType 1.0 ++ return 0; ++} ++ ++// @OPTIMIZE: binary search ++static stbtt_uint32 stbtt__find_table(stbtt_uint8 *data, stbtt_uint32 fontstart, const char *tag) ++{ ++ stbtt_int32 num_tables = ttUSHORT(data+fontstart+4); ++ stbtt_uint32 tabledir = fontstart + 12; ++ stbtt_int32 i; ++ for (i=0; i < num_tables; ++i) { ++ stbtt_uint32 loc = tabledir + 16*i; ++ if (stbtt_tag(data+loc+0, tag)) ++ return ttULONG(data+loc+8); ++ } ++ return 0; ++} ++ ++int stbtt_GetFontOffsetForIndex(const unsigned char *font_collection, int index) ++{ ++ // if it's just a font, there's only one valid index ++ if (stbtt__isfont(font_collection)) ++ return index == 0 ? 0 : -1; ++ ++ // check if it's a TTC ++ if (stbtt_tag(font_collection, "ttcf")) { ++ // version 1? ++ if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) { ++ stbtt_int32 n = ttLONG(font_collection+8); ++ if (index >= n) ++ return -1; ++ return ttULONG(font_collection+12+index*14); ++ } ++ } ++ return -1; ++} ++ ++int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data2, int fontstart) ++{ ++ stbtt_uint8 *data = (stbtt_uint8 *) data2; ++ stbtt_uint32 cmap, t; ++ stbtt_int32 i,numTables; ++ ++ info->data = data; ++ info->fontstart = fontstart; ++ ++ cmap = stbtt__find_table(data, fontstart, "cmap"); ++ info->loca = stbtt__find_table(data, fontstart, "loca"); ++ info->head = stbtt__find_table(data, fontstart, "head"); ++ info->glyf = stbtt__find_table(data, fontstart, "glyf"); ++ info->hhea = stbtt__find_table(data, fontstart, "hhea"); ++ info->hmtx = stbtt__find_table(data, fontstart, "hmtx"); ++ if (!cmap || !info->loca || !info->head || !info->glyf || !info->hhea || !info->hmtx) ++ return 0; ++ ++ t = stbtt__find_table(data, fontstart, "maxp"); ++ if (t) ++ info->numGlyphs = ttUSHORT(data+t+4); ++ else ++ info->numGlyphs = 0xffff; ++ ++ // find a cmap encoding table we understand *now* to avoid searching ++ // later. (todo: could make this installable) ++ // the same regardless of glyph. ++ numTables = ttUSHORT(data + cmap + 2); ++ info->index_map = 0; ++ for (i=0; i < numTables; ++i) { ++ stbtt_uint32 encoding_record = cmap + 4 + 8 * i; ++ // find an encoding we understand: ++ switch(ttUSHORT(data+encoding_record)) { ++ case STBTT_PLATFORM_ID_MICROSOFT: ++ switch (ttUSHORT(data+encoding_record+2)) { ++ case STBTT_MS_EID_UNICODE_BMP: ++ case STBTT_MS_EID_UNICODE_FULL: ++ // MS/Unicode ++ info->index_map = cmap + ttULONG(data+encoding_record+4); ++ break; ++ } ++ break; ++ } ++ } ++ if (info->index_map == 0) ++ return 0; ++ ++ info->indexToLocFormat = ttUSHORT(data+info->head + 50); ++ return 1; ++} ++ ++int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint) ++{ ++ stbtt_uint8 *data = info->data; ++ stbtt_uint32 index_map = info->index_map; ++ ++ stbtt_uint16 format = ttUSHORT(data + index_map + 0); ++ if (format == 0) { // apple byte encoding ++ stbtt_int32 bytes = ttUSHORT(data + index_map + 2); ++ if (unicode_codepoint < bytes-6) ++ return ttBYTE(data + index_map + 6 + unicode_codepoint); ++ return 0; ++ } else if (format == 6) { ++ stbtt_uint32 first = ttUSHORT(data + index_map + 6); ++ stbtt_uint32 count = ttUSHORT(data + index_map + 8); ++ if ((stbtt_uint32) unicode_codepoint >= first && (stbtt_uint32) unicode_codepoint < first+count) ++ return ttUSHORT(data + index_map + 10 + (unicode_codepoint - first)*2); ++ return 0; ++ } else if (format == 2) { ++ STBTT_assert(0); // @TODO: high-byte mapping for japanese/chinese/korean ++ return 0; ++ } else if (format == 4) { // standard mapping for windows fonts: binary search collection of ranges ++ stbtt_uint16 segcount = ttUSHORT(data+index_map+6) >> 1; ++ stbtt_uint16 searchRange = ttUSHORT(data+index_map+8) >> 1; ++ stbtt_uint16 entrySelector = ttUSHORT(data+index_map+10); ++ stbtt_uint16 rangeShift = ttUSHORT(data+index_map+12) >> 1; ++ stbtt_uint16 item, offset, start, end; ++ ++ // do a binary search of the segments ++ stbtt_uint32 endCount = index_map + 14; ++ stbtt_uint32 search = endCount; ++ ++ if (unicode_codepoint > 0xffff) ++ return 0; ++ ++ // they lie from endCount .. endCount + segCount ++ // but searchRange is the nearest power of two, so... ++ if (unicode_codepoint >= ttUSHORT(data + search + rangeShift*2)) ++ search += rangeShift*2; ++ ++ // now decrement to bias correctly to find smallest ++ search -= 2; ++ while (entrySelector) { ++ stbtt_uint16 start, end; ++ searchRange >>= 1; ++ start = ttUSHORT(data + search + 2 + segcount*2 + 2); ++ end = ttUSHORT(data + search + 2); ++ start = ttUSHORT(data + search + searchRange*2 + segcount*2 + 2); ++ end = ttUSHORT(data + search + searchRange*2); ++ if (unicode_codepoint > end) ++ search += searchRange*2; ++ --entrySelector; ++ } ++ search += 2; ++ ++ item = (stbtt_uint16) ((search - endCount) >> 1); ++ ++ STBTT_assert(unicode_codepoint <= ttUSHORT(data + endCount + 2*item)); ++ start = ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item); ++ end = ttUSHORT(data + index_map + 14 + 2 + 2*item); ++ if (unicode_codepoint < start) ++ return 0; ++ ++ offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item); ++ if (offset == 0) ++ return unicode_codepoint + ttSHORT(data + index_map + 14 + segcount*4 + 2 + 2*item); ++ ++ return ttUSHORT(data + offset + (unicode_codepoint-start)*2 + index_map + 14 + segcount*6 + 2 + 2*item); ++ } else if (format == 12) { ++ stbtt_uint16 ngroups = ttUSHORT(data+index_map+6); ++ stbtt_int32 low,high; ++ low = 0; high = (stbtt_int32)ngroups; ++ // Binary search the right group. ++ while (low <= high) { ++ stbtt_int32 mid = low + ((high-low) >> 1); // rounds down, so low <= mid < high ++ stbtt_uint32 start_char = ttULONG(data+index_map+16+mid*12); ++ stbtt_uint32 end_char = ttULONG(data+index_map+16+mid*12+4); ++ if ((stbtt_uint32) unicode_codepoint < start_char) ++ high = mid-1; ++ else if ((stbtt_uint32) unicode_codepoint > end_char) ++ low = mid+1; ++ else { ++ stbtt_uint32 start_glyph = ttULONG(data+index_map+16+mid*12+8); ++ return start_glyph + unicode_codepoint-start_char; ++ } ++ } ++ return 0; // not found ++ } ++ // @TODO ++ STBTT_assert(0); ++ return 0; ++} ++ ++int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices) ++{ ++ return stbtt_GetGlyphShape(info, stbtt_FindGlyphIndex(info, unicode_codepoint), vertices); ++} ++ ++static void stbtt_setvertex(stbtt_vertex *v, stbtt_uint8 type, stbtt_int16 x, stbtt_int16 y, stbtt_int16 cx, stbtt_int16 cy) ++{ ++ v->type = type; ++ v->x = x; ++ v->y = y; ++ v->cx = cx; ++ v->cy = cy; ++} ++ ++static int stbtt__GetGlyfOffset(const stbtt_fontinfo *info, int glyph_index) ++{ ++ int g1,g2; ++ ++ if (glyph_index >= info->numGlyphs) return -1; // glyph index out of range ++ if (info->indexToLocFormat >= 2) return -1; // unknown index->glyph map format ++ ++ if (info->indexToLocFormat == 0) { ++ g1 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2) * 2; ++ g2 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2 + 2) * 2; ++ } else { ++ g1 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4); ++ g2 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4 + 4); ++ } ++ ++ return g1==g2 ? -1 : g1; // if length is 0, return -1 ++} ++ ++int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1) ++{ ++ int g = stbtt__GetGlyfOffset(info, glyph_index); ++ if (g < 0) return 0; ++ ++ if (x0) *x0 = ttSHORT(info->data + g + 2); ++ if (y0) *y0 = ttSHORT(info->data + g + 4); ++ if (x1) *x1 = ttSHORT(info->data + g + 6); ++ if (y1) *y1 = ttSHORT(info->data + g + 8); ++ return 1; ++} ++ ++int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1) ++{ ++ return stbtt_GetGlyphBox(info, stbtt_FindGlyphIndex(info,codepoint), x0,y0,x1,y1); ++} ++ ++int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) ++{ ++ stbtt_int16 numberOfContours; ++ stbtt_uint8 *endPtsOfContours; ++ stbtt_uint8 *data = info->data; ++ stbtt_vertex *vertices=0; ++ int num_vertices=0; ++ int g = stbtt__GetGlyfOffset(info, glyph_index); ++ ++ *pvertices = NULL; ++ ++ if (g < 0) return 0; ++ ++ numberOfContours = ttSHORT(data + g); ++ ++ if (numberOfContours > 0) { ++ stbtt_uint8 flags=0,flagcount; ++ stbtt_int32 ins, i,j=0,m,n, next_move, was_off=0, off; ++ stbtt_int16 x,y,cx,cy,sx,sy; ++ stbtt_uint8 *points; ++ endPtsOfContours = (data + g + 10); ++ ins = ttUSHORT(data + g + 10 + numberOfContours * 2); ++ points = data + g + 10 + numberOfContours * 2 + 2 + ins; ++ ++ n = 1+ttUSHORT(endPtsOfContours + numberOfContours*2-2); ++ ++ m = n + numberOfContours; // a loose bound on how many vertices we might need ++ vertices = (stbtt_vertex *) STBTT_malloc(m * sizeof(vertices[0]), info->userdata); ++ if (vertices == 0) ++ return 0; ++ ++ next_move = 0; ++ flagcount=0; ++ ++ // in first pass, we load uninterpreted data into the allocated array ++ // above, shifted to the end of the array so we won't overwrite it when ++ // we create our final data starting from the front ++ ++ off = m - n; // starting offset for uninterpreted data, regardless of how m ends up being calculated ++ ++ // first load flags ++ ++ for (i=0; i < n; ++i) { ++ if (flagcount == 0) { ++ flags = *points++; ++ if (flags & 8) ++ flagcount = *points++; ++ } else ++ --flagcount; ++ vertices[off+i].type = flags; ++ } ++ ++ // now load x coordinates ++ x=0; ++ for (i=0; i < n; ++i) { ++ flags = vertices[off+i].type; ++ if (flags & 2) { ++ stbtt_int16 dx = *points++; ++ x += (flags & 16) ? dx : -dx; // ??? ++ } else { ++ if (!(flags & 16)) { ++ x = x + (stbtt_int16) (points[0]*256 + points[1]); ++ points += 2; ++ } ++ } ++ vertices[off+i].x = x; ++ } ++ ++ // now load y coordinates ++ y=0; ++ for (i=0; i < n; ++i) { ++ flags = vertices[off+i].type; ++ if (flags & 4) { ++ stbtt_int16 dy = *points++; ++ y += (flags & 32) ? dy : -dy; // ??? ++ } else { ++ if (!(flags & 32)) { ++ y = y + (stbtt_int16) (points[0]*256 + points[1]); ++ points += 2; ++ } ++ } ++ vertices[off+i].y = y; ++ } ++ ++ // now convert them to our format ++ num_vertices=0; ++ sx = sy = cx = cy = 0; ++ for (i=0; i < n; ++i) { ++ flags = vertices[off+i].type; ++ x = (stbtt_int16) vertices[off+i].x; ++ y = (stbtt_int16) vertices[off+i].y; ++ if (next_move == i) { ++ // when we get to the end, we have to close the shape explicitly ++ if (i != 0) { ++ if (was_off) ++ stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve,sx,sy,cx,cy); ++ else ++ stbtt_setvertex(&vertices[num_vertices++], STBTT_vline,sx,sy,0,0); ++ } ++ ++ // now start the new one ++ stbtt_setvertex(&vertices[num_vertices++], STBTT_vmove,x,y,0,0); ++ next_move = 1 + ttUSHORT(endPtsOfContours+j*2); ++ ++j; ++ was_off = 0; ++ sx = x; ++ sy = y; ++ } else { ++ if (!(flags & 1)) { // if it's a curve ++ if (was_off) // two off-curve control points in a row means interpolate an on-curve midpoint ++ stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+x)>>1, (cy+y)>>1, cx, cy); ++ cx = x; ++ cy = y; ++ was_off = 1; ++ } else { ++ if (was_off) ++ stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, x,y, cx, cy); ++ else ++ stbtt_setvertex(&vertices[num_vertices++], STBTT_vline, x,y,0,0); ++ was_off = 0; ++ } ++ } ++ } ++ if (i != 0) { ++ if (was_off) ++ stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve,sx,sy,cx,cy); ++ else ++ stbtt_setvertex(&vertices[num_vertices++], STBTT_vline,sx,sy,0,0); ++ } ++ } else if (numberOfContours == -1) { ++ // Compound shapes. ++ int more = 1; ++ stbtt_uint8 *comp = data + g + 10; ++ num_vertices = 0; ++ vertices = 0; ++ while (more) { ++ stbtt_uint16 flags, gidx; ++ int comp_num_verts = 0, i; ++ stbtt_vertex *comp_verts = 0, *tmp = 0; ++ float mtx[6] = {1,0,0,1,0,0}, m, n; ++ ++ flags = ttSHORT(comp); comp+=2; ++ gidx = ttSHORT(comp); comp+=2; ++ ++ if (flags & 2) { // XY values ++ if (flags & 1) { // shorts ++ mtx[4] = ttSHORT(comp); comp+=2; ++ mtx[5] = ttSHORT(comp); comp+=2; ++ } else { ++ mtx[4] = ttCHAR(comp); comp+=1; ++ mtx[5] = ttCHAR(comp); comp+=1; ++ } ++ } ++ else { ++ // @TODO handle matching point ++ STBTT_assert(0); ++ } ++ if (flags & (1<<3)) { // WE_HAVE_A_SCALE ++ mtx[0] = mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; ++ mtx[1] = mtx[2] = 0; ++ } else if (flags & (1<<6)) { // WE_HAVE_AN_X_AND_YSCALE ++ mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; ++ mtx[1] = mtx[2] = 0; ++ mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; ++ } else if (flags & (1<<7)) { // WE_HAVE_A_TWO_BY_TWO ++ mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; ++ mtx[1] = ttSHORT(comp)/16384.0f; comp+=2; ++ mtx[2] = ttSHORT(comp)/16384.0f; comp+=2; ++ mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; ++ } ++ ++ // Find transformation scales. ++ m = (float) sqrt(mtx[0]*mtx[0] + mtx[1]*mtx[1]); ++ n = (float) sqrt(mtx[2]*mtx[2] + mtx[3]*mtx[3]); ++ ++ // Get indexed glyph. ++ comp_num_verts = stbtt_GetGlyphShape(info, gidx, &comp_verts); ++ if (comp_num_verts > 0) { ++ // Transform vertices. ++ for (i = 0; i < comp_num_verts; ++i) { ++ stbtt_vertex* v = &comp_verts[i]; ++ stbtt_vertex_type x,y; ++ x=v->x; y=v->y; ++ v->x = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); ++ v->y = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); ++ x=v->cx; y=v->cy; ++ v->cx = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); ++ v->cy = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); ++ } ++ // Append vertices. ++ tmp = (stbtt_vertex*)STBTT_malloc((num_vertices+comp_num_verts)*sizeof(stbtt_vertex), info->userdata); ++ if (!tmp) { ++ if (vertices) STBTT_free(vertices, info->userdata); ++ if (comp_verts) STBTT_free(comp_verts, info->userdata); ++ return 0; ++ } ++ if (num_vertices > 0) memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex)); ++ memcpy(tmp+num_vertices, comp_verts, comp_num_verts*sizeof(stbtt_vertex)); ++ if (vertices) STBTT_free(vertices, info->userdata); ++ vertices = tmp; ++ STBTT_free(comp_verts, info->userdata); ++ num_vertices += comp_num_verts; ++ } ++ // More components ? ++ more = flags & (1<<5); ++ } ++ } else if (numberOfContours < 0) { ++ // @TODO other compound variations? ++ STBTT_assert(0); ++ } else { ++ // numberOfCounters == 0, do nothing ++ } ++ ++ *pvertices = vertices; ++ return num_vertices; ++} ++ ++void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing) ++{ ++ stbtt_uint16 numOfLongHorMetrics = ttUSHORT(info->data+info->hhea + 34); ++ if (glyph_index < numOfLongHorMetrics) { ++ if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*glyph_index); ++ if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*glyph_index + 2); ++ } else { ++ if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*(numOfLongHorMetrics-1)); ++ if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*numOfLongHorMetrics + 2*(glyph_index - numOfLongHorMetrics)); ++ } ++} ++ ++int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo * /*info*/, int /*glyph1*/, int /*glyph2*/) ++{ ++ return 0; ++} ++ ++int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo * /*info*/, int /*ch1*/, int /*ch2*/) ++{ ++ return 0; ++} ++ ++void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing) ++{ ++ stbtt_GetGlyphHMetrics(info, stbtt_FindGlyphIndex(info,codepoint), advanceWidth, leftSideBearing); ++} ++ ++void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap) ++{ ++ if (ascent ) *ascent = ttSHORT(info->data+info->hhea + 4); ++ if (descent) *descent = ttSHORT(info->data+info->hhea + 6); ++ if (lineGap) *lineGap = ttSHORT(info->data+info->hhea + 8); ++} ++ ++float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float height) ++{ ++ int fheight = ttSHORT(info->data + info->hhea + 4) - ttSHORT(info->data + info->hhea + 6); ++ return (float) height / fheight; ++} ++ ++void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v) ++{ ++ STBTT_free(v, info->userdata); ++} ++ ++////////////////////////////////////////////////////////////////////////////// ++// ++// antialiasing software rasterizer ++// ++ ++void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) ++{ ++ int x0,y0,x1,y1; ++ if (!stbtt_GetGlyphBox(font, glyph, &x0,&y0,&x1,&y1)) ++ x0=y0=x1=y1=0; // e.g. space character ++ // now move to integral bboxes (treating pixels as little squares, what pixels get touched)? ++ if (ix0) *ix0 = STBTT_ifloor(x0 * scale_x); ++ if (iy0) *iy0 = -STBTT_iceil (y1 * scale_y); ++ if (ix1) *ix1 = STBTT_iceil (x1 * scale_x); ++ if (iy1) *iy1 = -STBTT_ifloor(y0 * scale_y); ++} ++ ++void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) ++{ ++ stbtt_GetGlyphBitmapBox(font, stbtt_FindGlyphIndex(font,codepoint), scale_x, scale_y, ix0,iy0,ix1,iy1); ++} ++ ++typedef struct stbtt__edge { ++ float x0,y0, x1,y1; ++ int invert; ++} stbtt__edge; ++ ++typedef struct stbtt__active_edge ++{ ++ int x,dx; ++ float ey; ++ struct stbtt__active_edge *next; ++ int valid; ++} stbtt__active_edge; ++ ++#define FIXSHIFT 10 ++#define FIX (1 << FIXSHIFT) ++#define FIXMASK (FIX-1) ++ ++static stbtt__active_edge *new_active(stbtt__edge *e, int off_x, float start_point, void *userdata) ++{ ++ stbtt__active_edge *z = (stbtt__active_edge *) STBTT_malloc(sizeof(*z), userdata); // @TODO: make a pool of these!!! ++ float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); ++ STBTT_assert(e->y0 <= start_point); ++ if (!z) return z; ++ // round dx down to avoid going too far ++ if (dxdy < 0) ++ z->dx = -STBTT_ifloor(FIX * -dxdy); ++ else ++ z->dx = STBTT_ifloor(FIX * dxdy); ++ z->x = STBTT_ifloor(FIX * (e->x0 + dxdy * (start_point - e->y0))); ++ z->x -= off_x * FIX; ++ z->ey = e->y1; ++ z->next = 0; ++ z->valid = e->invert ? 1 : -1; ++ return z; ++} ++ ++// note: this routine clips fills that extend off the edges... ideally this ++// wouldn't happen, but it could happen if the truetype glyph bounding boxes ++// are wrong, or if the user supplies a too-small bitmap ++static void stbtt__fill_active_edges(unsigned char *scanline, int len, stbtt__active_edge *e, int max_weight) ++{ ++ // non-zero winding fill ++ int x0=0, w=0; ++ ++ while (e) { ++ if (w == 0) { ++ // if we're currently at zero, we need to record the edge start point ++ x0 = e->x; w += e->valid; ++ } else { ++ int x1 = e->x; w += e->valid; ++ // if we went to zero, we need to draw ++ if (w == 0) { ++ int i = x0 >> FIXSHIFT; ++ int j = x1 >> FIXSHIFT; ++ ++ if (i < len && j >= 0) { ++ if (i == j) { ++ // x0,x1 are the same pixel, so compute combined coverage ++ scanline[i] = scanline[i] + (stbtt_uint8) ((x1 - x0) * max_weight >> FIXSHIFT); ++ } else { ++ if (i >= 0) // add antialiasing for x0 ++ scanline[i] = scanline[i] + (stbtt_uint8) (((FIX - (x0 & FIXMASK)) * max_weight) >> FIXSHIFT); ++ else ++ i = -1; // clip ++ ++ if (j < len) // add antialiasing for x1 ++ scanline[j] = scanline[j] + (stbtt_uint8) (((x1 & FIXMASK) * max_weight) >> FIXSHIFT); ++ else ++ j = len; // clip ++ ++ for (++i; i < j; ++i) // fill pixels between x0 and x1 ++ scanline[i] = scanline[i] + (stbtt_uint8) max_weight; ++ } ++ } ++ } ++ } ++ ++ e = e->next; ++ } ++} ++ ++static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata) ++{ ++ stbtt__active_edge *active = NULL; ++ int y,j=0; ++ int max_weight = (255 / vsubsample); // weight per vertical scanline ++ int s; // vertical subsample index ++ unsigned char scanline_data[512], *scanline; ++ ++ if (result->w > 512) ++ scanline = (unsigned char *) STBTT_malloc(result->w, userdata); ++ else ++ scanline = scanline_data; ++ ++ y = off_y * vsubsample; ++ e[n].y0 = (off_y + result->h) * (float) vsubsample + 1; ++ ++ while (j < result->h) { ++ STBTT_memset(scanline, 0, result->w); ++ for (s=0; s < vsubsample; ++s) { ++ // find center of pixel for this scanline ++ float scan_y = y + 0.5f; ++ stbtt__active_edge **step = &active; ++ ++ // update all active edges; ++ // remove all active edges that terminate before the center of this scanline ++ while (*step) { ++ stbtt__active_edge * z = *step; ++ if (z->ey <= scan_y) { ++ *step = z->next; // delete from list ++ STBTT_assert(z->valid); ++ z->valid = 0; ++ STBTT_free(z, userdata); ++ } else { ++ z->x += z->dx; // advance to position for current scanline ++ step = &((*step)->next); // advance through list ++ } ++ } ++ ++ // resort the list if needed ++ for(;;) { ++ int changed=0; ++ step = &active; ++ while (*step && (*step)->next) { ++ if ((*step)->x > (*step)->next->x) { ++ stbtt__active_edge *t = *step; ++ stbtt__active_edge *q = t->next; ++ ++ t->next = q->next; ++ q->next = t; ++ *step = q; ++ changed = 1; ++ } ++ step = &(*step)->next; ++ } ++ if (!changed) break; ++ } ++ ++ // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline ++ while (e->y0 <= scan_y) { ++ if (e->y1 > scan_y) { ++ stbtt__active_edge *z = new_active(e, off_x, scan_y, userdata); ++ // find insertion point ++ if (active == NULL) ++ active = z; ++ else if (z->x < active->x) { ++ // insert at front ++ z->next = active; ++ active = z; ++ } else { ++ // find thing to insert AFTER ++ stbtt__active_edge *p = active; ++ while (p->next && p->next->x < z->x) ++ p = p->next; ++ // at this point, p->next->x is NOT < z->x ++ z->next = p->next; ++ p->next = z; ++ } ++ } ++ ++e; ++ } ++ ++ // now process all active edges in XOR fashion ++ if (active) ++ stbtt__fill_active_edges(scanline, result->w, active, max_weight); ++ ++ ++y; ++ } ++ STBTT_memcpy(result->pixels + j * result->stride, scanline, result->w); ++ ++j; ++ } ++ ++ while (active) { ++ stbtt__active_edge *z = active; ++ active = active->next; ++ STBTT_free(z, userdata); ++ } ++ ++ if (scanline != scanline_data) ++ STBTT_free(scanline, userdata); ++} ++ ++static int stbtt__edge_compare(const void *p, const void *q) ++{ ++ stbtt__edge *a = (stbtt__edge *) p; ++ stbtt__edge *b = (stbtt__edge *) q; ++ ++ if (a->y0 < b->y0) return -1; ++ if (a->y0 > b->y0) return 1; ++ return 0; ++} ++ ++typedef struct ++{ ++ float x,y; ++} stbtt__point; ++ ++static void stbtt__rasterize(stbtt__bitmap *result, stbtt__point *pts, int *wcount, int windings, float scale_x, float scale_y, int off_x, int off_y, int invert, void *userdata) ++{ ++ float y_scale_inv = invert ? -scale_y : scale_y; ++ stbtt__edge *e; ++ int n,i,j,k,m; ++ int vsubsample = result->h < 8 ? 15 : 5; ++ // vsubsample should divide 255 evenly; otherwise we won't reach full opacity ++ ++ // now we have to blow out the windings into explicit edge lists ++ n = 0; ++ for (i=0; i < windings; ++i) ++ n += wcount[i]; ++ ++ e = (stbtt__edge *) STBTT_malloc(sizeof(*e) * (n+1), userdata); // add an extra one as a sentinel ++ if (e == 0) return; ++ n = 0; ++ ++ m=0; ++ for (i=0; i < windings; ++i) { ++ stbtt__point *p = pts + m; ++ m += wcount[i]; ++ j = wcount[i]-1; ++ for (k=0; k < wcount[i]; j=k++) { ++ int a=k,b=j; ++ // skip the edge if horizontal ++ if (p[j].y == p[k].y) ++ continue; ++ // add edge from j to k to the list ++ e[n].invert = 0; ++ if (invert ? p[j].y > p[k].y : p[j].y < p[k].y) { ++ e[n].invert = 1; ++ a=j,b=k; ++ } ++ e[n].x0 = p[a].x * scale_x; ++ e[n].y0 = p[a].y * y_scale_inv * vsubsample; ++ e[n].x1 = p[b].x * scale_x; ++ e[n].y1 = p[b].y * y_scale_inv * vsubsample; ++ ++n; ++ } ++ } ++ ++ // now sort the edges by their highest point (should snap to integer, and then by x) ++ STBTT_sort(e, n, sizeof(e[0]), stbtt__edge_compare); ++ ++ // now, traverse the scanlines and find the intersections on each scanline, use xor winding rule ++ stbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y, userdata); ++ ++ STBTT_free(e, userdata); ++} ++ ++static void stbtt__add_point(stbtt__point *points, int n, float x, float y) ++{ ++ if (!points) return; // during first pass, it's unallocated ++ points[n].x = x; ++ points[n].y = y; ++} ++ ++// tesselate until threshhold p is happy... @TODO warped to compensate for non-linear stretching ++static int stbtt__tesselate_curve(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float objspace_flatness_squared, int n) ++{ ++ // midpoint ++ float mx = (x0 + 2*x1 + x2)/4; ++ float my = (y0 + 2*y1 + y2)/4; ++ // versus directly drawn line ++ float dx = (x0+x2)/2 - mx; ++ float dy = (y0+y2)/2 - my; ++ if (n > 16) // 65536 segments on one curve better be enough! ++ return 1; ++ if (dx*dx+dy*dy > objspace_flatness_squared) { // half-pixel error allowed... need to be smaller if AA ++ stbtt__tesselate_curve(points, num_points, x0,y0, (x0+x1)/2.0f,(y0+y1)/2.0f, mx,my, objspace_flatness_squared,n+1); ++ stbtt__tesselate_curve(points, num_points, mx,my, (x1+x2)/2.0f,(y1+y2)/2.0f, x2,y2, objspace_flatness_squared,n+1); ++ } else { ++ stbtt__add_point(points, *num_points,x2,y2); ++ *num_points = *num_points+1; ++ } ++ return 1; ++} ++ ++// returns number of contours ++stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours, void *userdata) ++{ ++ stbtt__point *points=0; ++ int num_points=0; ++ ++ float objspace_flatness_squared = objspace_flatness * objspace_flatness; ++ int i,n=0,start=0, pass; ++ ++ // count how many "moves" there are to get the contour count ++ for (i=0; i < num_verts; ++i) ++ if (vertices[i].type == STBTT_vmove) ++ ++n; ++ ++ *num_contours = n; ++ if (n == 0) return 0; ++ ++ *contour_lengths = (int *) STBTT_malloc(sizeof(**contour_lengths) * n, userdata); ++ ++ if (*contour_lengths == 0) { ++ *num_contours = 0; ++ return 0; ++ } ++ ++ // make two passes through the points so we don't need to realloc ++ for (pass=0; pass < 2; ++pass) { ++ float x=0,y=0; ++ if (pass == 1) { ++ points = (stbtt__point *) STBTT_malloc(num_points * sizeof(points[0]), userdata); ++ if (points == NULL) goto error; ++ } ++ num_points = 0; ++ n= -1; ++ for (i=0; i < num_verts; ++i) { ++ switch (vertices[i].type) { ++ case STBTT_vmove: ++ // start the next contour ++ if (n >= 0) ++ (*contour_lengths)[n] = num_points - start; ++ ++n; ++ start = num_points; ++ ++ x = vertices[i].x, y = vertices[i].y; ++ stbtt__add_point(points, num_points++, x,y); ++ break; ++ case STBTT_vline: ++ x = vertices[i].x, y = vertices[i].y; ++ stbtt__add_point(points, num_points++, x, y); ++ break; ++ case STBTT_vcurve: ++ stbtt__tesselate_curve(points, &num_points, x,y, ++ vertices[i].cx, vertices[i].cy, ++ vertices[i].x, vertices[i].y, ++ objspace_flatness_squared, 0); ++ x = vertices[i].x, y = vertices[i].y; ++ break; ++ } ++ } ++ (*contour_lengths)[n] = num_points - start; ++ } ++ ++ return points; ++error: ++ STBTT_free(points, userdata); ++ STBTT_free(*contour_lengths, userdata); ++ *contour_lengths = 0; ++ *num_contours = 0; ++ return NULL; ++} ++ ++void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, int x_off, int y_off, int invert, void *userdata) ++{ ++ float scale = scale_x > scale_y ? scale_y : scale_x; ++ int winding_count, *winding_lengths; ++ stbtt__point *windings = stbtt_FlattenCurves(vertices, num_verts, flatness_in_pixels / scale, &winding_lengths, &winding_count, userdata); ++ if (windings) { ++ stbtt__rasterize(result, windings, winding_lengths, winding_count, scale_x, scale_y, x_off, y_off, invert, userdata); ++ STBTT_free(winding_lengths, userdata); ++ STBTT_free(windings, userdata); ++ } ++} ++ ++void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata) ++{ ++ STBTT_free(bitmap, userdata); ++} ++ ++unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff) ++{ ++ int ix0,iy0,ix1,iy1; ++ stbtt__bitmap gbm; ++ stbtt_vertex *vertices; ++ int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); ++ ++ if (scale_x == 0) scale_x = scale_y; ++ if (scale_y == 0) { ++ if (scale_x == 0) return NULL; ++ scale_y = scale_x; ++ } ++ ++ stbtt_GetGlyphBitmapBox(info, glyph, scale_x, scale_y, &ix0,&iy0,&ix1,&iy1); ++ ++ // now we get the size ++ gbm.w = (ix1 - ix0); ++ gbm.h = (iy1 - iy0); ++ gbm.pixels = NULL; // in case we error ++ ++ if (width ) *width = gbm.w; ++ if (height) *height = gbm.h; ++ if (xoff ) *xoff = ix0; ++ if (yoff ) *yoff = iy0; ++ ++ if (gbm.w && gbm.h) { ++ gbm.pixels = (unsigned char *) STBTT_malloc(gbm.w * gbm.h, info->userdata); ++ if (gbm.pixels) { ++ gbm.stride = gbm.w; ++ ++ stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, ix0, iy0, 1, info->userdata); ++ } ++ } ++ STBTT_free(vertices, info->userdata); ++ return gbm.pixels; ++} ++ ++void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph) ++{ ++ int ix0,iy0; ++ stbtt_vertex *vertices; ++ int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); ++ stbtt__bitmap gbm; ++ ++ stbtt_GetGlyphBitmapBox(info, glyph, scale_x, scale_y, &ix0,&iy0,0,0); ++ gbm.pixels = output; ++ gbm.w = out_w; ++ gbm.h = out_h; ++ gbm.stride = out_stride; ++ ++ if (gbm.w && gbm.h) ++ stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, ix0,iy0, 1, info->userdata); ++ ++ STBTT_free(vertices, info->userdata); ++} ++ ++unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff) ++{ ++ return stbtt_GetGlyphBitmap(info, scale_x, scale_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff); ++} ++ ++void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint) ++{ ++ stbtt_MakeGlyphBitmap(info, output, out_w, out_h, out_stride, scale_x, scale_y, stbtt_FindGlyphIndex(info,codepoint)); ++} ++ ++////////////////////////////////////////////////////////////////////////////// ++// ++// bitmap baking ++// ++// This is SUPER-SHITTY packing to keep source code small ++ ++extern int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) ++ float pixel_height, // height of font in pixels ++ unsigned char *pixels, int pw, int ph, // bitmap to be filled in ++ int first_char, int num_chars, // characters to bake ++ stbtt_bakedchar *chardata) ++{ ++ float scale; ++ int x,y,bottom_y, i; ++ stbtt_fontinfo f; ++ stbtt_InitFont(&f, data, offset); ++ STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels ++ x=y=1; ++ bottom_y = 1; ++ ++ scale = stbtt_ScaleForPixelHeight(&f, pixel_height); ++ ++ for (i=0; i < num_chars; ++i) { ++ int advance, lsb, x0,y0,x1,y1,gw,gh; ++ int g = stbtt_FindGlyphIndex(&f, first_char + i); ++ stbtt_GetGlyphHMetrics(&f, g, &advance, &lsb); ++ stbtt_GetGlyphBitmapBox(&f, g, scale,scale, &x0,&y0,&x1,&y1); ++ gw = x1-x0; ++ gh = y1-y0; ++ if (x + gw + 1 >= pw) ++ y = bottom_y, x = 1; // advance to next row ++ if (y + gh + 1 >= ph) // check if it fits vertically AFTER potentially moving to next row ++ return -i; ++ STBTT_assert(x+gw < pw); ++ STBTT_assert(y+gh < ph); ++ stbtt_MakeGlyphBitmap(&f, pixels+x+y*pw, gw,gh,pw, scale,scale, g); ++ chardata[i].x0 = (stbtt_int16) x; ++ chardata[i].y0 = (stbtt_int16) y; ++ chardata[i].x1 = (stbtt_int16) (x + gw); ++ chardata[i].y1 = (stbtt_int16) (y + gh); ++ chardata[i].xadvance = scale * advance; ++ chardata[i].xoff = (float) x0; ++ chardata[i].yoff = (float) y0; ++ x = x + gw + 2; ++ if (y+gh+2 > bottom_y) ++ bottom_y = y+gh+2; ++ } ++ return bottom_y; ++} ++ ++void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int opengl_fillrule) ++{ ++ float d3d_bias = opengl_fillrule ? 0 : -0.5f; ++ float ipw = 1.0f / pw, iph = 1.0f / ph; ++ stbtt_bakedchar *b = chardata + char_index; ++ int round_x = STBTT_ifloor((*xpos + b->xoff) + 0.5); ++ int round_y = STBTT_ifloor((*ypos + b->yoff) + 0.5); ++ ++ q->x0 = round_x + d3d_bias; ++ q->y0 = round_y + d3d_bias; ++ q->x1 = round_x + b->x1 - b->x0 + d3d_bias; ++ q->y1 = round_y + b->y1 - b->y0 + d3d_bias; ++ ++ q->s0 = b->x0 * ipw; ++ q->t0 = b->y0 * ipw; ++ q->s1 = b->x1 * iph; ++ q->t1 = b->y1 * iph; ++ ++ *xpos += b->xadvance; ++} ++ ++////////////////////////////////////////////////////////////////////////////// ++// ++// font name matching -- recommended not to use this ++// ++ ++// check if a utf8 string contains a prefix which is the utf16 string; if so return length of matching utf8 string ++static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(stbtt_uint8 *s1, stbtt_int32 len1, stbtt_uint8 *s2, stbtt_int32 len2) ++{ ++ stbtt_int32 i=0; ++ ++ // convert utf16 to utf8 and compare the results while converting ++ while (len2) { ++ stbtt_uint16 ch = s2[0]*256 + s2[1]; ++ if (ch < 0x80) { ++ if (i >= len1) return -1; ++ if (s1[i++] != ch) return -1; ++ } else if (ch < 0x800) { ++ if (i+1 >= len1) return -1; ++ if (s1[i++] != 0xc0 + (ch >> 6)) return -1; ++ if (s1[i++] != 0x80 + (ch & 0x3f)) return -1; ++ } else if (ch >= 0xd800 && ch < 0xdc00) { ++ stbtt_uint32 c; ++ stbtt_uint16 ch2 = s2[2]*256 + s2[3]; ++ if (i+3 >= len1) return -1; ++ c = ((ch - 0xd800) << 10) + (ch2 - 0xdc00) + 0x10000; ++ if (s1[i++] != 0xf0 + (c >> 18)) return -1; ++ if (s1[i++] != 0x80 + ((c >> 12) & 0x3f)) return -1; ++ if (s1[i++] != 0x80 + ((c >> 6) & 0x3f)) return -1; ++ if (s1[i++] != 0x80 + ((c ) & 0x3f)) return -1; ++ s2 += 2; // plus another 2 below ++ len2 -= 2; ++ } else if (ch >= 0xdc00 && ch < 0xe000) { ++ return -1; ++ } else { ++ if (i+2 >= len1) return -1; ++ if (s1[i++] != 0xe0 + (ch >> 12)) return -1; ++ if (s1[i++] != 0x80 + ((ch >> 6) & 0x3f)) return -1; ++ if (s1[i++] != 0x80 + ((ch ) & 0x3f)) return -1; ++ } ++ s2 += 2; ++ len2 -= 2; ++ } ++ return i; ++} ++ ++int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2) ++{ ++ return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((stbtt_uint8*) s1, len1, (stbtt_uint8*) s2, len2); ++} ++ ++// returns results in whatever encoding you request... but note that 2-byte encodings ++// will be BIG-ENDIAN... use stbtt_CompareUTF8toUTF16_bigendian() to compare ++char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID) ++{ ++ stbtt_int32 i,count,stringOffset; ++ stbtt_uint8 *fc = font->data; ++ stbtt_uint32 offset = font->fontstart; ++ stbtt_uint32 nm = stbtt__find_table(fc, offset, "name"); ++ if (!nm) return NULL; ++ ++ count = ttUSHORT(fc+nm+2); ++ stringOffset = nm + ttUSHORT(fc+nm+4); ++ for (i=0; i < count; ++i) { ++ stbtt_uint32 loc = nm + 6 + 12 * i; ++ if (platformID == ttUSHORT(fc+loc+0) && encodingID == ttUSHORT(fc+loc+2) ++ && languageID == ttUSHORT(fc+loc+4) && nameID == ttUSHORT(fc+loc+6)) { ++ *length = ttUSHORT(fc+loc+8); ++ return (char *) (fc+stringOffset+ttUSHORT(fc+loc+10)); ++ } ++ } ++ return NULL; ++} ++ ++static int stbtt__matchpair(stbtt_uint8 *fc, stbtt_uint32 nm, stbtt_uint8 *name, stbtt_int32 nlen, stbtt_int32 target_id, stbtt_int32 next_id) ++{ ++ stbtt_int32 i; ++ stbtt_int32 count = ttUSHORT(fc+nm+2); ++ stbtt_int32 stringOffset = nm + ttUSHORT(fc+nm+4); ++ ++ for (i=0; i < count; ++i) { ++ stbtt_uint32 loc = nm + 6 + 12 * i; ++ stbtt_int32 id = ttUSHORT(fc+loc+6); ++ if (id == target_id) { ++ // find the encoding ++ stbtt_int32 platform = ttUSHORT(fc+loc+0), encoding = ttUSHORT(fc+loc+2), language = ttUSHORT(fc+loc+4); ++ ++ // is this a Unicode encoding? ++ if (platform == 0 || (platform == 3 && encoding == 1) || (platform == 3 && encoding == 10)) { ++ stbtt_int32 slen = ttUSHORT(fc+loc+8), off = ttUSHORT(fc+loc+10); ++ ++ // check if there's a prefix match ++ stbtt_int32 matchlen = stbtt__CompareUTF8toUTF16_bigendian_prefix(name, nlen, fc+stringOffset+off,slen); ++ if (matchlen >= 0) { ++ // check for target_id+1 immediately following, with same encoding & language ++ if (i+1 < count && ttUSHORT(fc+loc+12+6) == next_id && ttUSHORT(fc+loc+12) == platform && ttUSHORT(fc+loc+12+2) == encoding && ttUSHORT(fc+loc+12+4) == language) { ++ stbtt_int32 slen = ttUSHORT(fc+loc+12+8), off = ttUSHORT(fc+loc+12+10); ++ if (slen == 0) { ++ if (matchlen == nlen) ++ return 1; ++ } else if (matchlen < nlen && name[matchlen] == ' ') { ++ ++matchlen; ++ if (stbtt_CompareUTF8toUTF16_bigendian((char*) (name+matchlen), nlen-matchlen, (char*)(fc+stringOffset+off),slen)) ++ return 1; ++ } ++ } else { ++ // if nothing immediately following ++ if (matchlen == nlen) ++ return 1; ++ } ++ } ++ } ++ ++ // @TODO handle other encodings ++ } ++ } ++ return 0; ++} ++ ++static int stbtt__matches(stbtt_uint8 *fc, stbtt_uint32 offset, stbtt_uint8 *name, stbtt_int32 flags) ++{ ++ stbtt_int32 nlen = STBTT_strlen((char *) name); ++ stbtt_uint32 nm,hd; ++ if (!stbtt__isfont(fc+offset)) return 0; ++ ++ // check italics/bold/underline flags in macStyle... ++ if (flags) { ++ hd = stbtt__find_table(fc, offset, "head"); ++ if ((ttUSHORT(fc+hd+44) & 7) != (flags & 7)) return 0; ++ } ++ ++ nm = stbtt__find_table(fc, offset, "name"); ++ if (!nm) return 0; ++ ++ if (flags) { ++ // if we checked the macStyle flags, then just check the family and ignore the subfamily ++ if (stbtt__matchpair(fc, nm, name, nlen, 16, -1)) return 1; ++ if (stbtt__matchpair(fc, nm, name, nlen, 1, -1)) return 1; ++ if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; ++ } else { ++ if (stbtt__matchpair(fc, nm, name, nlen, 16, 17)) return 1; ++ if (stbtt__matchpair(fc, nm, name, nlen, 1, 2)) return 1; ++ if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; ++ } ++ ++ return 0; ++} ++ ++int stbtt_FindMatchingFont(const unsigned char *font_collection, const char *name_utf8, stbtt_int32 flags) ++{ ++ stbtt_int32 i; ++ for (i=0;;++i) { ++ stbtt_int32 off = stbtt_GetFontOffsetForIndex(font_collection, i); ++ if (off < 0) return off; ++ if (stbtt__matches((stbtt_uint8 *) font_collection, off, (stbtt_uint8*) name_utf8, flags)) ++ return off; ++ } ++} ++ ++#endif // STB_TRUETYPE_IMPLEMENTATION |
