From e01b1ef3b644a82d91eb480ebb9bb099e8d52b72 Mon Sep 17 00:00:00 2001
From: Cameron Hart
Date: Sun, 6 Oct 2013 10:45:10 +1100
Subject: [PATCH 01/49] Fixed bug where debug and release libs would have the
same file path.
This broke make when switching between debug and release configs.
---
RecastDemo/premake4.lua | 9 ++-------
1 file changed, 2 insertions(+), 7 deletions(-)
diff --git a/RecastDemo/premake4.lua b/RecastDemo/premake4.lua
index 300eb9f..a58e613 100644
--- a/RecastDemo/premake4.lua
+++ b/RecastDemo/premake4.lua
@@ -24,13 +24,13 @@ solution "recastnavigation"
configuration "Debug*"
defines { "DEBUG" }
flags { "Symbols" }
- targetdir ( "Build/" .. action .. "/Debug" )
+ targetdir ( todir .. "/lib/Debug" )
-- release configs
configuration "Release*"
defines { "NDEBUG" }
flags { "Optimize" }
- targetdir ( "Build/" .. action .. "/Release" )
+ targetdir ( todir .. "/lib/Release" )
-- windows specific
configuration "windows"
@@ -50,7 +50,6 @@ project "DebugUtils"
"../DebugUtils/Include/*.h",
"../DebugUtils/Source/*.cpp"
}
- targetdir (todir .. "/lib")
project "Detour"
language "C++"
@@ -62,7 +61,6 @@ project "Detour"
"../Detour/Include/*.h",
"../Detour/Source/*.cpp"
}
- targetdir (todir .. "/lib")
project "DetourCrowd"
language "C++"
@@ -76,7 +74,6 @@ project "DetourCrowd"
"../DetourCrowd/Include/*.h",
"../DetourCrowd/Source/*.cpp"
}
- targetdir (todir .. "/lib")
project "DetourTileCache"
language "C++"
@@ -90,7 +87,6 @@ project "DetourTileCache"
"../DetourTileCache/Include/*.h",
"../DetourTileCache/Source/*.cpp"
}
- targetdir (todir .. "/lib")
project "Recast"
language "C++"
@@ -102,7 +98,6 @@ project "Recast"
"../Recast/Include/*.h",
"../Recast/Source/*.cpp"
}
- targetdir (todir .. "/lib")
project "RecastDemo"
language "C++"
From 2d9033f3d4a39338f4b7e5df6ff91702244fd668 Mon Sep 17 00:00:00 2001
From: Cameron Hart
Date: Sun, 6 Oct 2013 12:07:38 +1100
Subject: [PATCH 02/49] Generate debug symbols in release builds and use fast
floats.
---
RecastDemo/premake4.lua | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/RecastDemo/premake4.lua b/RecastDemo/premake4.lua
index a58e613..2bd8f3a 100644
--- a/RecastDemo/premake4.lua
+++ b/RecastDemo/premake4.lua
@@ -16,14 +16,15 @@ solution "recastnavigation"
-- extra warnings, no exceptions or rtti
flags {
"ExtraWarnings",
+ "FloatFast",
"NoExceptions",
- "NoRTTI"
+ "NoRTTI",
+ "Symbols"
}
-- debug configs
configuration "Debug*"
defines { "DEBUG" }
- flags { "Symbols" }
targetdir ( todir .. "/lib/Debug" )
-- release configs
From dbfed86b08633a031e275722d1ad8f2fbc81927c Mon Sep 17 00:00:00 2001
From: Graham Pentheny
Date: Mon, 14 Oct 2013 13:41:17 -0400
Subject: [PATCH 03/49] Added vim swap file exclusions to .gitignore
---
.gitignore | 2 ++
1 file changed, 2 insertions(+)
diff --git a/.gitignore b/.gitignore
index c48caee..8406da9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,6 +19,8 @@
.Trashes
ehthumbs.db
Thumbs.db
+*.swp
+*.swo
## xcode specific
*xcuserdata*
From b493e7e6f61aed026cc874f45bbac9b8a70425eb Mon Sep 17 00:00:00 2001
From: grahamboree
Date: Mon, 14 Oct 2013 16:32:20 -0400
Subject: [PATCH 04/49] Added rcIngoreUnused which can be called to ignore
unused parameters.
---
Recast/Include/Recast.h | 5 +++++
Recast/Source/Recast.cpp | 20 ++++++++------------
RecastDemo/Source/CrowdTool.cpp | 4 ++++
RecastDemo/Source/NavMeshPruneTool.cpp | 11 ++++++++---
4 files changed, 25 insertions(+), 15 deletions(-)
diff --git a/Recast/Include/Recast.h b/Recast/Include/Recast.h
index 1ea40a3..88ce11c 100644
--- a/Recast/Include/Recast.h
+++ b/Recast/Include/Recast.h
@@ -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 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/Recast/Source/Recast.cpp b/Recast/Source/Recast.cpp
index 803daac..b9d8603 100644
--- a/Recast/Source/Recast.cpp
+++ b/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/RecastDemo/Source/CrowdTool.cpp b/RecastDemo/Source/CrowdTool.cpp
index 5caa6bd..98ded8e 100644
--- a/RecastDemo/Source/CrowdTool.cpp
+++ b/RecastDemo/Source/CrowdTool.cpp
@@ -1068,6 +1068,7 @@ void CrowdTool::handleToggle()
void CrowdTool::handleUpdate(const float dt)
{
+ rcIgnoreUnused(dt);
}
void CrowdTool::handleRender()
@@ -1076,6 +1077,9 @@ void CrowdTool::handleRender()
void CrowdTool::handleRenderOverlay(double* proj, double* model, int* view)
{
+ rcIgnoreUnused(model);
+ rcIgnoreUnused(proj);
+
// Tool help
const int h = view[3];
int ty = h-40;
diff --git a/RecastDemo/Source/NavMeshPruneTool.cpp b/RecastDemo/Source/NavMeshPruneTool.cpp
index 36e8e1a..193d79a 100644
--- a/RecastDemo/Source/NavMeshPruneTool.cpp
+++ b/RecastDemo/Source/NavMeshPruneTool.cpp
@@ -261,8 +261,11 @@ void NavMeshPruneTool::handleMenu()
}
}
-void NavMeshPruneTool::handleClick(const float* /*s*/, const float* p, bool shift)
+void NavMeshPruneTool::handleClick(const float* s, const float* p, bool shift)
{
+ rcIgnoreUnused(s);
+ rcIgnoreUnused(shift);
+
if (!m_sample) return;
InputGeom* geom = m_sample->getInputGeom();
if (!geom) return;
@@ -341,9 +344,11 @@ void NavMeshPruneTool::handleRender()
void NavMeshPruneTool::handleRenderOverlay(double* proj, double* model, int* view)
{
+ rcIgnoreUnused(model);
+ rcIgnoreUnused(proj);
+
// Tool help
const int h = view[3];
- imguiDrawText(280, h-40, IMGUI_ALIGN_LEFT, "LMB: Click fill area.", imguiRGBA(255,255,255,192));
-
+ imguiDrawText(280, h-40, IMGUI_ALIGN_LEFT, "LMB: Click fill area.", imguiRGBA(255,255,255,192));
}
From 35db2af8726814f1ffa3362d12ce1556fc500361 Mon Sep 17 00:00:00 2001
From: grahamboree
Date: Mon, 14 Oct 2013 16:36:49 -0400
Subject: [PATCH 05/49] Silenced additional unused parameter warning.
---
DetourTileCache/Source/DetourTileCacheBuilder.cpp | 2 ++
1 file changed, 2 insertions(+)
diff --git a/DetourTileCache/Source/DetourTileCacheBuilder.cpp b/DetourTileCache/Source/DetourTileCacheBuilder.cpp
index 19b5e91..18a26d0 100644
--- a/DetourTileCache/Source/DetourTileCacheBuilder.cpp
+++ b/DetourTileCache/Source/DetourTileCacheBuilder.cpp
@@ -21,6 +21,7 @@
#include "DetourStatus.h"
#include "DetourAssert.h"
#include "DetourTileCacheBuilder.h"
+#include "Recast.h"
#include
@@ -2116,6 +2117,7 @@ dtStatus dtDecompressTileCacheLayer(dtTileCacheAlloc* alloc, dtTileCacheCompress
bool dtTileCacheHeaderSwapEndian(unsigned char* data, const int dataSize)
{
+ rcIgnoreUnused(dataSize);
dtTileCacheLayerHeader* header = (dtTileCacheLayerHeader*)data;
int swappedMagic = DT_TILECACHE_MAGIC;
From 683acbb11c7e0144590dd89777f4612d6ccb731a Mon Sep 17 00:00:00 2001
From: grahamboree
Date: Mon, 14 Oct 2013 16:38:03 -0400
Subject: [PATCH 06/49] Silenced double->float conversion warning.
---
RecastDemo/Source/NavMeshTesterTool.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/RecastDemo/Source/NavMeshTesterTool.cpp b/RecastDemo/Source/NavMeshTesterTool.cpp
index 4784624..146624e 100644
--- a/RecastDemo/Source/NavMeshTesterTool.cpp
+++ b/RecastDemo/Source/NavMeshTesterTool.cpp
@@ -1297,7 +1297,7 @@ void NavMeshTesterTool::handleRender()
for (int i = 0; i < m_nrandPoints; i++)
{
const float* p = &m_randPoints[i*3];
- dd.vertex(p[0],p[1]+0.1,p[2], duRGBA(220,32,16,192));
+ dd.vertex(p[0],p[1]+0.1f,p[2], duRGBA(220,32,16,192));
}
dd.end();
From dc7e248de6e0da2a3a533977081c461137a75b4d Mon Sep 17 00:00:00 2001
From: grahamboree
Date: Tue, 15 Oct 2013 17:29:14 -0400
Subject: [PATCH 07/49] Removed header dependency between Detour and Recast due
to addition of rcIgnoreUnused. Added dtIgnoreUnused.
---
Detour/Include/DetourCommon.h | 5 +++++
DetourTileCache/Source/DetourTileCacheBuilder.cpp | 3 +--
2 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/Detour/Include/DetourCommon.h b/Detour/Include/DetourCommon.h
index ed7c514..0888614 100644
--- a/Detour/Include/DetourCommon.h
+++ b/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 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/DetourTileCache/Source/DetourTileCacheBuilder.cpp b/DetourTileCache/Source/DetourTileCacheBuilder.cpp
index 18a26d0..d6602b7 100644
--- a/DetourTileCache/Source/DetourTileCacheBuilder.cpp
+++ b/DetourTileCache/Source/DetourTileCacheBuilder.cpp
@@ -21,7 +21,6 @@
#include "DetourStatus.h"
#include "DetourAssert.h"
#include "DetourTileCacheBuilder.h"
-#include "Recast.h"
#include
@@ -2117,7 +2116,7 @@ dtStatus dtDecompressTileCacheLayer(dtTileCacheAlloc* alloc, dtTileCacheCompress
bool dtTileCacheHeaderSwapEndian(unsigned char* data, const int dataSize)
{
- rcIgnoreUnused(dataSize);
+ dtIgnoreUnused(dataSize);
dtTileCacheLayerHeader* header = (dtTileCacheLayerHeader*)data;
int swappedMagic = DT_TILECACHE_MAGIC;
From 3f8c0506fb9960bb3f12b61293ec6e91d14d6387 Mon Sep 17 00:00:00 2001
From: flippy84
Date: Sat, 21 Dec 2013 13:49:38 +0100
Subject: [PATCH 08/49] Update DetourNavMeshQuery.h
Changed existing and existingSize parameters of finalizeSlicedFindPathPartial to @param[in]
---
Detour/Include/DetourNavMeshQuery.h | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Detour/Include/DetourNavMeshQuery.h b/Detour/Include/DetourNavMeshQuery.h
index d431bf1..6edf5bf 100644
--- a/Detour/Include/DetourNavMeshQuery.h
+++ b/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.
From 6a7f5268ab47604f662254e515144cd3af0cb614 Mon Sep 17 00:00:00 2001
From: Mikko Mononen
Date: Sun, 22 Dec 2013 18:01:02 +0200
Subject: [PATCH 09/49] Added define to compile Detour with 64bit dtPolyRefs
---
Detour/Include/DetourNavMesh.h | 53 +++++++++++++++++++++++++++++++++
Detour/Source/DetourNavMesh.cpp | 17 ++++++++---
Detour/Source/DetourNode.cpp | 14 +++++++++
3 files changed, 80 insertions(+), 4 deletions(-)
diff --git a/Detour/Include/DetourNavMesh.h b/Detour/Include/DetourNavMesh.h
index 9539a19..d87fee8 100644
--- a/Detour/Include/DetourNavMesh.h
+++ b/Detour/Include/DetourNavMesh.h
@@ -22,16 +22,39 @@
#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
+#include
+#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.
/// 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
/// A handle to a tile within a navigation mesh.
/// @ingroup detour
+#ifdef DT_POLYREF64
+typedef uint64_t dtTileRef;
+#else
typedef unsigned int dtTileRef;
+#endif
/// The maximum number of vertices per navigation polygon.
/// @ingroup detour
@@ -469,7 +492,11 @@ 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.
@@ -481,12 +508,21 @@ 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_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_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.
@@ -495,8 +531,13 @@ public:
/// @see #encodePolyId
inline unsigned int decodePolyIdSalt(dtPolyRef ref) const
{
+#ifdef DT_POLYREF64
+ const dtPolyRef saltMask = ((dtPolyRef)1<> (DT_POLY_BITS+DT_TILE_BITS)) & saltMask);
+#else
const dtPolyRef saltMask = ((dtPolyRef)1<> (m_polyBits+m_tileBits)) & saltMask);
+#endif
}
/// Extracts the tile's index from the specified polygon reference.
@@ -505,8 +546,13 @@ public:
/// @see #encodePolyId
inline unsigned int decodePolyIdTile(dtPolyRef ref) const
{
+#ifdef DT_POLYREF64
+ const dtPolyRef tileMask = ((dtPolyRef)1<> DT_POLY_BITS) & tileMask);
+#else
const dtPolyRef tileMask = ((dtPolyRef)1<> m_polyBits) & tileMask);
+#endif
}
/// Extracts the polygon's index (within its tile) from the specified polygon reference.
@@ -515,8 +561,13 @@ public:
/// @see #encodePolyId
inline unsigned int decodePolyIdPoly(dtPolyRef ref) const
{
+#ifdef DT_POLYREF64
+ const dtPolyRef polyMask = ((dtPolyRef)1<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
return DT_SUCCESS;
}
@@ -1209,7 +1214,11 @@ 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<salt = (tile->salt+1) & ((1<salt == 0)
tile->salt++;
diff --git a/Detour/Source/DetourNode.cpp b/Detour/Source/DetourNode.cpp
index de7b159..57cb206 100644
--- a/Detour/Source/DetourNode.cpp
+++ b/Detour/Source/DetourNode.cpp
@@ -22,6 +22,19 @@
#include "DetourCommon.h"
#include
+#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;
+}
+#else
inline unsigned int dtHashRef(dtPolyRef a)
{
a += ~(a<<15);
@@ -32,6 +45,7 @@ inline unsigned int dtHashRef(dtPolyRef a)
a ^= (a>>16);
return (unsigned int)a;
}
+#endif
//////////////////////////////////////////////////////////////////////////////////////////
dtNodePool::dtNodePool(int maxNodes, int hashSize) :
From 64828488b19308016993795ade8cc97a46c1b7f0 Mon Sep 17 00:00:00 2001
From: Mikko Mononen
Date: Sun, 22 Dec 2013 18:24:58 +0200
Subject: [PATCH 10/49] Fixed docs of maxSimplificationError units.
---
Recast/Include/Recast.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Recast/Include/Recast.h b/Recast/Include/Recast.h
index 88ce11c..336837e 100644
--- a/Recast/Include/Recast.h
+++ b/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]
From b3d27bdb460ef11b5d61d00c7f129ced188f9238 Mon Sep 17 00:00:00 2001
From: Mikko Mononen
Date: Thu, 2 Jan 2014 21:03:01 +0200
Subject: [PATCH 11/49] Fix for Issue #12
- fixed errors reported by valgrind
---
Recast/Source/RecastMesh.cpp | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/Recast/Source/RecastMesh.cpp b/Recast/Source/RecastMesh.cpp
index 23a4660..534a72e 100644
--- a/Recast/Source/RecastMesh.cpp
+++ b/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--;
From 8e4633c64b8150b5dcb00289add05d3c8a48ccd2 Mon Sep 17 00:00:00 2001
From: axelrodR
Date: Wed, 8 Jan 2014 12:02:00 +0200
Subject: [PATCH 12/49] Performance optimizations: 1. Triangle rasterization:
replaced loops over all the grid cells of the AABB with looping over the area
of the triangle itself (i.e. bounds are set per row). 2. Region expansion:
instead of sweeping through all the cells at each expandRegion() and finding
the right level - sweep once per few levels and divide among several stacks.
Left over are appended.
---
Recast/Source/RecastRasterization.cpp | 19 ++--
Recast/Source/RecastRegion.cpp | 141 +++++++++++++++++++++-----
2 files changed, 128 insertions(+), 32 deletions(-)
diff --git a/Recast/Source/RecastRasterization.cpp b/Recast/Source/RecastRasterization.cpp
index d2bb7c9..1e7750d 100644
--- a/Recast/Source/RecastRasterization.cpp
+++ b/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;
@@ -222,14 +222,10 @@ 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.
@@ -248,6 +244,17 @@ static void rasterizeTri(const float* v0, const float* v1, const float* v2,
nvrow = clipPoly(out, nvrow, inrow, 0, -1, cz+cs);
if (nvrow < 3) continue;
+ float minX = inrow[0], maxX = inrow[0];
+ for (int i=1; i 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);
+
for (int x = x0; x <= x1; ++x)
{
// Clip polygon to column.
diff --git a/Recast/Source/RecastRegion.cpp b/Recast/Source/RecastRegion.cpp
index 76e631c..589fac2 100644
--- a/Recast/Source/RecastRegion.cpp
+++ b/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 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> 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; jstartTimer(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 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= 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);
From e063ba6f5adfff896906df3caf98b486492d952b Mon Sep 17 00:00:00 2001
From: axelrodR
Date: Wed, 15 Jan 2014 14:14:08 +0200
Subject: [PATCH 13/49] Rasterization optimization: replaced clipping for each
individual cell. the clipping line/plane between two adjacent cells is common
so data from former cell can be reused if we keep track of the "remaining
polygon".
---
Recast/Source/RecastRasterization.cpp | 79 +++++++++++++++++----------
1 file changed, 49 insertions(+), 30 deletions(-)
diff --git a/Recast/Source/RecastRasterization.cpp b/Recast/Source/RecastRasterization.cpp
index 1e7750d..f082438 100644
--- a/Recast/Source/RecastRasterization.cpp
+++ b/Recast/Source/RecastRasterization.cpp
@@ -169,36 +169,53 @@ 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 nbIn,
+ float* out1, int* nb1,
+ float* out2, int* nb2,
+ 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 < nbIn; ++i)
+ d[i] = x - in[i*3+axis];
+
+ int m = 0, n = 0;
+ for (int i = 0, j = nbIn-1; i < nbIn; 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++;
}
if (inb)
{
- out[m*3+0] = in[i*3+0];
- out[m*3+1] = in[i*3+1];
- out[m*3+2] = in[i*3+2];
+ out1[m*3+0] = in[i*3+0];
+ out1[m*3+1] = in[i*3+1];
+ out1[m*3+2] = in[i*3+2];
m++;
+ if (d[0] != 0) // not on the line
+ continue;
}
+
+ // i-th point is on the other half plane or on the line
+ out2[n*3+0] = in[i*3+0];
+ out2[n*3+1] = in[i*3+1];
+ out2[n*3+2] = in[i*3+2];
+ n++;
}
- return m;
+
+ *nb1 = m;
+ *nb2 = 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,
@@ -229,21 +246,23 @@ static void rasterizeTri(const float* v0, const float* v1, const float* v2,
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
Date: Sun, 19 Jan 2014 14:02:41 +0200
Subject: [PATCH 14/49] Optimization of of the mesh detail construction:
replaced the first of the 2 flood-fill algorithms (the one used to find the
span corresponding to the center of the polygon) with a search of span at the
center with the region matching the polygon.
---
Recast/Source/RecastMeshDetail.cpp | 132 ++++++++---------------------
1 file changed, 35 insertions(+), 97 deletions(-)
diff --git a/Recast/Source/RecastMeshDetail.cpp b/Recast/Source/RecastMeshDetail.cpp
index 77438fd..3b94663 100644
--- a/Recast/Source/RecastMeshDetail.cpp
+++ b/Recast/Source/RecastMeshDetail.cpp
@@ -744,60 +744,20 @@ static bool buildPolyDetail(rcContext* ctx, const float* in, const int nin,
static void getHeightData(const rcCompactHeightfield& chf,
const unsigned short* poly, const int npoly,
const unsigned short* verts, const int bs,
- rcHeightPatch& hp, rcIntArray& stack)
+ rcHeightPatch& hp, rcIntArray& stack,
+ int region)
{
- // Floodfill the heightfield to get 2D height data,
- // starting at vertex locations as seeds.
-
// Note: Reads to the compact heightfield are offset by border size (bs)
// since border size offset is already removed from the polymesh vertices.
-
- memset(hp.data, 0, sizeof(unsigned short)*hp.width*hp.height);
-
+
stack.resize(0);
-
+
static const int offset[9*2] =
{
- 0,0, -1,-1, 0,-1, 1,-1, 1,0, 1,1, 0,1, -1,1, -1,0,
+ 0,0, -1,0, 0,1, 1,0, 0,-1, -1,-1, -1,1, 1,1, 1,-1
};
-
- // Use poly vertices as seed points for the flood fill.
- for (int j = 0; j < npoly; ++j)
- {
- int cx = 0, cz = 0, ci =-1;
- int dmin = RC_UNSET_HEIGHT;
- for (int k = 0; k < 9; ++k)
- {
- const int ax = (int)verts[poly[j]*3+0] + offset[k*2+0];
- const int ay = (int)verts[poly[j]*3+1];
- const int az = (int)verts[poly[j]*3+2] + offset[k*2+1];
- if (ax < hp.xmin || ax >= hp.xmin+hp.width ||
- az < hp.ymin || az >= hp.ymin+hp.height)
- continue;
-
- const rcCompactCell& c = chf.cells[(ax+bs)+(az+bs)*chf.width];
- for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
- {
- const rcCompactSpan& s = chf.spans[i];
- int d = rcAbs(ay - (int)s.y);
- if (d < dmin)
- {
- cx = ax;
- cz = az;
- ci = i;
- dmin = d;
- }
- }
- }
- if (ci != -1)
- {
- stack.push(cx);
- stack.push(cz);
- stack.push(ci);
- }
- }
-
- // Find center of the polygon using flood fill.
+
+ // find the center of the polygon
int pcx = 0, pcz = 0;
for (int j = 0; j < npoly; ++j)
{
@@ -806,58 +766,37 @@ static void getHeightData(const rcCompactHeightfield& chf,
}
pcx /= npoly;
pcz /= npoly;
-
- for (int i = 0; i < stack.size(); i += 3)
- {
- int cx = stack[i+0];
- int cy = stack[i+1];
- int idx = cx-hp.xmin+(cy-hp.ymin)*hp.width;
- hp.data[idx] = 1;
- }
-
- while (stack.size() > 0)
- {
- int ci = stack.pop();
- int cy = stack.pop();
- int cx = stack.pop();
-
- // Check if close to center of the polygon.
- if (rcAbs(cx-pcx) <= 1 && rcAbs(cy-pcz) <= 1)
- {
- stack.resize(0);
- stack.push(cx);
- stack.push(cy);
- stack.push(ci);
- break;
- }
-
- const rcCompactSpan& cs = chf.spans[ci];
-
- for (int dir = 0; dir < 4; ++dir)
- {
- if (rcGetCon(cs, dir) == RC_NOT_CONNECTED) continue;
-
- const int ax = cx + rcGetDirOffsetX(dir);
- const int ay = cy + rcGetDirOffsetY(dir);
-
- if (ax < hp.xmin || ax >= (hp.xmin+hp.width) ||
- ay < hp.ymin || ay >= (hp.ymin+hp.height))
- continue;
-
- if (hp.data[ax-hp.xmin+(ay-hp.ymin)*hp.width] != 0)
- continue;
-
- const int ai = (int)chf.cells[(ax+bs)+(ay+bs)*chf.width].index + rcGetCon(cs, dir);
- int idx = ax-hp.xmin+(ay-hp.ymin)*hp.width;
- hp.data[idx] = 1;
-
- stack.push(ax);
- stack.push(ay);
- stack.push(ai);
+ // find a span with the right region around this point
+ // No need to check for connectivity because the region ensures it
+ for (int dir = 0; dir < 9; ++dir)
+ {
+ int ax = pcx + offset[dir*2+0];
+ int az = pcz + offset[dir*2+1];
+
+ if (ax < hp.xmin || ax >= hp.xmin+hp.width ||
+ az < hp.ymin || az >= hp.ymin+hp.height)
+ continue;
+
+ const rcCompactCell& c = chf.cells[(ax+bs)+(az+bs)*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)
+ {
+ stack.push(ax);
+ stack.push(az);
+ stack.push(i);
+ break;
+ }
}
+ if (stack.size() > 0)
+ break;
}
+ // Floodfill the heightfield to get 2D height data,
+ // starting at center location found above as seed.
+
memset(hp.data, 0xff, sizeof(unsigned short)*hp.width*hp.height);
// Mark start locations.
@@ -914,7 +853,6 @@ static void getHeightData(const rcCompactHeightfield& chf,
stack.push(ai);
}
}
-
}
static unsigned char getEdgeFlags(const float* va, const float* vb,
@@ -1072,7 +1010,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;
From 6469a99f119e6917715acab9210a5c7da8689e12 Mon Sep 17 00:00:00 2001
From: Mikko Mononen
Date: Sun, 19 Jan 2014 15:32:09 +0200
Subject: [PATCH 15/49] Fixed item type in PolyRefArray from int to dtPolyRef
---
RecastDemo/Source/NavMeshPruneTool.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/RecastDemo/Source/NavMeshPruneTool.cpp b/RecastDemo/Source/NavMeshPruneTool.cpp
index 193d79a..270c8f6 100644
--- a/RecastDemo/Source/NavMeshPruneTool.cpp
+++ b/RecastDemo/Source/NavMeshPruneTool.cpp
@@ -63,7 +63,7 @@ public:
}
m_size = n;
}
- inline void push(int item) { resize(m_size+1); m_data[m_size-1] = item; }
+ inline void push(dtPolyRef item) { resize(m_size+1); m_data[m_size-1] = item; }
inline dtPolyRef pop() { if (m_size > 0) m_size--; return m_data[m_size]; }
inline const dtPolyRef& operator[](int i) const { return m_data[i]; }
inline dtPolyRef& operator[](int i) { return m_data[i]; }
From ad7a13cbf3ecdacb741d24d2bc2c447e73314d3f Mon Sep 17 00:00:00 2001
From: Mikko Mononen
Date: Sun, 19 Jan 2014 16:47:26 +0200
Subject: [PATCH 16/49] Small formatting change to dividePoly()
---
Recast/Source/RecastRasterization.cpp | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/Recast/Source/RecastRasterization.cpp b/Recast/Source/RecastRasterization.cpp
index f082438..5ad6f01 100644
--- a/Recast/Source/RecastRasterization.cpp
+++ b/Recast/Source/RecastRasterization.cpp
@@ -170,17 +170,17 @@ void rcAddSpan(rcContext* /*ctx*/, rcHeightfield& hf, const int x, const int y,
}
// divides a convex polygons into two convex polygons on both sides of a line
-static void dividePoly(const float* in, int nbIn,
- float* out1, int* nb1,
- float* out2, int* nb2,
+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 < nbIn; ++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 = nbIn-1; i < nbIn; j=i, ++i)
+ for (int i = 0, j = nin-1; i < nin; j=i, ++i)
{
bool ina = d[j] >= 0;
bool inb = d[i] >= 0;
@@ -211,8 +211,8 @@ static void dividePoly(const float* in, int nbIn,
n++;
}
- *nb1 = m;
- *nb2 = n;
+ *nout1 = m;
+ *nout2 = n;
}
From 65da694f501ed69bb01319d057386355a6c6b911 Mon Sep 17 00:00:00 2001
From: Mikko Mononen
Date: Sun, 19 Jan 2014 16:47:47 +0200
Subject: [PATCH 17/49] gitignore meshes starting with underscore
---
.gitignore | 3 +++
1 file changed, 3 insertions(+)
diff --git a/.gitignore b/.gitignore
index 2e39568..b321a15 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,6 +14,9 @@ RecastDemo/Bin/RecastDemo
# Build directory
RecastDemo/Build
+# Ignore some meshes based on name
+RecastDemo/Bin/Meshes/_*
+
## Logs and databases #
*.log
*.sql
From 29db8cde1bf323e6f828db3b70a754a3025e9076 Mon Sep 17 00:00:00 2001
From: Mikko Mononen
Date: Sun, 19 Jan 2014 17:49:40 +0200
Subject: [PATCH 18/49] Fix for detail mesh corner cases
- fix case where thing long poly center may not be on the region
---
Recast/Source/RecastMeshDetail.cpp | 115 +++++++++++++----------------
1 file changed, 53 insertions(+), 62 deletions(-)
diff --git a/Recast/Source/RecastMeshDetail.cpp b/Recast/Source/RecastMeshDetail.cpp
index 3b94663..69ed147 100644
--- a/Recast/Source/RecastMeshDetail.cpp
+++ b/Recast/Source/RecastMeshDetail.cpp
@@ -751,64 +751,55 @@ static void getHeightData(const rcCompactHeightfield& chf,
// since border size offset is already removed from the polymesh vertices.
stack.resize(0);
-
- static const int offset[9*2] =
- {
- 0,0, -1,0, 0,1, 1,0, 0,-1, -1,-1, -1,1, 1,1, 1,-1
- };
-
- // find the center of the polygon
- int pcx = 0, pcz = 0;
- for (int j = 0; j < npoly; ++j)
- {
- pcx += (int)verts[poly[j]*3+0];
- pcz += (int)verts[poly[j]*3+2];
- }
- pcx /= npoly;
- pcz /= npoly;
-
- // find a span with the right region around this point
- // No need to check for connectivity because the region ensures it
- for (int dir = 0; dir < 9; ++dir)
- {
- int ax = pcx + offset[dir*2+0];
- int az = pcz + offset[dir*2+1];
-
- if (ax < hp.xmin || ax >= hp.xmin+hp.width ||
- az < hp.ymin || az >= hp.ymin+hp.height)
- continue;
-
- const rcCompactCell& c = chf.cells[(ax+bs)+(az+bs)*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)
- {
- stack.push(ax);
- stack.push(az);
- stack.push(i);
- break;
- }
- }
- if (stack.size() > 0)
- break;
- }
-
- // Floodfill the heightfield to get 2D height data,
- // starting at center location found above as seed.
-
memset(hp.data, 0xff, sizeof(unsigned short)*hp.width*hp.height);
- // Mark start locations.
- for (int i = 0; i < stack.size(); i += 3)
+ // 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 cx = stack[i+0];
- int cy = stack[i+1];
- int ci = stack[i+2];
- int idx = cx-hp.xmin+(cy-hp.ymin)*hp.width;
- const rcCompactSpan& cs = chf.spans[ci];
- hp.data[idx] = cs.y;
- }
+ 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;
+
+ // 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;
+ }
+ }
+ }
+ }
+
static const int RETRACT_SIZE = 256;
int head = 0;
@@ -834,19 +825,19 @@ 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);
From b4204ba9a20a8f13ff222ef74ab7b8ef5b3b1184 Mon Sep 17 00:00:00 2001
From: axelrodR
Date: Mon, 20 Jan 2014 00:12:53 +0200
Subject: [PATCH 19/49] bugfix: minor typo caused stack overrun when several
vertices happen to fall exactly on cell boundaries
---
Recast/Source/RecastRasterization.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Recast/Source/RecastRasterization.cpp b/Recast/Source/RecastRasterization.cpp
index f082438..e1b2bf3 100644
--- a/Recast/Source/RecastRasterization.cpp
+++ b/Recast/Source/RecastRasterization.cpp
@@ -200,7 +200,7 @@ static void dividePoly(const float* in, int nbIn,
out1[m*3+1] = in[i*3+1];
out1[m*3+2] = in[i*3+2];
m++;
- if (d[0] != 0) // not on the line
+ if (d[i] != 0) // not on the line
continue;
}
From 0c4016590897051fdd89a3c9d60c52fd0460373f Mon Sep 17 00:00:00 2001
From: axelrodR
Date: Mon, 20 Jan 2014 00:54:18 +0200
Subject: [PATCH 20/49] bugfix in RecastMeshDetail: in some rare dense cases
the polygon simplification moves the polygon to a point where the center is
very far from the region. For now we just fall back to the old code
(flood-fill from the vertices)
---
Recast/Source/RecastMeshDetail.cpp | 94 ++++++++++++++++++++++++++++++
1 file changed, 94 insertions(+)
diff --git a/Recast/Source/RecastMeshDetail.cpp b/Recast/Source/RecastMeshDetail.cpp
index 3b94663..e615e0a 100644
--- a/Recast/Source/RecastMeshDetail.cpp
+++ b/Recast/Source/RecastMeshDetail.cpp
@@ -794,6 +794,100 @@ static void getHeightData(const rcCompactHeightfield& chf,
break;
}
+ // in some dense cases the polygon simplification moves the polygon to a point where the center
+ // is very far from the region. So we search the span in flood-fill fashion from the vertics
+ if (stack.size() == 0)
+ {
+ memset(hp.data, 0, sizeof(unsigned short)*hp.width*hp.height);
+
+ // Use poly vertices as seed points for the flood fill.
+ for (int j = 0; j < npoly; ++j)
+ {
+ int cx = 0, cz = 0, ci =-1;
+ int dmin = RC_UNSET_HEIGHT;
+ for (int k = 0; k < 9; ++k)
+ {
+ const int ax = (int)verts[poly[j]*3+0] + offset[k*2+0];
+ const int ay = (int)verts[poly[j]*3+1];
+ const int az = (int)verts[poly[j]*3+2] + offset[k*2+1];
+ if (ax < hp.xmin || ax >= hp.xmin+hp.width ||
+ az < hp.ymin || az >= hp.ymin+hp.height)
+ continue;
+
+ const rcCompactCell& c = chf.cells[(ax+bs)+(az+bs)*chf.width];
+ for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
+ {
+ const rcCompactSpan& s = chf.spans[i];
+ int d = rcAbs(ay - (int)s.y);
+ if (d < dmin)
+ {
+ cx = ax;
+ cz = az;
+ ci = i;
+ dmin = d;
+ }
+ }
+ }
+ if (ci != -1)
+ {
+ stack.push(cx);
+ stack.push(cz);
+ stack.push(ci);
+ }
+ }
+
+ for (int i = 0; i < stack.size(); i += 3)
+ {
+ int cx = stack[i+0];
+ int cy = stack[i+1];
+ int idx = cx-hp.xmin+(cy-hp.ymin)*hp.width;
+ hp.data[idx] = 1;
+ }
+
+ while (stack.size() > 0)
+ {
+ int ci = stack.pop();
+ int cy = stack.pop();
+ int cx = stack.pop();
+
+ // Check if close to center of the polygon.
+ if (rcAbs(cx-pcx) <= 1 && rcAbs(cy-pcz) <= 1)
+ {
+ stack.resize(0);
+ stack.push(cx);
+ stack.push(cy);
+ stack.push(ci);
+ break;
+ }
+
+ const rcCompactSpan& cs = chf.spans[ci];
+
+ for (int dir = 0; dir < 4; ++dir)
+ {
+ if (rcGetCon(cs, dir) == RC_NOT_CONNECTED) continue;
+
+ const int ax = cx + rcGetDirOffsetX(dir);
+ const int ay = cy + rcGetDirOffsetY(dir);
+
+ if (ax < hp.xmin || ax >= (hp.xmin+hp.width) ||
+ ay < hp.ymin || ay >= (hp.ymin+hp.height))
+ continue;
+
+ if (hp.data[ax-hp.xmin+(ay-hp.ymin)*hp.width] != 0)
+ continue;
+
+ const int ai = (int)chf.cells[(ax+bs)+(ay+bs)*chf.width].index + rcGetCon(cs, dir);
+
+ int idx = ax-hp.xmin+(ay-hp.ymin)*hp.width;
+ hp.data[idx] = 1;
+
+ stack.push(ax);
+ stack.push(ay);
+ stack.push(ai);
+ }
+ }
+ }
+
// Floodfill the heightfield to get 2D height data,
// starting at center location found above as seed.
From 4d657b9561f9cf692b58f4d4870276cee7b5bfbc Mon Sep 17 00:00:00 2001
From: axelrodR
Date: Mon, 20 Jan 2014 00:55:40 +0200
Subject: [PATCH 21/49] changed remarks
---
Recast/Source/RecastMeshDetail.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Recast/Source/RecastMeshDetail.cpp b/Recast/Source/RecastMeshDetail.cpp
index e615e0a..f7cd77e 100644
--- a/Recast/Source/RecastMeshDetail.cpp
+++ b/Recast/Source/RecastMeshDetail.cpp
@@ -794,8 +794,8 @@ static void getHeightData(const rcCompactHeightfield& chf,
break;
}
- // in some dense cases the polygon simplification moves the polygon to a point where the center
- // is very far from the region. So we search the span in flood-fill fashion from the vertics
+ // in some rare dense cases the polygon simplification moves the polygon to a point where the center
+ // is very far from the region. So we search the span in flood-fill fashion from the vertices
if (stack.size() == 0)
{
memset(hp.data, 0, sizeof(unsigned short)*hp.width*hp.height);
From bc964f999405c6cb427a967f3afd1b29dca02a8c Mon Sep 17 00:00:00 2001
From: Mikko Mononen
Date: Tue, 21 Jan 2014 19:15:03 +0200
Subject: [PATCH 22/49] Fix for U-turn path case
---
RecastDemo/Source/NavMeshTesterTool.cpp | 66 ++++++++++++++++++++++++-
1 file changed, 65 insertions(+), 1 deletion(-)
diff --git a/RecastDemo/Source/NavMeshTesterTool.cpp b/RecastDemo/Source/NavMeshTesterTool.cpp
index 146624e..3b49d4b 100644
--- a/RecastDemo/Source/NavMeshTesterTool.cpp
+++ b/RecastDemo/Source/NavMeshTesterTool.cpp
@@ -101,6 +101,66 @@ static int fixupCorridor(dtPolyRef* path, const int npath, const int maxPath,
return req+size;
}
+// This function checks if the path has a small U-turn, that is,
+// a polygon further in the path is adjacent to the first polygon
+// in the path. If that happens, a shortcut is taken.
+// This can happen if the target (T) location is at tile boundary,
+// and we're (S) approaching it parallel to the tile edge.
+// The choice at the vertex can be arbitrary,
+// +---+---+
+// |:::|:::|
+// +-S-+-T-+
+// |:::| | <-- the step can end up in here, resulting U-turn path.
+// +---+---+
+static int fixupShortcuts(dtPolyRef* path, int npath, dtNavMeshQuery* navQuery)
+{
+ if (npath < 3)
+ return npath;
+
+ // Get connected polygons
+ static const int maxNeis = 16;
+ dtPolyRef neis[maxNeis];
+ int nneis = 0;
+
+ const dtMeshTile* tile = 0;
+ const dtPoly* poly = 0;
+ if (dtStatusFailed(navQuery->getAttachedNavMesh()->getTileAndPolyByRef(path[0], &tile, &poly)))
+ return npath;
+
+ for (unsigned int k = poly->firstLink; k != DT_NULL_LINK; k = tile->links[k].next)
+ {
+ const dtLink* link = &tile->links[k];
+ if (link->ref != 0)
+ {
+ if (nneis < maxNeis)
+ neis[nneis++] = link->ref;
+ }
+ }
+
+ // If any of the neighbour polygons is within the next few polygons
+ // in the path, short cut to that polygon directly.
+ static const int maxLookAhead = 6;
+ int cut = 0;
+ for (int i = dtMin(maxLookAhead, npath) - 1; i > 1 && cut == 0; i--) {
+ for (int j = 0; j < nneis; j++)
+ {
+ if (path[i] == neis[j]) {
+ cut = i;
+ break;
+ }
+ }
+ }
+ if (cut > 1)
+ {
+ int offset = cut-1;
+ npath -= offset;
+ for (int i = 1; i < npath; i++)
+ path[i] = path[i+offset];
+ }
+
+ return npath;
+}
+
static bool getSteerTarget(dtNavMeshQuery* navQuery, const float* startPos, const float* endPos,
const float minTargetDist,
const dtPolyRef* path, const int pathSize,
@@ -503,6 +563,8 @@ void NavMeshTesterTool::handleToggle()
m_navQuery->moveAlongSurface(m_pathIterPolys[0], m_iterPos, moveTgt, &m_filter,
result, visited, &nvisited, 16);
m_pathIterPolyCount = fixupCorridor(m_pathIterPolys, m_pathIterPolyCount, MAX_POLYS, visited, nvisited);
+ m_pathIterPolyCount = fixupShortcuts(m_pathIterPolys, m_pathIterPolyCount, m_navQuery);
+
float h = 0;
m_navQuery->getPolyHeight(m_pathIterPolys[0], result, &h);
result[1] = h;
@@ -698,8 +760,10 @@ void NavMeshTesterTool::recalc()
int nvisited = 0;
m_navQuery->moveAlongSurface(polys[0], iterPos, moveTgt, &m_filter,
result, visited, &nvisited, 16);
-
+
npolys = fixupCorridor(polys, npolys, MAX_POLYS, visited, nvisited);
+ npolys = fixupShortcuts(polys, npolys, m_navQuery);
+
float h = 0;
m_navQuery->getPolyHeight(polys[0], result, &h);
result[1] = h;
From adcd4f472e8e08bd7f1db11b39f3394dc82ef869 Mon Sep 17 00:00:00 2001
From: axelrodR
Date: Tue, 21 Jan 2014 22:05:40 +0200
Subject: [PATCH 23/49] bugfix: divPoly could produce an 8th point after
several calls because some of the old clipPoly code on which it was based can
add the same point twice consecutively (interpolation+adding the point).
Rather than raising the buffer I rewrote divPoly to avoid adding the same
point twice.
---
Recast/Source/RecastRasterization.cpp | 39 +++++++++++++++++----------
1 file changed, 25 insertions(+), 14 deletions(-)
diff --git a/Recast/Source/RecastRasterization.cpp b/Recast/Source/RecastRasterization.cpp
index 5801516..45a7d35 100644
--- a/Recast/Source/RecastRasterization.cpp
+++ b/Recast/Source/RecastRasterization.cpp
@@ -193,29 +193,40 @@ static void dividePoly(const float* in, int nin,
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
{
- out1[m*3+0] = in[i*3+0];
- out1[m*3+1] = in[i*3+1];
- out1[m*3+2] = in[i*3+2];
- m++;
- if (d[i] != 0) // not on the line
- continue;
+ // 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++;
}
-
- // i-th point is on the other half plane or on the line
- out2[n*3+0] = in[i*3+0];
- out2[n*3+1] = in[i*3+1];
- out2[n*3+2] = in[i*3+2];
- n++;
}
-
+
*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,
From 90d5a85b851fc2d3322b30bf664c627b03c800be Mon Sep 17 00:00:00 2001
From: Qian Qian
Date: Mon, 3 Feb 2014 16:34:47 +0800
Subject: [PATCH 24/49] calculate off-mesh-connection height correctly
---
Detour/Source/DetourNavMeshQuery.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Detour/Source/DetourNavMeshQuery.cpp b/Detour/Source/DetourNavMeshQuery.cpp
index bdbee00..66d56ea 100644
--- a/Detour/Source/DetourNavMeshQuery.cpp
+++ b/Detour/Source/DetourNavMeshQuery.cpp
@@ -682,8 +682,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;
From 6702143bc1d526e0ea98b0c1b6bcdc31490c5155 Mon Sep 17 00:00:00 2001
From: Mikko Mononen
Date: Tue, 11 Feb 2014 20:31:52 +0200
Subject: [PATCH 25/49] Fix for broken raycasts, changes how findNearestPoly
works, slight API change
- test case supports raycasts
- changes how findNearestPoly works directly over polygons
- API change: findNearestPoly has additional parameter
---
Detour/Include/DetourNavMesh.h | 3 +-
Detour/Include/DetourNavMeshQuery.h | 10 +-
Detour/Source/DetourNavMesh.cpp | 46 ++++++--
Detour/Source/DetourNavMeshQuery.cpp | 108 ++++++------------
DetourCrowd/Source/DetourCrowd.cpp | 6 +-
RecastDemo/Bin/Tests/raycast_test.txt | 5 +
RecastDemo/Include/TestCase.h | 2 +
RecastDemo/Source/NavMeshTesterTool.cpp | 12 +-
RecastDemo/Source/TestCase.cpp | 139 +++++++++++++++++++-----
RecastDemo/Source/main.cpp | 7 +-
10 files changed, 210 insertions(+), 128 deletions(-)
create mode 100644 RecastDemo/Bin/Tests/raycast_test.txt
diff --git a/Detour/Include/DetourNavMesh.h b/Detour/Include/DetourNavMesh.h
index d87fee8..95a63e4 100644
--- a/Detour/Include/DetourNavMesh.h
+++ b/Detour/Include/DetourNavMesh.h
@@ -613,8 +613,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/Detour/Include/DetourNavMeshQuery.h b/Detour/Include/DetourNavMeshQuery.h
index 6edf5bf..4a5112c 100644
--- a/Detour/Include/DetourNavMeshQuery.h
+++ b/Detour/Include/DetourNavMeshQuery.h
@@ -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/Detour/Source/DetourNavMesh.cpp b/Detour/Source/DetourNavMesh.cpp
index 3bc2b73..1063b10 100644
--- a/Detour/Source/DetourNavMesh.cpp
+++ b/Detour/Source/DetourNavMesh.cpp
@@ -617,10 +617,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)
{
@@ -630,11 +632,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];
@@ -660,6 +665,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.
@@ -698,17 +711,32 @@ dtPolyRef dtNavMesh::findNearestPolyInTile(const dtMeshTile* tile,
// Find nearest polygon amongst the nearby polygons.
dtPolyRef nearest = 0;
float nearestDistanceSqr = FLT_MAX;
+ bool nearestOverPoly = false;
for (int i = 0; i < polyCount; ++i)
{
dtPolyRef ref = polys[i];
float closestPtPoly[3];
- closestPointOnPolyInTile(tile, decodePolyIdPoly(ref), center, closestPtPoly);
- float d = dtVdistSqr(center, closestPtPoly);
- if (d < nearestDistanceSqr)
+ float diff[3];
+ bool posOverPoly = false;
+ 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)
{
- if (nearestPt)
- dtVcopy(nearestPt, closestPtPoly);
+ const dtMeshTile* tile = 0;
+ if (dtAbs(diff[1]) > tile->header->walkableClimb)
+ posOverPoly = false;
+ }
+
+ float d = dtVlenSqr(diff);
+
+ if (d < nearestDistanceSqr || (!nearestOverPoly && posOverPoly))
+ {
+ dtVcopy(nearestPt, closestPtPoly);
nearestDistanceSqr = d;
+ nearestOverPoly = posOverPoly;
nearest = ref;
}
}
diff --git a/Detour/Source/DetourNavMeshQuery.cpp b/Detour/Source/DetourNavMeshQuery.cpp
index bdbee00..ba67957 100644
--- a/Detour/Source/DetourNavMeshQuery.cpp
+++ b/Detour/Source/DetourNavMeshQuery.cpp
@@ -501,7 +501,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,14 +511,6 @@ dtStatus dtNavMeshQuery::closestPointOnPoly(dtPolyRef ref, const float* pos, flo
if (!tile)
return DT_FAILURE | DT_INVALID_PARAM;
- 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)
{
@@ -528,7 +520,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);
@@ -559,6 +553,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.
@@ -580,30 +582,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
@@ -743,17 +723,35 @@ dtStatus dtNavMeshQuery::findNearestPoly(const float* center, const float* exten
// Find nearest polygon amongst the nearby polygons.
dtPolyRef nearest = 0;
float nearestDistanceSqr = FLT_MAX;
+ bool nearestOverPoly = false;
for (int i = 0; i < polyCount; ++i)
{
dtPolyRef ref = polys[i];
float closestPtPoly[3];
- closestPointOnPoly(ref, center, closestPtPoly);
- float d = dtVdistSqr(center, closestPtPoly);
- if (d < nearestDistanceSqr)
+ float diff[3];
+ bool posOverPoly = false;
+ 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);
+ if (dtAbs(diff[1]) > tile->header->walkableClimb)
+ posOverPoly = false;
+ }
+
+ float d = dtVlenSqr(diff);
+
+ if (d < nearestDistanceSqr || (!nearestOverPoly && posOverPoly))
{
if (nearestPt)
dtVcopy(nearestPt, closestPtPoly);
nearestDistanceSqr = d;
+ nearestOverPoly = posOverPoly;
nearest = ref;
}
}
@@ -764,42 +762,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
diff --git a/DetourCrowd/Source/DetourCrowd.cpp b/DetourCrowd/Source/DetourCrowd.cpp
index 42b5b19..cf7a027 100644
--- a/DetourCrowd/Source/DetourCrowd.cpp
+++ b/DetourCrowd/Source/DetourCrowd.cpp
@@ -711,7 +711,7 @@ void dtCrowd::updateMoveRequest(const float /*dt*/)
if (reqPath[reqPathCount-1] != ag->targetRef)
{
// Partial path, constrain target position inside the last polygon.
- status = m_navquery->closestPointOnPoly(reqPath[reqPathCount-1], ag->targetPos, reqPos);
+ status = m_navquery->closestPointOnPoly(reqPath[reqPathCount-1], ag->targetPos, reqPos, 0);
if (dtStatusFailed(status))
reqPathCount = 0;
}
@@ -855,7 +855,7 @@ void dtCrowd::updateMoveRequest(const float /*dt*/)
{
// Partial path, constrain target position inside the last polygon.
float nearest[3];
- status = m_navquery->closestPointOnPoly(res[nres-1], targetPos, nearest);
+ status = m_navquery->closestPointOnPoly(res[nres-1], targetPos, nearest, 0);
if (dtStatusSucceed(status))
dtVcopy(targetPos, nearest);
else
@@ -949,7 +949,7 @@ void dtCrowd::checkPathValidity(dtCrowdAgent** agents, const int nagents, const
float nearest[3];
dtVcopy(nearest, agentPos);
agentRef = 0;
- dtStatus status = m_navquery->findNearestPoly(ag->npos, m_ext, &m_filter, &agentRef, nearest);
+ m_navquery->findNearestPoly(ag->npos, m_ext, &m_filter, &agentRef, nearest);
dtVcopy(agentPos, nearest);
if (!agentRef)
diff --git a/RecastDemo/Bin/Tests/raycast_test.txt b/RecastDemo/Bin/Tests/raycast_test.txt
new file mode 100644
index 0000000..a410aea
--- /dev/null
+++ b/RecastDemo/Bin/Tests/raycast_test.txt
@@ -0,0 +1,5 @@
+s Tile Mesh
+f nav_test.obj
+rc 45.133884 -0.533207 -3.775568 47.078232 7.797605 14.293253 0xffef 0x0
+rc 52.979847 -2.778793 -2.914886 50.628868 -2.350212 13.917850 0xffef 0x0
+rc 45.209217 2.024442 1.838851 46.888412 7.797606 15.772338 0xffef 0x0
diff --git a/RecastDemo/Include/TestCase.h b/RecastDemo/Include/TestCase.h
index 19d1b91..74994f1 100644
--- a/RecastDemo/Include/TestCase.h
+++ b/RecastDemo/Include/TestCase.h
@@ -26,6 +26,7 @@ class TestCase
enum TestType
{
TEST_PATHFIND,
+ TEST_RAYCAST,
};
struct Test
@@ -39,6 +40,7 @@ class TestCase
TestType type;
float spos[3], epos[3];
+ float nspos[3], nepos[3];
float radius;
int includeFlags, excludeFlags;
bool expand;
diff --git a/RecastDemo/Source/NavMeshTesterTool.cpp b/RecastDemo/Source/NavMeshTesterTool.cpp
index 3b49d4b..9839d2f 100644
--- a/RecastDemo/Source/NavMeshTesterTool.cpp
+++ b/RecastDemo/Source/NavMeshTesterTool.cpp
@@ -506,8 +506,8 @@ void NavMeshTesterTool::handleToggle()
if (m_pathIterPolyCount)
{
// Iterate over the path to find smooth path on the detail mesh surface.
- m_navQuery->closestPointOnPoly(m_startRef, m_spos, m_iterPos);
- m_navQuery->closestPointOnPoly(m_pathIterPolys[m_pathIterPolyCount-1], m_epos, m_targetPos);
+ m_navQuery->closestPointOnPoly(m_startRef, m_spos, m_iterPos, 0);
+ m_navQuery->closestPointOnPoly(m_pathIterPolys[m_pathIterPolyCount-1], m_epos, m_targetPos, 0);
m_nsmoothPath = 0;
@@ -650,7 +650,7 @@ void NavMeshTesterTool::handleUpdate(const float /*dt*/)
float epos[3];
dtVcopy(epos, m_epos);
if (m_polys[m_npolys-1] != m_endRef)
- m_navQuery->closestPointOnPoly(m_polys[m_npolys-1], m_epos, epos);
+ m_navQuery->closestPointOnPoly(m_polys[m_npolys-1], m_epos, epos, 0);
m_navQuery->findStraightPath(m_spos, epos, m_polys, m_npolys,
m_straightPath, m_straightPathFlags,
@@ -715,8 +715,8 @@ void NavMeshTesterTool::recalc()
int npolys = m_npolys;
float iterPos[3], targetPos[3];
- m_navQuery->closestPointOnPoly(m_startRef, m_spos, iterPos);
- m_navQuery->closestPointOnPoly(polys[npolys-1], m_epos, targetPos);
+ m_navQuery->closestPointOnPoly(m_startRef, m_spos, iterPos, 0);
+ m_navQuery->closestPointOnPoly(polys[npolys-1], m_epos, targetPos, 0);
static const float STEP_SIZE = 0.5f;
static const float SLOP = 0.01f;
@@ -855,7 +855,7 @@ void NavMeshTesterTool::recalc()
float epos[3];
dtVcopy(epos, m_epos);
if (m_polys[m_npolys-1] != m_endRef)
- m_navQuery->closestPointOnPoly(m_polys[m_npolys-1], m_epos, epos);
+ m_navQuery->closestPointOnPoly(m_polys[m_npolys-1], m_epos, epos, 0);
m_navQuery->findStraightPath(m_spos, epos, m_polys, m_npolys,
m_straightPath, m_straightPathFlags,
diff --git a/RecastDemo/Source/TestCase.cpp b/RecastDemo/Source/TestCase.cpp
index 95293f5..7d4052f 100644
--- a/RecastDemo/Source/TestCase.cpp
+++ b/RecastDemo/Source/TestCase.cpp
@@ -141,6 +141,20 @@ bool TestCase::load(const char* filePath)
&test->epos[0], &test->epos[1], &test->epos[2],
&test->includeFlags, &test->excludeFlags);
}
+ else if (row[0] == 'r' && row[1] == 'c')
+ {
+ // Pathfind test.
+ Test* test = new Test;
+ memset(test, 0, sizeof(Test));
+ test->type = TEST_RAYCAST;
+ test->expand = false;
+ test->next = m_tests;
+ m_tests = test;
+ sscanf(row+2, "%f %f %f %f %f %f %x %x",
+ &test->spos[0], &test->spos[1], &test->spos[2],
+ &test->epos[0], &test->epos[1], &test->epos[2],
+ &test->includeFlags, &test->excludeFlags);
+ }
}
delete [] buf;
@@ -187,8 +201,8 @@ void TestCase::doTests(dtNavMesh* navmesh, dtNavMeshQuery* navquery)
TimeVal findNearestPolyStart = getPerfTime();
dtPolyRef startRef, endRef;
- navquery->findNearestPoly(iter->spos, polyPickExt, &filter, &startRef, 0);
- navquery->findNearestPoly(iter->epos, polyPickExt, &filter, &endRef, 0);
+ navquery->findNearestPoly(iter->spos, polyPickExt, &filter, &startRef, iter->nspos);
+ navquery->findNearestPoly(iter->epos, polyPickExt, &filter, &endRef, iter->nepos);
TimeVal findNearestPolyEnd = getPerfTime();
iter->findNearestPolyTime += getPerfDeltaTimeUsec(findNearestPolyStart, findNearestPolyEnd);
@@ -196,35 +210,82 @@ void TestCase::doTests(dtNavMesh* navmesh, dtNavMeshQuery* navquery)
if (!startRef || ! endRef)
continue;
- // Find path
- TimeVal findPathStart = getPerfTime();
+ if (iter->type == TEST_PATHFIND)
+ {
+ // Find path
+ TimeVal findPathStart = getPerfTime();
- navquery->findPath(startRef, endRef, iter->spos, iter->epos, &filter, polys, &iter->npolys, MAX_POLYS);
-
- TimeVal findPathEnd = getPerfTime();
- iter->findPathTime += getPerfDeltaTimeUsec(findPathStart, findPathEnd);
-
- // Find straight path
- if (iter->npolys)
- {
- TimeVal findStraightPathStart = getPerfTime();
+ navquery->findPath(startRef, endRef, iter->spos, iter->epos, &filter, polys, &iter->npolys, MAX_POLYS);
- navquery->findStraightPath(iter->spos, iter->epos, polys, iter->npolys,
- straight, 0, 0, &iter->nstraight, MAX_POLYS);
- TimeVal findStraightPathEnd = getPerfTime();
- iter->findStraightPathTime += getPerfDeltaTimeUsec(findStraightPathStart, findStraightPathEnd);
- }
+ TimeVal findPathEnd = getPerfTime();
+ iter->findPathTime += getPerfDeltaTimeUsec(findPathStart, findPathEnd);
- // Copy results
- if (iter->npolys)
- {
- iter->polys = new dtPolyRef[iter->npolys];
- memcpy(iter->polys, polys, sizeof(dtPolyRef)*iter->npolys);
+ // Find straight path
+ if (iter->npolys)
+ {
+ TimeVal findStraightPathStart = getPerfTime();
+
+ navquery->findStraightPath(iter->spos, iter->epos, polys, iter->npolys,
+ straight, 0, 0, &iter->nstraight, MAX_POLYS);
+ TimeVal findStraightPathEnd = getPerfTime();
+ iter->findStraightPathTime += getPerfDeltaTimeUsec(findStraightPathStart, findStraightPathEnd);
+ }
+
+ // Copy results
+ if (iter->npolys)
+ {
+ iter->polys = new dtPolyRef[iter->npolys];
+ memcpy(iter->polys, polys, sizeof(dtPolyRef)*iter->npolys);
+ }
+ if (iter->nstraight)
+ {
+ iter->straight = new float[iter->nstraight*3];
+ memcpy(iter->straight, straight, sizeof(float)*3*iter->nstraight);
+ }
}
- if (iter->nstraight)
+ else if (iter->type == TEST_RAYCAST)
{
- iter->straight = new float[iter->nstraight*3];
- memcpy(iter->straight, straight, sizeof(float)*3*iter->nstraight);
+ float t = 0;
+ float hitNormal[3], hitPos[3];
+
+ iter->straight = new float[2*3];
+ iter->nstraight = 2;
+
+ iter->straight[0] = iter->spos[0];
+ iter->straight[1] = iter->spos[1];
+ iter->straight[2] = iter->spos[2];
+
+ TimeVal findPathStart = getPerfTime();
+
+ navquery->raycast(startRef, iter->spos, iter->epos, &filter, &t, hitNormal, polys, &iter->npolys, MAX_POLYS);
+
+ TimeVal findPathEnd = getPerfTime();
+ iter->findPathTime += getPerfDeltaTimeUsec(findPathStart, findPathEnd);
+
+ if (t > 1)
+ {
+ // No hit
+ dtVcopy(hitPos, iter->epos);
+ }
+ else
+ {
+ // Hit
+ dtVlerp(hitPos, iter->spos, iter->epos, t);
+ }
+ // Adjust height.
+ if (iter->npolys > 0)
+ {
+ float h = 0;
+ navquery->getPolyHeight(polys[iter->npolys-1], hitPos, &h);
+ hitPos[1] = h;
+ }
+ dtVcopy(&iter->straight[3], hitPos);
+
+ if (iter->npolys)
+ {
+ iter->polys = new dtPolyRef[iter->npolys];
+ memcpy(iter->polys, polys, sizeof(dtPolyRef)*iter->npolys);
+ }
}
}
@@ -259,6 +320,32 @@ void TestCase::handleRender()
glColor4ub(51,102,0,129);
glVertex3f(iter->epos[0],iter->epos[1]-0.3f,iter->epos[2]);
glVertex3f(iter->epos[0],iter->epos[1]+0.3f,iter->epos[2]);
+
+ if (iter->expand)
+ {
+ const float s = 0.1f;
+ glColor4ub(255,32,0,128);
+ glVertex3f(iter->spos[0]-s,iter->spos[1],iter->spos[2]);
+ glVertex3f(iter->spos[0]+s,iter->spos[1],iter->spos[2]);
+ glVertex3f(iter->spos[0],iter->spos[1],iter->spos[2]-s);
+ glVertex3f(iter->spos[0],iter->spos[1],iter->spos[2]+s);
+ glColor4ub(255,192,0,255);
+ glVertex3f(iter->nspos[0]-s,iter->nspos[1],iter->nspos[2]);
+ glVertex3f(iter->nspos[0]+s,iter->nspos[1],iter->nspos[2]);
+ glVertex3f(iter->nspos[0],iter->nspos[1],iter->nspos[2]-s);
+ glVertex3f(iter->nspos[0],iter->nspos[1],iter->nspos[2]+s);
+
+ glColor4ub(255,32,0,128);
+ glVertex3f(iter->epos[0]-s,iter->epos[1],iter->epos[2]);
+ glVertex3f(iter->epos[0]+s,iter->epos[1],iter->epos[2]);
+ glVertex3f(iter->epos[0],iter->epos[1],iter->epos[2]-s);
+ glVertex3f(iter->epos[0],iter->epos[1],iter->epos[2]+s);
+ glColor4ub(255,192,0,255);
+ glVertex3f(iter->nepos[0]-s,iter->nepos[1],iter->nepos[2]);
+ glVertex3f(iter->nepos[0]+s,iter->nepos[1],iter->nepos[2]);
+ glVertex3f(iter->nepos[0],iter->nepos[1],iter->nepos[2]-s);
+ glVertex3f(iter->nepos[0],iter->nepos[1],iter->nepos[2]+s);
+ }
if (iter->expand)
glColor4ub(255,192,0,255);
diff --git a/RecastDemo/Source/main.cpp b/RecastDemo/Source/main.cpp
index fc85942..248e2fb 100644
--- a/RecastDemo/Source/main.cpp
+++ b/RecastDemo/Source/main.cpp
@@ -817,7 +817,6 @@ int main(int /*argc*/, char** /*argv*/)
{
delete geom;
geom = 0;
-
showLog = true;
logScroll = 0;
ctx.dumpLog("Geom load log %s:", meshName);
@@ -827,6 +826,10 @@ int main(int /*argc*/, char** /*argv*/)
sample->handleMeshChanged(geom);
}
+ // This will ensure that tile & poly bits are updated in tiled sample.
+ if (sample)
+ sample->handleSettings();
+
ctx.resetLog();
if (sample && !sample->handleBuild())
{
@@ -860,7 +863,7 @@ int main(int /*argc*/, char** /*argv*/)
}
rx = 45;
ry = -45;
- glFogf(GL_FOG_START, camr*0.1f);
+ glFogf(GL_FOG_START, camr*0.2f);
glFogf(GL_FOG_END, camr*1.25f);
}
From 19e2d8dbe61c25c5aa447a0dd0b802d472b83dc0 Mon Sep 17 00:00:00 2001
From: Mikko Mononen
Date: Sun, 16 Feb 2014 11:27:45 +0200
Subject: [PATCH 26/49] Fixed pos-over-poly case for finding nearest polygon
---
Detour/Source/DetourNavMesh.cpp | 16 ++++++++--------
Detour/Source/DetourNavMeshQuery.cpp | 15 ++++++++-------
RecastDemo/Bin/Tests/raycast_test.txt | 1 +
3 files changed, 17 insertions(+), 15 deletions(-)
diff --git a/Detour/Source/DetourNavMesh.cpp b/Detour/Source/DetourNavMesh.cpp
index 1063b10..9d627be 100644
--- a/Detour/Source/DetourNavMesh.cpp
+++ b/Detour/Source/DetourNavMesh.cpp
@@ -711,13 +711,13 @@ dtPolyRef dtNavMesh::findNearestPolyInTile(const dtMeshTile* tile,
// Find nearest polygon amongst the nearby polygons.
dtPolyRef nearest = 0;
float nearestDistanceSqr = FLT_MAX;
- bool nearestOverPoly = false;
for (int i = 0; i < polyCount; ++i)
{
dtPolyRef ref = polys[i];
float closestPtPoly[3];
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
@@ -725,18 +725,18 @@ dtPolyRef dtNavMesh::findNearestPolyInTile(const dtMeshTile* tile,
dtVsub(diff, center, closestPtPoly);
if (posOverPoly)
{
- const dtMeshTile* tile = 0;
- if (dtAbs(diff[1]) > tile->header->walkableClimb)
- posOverPoly = false;
+ d = dtAbs(diff[1]) - tile->header->walkableClimb;
+ d = d > 0 ? d*d : 0;
+ }
+ else
+ {
+ d = dtVlenSqr(diff);
}
- float d = dtVlenSqr(diff);
-
- if (d < nearestDistanceSqr || (!nearestOverPoly && posOverPoly))
+ if (d < nearestDistanceSqr)
{
dtVcopy(nearestPt, closestPtPoly);
nearestDistanceSqr = d;
- nearestOverPoly = posOverPoly;
nearest = ref;
}
}
diff --git a/Detour/Source/DetourNavMeshQuery.cpp b/Detour/Source/DetourNavMeshQuery.cpp
index ba67957..cc16946 100644
--- a/Detour/Source/DetourNavMeshQuery.cpp
+++ b/Detour/Source/DetourNavMeshQuery.cpp
@@ -723,13 +723,13 @@ dtStatus dtNavMeshQuery::findNearestPoly(const float* center, const float* exten
// Find nearest polygon amongst the nearby polygons.
dtPolyRef nearest = 0;
float nearestDistanceSqr = FLT_MAX;
- bool nearestOverPoly = false;
for (int i = 0; i < polyCount; ++i)
{
dtPolyRef ref = polys[i];
float closestPtPoly[3];
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
@@ -740,18 +740,19 @@ dtStatus dtNavMeshQuery::findNearestPoly(const float* center, const float* exten
const dtMeshTile* tile = 0;
const dtPoly* poly = 0;
m_nav->getTileAndPolyByRefUnsafe(polys[i], &tile, &poly);
- if (dtAbs(diff[1]) > tile->header->walkableClimb)
- posOverPoly = false;
+ d = dtAbs(diff[1]) - tile->header->walkableClimb;
+ d = d > 0 ? d*d : 0;
+ }
+ else
+ {
+ d = dtVlenSqr(diff);
}
- float d = dtVlenSqr(diff);
-
- if (d < nearestDistanceSqr || (!nearestOverPoly && posOverPoly))
+ if (d < nearestDistanceSqr)
{
if (nearestPt)
dtVcopy(nearestPt, closestPtPoly);
nearestDistanceSqr = d;
- nearestOverPoly = posOverPoly;
nearest = ref;
}
}
diff --git a/RecastDemo/Bin/Tests/raycast_test.txt b/RecastDemo/Bin/Tests/raycast_test.txt
index a410aea..3fde0ee 100644
--- a/RecastDemo/Bin/Tests/raycast_test.txt
+++ b/RecastDemo/Bin/Tests/raycast_test.txt
@@ -3,3 +3,4 @@ f nav_test.obj
rc 45.133884 -0.533207 -3.775568 47.078232 7.797605 14.293253 0xffef 0x0
rc 52.979847 -2.778793 -2.914886 50.628868 -2.350212 13.917850 0xffef 0x0
rc 45.209217 2.024442 1.838851 46.888412 7.797606 15.772338 0xffef 0x0
+rc 45.388317 -0.562073 -3.673226 46.651001 7.797606 15.513507 0xffef 0x0
\ No newline at end of file
From 77ebf643531c90ea712b496e23c48f0b0671f43d Mon Sep 17 00:00:00 2001
From: jackpoz
Date: Wed, 19 Feb 2014 21:00:14 +0100
Subject: [PATCH 27/49] Fix rcMergePolyMeshes() ignoring portals of input
meshes.
Copy the portals from input meshes on border to the output mesh.
---
Recast/Source/RecastMesh.cpp | 36 ++++++++++++++++++++++++++++++++++++
1 file changed, 36 insertions(+)
diff --git a/Recast/Source/RecastMesh.cpp b/Recast/Source/RecastMesh.cpp
index 534a72e..8af609b 100644
--- a/Recast/Source/RecastMesh.cpp
+++ b/Recast/Source/RecastMesh.cpp
@@ -1324,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];
@@ -1344,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;
+ }
+ }
+ }
+ }
}
}
From 3e8b41303690c63985351f1e9c4e9063c6c2855f Mon Sep 17 00:00:00 2001
From: Mikko Mononen
Date: Fri, 21 Feb 2014 10:32:35 +0200
Subject: [PATCH 28/49] Fix for detail mesh height data generation
- if the new region based height data generation fails, use the old
flood fill based method
---
Recast/Source/RecastMeshDetail.cpp | 147 ++++++++++++++++++++++++++++-
1 file changed, 146 insertions(+), 1 deletion(-)
diff --git a/Recast/Source/RecastMeshDetail.cpp b/Recast/Source/RecastMeshDetail.cpp
index 69ed147..8325b88 100644
--- a/Recast/Source/RecastMeshDetail.cpp
+++ b/Recast/Source/RecastMeshDetail.cpp
@@ -741,6 +741,145 @@ static bool buildPolyDetail(rcContext* ctx, const float* in, const int nin,
return true;
}
+
+static void getHeightDataSeedsFromVertices(const rcCompactHeightfield& chf,
+ const unsigned short* poly, const int npoly,
+ const unsigned short* verts, const int bs,
+ rcHeightPatch& hp, rcIntArray& stack)
+{
+ // Floodfill the heightfield to get 2D height data,
+ // starting at vertex locations as seeds.
+
+ // Note: Reads to the compact heightfield are offset by border size (bs)
+ // since border size offset is already removed from the polymesh vertices.
+
+ memset(hp.data, 0, sizeof(unsigned short)*hp.width*hp.height);
+
+ stack.resize(0);
+
+ static const int offset[9*2] =
+ {
+ 0,0, -1,-1, 0,-1, 1,-1, 1,0, 1,1, 0,1, -1,1, -1,0,
+ };
+
+ // Use poly vertices as seed points for the flood fill.
+ for (int j = 0; j < npoly; ++j)
+ {
+ int cx = 0, cz = 0, ci =-1;
+ int dmin = RC_UNSET_HEIGHT;
+ for (int k = 0; k < 9; ++k)
+ {
+ const int ax = (int)verts[poly[j]*3+0] + offset[k*2+0];
+ const int ay = (int)verts[poly[j]*3+1];
+ const int az = (int)verts[poly[j]*3+2] + offset[k*2+1];
+ if (ax < hp.xmin || ax >= hp.xmin+hp.width ||
+ az < hp.ymin || az >= hp.ymin+hp.height)
+ continue;
+
+ const rcCompactCell& c = chf.cells[(ax+bs)+(az+bs)*chf.width];
+ for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
+ {
+ const rcCompactSpan& s = chf.spans[i];
+ int d = rcAbs(ay - (int)s.y);
+ if (d < dmin)
+ {
+ cx = ax;
+ cz = az;
+ ci = i;
+ dmin = d;
+ }
+ }
+ }
+ if (ci != -1)
+ {
+ stack.push(cx);
+ stack.push(cz);
+ stack.push(ci);
+ }
+ }
+
+ // Find center of the polygon using flood fill.
+ int pcx = 0, pcz = 0;
+ for (int j = 0; j < npoly; ++j)
+ {
+ pcx += (int)verts[poly[j]*3+0];
+ pcz += (int)verts[poly[j]*3+2];
+ }
+ pcx /= npoly;
+ pcz /= npoly;
+
+ for (int i = 0; i < stack.size(); i += 3)
+ {
+ int cx = stack[i+0];
+ int cy = stack[i+1];
+ int idx = cx-hp.xmin+(cy-hp.ymin)*hp.width;
+ hp.data[idx] = 1;
+ }
+
+ while (stack.size() > 0)
+ {
+ int ci = stack.pop();
+ int cy = stack.pop();
+ int cx = stack.pop();
+
+ // Check if close to center of the polygon.
+ if (rcAbs(cx-pcx) <= 1 && rcAbs(cy-pcz) <= 1)
+ {
+ stack.resize(0);
+ stack.push(cx);
+ stack.push(cy);
+ stack.push(ci);
+ break;
+ }
+
+ const rcCompactSpan& cs = chf.spans[ci];
+
+ for (int dir = 0; dir < 4; ++dir)
+ {
+ if (rcGetCon(cs, dir) == RC_NOT_CONNECTED) continue;
+
+ const int ax = cx + rcGetDirOffsetX(dir);
+ const int ay = cy + rcGetDirOffsetY(dir);
+
+ if (ax < hp.xmin || ax >= (hp.xmin+hp.width) ||
+ ay < hp.ymin || ay >= (hp.ymin+hp.height))
+ continue;
+
+ if (hp.data[ax-hp.xmin+(ay-hp.ymin)*hp.width] != 0)
+ continue;
+
+ const int ai = (int)chf.cells[(ax+bs)+(ay+bs)*chf.width].index + rcGetCon(cs, dir);
+
+ int idx = ax-hp.xmin+(ay-hp.ymin)*hp.width;
+ hp.data[idx] = 1;
+
+ stack.push(ax);
+ stack.push(ay);
+ stack.push(ai);
+ }
+ }
+
+ memset(hp.data, 0xff, sizeof(unsigned short)*hp.width*hp.height);
+
+ // Mark start locations.
+ for (int i = 0; i < stack.size(); i += 3)
+ {
+ int cx = stack[i+0];
+ int cy = stack[i+1];
+ int ci = stack[i+2];
+ 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,
@@ -752,6 +891,8 @@ static void getHeightData(const rcCompactHeightfield& chf,
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.
@@ -769,6 +910,7 @@ static void getHeightData(const rcCompactHeightfield& chf,
{
// 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
@@ -800,6 +942,10 @@ static void getHeightData(const rcCompactHeightfield& chf,
}
}
+ // 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;
@@ -1171,4 +1317,3 @@ bool rcMergePolyMeshDetails(rcContext* ctx, rcPolyMeshDetail** meshes, const int
return true;
}
-
From c98c31f33bc126c68e607a735507b6a615c8ce83 Mon Sep 17 00:00:00 2001
From: Jonathan Adamczewski
Date: Sun, 23 Mar 2014 01:20:54 -0700
Subject: [PATCH 29/49] Constrain the window's aspect ratio
---
RecastDemo/Source/main.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/RecastDemo/Source/main.cpp b/RecastDemo/Source/main.cpp
index 248e2fb..c9546a1 100644
--- a/RecastDemo/Source/main.cpp
+++ b/RecastDemo/Source/main.cpp
@@ -101,7 +101,8 @@ int main(int /*argc*/, char** /*argv*/)
}
else
{
- width = vi->current_w - 20;
+ width = rcMin(vi->current_w, (int)(vi->current_h * 16.0 / 9.0));
+ width = width - 80;
height = vi->current_h - 80;
screen = SDL_SetVideoMode(width, height, 0, SDL_OPENGL);
}
From 97fb44e422114306b1b0062199bbc7397a77c9f3 Mon Sep 17 00:00:00 2001
From: Jonathan Adamczewski
Date: Sun, 23 Mar 2014 00:19:32 -0700
Subject: [PATCH 30/49] Update to latest stb versions
Includes minor compile warning fix
---
RecastDemo/Contrib/stb_image.h | 3377 +++++++++++++++----------
RecastDemo/Contrib/stb_truetype.h | 3869 +++++++++++++++--------------
2 files changed, 4141 insertions(+), 3105 deletions(-)
diff --git a/RecastDemo/Contrib/stb_image.h b/RecastDemo/Contrib/stb_image.h
index aedc64e..9e84ca5 100644
--- a/RecastDemo/Contrib/stb_image.h
+++ b/RecastDemo/Contrib/stb_image.h
@@ -1,82 +1,81 @@
-/* stbi-1.18 - public domain JPEG/PNG reader - http://nothings.org/stb_image.c
- when you control the images you're loading
+/* stbi-1.33 - public domain JPEG/PNG reader - http://nothings.org/stb_image.c
+ when you control the images you're loading
+ no warranty implied; use at your own risk
QUICK NOTES:
Primarily of interest to game developers and other people who can
avoid problematic images and only need the trivial interface
- JPEG baseline (no JPEG progressive, no oddball channel decimations)
- PNG 8-bit only
- BMP non-1bpp, non-RLE
- TGA (not sure what subset, if a subset)
- PSD (composited view only, no extra channels)
- HDR (radiance rgbE format)
- writes BMP,TGA (define STBI_NO_WRITE to remove code)
- decoded from memory or through stdio FILE (define STBI_NO_STDIO to remove code)
- supports installable dequantizing-IDCT, YCbCr-to-RGB conversion (define STBI_SIMD)
-
- TODO:
- stbi_info_*
-
- history:
- 1.18 fix a threading bug (local mutable static)
- 1.17 support interlaced PNG
- 1.16 major bugfix - convert_format converted one too many pixels
- 1.15 initialize some fields for thread safety
- 1.14 fix threadsafe conversion bug; header-file-only version (#define STBI_HEADER_FILE_ONLY before including)
- 1.13 threadsafe
- 1.12 const qualifiers in the API
- 1.11 Support installable IDCT, colorspace conversion routines
- 1.10 Fixes for 64-bit (don't use "unsigned long")
- optimized upsampling by Fabian "ryg" Giesen
- 1.09 Fix format-conversion for PSD code (bad global variables!)
- 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz
- 1.07 attempt to fix C++ warning/errors again
- 1.06 attempt to fix C++ warning/errors again
- 1.05 fix TGA loading to return correct *comp and use good luminance calc
- 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free
- 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR
- 1.02 support for (subset of) HDR files, float interface for preferred access to them
- 1.01 fix bug: possible bug in handling right-side up bmps... not sure
- fix bug: the stbi_bmp_load() and stbi_tga_load() functions didn't work at all
- 1.00 interface to zlib that skips zlib header
- 0.99 correct handling of alpha in palette
- 0.98 TGA loader by lonesock; dynamically add loaders (untested)
- 0.97 jpeg errors on too large a file; also catch another malloc failure
- 0.96 fix detection of invalid v value - particleman@mollyrocket forum
- 0.95 during header scan, seek to markers in case of padding
- 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same
- 0.93 handle jpegtran output; verbose errors
- 0.92 read 4,8,16,24,32-bit BMP files of several formats
- 0.91 output 24-bit Windows 3.0 BMP files
- 0.90 fix a few more warnings; bump version number to approach 1.0
- 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd
- 0.60 fix compiling as c++
- 0.59 fix warnings: merge Dave Moore's -Wall fixes
- 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian
- 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less
- than 16 available
- 0.56 fix bug: zlib uncompressed mode len vs. nlen
- 0.55 fix bug: restart_interval not initialized to 0
- 0.54 allow NULL for 'int *comp'
- 0.53 fix bug in png 3->4; speedup png decoding
- 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments
- 0.51 obey req_comp requests, 1-component jpegs return as 1-component,
- on 'test' only check type, not whether we support this variant
-*/
+ JPEG baseline (no JPEG progressive)
+ PNG 8-bit-per-channel only
+ TGA (not sure what subset, if a subset)
+ BMP non-1bpp, non-RLE
+ PSD (composited view only, no extra channels)
+
+ GIF (*comp always reports as 4-channel)
+ HDR (radiance rgbE format)
+ PIC (Softimage PIC)
+
+ - decode from memory or through FILE (define STBI_NO_STDIO to remove code)
+ - decode from arbitrary I/O callbacks
+ - overridable dequantizing-IDCT, YCbCr-to-RGB conversion (define STBI_SIMD)
+
+ Latest revisions:
+ 1.33 (2011-07-14) minor fixes suggested by Dave Moore
+ 1.32 (2011-07-13) info support for all filetypes (SpartanJ)
+ 1.31 (2011-06-19) a few more leak fixes, bug in PNG handling (SpartanJ)
+ 1.30 (2011-06-11) added ability to load files via io callbacks (Ben Wenger)
+ 1.29 (2010-08-16) various warning fixes from Aurelien Pocheville
+ 1.28 (2010-08-01) fix bug in GIF palette transparency (SpartanJ)
+ 1.27 (2010-08-01) cast-to-uint8 to fix warnings (Laurent Gomila)
+ allow trailing 0s at end of image data (Laurent Gomila)
+ 1.26 (2010-07-24) fix bug in file buffering for PNG reported by SpartanJ
+
+ See end of file for full revision history.
+
+ TODO:
+ stbi_info support for BMP,PSD,HDR,PIC
+
+
+ ============================ Contributors =========================
+
+ Image formats Optimizations & bugfixes
+ Sean Barrett (jpeg, png, bmp) Fabian "ryg" Giesen
+ Nicolas Schulz (hdr, psd)
+ Jonathan Dummer (tga) Bug fixes & warning fixes
+ Jean-Marc Lienher (gif) Marc LeBlanc
+ Tom Seddon (pic) Christpher Lloyd
+ Thatcher Ulrich (psd) Dave Moore
+ Won Chun
+ the Horde3D community
+ Extensions, features Janez Zemva
+ Jetro Lauha (stbi_info) Jonathan Blow
+ James "moose2000" Brown (iPhone PNG) Laurent Gomila
+ Ben "Disch" Wenger (io callbacks) Aruelien Pocheville
+ Martin "SpartanJ" Golini Ryamond Barbiero
+ David Woo
+
+
+ If your name should be here but isn't, let Sean know.
+
+*/
#ifndef STBI_INCLUDE_STB_IMAGE_H
#define STBI_INCLUDE_STB_IMAGE_H
+// To get a header file for this, either cut and paste the header,
+// or create stb_image.h, #define STBI_HEADER_FILE_ONLY, and
+// then include stb_image.c from it.
+
//// begin header file ////////////////////////////////////////////////////
//
// Limitations:
-// - no progressive/interlaced support (jpeg, png)
-// - 8-bit samples only (jpeg, png)
-// - not threadsafe
-// - channel subsampling of at most 2 in each dimension (jpeg)
+// - no jpeg progressive support
+// - non-HDR formats support 8-bit samples only (jpeg, png)
// - no delayed line count (jpeg) -- IJG doesn't support either
+// - no 1-bit BMP
+// - GIF always returns *comp=4
//
// Basic usage (see HDR discussion below):
// int x,y,n;
@@ -84,6 +83,7 @@
// // ... process data if not NULL ...
// // ... x = width, y = height, n = # 8-bit components per pixel ...
// // ... replace '0' with '1'..'4' to force that many components per pixel
+// // ... but 'n' will always be the number that it would have been if you said 0
// stbi_image_free(data)
//
// Standard parameters:
@@ -118,8 +118,22 @@
// compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly
// more user-friendly ones.
//
-// Paletted PNG and BMP images are automatically depalettized.
+// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized.
//
+// ===========================================================================
+//
+// iPhone PNG support:
+//
+// By default we convert iphone-formatted PNGs back to RGB; nominally they
+// would silently load as BGR, except the existing code should have just
+// failed on such iPhone PNGs. But you can disable this conversion by
+// by calling stbi_convert_iphone_png_to_rgb(0), in which case
+// you will always just get the native iphone "format" through.
+//
+// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per
+// pixel to remove any premultiplied alpha *only* if the image file explicitly
+// says there's premultiplied data (currently only happens in iPhone images,
+// and only if iPhone convert-to-rgb processing is on).
//
// ===========================================================================
//
@@ -156,8 +170,26 @@
// not), using:
//
// stbi_is_hdr(char *filename);
+//
+// ===========================================================================
+//
+// I/O callbacks
+//
+// I/O callbacks allow you to read from arbitrary sources, like packaged
+// files or some other source. Data read from callbacks are processed
+// through a small internal buffer (currently 128 bytes) to try to reduce
+// overhead.
+//
+// The three functions you must define are "read" (reads some bytes of data),
+// "skip" (skips some bytes of data), "eof" (reports if the stream is at the end).
+
#ifndef STBI_NO_STDIO
+
+#if defined(_MSC_VER) && _MSC_VER >= 0x1400
+#define _CRT_SECURE_NO_WARNINGS // suppress bogus warnings about fopen()
+#endif
+
#include
#endif
@@ -170,7 +202,7 @@ enum
STBI_grey = 1,
STBI_grey_alpha = 2,
STBI_rgb = 3,
- STBI_rgb_alpha = 4,
+ STBI_rgb_alpha = 4
};
typedef unsigned char stbi_uc;
@@ -179,58 +211,87 @@ typedef unsigned char stbi_uc;
extern "C" {
#endif
-// WRITING API
-
-#if !defined(STBI_NO_WRITE) && !defined(STBI_NO_STDIO)
-// write a BMP/TGA file given tightly packed 'comp' channels (no padding, nor bmp-stride-padding)
-// (you must include the appropriate extension in the filename).
-// returns TRUE on success, FALSE if couldn't open file, error writing file
-extern int stbi_write_bmp (char const *filename, int x, int y, int comp, void *data);
-extern int stbi_write_tga (char const *filename, int x, int y, int comp, void *data);
-#endif
-
+//////////////////////////////////////////////////////////////////////////////
+//
// PRIMARY API - works on images of any type
+//
+//
// load image by filename, open file, or memory buffer
+//
+
+extern stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp);
+
#ifndef STBI_NO_STDIO
extern stbi_uc *stbi_load (char const *filename, int *x, int *y, int *comp, int req_comp);
extern stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp);
-extern int stbi_info_from_file (FILE *f, int *x, int *y, int *comp);
-#endif
-extern stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp);
// for stbi_load_from_file, file pointer is left pointing immediately after image
+#endif
+
+typedef struct
+{
+ int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read
+ void (*skip) (void *user,unsigned n); // skip the next 'n' bytes
+ int (*eof) (void *user); // returns nonzero if we are at end of file/data
+} stbi_io_callbacks;
+
+extern stbi_uc *stbi_load_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp);
#ifndef STBI_NO_HDR
-#ifndef STBI_NO_STDIO
-extern float *stbi_loadf (char const *filename, int *x, int *y, int *comp, int req_comp);
-extern float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *comp, int req_comp);
-#endif
-extern float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp);
+ extern float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp);
-extern void stbi_hdr_to_ldr_gamma(float gamma);
-extern void stbi_hdr_to_ldr_scale(float scale);
+ #ifndef STBI_NO_STDIO
+ extern float *stbi_loadf (char const *filename, int *x, int *y, int *comp, int req_comp);
+ extern float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *comp, int req_comp);
+ #endif
+
+ extern float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp);
-extern void stbi_ldr_to_hdr_gamma(float gamma);
-extern void stbi_ldr_to_hdr_scale(float scale);
+ extern void stbi_hdr_to_ldr_gamma(float gamma);
+ extern void stbi_hdr_to_ldr_scale(float scale);
+ extern void stbi_ldr_to_hdr_gamma(float gamma);
+ extern void stbi_ldr_to_hdr_scale(float scale);
#endif // STBI_NO_HDR
+// stbi_is_hdr is always defined
+extern int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user);
+extern int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len);
+#ifndef STBI_NO_STDIO
+extern int stbi_is_hdr (char const *filename);
+extern int stbi_is_hdr_from_file(FILE *f);
+#endif // STBI_NO_STDIO
+
+
// get a VERY brief reason for failure
// NOT THREADSAFE
-extern const char *stbi_failure_reason (void);
+extern const char *stbi_failure_reason (void);
// free the loaded image -- this is just free()
extern void stbi_image_free (void *retval_from_stbi_load);
// get image dimensions & components without fully decoding
extern int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp);
-extern int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len);
+extern int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp);
+
#ifndef STBI_NO_STDIO
extern int stbi_info (char const *filename, int *x, int *y, int *comp);
-extern int stbi_is_hdr (char const *filename);
-extern int stbi_is_hdr_from_file(FILE *f);
+extern int stbi_info_from_file (FILE *f, int *x, int *y, int *comp);
+
#endif
+
+
+// for image formats that explicitly notate that they have premultiplied alpha,
+// we just return the colors as stored in the file. set this flag to force
+// unpremultiplication. results are undefined if the unpremultiply overflow.
+extern void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply);
+
+// indicate whether we should process iphone images back to canonical format,
+// or just pass them through "as-is"
+extern void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert);
+
+
// ZLIB client - used by PNG, available for other purposes
extern char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen);
@@ -240,99 +301,15 @@ extern int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffe
extern char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen);
extern int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen);
-// TYPE-SPECIFIC ACCESS
-
-// is it a jpeg?
-extern int stbi_jpeg_test_memory (stbi_uc const *buffer, int len);
-extern stbi_uc *stbi_jpeg_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp);
-extern int stbi_jpeg_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp);
-
-#ifndef STBI_NO_STDIO
-extern stbi_uc *stbi_jpeg_load (char const *filename, int *x, int *y, int *comp, int req_comp);
-extern int stbi_jpeg_test_file (FILE *f);
-extern stbi_uc *stbi_jpeg_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp);
-
-extern int stbi_jpeg_info (char const *filename, int *x, int *y, int *comp);
-extern int stbi_jpeg_info_from_file (FILE *f, int *x, int *y, int *comp);
-#endif
-
-// is it a png?
-extern int stbi_png_test_memory (stbi_uc const *buffer, int len);
-extern stbi_uc *stbi_png_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp);
-extern int stbi_png_info_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp);
-
-#ifndef STBI_NO_STDIO
-extern stbi_uc *stbi_png_load (char const *filename, int *x, int *y, int *comp, int req_comp);
-extern int stbi_png_info (char const *filename, int *x, int *y, int *comp);
-extern int stbi_png_test_file (FILE *f);
-extern stbi_uc *stbi_png_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp);
-extern int stbi_png_info_from_file (FILE *f, int *x, int *y, int *comp);
-#endif
-
-// is it a bmp?
-extern int stbi_bmp_test_memory (stbi_uc const *buffer, int len);
-
-extern stbi_uc *stbi_bmp_load (char const *filename, int *x, int *y, int *comp, int req_comp);
-extern stbi_uc *stbi_bmp_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp);
-#ifndef STBI_NO_STDIO
-extern int stbi_bmp_test_file (FILE *f);
-extern stbi_uc *stbi_bmp_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp);
-#endif
-
-// is it a tga?
-extern int stbi_tga_test_memory (stbi_uc const *buffer, int len);
-
-extern stbi_uc *stbi_tga_load (char const *filename, int *x, int *y, int *comp, int req_comp);
-extern stbi_uc *stbi_tga_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp);
-#ifndef STBI_NO_STDIO
-extern int stbi_tga_test_file (FILE *f);
-extern stbi_uc *stbi_tga_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp);
-#endif
-
-// is it a psd?
-extern int stbi_psd_test_memory (stbi_uc const *buffer, int len);
-
-extern stbi_uc *stbi_psd_load (char const *filename, int *x, int *y, int *comp, int req_comp);
-extern stbi_uc *stbi_psd_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp);
-#ifndef STBI_NO_STDIO
-extern int stbi_psd_test_file (FILE *f);
-extern stbi_uc *stbi_psd_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp);
-#endif
-
-// is it an hdr?
-extern int stbi_hdr_test_memory (stbi_uc const *buffer, int len);
-
-extern float * stbi_hdr_load (char const *filename, int *x, int *y, int *comp, int req_comp);
-extern float * stbi_hdr_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp);
-#ifndef STBI_NO_STDIO
-extern int stbi_hdr_test_file (FILE *f);
-extern float * stbi_hdr_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp);
-#endif
-
-// define new loaders
-typedef struct
-{
- int (*test_memory)(stbi_uc const *buffer, int len);
- stbi_uc * (*load_from_memory)(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp);
- #ifndef STBI_NO_STDIO
- int (*test_file)(FILE *f);
- stbi_uc * (*load_from_file)(FILE *f, int *x, int *y, int *comp, int req_comp);
- #endif
-} stbi_loader;
-
-// register a loader by filling out the above structure (you must defined ALL functions)
-// returns 1 if added or already added, 0 if not added (too many loaders)
-// NOT THREADSAFE
-extern int stbi_register_loader(stbi_loader *loader);
// define faster low-level operations (typically SIMD support)
-#if STBI_SIMD
-typedef void (*stbi_idct_8x8)(uint8 *out, int out_stride, short data[64], unsigned short *dequantize);
+#ifdef STBI_SIMD
+typedef void (*stbi_idct_8x8)(stbi_uc *out, int out_stride, short data[64], unsigned short *dequantize);
// compute an integer IDCT on "input"
// input[x] = data[x] * dequantize[x]
// write results to 'out': 64 samples, each run of 8 spaced by 'out_stride'
// CLAMP results to 0..255
-typedef void (*stbi_YCbCr_to_RGB_run)(uint8 *output, uint8 const *y, uint8 const *cb, uint8 const *cr, int count, int step);
+typedef void (*stbi_YCbCr_to_RGB_run)(stbi_uc *output, stbi_uc const *y, stbi_uc const *cb, stbi_uc const *cr, int count, int step);
// compute a conversion from YCbCr to RGB
// 'count' pixels
// write pixels to 'output'; each pixel is 'step' bytes (either 3 or 4; if 4, write '255' as 4th), order R,G,B
@@ -344,6 +321,7 @@ extern void stbi_install_idct(stbi_idct_8x8 func);
extern void stbi_install_YCbCr_to_RGB(stbi_YCbCr_to_RGB_run func);
#endif // STBI_SIMD
+
#ifdef __cplusplus
}
#endif
@@ -357,7 +335,7 @@ extern void stbi_install_YCbCr_to_RGB(stbi_YCbCr_to_RGB_run func);
#ifndef STBI_NO_HDR
#include // ldexp
-#include // strcmp
+#include // strcmp, strtok
#endif
#ifndef STBI_NO_STDIO
@@ -369,16 +347,18 @@ extern void stbi_install_YCbCr_to_RGB(stbi_YCbCr_to_RGB_run func);
#include
#ifndef _MSC_VER
- #ifdef __cplusplus
- #define __forceinline inline
- #else
- #define __forceinline
- #endif
+ #ifdef __cplusplus
+ #define stbi_inline inline
+ #else
+ #define stbi_inline
+ #endif
+#else
+ #define stbi_inline __forceinline
#endif
// implementation:
-typedef unsigned char uint8;
+typedef unsigned char uint8;
typedef unsigned short uint16;
typedef signed short int16;
typedef unsigned int uint32;
@@ -386,16 +366,131 @@ typedef signed int int32;
typedef unsigned int uint;
// should produce compiler error if size is wrong
-typedef unsigned char validate_uint32[sizeof(uint32)==4];
+typedef unsigned char validate_uint32[sizeof(uint32)==4 ? 1 : -1];
#if defined(STBI_NO_STDIO) && !defined(STBI_NO_WRITE)
#define STBI_NO_WRITE
#endif
-//////////////////////////////////////////////////////////////////////////////
-//
-// Generic API that works on all image types
+#define STBI_NOTUSED(v) (void)sizeof(v)
+
+#ifdef _MSC_VER
+#define STBI_HAS_LROTL
+#endif
+
+#ifdef STBI_HAS_LROTL
+ #define stbi_lrot(x,y) _lrotl(x,y)
+#else
+ #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (32 - (y))))
+#endif
+
+///////////////////////////////////////////////
//
+// stbi struct and start_xxx functions
+
+// stbi structure is our basic context used by all images, so it
+// contains all the IO context, plus some basic image information
+typedef struct
+{
+ uint32 img_x, img_y;
+ int img_n, img_out_n;
+
+ stbi_io_callbacks io;
+ void *io_user_data;
+
+ int read_from_callbacks;
+ int buflen;
+ uint8 buffer_start[128];
+
+ uint8 *img_buffer, *img_buffer_end;
+ uint8 *img_buffer_original;
+} stbi;
+
+
+static void refill_buffer(stbi *s);
+
+// initialize a memory-decode context
+static void start_mem(stbi *s, uint8 const *buffer, int len)
+{
+ s->io.read = NULL;
+ s->read_from_callbacks = 0;
+ s->img_buffer = s->img_buffer_original = (uint8 *) buffer;
+ s->img_buffer_end = (uint8 *) buffer+len;
+}
+
+// initialize a callback-based context
+static void start_callbacks(stbi *s, stbi_io_callbacks *c, void *user)
+{
+ s->io = *c;
+ s->io_user_data = user;
+ s->buflen = sizeof(s->buffer_start);
+ s->read_from_callbacks = 1;
+ s->img_buffer_original = s->buffer_start;
+ refill_buffer(s);
+}
+
+#ifndef STBI_NO_STDIO
+
+static int stdio_read(void *user, char *data, int size)
+{
+ return (int) fread(data,1,size,(FILE*) user);
+}
+
+static void stdio_skip(void *user, unsigned n)
+{
+ fseek((FILE*) user, n, SEEK_CUR);
+}
+
+static int stdio_eof(void *user)
+{
+ return feof((FILE*) user);
+}
+
+static stbi_io_callbacks stbi_stdio_callbacks =
+{
+ stdio_read,
+ stdio_skip,
+ stdio_eof,
+};
+
+static void start_file(stbi *s, FILE *f)
+{
+ start_callbacks(s, &stbi_stdio_callbacks, (void *) f);
+}
+
+//static void stop_file(stbi *s) { }
+
+#endif // !STBI_NO_STDIO
+
+static void stbi_rewind(stbi *s)
+{
+ // conceptually rewind SHOULD rewind to the beginning of the stream,
+ // but we just rewind to the beginning of the initial buffer, because
+ // we only use it after doing 'test', which only ever looks at at most 92 bytes
+ s->img_buffer = s->img_buffer_original;
+}
+
+static int stbi_jpeg_test(stbi *s);
+static stbi_uc *stbi_jpeg_load(stbi *s, int *x, int *y, int *comp, int req_comp);
+static int stbi_jpeg_info(stbi *s, int *x, int *y, int *comp);
+static int stbi_png_test(stbi *s);
+static stbi_uc *stbi_png_load(stbi *s, int *x, int *y, int *comp, int req_comp);
+static int stbi_png_info(stbi *s, int *x, int *y, int *comp);
+static int stbi_bmp_test(stbi *s);
+static stbi_uc *stbi_bmp_load(stbi *s, int *x, int *y, int *comp, int req_comp);
+static int stbi_tga_test(stbi *s);
+static stbi_uc *stbi_tga_load(stbi *s, int *x, int *y, int *comp, int req_comp);
+static int stbi_tga_info(stbi *s, int *x, int *y, int *comp);
+static int stbi_psd_test(stbi *s);
+static stbi_uc *stbi_psd_load(stbi *s, int *x, int *y, int *comp, int req_comp);
+static int stbi_hdr_test(stbi *s);
+static float *stbi_hdr_load(stbi *s, int *x, int *y, int *comp, int req_comp);
+static int stbi_pic_test(stbi *s);
+static stbi_uc *stbi_pic_load(stbi *s, int *x, int *y, int *comp, int req_comp);
+static int stbi_gif_test(stbi *s);
+static stbi_uc *stbi_gif_load(stbi *s, int *x, int *y, int *comp, int req_comp);
+static int stbi_gif_info(stbi *s, int *x, int *y, int *comp);
+
// this is not threadsafe
static const char *failure_reason;
@@ -411,6 +506,10 @@ static int e(const char *str)
return 0;
}
+// e - error
+// epf - error returning pointer to float
+// epuc - error returning pointer to unsigned char
+
#ifdef STBI_NO_FAILURE_STRINGS
#define e(x,y) 0
#elif defined(STBI_FAILURE_USERMSG)
@@ -427,33 +526,33 @@ void stbi_image_free(void *retval_from_stbi_load)
free(retval_from_stbi_load);
}
-#define MAX_LOADERS 32
-stbi_loader *loaders[MAX_LOADERS];
-static int max_loaders = 0;
-
-int stbi_register_loader(stbi_loader *loader)
-{
- int i;
- for (i=0; i < MAX_LOADERS; ++i) {
- // already present?
- if (loaders[i] == loader)
- return 1;
- // end of the list?
- if (loaders[i] == NULL) {
- loaders[i] = loader;
- max_loaders = i+1;
- return 1;
- }
- }
- // no room for it
- return 0;
-}
-
#ifndef STBI_NO_HDR
static float *ldr_to_hdr(stbi_uc *data, int x, int y, int comp);
static stbi_uc *hdr_to_ldr(float *data, int x, int y, int comp);
#endif
+static unsigned char *stbi_load_main(stbi *s, int *x, int *y, int *comp, int req_comp)
+{
+ if (stbi_jpeg_test(s)) return stbi_jpeg_load(s,x,y,comp,req_comp);
+ if (stbi_png_test(s)) return stbi_png_load(s,x,y,comp,req_comp);
+ if (stbi_bmp_test(s)) return stbi_bmp_load(s,x,y,comp,req_comp);
+ if (stbi_gif_test(s)) return stbi_gif_load(s,x,y,comp,req_comp);
+ if (stbi_psd_test(s)) return stbi_psd_load(s,x,y,comp,req_comp);
+ if (stbi_pic_test(s)) return stbi_pic_load(s,x,y,comp,req_comp);
+
+ #ifndef STBI_NO_HDR
+ if (stbi_hdr_test(s)) {
+ float *hdr = stbi_hdr_load(s, x,y,comp,req_comp);
+ return hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp);
+ }
+ #endif
+
+ // test tga last because it's a crappy test!
+ if (stbi_tga_test(s))
+ return stbi_tga_load(s,x,y,comp,req_comp);
+ return epuc("unknown image type", "Image not of any known type, or corrupt");
+}
+
#ifndef STBI_NO_STDIO
unsigned char *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp)
{
@@ -467,59 +566,55 @@ unsigned char *stbi_load(char const *filename, int *x, int *y, int *comp, int re
unsigned char *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp)
{
- int i;
- if (stbi_jpeg_test_file(f))
- return stbi_jpeg_load_from_file(f,x,y,comp,req_comp);
- if (stbi_png_test_file(f))
- return stbi_png_load_from_file(f,x,y,comp,req_comp);
- if (stbi_bmp_test_file(f))
- return stbi_bmp_load_from_file(f,x,y,comp,req_comp);
- if (stbi_psd_test_file(f))
- return stbi_psd_load_from_file(f,x,y,comp,req_comp);
- #ifndef STBI_NO_HDR
- if (stbi_hdr_test_file(f)) {
- float *hdr = stbi_hdr_load_from_file(f, x,y,comp,req_comp);
- return hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp);
- }
- #endif
- for (i=0; i < max_loaders; ++i)
- if (loaders[i]->test_file(f))
- return loaders[i]->load_from_file(f,x,y,comp,req_comp);
- // test tga last because it's a crappy test!
- if (stbi_tga_test_file(f))
- return stbi_tga_load_from_file(f,x,y,comp,req_comp);
- return epuc("unknown image type", "Image not of any known type, or corrupt");
+ stbi s;
+ start_file(&s,f);
+ return stbi_load_main(&s,x,y,comp,req_comp);
}
-#endif
+#endif //!STBI_NO_STDIO
unsigned char *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp)
{
- int i;
- if (stbi_jpeg_test_memory(buffer,len))
- return stbi_jpeg_load_from_memory(buffer,len,x,y,comp,req_comp);
- if (stbi_png_test_memory(buffer,len))
- return stbi_png_load_from_memory(buffer,len,x,y,comp,req_comp);
- if (stbi_bmp_test_memory(buffer,len))
- return stbi_bmp_load_from_memory(buffer,len,x,y,comp,req_comp);
- if (stbi_psd_test_memory(buffer,len))
- return stbi_psd_load_from_memory(buffer,len,x,y,comp,req_comp);
- #ifndef STBI_NO_HDR
- if (stbi_hdr_test_memory(buffer, len)) {
- float *hdr = stbi_hdr_load_from_memory(buffer, len,x,y,comp,req_comp);
- return hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp);
- }
- #endif
- for (i=0; i < max_loaders; ++i)
- if (loaders[i]->test_memory(buffer,len))
- return loaders[i]->load_from_memory(buffer,len,x,y,comp,req_comp);
- // test tga last because it's a crappy test!
- if (stbi_tga_test_memory(buffer,len))
- return stbi_tga_load_from_memory(buffer,len,x,y,comp,req_comp);
- return epuc("unknown image type", "Image not of any known type, or corrupt");
+ stbi s;
+ start_mem(&s,buffer,len);
+ return stbi_load_main(&s,x,y,comp,req_comp);
+}
+
+unsigned char *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp)
+{
+ stbi s;
+ start_callbacks(&s, (stbi_io_callbacks *) clbk, user);
+ return stbi_load_main(&s,x,y,comp,req_comp);
}
#ifndef STBI_NO_HDR
+float *stbi_loadf_main(stbi *s, int *x, int *y, int *comp, int req_comp)
+{
+ unsigned char *data;
+ #ifndef STBI_NO_HDR
+ if (stbi_hdr_test(s))
+ return stbi_hdr_load(s,x,y,comp,req_comp);
+ #endif
+ data = stbi_load_main(s, x, y, comp, req_comp);
+ if (data)
+ return ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp);
+ return epf("unknown image type", "Image not of any known type, or corrupt");
+}
+
+float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp)
+{
+ stbi s;
+ start_mem(&s,buffer,len);
+ return stbi_loadf_main(&s,x,y,comp,req_comp);
+}
+
+float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp)
+{
+ stbi s;
+ start_callbacks(&s, (stbi_io_callbacks *) clbk, user);
+ return stbi_loadf_main(&s,x,y,comp,req_comp);
+}
+
#ifndef STBI_NO_STDIO
float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp)
{
@@ -533,31 +628,13 @@ float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp)
float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp)
{
- unsigned char *data;
- #ifndef STBI_NO_HDR
- if (stbi_hdr_test_file(f))
- return stbi_hdr_load_from_file(f,x,y,comp,req_comp);
- #endif
- data = stbi_load_from_file(f, x, y, comp, req_comp);
- if (data)
- return ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp);
- return epf("unknown image type", "Image not of any known type, or corrupt");
+ stbi s;
+ start_file(&s,f);
+ return stbi_loadf_main(&s,x,y,comp,req_comp);
}
-#endif
+#endif // !STBI_NO_STDIO
-float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp)
-{
- stbi_uc *data;
- #ifndef STBI_NO_HDR
- if (stbi_hdr_test_memory(buffer, len))
- return stbi_hdr_load_from_memory(buffer, len,x,y,comp,req_comp);
- #endif
- data = stbi_load_from_memory(buffer, len, x, y, comp, req_comp);
- if (data)
- return ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp);
- return epf("unknown image type", "Image not of any known type, or corrupt");
-}
-#endif
+#endif // !STBI_NO_HDR
// these is-hdr-or-not is defined independent of whether STBI_NO_HDR is
// defined, for API simplicity; if STBI_NO_HDR is defined, it always
@@ -566,8 +643,12 @@ float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, in
int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len)
{
#ifndef STBI_NO_HDR
- return stbi_hdr_test_memory(buffer, len);
+ stbi s;
+ start_mem(&s,buffer,len);
+ return stbi_hdr_test(&s);
#else
+ STBI_NOTUSED(buffer);
+ STBI_NOTUSED(len);
return 0;
#endif
}
@@ -587,20 +668,25 @@ extern int stbi_is_hdr (char const *filename)
extern int stbi_is_hdr_from_file(FILE *f)
{
#ifndef STBI_NO_HDR
- return stbi_hdr_test_file(f);
+ stbi s;
+ start_file(&s,f);
+ return stbi_hdr_test(&s);
#else
return 0;
#endif
}
+#endif // !STBI_NO_STDIO
-#endif
-
-// @TODO: get image dimensions & components without fully decoding
-#ifndef STBI_NO_STDIO
-extern int stbi_info (char const *filename, int *x, int *y, int *comp);
-extern int stbi_info_from_file (FILE *f, int *x, int *y, int *comp);
-#endif
-extern int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp);
+extern int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user)
+{
+ #ifndef STBI_NO_HDR
+ stbi s;
+ start_callbacks(&s, (stbi_io_callbacks *) clbk, user);
+ return stbi_hdr_test(&s);
+ #else
+ return 0;
+ #endif
+}
#ifndef STBI_NO_HDR
static float h2l_gamma_i=1.0f/2.2f, h2l_scale_i=1.0f;
@@ -623,73 +709,86 @@ enum
{
SCAN_load=0,
SCAN_type,
- SCAN_header,
+ SCAN_header
};
-typedef struct
+static void refill_buffer(stbi *s)
{
- uint32 img_x, img_y;
- int img_n, img_out_n;
-
- #ifndef STBI_NO_STDIO
- FILE *img_file;
- #endif
- uint8 *img_buffer, *img_buffer_end;
-} stbi;
-
-#ifndef STBI_NO_STDIO
-static void start_file(stbi *s, FILE *f)
-{
- s->img_file = f;
- s->img_buffer = NULL;
- s->img_buffer_end = NULL;
-}
-#endif
-
-static void start_mem(stbi *s, uint8 const *buffer, int len)
-{
-#ifndef STBI_NO_STDIO
- s->img_file = NULL;
-#endif
- s->img_buffer = (uint8 *) buffer;
- s->img_buffer_end = (uint8 *) buffer+len;
-}
-
-__forceinline static int get8(stbi *s)
-{
-#ifndef STBI_NO_STDIO
- if (s->img_file) {
- int c = fgetc(s->img_file);
- return c == EOF ? 0 : c;
+ int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen);
+ if (n == 0) {
+ // at end of file, treat same as if from memory
+ s->read_from_callbacks = 0;
+ s->img_buffer = s->img_buffer_end-1;
+ *s->img_buffer = 0;
+ } else {
+ s->img_buffer = s->buffer_start;
+ s->img_buffer_end = s->buffer_start + n;
}
-#endif
+}
+
+stbi_inline static int get8(stbi *s)
+{
if (s->img_buffer < s->img_buffer_end)
return *s->img_buffer++;
+ if (s->read_from_callbacks) {
+ refill_buffer(s);
+ return *s->img_buffer++;
+ }
return 0;
}
-__forceinline static int at_eof(stbi *s)
+stbi_inline static int at_eof(stbi *s)
{
-#ifndef STBI_NO_STDIO
- if (s->img_file)
- return feof(s->img_file);
-#endif
+ if (s->io.read) {
+ if (!(s->io.eof)(s->io_user_data)) return 0;
+ // if feof() is true, check if buffer = end
+ // special case: we've only got the special 0 character at the end
+ if (s->read_from_callbacks == 0) return 1;
+ }
+
return s->img_buffer >= s->img_buffer_end;
}
-__forceinline static uint8 get8u(stbi *s)
+stbi_inline static uint8 get8u(stbi *s)
{
return (uint8) get8(s);
}
static void skip(stbi *s, int n)
{
-#ifndef STBI_NO_STDIO
- if (s->img_file)
- fseek(s->img_file, n, SEEK_CUR);
- else
-#endif
+ if (s->io.read) {
+ int blen = s->img_buffer_end - s->img_buffer;
+ if (blen < n) {
+ s->img_buffer = s->img_buffer_end;
+ (s->io.skip)(s->io_user_data, n - blen);
+ return;
+ }
+ }
+ s->img_buffer += n;
+}
+
+static int getn(stbi *s, stbi_uc *buffer, int n)
+{
+ if (s->io.read) {
+ int blen = s->img_buffer_end - s->img_buffer;
+ if (blen < n) {
+ int res, count;
+
+ memcpy(buffer, s->img_buffer, blen);
+
+ count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen);
+ res = (count == (n-blen));
+ s->img_buffer = s->img_buffer_end;
+ return res;
+ }
+ }
+
+ if (s->img_buffer+n <= s->img_buffer_end) {
+ memcpy(buffer, s->img_buffer, n);
s->img_buffer += n;
+ return 1;
+ } else
+ return 0;
}
static int get16(stbi *s)
@@ -716,18 +815,6 @@ static uint32 get32le(stbi *s)
return z + (get16le(s) << 16);
}
-static void getn(stbi *s, stbi_uc *buffer, int n)
-{
-#ifndef STBI_NO_STDIO
- if (s->img_file) {
- fread(buffer, 1, n, s->img_file);
- return;
- }
-#endif
- memcpy(buffer, s->img_buffer, n);
- s->img_buffer += n;
-}
-
//////////////////////////////////////////////////////////////////////////////
//
// generic converter from built-in img_n to req_comp
@@ -766,7 +853,7 @@ static unsigned char *convert_format(unsigned char *data, int img_n, int req_com
#define CASE(a,b) case COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b)
// convert source image with img_n components to one with req_comp components;
// avoid switch per pixel, so use switch per scanline and massive macros
- switch(COMBO(img_n, req_comp)) {
+ switch (COMBO(img_n, req_comp)) {
CASE(1,2) dest[0]=src[0], dest[1]=255; break;
CASE(1,3) dest[0]=dest[1]=dest[2]=src[0]; break;
CASE(1,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; break;
@@ -819,13 +906,13 @@ static stbi_uc *hdr_to_ldr(float *data, int x, int y, int comp)
float z = (float) pow(data[i*comp+k]*h2l_scale_i, h2l_gamma_i) * 255 + 0.5f;
if (z < 0) z = 0;
if (z > 255) z = 255;
- output[i*comp + k] = (stbi_uc)float2int(z);
+ output[i*comp + k] = (uint8) float2int(z);
}
if (k < comp) {
float z = data[i*comp+k] * 255 + 0.5f;
if (z < 0) z = 0;
if (z > 255) z = 255;
- output[i*comp + k] = (stbi_uc)float2int(z);
+ output[i*comp + k] = (uint8) float2int(z);
}
}
free(data);
@@ -876,10 +963,10 @@ typedef struct
typedef struct
{
- #if STBI_SIMD
+ #ifdef STBI_SIMD
unsigned short dequant2[4][64];
#endif
- stbi s;
+ stbi *s;
huffman huff_dc[4];
huffman huff_ac[4];
uint8 dequant[4][64];
@@ -957,16 +1044,16 @@ static int build_huffman(huffman *h, int *count)
static void grow_buffer_unsafe(jpeg *j)
{
do {
- int b = j->nomore ? 0 : get8(&j->s);
+ int b = j->nomore ? 0 : get8(j->s);
if (b == 0xff) {
- int c = get8(&j->s);
+ int c = get8(j->s);
if (c != 0) {
j->marker = (unsigned char) c;
j->nomore = 1;
return;
}
}
- j->code_buffer = (j->code_buffer << 8) | b;
+ j->code_buffer |= b << (24 - j->code_bits);
j->code_bits += 8;
} while (j->code_bits <= 24);
}
@@ -975,7 +1062,7 @@ static void grow_buffer_unsafe(jpeg *j)
static uint32 bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535};
// decode a jpeg huffman value from the bitstream
-__forceinline static int decode(jpeg *j, huffman *h)
+stbi_inline static int decode(jpeg *j, huffman *h)
{
unsigned int temp;
int c,k;
@@ -984,12 +1071,14 @@ __forceinline static int decode(jpeg *j, huffman *h)
// look at the top FAST_BITS and determine what symbol ID it is,
// if the code is <= FAST_BITS
- c = (j->code_buffer >> (j->code_bits - FAST_BITS)) & ((1 << FAST_BITS)-1);
+ c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1);
k = h->fast[c];
if (k < 255) {
- if (h->size[k] > j->code_bits)
+ int s = h->size[k];
+ if (s > j->code_bits)
return -1;
- j->code_bits -= h->size[k];
+ j->code_buffer <<= s;
+ j->code_bits -= s;
return h->values[k];
}
@@ -999,10 +1088,7 @@ __forceinline static int decode(jpeg *j, huffman *h)
// end; in other words, regardless of the number of bits, it
// wants to be compared against something shifted to have 16;
// that way we don't need to shift inside the loop.
- if (j->code_bits < 16)
- temp = (j->code_buffer << (16 - j->code_bits)) & 0xffff;
- else
- temp = (j->code_buffer >> (j->code_bits - 16)) & 0xffff;
+ temp = j->code_buffer >> 16;
for (k=FAST_BITS+1 ; ; ++k)
if (temp < h->maxcode[k])
break;
@@ -1016,23 +1102,33 @@ __forceinline static int decode(jpeg *j, huffman *h)
return -1;
// convert the huffman code to the symbol id
- c = ((j->code_buffer >> (j->code_bits - k)) & bmask[k]) + h->delta[k];
- assert((((j->code_buffer) >> (j->code_bits - h->size[c])) & bmask[h->size[c]]) == h->code[c]);
+ c = ((j->code_buffer >> (32 - k)) & bmask[k]) + h->delta[k];
+ assert((((j->code_buffer) >> (32 - h->size[c])) & bmask[h->size[c]]) == h->code[c]);
// convert the id to a symbol
j->code_bits -= k;
+ j->code_buffer <<= k;
return h->values[c];
}
// combined JPEG 'receive' and JPEG 'extend', since baseline
// always extends everything it receives.
-__forceinline static int extend_receive(jpeg *j, int n)
+stbi_inline static int extend_receive(jpeg *j, int n)
{
unsigned int m = 1 << (n-1);
unsigned int k;
if (j->code_bits < n) grow_buffer_unsafe(j);
- k = (j->code_buffer >> (j->code_bits - n)) & bmask[n];
+
+ #if 1
+ k = stbi_lrot(j->code_buffer, n);
+ j->code_buffer = k & ~bmask[n];
+ k &= bmask[n];
j->code_bits -= n;
+ #else
+ k = (j->code_buffer >> (32 - n)) & bmask[n];
+ j->code_bits -= n;
+ j->code_buffer <<= n;
+ #endif
// the following test is probably a random branch that won't
// predict well. I tried to table accelerate it but failed.
// maybe it's compiling as a conditional move?
@@ -1095,9 +1191,8 @@ static int decode_block(jpeg *j, short data[64], huffman *hdc, huffman *hac, int
}
// take a -128..127 value and clamp it and convert to 0..255
-__forceinline static uint8 clamp(int x)
+stbi_inline static uint8 clamp(int x)
{
- x += 128;
// trick to use a single test to catch both cases
if ((unsigned int) x > 255) {
if (x < 0) return 0;
@@ -1147,65 +1242,18 @@ __forceinline static uint8 clamp(int x)
t1 += p2+p4; \
t0 += p1+p3;
-#if !STBI_SIMD
-// .344 seconds on 3*anemones.jpg
-static void idct_block(uint8 *out, int out_stride, short data[64], uint8 *dequantize)
-{
- int i,val[64],*v=val;
- uint8 *o,*dq = dequantize;
- short *d = data;
-
- // columns
- for (i=0; i < 8; ++i,++d,++dq, ++v) {
- // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing
- if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0
- && d[40]==0 && d[48]==0 && d[56]==0) {
- // no shortcut 0 seconds
- // (1|2|3|4|5|6|7)==0 0 seconds
- // all separate -0.047 seconds
- // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds
- int dcterm = d[0] * dq[0] << 2;
- v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm;
- } else {
- IDCT_1D(d[ 0]*dq[ 0],d[ 8]*dq[ 8],d[16]*dq[16],d[24]*dq[24],
- d[32]*dq[32],d[40]*dq[40],d[48]*dq[48],d[56]*dq[56])
- // constants scaled things up by 1<<12; let's bring them back
- // down, but keep 2 extra bits of precision
- x0 += 512; x1 += 512; x2 += 512; x3 += 512;
- v[ 0] = (x0+t3) >> 10;
- v[56] = (x0-t3) >> 10;
- v[ 8] = (x1+t2) >> 10;
- v[48] = (x1-t2) >> 10;
- v[16] = (x2+t1) >> 10;
- v[40] = (x2-t1) >> 10;
- v[24] = (x3+t0) >> 10;
- v[32] = (x3-t0) >> 10;
- }
- }
-
- for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) {
- // no fast case since the first 1D IDCT spread components out
- IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7])
- // constants scaled things up by 1<<12, plus we had 1<<2 from first
- // loop, plus horizontal and vertical each scale by sqrt(8) so together
- // we've got an extra 1<<3, so 1<<17 total we need to remove.
- x0 += 65536; x1 += 65536; x2 += 65536; x3 += 65536;
- o[0] = clamp((x0+t3) >> 17);
- o[7] = clamp((x0-t3) >> 17);
- o[1] = clamp((x1+t2) >> 17);
- o[6] = clamp((x1-t2) >> 17);
- o[2] = clamp((x2+t1) >> 17);
- o[5] = clamp((x2-t1) >> 17);
- o[3] = clamp((x3+t0) >> 17);
- o[4] = clamp((x3-t0) >> 17);
- }
-}
+#ifdef STBI_SIMD
+typedef unsigned short stbi_dequantize_t;
#else
-static void idct_block(uint8 *out, int out_stride, short data[64], unsigned short *dequantize)
+typedef uint8 stbi_dequantize_t;
+#endif
+
+// .344 seconds on 3*anemones.jpg
+static void idct_block(uint8 *out, int out_stride, short data[64], stbi_dequantize_t *dequantize)
{
int i,val[64],*v=val;
+ stbi_dequantize_t *dq = dequantize;
uint8 *o;
- unsigned short *dq = dequantize;
short *d = data;
// columns
@@ -1242,7 +1290,15 @@ static void idct_block(uint8 *out, int out_stride, short data[64], unsigned shor
// constants scaled things up by 1<<12, plus we had 1<<2 from first
// loop, plus horizontal and vertical each scale by sqrt(8) so together
// we've got an extra 1<<3, so 1<<17 total we need to remove.
- x0 += 65536; x1 += 65536; x2 += 65536; x3 += 65536;
+ // so we want to round that, which means adding 0.5 * 1<<17,
+ // aka 65536. Also, we'll end up with -128 to 127 that we want
+ // to encode as 0..255 by adding 128, so we'll add that before the shift
+ x0 += 65536 + (128<<17);
+ x1 += 65536 + (128<<17);
+ x2 += 65536 + (128<<17);
+ x3 += 65536 + (128<<17);
+ // tried computing the shifts into temps, or'ing the temps to see
+ // if any were out of range, but that was slower
o[0] = clamp((x0+t3) >> 17);
o[7] = clamp((x0-t3) >> 17);
o[1] = clamp((x1+t2) >> 17);
@@ -1253,9 +1309,11 @@ static void idct_block(uint8 *out, int out_stride, short data[64], unsigned shor
o[4] = clamp((x3-t0) >> 17);
}
}
+
+#ifdef STBI_SIMD
static stbi_idct_8x8 stbi_idct_installed = idct_block;
-extern void stbi_install_idct(stbi_idct_8x8 func)
+void stbi_install_idct(stbi_idct_8x8 func)
{
stbi_idct_installed = func;
}
@@ -1269,10 +1327,10 @@ static uint8 get_marker(jpeg *j)
{
uint8 x;
if (j->marker != MARKER_none) { x = j->marker; j->marker = MARKER_none; return x; }
- x = get8u(&j->s);
+ x = get8u(j->s);
if (x != 0xff) return MARKER_none;
while (x == 0xff)
- x = get8u(&j->s);
+ x = get8u(j->s);
return x;
}
@@ -1299,7 +1357,7 @@ static int parse_entropy_coded_data(jpeg *z)
reset(z);
if (z->scan_n == 1) {
int i,j;
- #if STBI_SIMD
+ #ifdef STBI_SIMD
__declspec(align(16))
#endif
short data[64];
@@ -1313,7 +1371,7 @@ static int parse_entropy_coded_data(jpeg *z)
for (j=0; j < h; ++j) {
for (i=0; i < w; ++i) {
if (!decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+z->img_comp[n].ha, n)) return 0;
- #if STBI_SIMD
+ #ifdef STBI_SIMD
stbi_idct_installed(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data, z->dequant2[z->img_comp[n].tq]);
#else
idct_block(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data, z->dequant[z->img_comp[n].tq]);
@@ -1343,7 +1401,7 @@ static int parse_entropy_coded_data(jpeg *z)
int x2 = (i*z->img_comp[n].h + x)*8;
int y2 = (j*z->img_comp[n].v + y)*8;
if (!decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+z->img_comp[n].ha, n)) return 0;
- #if STBI_SIMD
+ #ifdef STBI_SIMD
stbi_idct_installed(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data, z->dequant2[z->img_comp[n].tq]);
#else
idct_block(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data, z->dequant[z->img_comp[n].tq]);
@@ -1377,21 +1435,21 @@ static int process_marker(jpeg *z, int m)
return e("progressive jpeg","JPEG format not supported (progressive)");
case 0xDD: // DRI - specify restart interval
- if (get16(&z->s) != 4) return e("bad DRI len","Corrupt JPEG");
- z->restart_interval = get16(&z->s);
+ if (get16(z->s) != 4) return e("bad DRI len","Corrupt JPEG");
+ z->restart_interval = get16(z->s);
return 1;
case 0xDB: // DQT - define quantization table
- L = get16(&z->s)-2;
+ L = get16(z->s)-2;
while (L > 0) {
- int q = get8(&z->s);
+ int q = get8(z->s);
int p = q >> 4;
int t = q & 15,i;
if (p != 0) return e("bad DQT type","Corrupt JPEG");
if (t > 3) return e("bad DQT table","Corrupt JPEG");
for (i=0; i < 64; ++i)
- z->dequant[t][dezigzag[i]] = get8u(&z->s);
- #if STBI_SIMD
+ z->dequant[t][dezigzag[i]] = get8u(z->s);
+ #ifdef STBI_SIMD
for (i=0; i < 64; ++i)
z->dequant2[t][i] = z->dequant[t][i];
#endif
@@ -1400,16 +1458,16 @@ static int process_marker(jpeg *z, int m)
return L==0;
case 0xC4: // DHT - define huffman table
- L = get16(&z->s)-2;
+ L = get16(z->s)-2;
while (L > 0) {
uint8 *v;
int sizes[16],i,m=0;
- int q = get8(&z->s);
+ int q = get8(z->s);
int tc = q >> 4;
int th = q & 15;
if (tc > 1 || th > 3) return e("bad DHT header","Corrupt JPEG");
for (i=0; i < 16; ++i) {
- sizes[i] = get8(&z->s);
+ sizes[i] = get8(z->s);
m += sizes[i];
}
L -= 17;
@@ -1421,14 +1479,14 @@ static int process_marker(jpeg *z, int m)
v = z->huff_ac[th].values;
}
for (i=0; i < m; ++i)
- v[i] = get8u(&z->s);
+ v[i] = get8u(z->s);
L -= m;
}
return L==0;
}
// check for comment block or APP blocks
if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) {
- skip(&z->s, get16(&z->s)-2);
+ skip(z->s, get16(z->s)-2);
return 1;
}
return 0;
@@ -1438,31 +1496,31 @@ static int process_marker(jpeg *z, int m)
static int process_scan_header(jpeg *z)
{
int i;
- int Ls = get16(&z->s);
- z->scan_n = get8(&z->s);
- if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s.img_n) return e("bad SOS component count","Corrupt JPEG");
+ int Ls = get16(z->s);
+ z->scan_n = get8(z->s);
+ if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return e("bad SOS component count","Corrupt JPEG");
if (Ls != 6+2*z->scan_n) return e("bad SOS len","Corrupt JPEG");
for (i=0; i < z->scan_n; ++i) {
- int id = get8(&z->s), which;
- int q = get8(&z->s);
- for (which = 0; which < z->s.img_n; ++which)
+ int id = get8(z->s), which;
+ int q = get8(z->s);
+ for (which = 0; which < z->s->img_n; ++which)
if (z->img_comp[which].id == id)
break;
- if (which == z->s.img_n) return 0;
+ if (which == z->s->img_n) return 0;
z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return e("bad DC huff","Corrupt JPEG");
z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return e("bad AC huff","Corrupt JPEG");
z->order[i] = which;
}
- if (get8(&z->s) != 0) return e("bad SOS","Corrupt JPEG");
- get8(&z->s); // should be 63, but might be 0
- if (get8(&z->s) != 0) return e("bad SOS","Corrupt JPEG");
+ if (get8(z->s) != 0) return e("bad SOS","Corrupt JPEG");
+ get8(z->s); // should be 63, but might be 0
+ if (get8(z->s) != 0) return e("bad SOS","Corrupt JPEG");
return 1;
}
static int process_frame_header(jpeg *z, int scan)
{
- stbi *s = &z->s;
+ stbi *s = z->s;
int Lf,p,i,q, h_max=1,v_max=1,c;
Lf = get16(s); if (Lf < 11) return e("bad SOF len","Corrupt JPEG"); // JPEG
p = get8(s); if (p != 8) return e("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline
@@ -1552,7 +1610,7 @@ static int decode_jpeg_header(jpeg *z, int scan)
m = get_marker(z);
while (m == MARKER_none) {
// some files have extra padding after their blocks, so ok, we'll scan
- if (at_eof(&z->s)) return e("no SOF", "Corrupt JPEG");
+ if (at_eof(z->s)) return e("no SOF", "Corrupt JPEG");
m = get_marker(z);
}
}
@@ -1570,6 +1628,19 @@ static int decode_jpeg_image(jpeg *j)
if (SOS(m)) {
if (!process_scan_header(j)) return 0;
if (!parse_entropy_coded_data(j)) return 0;
+ if (j->marker == MARKER_none ) {
+ // handle 0s at the end of image data from IP Kamera 9060
+ while (!at_eof(j->s)) {
+ int x = get8(j->s);
+ if (x == 255) {
+ j->marker = get8u(j->s);
+ break;
+ } else if (x != 0) {
+ return 0;
+ }
+ }
+ // if we reach eof without hitting a marker, get_marker() below will fail and we'll eventually return 0
+ }
} else {
if (!process_marker(j, m)) return 0;
}
@@ -1585,25 +1656,31 @@ typedef uint8 *(*resample_row_func)(uint8 *out, uint8 *in0, uint8 *in1,
#define div4(x) ((uint8) ((x) >> 2))
-static uint8 *resample_row_1(uint8 * /*out*/, uint8 *in_near, uint8 * /*in_far*/, int /*w*/, int /*hs*/)
+static uint8 *resample_row_1(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs)
{
+ STBI_NOTUSED(out);
+ STBI_NOTUSED(in_far);
+ STBI_NOTUSED(w);
+ STBI_NOTUSED(hs);
return in_near;
}
-static uint8* resample_row_v_2(uint8 *out, uint8 *in_near, uint8 * in_far, int w, int /*hs*/)
+static uint8* resample_row_v_2(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs)
{
// need to generate two samples vertically for every one in input
int i;
+ STBI_NOTUSED(hs);
for (i=0; i < w; ++i)
out[i] = div4(3*in_near[i] + in_far[i] + 2);
return out;
}
-static uint8* resample_row_h_2(uint8 *out, uint8 *in_near, uint8 * /*in_far*/, int w, int /*hs*/)
+static uint8* resample_row_h_2(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs)
{
// need to generate two samples horizontally for every one in input
int i;
uint8 *input = in_near;
+
if (w == 1) {
// if only one sample, can't do any interpolation
out[0] = out[1] = input[0];
@@ -1619,12 +1696,16 @@ static uint8* resample_row_h_2(uint8 *out, uint8 *in_near, uint8 * /*in_far*/,
}
out[i*2+0] = div4(input[w-2]*3 + input[w-1] + 2);
out[i*2+1] = input[w-1];
+
+ STBI_NOTUSED(in_far);
+ STBI_NOTUSED(hs);
+
return out;
}
#define div16(x) ((uint8) ((x) >> 4))
-static uint8 *resample_row_hv_2(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int /*hs*/)
+static uint8 *resample_row_hv_2(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs)
{
// need to generate 2x2 samples for every one in input
int i,t0,t1;
@@ -1642,13 +1723,17 @@ static uint8 *resample_row_hv_2(uint8 *out, uint8 *in_near, uint8 *in_far, int w
out[i*2 ] = div16(3*t1 + t0 + 8);
}
out[w*2-1] = div4(t1+2);
+
+ STBI_NOTUSED(hs);
+
return out;
}
-static uint8 *resample_row_generic(uint8 *out, uint8 *in_near, uint8 * /*in_far*/, int w, int hs)
+static uint8 *resample_row_generic(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs)
{
// resample with nearest-neighbor
int i,j;
+ in_far = in_far;
for (i=0; i < w; ++i)
for (j=0; j < hs; ++j)
out[i*hs+j] = in_near[i];
@@ -1684,7 +1769,7 @@ static void YCbCr_to_RGB_row(uint8 *out, const uint8 *y, const uint8 *pcb, const
}
}
-#if STBI_SIMD
+#ifdef STBI_SIMD
static stbi_YCbCr_to_RGB_run stbi_YCbCr_installed = YCbCr_to_RGB_row;
void stbi_install_YCbCr_to_RGB(stbi_YCbCr_to_RGB_run func)
@@ -1698,7 +1783,7 @@ void stbi_install_YCbCr_to_RGB(stbi_YCbCr_to_RGB_run func)
static void cleanup_jpeg(jpeg *j)
{
int i;
- for (i=0; i < j->s.img_n; ++i) {
+ for (i=0; i < j->s->img_n; ++i) {
if (j->img_comp[i].data) {
free(j->img_comp[i].raw_data);
j->img_comp[i].data = NULL;
@@ -1725,18 +1810,18 @@ static uint8 *load_jpeg_image(jpeg *z, int *out_x, int *out_y, int *comp, int re
int n, decode_n;
// validate req_comp
if (req_comp < 0 || req_comp > 4) return epuc("bad req_comp", "Internal error");
- z->s.img_n = 0;
+ z->s->img_n = 0;
// load a jpeg image from whichever source
if (!decode_jpeg_image(z)) { cleanup_jpeg(z); return NULL; }
// determine actual number of components to generate
- n = req_comp ? req_comp : z->s.img_n;
+ n = req_comp ? req_comp : z->s->img_n;
- if (z->s.img_n == 3 && n < 3)
+ if (z->s->img_n == 3 && n < 3)
decode_n = 1;
else
- decode_n = z->s.img_n;
+ decode_n = z->s->img_n;
// resample and color-convert
{
@@ -1752,13 +1837,13 @@ static uint8 *load_jpeg_image(jpeg *z, int *out_x, int *out_y, int *comp, int re
// allocate line buffer big enough for upsampling off the edges
// with upsample factor of 4
- z->img_comp[k].linebuf = (uint8 *) malloc(z->s.img_x + 3);
+ z->img_comp[k].linebuf = (uint8 *) malloc(z->s->img_x + 3);
if (!z->img_comp[k].linebuf) { cleanup_jpeg(z); return epuc("outofmem", "Out of memory"); }
r->hs = z->img_h_max / z->img_comp[k].h;
r->vs = z->img_v_max / z->img_comp[k].v;
r->ystep = r->vs >> 1;
- r->w_lores = (z->s.img_x + r->hs-1) / r->hs;
+ r->w_lores = (z->s->img_x + r->hs-1) / r->hs;
r->ypos = 0;
r->line0 = r->line1 = z->img_comp[k].data;
@@ -1770,12 +1855,12 @@ static uint8 *load_jpeg_image(jpeg *z, int *out_x, int *out_y, int *comp, int re
}
// can't error after this so, this is safe
- output = (uint8 *) malloc(n * z->s.img_x * z->s.img_y + 1);
+ output = (uint8 *) malloc(n * z->s->img_x * z->s->img_y + 1);
if (!output) { cleanup_jpeg(z); return epuc("outofmem", "Out of memory"); }
// now go ahead and resample
- for (j=0; j < z->s.img_y; ++j) {
- uint8 *out = output + n * z->s.img_x * j;
+ for (j=0; j < z->s->img_y; ++j) {
+ uint8 *out = output + n * z->s->img_x * j;
for (k=0; k < decode_n; ++k) {
stbi_resample *r = &res_comp[k];
int y_bot = r->ystep >= (r->vs >> 1);
@@ -1792,14 +1877,14 @@ static uint8 *load_jpeg_image(jpeg *z, int *out_x, int *out_y, int *comp, int re
}
if (n >= 3) {
uint8 *y = coutput[0];
- if (z->s.img_n == 3) {
- #if STBI_SIMD
+ if (z->s->img_n == 3) {
+ #ifdef STBI_SIMD
stbi_YCbCr_installed(out, y, coutput[1], coutput[2], z->s.img_x, n);
#else
- YCbCr_to_RGB_row(out, y, coutput[1], coutput[2], z->s.img_x, n);
+ YCbCr_to_RGB_row(out, y, coutput[1], coutput[2], z->s->img_x, n);
#endif
} else
- for (i=0; i < z->s.img_x; ++i) {
+ for (i=0; i < z->s->img_x; ++i) {
out[0] = out[1] = out[2] = y[i];
out[3] = 255; // not used if n==3
out += n;
@@ -1807,71 +1892,54 @@ static uint8 *load_jpeg_image(jpeg *z, int *out_x, int *out_y, int *comp, int re
} else {
uint8 *y = coutput[0];
if (n == 1)
- for (i=0; i < z->s.img_x; ++i) out[i] = y[i];
+ for (i=0; i < z->s->img_x; ++i) out[i] = y[i];
else
- for (i=0; i < z->s.img_x; ++i) *out++ = y[i], *out++ = 255;
+ for (i=0; i < z->s->img_x; ++i) *out++ = y[i], *out++ = 255;
}
}
cleanup_jpeg(z);
- *out_x = z->s.img_x;
- *out_y = z->s.img_y;
- if (comp) *comp = z->s.img_n; // report original components, not output
+ *out_x = z->s->img_x;
+ *out_y = z->s->img_y;
+ if (comp) *comp = z->s->img_n; // report original components, not output
return output;
}
}
-#ifndef STBI_NO_STDIO
-unsigned char *stbi_jpeg_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp)
+static unsigned char *stbi_jpeg_load(stbi *s, int *x, int *y, int *comp, int req_comp)
{
jpeg j;
- start_file(&j.s, f);
+ j.s = s;
return load_jpeg_image(&j, x,y,comp,req_comp);
}
-unsigned char *stbi_jpeg_load(char const *filename, int *x, int *y, int *comp, int req_comp)
-{
- unsigned char *data;
- FILE *f = fopen(filename, "rb");
- if (!f) return NULL;
- data = stbi_jpeg_load_from_file(f,x,y,comp,req_comp);
- fclose(f);
- return data;
-}
-#endif
-
-unsigned char *stbi_jpeg_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp)
+static int stbi_jpeg_test(stbi *s)
{
+ int r;
jpeg j;
- start_mem(&j.s, buffer,len);
- return load_jpeg_image(&j, x,y,comp,req_comp);
-}
-
-#ifndef STBI_NO_STDIO
-int stbi_jpeg_test_file(FILE *f)
-{
- int n,r;
- jpeg j;
- n = ftell(f);
- start_file(&j.s, f);
+ j.s = s;
r = decode_jpeg_header(&j, SCAN_type);
- fseek(f,n,SEEK_SET);
+ stbi_rewind(s);
return r;
}
-#endif
-int stbi_jpeg_test_memory(stbi_uc const *buffer, int len)
+static int stbi_jpeg_info_raw(jpeg *j, int *x, int *y, int *comp)
{
- jpeg j;
- start_mem(&j.s, buffer,len);
- return decode_jpeg_header(&j, SCAN_type);
+ if (!decode_jpeg_header(j, SCAN_header)) {
+ stbi_rewind( j->s );
+ return 0;
+ }
+ if (x) *x = j->s->img_x;
+ if (y) *y = j->s->img_y;
+ if (comp) *comp = j->s->img_n;
+ return 1;
}
-// @TODO:
-#ifndef STBI_NO_STDIO
-extern int stbi_jpeg_info (char const *filename, int *x, int *y, int *comp);
-extern int stbi_jpeg_info_from_file (FILE *f, int *x, int *y, int *comp);
-#endif
-extern int stbi_jpeg_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp);
+static int stbi_jpeg_info(stbi *s, int *x, int *y, int *comp)
+{
+ jpeg j;
+ j.s = s;
+ return stbi_jpeg_info_raw(&j, x, y, comp);
+}
// public domain zlib decode v0.2 Sean Barrett 2006-11-18
// simple implementation
@@ -1896,7 +1964,7 @@ typedef struct
uint16 value[288];
} zhuffman;
-__forceinline static int bitreverse16(int n)
+stbi_inline static int bitreverse16(int n)
{
n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1);
n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2);
@@ -1905,7 +1973,7 @@ __forceinline static int bitreverse16(int n)
return n;
}
-__forceinline static int bit_reverse(int v, int bits)
+stbi_inline static int bit_reverse(int v, int bits)
{
assert(bits <= 16);
// to bit reverse n bits, reverse 16 and shift
@@ -1978,7 +2046,7 @@ typedef struct
zhuffman z_length, z_distance;
} zbuf;
-__forceinline static int zget8(zbuf *z)
+stbi_inline static int zget8(zbuf *z)
{
if (z->zbuffer >= z->zbuffer_end) return 0;
return *z->zbuffer++;
@@ -1993,7 +2061,7 @@ static void fill_bits(zbuf *z)
} while (z->num_bits <= 24);
}
-__forceinline static unsigned int zreceive(zbuf *z, int n)
+stbi_inline static unsigned int zreceive(zbuf *z, int n)
{
unsigned int k;
if (z->num_bits < n) fill_bits(z);
@@ -2003,7 +2071,7 @@ __forceinline static unsigned int zreceive(zbuf *z, int n)
return k;
}
-__forceinline static int zhuffman_decode(zbuf *a, zhuffman *z)
+stbi_inline static int zhuffman_decode(zbuf *a, zhuffman *z)
{
int b,s,k;
if (a->num_bits < 16) fill_bits(a);
@@ -2253,6 +2321,22 @@ char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen)
return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen);
}
+char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header)
+{
+ zbuf a;
+ char *p = (char *) malloc(initial_size);
+ if (p == NULL) return NULL;
+ a.zbuffer = (uint8 *) buffer;
+ a.zbuffer_end = (uint8 *) buffer + len;
+ if (do_zlib(&a, p, initial_size, 1, parse_header)) {
+ if (outlen) *outlen = (int) (a.zout - a.zout_start);
+ return a.zout_start;
+ } else {
+ free(a.zout_start);
+ return NULL;
+ }
+}
+
int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen)
{
zbuf a;
@@ -2323,20 +2407,20 @@ static int check_png_header(stbi *s)
static uint8 png_sig[8] = { 137,80,78,71,13,10,26,10 };
int i;
for (i=0; i < 8; ++i)
- if (get8(s) != png_sig[i]) return e("bad png sig","Not a PNG");
+ if (get8u(s) != png_sig[i]) return e("bad png sig","Not a PNG");
return 1;
}
typedef struct
{
- stbi s;
+ stbi *s;
uint8 *idata, *expanded, *out;
} png;
enum {
F_none=0, F_sub=1, F_up=2, F_avg=3, F_paeth=4,
- F_avg_first, F_paeth_first,
+ F_avg_first, F_paeth_first
};
static uint8 first_row_filter[5] =
@@ -2358,7 +2442,7 @@ static int paeth(int a, int b, int c)
// create the png data from post-deflated data
static int create_png_image_raw(png *a, uint8 *raw, uint32 raw_len, int out_n, uint32 x, uint32 y)
{
- stbi *s = &a->s;
+ stbi *s = a->s;
uint32 i,j,stride = x*out_n;
int k;
int img_n = s->img_n; // copy it into a local for later
@@ -2367,10 +2451,11 @@ static int create_png_image_raw(png *a, uint8 *raw, uint32 raw_len, int out_n, u
a->out = (uint8 *) malloc(x * y * out_n);
if (!a->out) return e("outofmem", "Out of memory");
if (!stbi_png_partial) {
- if (s->img_x == x && s->img_y == y)
+ if (s->img_x == x && s->img_y == y) {
if (raw_len != (img_n * x + 1) * y) return e("not enough pixels","Corrupt PNG");
- else // interlaced:
+ } else { // interlaced:
if (raw_len < (img_n * x + 1) * y) return e("not enough pixels","Corrupt PNG");
+ }
}
for (j=0; j < y; ++j) {
uint8 *cur = a->out + stride*j;
@@ -2381,7 +2466,7 @@ static int create_png_image_raw(png *a, uint8 *raw, uint32 raw_len, int out_n, u
if (j == 0) filter = first_row_filter[filter];
// handle first pixel explicitly
for (k=0; k < img_n; ++k) {
- switch(filter) {
+ switch (filter) {
case F_none : cur[k] = raw[k]; break;
case F_sub : cur[k] = raw[k]; break;
case F_up : cur[k] = raw[k] + prior[k]; break;
@@ -2401,7 +2486,7 @@ static int create_png_image_raw(png *a, uint8 *raw, uint32 raw_len, int out_n, u
case f: \
for (i=x-1; i >= 1; --i, raw+=img_n,cur+=img_n,prior+=img_n) \
for (k=0; k < img_n; ++k)
- switch(filter) {
+ switch (filter) {
CASE(F_none) cur[k] = raw[k]; break;
CASE(F_sub) cur[k] = raw[k] + cur[k-img_n]; break;
CASE(F_up) cur[k] = raw[k] + prior[k]; break;
@@ -2417,7 +2502,7 @@ static int create_png_image_raw(png *a, uint8 *raw, uint32 raw_len, int out_n, u
case f: \
for (i=x-1; i >= 1; --i, cur[img_n]=255,raw+=img_n,cur+=out_n,prior+=out_n) \
for (k=0; k < img_n; ++k)
- switch(filter) {
+ switch (filter) {
CASE(F_none) cur[k] = raw[k]; break;
CASE(F_sub) cur[k] = raw[k] + cur[k-out_n]; break;
CASE(F_up) cur[k] = raw[k] + prior[k]; break;
@@ -2438,12 +2523,12 @@ static int create_png_image(png *a, uint8 *raw, uint32 raw_len, int out_n, int i
int p;
int save;
if (!interlaced)
- return create_png_image_raw(a, raw, raw_len, out_n, a->s.img_x, a->s.img_y);
+ return create_png_image_raw(a, raw, raw_len, out_n, a->s->img_x, a->s->img_y);
save = stbi_png_partial;
stbi_png_partial = 0;
// de-interlacing
- final = (uint8 *) malloc(a->s.img_x * a->s.img_y * out_n);
+ final = (uint8 *) malloc(a->s->img_x * a->s->img_y * out_n);
for (p=0; p < 7; ++p) {
int xorig[] = { 0,4,0,2,0,1,0 };
int yorig[] = { 0,0,4,0,2,0,1 };
@@ -2451,8 +2536,8 @@ static int create_png_image(png *a, uint8 *raw, uint32 raw_len, int out_n, int i
int yspc[] = { 8,8,8,4,4,2,2 };
int i,j,x,y;
// pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1
- x = (a->s.img_x - xorig[p] + xspc[p]-1) / xspc[p];
- y = (a->s.img_y - yorig[p] + yspc[p]-1) / yspc[p];
+ x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p];
+ y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p];
if (x && y) {
if (!create_png_image_raw(a, raw, raw_len, out_n, x, y)) {
free(final);
@@ -2460,7 +2545,7 @@ static int create_png_image(png *a, uint8 *raw, uint32 raw_len, int out_n, int i
}
for (j=0; j < y; ++j)
for (i=0; i < x; ++i)
- memcpy(final + (j*yspc[p]+yorig[p])*a->s.img_x*out_n + (i*xspc[p]+xorig[p])*out_n,
+ memcpy(final + (j*yspc[p]+yorig[p])*a->s->img_x*out_n + (i*xspc[p]+xorig[p])*out_n,
a->out + (j*x+i)*out_n, out_n);
free(a->out);
raw += (x*out_n+1)*y;
@@ -2475,7 +2560,7 @@ static int create_png_image(png *a, uint8 *raw, uint32 raw_len, int out_n, int i
static int compute_transparency(png *z, uint8 tc[3], int out_n)
{
- stbi *s = &z->s;
+ stbi *s = z->s;
uint32 i, pixel_count = s->img_x * s->img_y;
uint8 *p = z->out;
@@ -2498,9 +2583,9 @@ static int compute_transparency(png *z, uint8 tc[3], int out_n)
return 1;
}
-static int expand_palette(png *a, uint8 *palette, int /*len*/, int pal_img_n)
+static int expand_palette(png *a, uint8 *palette, int len, int pal_img_n)
{
- uint32 i, pixel_count = a->s.img_x * a->s.img_y;
+ uint32 i, pixel_count = a->s->img_x * a->s->img_y;
uint8 *p, *temp_out, *orig = a->out;
p = (uint8 *) malloc(pixel_count * pal_img_n);
@@ -2529,29 +2614,93 @@ static int expand_palette(png *a, uint8 *palette, int /*len*/, int pal_img_n)
}
free(a->out);
a->out = temp_out;
+
+ STBI_NOTUSED(len);
+
return 1;
}
+static int stbi_unpremultiply_on_load = 0;
+static int stbi_de_iphone_flag = 0;
+
+void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply)
+{
+ stbi_unpremultiply_on_load = flag_true_if_should_unpremultiply;
+}
+void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert)
+{
+ stbi_de_iphone_flag = flag_true_if_should_convert;
+}
+
+static void stbi_de_iphone(png *z)
+{
+ stbi *s = z->s;
+ uint32 i, pixel_count = s->img_x * s->img_y;
+ uint8 *p = z->out;
+
+ if (s->img_out_n == 3) { // convert bgr to rgb
+ for (i=0; i < pixel_count; ++i) {
+ uint8 t = p[0];
+ p[0] = p[2];
+ p[2] = t;
+ p += 3;
+ }
+ } else {
+ assert(s->img_out_n == 4);
+ if (stbi_unpremultiply_on_load) {
+ // convert bgr to rgb and unpremultiply
+ for (i=0; i < pixel_count; ++i) {
+ uint8 a = p[3];
+ uint8 t = p[0];
+ if (a) {
+ p[0] = p[2] * 255 / a;
+ p[1] = p[1] * 255 / a;
+ p[2] = t * 255 / a;
+ } else {
+ p[0] = p[2];
+ p[2] = t;
+ }
+ p += 4;
+ }
+ } else {
+ // convert bgr to rgb
+ for (i=0; i < pixel_count; ++i) {
+ uint8 t = p[0];
+ p[0] = p[2];
+ p[2] = t;
+ p += 4;
+ }
+ }
+ }
+}
+
static int parse_png_file(png *z, int scan, int req_comp)
{
uint8 palette[1024], pal_img_n=0;
uint8 has_trans=0, tc[3];
uint32 ioff=0, idata_limit=0, i, pal_len=0;
- int first=1,k,interlace=0;
- stbi *s = &z->s;
+ int first=1,k,interlace=0, iphone=0;
+ stbi *s = z->s;
+
+ z->expanded = NULL;
+ z->idata = NULL;
+ z->out = NULL;
if (!check_png_header(s)) return 0;
if (scan == SCAN_type) return 1;
- for(;;first=0) {
+ for (;;) {
chunk c = get_chunk_header(s);
- if (first && c.type != PNG_TYPE('I','H','D','R'))
- return e("first not IHDR","Corrupt PNG");
switch (c.type) {
+ case PNG_TYPE('C','g','B','I'):
+ iphone = stbi_de_iphone_flag;
+ skip(s, c.length);
+ break;
case PNG_TYPE('I','H','D','R'): {
int depth,color,comp,filter;
if (!first) return e("multiple IHDR","Corrupt PNG");
+ first = 0;
if (c.length != 13) return e("bad IHDR len","Corrupt PNG");
s->img_x = get32(s); if (s->img_x > (1 << 24)) return e("too large","Very large image (corrupt?)");
s->img_y = get32(s); if (s->img_y > (1 << 24)) return e("too large","Very large image (corrupt?)");
@@ -2577,6 +2726,7 @@ static int parse_png_file(png *z, int scan, int req_comp)
}
case PNG_TYPE('P','L','T','E'): {
+ if (first) return e("first not IHDR", "Corrupt PNG");
if (c.length > 256*3) return e("invalid PLTE","Corrupt PNG");
pal_len = c.length / 3;
if (pal_len * 3 != c.length) return e("invalid PLTE","Corrupt PNG");
@@ -2590,6 +2740,7 @@ static int parse_png_file(png *z, int scan, int req_comp)
}
case PNG_TYPE('t','R','N','S'): {
+ if (first) return e("first not IHDR", "Corrupt PNG");
if (z->idata) return e("tRNS after IDAT","Corrupt PNG");
if (pal_img_n) {
if (scan == SCAN_header) { s->img_n = 4; return 1; }
@@ -2609,6 +2760,7 @@ static int parse_png_file(png *z, int scan, int req_comp)
}
case PNG_TYPE('I','D','A','T'): {
+ if (first) return e("first not IHDR", "Corrupt PNG");
if (pal_img_n && !pal_len) return e("no PLTE","Corrupt PNG");
if (scan == SCAN_header) { s->img_n = pal_img_n; return 1; }
if (ioff + c.length > idata_limit) {
@@ -2619,26 +2771,17 @@ static int parse_png_file(png *z, int scan, int req_comp)
p = (uint8 *) realloc(z->idata, idata_limit); if (p == NULL) return e("outofmem", "Out of memory");
z->idata = p;
}
- #ifndef STBI_NO_STDIO
- if (s->img_file)
- {
- if (fread(z->idata+ioff,1,c.length,s->img_file) != c.length) return e("outofdata","Corrupt PNG");
- }
- else
- #endif
- {
- memcpy(z->idata+ioff, s->img_buffer, c.length);
- s->img_buffer += c.length;
- }
+ if (!getn(s, z->idata+ioff,c.length)) return e("outofdata","Corrupt PNG");
ioff += c.length;
break;
}
case PNG_TYPE('I','E','N','D'): {
uint32 raw_len;
+ if (first) return e("first not IHDR", "Corrupt PNG");
if (scan != SCAN_load) return 1;
if (z->idata == NULL) return e("no IDAT","Corrupt PNG");
- z->expanded = (uint8 *) stbi_zlib_decode_malloc((char *) z->idata, ioff, (int *) &raw_len);
+ z->expanded = (uint8 *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, 16384, (int *) &raw_len, !iphone);
if (z->expanded == NULL) return 0; // zlib should set error
free(z->idata); z->idata = NULL;
if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans)
@@ -2648,6 +2791,8 @@ static int parse_png_file(png *z, int scan, int req_comp)
if (!create_png_image(z, z->expanded, raw_len, s->img_out_n, interlace)) return 0;
if (has_trans)
if (!compute_transparency(z, tc, s->img_out_n)) return 0;
+ if (iphone && s->img_out_n > 2)
+ stbi_de_iphone(z);
if (pal_img_n) {
// pal_img_n == 3 or 4
s->img_n = pal_img_n; // record the actual colors we had
@@ -2662,6 +2807,7 @@ static int parse_png_file(png *z, int scan, int req_comp)
default:
// if critical, fail
+ if (first) return e("first not IHDR", "Corrupt PNG");
if ((c.type & (1 << 29)) == 0) {
#ifndef STBI_NO_FAILURE_STRINGS
// not threadsafe
@@ -2684,21 +2830,18 @@ static int parse_png_file(png *z, int scan, int req_comp)
static unsigned char *do_png(png *p, int *x, int *y, int *n, int req_comp)
{
unsigned char *result=NULL;
- p->expanded = NULL;
- p->idata = NULL;
- p->out = NULL;
if (req_comp < 0 || req_comp > 4) return epuc("bad req_comp", "Internal error");
if (parse_png_file(p, SCAN_load, req_comp)) {
result = p->out;
p->out = NULL;
- if (req_comp && req_comp != p->s.img_out_n) {
- result = convert_format(result, p->s.img_out_n, req_comp, p->s.img_x, p->s.img_y);
- p->s.img_out_n = req_comp;
+ if (req_comp && req_comp != p->s->img_out_n) {
+ result = convert_format(result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y);
+ p->s->img_out_n = req_comp;
if (result == NULL) return result;
}
- *x = p->s.img_x;
- *y = p->s.img_y;
- if (n) *n = p->s.img_n;
+ *x = p->s->img_x;
+ *y = p->s->img_y;
+ if (n) *n = p->s->img_n;
}
free(p->out); p->out = NULL;
free(p->expanded); p->expanded = NULL;
@@ -2707,74 +2850,39 @@ static unsigned char *do_png(png *p, int *x, int *y, int *n, int req_comp)
return result;
}
-#ifndef STBI_NO_STDIO
-unsigned char *stbi_png_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp)
+static unsigned char *stbi_png_load(stbi *s, int *x, int *y, int *comp, int req_comp)
{
png p;
- start_file(&p.s, f);
+ p.s = s;
return do_png(&p, x,y,comp,req_comp);
}
-unsigned char *stbi_png_load(char const *filename, int *x, int *y, int *comp, int req_comp)
+static int stbi_png_test(stbi *s)
{
- unsigned char *data;
- FILE *f = fopen(filename, "rb");
- if (!f) return NULL;
- data = stbi_png_load_from_file(f,x,y,comp,req_comp);
- fclose(f);
- return data;
-}
-#endif
-
-unsigned char *stbi_png_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp)
-{
- png p;
- start_mem(&p.s, buffer,len);
- return do_png(&p, x,y,comp,req_comp);
-}
-
-#ifndef STBI_NO_STDIO
-int stbi_png_test_file(FILE *f)
-{
- png p;
- int n,r;
- n = ftell(f);
- start_file(&p.s, f);
- r = parse_png_file(&p, SCAN_type,STBI_default);
- fseek(f,n,SEEK_SET);
+ int r;
+ r = check_png_header(s);
+ stbi_rewind(s);
return r;
}
-#endif
-int stbi_png_test_memory(stbi_uc const *buffer, int len)
+static int stbi_png_info_raw(png *p, int *x, int *y, int *comp)
{
- png p;
- start_mem(&p.s, buffer, len);
- return parse_png_file(&p, SCAN_type,STBI_default);
-}
-
-// TODO: load header from png
-#ifndef STBI_NO_STDIO
-int stbi_png_info (char const *filename, int *x, int *y, int *comp)
-{
- png p;
- FILE *f = fopen(filename, "rb");
- if (!f) return 0;
- start_file(&p.s, f);
- if (parse_png_file(&p, SCAN_header, 0)) {
- if(x) *x = p.s.img_x;
- if(y) *y = p.s.img_y;
- if (comp) *comp = p.s.img_n;
- fclose(f);
- return 1;
+ if (!parse_png_file(p, SCAN_header, 0)) {
+ stbi_rewind( p->s );
+ return 0;
}
- fclose(f);
- return 0;
+ if (x) *x = p->s->img_x;
+ if (y) *y = p->s->img_y;
+ if (comp) *comp = p->s->img_n;
+ return 1;
}
-extern int stbi_png_info_from_file (FILE *f, int *x, int *y, int *comp);
-#endif
-extern int stbi_png_info_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp);
+static int stbi_png_info(stbi *s, int *x, int *y, int *comp)
+{
+ png p;
+ p.s = s;
+ return stbi_png_info_raw(&p, x, y, comp);
+}
// Microsoft/Windows BMP image
@@ -2792,24 +2900,13 @@ static int bmp_test(stbi *s)
return 0;
}
-#ifndef STBI_NO_STDIO
-int stbi_bmp_test_file (FILE *f)
+static int stbi_bmp_test(stbi *s)
{
- stbi s;
- int r,n = ftell(f);
- start_file(&s,f);
- r = bmp_test(&s);
- fseek(f,n,SEEK_SET);
+ int r = bmp_test(s);
+ stbi_rewind(s);
return r;
}
-#endif
-int stbi_bmp_test_memory (stbi_uc const *buffer, int len)
-{
- stbi s;
- start_mem(&s, buffer, len);
- return bmp_test(&s);
-}
// returns 0..31 for the highest set bit
static int high_bit(unsigned int z)
@@ -2854,7 +2951,7 @@ static int shiftsigned(int v, int shift, int bits)
static stbi_uc *bmp_load(stbi *s, int *x, int *y, int *comp, int req_comp)
{
uint8 *out;
- unsigned int mr=0,mg=0,mb=0,ma=0, fake_a=0;
+ unsigned int mr=0,mg=0,mb=0,ma=0;
stbi_uc pal[256][4];
int psize=0,i,j,compress=0,width;
int bpp, flip_vertically, pad, target, offset, hsz;
@@ -2865,7 +2962,6 @@ static stbi_uc *bmp_load(stbi *s, int *x, int *y, int *comp, int req_comp)
offset = get32le(s);
hsz = get32le(s);
if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108) return epuc("unknown BMP", "BMP type not supported: unknown");
- failure_reason = "bad BMP";
if (hsz == 12) {
s->img_x = get16le(s);
s->img_y = get16le(s);
@@ -2873,7 +2969,7 @@ static stbi_uc *bmp_load(stbi *s, int *x, int *y, int *comp, int req_comp)
s->img_x = get32le(s);
s->img_y = get32le(s);
}
- if (get16le(s) != 1) return 0;
+ if (get16le(s) != 1) return epuc("bad BMP", "bad BMP");
bpp = get16le(s);
if (bpp == 1) return epuc("monochrome", "BMP type not supported: 1-bit");
flip_vertically = ((int) s->img_y) > 0;
@@ -2900,15 +2996,14 @@ static stbi_uc *bmp_load(stbi *s, int *x, int *y, int *comp, int req_comp)
mr = mg = mb = 0;
if (compress == 0) {
if (bpp == 32) {
- mr = 0xff << 16;
- mg = 0xff << 8;
- mb = 0xff << 0;
- ma = (unsigned int)(0xff << 24);
- fake_a = 1; // @TODO: check for cases like alpha value is all 0 and switch it to 255
+ mr = 0xffu << 16;
+ mg = 0xffu << 8;
+ mb = 0xffu << 0;
+ ma = 0xffu << 24;
} else {
- mr = 31 << 10;
- mg = 31 << 5;
- mb = 31 << 0;
+ mr = 31u << 10;
+ mg = 31u << 5;
+ mb = 31u << 0;
}
} else if (compress == 3) {
mr = get32le(s);
@@ -2917,10 +3012,10 @@ static stbi_uc *bmp_load(stbi *s, int *x, int *y, int *comp, int req_comp)
// not documented, but generated by photoshop and handled by mspaint
if (mr == mg && mg == mb) {
// ?!?!?
- return NULL;
+ return epuc("bad BMP", "bad BMP");
}
} else
- return NULL;
+ return epuc("bad BMP", "bad BMP");
}
} else {
assert(hsz == 108);
@@ -2946,9 +3041,9 @@ static stbi_uc *bmp_load(stbi *s, int *x, int *y, int *comp, int req_comp)
int z=0;
if (psize == 0 || psize > 256) { free(out); return epuc("invalid", "Corrupt BMP"); }
for (i=0; i < psize; ++i) {
- pal[i][2] = (stbi_uc)get8(s);
- pal[i][1] = (stbi_uc)get8(s);
- pal[i][0] = (stbi_uc)get8(s);
+ pal[i][2] = get8u(s);
+ pal[i][1] = get8u(s);
+ pal[i][0] = get8u(s);
if (hsz != 12) get8(s);
pal[i][3] = 255;
}
@@ -2989,11 +3084,11 @@ static stbi_uc *bmp_load(stbi *s, int *x, int *y, int *comp, int req_comp)
if (bpp == 24) {
easy = 1;
} else if (bpp == 32) {
- if (mb == 0xff && mg == 0xff00 && mr == 0xff000000 && ma == 0xff000000)
+ if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000)
easy = 2;
}
if (!easy) {
- if (!mr || !mg || !mb) return epuc("bad masks", "Corrupt BMP");
+ if (!mr || !mg || !mb) { free(out); return epuc("bad masks", "Corrupt BMP"); }
// right shift amt to put high bit in position #7
rshift = high_bit(mr)-7; rcount = bitcount(mr);
gshift = high_bit(mg)-7; gcount = bitcount(mr);
@@ -3004,22 +3099,22 @@ static stbi_uc *bmp_load(stbi *s, int *x, int *y, int *comp, int req_comp)
if (easy) {
for (i=0; i < (int) s->img_x; ++i) {
int a;
- out[z+2] = (uint8)get8(s);
- out[z+1] = (uint8)get8(s);
- out[z+0] = (uint8)get8(s);
+ out[z+2] = get8u(s);
+ out[z+1] = get8u(s);
+ out[z+0] = get8u(s);
z += 3;
a = (easy == 2 ? get8(s) : 255);
- if (target == 4) out[z++] = (uint8)a;
+ if (target == 4) out[z++] = (uint8) a;
}
} else {
for (i=0; i < (int) s->img_x; ++i) {
uint32 v = (bpp == 16 ? get16le(s) : get32le(s));
int a;
- out[z++] = (uint8)shiftsigned(v & mr, rshift, rcount);
- out[z++] = (uint8)shiftsigned(v & mg, gshift, gcount);
- out[z++] = (uint8)shiftsigned(v & mb, bshift, bcount);
+ out[z++] = (uint8) shiftsigned(v & mr, rshift, rcount);
+ out[z++] = (uint8) shiftsigned(v & mg, gshift, gcount);
+ out[z++] = (uint8) shiftsigned(v & mb, bshift, bcount);
a = (ma ? shiftsigned(v & ma, ashift, acount) : 255);
- if (target == 4) out[z++] = (uint8)a;
+ if (target == 4) out[z++] = (uint8) a;
}
}
skip(s, pad);
@@ -3043,534 +3138,1055 @@ static stbi_uc *bmp_load(stbi *s, int *x, int *y, int *comp, int req_comp)
*x = s->img_x;
*y = s->img_y;
- if (comp) *comp = target;
+ if (comp) *comp = s->img_n;
return out;
}
-#ifndef STBI_NO_STDIO
-stbi_uc *stbi_bmp_load (char const *filename, int *x, int *y, int *comp, int req_comp)
+static stbi_uc *stbi_bmp_load(stbi *s,int *x, int *y, int *comp, int req_comp)
{
- stbi_uc *data;
- FILE *f = fopen(filename, "rb");
- if (!f) return NULL;
- data = stbi_bmp_load_from_file(f, x,y,comp,req_comp);
- fclose(f);
- return data;
+ return bmp_load(s, x,y,comp,req_comp);
}
-stbi_uc *stbi_bmp_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp)
-{
- stbi s;
- start_file(&s, f);
- return bmp_load(&s, x,y,comp,req_comp);
-}
-#endif
-
-stbi_uc *stbi_bmp_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp)
-{
- stbi s;
- start_mem(&s, buffer, len);
- return bmp_load(&s, x,y,comp,req_comp);
-}
// Targa Truevision - TGA
// by Jonathan Dummer
+static int tga_info(stbi *s, int *x, int *y, int *comp)
+{
+ int tga_w, tga_h, tga_comp;
+ int sz;
+ get8u(s); // discard Offset
+ sz = get8u(s); // color type
+ if( sz > 1 ) {
+ stbi_rewind(s);
+ return 0; // only RGB or indexed allowed
+ }
+ sz = get8u(s); // image type
+ // only RGB or grey allowed, +/- RLE
+ if ((sz != 1) && (sz != 2) && (sz != 3) && (sz != 9) && (sz != 10) && (sz != 11)) return 0;
+ skip(s,9);
+ tga_w = get16le(s);
+ if( tga_w < 1 ) {
+ stbi_rewind(s);
+ return 0; // test width
+ }
+ tga_h = get16le(s);
+ if( tga_h < 1 ) {
+ stbi_rewind(s);
+ return 0; // test height
+ }
+ sz = get8(s); // bits per pixel
+ // only RGB or RGBA or grey allowed
+ if ((sz != 8) && (sz != 16) && (sz != 24) && (sz != 32)) {
+ stbi_rewind(s);
+ return 0;
+ }
+ tga_comp = sz;
+ if (x) *x = tga_w;
+ if (y) *y = tga_h;
+ if (comp) *comp = tga_comp / 8;
+ return 1; // seems to have passed everything
+}
+
+int stbi_tga_info(stbi *s, int *x, int *y, int *comp)
+{
+ return tga_info(s, x, y, comp);
+}
+
static int tga_test(stbi *s)
{
- int sz;
- get8u(s); // discard Offset
- sz = get8u(s); // color type
- if( sz > 1 ) return 0; // only RGB or indexed allowed
- sz = get8u(s); // image type
- if( (sz != 1) && (sz != 2) && (sz != 3) && (sz != 9) && (sz != 10) && (sz != 11) ) return 0; // only RGB or grey allowed, +/- RLE
- get16(s); // discard palette start
- get16(s); // discard palette length
- get8(s); // discard bits per palette color entry
- get16(s); // discard x origin
- get16(s); // discard y origin
- if( get16(s) < 1 ) return 0; // test width
- if( get16(s) < 1 ) return 0; // test height
- sz = get8(s); // bits per pixel
- if( (sz != 8) && (sz != 16) && (sz != 24) && (sz != 32) ) return 0; // only RGB or RGBA or grey allowed
- return 1; // seems to have passed everything
+ int sz;
+ get8u(s); // discard Offset
+ sz = get8u(s); // color type
+ if ( sz > 1 ) return 0; // only RGB or indexed allowed
+ sz = get8u(s); // image type
+ if ( (sz != 1) && (sz != 2) && (sz != 3) && (sz != 9) && (sz != 10) && (sz != 11) ) return 0; // only RGB or grey allowed, +/- RLE
+ get16(s); // discard palette start
+ get16(s); // discard palette length
+ get8(s); // discard bits per palette color entry
+ get16(s); // discard x origin
+ get16(s); // discard y origin
+ if ( get16(s) < 1 ) return 0; // test width
+ if ( get16(s) < 1 ) return 0; // test height
+ sz = get8(s); // bits per pixel
+ if ( (sz != 8) && (sz != 16) && (sz != 24) && (sz != 32) ) return 0; // only RGB or RGBA or grey allowed
+ return 1; // seems to have passed everything
}
-#ifndef STBI_NO_STDIO
-int stbi_tga_test_file (FILE *f)
+static int stbi_tga_test(stbi *s)
{
- stbi s;
- int r,n = ftell(f);
- start_file(&s, f);
- r = tga_test(&s);
- fseek(f,n,SEEK_SET);
- return r;
-}
-#endif
-
-int stbi_tga_test_memory (stbi_uc const *buffer, int len)
-{
- stbi s;
- start_mem(&s, buffer, len);
- return tga_test(&s);
+ int res = tga_test(s);
+ stbi_rewind(s);
+ return res;
}
static stbi_uc *tga_load(stbi *s, int *x, int *y, int *comp, int req_comp)
{
- // read in the TGA header stuff
- int tga_offset = get8u(s);
- int tga_indexed = get8u(s);
- int tga_image_type = get8u(s);
- int tga_is_RLE = 0;
- int tga_palette_start = get16le(s);
- int tga_palette_len = get16le(s);
- int tga_palette_bits = get8u(s);
- int tga_x_origin = get16le(s);
- int tga_y_origin = get16le(s);
- int tga_width = get16le(s);
- int tga_height = get16le(s);
- int tga_bits_per_pixel = get8u(s);
- int tga_inverted = get8u(s);
- // image data
- unsigned char *tga_data;
- unsigned char *tga_palette = NULL;
- int i, j;
- unsigned char raw_data[4] = {0,0,0,0};
- unsigned char trans_data[4] = {0,0,0,0};
- int RLE_count = 0;
- int RLE_repeating = 0;
- int read_next_pixel = 1;
- // do a tiny bit of precessing
- if( tga_image_type >= 8 )
- {
- tga_image_type -= 8;
- tga_is_RLE = 1;
- }
- /* int tga_alpha_bits = tga_inverted & 15; */
- tga_inverted = 1 - ((tga_inverted >> 5) & 1);
+ // read in the TGA header stuff
+ int tga_offset = get8u(s);
+ int tga_indexed = get8u(s);
+ int tga_image_type = get8u(s);
+ int tga_is_RLE = 0;
+ int tga_palette_start = get16le(s);
+ int tga_palette_len = get16le(s);
+ int tga_palette_bits = get8u(s);
+ int tga_x_origin = get16le(s);
+ int tga_y_origin = get16le(s);
+ int tga_width = get16le(s);
+ int tga_height = get16le(s);
+ int tga_bits_per_pixel = get8u(s);
+ int tga_inverted = get8u(s);
+ // image data
+ unsigned char *tga_data;
+ unsigned char *tga_palette = NULL;
+ int i, j;
+ unsigned char raw_data[4];
+ unsigned char trans_data[4];
+ int RLE_count = 0;
+ int RLE_repeating = 0;
+ int read_next_pixel = 1;
- // error check
- if( //(tga_indexed) ||
- (tga_width < 1) || (tga_height < 1) ||
- (tga_image_type < 1) || (tga_image_type > 3) ||
- ((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16) &&
- (tga_bits_per_pixel != 24) && (tga_bits_per_pixel != 32))
- )
- {
- return NULL;
- }
+ // do a tiny bit of precessing
+ if ( tga_image_type >= 8 )
+ {
+ tga_image_type -= 8;
+ tga_is_RLE = 1;
+ }
+ /* int tga_alpha_bits = tga_inverted & 15; */
+ tga_inverted = 1 - ((tga_inverted >> 5) & 1);
- // If I'm paletted, then I'll use the number of bits from the palette
- if( tga_indexed )
- {
- tga_bits_per_pixel = tga_palette_bits;
- }
+ // error check
+ if ( //(tga_indexed) ||
+ (tga_width < 1) || (tga_height < 1) ||
+ (tga_image_type < 1) || (tga_image_type > 3) ||
+ ((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16) &&
+ (tga_bits_per_pixel != 24) && (tga_bits_per_pixel != 32))
+ )
+ {
+ return NULL; // we don't report this as a bad TGA because we don't even know if it's TGA
+ }
- // tga info
- *x = tga_width;
- *y = tga_height;
- if( (req_comp < 1) || (req_comp > 4) )
- {
- // just use whatever the file was
- req_comp = tga_bits_per_pixel / 8;
- *comp = req_comp;
- } else
- {
- // force a new number of components
- *comp = tga_bits_per_pixel/8;
- }
- tga_data = (unsigned char*)malloc( tga_width * tga_height * req_comp );
+ // If I'm paletted, then I'll use the number of bits from the palette
+ if ( tga_indexed )
+ {
+ tga_bits_per_pixel = tga_palette_bits;
+ }
- // skip to the data's starting position (offset usually = 0)
- skip(s, tga_offset );
- // do I need to load a palette?
- if( tga_indexed )
- {
- // any data to skip? (offset usually = 0)
- skip(s, tga_palette_start );
- // load the palette
- tga_palette = (unsigned char*)malloc( tga_palette_len * tga_palette_bits / 8 );
- getn(s, tga_palette, tga_palette_len * tga_palette_bits / 8 );
- }
- // load the data
- for( i = 0; i < tga_width * tga_height; ++i )
- {
- // if I'm in RLE mode, do I need to get a RLE chunk?
- if( tga_is_RLE )
- {
- if( RLE_count == 0 )
- {
- // yep, get the next byte as a RLE command
- int RLE_cmd = get8u(s);
- RLE_count = 1 + (RLE_cmd & 127);
- RLE_repeating = RLE_cmd >> 7;
- read_next_pixel = 1;
- } else if( !RLE_repeating )
- {
- read_next_pixel = 1;
- }
- } else
- {
- read_next_pixel = 1;
- }
- // OK, if I need to read a pixel, do it now
- if( read_next_pixel )
- {
- // load however much data we did have
- if( tga_indexed )
- {
- // read in 1 byte, then perform the lookup
- int pal_idx = get8u(s);
- if( pal_idx >= tga_palette_len )
- {
- // invalid index
- pal_idx = 0;
- }
- pal_idx *= tga_bits_per_pixel / 8;
- for( j = 0; j*8 < tga_bits_per_pixel; ++j )
- {
- raw_data[j] = tga_palette[pal_idx+j];
- }
- } else
- {
- // read in the data raw
- for( j = 0; j*8 < tga_bits_per_pixel; ++j )
- {
- raw_data[j] = get8u(s);
- }
- }
- // convert raw to the intermediate format
- switch( tga_bits_per_pixel )
- {
- case 8:
- // Luminous => RGBA
- trans_data[0] = raw_data[0];
- trans_data[1] = raw_data[0];
- trans_data[2] = raw_data[0];
- trans_data[3] = 255;
- break;
- case 16:
- // Luminous,Alpha => RGBA
- trans_data[0] = raw_data[0];
- trans_data[1] = raw_data[0];
- trans_data[2] = raw_data[0];
- trans_data[3] = raw_data[1];
- break;
- case 24:
- // BGR => RGBA
- trans_data[0] = raw_data[2];
- trans_data[1] = raw_data[1];
- trans_data[2] = raw_data[0];
- trans_data[3] = 255;
- break;
- case 32:
- // BGRA => RGBA
- trans_data[0] = raw_data[2];
- trans_data[1] = raw_data[1];
- trans_data[2] = raw_data[0];
- trans_data[3] = raw_data[3];
- break;
- }
- // clear the reading flag for the next pixel
- read_next_pixel = 0;
- } // end of reading a pixel
- // convert to final format
- switch( req_comp )
- {
- case 1:
- // RGBA => Luminance
- tga_data[i*req_comp+0] = compute_y(trans_data[0],trans_data[1],trans_data[2]);
- break;
- case 2:
- // RGBA => Luminance,Alpha
- tga_data[i*req_comp+0] = compute_y(trans_data[0],trans_data[1],trans_data[2]);
- tga_data[i*req_comp+1] = trans_data[3];
- break;
- case 3:
- // RGBA => RGB
- tga_data[i*req_comp+0] = trans_data[0];
- tga_data[i*req_comp+1] = trans_data[1];
- tga_data[i*req_comp+2] = trans_data[2];
- break;
- case 4:
- // RGBA => RGBA
- tga_data[i*req_comp+0] = trans_data[0];
- tga_data[i*req_comp+1] = trans_data[1];
- tga_data[i*req_comp+2] = trans_data[2];
- tga_data[i*req_comp+3] = trans_data[3];
- break;
- }
- // in case we're in RLE mode, keep counting down
- --RLE_count;
- }
- // do I need to invert the image?
- if( tga_inverted )
- {
- for( j = 0; j*2 < tga_height; ++j )
- {
- int index1 = j * tga_width * req_comp;
- int index2 = (tga_height - 1 - j) * tga_width * req_comp;
- for( i = tga_width * req_comp; i > 0; --i )
- {
- unsigned char temp = tga_data[index1];
- tga_data[index1] = tga_data[index2];
- tga_data[index2] = temp;
- ++index1;
- ++index2;
- }
- }
- }
- // clear my palette, if I had one
- if( tga_palette != NULL )
- {
- free( tga_palette );
- }
- // the things I do to get rid of an error message, and yet keep
- // Microsoft's C compilers happy... [8^(
- tga_palette_start = tga_palette_len = tga_palette_bits =
- tga_x_origin = tga_y_origin = 0;
- // OK, done
- return tga_data;
+ // tga info
+ *x = tga_width;
+ *y = tga_height;
+ if ( (req_comp < 1) || (req_comp > 4) )
+ {
+ // just use whatever the file was
+ req_comp = tga_bits_per_pixel / 8;
+ *comp = req_comp;
+ } else
+ {
+ // force a new number of components
+ *comp = tga_bits_per_pixel/8;
+ }
+ tga_data = (unsigned char*)malloc( tga_width * tga_height * req_comp );
+ if (!tga_data) return epuc("outofmem", "Out of memory");
+
+ // skip to the data's starting position (offset usually = 0)
+ skip(s, tga_offset );
+ // do I need to load a palette?
+ if ( tga_indexed )
+ {
+ // any data to skip? (offset usually = 0)
+ skip(s, tga_palette_start );
+ // load the palette
+ tga_palette = (unsigned char*)malloc( tga_palette_len * tga_palette_bits / 8 );
+ if (!tga_palette) return epuc("outofmem", "Out of memory");
+ if (!getn(s, tga_palette, tga_palette_len * tga_palette_bits / 8 )) {
+ free(tga_data);
+ free(tga_palette);
+ return epuc("bad palette", "Corrupt TGA");
+ }
+ }
+ // load the data
+ trans_data[0] = trans_data[1] = trans_data[2] = trans_data[3] = 0;
+ for (i=0; i < tga_width * tga_height; ++i)
+ {
+ // if I'm in RLE mode, do I need to get a RLE chunk?
+ if ( tga_is_RLE )
+ {
+ if ( RLE_count == 0 )
+ {
+ // yep, get the next byte as a RLE command
+ int RLE_cmd = get8u(s);
+ RLE_count = 1 + (RLE_cmd & 127);
+ RLE_repeating = RLE_cmd >> 7;
+ read_next_pixel = 1;
+ } else if ( !RLE_repeating )
+ {
+ read_next_pixel = 1;
+ }
+ } else
+ {
+ read_next_pixel = 1;
+ }
+ // OK, if I need to read a pixel, do it now
+ if ( read_next_pixel )
+ {
+ // load however much data we did have
+ if ( tga_indexed )
+ {
+ // read in 1 byte, then perform the lookup
+ int pal_idx = get8u(s);
+ if ( pal_idx >= tga_palette_len )
+ {
+ // invalid index
+ pal_idx = 0;
+ }
+ pal_idx *= tga_bits_per_pixel / 8;
+ for (j = 0; j*8 < tga_bits_per_pixel; ++j)
+ {
+ raw_data[j] = tga_palette[pal_idx+j];
+ }
+ } else
+ {
+ // read in the data raw
+ for (j = 0; j*8 < tga_bits_per_pixel; ++j)
+ {
+ raw_data[j] = get8u(s);
+ }
+ }
+ // convert raw to the intermediate format
+ switch (tga_bits_per_pixel)
+ {
+ case 8:
+ // Luminous => RGBA
+ trans_data[0] = raw_data[0];
+ trans_data[1] = raw_data[0];
+ trans_data[2] = raw_data[0];
+ trans_data[3] = 255;
+ break;
+ case 16:
+ // Luminous,Alpha => RGBA
+ trans_data[0] = raw_data[0];
+ trans_data[1] = raw_data[0];
+ trans_data[2] = raw_data[0];
+ trans_data[3] = raw_data[1];
+ break;
+ case 24:
+ // BGR => RGBA
+ trans_data[0] = raw_data[2];
+ trans_data[1] = raw_data[1];
+ trans_data[2] = raw_data[0];
+ trans_data[3] = 255;
+ break;
+ case 32:
+ // BGRA => RGBA
+ trans_data[0] = raw_data[2];
+ trans_data[1] = raw_data[1];
+ trans_data[2] = raw_data[0];
+ trans_data[3] = raw_data[3];
+ break;
+ }
+ // clear the reading flag for the next pixel
+ read_next_pixel = 0;
+ } // end of reading a pixel
+ // convert to final format
+ switch (req_comp)
+ {
+ case 1:
+ // RGBA => Luminance
+ tga_data[i*req_comp+0] = compute_y(trans_data[0],trans_data[1],trans_data[2]);
+ break;
+ case 2:
+ // RGBA => Luminance,Alpha
+ tga_data[i*req_comp+0] = compute_y(trans_data[0],trans_data[1],trans_data[2]);
+ tga_data[i*req_comp+1] = trans_data[3];
+ break;
+ case 3:
+ // RGBA => RGB
+ tga_data[i*req_comp+0] = trans_data[0];
+ tga_data[i*req_comp+1] = trans_data[1];
+ tga_data[i*req_comp+2] = trans_data[2];
+ break;
+ case 4:
+ // RGBA => RGBA
+ tga_data[i*req_comp+0] = trans_data[0];
+ tga_data[i*req_comp+1] = trans_data[1];
+ tga_data[i*req_comp+2] = trans_data[2];
+ tga_data[i*req_comp+3] = trans_data[3];
+ break;
+ }
+ // in case we're in RLE mode, keep counting down
+ --RLE_count;
+ }
+ // do I need to invert the image?
+ if ( tga_inverted )
+ {
+ for (j = 0; j*2 < tga_height; ++j)
+ {
+ int index1 = j * tga_width * req_comp;
+ int index2 = (tga_height - 1 - j) * tga_width * req_comp;
+ for (i = tga_width * req_comp; i > 0; --i)
+ {
+ unsigned char temp = tga_data[index1];
+ tga_data[index1] = tga_data[index2];
+ tga_data[index2] = temp;
+ ++index1;
+ ++index2;
+ }
+ }
+ }
+ // clear my palette, if I had one
+ if ( tga_palette != NULL )
+ {
+ free( tga_palette );
+ }
+ // the things I do to get rid of an error message, and yet keep
+ // Microsoft's C compilers happy... [8^(
+ tga_palette_start = tga_palette_len = tga_palette_bits =
+ tga_x_origin = tga_y_origin = 0;
+ // OK, done
+ return tga_data;
}
-#ifndef STBI_NO_STDIO
-stbi_uc *stbi_tga_load (char const *filename, int *x, int *y, int *comp, int req_comp)
+static stbi_uc *stbi_tga_load(stbi *s, int *x, int *y, int *comp, int req_comp)
{
- stbi_uc *data;
- FILE *f = fopen(filename, "rb");
- if (!f) return NULL;
- data = stbi_tga_load_from_file(f, x,y,comp,req_comp);
- fclose(f);
- return data;
-}
-
-stbi_uc *stbi_tga_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp)
-{
- stbi s;
- start_file(&s, f);
- return tga_load(&s, x,y,comp,req_comp);
-}
-#endif
-
-stbi_uc *stbi_tga_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp)
-{
- stbi s;
- start_mem(&s, buffer, len);
- return tga_load(&s, x,y,comp,req_comp);
+ return tga_load(s,x,y,comp,req_comp);
}
// *************************************************************************************************
-// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicholas Schulz, tweaked by STB
+// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB
static int psd_test(stbi *s)
{
- if (get32(s) != 0x38425053) return 0; // "8BPS"
- else return 1;
+ if (get32(s) != 0x38425053) return 0; // "8BPS"
+ else return 1;
}
-#ifndef STBI_NO_STDIO
-int stbi_psd_test_file(FILE *f)
+static int stbi_psd_test(stbi *s)
{
- stbi s;
- int r,n = ftell(f);
- start_file(&s, f);
- r = psd_test(&s);
- fseek(f,n,SEEK_SET);
+ int r = psd_test(s);
+ stbi_rewind(s);
return r;
}
-#endif
-
-int stbi_psd_test_memory(stbi_uc const *buffer, int len)
-{
- stbi s;
- start_mem(&s, buffer, len);
- return psd_test(&s);
-}
static stbi_uc *psd_load(stbi *s, int *x, int *y, int *comp, int req_comp)
{
- int pixelCount;
- int channelCount, compression;
- int channel, i, count, len;
+ int pixelCount;
+ int channelCount, compression;
+ int channel, i, count, len;
int w,h;
uint8 *out;
- // Check identifier
- if (get32(s) != 0x38425053) // "8BPS"
- return epuc("not PSD", "Corrupt PSD image");
+ // Check identifier
+ if (get32(s) != 0x38425053) // "8BPS"
+ return epuc("not PSD", "Corrupt PSD image");
- // Check file type version.
- if (get16(s) != 1)
- return epuc("wrong version", "Unsupported version of PSD image");
+ // Check file type version.
+ if (get16(s) != 1)
+ return epuc("wrong version", "Unsupported version of PSD image");
- // Skip 6 reserved bytes.
- skip(s, 6 );
+ // Skip 6 reserved bytes.
+ skip(s, 6 );
- // Read the number of channels (R, G, B, A, etc).
- channelCount = get16(s);
- if (channelCount < 0 || channelCount > 16)
- return epuc("wrong channel count", "Unsupported number of channels in PSD image");
+ // Read the number of channels (R, G, B, A, etc).
+ channelCount = get16(s);
+ if (channelCount < 0 || channelCount > 16)
+ return epuc("wrong channel count", "Unsupported number of channels in PSD image");
- // Read the rows and columns of the image.
+ // Read the rows and columns of the image.
h = get32(s);
w = get32(s);
-
- // Make sure the depth is 8 bits.
- if (get16(s) != 8)
- return epuc("unsupported bit depth", "PSD bit depth is not 8 bit");
+
+ // Make sure the depth is 8 bits.
+ if (get16(s) != 8)
+ return epuc("unsupported bit depth", "PSD bit depth is not 8 bit");
- // Make sure the color mode is RGB.
- // Valid options are:
- // 0: Bitmap
- // 1: Grayscale
- // 2: Indexed color
- // 3: RGB color
- // 4: CMYK color
- // 7: Multichannel
- // 8: Duotone
- // 9: Lab color
- if (get16(s) != 3)
- return epuc("wrong color format", "PSD is not in RGB color format");
+ // Make sure the color mode is RGB.
+ // Valid options are:
+ // 0: Bitmap
+ // 1: Grayscale
+ // 2: Indexed color
+ // 3: RGB color
+ // 4: CMYK color
+ // 7: Multichannel
+ // 8: Duotone
+ // 9: Lab color
+ if (get16(s) != 3)
+ return epuc("wrong color format", "PSD is not in RGB color format");
- // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.)
- skip(s,get32(s) );
+ // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.)
+ skip(s,get32(s) );
- // Skip the image resources. (resolution, pen tool paths, etc)
- skip(s, get32(s) );
+ // Skip the image resources. (resolution, pen tool paths, etc)
+ skip(s, get32(s) );
- // Skip the reserved data.
- skip(s, get32(s) );
+ // Skip the reserved data.
+ skip(s, get32(s) );
- // Find out if the data is compressed.
- // Known values:
- // 0: no compression
- // 1: RLE compressed
- compression = get16(s);
- if (compression > 1)
- return epuc("bad compression", "PSD has an unknown compression format");
+ // Find out if the data is compressed.
+ // Known values:
+ // 0: no compression
+ // 1: RLE compressed
+ compression = get16(s);
+ if (compression > 1)
+ return epuc("bad compression", "PSD has an unknown compression format");
- // Create the destination image.
- out = (stbi_uc *) malloc(4 * w*h);
- if (!out) return epuc("outofmem", "Out of memory");
+ // Create the destination image.
+ out = (stbi_uc *) malloc(4 * w*h);
+ if (!out) return epuc("outofmem", "Out of memory");
pixelCount = w*h;
- // Initialize the data to zero.
- //memset( out, 0, pixelCount * 4 );
-
- // Finally, the image data.
- if (compression) {
- // RLE as used by .PSD and .TIFF
- // Loop until you get the number of unpacked bytes you are expecting:
- // Read the next source byte into n.
- // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally.
- // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times.
- // Else if n is 128, noop.
- // Endloop
+ // Initialize the data to zero.
+ //memset( out, 0, pixelCount * 4 );
+
+ // Finally, the image data.
+ if (compression) {
+ // RLE as used by .PSD and .TIFF
+ // Loop until you get the number of unpacked bytes you are expecting:
+ // Read the next source byte into n.
+ // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally.
+ // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times.
+ // Else if n is 128, noop.
+ // Endloop
- // The RLE-compressed data is preceeded by a 2-byte data count for each row in the data,
- // which we're going to just skip.
- skip(s, h * channelCount * 2 );
+ // The RLE-compressed data is preceeded by a 2-byte data count for each row in the data,
+ // which we're going to just skip.
+ skip(s, h * channelCount * 2 );
- // Read the RLE data by channel.
- for (channel = 0; channel < 4; channel++) {
- uint8 *p;
-
+ // Read the RLE data by channel.
+ for (channel = 0; channel < 4; channel++) {
+ uint8 *p;
+
p = out+channel;
- if (channel >= channelCount) {
- // Fill this channel with default data.
- for (i = 0; i < pixelCount; i++) *p = (channel == 3 ? 255 : 0), p += 4;
- } else {
- // Read the RLE data.
- count = 0;
- while (count < pixelCount) {
- len = get8(s);
- if (len == 128) {
- // No-op.
- } else if (len < 128) {
- // Copy next len+1 bytes literally.
- len++;
- count += len;
- while (len) {
- *p = (uint8)get8(s);
+ if (channel >= channelCount) {
+ // Fill this channel with default data.
+ for (i = 0; i < pixelCount; i++) *p = (channel == 3 ? 255 : 0), p += 4;
+ } else {
+ // Read the RLE data.
+ count = 0;
+ while (count < pixelCount) {
+ len = get8(s);
+ if (len == 128) {
+ // No-op.
+ } else if (len < 128) {
+ // Copy next len+1 bytes literally.
+ len++;
+ count += len;
+ while (len) {
+ *p = get8u(s);
p += 4;
- len--;
- }
- } else if (len > 128) {
- uint32 val;
- // Next -len+1 bytes in the dest are replicated from next source byte.
- // (Interpret len as a negative 8-bit int.)
- len ^= 0x0FF;
- len += 2;
- val = get8(s);
- count += len;
- while (len) {
- *p = (uint8)val;
+ len--;
+ }
+ } else if (len > 128) {
+ uint8 val;
+ // Next -len+1 bytes in the dest are replicated from next source byte.
+ // (Interpret len as a negative 8-bit int.)
+ len ^= 0x0FF;
+ len += 2;
+ val = get8u(s);
+ count += len;
+ while (len) {
+ *p = val;
p += 4;
- len--;
- }
- }
- }
- }
- }
-
- } else {
- // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...)
- // where each channel consists of an 8-bit value for each pixel in the image.
-
- // Read the data by channel.
- for (channel = 0; channel < 4; channel++) {
- uint8 *p;
-
+ len--;
+ }
+ }
+ }
+ }
+ }
+
+ } else {
+ // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...)
+ // where each channel consists of an 8-bit value for each pixel in the image.
+
+ // Read the data by channel.
+ for (channel = 0; channel < 4; channel++) {
+ uint8 *p;
+
p = out + channel;
- if (channel > channelCount) {
- // Fill this channel with default data.
- for (i = 0; i < pixelCount; i++) *p = channel == 3 ? 255 : 0, p += 4;
- } else {
- // Read the data.
- count = 0;
- for (i = 0; i < pixelCount; i++)
- *p = (uint8)get8(s), p += 4;
- }
- }
- }
+ if (channel > channelCount) {
+ // Fill this channel with default data.
+ for (i = 0; i < pixelCount; i++) *p = channel == 3 ? 255 : 0, p += 4;
+ } else {
+ // Read the data.
+ for (i = 0; i < pixelCount; i++)
+ *p = get8u(s), p += 4;
+ }
+ }
+ }
- if (req_comp && req_comp != 4) {
- out = convert_format(out, 4, req_comp, w, h);
- if (out == NULL) return out; // convert_format frees input on failure
- }
+ if (req_comp && req_comp != 4) {
+ out = convert_format(out, 4, req_comp, w, h);
+ if (out == NULL) return out; // convert_format frees input on failure
+ }
- if (comp) *comp = channelCount;
- *y = h;
- *x = w;
-
- return out;
+ if (comp) *comp = channelCount;
+ *y = h;
+ *x = w;
+
+ return out;
}
-#ifndef STBI_NO_STDIO
-stbi_uc *stbi_psd_load(char const *filename, int *x, int *y, int *comp, int req_comp)
+static stbi_uc *stbi_psd_load(stbi *s, int *x, int *y, int *comp, int req_comp)
{
- stbi_uc *data;
- FILE *f = fopen(filename, "rb");
- if (!f) return NULL;
- data = stbi_psd_load_from_file(f, x,y,comp,req_comp);
- fclose(f);
- return data;
+ return psd_load(s,x,y,comp,req_comp);
}
-stbi_uc *stbi_psd_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp)
-{
- stbi s;
- start_file(&s, f);
- return psd_load(&s, x,y,comp,req_comp);
-}
-#endif
+// *************************************************************************************************
+// Softimage PIC loader
+// by Tom Seddon
+//
+// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format
+// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/
-stbi_uc *stbi_psd_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp)
+static int pic_is4(stbi *s,const char *str)
{
- stbi s;
- start_mem(&s, buffer, len);
- return psd_load(&s, x,y,comp,req_comp);
+ int i;
+ for (i=0; i<4; ++i)
+ if (get8(s) != (stbi_uc)str[i])
+ return 0;
+
+ return 1;
+}
+
+static int pic_test(stbi *s)
+{
+ int i;
+
+ if (!pic_is4(s,"\x53\x80\xF6\x34"))
+ return 0;
+
+ for(i=0;i<84;++i)
+ get8(s);
+
+ if (!pic_is4(s,"PICT"))
+ return 0;
+
+ return 1;
+}
+
+typedef struct
+{
+ stbi_uc size,type,channel;
+} pic_packet_t;
+
+static stbi_uc *pic_readval(stbi *s, int channel, stbi_uc *dest)
+{
+ int mask=0x80, i;
+
+ for (i=0; i<4; ++i, mask>>=1) {
+ if (channel & mask) {
+ if (at_eof(s)) return epuc("bad file","PIC file too short");
+ dest[i]=get8u(s);
+ }
+ }
+
+ return dest;
+}
+
+static void pic_copyval(int channel,stbi_uc *dest,const stbi_uc *src)
+{
+ int mask=0x80,i;
+
+ for (i=0;i<4; ++i, mask>>=1)
+ if (channel&mask)
+ dest[i]=src[i];
+}
+
+static stbi_uc *pic_load2(stbi *s,int width,int height,int *comp, stbi_uc *result)
+{
+ int act_comp=0,num_packets=0,y,chained;
+ pic_packet_t packets[10];
+
+ // this will (should...) cater for even some bizarre stuff like having data
+ // for the same channel in multiple packets.
+ do {
+ pic_packet_t *packet;
+
+ if (num_packets==sizeof(packets)/sizeof(packets[0]))
+ return epuc("bad format","too many packets");
+
+ packet = &packets[num_packets++];
+
+ chained = get8(s);
+ packet->size = get8u(s);
+ packet->type = get8u(s);
+ packet->channel = get8u(s);
+
+ act_comp |= packet->channel;
+
+ if (at_eof(s)) return epuc("bad file","file too short (reading packets)");
+ if (packet->size != 8) return epuc("bad format","packet isn't 8bpp");
+ } while (chained);
+
+ *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel?
+
+ for(y=0; ytype) {
+ default:
+ return epuc("bad format","packet has bad compression type");
+
+ case 0: {//uncompressed
+ int x;
+
+ for(x=0;xchannel,dest))
+ return 0;
+ break;
+ }
+
+ case 1://Pure RLE
+ {
+ int left=width, i;
+
+ while (left>0) {
+ stbi_uc count,value[4];
+
+ count=get8u(s);
+ if (at_eof(s)) return epuc("bad file","file too short (pure read count)");
+
+ if (count > left)
+ count = (uint8) left;
+
+ if (!pic_readval(s,packet->channel,value)) return 0;
+
+ for(i=0; ichannel,dest,value);
+ left -= count;
+ }
+ }
+ break;
+
+ case 2: {//Mixed RLE
+ int left=width;
+ while (left>0) {
+ int count = get8(s), i;
+ if (at_eof(s)) return epuc("bad file","file too short (mixed read count)");
+
+ if (count >= 128) { // Repeated
+ stbi_uc value[4];
+ int i;
+
+ if (count==128)
+ count = get16(s);
+ else
+ count -= 127;
+ if (count > left)
+ return epuc("bad file","scanline overrun");
+
+ if (!pic_readval(s,packet->channel,value))
+ return 0;
+
+ for(i=0;ichannel,dest,value);
+ } else { // Raw
+ ++count;
+ if (count>left) return epuc("bad file","scanline overrun");
+
+ for(i=0;ichannel,dest))
+ return 0;
+ }
+ left-=count;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+static stbi_uc *pic_load(stbi *s,int *px,int *py,int *comp,int req_comp)
+{
+ stbi_uc *result;
+ int i, x,y;
+
+ for (i=0; i<92; ++i)
+ get8(s);
+
+ x = get16(s);
+ y = get16(s);
+ if (at_eof(s)) return epuc("bad file","file too short (pic header)");
+ if ((1 << 28) / x < y) return epuc("too large", "Image too large to decode");
+
+ get32(s); //skip `ratio'
+ get16(s); //skip `fields'
+ get16(s); //skip `pad'
+
+ // intermediate buffer is RGBA
+ result = (stbi_uc *) malloc(x*y*4);
+ memset(result, 0xff, x*y*4);
+
+ if (!pic_load2(s,x,y,comp, result)) {
+ free(result);
+ result=0;
+ }
+ *px = x;
+ *py = y;
+ if (req_comp == 0) req_comp = *comp;
+ result=convert_format(result,4,req_comp,x,y);
+
+ return result;
+}
+
+static int stbi_pic_test(stbi *s)
+{
+ int r = pic_test(s);
+ stbi_rewind(s);
+ return r;
+}
+
+static stbi_uc *stbi_pic_load(stbi *s, int *x, int *y, int *comp, int req_comp)
+{
+ return pic_load(s,x,y,comp,req_comp);
+}
+
+// *************************************************************************************************
+// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb
+typedef struct stbi_gif_lzw_struct {
+ int16 prefix;
+ uint8 first;
+ uint8 suffix;
+} stbi_gif_lzw;
+
+typedef struct stbi_gif_struct
+{
+ int w,h;
+ stbi_uc *out; // output buffer (always 4 components)
+ int flags, bgindex, ratio, transparent, eflags;
+ uint8 pal[256][4];
+ uint8 lpal[256][4];
+ stbi_gif_lzw codes[4096];
+ uint8 *color_table;
+ int parse, step;
+ int lflags;
+ int start_x, start_y;
+ int max_x, max_y;
+ int cur_x, cur_y;
+ int line_size;
+} stbi_gif;
+
+static int gif_test(stbi *s)
+{
+ int sz;
+ if (get8(s) != 'G' || get8(s) != 'I' || get8(s) != 'F' || get8(s) != '8') return 0;
+ sz = get8(s);
+ if (sz != '9' && sz != '7') return 0;
+ if (get8(s) != 'a') return 0;
+ return 1;
+}
+
+static int stbi_gif_test(stbi *s)
+{
+ int r = gif_test(s);
+ stbi_rewind(s);
+ return r;
+}
+
+static void stbi_gif_parse_colortable(stbi *s, uint8 pal[256][4], int num_entries, int transp)
+{
+ int i;
+ for (i=0; i < num_entries; ++i) {
+ pal[i][2] = get8u(s);
+ pal[i][1] = get8u(s);
+ pal[i][0] = get8u(s);
+ pal[i][3] = transp ? 0 : 255;
+ }
+}
+
+static int stbi_gif_header(stbi *s, stbi_gif *g, int *comp, int is_info)
+{
+ uint8 version;
+ if (get8(s) != 'G' || get8(s) != 'I' || get8(s) != 'F' || get8(s) != '8')
+ return e("not GIF", "Corrupt GIF");
+
+ version = get8u(s);
+ if (version != '7' && version != '9') return e("not GIF", "Corrupt GIF");
+ if (get8(s) != 'a') return e("not GIF", "Corrupt GIF");
+
+ failure_reason = "";
+ g->w = get16le(s);
+ g->h = get16le(s);
+ g->flags = get8(s);
+ g->bgindex = get8(s);
+ g->ratio = get8(s);
+ g->transparent = -1;
+
+ if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments
+
+ if (is_info) return 1;
+
+ if (g->flags & 0x80)
+ stbi_gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1);
+
+ return 1;
+}
+
+static int stbi_gif_info_raw(stbi *s, int *x, int *y, int *comp)
+{
+ stbi_gif g;
+ if (!stbi_gif_header(s, &g, comp, 1)) {
+ stbi_rewind( s );
+ return 0;
+ }
+ if (x) *x = g.w;
+ if (y) *y = g.h;
+ return 1;
+}
+
+static void stbi_out_gif_code(stbi_gif *g, uint16 code)
+{
+ uint8 *p, *c;
+
+ // recurse to decode the prefixes, since the linked-list is backwards,
+ // and working backwards through an interleaved image would be nasty
+ if (g->codes[code].prefix >= 0)
+ stbi_out_gif_code(g, g->codes[code].prefix);
+
+ if (g->cur_y >= g->max_y) return;
+
+ p = &g->out[g->cur_x + g->cur_y];
+ c = &g->color_table[g->codes[code].suffix * 4];
+
+ if (c[3] >= 128) {
+ p[0] = c[2];
+ p[1] = c[1];
+ p[2] = c[0];
+ p[3] = c[3];
+ }
+ g->cur_x += 4;
+
+ if (g->cur_x >= g->max_x) {
+ g->cur_x = g->start_x;
+ g->cur_y += g->step;
+
+ while (g->cur_y >= g->max_y && g->parse > 0) {
+ g->step = (1 << g->parse) * g->line_size;
+ g->cur_y = g->start_y + (g->step >> 1);
+ --g->parse;
+ }
+ }
+}
+
+static uint8 *stbi_process_gif_raster(stbi *s, stbi_gif *g)
+{
+ uint8 lzw_cs;
+ int32 len, code;
+ uint32 first;
+ int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear;
+ stbi_gif_lzw *p;
+
+ lzw_cs = get8u(s);
+ clear = 1 << lzw_cs;
+ first = 1;
+ codesize = lzw_cs + 1;
+ codemask = (1 << codesize) - 1;
+ bits = 0;
+ valid_bits = 0;
+ for (code = 0; code < clear; code++) {
+ g->codes[code].prefix = -1;
+ g->codes[code].first = (uint8) code;
+ g->codes[code].suffix = (uint8) code;
+ }
+
+ // support no starting clear code
+ avail = clear+2;
+ oldcode = -1;
+
+ len = 0;
+ for(;;) {
+ if (valid_bits < codesize) {
+ if (len == 0) {
+ len = get8(s); // start new block
+ if (len == 0)
+ return g->out;
+ }
+ --len;
+ bits |= (int32) get8(s) << valid_bits;
+ valid_bits += 8;
+ } else {
+ int32 code = bits & codemask;
+ bits >>= codesize;
+ valid_bits -= codesize;
+ // @OPTIMIZE: is there some way we can accelerate the non-clear path?
+ if (code == clear) { // clear code
+ codesize = lzw_cs + 1;
+ codemask = (1 << codesize) - 1;
+ avail = clear + 2;
+ oldcode = -1;
+ first = 0;
+ } else if (code == clear + 1) { // end of stream code
+ skip(s, len);
+ while ((len = get8(s)) > 0)
+ skip(s,len);
+ return g->out;
+ } else if (code <= avail) {
+ if (first) return epuc("no clear code", "Corrupt GIF");
+
+ if (oldcode >= 0) {
+ p = &g->codes[avail++];
+ if (avail > 4096) return epuc("too many codes", "Corrupt GIF");
+ p->prefix = (int16) oldcode;
+ p->first = g->codes[oldcode].first;
+ p->suffix = (code == avail) ? p->first : g->codes[code].first;
+ } else if (code == avail)
+ return epuc("illegal code in raster", "Corrupt GIF");
+
+ stbi_out_gif_code(g, (uint16) code);
+
+ if ((avail & codemask) == 0 && avail <= 0x0FFF) {
+ codesize++;
+ codemask = (1 << codesize) - 1;
+ }
+
+ oldcode = code;
+ } else {
+ return epuc("illegal code in raster", "Corrupt GIF");
+ }
+ }
+ }
+}
+
+static void stbi_fill_gif_background(stbi_gif *g)
+{
+ int i;
+ uint8 *c = g->pal[g->bgindex];
+ // @OPTIMIZE: write a dword at a time
+ for (i = 0; i < g->w * g->h * 4; i += 4) {
+ uint8 *p = &g->out[i];
+ p[0] = c[2];
+ p[1] = c[1];
+ p[2] = c[0];
+ p[3] = c[3];
+ }
+}
+
+// this function is designed to support animated gifs, although stb_image doesn't support it
+static uint8 *stbi_gif_load_next(stbi *s, stbi_gif *g, int *comp, int req_comp)
+{
+ int i;
+ uint8 *old_out = 0;
+
+ if (g->out == 0) {
+ if (!stbi_gif_header(s, g, comp,0)) return 0; // failure_reason set by stbi_gif_header
+ g->out = (uint8 *) malloc(4 * g->w * g->h);
+ if (g->out == 0) return epuc("outofmem", "Out of memory");
+ stbi_fill_gif_background(g);
+ } else {
+ // animated-gif-only path
+ if (((g->eflags & 0x1C) >> 2) == 3) {
+ old_out = g->out;
+ g->out = (uint8 *) malloc(4 * g->w * g->h);
+ if (g->out == 0) return epuc("outofmem", "Out of memory");
+ memcpy(g->out, old_out, g->w*g->h*4);
+ }
+ }
+
+ for (;;) {
+ switch (get8(s)) {
+ case 0x2C: /* Image Descriptor */
+ {
+ int32 x, y, w, h;
+ uint8 *o;
+
+ x = get16le(s);
+ y = get16le(s);
+ w = get16le(s);
+ h = get16le(s);
+ if (((x + w) > (g->w)) || ((y + h) > (g->h)))
+ return epuc("bad Image Descriptor", "Corrupt GIF");
+
+ g->line_size = g->w * 4;
+ g->start_x = x * 4;
+ g->start_y = y * g->line_size;
+ g->max_x = g->start_x + w * 4;
+ g->max_y = g->start_y + h * g->line_size;
+ g->cur_x = g->start_x;
+ g->cur_y = g->start_y;
+
+ g->lflags = get8(s);
+
+ if (g->lflags & 0x40) {
+ g->step = 8 * g->line_size; // first interlaced spacing
+ g->parse = 3;
+ } else {
+ g->step = g->line_size;
+ g->parse = 0;
+ }
+
+ if (g->lflags & 0x80) {
+ stbi_gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1);
+ g->color_table = (uint8 *) g->lpal;
+ } else if (g->flags & 0x80) {
+ for (i=0; i < 256; ++i) // @OPTIMIZE: reset only the previous transparent
+ g->pal[i][3] = 255;
+ if (g->transparent >= 0 && (g->eflags & 0x01))
+ g->pal[g->transparent][3] = 0;
+ g->color_table = (uint8 *) g->pal;
+ } else
+ return epuc("missing color table", "Corrupt GIF");
+
+ o = stbi_process_gif_raster(s, g);
+ if (o == NULL) return NULL;
+
+ if (req_comp && req_comp != 4)
+ o = convert_format(o, 4, req_comp, g->w, g->h);
+ return o;
+ }
+
+ case 0x21: // Comment Extension.
+ {
+ int len;
+ if (get8(s) == 0xF9) { // Graphic Control Extension.
+ len = get8(s);
+ if (len == 4) {
+ g->eflags = get8(s);
+ get16le(s); // delay
+ g->transparent = get8(s);
+ } else {
+ skip(s, len);
+ break;
+ }
+ }
+ while ((len = get8(s)) != 0)
+ skip(s, len);
+ break;
+ }
+
+ case 0x3B: // gif stream termination code
+ return (uint8 *) 1;
+
+ default:
+ return epuc("unknown code", "Corrupt GIF");
+ }
+ }
+}
+
+static stbi_uc *stbi_gif_load(stbi *s, int *x, int *y, int *comp, int req_comp)
+{
+ uint8 *u = 0;
+ stbi_gif g={0};
+
+ u = stbi_gif_load_next(s, &g, comp, req_comp);
+ if (u == (void *) 1) u = 0; // end of animated gif marker
+ if (u) {
+ *x = g.w;
+ *y = g.h;
+ }
+
+ return u;
+}
+
+static int stbi_gif_info(stbi *s, int *x, int *y, int *comp)
+{
+ return stbi_gif_info_raw(s,x,y,comp);
}
@@ -3585,27 +4201,15 @@ static int hdr_test(stbi *s)
for (i=0; signature[i]; ++i)
if (get8(s) != signature[i])
return 0;
- return 1;
+ return 1;
}
-int stbi_hdr_test_memory(stbi_uc const *buffer, int len)
+static int stbi_hdr_test(stbi* s)
{
- stbi s;
- start_mem(&s, buffer, len);
- return hdr_test(&s);
-}
-
-#ifndef STBI_NO_STDIO
-int stbi_hdr_test_file(FILE *f)
-{
- stbi s;
- int r,n = ftell(f);
- start_file(&s, f);
- r = hdr_test(&s);
- fseek(f,n,SEEK_SET);
+ int r = hdr_test(s);
+ stbi_rewind(s);
return r;
}
-#endif
#define HDR_BUFLEN 1024
static char *hdr_gettoken(stbi *z, char *buffer)
@@ -3613,29 +4217,29 @@ static char *hdr_gettoken(stbi *z, char *buffer)
int len=0;
char c = '\0';
- c = (char)get8(z);
+ c = (char) get8(z);
- while (!at_eof(z) && c != '\n') {
- buffer[len++] = c;
+ while (!at_eof(z) && c != '\n') {
+ buffer[len++] = c;
if (len == HDR_BUFLEN-1) {
// flush to end of line
while (!at_eof(z) && get8(z) != '\n')
;
break;
}
- c = (char)get8(z);
- }
+ c = (char) get8(z);
+ }
buffer[len] = 0;
- return buffer;
+ return buffer;
}
static void hdr_convert(float *output, stbi_uc *input, int req_comp)
{
- if( input[3] != 0 ) {
+ if ( input[3] != 0 ) {
float f1;
- // Exponent
- f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8));
+ // Exponent
+ f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8));
if (req_comp <= 2)
output[0] = (input[0] + input[1] + input[2]) * f1 / 3;
else {
@@ -3645,7 +4249,7 @@ static void hdr_convert(float *output, stbi_uc *input, int req_comp)
}
if (req_comp == 2) output[1] = 1;
if (req_comp == 4) output[3] = 1;
- } else {
+ } else {
switch (req_comp) {
case 4: output[3] = 1; /* fallthrough */
case 3: output[0] = output[1] = output[2] = 0;
@@ -3654,35 +4258,34 @@ static void hdr_convert(float *output, stbi_uc *input, int req_comp)
case 1: output[0] = 0;
break;
}
- }
+ }
}
-
static float *hdr_load(stbi *s, int *x, int *y, int *comp, int req_comp)
{
char buffer[HDR_BUFLEN];
- char *token;
- int valid = 0;
- int width, height;
+ char *token;
+ int valid = 0;
+ int width, height;
stbi_uc *scanline;
- float *hdr_data;
- int len;
- unsigned char count, value;
- int i, j, k, c1,c2, z;
+ float *hdr_data;
+ int len;
+ unsigned char count, value;
+ int i, j, k, c1,c2, z;
- // Check identifier
- if (strcmp(hdr_gettoken(s,buffer), "#?RADIANCE") != 0)
- return epf("not HDR", "Corrupt HDR image");
-
- // Parse header
- for(;;) {
- token = hdr_gettoken(s,buffer);
+ // Check identifier
+ if (strcmp(hdr_gettoken(s,buffer), "#?RADIANCE") != 0)
+ return epf("not HDR", "Corrupt HDR image");
+
+ // Parse header
+ for(;;) {
+ token = hdr_gettoken(s,buffer);
if (token[0] == 0) break;
- if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1;
+ if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1;
}
- if (!valid) return epf("unsupported format", "Unsupported HDR format");
+ if (!valid) return epf("unsupported format", "Unsupported HDR format");
// Parse width and height
// can't use sscanf() if we're not using stdio!
@@ -3695,19 +4298,19 @@ static float *hdr_load(stbi *s, int *x, int *y, int *comp, int req_comp)
token += 3;
width = strtol(token, NULL, 10);
- *x = width;
- *y = height;
+ *x = width;
+ *y = height;
*comp = 3;
- if (req_comp == 0) req_comp = 3;
+ if (req_comp == 0) req_comp = 3;
- // Read data
- hdr_data = (float *) malloc(height * width * req_comp * sizeof(float));
+ // Read data
+ hdr_data = (float *) malloc(height * width * req_comp * sizeof(float));
- // Load image data
+ // Load image data
// image data is stored as some number of sca
- if( width < 8 || width >= 32768) {
- // Read flat data
+ if ( width < 8 || width >= 32768) {
+ // Read flat data
for (j=0; j < height; ++j) {
for (i=0; i < width; ++i) {
stbi_uc rgbe[4];
@@ -3716,178 +4319,354 @@ static float *hdr_load(stbi *s, int *x, int *y, int *comp, int req_comp)
hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp);
}
}
- } else {
- // Read RLE-encoded data
- scanline = NULL;
+ } else {
+ // Read RLE-encoded data
+ scanline = NULL;
- for (j = 0; j < height; ++j) {
+ for (j = 0; j < height; ++j) {
c1 = get8(s);
c2 = get8(s);
len = get8(s);
if (c1 != 2 || c2 != 2 || (len & 0x80)) {
// not run-length encoded, so we have to actually use THIS data as a decoded
// pixel (note this can't be a valid pixel--one of RGB must be >= 128)
- stbi_uc rgbe[4] = { (stbi_uc)c1,(stbi_uc)c2,(stbi_uc)len, (stbi_uc)get8(s) };
+ uint8 rgbe[4];
+ rgbe[0] = (uint8) c1;
+ rgbe[1] = (uint8) c2;
+ rgbe[2] = (uint8) len;
+ rgbe[3] = (uint8) get8u(s);
hdr_convert(hdr_data, rgbe, req_comp);
i = 1;
j = 0;
free(scanline);
- goto main_decode_loop; // yes, this is fucking insane; blame the fucking insane format
+ goto main_decode_loop; // yes, this makes no sense
}
len <<= 8;
len |= get8(s);
if (len != width) { free(hdr_data); free(scanline); return epf("invalid decoded scanline length", "corrupt HDR"); }
if (scanline == NULL) scanline = (stbi_uc *) malloc(width * 4);
-
- for (k = 0; k < 4; ++k) {
- i = 0;
- while (i < width) {
- count = (unsigned char)get8(s);
- if (count > 128) {
- // Run
- value = (unsigned char)get8(s);
+
+ for (k = 0; k < 4; ++k) {
+ i = 0;
+ while (i < width) {
+ count = get8u(s);
+ if (count > 128) {
+ // Run
+ value = get8u(s);
count -= 128;
- for (z = 0; z < count; ++z)
- scanline[i++ * 4 + k] = value;
- } else {
- // Dump
- for (z = 0; z < count; ++z)
- scanline[i++ * 4 + k] = (stbi_uc)get8(s);
- }
- }
- }
+ for (z = 0; z < count; ++z)
+ scanline[i++ * 4 + k] = value;
+ } else {
+ // Dump
+ for (z = 0; z < count; ++z)
+ scanline[i++ * 4 + k] = get8u(s);
+ }
+ }
+ }
for (i=0; i < width; ++i)
hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp);
- }
+ }
free(scanline);
- }
+ }
return hdr_data;
}
-#ifndef STBI_NO_STDIO
-float *stbi_hdr_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp)
+static float *stbi_hdr_load(stbi *s, int *x, int *y, int *comp, int req_comp)
{
- stbi s;
- start_file(&s,f);
- return hdr_load(&s,x,y,comp,req_comp);
-}
-#endif
-
-float *stbi_hdr_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp)
-{
- stbi s;
- start_mem(&s,buffer, len);
- return hdr_load(&s,x,y,comp,req_comp);
+ return hdr_load(s,x,y,comp,req_comp);
}
+static int stbi_hdr_info(stbi *s, int *x, int *y, int *comp)
+{
+ char buffer[HDR_BUFLEN];
+ char *token;
+ int valid = 0;
+
+ if (strcmp(hdr_gettoken(s,buffer), "#?RADIANCE") != 0) {
+ stbi_rewind( s );
+ return 0;
+ }
+
+ for(;;) {
+ token = hdr_gettoken(s,buffer);
+ if (token[0] == 0) break;
+ if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1;
+ }
+
+ if (!valid) {
+ stbi_rewind( s );
+ return 0;
+ }
+ token = hdr_gettoken(s,buffer);
+ if (strncmp(token, "-Y ", 3)) {
+ stbi_rewind( s );
+ return 0;
+ }
+ token += 3;
+ *y = strtol(token, &token, 10);
+ while (*token == ' ') ++token;
+ if (strncmp(token, "+X ", 3)) {
+ stbi_rewind( s );
+ return 0;
+ }
+ token += 3;
+ *x = strtol(token, NULL, 10);
+ *comp = 3;
+ return 1;
+}
#endif // STBI_NO_HDR
-/////////////////////// write image ///////////////////////
-
-#ifndef STBI_NO_WRITE
-
-static void write8(FILE *f, int x) { uint8 z = (uint8) x; fwrite(&z,1,1,f); }
-
-static void writefv(FILE *f, const char *fmt, va_list v)
+static int stbi_bmp_info(stbi *s, int *x, int *y, int *comp)
{
- while (*fmt) {
- switch (*fmt++) {
- case ' ': break;
- case '1': { uint8 x = (uint8)va_arg(v, int); write8(f,x); break; }
- case '2': { int16 x = (int16)va_arg(v, int); write8(f,x); write8(f,x>>8); break; }
- case '4': { int32 x = va_arg(v, int); write8(f,x); write8(f,x>>8); write8(f,x>>16); write8(f,x>>24); break; }
- default:
- assert(0);
- va_end(v);
- return;
+ int hsz;
+ if (get8(s) != 'B' || get8(s) != 'M') {
+ stbi_rewind( s );
+ return 0;
+ }
+ skip(s,12);
+ hsz = get32le(s);
+ if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108) {
+ stbi_rewind( s );
+ return 0;
+ }
+ if (hsz == 12) {
+ *x = get16le(s);
+ *y = get16le(s);
+ } else {
+ *x = get32le(s);
+ *y = get32le(s);
+ }
+ if (get16le(s) != 1) {
+ stbi_rewind( s );
+ return 0;
+ }
+ *comp = get16le(s) / 8;
+ return 1;
+}
+
+static int stbi_psd_info(stbi *s, int *x, int *y, int *comp)
+{
+ int channelCount;
+ if (get32(s) != 0x38425053) {
+ stbi_rewind( s );
+ return 0;
+ }
+ if (get16(s) != 1) {
+ stbi_rewind( s );
+ return 0;
+ }
+ skip(s, 6);
+ channelCount = get16(s);
+ if (channelCount < 0 || channelCount > 16) {
+ stbi_rewind( s );
+ return 0;
+ }
+ *y = get32(s);
+ *x = get32(s);
+ if (get16(s) != 8) {
+ stbi_rewind( s );
+ return 0;
+ }
+ if (get16(s) != 3) {
+ stbi_rewind( s );
+ return 0;
+ }
+ *comp = 4;
+ return 1;
+}
+
+static int stbi_pic_info(stbi *s, int *x, int *y, int *comp)
+{
+ int act_comp=0,num_packets=0,chained;
+ pic_packet_t packets[10];
+
+ skip(s, 92);
+
+ *x = get16(s);
+ *y = get16(s);
+ if (at_eof(s)) return 0;
+ if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) {
+ stbi_rewind( s );
+ return 0;
+ }
+
+ skip(s, 8);
+
+ do {
+ pic_packet_t *packet;
+
+ if (num_packets==sizeof(packets)/sizeof(packets[0]))
+ return 0;
+
+ packet = &packets[num_packets++];
+ chained = get8(s);
+ packet->size = get8u(s);
+ packet->type = get8u(s);
+ packet->channel = get8u(s);
+ act_comp |= packet->channel;
+
+ if (at_eof(s)) {
+ stbi_rewind( s );
+ return 0;
}
- }
-}
-
-static void writef(FILE *f, const char *fmt, ...)
-{
- va_list v;
- va_start(v, fmt);
- writefv(f,fmt,v);
- va_end(v);
-}
-
-static void write_pixels(FILE *f, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad)
-{
- uint8 bg[3] = { 255, 0, 255}, px[3];
- uint32 zero = 0;
- int i,j,k, j_end;
-
- if (vdir < 0)
- j_end = -1, j = y-1;
- else
- j_end = y, j = 0;
-
- for (; j != j_end; j += vdir) {
- for (i=0; i < x; ++i) {
- uint8 *d = (uint8 *) data + (j*x+i)*comp;
- if (write_alpha < 0)
- fwrite(&d[comp-1], 1, 1, f);
- switch (comp) {
- case 1:
- case 2: writef(f, "111", d[0],d[0],d[0]);
- break;
- case 4:
- if (!write_alpha) {
- for (k=0; k < 3; ++k)
- px[k] = bg[k] + ((d[k] - bg[k]) * d[3])/255;
- writef(f, "111", px[1-rgb_dir],px[1],px[1+rgb_dir]);
- break;
- }
- /* FALLTHROUGH */
- case 3:
- writef(f, "111", d[1-rgb_dir],d[1],d[1+rgb_dir]);
- break;
- }
- if (write_alpha > 0)
- fwrite(&d[comp-1], 1, 1, f);
+ if (packet->size != 8) {
+ stbi_rewind( s );
+ return 0;
}
- fwrite(&zero,scanline_pad,1,f);
- }
+ } while (chained);
+
+ *comp = (act_comp & 0x10 ? 4 : 3);
+
+ return 1;
}
-static int outfile(char const *filename, int rgb_dir, int vdir, int x, int y, int comp, void *data, int alpha, int pad, const char *fmt, ...)
+static int stbi_info_main(stbi *s, int *x, int *y, int *comp)
{
- FILE *f = fopen(filename, "wb");
- if (f) {
- va_list v;
- va_start(v, fmt);
- writefv(f, fmt, v);
- va_end(v);
- write_pixels(f,rgb_dir,vdir,x,y,comp,data,alpha,pad);
- fclose(f);
- }
- return f != NULL;
+ if (stbi_jpeg_info(s, x, y, comp))
+ return 1;
+ if (stbi_png_info(s, x, y, comp))
+ return 1;
+ if (stbi_gif_info(s, x, y, comp))
+ return 1;
+ if (stbi_bmp_info(s, x, y, comp))
+ return 1;
+ if (stbi_psd_info(s, x, y, comp))
+ return 1;
+ if (stbi_pic_info(s, x, y, comp))
+ return 1;
+ #ifndef STBI_NO_HDR
+ if (stbi_hdr_info(s, x, y, comp))
+ return 1;
+ #endif
+ // test tga last because it's a crappy test!
+ if (stbi_tga_info(s, x, y, comp))
+ return 1;
+ return e("unknown image type", "Image not of any known type, or corrupt");
}
-int stbi_write_bmp(char const *filename, int x, int y, int comp, void *data)
+#ifndef STBI_NO_STDIO
+int stbi_info(char const *filename, int *x, int *y, int *comp)
{
- int pad = (-x*3) & 3;
- return outfile(filename,-1,-1,x,y,comp,data,0,pad,
- "11 4 22 4" "4 44 22 444444",
- 'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header
- 40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header
+ FILE *f = fopen(filename, "rb");
+ int result;
+ if (!f) return e("can't fopen", "Unable to open file");
+ result = stbi_info_from_file(f, x, y, comp);
+ fclose(f);
+ return result;
}
-int stbi_write_tga(char const *filename, int x, int y, int comp, void *data)
+int stbi_info_from_file(FILE *f, int *x, int *y, int *comp)
{
- int has_alpha = !(comp & 1);
- return outfile(filename, -1,-1, x, y, comp, data, has_alpha, 0,
- "111 221 2222 11", 0,0,2, 0,0,0, 0,0,x,y, 24+8*has_alpha, 8*has_alpha);
+ int r;
+ stbi s;
+ long pos = ftell(f);
+ start_file(&s, f);
+ r = stbi_info_main(&s,x,y,comp);
+ fseek(f,pos,SEEK_SET);
+ return r;
+}
+#endif // !STBI_NO_STDIO
+
+int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp)
+{
+ stbi s;
+ start_mem(&s,buffer,len);
+ return stbi_info_main(&s,x,y,comp);
}
-// any other image formats that do interleaved rgb data?
-// PNG: requires adler32,crc32 -- significant amount of code
-// PSD: no, channels output separately
-// TIFF: no, stripwise-interleaved... i think
-
-#endif // STBI_NO_WRITE
+int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp)
+{
+ stbi s;
+ start_callbacks(&s, (stbi_io_callbacks *) c, user);
+ return stbi_info_main(&s,x,y,comp);
+}
#endif // STBI_HEADER_FILE_ONLY
+/*
+ revision history:
+ 1.33 (2011-07-14)
+ make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements
+ 1.32 (2011-07-13)
+ support for "info" function for all supported filetypes (SpartanJ)
+ 1.31 (2011-06-20)
+ a few more leak fixes, bug in PNG handling (SpartanJ)
+ 1.30 (2011-06-11)
+ added ability to load files via callbacks to accomidate custom input streams (Ben Wenger)
+ removed deprecated format-specific test/load functions
+ removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway
+ error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha)
+ fix inefficiency in decoding 32-bit BMP (David Woo)
+ 1.29 (2010-08-16)
+ various warning fixes from Aurelien Pocheville
+ 1.28 (2010-08-01)
+ fix bug in GIF palette transparency (SpartanJ)
+ 1.27 (2010-08-01)
+ cast-to-uint8 to fix warnings
+ 1.26 (2010-07-24)
+ fix bug in file buffering for PNG reported by SpartanJ
+ 1.25 (2010-07-17)
+ refix trans_data warning (Won Chun)
+ 1.24 (2010-07-12)
+ perf improvements reading from files on platforms with lock-heavy fgetc()
+ minor perf improvements for jpeg
+ deprecated type-specific functions so we'll get feedback if they're needed
+ attempt to fix trans_data warning (Won Chun)
+ 1.23 fixed bug in iPhone support
+ 1.22 (2010-07-10)
+ removed image *writing* support
+ stbi_info support from Jetro Lauha
+ GIF support from Jean-Marc Lienher
+ iPhone PNG-extensions from James Brown
+ warning-fixes from Nicolas Schulz and Janez Zemva (i.e. Janez (U+017D)emva)
+ 1.21 fix use of 'uint8' in header (reported by jon blow)
+ 1.20 added support for Softimage PIC, by Tom Seddon
+ 1.19 bug in interlaced PNG corruption check (found by ryg)
+ 1.18 2008-08-02
+ fix a threading bug (local mutable static)
+ 1.17 support interlaced PNG
+ 1.16 major bugfix - convert_format converted one too many pixels
+ 1.15 initialize some fields for thread safety
+ 1.14 fix threadsafe conversion bug
+ header-file-only version (#define STBI_HEADER_FILE_ONLY before including)
+ 1.13 threadsafe
+ 1.12 const qualifiers in the API
+ 1.11 Support installable IDCT, colorspace conversion routines
+ 1.10 Fixes for 64-bit (don't use "unsigned long")
+ optimized upsampling by Fabian "ryg" Giesen
+ 1.09 Fix format-conversion for PSD code (bad global variables!)
+ 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz
+ 1.07 attempt to fix C++ warning/errors again
+ 1.06 attempt to fix C++ warning/errors again
+ 1.05 fix TGA loading to return correct *comp and use good luminance calc
+ 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free
+ 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR
+ 1.02 support for (subset of) HDR files, float interface for preferred access to them
+ 1.01 fix bug: possible bug in handling right-side up bmps... not sure
+ fix bug: the stbi_bmp_load() and stbi_tga_load() functions didn't work at all
+ 1.00 interface to zlib that skips zlib header
+ 0.99 correct handling of alpha in palette
+ 0.98 TGA loader by lonesock; dynamically add loaders (untested)
+ 0.97 jpeg errors on too large a file; also catch another malloc failure
+ 0.96 fix detection of invalid v value - particleman@mollyrocket forum
+ 0.95 during header scan, seek to markers in case of padding
+ 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same
+ 0.93 handle jpegtran output; verbose errors
+ 0.92 read 4,8,16,24,32-bit BMP files of several formats
+ 0.91 output 24-bit Windows 3.0 BMP files
+ 0.90 fix a few more warnings; bump version number to approach 1.0
+ 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd
+ 0.60 fix compiling as c++
+ 0.59 fix warnings: merge Dave Moore's -Wall fixes
+ 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian
+ 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available
+ 0.56 fix bug: zlib uncompressed mode len vs. nlen
+ 0.55 fix bug: restart_interval not initialized to 0
+ 0.54 allow NULL for 'int *comp'
+ 0.53 fix bug in png 3->4; speedup png decoding
+ 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments
+ 0.51 obey req_comp requests, 1-component jpegs return as 1-component,
+ on 'test' only check type, not whether we support this variant
+ 0.50 first released version
+*/
diff --git a/RecastDemo/Contrib/stb_truetype.h b/RecastDemo/Contrib/stb_truetype.h
index fd72578..12aa573 100644
--- a/RecastDemo/Contrib/stb_truetype.h
+++ b/RecastDemo/Contrib/stb_truetype.h
@@ -1,1806 +1,2063 @@
-// 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
-#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
- #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
- #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
- #define STBTT_malloc(x,u) malloc(x)
- #define STBTT_free(x,u) free(x)
- #endif
-
- #ifndef STBTT_assert
- #include
- #define STBTT_assert(x) assert(x)
- #endif
-
- #ifndef STBTT_strlen
- #include
- #define STBTT_strlen(x) strlen(x)
- #endif
-
- #ifndef STBTT_memcpy
- #include
- #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.7 - public domain
+// authored from 2009-2013 by 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? (no longer patented)
+// cleartype-style AA?
+// optimize: use simple memory allocator for intermediates
+// optimize: build edge-list directly from curves
+// optimize: rasterize directly from curves?
+//
+// ADDITIONAL CONTRIBUTORS
+//
+// Mikko Mononen: compound shape support, more cmap formats
+// Tor Andersson: kerning, subpixel rendering
+//
+// Bug/warning reports:
+// "Zer" on mollyrocket (with fix)
+// Cass Everitt
+// stoiko (Haemimont Games)
+// Brian Hook
+// Walter van Niftrik
+//
+// VERSION HISTORY
+//
+// 0.7 (2013-09-25) bugfix: subpixel glyph bug fixed in 0.5 had come back
+// 0.6c (2012-07-24) improve documentation
+// 0.6b (2012-07-20) fix a few more warnings
+// 0.6 (2012-07-17) fix warnings; added stbtt_ScaleForMappingEmToPixels,
+// stbtt_GetFontBoundingBox, stbtt_IsGlyphEmpty
+// 0.5 (2011-12-09) bugfixes:
+// subpixel glyph renderer computed wrong bounding box
+// first vertex of shape can be off-curve (FreeSans)
+// 0.4b (2011-12-03) fixed an error in the font baking example
+// 0.4 (2011-12-01) kerning, subpixel rendering (tor)
+// bugfixes for:
+// codepoint-to-glyph conversion using table fmt=12
+// codepoint-to-glyph conversion using table fmt=4
+// stbtt_GetBakedQuad with non-square texture (Zer)
+// updated Hello World! sample to use kerning and subpixel
+// fixed some warnings
+// 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
+//
+// LICENSE
+//
+// This software is in the public domain. Where that dedication is not
+// recognized, you are granted a perpetual, irrevokable license to copy
+// and modify this file as you see fit.
+//
+// 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.
+//
+// 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()
+// stbtt_GetCodepointKernAdvance()
+//
+// ADDITIONAL DOCUMENTATION
+//
+// Immediately after this block comment are a series of sample programs.
+//
+// After the sample programs is the "header file" section. This section
+// includes documentation for each API function.
+//
+// Some important concepts to understand to use this library:
+//
+// Codepoint
+// Characters are defined by unicode codepoints, e.g. 65 is
+// uppercase A, 231 is lowercase c with a cedilla, 0x7e30 is
+// the hiragana for "ma".
+//
+// Glyph
+// A visual character shape (every codepoint is rendered as
+// some glyph)
+//
+// Glyph index
+// A font-specific integer ID representing a glyph
+//
+// Baseline
+// Glyph shapes are defined relative to a baseline, which is the
+// bottom of uppercase characters. Characters extend both above
+// and below the baseline.
+//
+// Current Point
+// As you draw text to the screen, you keep track of a "current point"
+// which is the origin of each character. The current point's vertical
+// position is the baseline. Even "baked fonts" use this model.
+//
+// Vertical Font Metrics
+// The vertical qualities of the font, used to vertically position
+// and space the characters. See docs for stbtt_GetFontVMetrics.
+//
+// Font Size in Pixels or Points
+// The preferred interface for specifying font sizes in stb_truetype
+// is to specify how tall the font's vertical extent should be in pixels.
+// If that sounds good enough, skip the next paragraph.
+//
+// Most font APIs instead use "points", which are a common typographic
+// measurement for describing font size, defined as 72 points per inch.
+// stb_truetype provides a point API for compatibility. However, true
+// "per inch" conventions don't make much sense on computer displays
+// since they different monitors have different number of pixels per
+// inch. For example, Windows traditionally uses a convention that
+// there are 96 pixels per inch, thus making 'inch' measurements have
+// nothing to do with inches, and thus effectively defining a point to
+// be 1.333 pixels. Additionally, the TrueType font data provides
+// an explicit scale factor to scale a given font's glyphs to points,
+// but the author has observed that this scale factor is often wrong
+// for non-commercial fonts, thus making fonts scaled in points
+// according to the TrueType spec incoherently sized in practice.
+//
+// ADVANCED USAGE
+//
+// Quality:
+//
+// - Use the functions with Subpixel at the end to allow your characters
+// to have subpixel positioning. Since the font is anti-aliased, not
+// hinted, this is very import for quality. (This is not possible with
+// baked fonts.)
+//
+// - Kerning is now supported, and if you're supporting subpixel rendering
+// then kerning is worth using to give your text a polished look.
+//
+// Performance:
+//
+// - Convert Unicode codepoints to glyph indexes and operate on the glyphs;
+// if you don't do this, stb_truetype is forced to do the conversion on
+// every call.
+//
+// - There are a lot of memory allocations. We should modify it to take
+// a temp buffer and allocate from the temp buffer (without freeing),
+// should help performance a lot.
+//
+// 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.6c, 2050 LOC)
+//
+// Documentation & header file 520 LOC \___ 660 LOC documentation
+// Sample code 140 LOC /
+// Truetype parsing 620 LOC ---- 620 LOC TrueType
+// Software rasterization 240 LOC \ .
+// Curve tesselation 120 LOC \__ 550 LOC Bitmap creation
+// Bitmap management 100 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_bakedchar 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
+#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
+char buffer[24<<20];
+unsigned char screen[20][79];
+
+int main(int arg, char **argv)
+{
+ stbtt_fontinfo font;
+ int i,j,ascent,baseline,ch=0;
+ float scale, xpos=0;
+ char *text = "Heljo World!";
+
+ fread(buffer, 1, 1000000, fopen("c:/windows/fonts/arialbd.ttf", "rb"));
+ stbtt_InitFont(&font, buffer, 0);
+
+ scale = stbtt_ScaleForPixelHeight(&font, 15);
+ stbtt_GetFontVMetrics(&font, &ascent,0,0);
+ baseline = (int) (ascent*scale);
+
+ while (text[ch]) {
+ int advance,lsb,x0,y0,x1,y1;
+ float x_shift = xpos - (float) floor(xpos);
+ stbtt_GetCodepointHMetrics(&font, text[ch], &advance, &lsb);
+ stbtt_GetCodepointBitmapBoxSubpixel(&font, text[ch], scale,scale,x_shift,0, &x0,&y0,&x1,&y1);
+ stbtt_MakeCodepointBitmapSubpixel(&font, &screen[baseline + y0][(int) xpos + x0], x1-x0,y1-y0, 79, scale,scale,x_shift,0, text[ch]);
+ // 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. if you want to render
+ // a sequence of characters, you really need to render each bitmap to a temp buffer, then
+ // "alpha blend" that into the working buffer
+ xpos += (advance * scale);
+ if (text[ch+1])
+ xpos += scale*stbtt_GetCodepointKernAdvance(&font, text[ch],text[ch+1]);
+ ++ch;
+ }
+
+ for (j=0; j < 20; ++j) {
+ for (i=0; i < 78; ++i)
+ putchar(" .:ioVM@"[screen[j][i]>>5]);
+ putchar('\n');
+ }
+
+ return 0;
+}
+#endif
+
+
+//////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+////
+//// INTEGRATION WITH YOUR CODEBASE
+////
+//// The following sections allow you to supply alternate definitions
+//// of C library functions used by stb_truetype.
+
+#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
+ #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
+ #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
+ #define STBTT_malloc(x,u) malloc(x)
+ #define STBTT_free(x,u) free(x)
+ #endif
+
+ #ifndef STBTT_assert
+ #include
+ #define STBTT_assert(x) assert(x)
+ #endif
+
+ #ifndef STBTT_strlen
+ #include
+ #define STBTT_strlen(x) strlen(x)
+ #endif
+
+ #ifndef STBTT_memcpy
+ #include
+ #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.
+//
+// The coordinate system used assumes y increases downwards.
+//
+// Characters will extend both above and below the current position;
+// see discussion of "BASELINE" above.
+//
+// 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/.ttc file may have more than one font. Each font 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, but you should treat it as opaque.
+typedef struct stbtt_fontinfo
+{
+ 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,kern; // 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 pure
+// value data 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 float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels);
+// computes a scale factor to produce a font whose EM size is mapped to
+// 'pixels' tall. This is probably what traditional APIs compute, but
+// I'm not positive.
+
+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, so you must multiply by
+// the scale factor for a given size
+
+extern void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1);
+// the bounding box around all possible characters
+
+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_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index);
+// returns non-zero if nothing is drawn for this glyph
+
+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 unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff);
+// the same as stbtt_GetCodepoitnBitmap, but you can specify a subpixel
+// shift for the character
+
+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 stbtt_GetCodepointBitmap, 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 stbtt_GetCodepointBitmapBox to get the
+// width and height and positioning info for it first.
+
+extern void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint);
+// same as stbtt_MakeCodepointBitmap, but you can specify a subpixel
+// shift for the character
+
+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 void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1);
+// same as stbtt_GetCodepointBitmapBox, but you can specify a subpixel
+// shift for the character
+
+// the following functions are equivalent to the above functions, but operate
+// on glyph indices instead of Unicode codepoints (for efficiency)
+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 unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff);
+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_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph);
+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_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_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, float shift_x, float shift_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 const 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_tag4(font, '1',0,0,0)) 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"); // required
+ info->loca = stbtt__find_table(data, fontstart, "loca"); // required
+ info->head = stbtt__find_table(data, fontstart, "head"); // required
+ info->glyf = stbtt__find_table(data, fontstart, "glyf"); // required
+ info->hhea = stbtt__find_table(data, fontstart, "hhea"); // required
+ info->hmtx = stbtt__find_table(data, fontstart, "hmtx"); // required
+ info->kern = stbtt__find_table(data, fontstart, "kern"); // not required
+ 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;
+
+ // 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 end;
+ searchRange >>= 1;
+ end = ttUSHORT(data + search + 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);
+ if (unicode_codepoint < start)
+ return 0;
+
+ offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item);
+ if (offset == 0)
+ return (stbtt_uint16) (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 || format == 13) {
+ stbtt_uint32 ngroups = ttULONG(data+index_map+12);
+ 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;
+ else if ((stbtt_uint32) unicode_codepoint > end_char)
+ low = mid+1;
+ else {
+ stbtt_uint32 start_glyph = ttULONG(data+index_map+16+mid*12+8);
+ if (format == 12)
+ return start_glyph + unicode_codepoint-start_char;
+ else // format == 13
+ return start_glyph;
+ }
+ }
+ 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_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy)
+{
+ v->type = type;
+ v->x = (stbtt_int16) x;
+ v->y = (stbtt_int16) y;
+ v->cx = (stbtt_int16) cx;
+ v->cy = (stbtt_int16) 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_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index)
+{
+ stbtt_int16 numberOfContours;
+ int g = stbtt__GetGlyfOffset(info, glyph_index);
+ if (g < 0) return 1;
+ numberOfContours = ttSHORT(info->data + g);
+ return numberOfContours == 0;
+}
+
+static int stbtt__close_shape(stbtt_vertex *vertices, int num_vertices, int was_off, int start_off,
+ stbtt_int32 sx, stbtt_int32 sy, stbtt_int32 scx, stbtt_int32 scy, stbtt_int32 cx, stbtt_int32 cy)
+{
+ if (start_off) {
+ if (was_off)
+ stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+scx)>>1, (cy+scy)>>1, cx,cy);
+ stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, sx,sy,scx,scy);
+ } else {
+ 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);
+ }
+ return num_vertices;
+}
+
+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, start_off=0;
+ stbtt_int32 x,y,cx,cy,sx,sy, scx,scy;
+ 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 + 2*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 = (stbtt_int16) 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 = (stbtt_int16) y;
+ }
+
+ // now convert them to our format
+ num_vertices=0;
+ sx = sy = cx = cy = scx = scy = 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) {
+ if (i != 0)
+ num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy);
+
+ // now start the new one
+ start_off = !(flags & 1);
+ if (start_off) {
+ // if we start off with an off-curve point, then when we need to find a point on the curve
+ // where we can start, and we need to save some state for when we wraparound.
+ scx = x;
+ scy = y;
+ if (!(vertices[off+i+1].type & 1)) {
+ // next point is also a curve point, so interpolate an on-point curve
+ sx = (x + (stbtt_int32) vertices[off+i+1].x) >> 1;
+ sy = (y + (stbtt_int32) vertices[off+i+1].y) >> 1;
+ } else {
+ // otherwise just use the next point as our start point
+ sx = (stbtt_int32) vertices[off+i+1].x;
+ sy = (stbtt_int32) vertices[off+i+1].y;
+ ++i; // we're using point i+1 as the starting point, so skip it
+ }
+ } else {
+ sx = x;
+ sy = y;
+ }
+ stbtt_setvertex(&vertices[num_vertices++], STBTT_vmove,sx,sy,0,0);
+ was_off = 0;
+ next_move = 1 + ttUSHORT(endPtsOfContours+j*2);
+ ++j;
+ } 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;
+ }
+ }
+ }
+ num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy);
+ } 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)
+{
+ stbtt_uint8 *data = info->data + info->kern;
+ stbtt_uint32 needle, straw;
+ int l, r, m;
+
+ // we only look at the first table. it must be 'horizontal' and format 0.
+ if (!info->kern)
+ return 0;
+ if (ttUSHORT(data+2) < 1) // number of tables, need at least 1
+ return 0;
+ if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format
+ return 0;
+
+ l = 0;
+ r = ttUSHORT(data+10) - 1;
+ needle = glyph1 << 16 | glyph2;
+ while (l <= r) {
+ m = (l + r) >> 1;
+ straw = ttULONG(data+18+(m*6)); // note: unaligned read
+ if (needle < straw)
+ r = m - 1;
+ else if (needle > straw)
+ l = m + 1;
+ else
+ return ttSHORT(data+22+(m*6));
+ }
+ return 0;
+}
+
+int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2)
+{
+ if (!info->kern) // if no kerning table, don't waste time looking up both codepoint->glyphs
+ return 0;
+ return stbtt_GetGlyphKernAdvance(info, stbtt_FindGlyphIndex(info,ch1), stbtt_FindGlyphIndex(info,ch2));
+}
+
+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);
+}
+
+void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1)
+{
+ *x0 = ttSHORT(info->data + info->head + 36);
+ *y0 = ttSHORT(info->data + info->head + 38);
+ *x1 = ttSHORT(info->data + info->head + 40);
+ *y1 = ttSHORT(info->data + info->head + 42);
+}
+
+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;
+}
+
+float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels)
+{
+ int unitsPerEm = ttUSHORT(info->data + info->head + 18);
+ return pixels / unitsPerEm;
+}
+
+void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v)
+{
+ STBTT_free(v, info->userdata);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// antialiasing software rasterizer
+//
+
+void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_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 + shift_x);
+ if (iy0) *iy0 = -STBTT_iceil (y1 * scale_y + shift_y);
+ if (ix1) *ix1 = STBTT_iceil (x1 * scale_x + shift_x);
+ if (iy1) *iy1 = -STBTT_ifloor(y0 * scale_y + shift_y);
+}
+void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1)
+{
+ stbtt_GetGlyphBitmapBoxSubpixel(font, glyph, scale_x, scale_y,0.0f,0.0f, ix0, iy0, ix1, iy1);
+}
+
+void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1)
+{
+ stbtt_GetGlyphBitmapBoxSubpixel(font, stbtt_FindGlyphIndex(font,codepoint), scale_x, scale_y,shift_x,shift_y, ix0,iy0,ix1,iy1);
+}
+
+void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1)
+{
+ stbtt_GetCodepointBitmapBoxSubpixel(font, codepoint, scale_x, scale_y,0.0f,0.0f, 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, float shift_x, float shift_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 + shift_x;
+ e[n].y0 = p[a].y * y_scale_inv * vsubsample + shift_y;
+ e[n].x1 = p[b].x * scale_x + shift_x;
+ e[n].y1 = p[b].y * y_scale_inv * vsubsample + shift_y;
+ ++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, float shift_x, float shift_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, shift_x, shift_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_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_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_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_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, shift_x, shift_y, ix0, iy0, 1, info->userdata);
+ }
+ }
+ STBTT_free(vertices, info->userdata);
+ return gbm.pixels;
+}
+
+unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff)
+{
+ return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y, 0.0f, 0.0f, glyph, width, height, xoff, yoff);
+}
+
+void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph)
+{
+ int ix0,iy0;
+ stbtt_vertex *vertices;
+ int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices);
+ stbtt__bitmap gbm;
+
+ stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_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, shift_x, shift_y, ix0,iy0, 1, info->userdata);
+
+ STBTT_free(vertices, info->userdata);
+}
+
+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)
+{
+ stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, glyph);
+}
+
+unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff)
+{
+ return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y,shift_x,shift_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff);
+}
+
+void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint)
+{
+ stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, stbtt_FindGlyphIndex(info,codepoint));
+}
+
+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_GetCodepointBitmapSubpixel(info, scale_x, scale_y, 0.0f,0.0f, 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_MakeCodepointBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, codepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// bitmap baking
+//
+// This is SUPER-CRAPPY 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 * iph;
+ q->s1 = b->x1 * ipw;
+ 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(const stbtt_uint8 *s1, stbtt_int32 len1, const 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((const stbtt_uint8*) s1, len1, (const 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
+const 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 (const 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_int32) 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
From 88484cbd7d83f5a1c9faeeece3981d8a3628dc30 Mon Sep 17 00:00:00 2001
From: Jonathan Adamczewski
Date: Sun, 23 Mar 2014 00:20:20 -0700
Subject: [PATCH 31/49] Remove unused variable
---
RecastDemo/Source/MeshLoaderObj.cpp | 3 ---
1 file changed, 3 deletions(-)
diff --git a/RecastDemo/Source/MeshLoaderObj.cpp b/RecastDemo/Source/MeshLoaderObj.cpp
index 4216da7..1d7ca37 100644
--- a/RecastDemo/Source/MeshLoaderObj.cpp
+++ b/RecastDemo/Source/MeshLoaderObj.cpp
@@ -78,7 +78,6 @@ void rcMeshLoaderObj::addTriangle(int a, int b, int c, int& cap)
static char* parseRow(char* buf, char* bufEnd, char* row, int len)
{
- bool cont = false;
bool start = true;
bool done = false;
int n = 0;
@@ -90,7 +89,6 @@ static char* parseRow(char* buf, char* bufEnd, char* row, int len)
switch (c)
{
case '\\':
- cont = true; // multirow
break;
case '\n':
if (start) break;
@@ -103,7 +101,6 @@ static char* parseRow(char* buf, char* bufEnd, char* row, int len)
if (start) break;
default:
start = false;
- cont = false;
row[n++] = c;
if (n >= len-1)
done = true;
From 1d05c18c003998d793cff20b3564cd728b98ed32 Mon Sep 17 00:00:00 2001
From: Jonathan Adamczewski
Date: Sun, 23 Mar 2014 23:07:40 -0700
Subject: [PATCH 32/49] Used read length (and suppress warning)
---
RecastDemo/Source/InputGeom.cpp | 6 +++++-
RecastDemo/Source/MeshLoaderObj.cpp | 7 ++++++-
RecastDemo/Source/SampleInterfaces.cpp | 4 ++--
RecastDemo/Source/Sample_TileMesh.cpp | 18 ++++++++++++++----
RecastDemo/Source/TestCase.cpp | 6 +++++-
RecastDemo/Source/imguiRenderGL.cpp | 9 +++++++--
6 files changed, 39 insertions(+), 11 deletions(-)
diff --git a/RecastDemo/Source/InputGeom.cpp b/RecastDemo/Source/InputGeom.cpp
index 2a8300f..f1e0186 100644
--- a/RecastDemo/Source/InputGeom.cpp
+++ b/RecastDemo/Source/InputGeom.cpp
@@ -174,8 +174,12 @@ bool InputGeom::load(rcContext* ctx, const char* filePath)
fclose(fp);
return false;
}
- fread(buf, bufSize, 1, fp);
+ size_t readLen = fread(buf, bufSize, 1, fp);
fclose(fp);
+ if (readLen != 1)
+ {
+ return false;
+ }
m_offMeshConCount = 0;
m_volumeCount = 0;
diff --git a/RecastDemo/Source/MeshLoaderObj.cpp b/RecastDemo/Source/MeshLoaderObj.cpp
index 1d7ca37..2ba8fd8 100644
--- a/RecastDemo/Source/MeshLoaderObj.cpp
+++ b/RecastDemo/Source/MeshLoaderObj.cpp
@@ -150,9 +150,14 @@ bool rcMeshLoaderObj::load(const char* filename)
fclose(fp);
return false;
}
- fread(buf, bufSize, 1, fp);
+ size_t readLen = fread(buf, bufSize, 1, fp);
fclose(fp);
+ if (readLen != 1)
+ {
+ return false;
+ }
+
char* src = buf;
char* srcEnd = buf + bufSize;
char row[512];
diff --git a/RecastDemo/Source/SampleInterfaces.cpp b/RecastDemo/Source/SampleInterfaces.cpp
index b6e64ee..75d5f06 100644
--- a/RecastDemo/Source/SampleInterfaces.cpp
+++ b/RecastDemo/Source/SampleInterfaces.cpp
@@ -312,8 +312,8 @@ bool FileIO::write(const void* ptr, const size_t size)
bool FileIO::read(void* ptr, const size_t size)
{
if (!m_fp || m_mode != 2) return false;
- fread(ptr, size, 1, m_fp);
- return true;
+ size_t readLen = fread(ptr, size, 1, m_fp);
+ return readLen == 1;
}
diff --git a/RecastDemo/Source/Sample_TileMesh.cpp b/RecastDemo/Source/Sample_TileMesh.cpp
index 8c7a36c..3c75dba 100644
--- a/RecastDemo/Source/Sample_TileMesh.cpp
+++ b/RecastDemo/Source/Sample_TileMesh.cpp
@@ -285,7 +285,12 @@ dtNavMesh* Sample_TileMesh::loadAll(const char* path)
// Read header.
NavMeshSetHeader header;
- fread(&header, sizeof(NavMeshSetHeader), 1, fp);
+ size_t readLen = fread(&header, sizeof(NavMeshSetHeader), 1, fp);
+ if (readLen != 1)
+ {
+ fclose(fp);
+ return 0;
+ }
if (header.magic != NAVMESHSET_MAGIC)
{
fclose(fp);
@@ -314,15 +319,20 @@ dtNavMesh* Sample_TileMesh::loadAll(const char* path)
for (int i = 0; i < header.numTiles; ++i)
{
NavMeshTileHeader tileHeader;
- fread(&tileHeader, sizeof(tileHeader), 1, fp);
+ readLen = fread(&tileHeader, sizeof(tileHeader), 1, fp);
+ if (readLen != 1)
+ return 0;
+
if (!tileHeader.tileRef || !tileHeader.dataSize)
break;
unsigned char* data = (unsigned char*)dtAlloc(tileHeader.dataSize, DT_ALLOC_PERM);
if (!data) break;
memset(data, 0, tileHeader.dataSize);
- fread(data, tileHeader.dataSize, 1, fp);
-
+ readLen = fread(data, tileHeader.dataSize, 1, fp);
+ if (readLen != 1)
+ return 0;
+
mesh->addTile(data, tileHeader.dataSize, DT_TILE_FREE_DATA, tileHeader.tileRef, 0);
}
diff --git a/RecastDemo/Source/TestCase.cpp b/RecastDemo/Source/TestCase.cpp
index 7d4052f..be72d24 100644
--- a/RecastDemo/Source/TestCase.cpp
+++ b/RecastDemo/Source/TestCase.cpp
@@ -106,8 +106,12 @@ bool TestCase::load(const char* filePath)
fclose(fp);
return false;
}
- fread(buf, bufSize, 1, fp);
+ size_t readLen = fread(buf, bufSize, 1, fp);
fclose(fp);
+ if (readLen != 1)
+ {
+ return false;
+ }
char* src = buf;
char* srcEnd = buf + bufSize;
diff --git a/RecastDemo/Source/imguiRenderGL.cpp b/RecastDemo/Source/imguiRenderGL.cpp
index 2dddffe..2c4e375 100644
--- a/RecastDemo/Source/imguiRenderGL.cpp
+++ b/RecastDemo/Source/imguiRenderGL.cpp
@@ -247,7 +247,7 @@ bool imguiRenderGLInit(const char* fontpath)
FILE* fp = fopen(fontpath, "rb");
if (!fp) return false;
fseek(fp, 0, SEEK_END);
- int size = ftell(fp);
+ size_t size = ftell(fp);
fseek(fp, 0, SEEK_SET);
unsigned char* ttfBuffer = (unsigned char*)malloc(size);
@@ -257,8 +257,13 @@ bool imguiRenderGLInit(const char* fontpath)
return false;
}
- fread(ttfBuffer, 1, size, fp);
+ size_t readLen = fread(ttfBuffer, 1, size, fp);
fclose(fp);
+ if (readLen != size)
+ {
+ return false;
+ }
+
fp = 0;
unsigned char* bmap = (unsigned char*)malloc(512*512);
From 0f2b03a8c0cc30e143bde1697789c753df2b3e38 Mon Sep 17 00:00:00 2001
From: JimmyJames707
Date: Fri, 11 Apr 2014 16:50:57 +1000
Subject: [PATCH 33/49] I would like to submit a query filter for each agent
type:
https://groups.google.com/forum/#!searchin/recastnavigation/dtQueryFilter|sort:relevance|spell:false/recastnavigation/t-rFg2Ku9IY/PJNNkCjARccJ
Search on
queryFilterType and m_filters to see the changes.
Also in this code there are bugfixes, that have already been submitted
as issues. They can be ignored.
Possible bug with requestMoveVelocity and navmesh regeneration
https://groups.google.com/forum/#!topic/recastnavigation/E6khmmKJkzk
Bug fix for No guard on dtCrowd::getAgent?
https://groups.google.com/forum/#!topic/recastnavigation/yJ1EEVbOaKg
---
DetourCrowd/Include/DetourCrowd.h | 29 ++++++++++++---
DetourCrowd/Source/DetourCrowd.cpp | 57 ++++++++++++++++++++----------
2 files changed, 63 insertions(+), 23 deletions(-)
diff --git a/DetourCrowd/Include/DetourCrowd.h b/DetourCrowd/Include/DetourCrowd.h
index 243ac5a..c8cc392 100644
--- a/DetourCrowd/Include/DetourCrowd.h
+++ b/DetourCrowd/Include/DetourCrowd.h
@@ -87,6 +87,9 @@ struct dtCrowdAgentParams
/// [Limits: 0 <= value <= #DT_CROWD_MAX_OBSTAVOIDANCE_PARAMS]
unsigned char obstacleAvoidanceType;
+ // [F]
+ unsigned char queryFilterType;
+
/// User defined data attached to the agent.
void* userData;
};
@@ -102,6 +105,9 @@ enum MoveRequestState
DT_CROWDAGENT_TARGET_VELOCITY,
};
+// [F]
+static const int DT_CROWD_MAX_QUERY_FILTER_TYPE = 16;
+
/// Represents an agent managed by a #dtCrowd object.
/// @ingroup crowd
struct dtCrowdAgent
@@ -157,6 +163,9 @@ struct dtCrowdAgent
dtPathQueueRef targetPathqRef; ///< Path finder ref.
bool targetReplan; ///< Flag indicating that the current path is being replanned.
float targetReplanTime; /// = 0 && i < DT_CROWD_MAX_QUERY_FILTER_TYPE) ? &m_filters[i] : 0; }
/// Gets the filter used by the crowd.
/// @return The filter used by the crowd.
- dtQueryFilter* getEditableFilter() { return &m_filter; }
+ //dtQueryFilter* getEditableFilter() { return &m_filter; }
+ // [F]
+ //dtQueryFilter* getEditableFilter() {return &m_filters[0];}
+ dtQueryFilter* getIndexedEditableFilter(const int i = 0) { return (i >= 0 && i < DT_CROWD_MAX_QUERY_FILTER_TYPE) ? &m_filters[i] : 0; }
/// Gets the search extents [(x, y, z)] used by the crowd for query operations.
/// @return The search extents used by the crowd. [(x, y, z)]
diff --git a/DetourCrowd/Source/DetourCrowd.cpp b/DetourCrowd/Source/DetourCrowd.cpp
index cf7a027..4746f82 100644
--- a/DetourCrowd/Source/DetourCrowd.cpp
+++ b/DetourCrowd/Source/DetourCrowd.cpp
@@ -482,8 +482,10 @@ int dtCrowd::getAgentCount() const
/// @par
///
/// Agents in the pool may not be in use. Check #dtCrowdAgent.active before using the returned object.
-const dtCrowdAgent* dtCrowd::getAgent(const int idx)
+dtCrowdAgent* dtCrowd::getAgent(const int idx)
{
+ if (idx < 0 || idx >= m_maxAgents)
+ return 0;
return &m_agents[idx];
}
@@ -514,11 +516,18 @@ int dtCrowd::addAgent(const float* pos, const dtCrowdAgentParams* params)
dtCrowdAgent* ag = &m_agents[idx];
+ // [F]
+ if (params->queryFilterType < DT_CROWD_MAX_QUERY_FILTER_TYPE)
+ ag->queryFilterType = params->queryFilterType;
+ else
+ ag->queryFilterType = 0;
+
+
// Find nearest position on navmesh and place the agent there.
float nearest[3];
dtPolyRef ref = 0;
dtVcopy(nearest, pos);
- dtStatus status = m_navquery->findNearestPoly(pos, m_ext, &m_filter, &ref, nearest);
+ dtStatus status = m_navquery->findNearestPoly(pos, m_ext, &m_filters[ag->queryFilterType], &ref, nearest);
if (dtStatusFailed(status))
{
dtVcopy(nearest, pos);
@@ -691,7 +700,7 @@ void dtCrowd::updateMoveRequest(const float /*dt*/)
// Quick seach towards the goal.
static const int MAX_ITER = 20;
- m_navquery->initSlicedFindPath(path[0], ag->targetRef, ag->npos, ag->targetPos, &m_filter);
+ m_navquery->initSlicedFindPath(path[0], ag->targetRef, ag->npos, ag->targetPos, &m_filters[ag->queryFilterType]);
m_navquery->updateSlicedFindPath(MAX_ITER, 0);
dtStatus status = 0;
if (ag->targetReplan) // && npath > 10)
@@ -758,7 +767,7 @@ void dtCrowd::updateMoveRequest(const float /*dt*/)
{
dtCrowdAgent* ag = queue[i];
ag->targetPathqRef = m_pathq.request(ag->corridor.getLastPoly(), ag->targetRef,
- ag->corridor.getTarget(), ag->targetPos, &m_filter);
+ ag->corridor.getTarget(), ag->targetPos, &m_filters[ag->queryFilterType]);
if (ag->targetPathqRef != DT_PATHQ_INVALID)
ag->targetState = DT_CROWDAGENT_TARGET_WAITING_FOR_PATH;
}
@@ -912,7 +921,7 @@ void dtCrowd::updateTopologyOptimization(dtCrowdAgent** agents, const int nagent
for (int i = 0; i < nqueue; ++i)
{
dtCrowdAgent* ag = queue[i];
- ag->corridor.optimizePathTopology(m_navquery, &m_filter);
+ ag->corridor.optimizePathTopology(m_navquery, &m_filters[ag->queryFilterType]);
ag->topologyOptTime = 0;
}
@@ -930,8 +939,9 @@ void dtCrowd::checkPathValidity(dtCrowdAgent** agents, const int nagents, const
if (ag->state != DT_CROWDAGENT_STATE_WALKING)
continue;
- if (ag->targetState == DT_CROWDAGENT_TARGET_NONE || ag->targetState == DT_CROWDAGENT_TARGET_VELOCITY)
- continue;
+ // BUG FIX: https://groups.google.com/forum/#!topic/recastnavigation/E6khmmKJkzk
+ //if (ag->targetState == DT_CROWDAGENT_TARGET_NONE || ag->targetState == DT_CROWDAGENT_TARGET_VELOCITY)
+ // continue;
ag->targetReplanTime += dt;
@@ -942,14 +952,14 @@ void dtCrowd::checkPathValidity(dtCrowdAgent** agents, const int nagents, const
float agentPos[3];
dtPolyRef agentRef = ag->corridor.getFirstPoly();
dtVcopy(agentPos, ag->npos);
- if (!m_navquery->isValidPolyRef(agentRef, &m_filter))
+ if (!m_navquery->isValidPolyRef(agentRef, &m_filters[ag->queryFilterType]))
{
// Current location is not valid, try to reposition.
// TODO: this can snap agents, how to handle that?
float nearest[3];
dtVcopy(nearest, agentPos);
agentRef = 0;
- m_navquery->findNearestPoly(ag->npos, m_ext, &m_filter, &agentRef, nearest);
+ m_navquery->findNearestPoly(ag->npos, m_ext, &m_filters[ag->queryFilterType], &agentRef, nearest);
dtVcopy(agentPos, nearest);
if (!agentRef)
@@ -971,16 +981,20 @@ void dtCrowd::checkPathValidity(dtCrowdAgent** agents, const int nagents, const
replan = true;
}
+ // Davo BUG FIX: https://groups.google.com/forum/#!topic/recastnavigation/E6khmmKJkzk
+ if (ag->targetState == DT_CROWDAGENT_TARGET_NONE || ag->targetState == DT_CROWDAGENT_TARGET_VELOCITY)
+ continue;
+
// Try to recover move request position.
if (ag->targetState != DT_CROWDAGENT_TARGET_NONE && ag->targetState != DT_CROWDAGENT_TARGET_FAILED)
{
- if (!m_navquery->isValidPolyRef(ag->targetRef, &m_filter))
+ if (!m_navquery->isValidPolyRef(ag->targetRef, &m_filters[ag->queryFilterType]))
{
// Current target is not valid, try to reposition.
float nearest[3];
dtVcopy(nearest, ag->targetPos);
ag->targetRef = 0;
- m_navquery->findNearestPoly(ag->targetPos, m_ext, &m_filter, &ag->targetRef, nearest);
+ m_navquery->findNearestPoly(ag->targetPos, m_ext, &m_filters[ag->queryFilterType], &ag->targetRef, nearest);
dtVcopy(ag->targetPos, nearest);
replan = true;
}
@@ -993,7 +1007,7 @@ void dtCrowd::checkPathValidity(dtCrowdAgent** agents, const int nagents, const
}
// If nearby corridor is not valid, replan.
- if (!ag->corridor.isValid(CHECK_LOOKAHEAD, m_navquery, &m_filter))
+ if (!ag->corridor.isValid(CHECK_LOOKAHEAD, m_navquery, &m_filters[ag->queryFilterType]))
{
// Fix current path.
// ag->corridor.trimInvalidPath(agentRef, agentPos, m_navquery, &m_filter);
@@ -1030,6 +1044,13 @@ void dtCrowd::update(const float dt, dtCrowdAgentDebugInfo* debug)
dtCrowdAgent** agents = m_activeAgents;
int nagents = getActiveAgents(agents, m_maxAgents);
+ // [F]
+ for (int i = 0; i < nagents; ++i)
+ {
+ dtCrowdAgent* ag = agents[i];
+ ag->queryFilterType = ag->params.queryFilterType;
+ }
+
// Check that all agents still have valid paths.
checkPathValidity(agents, nagents, dt);
@@ -1060,10 +1081,10 @@ void dtCrowd::update(const float dt, dtCrowdAgentDebugInfo* debug)
// if it has become invalid.
const float updateThr = ag->params.collisionQueryRange*0.25f;
if (dtVdist2DSqr(ag->npos, ag->boundary.getCenter()) > dtSqr(updateThr) ||
- !ag->boundary.isValid(m_navquery, &m_filter))
+ !ag->boundary.isValid(m_navquery, &m_filters[ag->queryFilterType]))
{
ag->boundary.update(ag->corridor.getFirstPoly(), ag->npos, ag->params.collisionQueryRange,
- m_navquery, &m_filter);
+ m_navquery, &m_filters[ag->queryFilterType]);
}
// Query neighbour agents
ag->nneis = getNeighbours(ag->npos, ag->params.height, ag->params.collisionQueryRange,
@@ -1085,14 +1106,14 @@ void dtCrowd::update(const float dt, dtCrowdAgentDebugInfo* debug)
// Find corners for steering
ag->ncorners = ag->corridor.findCorners(ag->cornerVerts, ag->cornerFlags, ag->cornerPolys,
- DT_CROWDAGENT_MAX_CORNERS, m_navquery, &m_filter);
+ DT_CROWDAGENT_MAX_CORNERS, m_navquery, &m_filters[ag->queryFilterType]);
// Check to see if the corner after the next corner is directly visible,
// and short cut to there.
if ((ag->params.updateFlags & DT_CROWD_OPTIMIZE_VIS) && ag->ncorners > 0)
{
const float* target = &ag->cornerVerts[dtMin(1,ag->ncorners-1)*3];
- ag->corridor.optimizePathVisibility(target, ag->params.pathOptimizationRange, m_navquery, &m_filter);
+ ag->corridor.optimizePathVisibility(target, ag->params.pathOptimizationRange, m_navquery, &m_filters[ag->queryFilterType]);
// Copy data for debug purposes.
if (debugIdx == i)
@@ -1264,7 +1285,7 @@ void dtCrowd::update(const float dt, dtCrowdAgentDebugInfo* debug)
if (debugIdx == i)
vod = debug->vod;
- // Sample new safe velocity.
+ // MySample new safe velocity.
bool adaptive = true;
int ns = 0;
@@ -1372,7 +1393,7 @@ void dtCrowd::update(const float dt, dtCrowdAgentDebugInfo* debug)
continue;
// Move along navmesh.
- ag->corridor.movePosition(ag->npos, m_navquery, &m_filter);
+ ag->corridor.movePosition(ag->npos, m_navquery, &m_filters[ag->queryFilterType]);
// Get valid constrained position back.
dtVcopy(ag->npos, ag->corridor.getPos());
From b5f70b51a5e627f98a6f5d28aa5704afa9cdff6a Mon Sep 17 00:00:00 2001
From: JimmyJames707
Date: Fri, 11 Apr 2014 21:45:42 +1000
Subject: [PATCH 34/49] query filter for each agent type with changes
---
DetourCrowd/Include/DetourCrowd.h | 35 +++++++---------
DetourCrowd/Source/DetourCrowd.cpp | 65 +++++++++++++-----------------
2 files changed, 41 insertions(+), 59 deletions(-)
diff --git a/DetourCrowd/Include/DetourCrowd.h b/DetourCrowd/Include/DetourCrowd.h
index c8cc392..24c815b 100644
--- a/DetourCrowd/Include/DetourCrowd.h
+++ b/DetourCrowd/Include/DetourCrowd.h
@@ -45,6 +45,9 @@ static const int DT_CROWDAGENT_MAX_CORNERS = 4;
/// dtCrowdAgentParams::obstacleAvoidanceType
static const int DT_CROWD_MAX_OBSTAVOIDANCE_PARAMS = 8;
+/// The maximum number of query filter types
+static const int DT_CROWD_MAX_QUERY_FILTER_TYPE = 16;
+
/// Provides neighbor data for agents managed by the crowd.
/// @ingroup crowd
/// @see dtCrowdAgent::neis, dtCrowd
@@ -87,7 +90,7 @@ struct dtCrowdAgentParams
/// [Limits: 0 <= value <= #DT_CROWD_MAX_OBSTAVOIDANCE_PARAMS]
unsigned char obstacleAvoidanceType;
- // [F]
+ /// The index of the query filter used by this agent.
unsigned char queryFilterType;
/// User defined data attached to the agent.
@@ -105,9 +108,6 @@ enum MoveRequestState
DT_CROWDAGENT_TARGET_VELOCITY,
};
-// [F]
-static const int DT_CROWD_MAX_QUERY_FILTER_TYPE = 16;
-
/// Represents an agent managed by a #dtCrowd object.
/// @ingroup crowd
struct dtCrowdAgent
@@ -163,9 +163,6 @@ struct dtCrowdAgent
dtPathQueueRef targetPathqRef; ///< Path finder ref.
bool targetReplan; ///< Flag indicating that the current path is being replanned.
float targetReplanTime; /// = 0 && i < DT_CROWD_MAX_QUERY_FILTER_TYPE) ? &m_filters[i] : 0; }
-
+ inline const dtQueryFilter* getFilter(const int i) const { return (i >= 0 && i < DT_CROWD_MAX_QUERY_FILTER_TYPE) ? &m_filters[i] : 0; }
+
/// Gets the filter used by the crowd.
/// @return The filter used by the crowd.
- //dtQueryFilter* getEditableFilter() { return &m_filter; }
- // [F]
- //dtQueryFilter* getEditableFilter() {return &m_filters[0];}
- dtQueryFilter* getIndexedEditableFilter(const int i = 0) { return (i >= 0 && i < DT_CROWD_MAX_QUERY_FILTER_TYPE) ? &m_filters[i] : 0; }
+ inline dtQueryFilter* getEditableFilter(const int i) { return (i >= 0 && i < DT_CROWD_MAX_QUERY_FILTER_TYPE) ? &m_filters[i] : 0; }
/// Gets the search extents [(x, y, z)] used by the crowd for query operations.
/// @return The search extents used by the crowd. [(x, y, z)]
diff --git a/DetourCrowd/Source/DetourCrowd.cpp b/DetourCrowd/Source/DetourCrowd.cpp
index 4746f82..4297b65 100644
--- a/DetourCrowd/Source/DetourCrowd.cpp
+++ b/DetourCrowd/Source/DetourCrowd.cpp
@@ -482,7 +482,16 @@ int dtCrowd::getAgentCount() const
/// @par
///
/// Agents in the pool may not be in use. Check #dtCrowdAgent.active before using the returned object.
-dtCrowdAgent* dtCrowd::getAgent(const int idx)
+const dtCrowdAgent* dtCrowd::getAgent(const int idx)
+{
+ if (idx < 0 || idx >= m_maxAgents)
+ return 0;
+ return &m_agents[idx];
+}
+
+///
+/// Agents in the pool may not be in use. Check #dtCrowdAgent.active before using the returned object.
+dtCrowdAgent* dtCrowd::getEditableAgent(const int idx)
{
if (idx < 0 || idx >= m_maxAgents)
return 0;
@@ -514,20 +523,13 @@ int dtCrowd::addAgent(const float* pos, const dtCrowdAgentParams* params)
if (idx == -1)
return -1;
- dtCrowdAgent* ag = &m_agents[idx];
-
- // [F]
- if (params->queryFilterType < DT_CROWD_MAX_QUERY_FILTER_TYPE)
- ag->queryFilterType = params->queryFilterType;
- else
- ag->queryFilterType = 0;
-
+ dtCrowdAgent* ag = &m_agents[idx];
// Find nearest position on navmesh and place the agent there.
float nearest[3];
dtPolyRef ref = 0;
dtVcopy(nearest, pos);
- dtStatus status = m_navquery->findNearestPoly(pos, m_ext, &m_filters[ag->queryFilterType], &ref, nearest);
+ dtStatus status = m_navquery->findNearestPoly(pos, m_ext, &m_filters[ag->params.queryFilterType], &ref, nearest);
if (dtStatusFailed(status))
{
dtVcopy(nearest, pos);
@@ -700,7 +702,7 @@ void dtCrowd::updateMoveRequest(const float /*dt*/)
// Quick seach towards the goal.
static const int MAX_ITER = 20;
- m_navquery->initSlicedFindPath(path[0], ag->targetRef, ag->npos, ag->targetPos, &m_filters[ag->queryFilterType]);
+ m_navquery->initSlicedFindPath(path[0], ag->targetRef, ag->npos, ag->targetPos, &m_filters[ag->params.queryFilterType]);
m_navquery->updateSlicedFindPath(MAX_ITER, 0);
dtStatus status = 0;
if (ag->targetReplan) // && npath > 10)
@@ -767,7 +769,7 @@ void dtCrowd::updateMoveRequest(const float /*dt*/)
{
dtCrowdAgent* ag = queue[i];
ag->targetPathqRef = m_pathq.request(ag->corridor.getLastPoly(), ag->targetRef,
- ag->corridor.getTarget(), ag->targetPos, &m_filters[ag->queryFilterType]);
+ ag->corridor.getTarget(), ag->targetPos, &m_filters[ag->params.queryFilterType]);
if (ag->targetPathqRef != DT_PATHQ_INVALID)
ag->targetState = DT_CROWDAGENT_TARGET_WAITING_FOR_PATH;
}
@@ -921,7 +923,7 @@ void dtCrowd::updateTopologyOptimization(dtCrowdAgent** agents, const int nagent
for (int i = 0; i < nqueue; ++i)
{
dtCrowdAgent* ag = queue[i];
- ag->corridor.optimizePathTopology(m_navquery, &m_filters[ag->queryFilterType]);
+ ag->corridor.optimizePathTopology(m_navquery, &m_filters[ag->params.queryFilterType]);
ag->topologyOptTime = 0;
}
@@ -938,10 +940,6 @@ void dtCrowd::checkPathValidity(dtCrowdAgent** agents, const int nagents, const
if (ag->state != DT_CROWDAGENT_STATE_WALKING)
continue;
-
- // BUG FIX: https://groups.google.com/forum/#!topic/recastnavigation/E6khmmKJkzk
- //if (ag->targetState == DT_CROWDAGENT_TARGET_NONE || ag->targetState == DT_CROWDAGENT_TARGET_VELOCITY)
- // continue;
ag->targetReplanTime += dt;
@@ -952,14 +950,14 @@ void dtCrowd::checkPathValidity(dtCrowdAgent** agents, const int nagents, const
float agentPos[3];
dtPolyRef agentRef = ag->corridor.getFirstPoly();
dtVcopy(agentPos, ag->npos);
- if (!m_navquery->isValidPolyRef(agentRef, &m_filters[ag->queryFilterType]))
+ if (!m_navquery->isValidPolyRef(agentRef, &m_filters[ag->params.queryFilterType]))
{
// Current location is not valid, try to reposition.
// TODO: this can snap agents, how to handle that?
float nearest[3];
dtVcopy(nearest, agentPos);
agentRef = 0;
- m_navquery->findNearestPoly(ag->npos, m_ext, &m_filters[ag->queryFilterType], &agentRef, nearest);
+ m_navquery->findNearestPoly(ag->npos, m_ext, &m_filters[ag->params.queryFilterType], &agentRef, nearest);
dtVcopy(agentPos, nearest);
if (!agentRef)
@@ -981,20 +979,20 @@ void dtCrowd::checkPathValidity(dtCrowdAgent** agents, const int nagents, const
replan = true;
}
- // Davo BUG FIX: https://groups.google.com/forum/#!topic/recastnavigation/E6khmmKJkzk
+ // If the agent does not have move target or is controlled by velocity, no need to recover the target nor replan.
if (ag->targetState == DT_CROWDAGENT_TARGET_NONE || ag->targetState == DT_CROWDAGENT_TARGET_VELOCITY)
continue;
// Try to recover move request position.
if (ag->targetState != DT_CROWDAGENT_TARGET_NONE && ag->targetState != DT_CROWDAGENT_TARGET_FAILED)
{
- if (!m_navquery->isValidPolyRef(ag->targetRef, &m_filters[ag->queryFilterType]))
+ if (!m_navquery->isValidPolyRef(ag->targetRef, &m_filters[ag->params.queryFilterType]))
{
// Current target is not valid, try to reposition.
float nearest[3];
dtVcopy(nearest, ag->targetPos);
ag->targetRef = 0;
- m_navquery->findNearestPoly(ag->targetPos, m_ext, &m_filters[ag->queryFilterType], &ag->targetRef, nearest);
+ m_navquery->findNearestPoly(ag->targetPos, m_ext, &m_filters[ag->params.queryFilterType], &ag->targetRef, nearest);
dtVcopy(ag->targetPos, nearest);
replan = true;
}
@@ -1007,7 +1005,7 @@ void dtCrowd::checkPathValidity(dtCrowdAgent** agents, const int nagents, const
}
// If nearby corridor is not valid, replan.
- if (!ag->corridor.isValid(CHECK_LOOKAHEAD, m_navquery, &m_filters[ag->queryFilterType]))
+ if (!ag->corridor.isValid(CHECK_LOOKAHEAD, m_navquery, &m_filters[ag->params.queryFilterType]))
{
// Fix current path.
// ag->corridor.trimInvalidPath(agentRef, agentPos, m_navquery, &m_filter);
@@ -1043,13 +1041,6 @@ void dtCrowd::update(const float dt, dtCrowdAgentDebugInfo* debug)
dtCrowdAgent** agents = m_activeAgents;
int nagents = getActiveAgents(agents, m_maxAgents);
-
- // [F]
- for (int i = 0; i < nagents; ++i)
- {
- dtCrowdAgent* ag = agents[i];
- ag->queryFilterType = ag->params.queryFilterType;
- }
// Check that all agents still have valid paths.
checkPathValidity(agents, nagents, dt);
@@ -1081,10 +1072,10 @@ void dtCrowd::update(const float dt, dtCrowdAgentDebugInfo* debug)
// if it has become invalid.
const float updateThr = ag->params.collisionQueryRange*0.25f;
if (dtVdist2DSqr(ag->npos, ag->boundary.getCenter()) > dtSqr(updateThr) ||
- !ag->boundary.isValid(m_navquery, &m_filters[ag->queryFilterType]))
+ !ag->boundary.isValid(m_navquery, &m_filters[ag->params.queryFilterType]))
{
ag->boundary.update(ag->corridor.getFirstPoly(), ag->npos, ag->params.collisionQueryRange,
- m_navquery, &m_filters[ag->queryFilterType]);
+ m_navquery, &m_filters[ag->params.queryFilterType]);
}
// Query neighbour agents
ag->nneis = getNeighbours(ag->npos, ag->params.height, ag->params.collisionQueryRange,
@@ -1106,14 +1097,14 @@ void dtCrowd::update(const float dt, dtCrowdAgentDebugInfo* debug)
// Find corners for steering
ag->ncorners = ag->corridor.findCorners(ag->cornerVerts, ag->cornerFlags, ag->cornerPolys,
- DT_CROWDAGENT_MAX_CORNERS, m_navquery, &m_filters[ag->queryFilterType]);
+ DT_CROWDAGENT_MAX_CORNERS, m_navquery, &m_filters[ag->params.queryFilterType]);
// Check to see if the corner after the next corner is directly visible,
// and short cut to there.
if ((ag->params.updateFlags & DT_CROWD_OPTIMIZE_VIS) && ag->ncorners > 0)
{
const float* target = &ag->cornerVerts[dtMin(1,ag->ncorners-1)*3];
- ag->corridor.optimizePathVisibility(target, ag->params.pathOptimizationRange, m_navquery, &m_filters[ag->queryFilterType]);
+ ag->corridor.optimizePathVisibility(target, ag->params.pathOptimizationRange, m_navquery, &m_filters[ag->params.queryFilterType]);
// Copy data for debug purposes.
if (debugIdx == i)
@@ -1285,7 +1276,7 @@ void dtCrowd::update(const float dt, dtCrowdAgentDebugInfo* debug)
if (debugIdx == i)
vod = debug->vod;
- // MySample new safe velocity.
+ // Sample new safe velocity.
bool adaptive = true;
int ns = 0;
@@ -1393,7 +1384,7 @@ void dtCrowd::update(const float dt, dtCrowdAgentDebugInfo* debug)
continue;
// Move along navmesh.
- ag->corridor.movePosition(ag->npos, m_navquery, &m_filters[ag->queryFilterType]);
+ ag->corridor.movePosition(ag->npos, m_navquery, &m_filters[ag->params.queryFilterType]);
// Get valid constrained position back.
dtVcopy(ag->npos, ag->corridor.getPos());
@@ -1443,5 +1434,3 @@ void dtCrowd::update(const float dt, dtCrowdAgentDebugInfo* debug)
}
}
-
-
From ce552fd57890e7651916f61118abfd872e747d5b Mon Sep 17 00:00:00 2001
From: JimmyJames707
Date: Fri, 11 Apr 2014 21:50:58 +1000
Subject: [PATCH 35/49] Revert "query filter for each agent type with changes"
This reverts commit b5f70b51a5e627f98a6f5d28aa5704afa9cdff6a.
---
DetourCrowd/Include/DetourCrowd.h | 35 +++++++++-------
DetourCrowd/Source/DetourCrowd.cpp | 65 +++++++++++++++++-------------
2 files changed, 59 insertions(+), 41 deletions(-)
diff --git a/DetourCrowd/Include/DetourCrowd.h b/DetourCrowd/Include/DetourCrowd.h
index 24c815b..c8cc392 100644
--- a/DetourCrowd/Include/DetourCrowd.h
+++ b/DetourCrowd/Include/DetourCrowd.h
@@ -45,9 +45,6 @@ static const int DT_CROWDAGENT_MAX_CORNERS = 4;
/// dtCrowdAgentParams::obstacleAvoidanceType
static const int DT_CROWD_MAX_OBSTAVOIDANCE_PARAMS = 8;
-/// The maximum number of query filter types
-static const int DT_CROWD_MAX_QUERY_FILTER_TYPE = 16;
-
/// Provides neighbor data for agents managed by the crowd.
/// @ingroup crowd
/// @see dtCrowdAgent::neis, dtCrowd
@@ -90,7 +87,7 @@ struct dtCrowdAgentParams
/// [Limits: 0 <= value <= #DT_CROWD_MAX_OBSTAVOIDANCE_PARAMS]
unsigned char obstacleAvoidanceType;
- /// The index of the query filter used by this agent.
+ // [F]
unsigned char queryFilterType;
/// User defined data attached to the agent.
@@ -108,6 +105,9 @@ enum MoveRequestState
DT_CROWDAGENT_TARGET_VELOCITY,
};
+// [F]
+static const int DT_CROWD_MAX_QUERY_FILTER_TYPE = 16;
+
/// Represents an agent managed by a #dtCrowd object.
/// @ingroup crowd
struct dtCrowdAgent
@@ -163,6 +163,9 @@ struct dtCrowdAgent
dtPathQueueRef targetPathqRef; ///< Path finder ref.
bool targetReplan; ///< Flag indicating that the current path is being replanned.
float targetReplanTime; /// = 0 && i < DT_CROWD_MAX_QUERY_FILTER_TYPE) ? &m_filters[i] : 0; }
-
+ //const dtQueryFilter* getFilter() const { return &m_filter; }
+ // [F]
+ //const dtQueryFilter* getFilter() const {return &m_filters[0];}
+ const dtQueryFilter* getIndexedFilter(const int i = 0) const { return (i >= 0 && i < DT_CROWD_MAX_QUERY_FILTER_TYPE) ? &m_filters[i] : 0; }
+
/// Gets the filter used by the crowd.
/// @return The filter used by the crowd.
- inline dtQueryFilter* getEditableFilter(const int i) { return (i >= 0 && i < DT_CROWD_MAX_QUERY_FILTER_TYPE) ? &m_filters[i] : 0; }
+ //dtQueryFilter* getEditableFilter() { return &m_filter; }
+ // [F]
+ //dtQueryFilter* getEditableFilter() {return &m_filters[0];}
+ dtQueryFilter* getIndexedEditableFilter(const int i = 0) { return (i >= 0 && i < DT_CROWD_MAX_QUERY_FILTER_TYPE) ? &m_filters[i] : 0; }
/// Gets the search extents [(x, y, z)] used by the crowd for query operations.
/// @return The search extents used by the crowd. [(x, y, z)]
diff --git a/DetourCrowd/Source/DetourCrowd.cpp b/DetourCrowd/Source/DetourCrowd.cpp
index 4297b65..4746f82 100644
--- a/DetourCrowd/Source/DetourCrowd.cpp
+++ b/DetourCrowd/Source/DetourCrowd.cpp
@@ -482,16 +482,7 @@ int dtCrowd::getAgentCount() const
/// @par
///
/// Agents in the pool may not be in use. Check #dtCrowdAgent.active before using the returned object.
-const dtCrowdAgent* dtCrowd::getAgent(const int idx)
-{
- if (idx < 0 || idx >= m_maxAgents)
- return 0;
- return &m_agents[idx];
-}
-
-///
-/// Agents in the pool may not be in use. Check #dtCrowdAgent.active before using the returned object.
-dtCrowdAgent* dtCrowd::getEditableAgent(const int idx)
+dtCrowdAgent* dtCrowd::getAgent(const int idx)
{
if (idx < 0 || idx >= m_maxAgents)
return 0;
@@ -523,13 +514,20 @@ int dtCrowd::addAgent(const float* pos, const dtCrowdAgentParams* params)
if (idx == -1)
return -1;
- dtCrowdAgent* ag = &m_agents[idx];
+ dtCrowdAgent* ag = &m_agents[idx];
+
+ // [F]
+ if (params->queryFilterType < DT_CROWD_MAX_QUERY_FILTER_TYPE)
+ ag->queryFilterType = params->queryFilterType;
+ else
+ ag->queryFilterType = 0;
+
// Find nearest position on navmesh and place the agent there.
float nearest[3];
dtPolyRef ref = 0;
dtVcopy(nearest, pos);
- dtStatus status = m_navquery->findNearestPoly(pos, m_ext, &m_filters[ag->params.queryFilterType], &ref, nearest);
+ dtStatus status = m_navquery->findNearestPoly(pos, m_ext, &m_filters[ag->queryFilterType], &ref, nearest);
if (dtStatusFailed(status))
{
dtVcopy(nearest, pos);
@@ -702,7 +700,7 @@ void dtCrowd::updateMoveRequest(const float /*dt*/)
// Quick seach towards the goal.
static const int MAX_ITER = 20;
- m_navquery->initSlicedFindPath(path[0], ag->targetRef, ag->npos, ag->targetPos, &m_filters[ag->params.queryFilterType]);
+ m_navquery->initSlicedFindPath(path[0], ag->targetRef, ag->npos, ag->targetPos, &m_filters[ag->queryFilterType]);
m_navquery->updateSlicedFindPath(MAX_ITER, 0);
dtStatus status = 0;
if (ag->targetReplan) // && npath > 10)
@@ -769,7 +767,7 @@ void dtCrowd::updateMoveRequest(const float /*dt*/)
{
dtCrowdAgent* ag = queue[i];
ag->targetPathqRef = m_pathq.request(ag->corridor.getLastPoly(), ag->targetRef,
- ag->corridor.getTarget(), ag->targetPos, &m_filters[ag->params.queryFilterType]);
+ ag->corridor.getTarget(), ag->targetPos, &m_filters[ag->queryFilterType]);
if (ag->targetPathqRef != DT_PATHQ_INVALID)
ag->targetState = DT_CROWDAGENT_TARGET_WAITING_FOR_PATH;
}
@@ -923,7 +921,7 @@ void dtCrowd::updateTopologyOptimization(dtCrowdAgent** agents, const int nagent
for (int i = 0; i < nqueue; ++i)
{
dtCrowdAgent* ag = queue[i];
- ag->corridor.optimizePathTopology(m_navquery, &m_filters[ag->params.queryFilterType]);
+ ag->corridor.optimizePathTopology(m_navquery, &m_filters[ag->queryFilterType]);
ag->topologyOptTime = 0;
}
@@ -940,6 +938,10 @@ void dtCrowd::checkPathValidity(dtCrowdAgent** agents, const int nagents, const
if (ag->state != DT_CROWDAGENT_STATE_WALKING)
continue;
+
+ // BUG FIX: https://groups.google.com/forum/#!topic/recastnavigation/E6khmmKJkzk
+ //if (ag->targetState == DT_CROWDAGENT_TARGET_NONE || ag->targetState == DT_CROWDAGENT_TARGET_VELOCITY)
+ // continue;
ag->targetReplanTime += dt;
@@ -950,14 +952,14 @@ void dtCrowd::checkPathValidity(dtCrowdAgent** agents, const int nagents, const
float agentPos[3];
dtPolyRef agentRef = ag->corridor.getFirstPoly();
dtVcopy(agentPos, ag->npos);
- if (!m_navquery->isValidPolyRef(agentRef, &m_filters[ag->params.queryFilterType]))
+ if (!m_navquery->isValidPolyRef(agentRef, &m_filters[ag->queryFilterType]))
{
// Current location is not valid, try to reposition.
// TODO: this can snap agents, how to handle that?
float nearest[3];
dtVcopy(nearest, agentPos);
agentRef = 0;
- m_navquery->findNearestPoly(ag->npos, m_ext, &m_filters[ag->params.queryFilterType], &agentRef, nearest);
+ m_navquery->findNearestPoly(ag->npos, m_ext, &m_filters[ag->queryFilterType], &agentRef, nearest);
dtVcopy(agentPos, nearest);
if (!agentRef)
@@ -979,20 +981,20 @@ void dtCrowd::checkPathValidity(dtCrowdAgent** agents, const int nagents, const
replan = true;
}
- // If the agent does not have move target or is controlled by velocity, no need to recover the target nor replan.
+ // Davo BUG FIX: https://groups.google.com/forum/#!topic/recastnavigation/E6khmmKJkzk
if (ag->targetState == DT_CROWDAGENT_TARGET_NONE || ag->targetState == DT_CROWDAGENT_TARGET_VELOCITY)
continue;
// Try to recover move request position.
if (ag->targetState != DT_CROWDAGENT_TARGET_NONE && ag->targetState != DT_CROWDAGENT_TARGET_FAILED)
{
- if (!m_navquery->isValidPolyRef(ag->targetRef, &m_filters[ag->params.queryFilterType]))
+ if (!m_navquery->isValidPolyRef(ag->targetRef, &m_filters[ag->queryFilterType]))
{
// Current target is not valid, try to reposition.
float nearest[3];
dtVcopy(nearest, ag->targetPos);
ag->targetRef = 0;
- m_navquery->findNearestPoly(ag->targetPos, m_ext, &m_filters[ag->params.queryFilterType], &ag->targetRef, nearest);
+ m_navquery->findNearestPoly(ag->targetPos, m_ext, &m_filters[ag->queryFilterType], &ag->targetRef, nearest);
dtVcopy(ag->targetPos, nearest);
replan = true;
}
@@ -1005,7 +1007,7 @@ void dtCrowd::checkPathValidity(dtCrowdAgent** agents, const int nagents, const
}
// If nearby corridor is not valid, replan.
- if (!ag->corridor.isValid(CHECK_LOOKAHEAD, m_navquery, &m_filters[ag->params.queryFilterType]))
+ if (!ag->corridor.isValid(CHECK_LOOKAHEAD, m_navquery, &m_filters[ag->queryFilterType]))
{
// Fix current path.
// ag->corridor.trimInvalidPath(agentRef, agentPos, m_navquery, &m_filter);
@@ -1041,6 +1043,13 @@ void dtCrowd::update(const float dt, dtCrowdAgentDebugInfo* debug)
dtCrowdAgent** agents = m_activeAgents;
int nagents = getActiveAgents(agents, m_maxAgents);
+
+ // [F]
+ for (int i = 0; i < nagents; ++i)
+ {
+ dtCrowdAgent* ag = agents[i];
+ ag->queryFilterType = ag->params.queryFilterType;
+ }
// Check that all agents still have valid paths.
checkPathValidity(agents, nagents, dt);
@@ -1072,10 +1081,10 @@ void dtCrowd::update(const float dt, dtCrowdAgentDebugInfo* debug)
// if it has become invalid.
const float updateThr = ag->params.collisionQueryRange*0.25f;
if (dtVdist2DSqr(ag->npos, ag->boundary.getCenter()) > dtSqr(updateThr) ||
- !ag->boundary.isValid(m_navquery, &m_filters[ag->params.queryFilterType]))
+ !ag->boundary.isValid(m_navquery, &m_filters[ag->queryFilterType]))
{
ag->boundary.update(ag->corridor.getFirstPoly(), ag->npos, ag->params.collisionQueryRange,
- m_navquery, &m_filters[ag->params.queryFilterType]);
+ m_navquery, &m_filters[ag->queryFilterType]);
}
// Query neighbour agents
ag->nneis = getNeighbours(ag->npos, ag->params.height, ag->params.collisionQueryRange,
@@ -1097,14 +1106,14 @@ void dtCrowd::update(const float dt, dtCrowdAgentDebugInfo* debug)
// Find corners for steering
ag->ncorners = ag->corridor.findCorners(ag->cornerVerts, ag->cornerFlags, ag->cornerPolys,
- DT_CROWDAGENT_MAX_CORNERS, m_navquery, &m_filters[ag->params.queryFilterType]);
+ DT_CROWDAGENT_MAX_CORNERS, m_navquery, &m_filters[ag->queryFilterType]);
// Check to see if the corner after the next corner is directly visible,
// and short cut to there.
if ((ag->params.updateFlags & DT_CROWD_OPTIMIZE_VIS) && ag->ncorners > 0)
{
const float* target = &ag->cornerVerts[dtMin(1,ag->ncorners-1)*3];
- ag->corridor.optimizePathVisibility(target, ag->params.pathOptimizationRange, m_navquery, &m_filters[ag->params.queryFilterType]);
+ ag->corridor.optimizePathVisibility(target, ag->params.pathOptimizationRange, m_navquery, &m_filters[ag->queryFilterType]);
// Copy data for debug purposes.
if (debugIdx == i)
@@ -1276,7 +1285,7 @@ void dtCrowd::update(const float dt, dtCrowdAgentDebugInfo* debug)
if (debugIdx == i)
vod = debug->vod;
- // Sample new safe velocity.
+ // MySample new safe velocity.
bool adaptive = true;
int ns = 0;
@@ -1384,7 +1393,7 @@ void dtCrowd::update(const float dt, dtCrowdAgentDebugInfo* debug)
continue;
// Move along navmesh.
- ag->corridor.movePosition(ag->npos, m_navquery, &m_filters[ag->params.queryFilterType]);
+ ag->corridor.movePosition(ag->npos, m_navquery, &m_filters[ag->queryFilterType]);
// Get valid constrained position back.
dtVcopy(ag->npos, ag->corridor.getPos());
@@ -1434,3 +1443,5 @@ void dtCrowd::update(const float dt, dtCrowdAgentDebugInfo* debug)
}
}
+
+
From 907b67bd184d056844ab2354d2004d15a956276c Mon Sep 17 00:00:00 2001
From: JimmyJames707
Date: Fri, 11 Apr 2014 22:17:21 +1000
Subject: [PATCH 36/49] query filter for each agent type with changes
---
DetourCrowd/Include/DetourCrowd.h | 35 +++++++---------
DetourCrowd/Source/DetourCrowd.cpp | 65 +++++++++++++-----------------
2 files changed, 41 insertions(+), 59 deletions(-)
diff --git a/DetourCrowd/Include/DetourCrowd.h b/DetourCrowd/Include/DetourCrowd.h
index c8cc392..24c815b 100644
--- a/DetourCrowd/Include/DetourCrowd.h
+++ b/DetourCrowd/Include/DetourCrowd.h
@@ -45,6 +45,9 @@ static const int DT_CROWDAGENT_MAX_CORNERS = 4;
/// dtCrowdAgentParams::obstacleAvoidanceType
static const int DT_CROWD_MAX_OBSTAVOIDANCE_PARAMS = 8;
+/// The maximum number of query filter types
+static const int DT_CROWD_MAX_QUERY_FILTER_TYPE = 16;
+
/// Provides neighbor data for agents managed by the crowd.
/// @ingroup crowd
/// @see dtCrowdAgent::neis, dtCrowd
@@ -87,7 +90,7 @@ struct dtCrowdAgentParams
/// [Limits: 0 <= value <= #DT_CROWD_MAX_OBSTAVOIDANCE_PARAMS]
unsigned char obstacleAvoidanceType;
- // [F]
+ /// The index of the query filter used by this agent.
unsigned char queryFilterType;
/// User defined data attached to the agent.
@@ -105,9 +108,6 @@ enum MoveRequestState
DT_CROWDAGENT_TARGET_VELOCITY,
};
-// [F]
-static const int DT_CROWD_MAX_QUERY_FILTER_TYPE = 16;
-
/// Represents an agent managed by a #dtCrowd object.
/// @ingroup crowd
struct dtCrowdAgent
@@ -163,9 +163,6 @@ struct dtCrowdAgent
dtPathQueueRef targetPathqRef; ///< Path finder ref.
bool targetReplan; ///< Flag indicating that the current path is being replanned.
float targetReplanTime; /// = 0 && i < DT_CROWD_MAX_QUERY_FILTER_TYPE) ? &m_filters[i] : 0; }
-
+ inline const dtQueryFilter* getFilter(const int i) const { return (i >= 0 && i < DT_CROWD_MAX_QUERY_FILTER_TYPE) ? &m_filters[i] : 0; }
+
/// Gets the filter used by the crowd.
/// @return The filter used by the crowd.
- //dtQueryFilter* getEditableFilter() { return &m_filter; }
- // [F]
- //dtQueryFilter* getEditableFilter() {return &m_filters[0];}
- dtQueryFilter* getIndexedEditableFilter(const int i = 0) { return (i >= 0 && i < DT_CROWD_MAX_QUERY_FILTER_TYPE) ? &m_filters[i] : 0; }
+ inline dtQueryFilter* getEditableFilter(const int i) { return (i >= 0 && i < DT_CROWD_MAX_QUERY_FILTER_TYPE) ? &m_filters[i] : 0; }
/// Gets the search extents [(x, y, z)] used by the crowd for query operations.
/// @return The search extents used by the crowd. [(x, y, z)]
diff --git a/DetourCrowd/Source/DetourCrowd.cpp b/DetourCrowd/Source/DetourCrowd.cpp
index 4746f82..4297b65 100644
--- a/DetourCrowd/Source/DetourCrowd.cpp
+++ b/DetourCrowd/Source/DetourCrowd.cpp
@@ -482,7 +482,16 @@ int dtCrowd::getAgentCount() const
/// @par
///
/// Agents in the pool may not be in use. Check #dtCrowdAgent.active before using the returned object.
-dtCrowdAgent* dtCrowd::getAgent(const int idx)
+const dtCrowdAgent* dtCrowd::getAgent(const int idx)
+{
+ if (idx < 0 || idx >= m_maxAgents)
+ return 0;
+ return &m_agents[idx];
+}
+
+///
+/// Agents in the pool may not be in use. Check #dtCrowdAgent.active before using the returned object.
+dtCrowdAgent* dtCrowd::getEditableAgent(const int idx)
{
if (idx < 0 || idx >= m_maxAgents)
return 0;
@@ -514,20 +523,13 @@ int dtCrowd::addAgent(const float* pos, const dtCrowdAgentParams* params)
if (idx == -1)
return -1;
- dtCrowdAgent* ag = &m_agents[idx];
-
- // [F]
- if (params->queryFilterType < DT_CROWD_MAX_QUERY_FILTER_TYPE)
- ag->queryFilterType = params->queryFilterType;
- else
- ag->queryFilterType = 0;
-
+ dtCrowdAgent* ag = &m_agents[idx];
// Find nearest position on navmesh and place the agent there.
float nearest[3];
dtPolyRef ref = 0;
dtVcopy(nearest, pos);
- dtStatus status = m_navquery->findNearestPoly(pos, m_ext, &m_filters[ag->queryFilterType], &ref, nearest);
+ dtStatus status = m_navquery->findNearestPoly(pos, m_ext, &m_filters[ag->params.queryFilterType], &ref, nearest);
if (dtStatusFailed(status))
{
dtVcopy(nearest, pos);
@@ -700,7 +702,7 @@ void dtCrowd::updateMoveRequest(const float /*dt*/)
// Quick seach towards the goal.
static const int MAX_ITER = 20;
- m_navquery->initSlicedFindPath(path[0], ag->targetRef, ag->npos, ag->targetPos, &m_filters[ag->queryFilterType]);
+ m_navquery->initSlicedFindPath(path[0], ag->targetRef, ag->npos, ag->targetPos, &m_filters[ag->params.queryFilterType]);
m_navquery->updateSlicedFindPath(MAX_ITER, 0);
dtStatus status = 0;
if (ag->targetReplan) // && npath > 10)
@@ -767,7 +769,7 @@ void dtCrowd::updateMoveRequest(const float /*dt*/)
{
dtCrowdAgent* ag = queue[i];
ag->targetPathqRef = m_pathq.request(ag->corridor.getLastPoly(), ag->targetRef,
- ag->corridor.getTarget(), ag->targetPos, &m_filters[ag->queryFilterType]);
+ ag->corridor.getTarget(), ag->targetPos, &m_filters[ag->params.queryFilterType]);
if (ag->targetPathqRef != DT_PATHQ_INVALID)
ag->targetState = DT_CROWDAGENT_TARGET_WAITING_FOR_PATH;
}
@@ -921,7 +923,7 @@ void dtCrowd::updateTopologyOptimization(dtCrowdAgent** agents, const int nagent
for (int i = 0; i < nqueue; ++i)
{
dtCrowdAgent* ag = queue[i];
- ag->corridor.optimizePathTopology(m_navquery, &m_filters[ag->queryFilterType]);
+ ag->corridor.optimizePathTopology(m_navquery, &m_filters[ag->params.queryFilterType]);
ag->topologyOptTime = 0;
}
@@ -938,10 +940,6 @@ void dtCrowd::checkPathValidity(dtCrowdAgent** agents, const int nagents, const
if (ag->state != DT_CROWDAGENT_STATE_WALKING)
continue;
-
- // BUG FIX: https://groups.google.com/forum/#!topic/recastnavigation/E6khmmKJkzk
- //if (ag->targetState == DT_CROWDAGENT_TARGET_NONE || ag->targetState == DT_CROWDAGENT_TARGET_VELOCITY)
- // continue;
ag->targetReplanTime += dt;
@@ -952,14 +950,14 @@ void dtCrowd::checkPathValidity(dtCrowdAgent** agents, const int nagents, const
float agentPos[3];
dtPolyRef agentRef = ag->corridor.getFirstPoly();
dtVcopy(agentPos, ag->npos);
- if (!m_navquery->isValidPolyRef(agentRef, &m_filters[ag->queryFilterType]))
+ if (!m_navquery->isValidPolyRef(agentRef, &m_filters[ag->params.queryFilterType]))
{
// Current location is not valid, try to reposition.
// TODO: this can snap agents, how to handle that?
float nearest[3];
dtVcopy(nearest, agentPos);
agentRef = 0;
- m_navquery->findNearestPoly(ag->npos, m_ext, &m_filters[ag->queryFilterType], &agentRef, nearest);
+ m_navquery->findNearestPoly(ag->npos, m_ext, &m_filters[ag->params.queryFilterType], &agentRef, nearest);
dtVcopy(agentPos, nearest);
if (!agentRef)
@@ -981,20 +979,20 @@ void dtCrowd::checkPathValidity(dtCrowdAgent** agents, const int nagents, const
replan = true;
}
- // Davo BUG FIX: https://groups.google.com/forum/#!topic/recastnavigation/E6khmmKJkzk
+ // If the agent does not have move target or is controlled by velocity, no need to recover the target nor replan.
if (ag->targetState == DT_CROWDAGENT_TARGET_NONE || ag->targetState == DT_CROWDAGENT_TARGET_VELOCITY)
continue;
// Try to recover move request position.
if (ag->targetState != DT_CROWDAGENT_TARGET_NONE && ag->targetState != DT_CROWDAGENT_TARGET_FAILED)
{
- if (!m_navquery->isValidPolyRef(ag->targetRef, &m_filters[ag->queryFilterType]))
+ if (!m_navquery->isValidPolyRef(ag->targetRef, &m_filters[ag->params.queryFilterType]))
{
// Current target is not valid, try to reposition.
float nearest[3];
dtVcopy(nearest, ag->targetPos);
ag->targetRef = 0;
- m_navquery->findNearestPoly(ag->targetPos, m_ext, &m_filters[ag->queryFilterType], &ag->targetRef, nearest);
+ m_navquery->findNearestPoly(ag->targetPos, m_ext, &m_filters[ag->params.queryFilterType], &ag->targetRef, nearest);
dtVcopy(ag->targetPos, nearest);
replan = true;
}
@@ -1007,7 +1005,7 @@ void dtCrowd::checkPathValidity(dtCrowdAgent** agents, const int nagents, const
}
// If nearby corridor is not valid, replan.
- if (!ag->corridor.isValid(CHECK_LOOKAHEAD, m_navquery, &m_filters[ag->queryFilterType]))
+ if (!ag->corridor.isValid(CHECK_LOOKAHEAD, m_navquery, &m_filters[ag->params.queryFilterType]))
{
// Fix current path.
// ag->corridor.trimInvalidPath(agentRef, agentPos, m_navquery, &m_filter);
@@ -1043,13 +1041,6 @@ void dtCrowd::update(const float dt, dtCrowdAgentDebugInfo* debug)
dtCrowdAgent** agents = m_activeAgents;
int nagents = getActiveAgents(agents, m_maxAgents);
-
- // [F]
- for (int i = 0; i < nagents; ++i)
- {
- dtCrowdAgent* ag = agents[i];
- ag->queryFilterType = ag->params.queryFilterType;
- }
// Check that all agents still have valid paths.
checkPathValidity(agents, nagents, dt);
@@ -1081,10 +1072,10 @@ void dtCrowd::update(const float dt, dtCrowdAgentDebugInfo* debug)
// if it has become invalid.
const float updateThr = ag->params.collisionQueryRange*0.25f;
if (dtVdist2DSqr(ag->npos, ag->boundary.getCenter()) > dtSqr(updateThr) ||
- !ag->boundary.isValid(m_navquery, &m_filters[ag->queryFilterType]))
+ !ag->boundary.isValid(m_navquery, &m_filters[ag->params.queryFilterType]))
{
ag->boundary.update(ag->corridor.getFirstPoly(), ag->npos, ag->params.collisionQueryRange,
- m_navquery, &m_filters[ag->queryFilterType]);
+ m_navquery, &m_filters[ag->params.queryFilterType]);
}
// Query neighbour agents
ag->nneis = getNeighbours(ag->npos, ag->params.height, ag->params.collisionQueryRange,
@@ -1106,14 +1097,14 @@ void dtCrowd::update(const float dt, dtCrowdAgentDebugInfo* debug)
// Find corners for steering
ag->ncorners = ag->corridor.findCorners(ag->cornerVerts, ag->cornerFlags, ag->cornerPolys,
- DT_CROWDAGENT_MAX_CORNERS, m_navquery, &m_filters[ag->queryFilterType]);
+ DT_CROWDAGENT_MAX_CORNERS, m_navquery, &m_filters[ag->params.queryFilterType]);
// Check to see if the corner after the next corner is directly visible,
// and short cut to there.
if ((ag->params.updateFlags & DT_CROWD_OPTIMIZE_VIS) && ag->ncorners > 0)
{
const float* target = &ag->cornerVerts[dtMin(1,ag->ncorners-1)*3];
- ag->corridor.optimizePathVisibility(target, ag->params.pathOptimizationRange, m_navquery, &m_filters[ag->queryFilterType]);
+ ag->corridor.optimizePathVisibility(target, ag->params.pathOptimizationRange, m_navquery, &m_filters[ag->params.queryFilterType]);
// Copy data for debug purposes.
if (debugIdx == i)
@@ -1285,7 +1276,7 @@ void dtCrowd::update(const float dt, dtCrowdAgentDebugInfo* debug)
if (debugIdx == i)
vod = debug->vod;
- // MySample new safe velocity.
+ // Sample new safe velocity.
bool adaptive = true;
int ns = 0;
@@ -1393,7 +1384,7 @@ void dtCrowd::update(const float dt, dtCrowdAgentDebugInfo* debug)
continue;
// Move along navmesh.
- ag->corridor.movePosition(ag->npos, m_navquery, &m_filters[ag->queryFilterType]);
+ ag->corridor.movePosition(ag->npos, m_navquery, &m_filters[ag->params.queryFilterType]);
// Get valid constrained position back.
dtVcopy(ag->npos, ag->corridor.getPos());
@@ -1443,5 +1434,3 @@ void dtCrowd::update(const float dt, dtCrowdAgentDebugInfo* debug)
}
}
-
-
From e29b2c8a512b40ecec96b3ce9993c8f3799b3700 Mon Sep 17 00:00:00 2001
From: JimmyJames707
Date: Fri, 11 Apr 2014 22:33:33 +1000
Subject: [PATCH 37/49] query filter for each agent type
This has the changes Mikko requested
---
DetourCrowd/Include/DetourCrowd.h | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/DetourCrowd/Include/DetourCrowd.h b/DetourCrowd/Include/DetourCrowd.h
index 24c815b..25c8b42 100644
--- a/DetourCrowd/Include/DetourCrowd.h
+++ b/DetourCrowd/Include/DetourCrowd.h
@@ -45,7 +45,11 @@ static const int DT_CROWDAGENT_MAX_CORNERS = 4;
/// dtCrowdAgentParams::obstacleAvoidanceType
static const int DT_CROWD_MAX_OBSTAVOIDANCE_PARAMS = 8;
-/// The maximum number of query filter types
+/// The maximum number of query filter types supported by the crowd manager.
+/// @ingroup crowd
+/// @see dtQueryFilter, dtCrowd::getFilter() dtCrowd::getEditableFilter(),
+/// dtCrowdAgentParams::queryFilterType
+
static const int DT_CROWD_MAX_QUERY_FILTER_TYPE = 16;
/// Provides neighbor data for agents managed by the crowd.
From 52efad386b3e454d3c64131417875e0e948abf82 Mon Sep 17 00:00:00 2001
From: JimmyJames707
Date: Fri, 11 Apr 2014 22:36:00 +1000
Subject: [PATCH 38/49] query filter for each agent type
This has the changes Mikko requested
---
DetourCrowd/Include/DetourCrowd.h | 1 -
1 file changed, 1 deletion(-)
diff --git a/DetourCrowd/Include/DetourCrowd.h b/DetourCrowd/Include/DetourCrowd.h
index 25c8b42..9f92cdf 100644
--- a/DetourCrowd/Include/DetourCrowd.h
+++ b/DetourCrowd/Include/DetourCrowd.h
@@ -49,7 +49,6 @@ static const int DT_CROWD_MAX_OBSTAVOIDANCE_PARAMS = 8;
/// @ingroup crowd
/// @see dtQueryFilter, dtCrowd::getFilter() dtCrowd::getEditableFilter(),
/// dtCrowdAgentParams::queryFilterType
-
static const int DT_CROWD_MAX_QUERY_FILTER_TYPE = 16;
/// Provides neighbor data for agents managed by the crowd.
From 19871f5f5ff6358664002a02b3c0295868178a7c Mon Sep 17 00:00:00 2001
From: Stephen Pratt
Date: Wed, 23 Apr 2014 11:59:18 -0600
Subject: [PATCH 39/49] Add Docs/html to Git Ignore
Docs/html holds the files generated by Doxygen. It should be ignored by
Git.
---
.gitignore | 3 +++
1 file changed, 3 insertions(+)
diff --git a/.gitignore b/.gitignore
index b321a15..fe4cf93 100644
--- a/.gitignore
+++ b/.gitignore
@@ -38,3 +38,6 @@ Thumbs.db
## SDL contrib
RecastDemo/Contrib/SDL/*
+
+## Generated doc files
+Docs/html
From 3458b9c820a2c793d5743fcdb26aa4715e661a43 Mon Sep 17 00:00:00 2001
From: Stephen Pratt
Date: Wed, 23 Apr 2014 12:09:50 -0600
Subject: [PATCH 40/49] Upgrade to Doxygen 1.8.6 from 1.7.3
Upgrades Doxygen configuration files to 1.8.6. The upgrade results in
breaking changes (can't use original configuration) so all Doxygen files
were regenerated and updated as needed.
The Doxyfile shows a large number of changes, but most are the comments
and new settings created by the Doxygen auto-upgrade process.
Otherwise, some obsolete settings were removed and the logo was set to
none since its source image no longer exists in the repository.
Where appropriate the configuration files were updated to link to the
new Git repository. This is the only significant document content
change.
---
Docs/DoxygenLayout.xml | 49 +-
Docs/customdoxygen.css | 847 --------------
Docs/footer.html | 23 +-
Docs/header.html | 19 +-
Doxyfile | 2506 +++++++++++++++++++++++++---------------
5 files changed, 1629 insertions(+), 1815 deletions(-)
delete mode 100644 Docs/customdoxygen.css
diff --git a/Docs/DoxygenLayout.xml b/Docs/DoxygenLayout.xml
index f847812..b20079c 100644
--- a/Docs/DoxygenLayout.xml
+++ b/Docs/DoxygenLayout.xml
@@ -1,24 +1,24 @@
+
-
+
-
-
-
-
+
+
+
+
-
-
-
+
+
+
-
@@ -28,10 +28,11 @@
-
-
+
+
+
@@ -61,10 +62,13 @@
-
+
+
+
+
@@ -72,6 +76,7 @@
+
@@ -81,6 +86,7 @@
+
@@ -88,8 +94,9 @@
-
+
+
@@ -100,7 +107,8 @@
-
+
+
@@ -108,6 +116,7 @@
+
@@ -115,8 +124,8 @@
-
+
@@ -128,15 +137,14 @@
-
-
+
-
-
-
+
+
+
@@ -152,6 +160,7 @@
+
diff --git a/Docs/customdoxygen.css b/Docs/customdoxygen.css
deleted file mode 100644
index 06ee291..0000000
--- a/Docs/customdoxygen.css
+++ /dev/null
@@ -1,847 +0,0 @@
-/* The standard CSS for doxygen */
-
-body, table, div, p, dl {
- font-family: Lucida Grande, Verdana, Geneva, Arial, sans-serif;
- font-size: 12px;
-}
-
-/* @group Heading Levels */
-
-h1 {
- font-size: 150%;
-}
-
-.title {
- font-size: 150%;
- font-weight: bold;
- margin: 10px 2px;
-}
-
-h2 {
- font-size: 120%;
-}
-
-div.textblock h2 {
- border-bottom-width: 1px;
- border-bottom-style: solid;
- border-bottom-color: #336699;
-}
-
-h3 {
- font-size: 100%;
-}
-
-dt {
- font-weight: bold;
-}
-
-div.multicol {
- -moz-column-gap: 1em;
- -webkit-column-gap: 1em;
- -moz-column-count: 3;
- -webkit-column-count: 3;
-}
-
-p.startli, p.startdd, p.starttd {
- margin-top: 2px;
-}
-
-p.endli {
- margin-bottom: 0px;
-}
-
-p.enddd {
- margin-bottom: 4px;
-}
-
-p.endtd {
- margin-bottom: 2px;
-}
-
-/* @end */
-
-caption {
- font-weight: bold;
-}
-
-span.legend {
- font-size: 70%;
- text-align: center;
-}
-
-h3.version {
- font-size: 90%;
- text-align: center;
-}
-
-div.qindex, div.navtab{
- background-color: #EBEFF6;
- border: 1px solid #A3B4D7;
- text-align: center;
- margin: 2px;
- padding: 2px;
-}
-
-div.qindex, div.navpath {
- width: 100%;
- line-height: 140%;
-}
-
-div.navtab {
- margin-right: 15px;
-}
-
-/* @group Link Styling */
-
-a {
- color: #3D578C;
- font-weight: normal;
- text-decoration: none;
-}
-
-.contents a:visited {
- color: #4665A2;
-}
-
-a:hover {
- text-decoration: underline;
-}
-
-a.qindex {
- font-weight: bold;
-}
-
-a.qindexHL {
- font-weight: bold;
- background-color: #9CAFD4;
- color: #ffffff;
- border: 1px double #869DCA;
-}
-
-.contents a.qindexHL:visited {
- color: #ffffff;
-}
-
-a.el {
- font-weight: bold;
-}
-
-a.elRef {
-}
-
-a.code {
- color: #4665A2;
-}
-
-a.codeRef {
- color: #4665A2;
-}
-
-/* @end */
-
-dl.el {
- margin-left: -1cm;
-}
-
-.fragment {
- font-family: monospace, fixed;
- font-size: 105%;
-}
-
-pre.fragment {
- border: 1px solid #C4CFE5;
- background-color: #FBFCFD;
- padding: 4px 6px;
- margin: 4px 8px 4px 2px;
- overflow: auto;
- word-wrap: break-word;
- font-size: 9pt;
- line-height: 125%;
-}
-
-div.ah {
- background-color: black;
- font-weight: bold;
- color: #ffffff;
- margin-bottom: 3px;
- margin-top: 3px;
- padding: 0.2em;
- border: solid thin #333;
- border-radius: 0.5em;
- -webkit-border-radius: .5em;
- -moz-border-radius: .5em;
- box-shadow: 2px 2px 3px #999;
- -webkit-box-shadow: 2px 2px 3px #999;
- -moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px;
- background-image: -webkit-gradient(linear, left top, left bottom, from(#eee), to(#000),color-stop(0.3, #444));
- background-image: -moz-linear-gradient(center top, #eee 0%, #444 40%, #000);
-}
-
-div.groupHeader {
- margin-left: 16px;
- margin-top: 12px;
- font-weight: bold;
- padding-bottom: 8px;
-}
-
-div.groupText {
- margin-left: 16px;
- font-style: italic;
-}
-
-body {
- background: white;
- color: black;
- margin: 0;
-}
-
-div.contents {
- margin-top: 10px;
- margin-left: 10px;
- margin-right: 5px;
-}
-
-td.indexkey {
- background-color: #EBEFF6;
- font-weight: bold;
- border: 1px solid #C4CFE5;
- margin: 2px 0px 2px 0;
- padding: 2px 10px;
-}
-
-td.indexvalue {
- background-color: #EBEFF6;
- border: 1px solid #C4CFE5;
- padding: 2px 10px;
- margin: 2px 0px;
-}
-
-tr.memlist {
- background-color: #EEF1F7;
-}
-
-p.formulaDsp {
- text-align: center;
-}
-
-img.formulaDsp {
-
-}
-
-img.formulaInl {
- vertical-align: middle;
-}
-
-div.center {
- text-align: center;
- margin-top: 0px;
- margin-bottom: 0px;
- padding: 0px;
-}
-
-div.center img {
- border: 0px;
-}
-
-address.footer {
- text-align: right;
- padding-right: 12px;
-}
-
-img.footer {
- border: 0px;
- vertical-align: middle;
-}
-
-/* @group Code Colorization */
-
-span.keyword {
- color: #008000
-}
-
-span.keywordtype {
- color: #604020
-}
-
-span.keywordflow {
- color: #e08000
-}
-
-span.comment {
- color: #800000
-}
-
-span.preprocessor {
- color: #806020
-}
-
-span.stringliteral {
- color: #002080
-}
-
-span.charliteral {
- color: #008080
-}
-
-span.vhdldigit {
- color: #ff00ff
-}
-
-span.vhdlchar {
- color: #000000
-}
-
-span.vhdlkeyword {
- color: #700070
-}
-
-span.vhdllogic {
- color: #ff0000
-}
-
-/* @end */
-
-/*
-.search {
- color: #003399;
- font-weight: bold;
-}
-
-form.search {
- margin-bottom: 0px;
- margin-top: 0px;
-}
-
-input.search {
- font-size: 75%;
- color: #000080;
- font-weight: normal;
- background-color: #e8eef2;
-}
-*/
-
-td.tiny {
- font-size: 75%;
-}
-
-.dirtab {
- padding: 4px;
- border-collapse: collapse;
- border: 1px solid #A3B4D7;
-}
-
-th.dirtab {
- background: #EBEFF6;
- font-weight: bold;
-}
-
-hr {
- height: 0px;
- border: none;
- border-top: 1px solid #4A6AAA;
-}
-
-hr.footer {
- height: 1px;
-}
-
-/* @group Member Descriptions */
-
-table.memberdecls {
- border-spacing: 0px;
- padding: 0px;
-}
-
-.mdescLeft, .mdescRight,
-.memItemLeft, .memItemRight,
-.memTemplItemLeft, .memTemplItemRight, .memTemplParams {
- background-color: #F9FAFC;
- border: none;
- margin: 4px;
- padding: 8px 0 8px 8px;
-}
-
-.mdescLeft {
- padding: 0px 8px 4px 8px;
- color: #555;
-}
-
-.mdescRight {
- padding: 4px 8px 8px 24px;
- color: #555;
-}
-
-.memItemLeft, .memItemRight, .memTemplParams {
- border-top: 1px solid #C4CFE5;
-}
-
-.memItemLeft, .memTemplItemLeft {
- white-space: nowrap;
-}
-
-.memItemRight {
- width: 100%;
-}
-
-.memTemplParams {
- color: #4665A2;
- white-space: nowrap;
-}
-
-/* @end */
-
-/* @group Member Details */
-
-/* Styles for detailed member documentation */
-
-.memtemplate {
- font-size: 80%;
- color: #4665A2;
- font-weight: normal;
- margin-left: 9px;
-}
-
-.memnav {
- background-color: #EBEFF6;
- border: 1px solid #A3B4D7;
- text-align: center;
- margin: 2px;
- margin-right: 15px;
- padding: 2px;
-}
-
-.mempage {
- width: 100%;
-}
-
-.memitem {
- padding: 0;
- margin-bottom: 10px;
- margin-right: 5px;
-}
-
-.memname {
- white-space: nowrap;
- font-weight: bold;
- margin-left: 6px;
-}
-
-.memproto {
- border-top: 1px solid #A8B8D9;
- border-left: 1px solid #A8B8D9;
- border-right: 1px solid #A8B8D9;
- padding: 6px 0px 6px 0px;
- color: #253555;
- font-weight: bold;
- text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9);
- /* opera specific markup */
- box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15);
- border-top-right-radius: 8px;
- border-top-left-radius: 8px;
- /* firefox specific markup */
- -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px;
- -moz-border-radius-topright: 8px;
- -moz-border-radius-topleft: 8px;
- /* webkit specific markup */
- -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15);
- -webkit-border-top-right-radius: 8px;
- -webkit-border-top-left-radius: 8px;
- background-image:url('nav_f.png');
- background-repeat:repeat-x;
- background-color: #E2E8F2;
-
-}
-
-.memdoc {
- border-bottom: 1px solid #A8B8D9;
- border-left: 1px solid #A8B8D9;
- border-right: 1px solid #A8B8D9;
- padding: 2px 5px;
- background-color: #FBFCFD;
- border-top-width: 0;
- /* opera specific markup */
- border-bottom-left-radius: 8px;
- border-bottom-right-radius: 8px;
- box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15);
- /* firefox specific markup */
- -moz-border-radius-bottomleft: 8px;
- -moz-border-radius-bottomright: 8px;
- -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px;
- background-image: -moz-linear-gradient(center top, #FFFFFF 0%, #FFFFFF 60%, #F7F8FB 95%, #EEF1F7);
- /* webkit specific markup */
- -webkit-border-bottom-left-radius: 8px;
- -webkit-border-bottom-right-radius: 8px;
- -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15);
- background-image: -webkit-gradient(linear,center top,center bottom,from(#FFFFFF), color-stop(0.6,#FFFFFF), color-stop(0.60,#FFFFFF), color-stop(0.95,#F7F8FB), to(#EEF1F7));
-}
-
-.paramkey {
- text-align: right;
-}
-
-.paramtype {
- white-space: nowrap;
-}
-
-.paramname {
- color: #602020;
- white-space: nowrap;
-}
-.paramname em {
- font-style: normal;
-}
-
-.params, .retval, .exception, .tparams {
- border-spacing: 6px 2px;
-}
-
-.params .paramname, .retval .paramname {
- font-weight: bold;
- vertical-align: top;
-}
-
-.params .paramtype {
- font-style: italic;
- vertical-align: top;
-}
-
-.params .paramdir {
- font-family: "courier new",courier,monospace;
- vertical-align: top;
-}
-
-
-
-
-/* @end */
-
-/* @group Directory (tree) */
-
-/* for the tree view */
-
-.ftvtree {
- font-family: sans-serif;
- margin: 0px;
-}
-
-/* these are for tree view when used as main index */
-
-.directory {
- font-size: 9pt;
- font-weight: bold;
- margin: 5px;
-}
-
-.directory h3 {
- margin: 0px;
- margin-top: 1em;
- font-size: 11pt;
-}
-
-/*
-The following two styles can be used to replace the root node title
-with an image of your choice. Simply uncomment the next two styles,
-specify the name of your image and be sure to set 'height' to the
-proper pixel height of your image.
-*/
-
-/*
-.directory h3.swap {
- height: 61px;
- background-repeat: no-repeat;
- background-image: url("yourimage.gif");
-}
-.directory h3.swap span {
- display: none;
-}
-*/
-
-.directory > h3 {
- margin-top: 0;
-}
-
-.directory p {
- margin: 0px;
- white-space: nowrap;
-}
-
-.directory div {
- display: none;
- margin: 0px;
-}
-
-.directory img {
- vertical-align: -30%;
-}
-
-/* these are for tree view when not used as main index */
-
-.directory-alt {
- font-size: 100%;
- font-weight: bold;
-}
-
-.directory-alt h3 {
- margin: 0px;
- margin-top: 1em;
- font-size: 11pt;
-}
-
-.directory-alt > h3 {
- margin-top: 0;
-}
-
-.directory-alt p {
- margin: 0px;
- white-space: nowrap;
-}
-
-.directory-alt div {
- display: none;
- margin: 0px;
-}
-
-.directory-alt img {
- vertical-align: -30%;
-}
-
-/* @end */
-
-div.dynheader {
- margin-top: 8px;
-}
-
-address {
- font-style: normal;
- color: #2A3D61;
-}
-
-table.doxtable {
- border-collapse:collapse;
-}
-
-table.doxtable td, table.doxtable th {
- border: 1px solid #2D4068;
- padding: 3px 7px 2px;
-}
-
-table.doxtable th {
- background-color: #374F7F;
- color: #FFFFFF;
- font-size: 110%;
- padding-bottom: 4px;
- padding-top: 5px;
- text-align:left;
-}
-
-.tabsearch {
- top: 0px;
- left: 10px;
- height: 36px;
- background-image: url('tab_b.png');
- z-index: 101;
- overflow: hidden;
- font-size: 13px;
-}
-
-.navpath ul
-{
- font-size: 11px;
- background-image:url('tab_b.png');
- background-repeat:repeat-x;
- height:30px;
- line-height:30px;
- color:#8AA0CC;
- border:solid 1px #C2CDE4;
- overflow:hidden;
- margin:0px;
- padding:0px;
-}
-
-.navpath li
-{
- list-style-type:none;
- float:left;
- padding-left:10px;
- padding-right:15px;
- background-image:url('bc_s.png');
- background-repeat:no-repeat;
- background-position:right;
- color:#364D7C;
-}
-
-.navpath li.navelem a
-{
- height:32px;
- display:block;
- text-decoration: none;
- outline: none;
-}
-
-.navpath li.navelem a:hover
-{
- color:#6884BD;
-}
-
-.navpath li.footer
-{
- list-style-type:none;
- float:right;
- padding-left:10px;
- padding-right:15px;
- background-image:none;
- background-repeat:no-repeat;
- background-position:right;
- color:#364D7C;
- font-size: 8pt;
-}
-
-
-div.summary
-{
- float: right;
- font-size: 8pt;
- padding-right: 5px;
- width: 50%;
- text-align: right;
-}
-
-div.summary a
-{
- white-space: nowrap;
-}
-
-div.ingroups
-{
- font-size: 8pt;
- padding-left: 5px;
- width: 50%;
- text-align: left;
-}
-
-div.ingroups a
-{
- white-space: nowrap;
-}
-
-div.header
-{
- background-image:url('nav_h.png');
- background-repeat:repeat-x;
- background-color: #F9FAFC;
- margin: 0px;
- border-bottom: 1px solid #C4CFE5;
-}
-
-div.headertitle
-{
- padding: 5px 5px 5px 10px;
-}
-
-dl
-{
- padding: 0 0 0 10px;
-}
-
-dl.note, dl.warning, dl.attention, dl.pre, dl.post, dl.invariant, dl.deprecated, dl.todo, dl.test, dl.bug
-{
- border-left:4px solid;
- padding: 0 0 0 6px;
-}
-
-dl.note
-{
- border-color: #D0C000;
-}
-
-dl.warning, dl.attention
-{
- border-color: #FF0000;
-}
-
-dl.pre, dl.post, dl.invariant
-{
- border-color: #00D000;
-}
-
-dl.deprecated
-{
- border-color: #505050;
-}
-
-dl.todo
-{
- border-color: #00C0E0;
-}
-
-dl.test
-{
- border-color: #3030E0;
-}
-
-dl.bug
-{
- border-color: #C08050;
-}
-
-#projectlogo
-{
- text-align: center;
- vertical-align: bottom;
- border-collapse: separate;
-}
-
-#projectlogo img
-{
- border: 0px none;
-}
-
-#projectname
-{
- font: 300% Tahoma, Arial,sans-serif;
- margin: 0px;
- padding: 2px 0px;
-}
-
-#projectbrief
-{
- font: 120% Tahoma, Arial,sans-serif;
- margin: 0px;
- padding: 0px;
-}
-
-#projectnumber
-{
- font: 50% Tahoma, Arial,sans-serif;
- margin: 0px;
- padding: 0px;
-}
-
-#titlearea
-{
- padding: 0px;
- margin: 0px;
- width: 100%;
- border-bottom: 1px solid #5373B4;
-}
-
-.image
-{
- text-align: left
-}
-
-.dotgraph
-{
- text-align: center;
-}
-
-.mscgraph
-{
- text-align: center;
-}
-
-.caption
-{
- font-weight: bold;
-}
-
diff --git a/Docs/footer.html b/Docs/footer.html
index fe53010..0448037 100644
--- a/Docs/footer.html
+++ b/Docs/footer.html
@@ -1,17 +1,20 @@
+
+
-
-
-
+
-
+
+
-
+
@@ -44,3 +52,4 @@ $mathjax
+
diff --git a/Doxyfile b/Doxyfile
index a464bf9..2516b3b 100644
--- a/Doxyfile
+++ b/Doxyfile
@@ -1,103 +1,114 @@
-# Doxyfile 1.7.3
+# Doxyfile 1.8.6
# This file describes the settings to be used by the documentation system
-# doxygen (www.doxygen.org) for a project
+# doxygen (www.doxygen.org) for a project.
#
-# All text after a hash (#) is considered a comment and will be ignored
+# All text after a double hash (##) is considered a comment and is placed in
+# front of the TAG it is preceding.
+#
+# All text after a single hash (#) is considered a comment and will be ignored.
# The format is:
-# TAG = value [value, ...]
-# For lists items can also be appended using:
-# TAG += value [value, ...]
-# Values that contain spaces should be placed between quotes (" ")
+# TAG = value [value, ...]
+# For lists, items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (\" \").
#---------------------------------------------------------------------------
# Project related configuration options
#---------------------------------------------------------------------------
-# This tag specifies the encoding used for all characters in the config file
-# that follow. The default is UTF-8 which is also the encoding used for all
-# text before the first occurrence of this tag. Doxygen uses libiconv (or the
-# iconv built into libc) for the transcoding. See
-# http://www.gnu.org/software/libiconv for the list of possible encodings.
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all text
+# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv
+# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv
+# for the list of possible encodings.
+# The default value is: UTF-8.
DOXYFILE_ENCODING = UTF-8
-# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
-# by quotes) that should identify the project.
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
+# double-quotes, unless you are using Doxywizard) that should identify the
+# project for which the documentation is generated. This name is used in the
+# title of most generated pages and in a few other places.
+# The default value is: My Project.
PROJECT_NAME = "Recast Navigation"
-# The PROJECT_NUMBER tag can be used to enter a project or revision number.
-# This could be handy for archiving the generated documentation or
-# if some version control system is used.
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
+# could be handy for archiving the generated documentation or if some version
+# control system is used.
PROJECT_NUMBER =
# Using the PROJECT_BRIEF tag one can provide an optional one line description
-# for a project that appears at the top of each page and should give viewer
-# a quick idea about the purpose of the project. Keep the description short.
+# for a project that appears at the top of each page and should give viewer a
+# quick idea about the purpose of the project. Keep the description short.
PROJECT_BRIEF =
-# With the PROJECT_LOGO tag one can specify an logo or icon that is
-# included in the documentation. The maximum height of the logo should not
-# exceed 55 pixels and the maximum width should not exceed 200 pixels.
-# Doxygen will copy the logo to the output directory.
+# With the PROJECT_LOGO tag one can specify an logo or icon that is included in
+# the documentation. The maximum height of the logo should not exceed 55 pixels
+# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo
+# to the output directory.
-PROJECT_LOGO = RecastDemo/Build/Icon.png
+PROJECT_LOGO =
-# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
-# base path where the generated documentation will be put.
-# If a relative path is entered, it will be relative to the location
-# where doxygen was started. If left blank the current directory will be used.
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
+# into which the generated documentation will be written. If a relative path is
+# entered, it will be relative to the location where doxygen was started. If
+# left blank the current directory will be used.
OUTPUT_DIRECTORY = Docs
-# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
-# 4096 sub-directories (in 2 levels) under the output directory of each output
-# format and will distribute the generated files over these directories.
-# Enabling this option can be useful when feeding doxygen a huge amount of
-# source files, where putting all generated files in the same directory would
-# otherwise cause performance problems for the file system.
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub-
+# directories (in 2 levels) under the output directory of each output format and
+# will distribute the generated files over these directories. Enabling this
+# option can be useful when feeding doxygen a huge amount of source files, where
+# putting all generated files in the same directory would otherwise causes
+# performance problems for the file system.
+# The default value is: NO.
CREATE_SUBDIRS = NO
-# The OUTPUT_LANGUAGE tag is used to specify the language in which all
-# documentation generated by doxygen is written. Doxygen will use this
-# information to generate all constant output in the proper language.
-# The default language is English, other supported languages are:
-# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
-# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
-# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
-# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
-# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak,
-# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
+# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
+# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
+# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
+# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
+# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
+# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
+# Ukrainian and Vietnamese.
+# The default value is: English.
OUTPUT_LANGUAGE = English
-# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
-# include brief member descriptions after the members that are listed in
-# the file and class documentation (similar to JavaDoc).
-# Set to NO to disable this.
+# If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member
+# descriptions after the members that are listed in the file and class
+# documentation (similar to Javadoc). Set to NO to disable this.
+# The default value is: YES.
BRIEF_MEMBER_DESC = YES
-# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
-# the brief description of a member or function before the detailed description.
-# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief
+# description of a member or function before the detailed description
+#
+# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
# brief descriptions will be completely suppressed.
+# The default value is: YES.
REPEAT_BRIEF = YES
-# This tag implements a quasi-intelligent brief description abbreviator
-# that is used to form the text in various listings. Each string
-# in this list, if found as the leading text of the brief description, will be
-# stripped from the text and the result after processing the whole list, is
-# used as the annotated text. Otherwise, the brief description is used as-is.
-# If left blank, the following values are used ("$name" is automatically
-# replaced with the name of the entity): "The $name class" "The $name widget"
-# "The $name file" "is" "provides" "specifies" "contains"
-# "represents" "a" "an" "the"
+# This tag implements a quasi-intelligent brief description abbreviator that is
+# used to form the text in various listings. Each string in this list, if found
+# as the leading text of the brief description, will be stripped from the text
+# and the result, after processing the whole list, is used as the annotated
+# text. Otherwise, the brief description is used as-is. If left blank, the
+# following values are used ($name is automatically replaced with the name of
+# the entity):The $name class, The $name widget, The $name file, is, provides,
+# specifies, contains, represents, a, an and the.
ABBREVIATE_BRIEF = "The $name class" \
"The $name widget" \
@@ -111,506 +122,632 @@ ABBREVIATE_BRIEF = "The $name class" \
an \
the
-# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
-# Doxygen will generate a detailed section even if there is only a brief
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# doxygen will generate a detailed section even if there is only a brief
# description.
+# The default value is: NO.
ALWAYS_DETAILED_SEC = NO
-# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
-# inherited members of a class in the documentation of that class as if those
-# members were ordinary class members. Constructors, destructors and assignment
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
# operators of the base classes will not be shown.
+# The default value is: NO.
INLINE_INHERITED_MEMB = NO
-# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
-# path before files name in the file list and in the header files. If set
-# to NO the shortest path that makes the file name unique will be used.
+# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path
+# before files name in the file list and in the header files. If set to NO the
+# shortest path that makes the file name unique will be used
+# The default value is: YES.
FULL_PATH_NAMES = NO
-# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
-# can be used to strip a user-defined part of the path. Stripping is
-# only done if one of the specified strings matches the left-hand part of
-# the path. The tag can be used to show relative paths in the file list.
-# If left blank the directory from which doxygen is run is used as the
-# path to strip.
+# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
+# Stripping is only done if one of the specified strings matches the left-hand
+# part of the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the path to
+# strip.
+#
+# Note that you can specify absolute paths here, but also relative paths, which
+# will be relative from the directory where doxygen is started.
+# This tag requires that the tag FULL_PATH_NAMES is set to YES.
STRIP_FROM_PATH =
-# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
-# the path mentioned in the documentation of a class, which tells
-# the reader which header file to include in order to use a class.
-# If left blank only the name of the header file containing the class
-# definition is used. Otherwise one should specify the include paths that
-# are normally passed to the compiler using the -I flag.
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
+# path mentioned in the documentation of a class, which tells the reader which
+# header file to include in order to use a class. If left blank only the name of
+# the header file containing the class definition is used. Otherwise one should
+# specify the list of include paths that are normally passed to the compiler
+# using the -I flag.
STRIP_FROM_INC_PATH =
-# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
-# (but less readable) file names. This can be useful if your file system
-# doesn't support long names like on DOS, Mac, or CD-ROM.
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
+# less readable) file names. This can be useful is your file systems doesn't
+# support long names like on DOS, Mac, or CD-ROM.
+# The default value is: NO.
SHORT_NAMES = NO
-# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
-# will interpret the first line (until the first dot) of a JavaDoc-style
-# comment as the brief description. If set to NO, the JavaDoc
-# comments will behave just like regular Qt-style comments
-# (thus requiring an explicit @brief command for a brief description.)
+# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
+# first line (until the first dot) of a Javadoc-style comment as the brief
+# description. If set to NO, the Javadoc-style will behave just like regular Qt-
+# style comments (thus requiring an explicit @brief command for a brief
+# description.)
+# The default value is: NO.
JAVADOC_AUTOBRIEF = YES
-# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
-# interpret the first line (until the first dot) of a Qt-style
-# comment as the brief description. If set to NO, the comments
-# will behave just like regular Qt-style comments (thus requiring
-# an explicit \brief command for a brief description.)
+# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
+# line (until the first dot) of a Qt-style comment as the brief description. If
+# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
+# requiring an explicit \brief command for a brief description.)
+# The default value is: NO.
QT_AUTOBRIEF = NO
-# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
-# treat a multi-line C++ special comment block (i.e. a block of //! or ///
-# comments) as a brief description. This used to be the default behaviour.
-# The new default is to treat a multi-line C++ comment block as a detailed
-# description. Set this tag to YES if you prefer the old behaviour instead.
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
+# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
+# a brief description. This used to be the default behavior. The new default is
+# to treat a multi-line C++ comment block as a detailed description. Set this
+# tag to YES if you prefer the old behavior instead.
+#
+# Note that setting this tag to YES also means that rational rose comments are
+# not recognized any more.
+# The default value is: NO.
MULTILINE_CPP_IS_BRIEF = NO
-# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
-# member inherits the documentation from any documented member that it
-# re-implements.
+# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
+# documentation from any documented member that it re-implements.
+# The default value is: YES.
INHERIT_DOCS = YES
-# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
-# a new page for each member. If set to NO, the documentation of a member will
-# be part of the file/class/namespace that contains it.
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a
+# new page for each member. If set to NO, the documentation of a member will be
+# part of the file/class/namespace that contains it.
+# The default value is: NO.
SEPARATE_MEMBER_PAGES = NO
-# The TAB_SIZE tag can be used to set the number of spaces in a tab.
-# Doxygen uses this value to replace tabs by spaces in code fragments.
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
+# uses this value to replace tabs by spaces in code fragments.
+# Minimum value: 1, maximum value: 16, default value: 4.
TAB_SIZE = 4
-# This tag can be used to specify a number of aliases that acts
-# as commands in the documentation. An alias has the form "name=value".
-# For example adding "sideeffect=\par Side Effects:\n" will allow you to
-# put the command \sideeffect (or @sideeffect) in the documentation, which
-# will result in a user-defined paragraph with heading "Side Effects:".
-# You can put \n's in the value part of an alias to insert newlines.
+# This tag can be used to specify a number of aliases that act as commands in
+# the documentation. An alias has the form:
+# name=value
+# For example adding
+# "sideeffect=@par Side Effects:\n"
+# will allow you to put the command \sideeffect (or @sideeffect) in the
+# documentation, which will result in a user-defined paragraph with heading
+# "Side Effects:". You can put \n's in the value part of an alias to insert
+# newlines.
ALIASES =
-# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
-# sources only. Doxygen will then generate output that is more tailored for C.
-# For instance, some of the names that are used will be different. The list
-# of all members will be omitted, etc.
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding "class=itcl::class"
+# will allow you to use the command class in the itcl::class meaning.
+
+TCL_SUBST =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C. For
+# instance, some of the names that are used will be different. The list of all
+# members will be omitted, etc.
+# The default value is: NO.
OPTIMIZE_OUTPUT_FOR_C = NO
-# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
-# sources only. Doxygen will then generate output that is more tailored for
-# Java. For instance, namespaces will be presented as packages, qualified
-# scopes will look different, etc.
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
+# Python sources only. Doxygen will then generate output that is more tailored
+# for that language. For instance, namespaces will be presented as packages,
+# qualified scopes will look different, etc.
+# The default value is: NO.
OPTIMIZE_OUTPUT_JAVA = NO
-# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
-# sources only. Doxygen will then generate output that is more tailored for
-# Fortran.
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources. Doxygen will then generate output that is tailored for Fortran.
+# The default value is: NO.
OPTIMIZE_FOR_FORTRAN = NO
-# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
-# sources. Doxygen will then generate output that is tailored for
-# VHDL.
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for VHDL.
+# The default value is: NO.
OPTIMIZE_OUTPUT_VHDL = NO
-# Doxygen selects the parser to use depending on the extension of the files it
-# parses. With this tag you can assign which parser to use for a given extension.
-# Doxygen has a built-in mapping, but you can override or extend it using this
-# tag. The format is ext=language, where ext is a file extension, and language
-# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C,
-# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make
-# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
-# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions
-# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen.
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension, and
+# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
+# C#, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL. For instance to make
+# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
+# (default is Fortran), use: inc=Fortran f=C.
+#
+# Note For files without extension you can use no_extension as a placeholder.
+#
+# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
+# the files are not read by doxygen.
EXTENSION_MAPPING =
-# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
-# to include (a tag file for) the STL sources as input, then you should
-# set this tag to YES in order to let doxygen match functions declarations and
-# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
-# func(std::string) {}). This also makes the inheritance and collaboration
+# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
+# according to the Markdown format, which allows for more readable
+# documentation. See http://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you can
+# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
+# case of backward compatibilities issues.
+# The default value is: YES.
+
+MARKDOWN_SUPPORT = YES
+
+# When enabled doxygen tries to link words that correspond to documented
+# classes, or namespaces to their corresponding documentation. Such a link can
+# be prevented in individual cases by by putting a % sign in front of the word
+# or globally by setting AUTOLINK_SUPPORT to NO.
+# The default value is: YES.
+
+AUTOLINK_SUPPORT = YES
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should set this
+# tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string);
+# versus func(std::string) {}). This also make the inheritance and collaboration
# diagrams that involve STL classes more complete and accurate.
+# The default value is: NO.
BUILTIN_STL_SUPPORT = NO
-# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
# enable parsing support.
+# The default value is: NO.
CPP_CLI_SUPPORT = NO
-# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
-# Doxygen will parse them like normal C++ but will assume all classes use public
-# instead of private inheritance when no explicit protection keyword is present.
+# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
+# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen
+# will parse them like normal C++ but will assume all classes use public instead
+# of private inheritance when no explicit protection keyword is present.
+# The default value is: NO.
SIP_SUPPORT = NO
-# For Microsoft's IDL there are propget and propput attributes to indicate getter
-# and setter methods for a property. Setting this option to YES (the default)
-# will make doxygen replace the get and set methods by a property in the
-# documentation. This will only work if the methods are indeed getting or
-# setting a simple type. If this is not the case, or you want to show the
-# methods anyway, you should set this option to NO.
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES will make
+# doxygen to replace the get and set methods by a property in the documentation.
+# This will only work if the methods are indeed getting or setting a simple
+# type. If this is not the case, or you want to show the methods anyway, you
+# should set this option to NO.
+# The default value is: YES.
IDL_PROPERTY_SUPPORT = YES
-# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
-# tag is set to YES, then doxygen will reuse the documentation of the first
-# member in the group (if any) for the other members of the group. By default
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
# all members of a group must be documented explicitly.
+# The default value is: NO.
DISTRIBUTE_GROUP_DOC = NO
-# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
-# the same type (for instance a group of public functions) to be put as a
-# subgroup of that type (e.g. under the Public Functions section). Set it to
-# NO to prevent subgrouping. Alternatively, this can be done per class using
-# the \nosubgrouping command.
+# Set the SUBGROUPING tag to YES to allow class member groups of the same type
+# (for instance a group of public functions) to be put as a subgroup of that
+# type (e.g. under the Public Functions section). Set it to NO to prevent
+# subgrouping. Alternatively, this can be done per class using the
+# \nosubgrouping command.
+# The default value is: YES.
SUBGROUPING = YES
-# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
-# is documented as struct, union, or enum with the name of the typedef. So
-# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
-# with name TypeT. When disabled the typedef will appear as a member of a file,
-# namespace, or class. And the struct will be named TypeS. This can typically
-# be useful for C code in case the coding convention dictates that all compound
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
+# are shown inside the group in which they are included (e.g. using \ingroup)
+# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
+# and RTF).
+#
+# Note that this feature does not work in combination with
+# SEPARATE_MEMBER_PAGES.
+# The default value is: NO.
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
+# with only public data fields or simple typedef fields will be shown inline in
+# the documentation of the scope in which they are defined (i.e. file,
+# namespace, or group documentation), provided this scope is documented. If set
+# to NO, structs, classes, and unions are shown on a separate page (for HTML and
+# Man pages) or section (for LaTeX and RTF).
+# The default value is: NO.
+
+INLINE_SIMPLE_STRUCTS = NO
+
+# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
+# enum is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically be
+# useful for C code in case the coding convention dictates that all compound
# types are typedef'ed and only the typedef is referenced, never the tag name.
+# The default value is: NO.
TYPEDEF_HIDES_STRUCT = NO
-# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
-# determine which symbols to keep in memory and which to flush to disk.
-# When the cache is full, less often used symbols will be written to disk.
-# For small to medium size projects (<1000 input files) the default value is
-# probably good enough. For larger projects a too small cache size can cause
-# doxygen to be busy swapping symbols to and from disk most of the time
-# causing a significant performance penalty.
-# If the system has enough physical memory increasing the cache will improve the
-# performance by keeping more symbols in memory. Note that the value works on
-# a logarithmic scale so increasing the size by one will roughly double the
-# memory usage. The cache size is given by this formula:
-# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
-# corresponding to a cache size of 2^16 = 65536 symbols
+# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
+# cache is used to resolve symbols given their name and scope. Since this can be
+# an expensive process and often the same symbol appears multiple times in the
+# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
+# doxygen will become slower. If the cache is too large, memory is wasted. The
+# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
+# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
+# symbols. At the end of a run doxygen will report the cache usage and suggest
+# the optimal cache size from a speed point of view.
+# Minimum value: 0, maximum value: 9, default value: 0.
-SYMBOL_CACHE_SIZE = 0
+LOOKUP_CACHE_SIZE = 0
#---------------------------------------------------------------------------
# Build related configuration options
#---------------------------------------------------------------------------
-# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
-# documentation are documented, even if no documentation was available.
-# Private class members and static file members will be hidden unless
-# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available. Private
+# class members and static file members will be hidden unless the
+# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
+# Note: This will also disable the warnings about undocumented members that are
+# normally produced when WARNINGS is set to YES.
+# The default value is: NO.
EXTRACT_ALL = YES
-# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
-# will be included in the documentation.
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will
+# be included in the documentation.
+# The default value is: NO.
EXTRACT_PRIVATE = NO
-# If the EXTRACT_STATIC tag is set to YES all static members of a file
-# will be included in the documentation.
+# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal
+# scope will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PACKAGE = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file will be
+# included in the documentation.
+# The default value is: NO.
EXTRACT_STATIC = YES
-# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
-# defined locally in source files will be included in the documentation.
-# If set to NO only classes defined in header files are included.
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined
+# locally in source files will be included in the documentation. If set to NO
+# only classes defined in header files are included. Does not have any effect
+# for Java sources.
+# The default value is: YES.
EXTRACT_LOCAL_CLASSES = YES
-# This flag is only useful for Objective-C code. When set to YES local
-# methods, which are defined in the implementation section but not in
-# the interface are included in the documentation.
-# If set to NO (the default) only methods in the interface are included.
+# This flag is only useful for Objective-C code. When set to YES local methods,
+# which are defined in the implementation section but not in the interface are
+# included in the documentation. If set to NO only methods in the interface are
+# included.
+# The default value is: NO.
EXTRACT_LOCAL_METHODS = NO
-# If this flag is set to YES, the members of anonymous namespaces will be
-# extracted and appear in the documentation as a namespace called
-# 'anonymous_namespace{file}', where file will be replaced with the base
-# name of the file that contains the anonymous namespace. By default
-# anonymous namespaces are hidden.
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base name of
+# the file that contains the anonymous namespace. By default anonymous namespace
+# are hidden.
+# The default value is: NO.
EXTRACT_ANON_NSPACES = NO
-# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
-# undocumented members of documented classes, files or namespaces.
-# If set to NO (the default) these members will be included in the
-# various overviews, but no documentation section is generated.
-# This option has no effect if EXTRACT_ALL is enabled.
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
+# undocumented members inside documented classes or files. If set to NO these
+# members will be included in the various overviews, but no documentation
+# section is generated. This option has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
HIDE_UNDOC_MEMBERS = NO
-# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
-# undocumented classes that are normally visible in the class hierarchy.
-# If set to NO (the default) these classes will be included in the various
-# overviews. This option has no effect if EXTRACT_ALL is enabled.
+# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy. If set
+# to NO these classes will be included in the various overviews. This option has
+# no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
HIDE_UNDOC_CLASSES = NO
-# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
-# friend (class|struct|union) declarations.
-# If set to NO (the default) these declarations will be included in the
-# documentation.
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
+# (class|struct|union) declarations. If set to NO these declarations will be
+# included in the documentation.
+# The default value is: NO.
HIDE_FRIEND_COMPOUNDS = NO
-# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
-# documentation blocks found inside the body of a function.
-# If set to NO (the default) these blocks will be appended to the
-# function's detailed documentation block.
+# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
+# documentation blocks found inside the body of a function. If set to NO these
+# blocks will be appended to the function's detailed documentation block.
+# The default value is: NO.
HIDE_IN_BODY_DOCS = NO
-# The INTERNAL_DOCS tag determines if documentation
-# that is typed after a \internal command is included. If the tag is set
-# to NO (the default) then the documentation will be excluded.
-# Set it to YES to include the internal documentation.
+# The INTERNAL_DOCS tag determines if documentation that is typed after a
+# \internal command is included. If the tag is set to NO then the documentation
+# will be excluded. Set it to YES to include the internal documentation.
+# The default value is: NO.
INTERNAL_DOCS = NO
-# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
-# file names in lower-case letters. If set to YES upper-case letters are also
-# allowed. This is useful if you have classes or files whose names only differ
-# in case and if your file system supports case sensitive file names. Windows
+# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
+# names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
# and Mac users are advised to set this option to NO.
+# The default value is: system dependent.
CASE_SENSE_NAMES = YES
-# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
-# will show members with their full class and namespace scopes in the
-# documentation. If set to YES the scope will be hidden.
+# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
+# their full class and namespace scopes in the documentation. If set to YES the
+# scope will be hidden.
+# The default value is: NO.
HIDE_SCOPE_NAMES = NO
-# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
-# will put a list of the files that are included by a file in the documentation
-# of that file.
+# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
+# the files that are included by a file in the documentation of that file.
+# The default value is: YES.
SHOW_INCLUDE_FILES = YES
-# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
-# will list include files with double quotes in the documentation
-# rather than with sharp brackets.
+
+SHOW_GROUPED_MEMB_INC = NO
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
+# files with double quotes in the documentation rather than with sharp brackets.
+# The default value is: NO.
FORCE_LOCAL_INCLUDES = NO
-# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
-# is inserted in the documentation for inline members.
+# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
+# documentation for inline members.
+# The default value is: YES.
INLINE_INFO = YES
-# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
-# will sort the (detailed) documentation of file and class members
-# alphabetically by member name. If set to NO the members will appear in
-# declaration order.
+# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
+# (detailed) documentation of file and class members alphabetically by member
+# name. If set to NO the members will appear in declaration order.
+# The default value is: YES.
SORT_MEMBER_DOCS = YES
-# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
-# brief documentation of file, namespace and class members alphabetically
-# by member name. If set to NO (the default) the members will appear in
-# declaration order.
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
+# descriptions of file, namespace and class members alphabetically by member
+# name. If set to NO the members will appear in declaration order. Note that
+# this will also influence the order of the classes in the class list.
+# The default value is: NO.
SORT_BRIEF_DOCS = NO
-# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
-# will sort the (brief and detailed) documentation of class members so that
-# constructors and destructors are listed first. If set to NO (the default)
-# the constructors will appear in the respective orders defined by
-# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS.
-# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO
-# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
+# (brief and detailed) documentation of class members so that constructors and
+# destructors are listed first. If set to NO the constructors will appear in the
+# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
+# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
+# member documentation.
+# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
+# detailed member documentation.
+# The default value is: NO.
SORT_MEMBERS_CTORS_1ST = NO
-# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
-# hierarchy of group names into alphabetical order. If set to NO (the default)
-# the group names will appear in their defined order.
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
+# of group names into alphabetical order. If set to NO the group names will
+# appear in their defined order.
+# The default value is: NO.
SORT_GROUP_NAMES = NO
-# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
-# sorted by fully-qualified names, including namespaces. If set to
-# NO (the default), the class list will be sorted only by class name,
-# not including the namespace part.
-# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
-# Note: This option applies only to the class list, not to the
-# alphabetical list.
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
+# fully-qualified names, including namespaces. If set to NO, the class list will
+# be sorted only by class name, not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the alphabetical
+# list.
+# The default value is: NO.
SORT_BY_SCOPE_NAME = NO
-# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to
-# do proper type resolution of all parameters of a function it will reject a
-# match between the prototype and the implementation of a member function even
-# if there is only one candidate or it is obvious which candidate to choose
-# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen
-# will still accept a match between prototype and implementation in such cases.
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
+# type resolution of all parameters of a function it will reject a match between
+# the prototype and the implementation of a member function even if there is
+# only one candidate or it is obvious which candidate to choose by doing a
+# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
+# accept a match between prototype and implementation in such cases.
+# The default value is: NO.
STRICT_PROTO_MATCHING = NO
-# The GENERATE_TODOLIST tag can be used to enable (YES) or
-# disable (NO) the todo list. This list is created by putting \todo
-# commands in the documentation.
+# The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the
+# todo list. This list is created by putting \todo commands in the
+# documentation.
+# The default value is: YES.
GENERATE_TODOLIST = YES
-# The GENERATE_TESTLIST tag can be used to enable (YES) or
-# disable (NO) the test list. This list is created by putting \test
-# commands in the documentation.
+# The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the
+# test list. This list is created by putting \test commands in the
+# documentation.
+# The default value is: YES.
GENERATE_TESTLIST = YES
-# The GENERATE_BUGLIST tag can be used to enable (YES) or
-# disable (NO) the bug list. This list is created by putting \bug
-# commands in the documentation.
+# The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug
+# list. This list is created by putting \bug commands in the documentation.
+# The default value is: YES.
GENERATE_BUGLIST = YES
-# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
-# disable (NO) the deprecated list. This list is created by putting
-# \deprecated commands in the documentation.
+# The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO)
+# the deprecated list. This list is created by putting \deprecated commands in
+# the documentation.
+# The default value is: YES.
GENERATE_DEPRECATEDLIST= YES
-# The ENABLED_SECTIONS tag can be used to enable conditional
-# documentation sections, marked by \if sectionname ... \endif.
+# The ENABLED_SECTIONS tag can be used to enable conditional documentation
+# sections, marked by \if
... \endif and \cond
+# ... \endcond blocks.
ENABLED_SECTIONS =
-# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
-# the initial value of a variable or macro consists of for it to appear in
-# the documentation. If the initializer consists of more lines than specified
-# here it will be hidden. Use a value of 0 to hide initializers completely.
-# The appearance of the initializer of individual variables and macros in the
-# documentation can be controlled using \showinitializer or \hideinitializer
-# command in the documentation regardless of this setting.
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
+# initial value of a variable or macro / define can have for it to appear in the
+# documentation. If the initializer consists of more lines than specified here
+# it will be hidden. Use a value of 0 to hide initializers completely. The
+# appearance of the value of individual variables and macros / defines can be
+# controlled using \showinitializer or \hideinitializer command in the
+# documentation regardless of this setting.
+# Minimum value: 0, maximum value: 10000, default value: 30.
MAX_INITIALIZER_LINES = 30
-# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
-# at the bottom of the documentation of classes and structs. If set to YES the
-# list will mention the files that were used to generate the documentation.
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
+# the bottom of the documentation of classes and structs. If set to YES the list
+# will mention the files that were used to generate the documentation.
+# The default value is: YES.
SHOW_USED_FILES = YES
-# If the sources in your project are distributed over multiple directories
-# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
-# in the documentation. The default is NO.
-
-SHOW_DIRECTORIES = NO
-
-# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
-# This will remove the Files entry from the Quick Index and from the
-# Folder Tree View (if specified). The default is YES.
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
+# will remove the Files entry from the Quick Index and from the Folder Tree View
+# (if specified).
+# The default value is: YES.
SHOW_FILES = YES
-# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
-# Namespaces page. This will remove the Namespaces entry from the Quick Index
-# and from the Folder Tree View (if specified). The default is YES.
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
+# page. This will remove the Namespaces entry from the Quick Index and from the
+# Folder Tree View (if specified).
+# The default value is: YES.
SHOW_NAMESPACES = YES
-# The FILE_VERSION_FILTER tag can be used to specify a program or script that
-# doxygen should invoke to get the current version for each file (typically from
-# the version control system). Doxygen will invoke the program by executing (via
-# popen()) the command , where is the value of
-# the FILE_VERSION_FILTER tag, and is the name of an input file
-# provided by doxygen. Whatever the program writes to standard output
-# is used as the file version. See the manual for examples.
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command command input-file, where command is the value of the
+# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
+# by doxygen. Whatever the program writes to standard output is used as the file
+# version. For an example see the documentation.
FILE_VERSION_FILTER =
-# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
-# by doxygen. The layout file controls the global structure of the generated
-# output files in an output format independent way. The create the layout file
-# that represents doxygen's defaults, run doxygen with the -l option.
-# You can optionally specify a file name after the option, if omitted
-# DoxygenLayout.xml will be used as the name of the layout file.
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option. You can
+# optionally specify a file name after the option, if omitted DoxygenLayout.xml
+# will be used as the name of the layout file.
+#
+# Note that if you run doxygen from a directory containing a file called
+# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
+# tag is left empty.
LAYOUT_FILE = Docs/DoxygenLayout.xml
+# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
+# the reference definitions. This must be a list of .bib files. The .bib
+# extension is automatically appended if omitted. This requires the bibtex tool
+# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.
+# For LaTeX the style of the bibliography can be controlled using
+# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
+# search path. Do not use file names with spaces, bibtex cannot handle them. See
+# also \cite for info how to create references.
+
+CITE_BIB_FILES =
+
#---------------------------------------------------------------------------
-# configuration options related to warning and progress messages
+# Configuration options related to warning and progress messages
#---------------------------------------------------------------------------
-# The QUIET tag can be used to turn on/off the messages that are generated
-# by doxygen. Possible values are YES and NO. If left blank NO is used.
+# The QUIET tag can be used to turn on/off the messages that are generated to
+# standard output by doxygen. If QUIET is set to YES this implies that the
+# messages are off.
+# The default value is: NO.
QUIET = NO
-# The WARNINGS tag can be used to turn on/off the warning messages that are
-# generated by doxygen. Possible values are YES and NO. If left blank
-# NO is used.
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES
+# this implies that the warnings are on.
+#
+# Tip: Turn warnings on while writing the documentation.
+# The default value is: YES.
WARNINGS = YES
-# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
-# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
-# automatically be disabled.
+# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate
+# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
+# will automatically be disabled.
+# The default value is: YES.
WARN_IF_UNDOCUMENTED = YES
-# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
-# potential errors in the documentation, such as not documenting some
-# parameters in a documented function, or documenting parameters that
-# don't exist or using markup commands wrongly.
+# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some parameters
+# in a documented function, or documenting parameters that don't exist or using
+# markup commands wrongly.
+# The default value is: YES.
WARN_IF_DOC_ERROR = YES
-# The WARN_NO_PARAMDOC option can be enabled to get warnings for
-# functions that are documented, but have no documentation for their parameters
-# or return value. If set to NO (the default) doxygen will only warn about
-# wrong or incomplete parameter documentation, but not about the absence of
-# documentation.
+# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
+# are documented, but have no documentation for their parameters or return
+# value. If set to NO doxygen will only warn about wrong or incomplete parameter
+# documentation, but not about the absence of documentation.
+# The default value is: NO.
WARN_NO_PARAMDOC = NO
-# The WARN_FORMAT tag determines the format of the warning messages that
-# doxygen can produce. The string should contain the $file, $line, and $text
-# tags, which will be replaced by the file and line number from which the
-# warning originated and the warning text. Optionally the format may contain
-# $version, which will be replaced by the version of the file (if it could
-# be obtained via FILE_VERSION_FILTER)
+# The WARN_FORMAT tag determines the format of the warning messages that doxygen
+# can produce. The string should contain the $file, $line, and $text tags, which
+# will be replaced by the file and line number from which the warning originated
+# and the warning text. Optionally the format may contain $version, which will
+# be replaced by the version of the file (if it could be obtained via
+# FILE_VERSION_FILTER)
+# The default value is: $file:$line: $text.
WARN_FORMAT = "$file:$line: $text"
-# The WARN_LOGFILE tag can be used to specify a file to which warning
-# and error messages should be written. If left blank the output is written
-# to stderr.
+# The WARN_LOGFILE tag can be used to specify a file to which warning and error
+# messages should be written. If left blank the output is written to standard
+# error (stderr).
WARN_LOGFILE =
#---------------------------------------------------------------------------
-# configuration options related to the input files
+# Configuration options related to the input files
#---------------------------------------------------------------------------
-# The INPUT tag can be used to specify the files and/or directories that contain
-# documented source files. You may enter file names like "myfile.cpp" or
-# directories like "/usr/src/myproject". Separate the files or directories
-# with spaces.
+# The INPUT tag is used to specify the files and/or directories that contain
+# documented source files. You may enter file names like myfile.cpp or
+# directories like /usr/src/myproject. Separate the files or directories with
+# spaces.
+# Note: If this tag is empty the current directory is searched.
INPUT = Detour \
DetourCrowd \
@@ -619,36 +756,41 @@ INPUT = Detour \
Docs/Conceptual \
Docs/Extern
-# This tag can be used to specify the character encoding of the source files
-# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
-# also the default input encoding. Doxygen uses libiconv (or the iconv built
-# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
-# the list of possible encodings.
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
+# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
+# documentation (see: http://www.gnu.org/software/libiconv) for the list of
+# possible encodings.
+# The default value is: UTF-8.
INPUT_ENCODING = UTF-8
-# If the value of the INPUT tag contains directories, you can use the
-# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
-# and *.h) to filter out the source-files in the directories. If left
-# blank the following patterns are tested:
-# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh
-# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py
-# *.f90 *.f *.for *.vhd *.vhdl
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank the
+# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii,
+# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp,
+# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown,
+# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf,
+# *.qsf, *.as and *.js.
FILE_PATTERNS = *.cpp \
*.h \
*_c.txt \
*_api.txt
-# The RECURSIVE tag can be used to turn specify whether or not subdirectories
-# should be searched for input files as well. Possible values are YES and NO.
-# If left blank NO is used.
+# The RECURSIVE tag can be used to specify whether or not subdirectories should
+# be searched for input files as well.
+# The default value is: NO.
RECURSIVE = YES
-# The EXCLUDE tag can be used to specify files and/or directories that should
-# excluded from the INPUT source files. This way you can easily exclude a
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
# subdirectory from a directory tree whose root is specified with the INPUT tag.
+#
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
EXCLUDE = Doxyfile \
License.txt \
@@ -658,731 +800,1120 @@ EXCLUDE = Doxyfile \
RecastDemo/Build \
RecastDemo/Bin
-# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
-# directories that are symbolic links (a Unix file system feature) are excluded
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
# from the input.
+# The default value is: NO.
EXCLUDE_SYMLINKS = NO
-# If the value of the INPUT tag contains directories, you can use the
-# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
-# certain files from those directories. Note that the wildcards are matched
-# against the file with absolute path, so to exclude all test directories
-# for example use the pattern */test/*
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories for example use the pattern */test/*
EXCLUDE_PATTERNS = CMakeLists.txt
-# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
-# (namespaces, classes, functions, etc.) that should be excluded from the
-# output. The symbol name can be a fully qualified name, a word, or if the
-# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
# AClass::ANamespace, ANamespace::*Test
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories use the pattern */test/*
EXCLUDE_SYMBOLS =
-# The EXAMPLE_PATH tag can be used to specify one or more files or
-# directories that contain example code fragments that are included (see
-# the \include command).
+# The EXAMPLE_PATH tag can be used to specify one or more files or directories
+# that contain example code fragments that are included (see the \include
+# command).
EXAMPLE_PATH =
-# If the value of the EXAMPLE_PATH tag contains directories, you can use the
-# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
-# and *.h) to filter out the source-files in the directories. If left
-# blank all files are included.
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank all
+# files are included.
EXAMPLE_PATTERNS =
-# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
-# searched for input files to be used with the \include or \dontinclude
-# commands irrespective of the value of the RECURSIVE tag.
-# Possible values are YES and NO. If left blank NO is used.
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude commands
+# irrespective of the value of the RECURSIVE tag.
+# The default value is: NO.
EXAMPLE_RECURSIVE = NO
-# The IMAGE_PATH tag can be used to specify one or more files or
-# directories that contain image that are included in the documentation (see
-# the \image command).
+# The IMAGE_PATH tag can be used to specify one or more files or directories
+# that contain images that are to be included in the documentation (see the
+# \image command).
IMAGE_PATH = Docs/Images
-# The INPUT_FILTER tag can be used to specify a program that doxygen should
-# invoke to filter for each input file. Doxygen will invoke the filter program
-# by executing (via popen()) the command , where
-# is the value of the INPUT_FILTER tag, and is the name of an
-# input file. Doxygen will then use the output that the filter program writes
-# to standard output. If FILTER_PATTERNS is specified, this tag will be
-# ignored.
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command:
+#
+#
+#
+# where is the value of the INPUT_FILTER tag, and is the
+# name of an input file. Doxygen will then use the output that the filter
+# program writes to standard output. If FILTER_PATTERNS is specified, this tag
+# will be ignored.
+#
+# Note that the filter must not add or remove lines; it is applied before the
+# code is scanned, but not when the output code is generated. If lines are added
+# or removed, the anchors will not be placed correctly.
INPUT_FILTER =
-# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
-# basis. Doxygen will compare the file name with each pattern and apply the
-# filter if there is a match. The filters are a list of the form:
-# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
-# info on how filters are used. If FILTER_PATTERNS is empty or if
-# non of the patterns match the file name, INPUT_FILTER is applied.
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form: pattern=filter
+# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
+# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
+# patterns match the file name, INPUT_FILTER is applied.
FILTER_PATTERNS =
-# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
-# INPUT_FILTER) will be used to filter the input files when producing source
-# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER ) will also be used to filter the input files that are used for
+# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# The default value is: NO.
FILTER_SOURCE_FILES = NO
-# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
-# pattern. A pattern will override the setting for FILTER_PATTERN (if any)
-# and it is also possible to disable source filtering for a specific pattern
-# using *.ext= (so without naming a filter). This option only has effect when
-# FILTER_SOURCE_FILES is enabled.
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
+# it is also possible to disable source filtering for a specific pattern using
+# *.ext= (so without naming a filter).
+# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
FILTER_SOURCE_PATTERNS =
+# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page
+# (index.html). This can be useful if you have a project on for instance GitHub
+# and want to reuse the introduction page also for the doxygen output.
+
+USE_MDFILE_AS_MAINPAGE =
+
#---------------------------------------------------------------------------
-# configuration options related to source browsing
+# Configuration options related to source browsing
#---------------------------------------------------------------------------
-# If the SOURCE_BROWSER tag is set to YES then a list of source files will
-# be generated. Documented entities will be cross-referenced with these sources.
-# Note: To get rid of all source code in the generated output, make sure also
-# VERBATIM_HEADERS is set to NO.
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
+# generated. Documented entities will be cross-referenced with these sources.
+#
+# Note: To get rid of all source code in the generated output, make sure that
+# also VERBATIM_HEADERS is set to NO.
+# The default value is: NO.
SOURCE_BROWSER = NO
-# Setting the INLINE_SOURCES tag to YES will include the body
-# of functions and classes directly in the documentation.
+# Setting the INLINE_SOURCES tag to YES will include the body of functions,
+# classes and enums directly into the documentation.
+# The default value is: NO.
INLINE_SOURCES = NO
-# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
-# doxygen to hide any special comment blocks from generated source code
-# fragments. Normal C and C++ comments will always remain visible.
+# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
+# special comment blocks from generated source code fragments. Normal C, C++ and
+# Fortran comments will always remain visible.
+# The default value is: YES.
STRIP_CODE_COMMENTS = YES
-# If the REFERENCED_BY_RELATION tag is set to YES
-# then for each documented function all documented
-# functions referencing it will be listed.
+# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
+# function all documented functions referencing it will be listed.
+# The default value is: NO.
REFERENCED_BY_RELATION = NO
-# If the REFERENCES_RELATION tag is set to YES
-# then for each documented function all documented entities
-# called/used by that function will be listed.
+# If the REFERENCES_RELATION tag is set to YES then for each documented function
+# all documented entities called/used by that function will be listed.
+# The default value is: NO.
REFERENCES_RELATION = NO
-# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
-# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
-# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
-# link to the source code. Otherwise they will link to the documentation.
+# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
+# to YES, then the hyperlinks from functions in REFERENCES_RELATION and
+# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
+# link to the documentation.
+# The default value is: YES.
REFERENCES_LINK_SOURCE = YES
-# If the USE_HTAGS tag is set to YES then the references to source code
-# will point to the HTML generated by the htags(1) tool instead of doxygen
-# built-in source browser. The htags tool is part of GNU's global source
-# tagging system (see http://www.gnu.org/software/global/global.html). You
-# will need version 4.8.6 or higher.
+# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
+# source code will show a tooltip with additional information such as prototype,
+# brief description and links to the definition and documentation. Since this
+# will make the HTML file larger and loading of large files a bit slower, you
+# can opt to disable this feature.
+# The default value is: YES.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+SOURCE_TOOLTIPS = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code will
+# point to the HTML generated by the htags(1) tool instead of doxygen built-in
+# source browser. The htags tool is part of GNU's global source tagging system
+# (see http://www.gnu.org/software/global/global.html). You will need version
+# 4.8.6 or higher.
+#
+# To use it do the following:
+# - Install the latest version of global
+# - Enable SOURCE_BROWSER and USE_HTAGS in the config file
+# - Make sure the INPUT points to the root of the source tree
+# - Run doxygen as normal
+#
+# Doxygen will invoke htags (and that will in turn invoke gtags), so these
+# tools must be available from the command line (i.e. in the search path).
+#
+# The result: instead of the source browser generated by doxygen, the links to
+# source code will now point to the output of htags.
+# The default value is: NO.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
USE_HTAGS = NO
-# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
-# will generate a verbatim copy of the header file for each class for
-# which an include is specified. Set to NO to disable this.
+# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
+# verbatim copy of the header file for each class for which an include is
+# specified. Set to NO to disable this.
+# See also: Section \class.
+# The default value is: YES.
VERBATIM_HEADERS = YES
+# If the CLANG_ASSISTED_PARSING tag is set to YES, then doxygen will use the
+# clang parser (see: http://clang.llvm.org/) for more acurate parsing at the
+# cost of reduced performance. This can be particularly helpful with template
+# rich C++ code for which doxygen's built-in parser lacks the necessary type
+# information.
+# Note: The availability of this option depends on whether or not doxygen was
+# compiled with the --with-libclang option.
+# The default value is: NO.
+
+CLANG_ASSISTED_PARSING = NO
+
+# If clang assisted parsing is enabled you can provide the compiler with command
+# line options that you would normally use when invoking the compiler. Note that
+# the include paths will already be set by doxygen for the files and directories
+# specified with INPUT and INCLUDE_PATH.
+# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
+
+CLANG_OPTIONS =
+
#---------------------------------------------------------------------------
-# configuration options related to the alphabetical class index
+# Configuration options related to the alphabetical class index
#---------------------------------------------------------------------------
-# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
-# of all compounds will be generated. Enable this if the project
-# contains a lot of classes, structs, unions or interfaces.
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
+# compounds will be generated. Enable this if the project contains a lot of
+# classes, structs, unions or interfaces.
+# The default value is: YES.
ALPHABETICAL_INDEX = YES
-# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
-# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
-# in which this list will be split (can be a number in the range [1..20])
+# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
+# which the alphabetical index list will be split.
+# Minimum value: 1, maximum value: 20, default value: 5.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
COLS_IN_ALPHA_INDEX = 5
-# In case all classes in a project start with a common prefix, all
-# classes will be put under the same header in the alphabetical index.
-# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
-# should be ignored while generating the index headers.
+# In case all classes in a project start with a common prefix, all classes will
+# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
+# can be used to specify a prefix (or a list of prefixes) that should be ignored
+# while generating the index headers.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
IGNORE_PREFIX =
#---------------------------------------------------------------------------
-# configuration options related to the HTML output
+# Configuration options related to the HTML output
#---------------------------------------------------------------------------
-# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
-# generate HTML output.
+# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output
+# The default value is: YES.
GENERATE_HTML = YES
-# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `html' will be used as the default path.
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_OUTPUT = html
-# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
-# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
-# doxygen will generate files with .html extension.
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
+# generated HTML page (for example: .htm, .php, .asp).
+# The default value is: .html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_FILE_EXTENSION = .html
-# The HTML_HEADER tag can be used to specify a personal HTML header for
-# each generated HTML page. If it is left blank doxygen will generate a
+# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
+# each generated HTML page. If the tag is left blank doxygen will generate a
# standard header.
+#
+# To get valid HTML the header file that includes any scripts and style sheets
+# that doxygen needs, which is dependent on the configuration options used (e.g.
+# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
+# default header using
+# doxygen -w html new_header.html new_footer.html new_stylesheet.css
+# YourConfigFile
+# and then modify the file new_header.html. See also section "Doxygen usage"
+# for information on how to generate the default header that doxygen normally
+# uses.
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. For a description
+# of the possible markers and block names see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_HEADER =
-# The HTML_FOOTER tag can be used to specify a personal HTML footer for
-# each generated HTML page. If it is left blank doxygen will generate a
-# standard footer.
+# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
+# generated HTML page. If the tag is left blank doxygen will generate a standard
+# footer. See HTML_HEADER for more information on how to generate a default
+# footer and what special commands can be used inside the footer. See also
+# section "Doxygen usage" for information on how to generate the default footer
+# that doxygen normally uses.
+# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_FOOTER = Docs/footer.html
-# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
-# style sheet that is used by each HTML page. It can be used to
-# fine-tune the look of the HTML output. If the tag is left blank doxygen
-# will generate a default style sheet. Note that doxygen will try to copy
-# the style sheet file to the HTML output directory, so don't put your own
-# stylesheet in the HTML output directory as well, or it will be erased!
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
+# sheet that is used by each HTML page. It can be used to fine-tune the look of
+# the HTML output. If left blank doxygen will generate a default style sheet.
+# See also section "Doxygen usage" for information on how to generate the style
+# sheet that doxygen normally uses.
+# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
+# it is more robust and this tag (HTML_STYLESHEET) will in the future become
+# obsolete.
+# This tag requires that the tag GENERATE_HTML is set to YES.
-HTML_STYLESHEET = Docs/customdoxygen.css
+HTML_STYLESHEET =
-# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.
-# Doxygen will adjust the colors in the stylesheet and background images
-# according to this color. Hue is specified as an angle on a colorwheel,
-# see http://en.wikipedia.org/wiki/Hue for more information.
-# For instance the value 0 represents red, 60 is yellow, 120 is green,
-# 180 is cyan, 240 is blue, 300 purple, and 360 is red again.
-# The allowed range is 0 to 359.
+# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user-
+# defined cascading style sheet that is included after the standard style sheets
+# created by doxygen. Using this option one can overrule certain style aspects.
+# This is preferred over using HTML_STYLESHEET since it does not replace the
+# standard style sheet and is therefor more robust against future updates.
+# Doxygen will copy the style sheet file to the output directory. For an example
+# see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_STYLESHEET =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
+# files will be copied as-is; there are no commands or markers available.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_FILES =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
+# will adjust the colors in the stylesheet and background images according to
+# this color. Hue is specified as an angle on a colorwheel, see
+# http://en.wikipedia.org/wiki/Hue for more information. For instance the value
+# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
+# purple, and 360 is red again.
+# Minimum value: 0, maximum value: 359, default value: 220.
+# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_COLORSTYLE_HUE = 220
-# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of
-# the colors in the HTML output. For a value of 0 the output will use
-# grayscales only. A value of 255 will produce the most vivid colors.
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
+# in the HTML output. For a value of 0 the output will use grayscales only. A
+# value of 255 will produce the most vivid colors.
+# Minimum value: 0, maximum value: 255, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_COLORSTYLE_SAT = 100
-# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to
-# the luminance component of the colors in the HTML output. Values below
-# 100 gradually make the output lighter, whereas values above 100 make
-# the output darker. The value divided by 100 is the actual gamma applied,
-# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2,
-# and 100 does not change the gamma.
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
+# luminance component of the colors in the HTML output. Values below 100
+# gradually make the output lighter, whereas values above 100 make the output
+# darker. The value divided by 100 is the actual gamma applied, so 80 represents
+# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
+# change the gamma.
+# Minimum value: 40, maximum value: 240, default value: 80.
+# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_COLORSTYLE_GAMMA = 80
-# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
-# page will contain the date and time when the page was generated. Setting
-# this to NO can help when comparing the output of multiple runs.
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting this
+# to NO can help when comparing the output of multiple runs.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_TIMESTAMP = YES
-# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
-# files or namespaces will be aligned in HTML using tables. If set to
-# NO a bullet list will be used.
-
-HTML_ALIGN_MEMBERS = YES
-
-# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
-# documentation will contain sections that can be hidden and shown after the
-# page has loaded. For this to work a browser that supports
-# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
-# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_DYNAMIC_SECTIONS = NO
-# If the GENERATE_DOCSET tag is set to YES, additional index files
-# will be generated that can be used as input for Apple's Xcode 3
-# integrated development environment, introduced with OSX 10.5 (Leopard).
-# To create a documentation set, doxygen will generate a Makefile in the
-# HTML output directory. Running make will produce the docset in that
-# directory and running "make install" will install the docset in
-# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
-# it at startup.
-# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
+# shown in the various tree structured indices initially; the user can expand
+# and collapse entries dynamically later on. Doxygen will expand the tree to
+# such a level that at most the specified number of entries are visible (unless
+# a fully collapsed tree already exceeds this amount). So setting the number of
+# entries 1 will produce a full collapsed tree by default. 0 is a special value
+# representing an infinite number of entries and will result in a full expanded
+# tree by default.
+# Minimum value: 0, maximum value: 9999, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files will be
+# generated that can be used as input for Apple's Xcode 3 integrated development
+# environment (see: http://developer.apple.com/tools/xcode/), introduced with
+# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a
+# Makefile in the HTML output directory. Running make will produce the docset in
+# that directory and running make install will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
+# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
# for more information.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
GENERATE_DOCSET = NO
-# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
-# feed. A documentation feed provides an umbrella under which multiple
-# documentation sets from a single provider (such as a company or product suite)
-# can be grouped.
+# This tag determines the name of the docset feed. A documentation feed provides
+# an umbrella under which multiple documentation sets from a single provider
+# (such as a company or product suite) can be grouped.
+# The default value is: Doxygen generated docs.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
DOCSET_FEEDNAME = "Doxygen generated docs"
-# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
-# should uniquely identify the documentation set bundle. This should be a
-# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
-# will append .docset to the name.
+# This tag specifies a string that should uniquely identify the documentation
+# set bundle. This should be a reverse domain-name style string, e.g.
+# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
DOCSET_BUNDLE_ID = org.doxygen.Project
-# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify
-# the documentation publisher. This should be a reverse domain-name style
+# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
# string, e.g. com.mycompany.MyDocSet.documentation.
+# The default value is: org.doxygen.Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
DOCSET_PUBLISHER_ID = org.doxygen.Publisher
-# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher.
+# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
+# The default value is: Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
DOCSET_PUBLISHER_NAME = Publisher
-# If the GENERATE_HTMLHELP tag is set to YES, additional index files
-# will be generated that can be used as input for tools like the
-# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
-# of the generated HTML documentation.
+# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
+# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
+# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
+# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on
+# Windows.
+#
+# The HTML Help Workshop contains a compiler that can convert all HTML output
+# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
+# files are now used as the Windows 98 help format, and will replace the old
+# Windows help format (.hlp) on all Windows platforms in the future. Compressed
+# HTML files also contain an index, a table of contents, and you can search for
+# words in the documentation. The HTML workshop also contains a viewer for
+# compressed HTML files.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
GENERATE_HTMLHELP = NO
-# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
-# be used to specify the file name of the resulting .chm file. You
-# can add a path in front of the file if the result should not be
+# The CHM_FILE tag can be used to specify the file name of the resulting .chm
+# file. You can add a path in front of the file if the result should not be
# written to the html output directory.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
CHM_FILE =
-# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
-# be used to specify the location (absolute path including file name) of
-# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
-# the HTML help compiler on the generated index.hhp.
+# The HHC_LOCATION tag can be used to specify the location (absolute path
+# including file name) of the HTML help compiler ( hhc.exe). If non-empty
+# doxygen will try to run the HTML help compiler on the generated index.hhp.
+# The file has to be specified with full path.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
HHC_LOCATION =
-# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
-# controls if a separate .chi index file is generated (YES) or that
-# it should be included in the master .chm file (NO).
+# The GENERATE_CHI flag controls if a separate .chi index file is generated (
+# YES) or that it should be included in the master .chm file ( NO).
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
GENERATE_CHI = NO
-# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
-# is used to encode HtmlHelp index (hhk), content (hhc) and project file
-# content.
+# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc)
+# and project file content.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
CHM_INDEX_ENCODING =
-# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
-# controls whether a binary table of contents is generated (YES) or a
-# normal table of contents (NO) in the .chm file.
+# The BINARY_TOC flag controls whether a binary table of contents is generated (
+# YES) or a normal table of contents ( NO) in the .chm file.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
BINARY_TOC = NO
-# The TOC_EXPAND flag can be set to YES to add extra items for group members
-# to the contents of the HTML help documentation and to the tree view.
+# The TOC_EXPAND flag can be set to YES to add extra items for group members to
+# the table of contents of the HTML help documentation and to the tree view.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
TOC_EXPAND = NO
-# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
-# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated
-# that can be used as input for Qt's qhelpgenerator to generate a
-# Qt Compressed Help (.qch) of the generated HTML documentation.
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
+# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
+# (.qch) of the generated HTML documentation.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
GENERATE_QHP = NO
-# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
-# be used to specify the file name of the resulting .qch file.
-# The path specified is relative to the HTML output folder.
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
+# the file name of the resulting .qch file. The path specified is relative to
+# the HTML output folder.
+# This tag requires that the tag GENERATE_QHP is set to YES.
QCH_FILE =
-# The QHP_NAMESPACE tag specifies the namespace to use when generating
-# Qt Help Project output. For more information please see
-# http://doc.trolltech.com/qthelpproject.html#namespace
+# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
+# Project output. For more information please see Qt Help Project / Namespace
+# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace).
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_NAMESPACE = org.doxygen.Project
-# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
-# Qt Help Project output. For more information please see
-# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
+# Help Project output. For more information please see Qt Help Project / Virtual
+# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-
+# folders).
+# The default value is: doc.
+# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_VIRTUAL_FOLDER = doc
-# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to
-# add. For more information please see
-# http://doc.trolltech.com/qthelpproject.html#custom-filters
+# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
+# filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_CUST_FILTER_NAME =
-# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
-# custom filter to add. For more information please see
-#
-# Qt Help Project / Custom Filters .
+# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_CUST_FILTER_ATTRS =
-# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
-# project's
-# filter section matches.
-#
-# Qt Help Project / Filter Attributes .
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's filter section matches. Qt Help Project / Filter Attributes (see:
+# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
+# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_SECT_FILTER_ATTRS =
-# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
-# be used to specify the location of Qt's qhelpgenerator.
-# If non-empty doxygen will try to run qhelpgenerator on the generated
-# .qhp file.
+# The QHG_LOCATION tag can be used to specify the location of Qt's
+# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
+# generated .qhp file.
+# This tag requires that the tag GENERATE_QHP is set to YES.
QHG_LOCATION =
-# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
-# will be generated, which together with the HTML files, form an Eclipse help
-# plugin. To install this plugin and make it available under the help contents
-# menu in Eclipse, the contents of the directory containing the HTML and XML
-# files needs to be copied into the plugins directory of eclipse. The name of
-# the directory within the plugins directory should be the same as
-# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before
-# the help appears.
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
+# generated, together with the HTML files, they form an Eclipse help plugin. To
+# install this plugin and make it available under the help contents menu in
+# Eclipse, the contents of the directory containing the HTML and XML files needs
+# to be copied into the plugins directory of eclipse. The name of the directory
+# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
+# After copying Eclipse needs to be restarted before the help appears.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
GENERATE_ECLIPSEHELP = NO
-# A unique identifier for the eclipse help plugin. When installing the plugin
-# the directory name containing the HTML and XML files should also have
-# this name.
+# A unique identifier for the Eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have this
+# name. Each documentation set should have its own identifier.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
ECLIPSE_DOC_ID = org.doxygen.Project
-# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
-# top of each HTML page. The value NO (the default) enables the index and
-# the value YES disables it.
+# If you want full control over the layout of the generated HTML pages it might
+# be necessary to disable the index and replace it with your own. The
+# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
+# of each HTML page. A value of NO enables the index and the value YES disables
+# it. Since the tabs in the index contain the same information as the navigation
+# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
DISABLE_INDEX = NO
-# This tag can be used to set the number of enum values (range [0,1..20])
-# that doxygen will group on one line in the generated HTML documentation.
-# Note that a value of 0 will completely suppress the enum values from
-# appearing in the overview section.
-
-ENUM_VALUES_PER_LINE = 4
-
-# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
-# structure should be generated to display hierarchical information.
-# If the tag value is set to YES, a side panel will be generated
-# containing a tree-like index structure (just like the one that
-# is generated for HTML Help). For this to work a browser that supports
-# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
-# Windows users are probably better off using the HTML help feature.
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information. If the tag
+# value is set to YES, a side panel will be generated containing a tree-like
+# index structure (just like the one that is generated for HTML Help). For this
+# to work a browser that supports JavaScript, DHTML, CSS and frames is required
+# (i.e. any modern browser). Windows users are probably better off using the
+# HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can
+# further fine-tune the look of the index. As an example, the default style
+# sheet generated by doxygen has an example that shows how to put an image at
+# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
+# the same information as the tab index, you could consider setting
+# DISABLE_INDEX to YES when enabling this option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
GENERATE_TREEVIEW = YES
-# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories,
-# and Class Hierarchy pages using a tree view instead of an ordered list.
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
+# doxygen will group on one line in the generated HTML documentation.
+#
+# Note that a value of 0 will completely suppress the enum values from appearing
+# in the overview section.
+# Minimum value: 0, maximum value: 20, default value: 4.
+# This tag requires that the tag GENERATE_HTML is set to YES.
-USE_INLINE_TREES = YES
+ENUM_VALUES_PER_LINE = 4
-# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
-# used to set the initial width (in pixels) of the frame in which the tree
-# is shown.
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
+# to set the initial width (in pixels) of the frame in which the tree is shown.
+# Minimum value: 0, maximum value: 1500, default value: 250.
+# This tag requires that the tag GENERATE_HTML is set to YES.
TREEVIEW_WIDTH = 250
-# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open
-# links to external symbols imported via tag files in a separate window.
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to
+# external symbols imported via tag files in a separate window.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
EXT_LINKS_IN_WINDOW = NO
-# Use this tag to change the font size of Latex formulas included
-# as images in the HTML documentation. The default is 10. Note that
-# when you change the font size after a successful doxygen run you need
-# to manually remove any form_*.png images from the HTML output directory
-# to force them to be regenerated.
+# Use this tag to change the font size of LaTeX formulas included as images in
+# the HTML documentation. When you change the font size after a successful
+# doxygen run you need to manually remove any form_*.png images from the HTML
+# output directory to force them to be regenerated.
+# Minimum value: 8, maximum value: 50, default value: 10.
+# This tag requires that the tag GENERATE_HTML is set to YES.
FORMULA_FONTSIZE = 10
-# Use the FORMULA_TRANPARENT tag to determine whether or not the images
-# generated for formulas are transparent PNGs. Transparent PNGs are
-# not supported properly for IE 6.0, but are supported on all modern browsers.
-# Note that when changing this option you need to delete any form_*.png files
-# in the HTML output before the changes have effect.
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are not
+# supported properly for IE 6.0, but are supported on all modern browsers.
+#
+# Note that when changing this option you need to delete any form_*.png files in
+# the HTML output directory before the changes have effect.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
FORMULA_TRANSPARENT = YES
-# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax
-# (see http://www.mathjax.org) which uses client side Javascript for the
-# rendering instead of using prerendered bitmaps. Use this if you do not
-# have LaTeX installed or if you want to formulas look prettier in the HTML
-# output. When enabled you also need to install MathJax separately and
-# configure the path to it using the MATHJAX_RELPATH option.
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
+# http://www.mathjax.org) which uses client side Javascript for the rendering
+# instead of using prerendered bitmaps. Use this if you do not have LaTeX
+# installed or if you want to formulas look prettier in the HTML output. When
+# enabled you may also need to install MathJax separately and configure the path
+# to it using the MATHJAX_RELPATH option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
USE_MATHJAX = NO
-# When MathJax is enabled you need to specify the location relative to the
-# HTML output directory using the MATHJAX_RELPATH option. The destination
-# directory should contain the MathJax.js script. For instance, if the mathjax
-# directory is located at the same level as the HTML output directory, then
-# MATHJAX_RELPATH should be ../mathjax. The default value points to the
-# mathjax.org site, so you can quickly see the result without installing
-# MathJax, but it is strongly recommended to install a local copy of MathJax
-# before deployment.
+# When MathJax is enabled you can set the default output format to be used for
+# the MathJax output. See the MathJax site (see:
+# http://docs.mathjax.org/en/latest/output.html) for more details.
+# Possible values are: HTML-CSS (which is slower, but has the best
+# compatibility), NativeMML (i.e. MathML) and SVG.
+# The default value is: HTML-CSS.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_FORMAT = HTML-CSS
+
+# When MathJax is enabled you need to specify the location relative to the HTML
+# output directory using the MATHJAX_RELPATH option. The destination directory
+# should contain the MathJax.js script. For instance, if the mathjax directory
+# is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
+# Content Delivery Network so you can quickly see the result without installing
+# MathJax. However, it is strongly recommended to install a local copy of
+# MathJax from http://www.mathjax.org before deployment.
+# The default value is: http://cdn.mathjax.org/mathjax/latest.
+# This tag requires that the tag USE_MATHJAX is set to YES.
MATHJAX_RELPATH = http://www.mathjax.org/mathjax
-# When the SEARCHENGINE tag is enabled doxygen will generate a search box
-# for the HTML output. The underlying search engine uses javascript
-# and DHTML and should work on any modern browser. Note that when using
-# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets
-# (GENERATE_DOCSET) there is already a search function so this one should
-# typically be disabled. For large projects the javascript based search engine
-# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
+# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
+# extension names that should be enabled during MathJax rendering. For example
+# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_EXTENSIONS =
+
+# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
+# of code that will be used on startup of the MathJax code. See the MathJax site
+# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
+# example see the documentation.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_CODEFILE =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
+# the HTML output. The underlying search engine uses javascript and DHTML and
+# should work on any modern browser. Note that when using HTML help
+# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
+# there is already a search function so this one should typically be disabled.
+# For large projects the javascript based search engine can be slow, then
+# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
+# search using the keyboard; to jump to the search box use + S
+# (what the is depends on the OS and browser, but it is typically
+# , /, or both). Inside the search box use the to jump into the search results window, the results can be navigated
+# using the . Press to select an item or to cancel
+# the search. The filter options can be selected when the cursor is inside the
+# search box by pressing +. Also here use the
+# to select a filter and or to activate or cancel the filter
+# option.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
SEARCHENGINE = YES
-# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
-# implemented using a PHP enabled web server instead of at the web client
-# using Javascript. Doxygen will generate the search PHP script and index
-# file to put on the web server. The advantage of the server
-# based approach is that it scales better to large projects and allows
-# full text search. The disadvantages are that it is more difficult to setup
-# and does not have live searching capabilities.
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a web server instead of a web client using Javascript. There
+# are two flavours of web server based searching depending on the
+# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for
+# searching and an index file used by the script. When EXTERNAL_SEARCH is
+# enabled the indexing and searching needs to be provided by external tools. See
+# the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
SERVER_BASED_SEARCH = NO
+# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
+# search results.
+#
+# Doxygen ships with an example indexer ( doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/).
+#
+# See the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH = NO
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will return the search results when EXTERNAL_SEARCH is enabled.
+#
+# Doxygen ships with an example indexer ( doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/). See the section "External Indexing and
+# Searching" for details.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHENGINE_URL =
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
+# SEARCHDATA_FILE tag the name of this file can be specified.
+# The default file is: searchdata.xml.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHDATA_FILE = searchdata.xml
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
+# projects and redirect the results back to the right project.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH_ID =
+
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
+# to a relative location where the documentation can be found. The format is:
+# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTRA_SEARCH_MAPPINGS =
+
#---------------------------------------------------------------------------
-# configuration options related to the LaTeX output
+# Configuration options related to the LaTeX output
#---------------------------------------------------------------------------
-# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
-# generate Latex output.
+# If the GENERATE_LATEX tag is set to YES doxygen will generate LaTeX output.
+# The default value is: YES.
GENERATE_LATEX = NO
-# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `latex' will be used as the default path.
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_OUTPUT = latex
-# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
-# invoked. If left blank `latex' will be used as the default command name.
-# Note that when enabling USE_PDFLATEX this option is only used for
-# generating bitmaps for formulas in the HTML output, but not in the
-# Makefile that is written to the output directory.
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked.
+#
+# Note that when enabling USE_PDFLATEX this option is only used for generating
+# bitmaps for formulas in the HTML output, but not in the Makefile that is
+# written to the output directory.
+# The default file is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_CMD_NAME = latex
-# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
-# generate index for LaTeX. If left blank `makeindex' will be used as the
-# default command name.
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
+# index for LaTeX.
+# The default file is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
MAKEINDEX_CMD_NAME = makeindex
-# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
-# LaTeX documents. This may be useful for small projects and may help to
-# save some trees in general.
+# If the COMPACT_LATEX tag is set to YES doxygen generates more compact LaTeX
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
COMPACT_LATEX = NO
-# The PAPER_TYPE tag can be used to set the paper type that is used
-# by the printer. Possible values are: a4, letter, legal and
-# executive. If left blank a4wide will be used.
+# The PAPER_TYPE tag can be used to set the paper type that is used by the
+# printer.
+# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
+# 14 inches) and executive (7.25 x 10.5 inches).
+# The default value is: a4.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
PAPER_TYPE = a4wide
-# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
-# packages that should be included in the LaTeX output.
+# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
+# that should be included in the LaTeX output. To get the times font for
+# instance you can specify
+# EXTRA_PACKAGES=times
+# If left blank no extra packages will be included.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
EXTRA_PACKAGES =
-# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
-# the generated latex document. The header should contain everything until
-# the first chapter. If it is left blank doxygen will generate a
-# standard header. Notice: only use this tag if you know what you are doing!
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
+# generated LaTeX document. The header should contain everything until the first
+# chapter. If it is left blank doxygen will generate a standard header. See
+# section "Doxygen usage" for information on how to let doxygen write the
+# default header to a separate file.
+#
+# Note: Only use a user-defined header if you know what you are doing! The
+# following commands have a special meaning inside the header: $title,
+# $datetime, $date, $doxygenversion, $projectname, $projectnumber. Doxygen will
+# replace them by respectively the title of the page, the current date and time,
+# only the current date, the version number of doxygen, the project name (see
+# PROJECT_NAME), or the project number (see PROJECT_NUMBER).
+# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_HEADER =
-# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
-# is prepared for conversion to pdf (using ps2pdf). The pdf file will
-# contain links (just like the HTML output) instead of page references
-# This makes the output suitable for online browsing using a pdf viewer.
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
+# generated LaTeX document. The footer should contain everything after the last
+# chapter. If it is left blank doxygen will generate a standard footer.
+#
+# Note: Only use a user-defined footer if you know what you are doing!
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_FOOTER =
+
+# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the LATEX_OUTPUT output
+# directory. Note that the files will be copied as-is; there are no commands or
+# markers available.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_FILES =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
+# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
+# contain links (just like the HTML output) instead of page references. This
+# makes the output suitable for online browsing using a PDF viewer.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
PDF_HYPERLINKS = NO
-# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
-# plain latex in the generated Makefile. Set this option to YES to get a
+# If the LATEX_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
+# the PDF file directly from the LaTeX files. Set this option to YES to get a
# higher quality PDF documentation.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
USE_PDFLATEX = NO
-# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
-# command to the generated LaTeX files. This will instruct LaTeX to keep
-# running if errors occur, instead of asking the user for help.
-# This option is also used when generating formulas in HTML.
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
+# command to the generated LaTeX files. This will instruct LaTeX to keep running
+# if errors occur, instead of asking the user for help. This option is also used
+# when generating formulas in HTML.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_BATCHMODE = NO
-# If LATEX_HIDE_INDICES is set to YES then doxygen will not
-# include the index chapters (such as File Index, Compound Index, etc.)
-# in the output.
+# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
+# index chapters (such as File Index, Compound Index, etc.) in the output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_HIDE_INDICES = NO
-# If LATEX_SOURCE_CODE is set to YES then doxygen will include
-# source code with syntax highlighting in the LaTeX output.
-# Note that which sources are shown also depends on other settings
-# such as SOURCE_BROWSER.
+# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
+# code with syntax highlighting in the LaTeX output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_SOURCE_CODE = NO
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. See
+# http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
+# The default value is: plain.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BIB_STYLE = plain
+
#---------------------------------------------------------------------------
-# configuration options related to the RTF output
+# Configuration options related to the RTF output
#---------------------------------------------------------------------------
-# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
-# The RTF output is optimized for Word 97 and may not look very pretty with
-# other RTF readers or editors.
+# If the GENERATE_RTF tag is set to YES doxygen will generate RTF output. The
+# RTF output is optimized for Word 97 and may not look too pretty with other RTF
+# readers/editors.
+# The default value is: NO.
GENERATE_RTF = NO
-# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `rtf' will be used as the default path.
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: rtf.
+# This tag requires that the tag GENERATE_RTF is set to YES.
RTF_OUTPUT = rtf
-# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
-# RTF documents. This may be useful for small projects and may help to
-# save some trees in general.
+# If the COMPACT_RTF tag is set to YES doxygen generates more compact RTF
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
COMPACT_RTF = NO
-# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
-# will contain hyperlink fields. The RTF file will
-# contain links (just like the HTML output) instead of page references.
-# This makes the output suitable for online browsing using WORD or other
-# programs which support those fields.
-# Note: wordpad (write) and others do not support links.
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
+# contain hyperlink fields. The RTF file will contain links (just like the HTML
+# output) instead of page references. This makes the output suitable for online
+# browsing using Word or some other Word compatible readers that support those
+# fields.
+#
+# Note: WordPad (write) and others do not support links.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
RTF_HYPERLINKS = NO
-# Load stylesheet definitions from file. Syntax is similar to doxygen's
-# config file, i.e. a series of assignments. You only have to provide
-# replacements, missing definitions are set to their default value.
+# Load stylesheet definitions from file. Syntax is similar to doxygen's config
+# file, i.e. a series of assignments. You only have to provide replacements,
+# missing definitions are set to their default value.
+#
+# See also section "Doxygen usage" for information on how to generate the
+# default style sheet that doxygen normally uses.
+# This tag requires that the tag GENERATE_RTF is set to YES.
RTF_STYLESHEET_FILE =
-# Set optional variables used in the generation of an rtf document.
-# Syntax is similar to doxygen's config file.
+# Set optional variables used in the generation of an RTF document. Syntax is
+# similar to doxygen's config file. A template extensions file can be generated
+# using doxygen -e rtf extensionFile.
+# This tag requires that the tag GENERATE_RTF is set to YES.
RTF_EXTENSIONS_FILE =
#---------------------------------------------------------------------------
-# configuration options related to the man page output
+# Configuration options related to the man page output
#---------------------------------------------------------------------------
-# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
-# generate man pages
+# If the GENERATE_MAN tag is set to YES doxygen will generate man pages for
+# classes and files.
+# The default value is: NO.
GENERATE_MAN = NO
-# The MAN_OUTPUT tag is used to specify where the man pages will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `man' will be used as the default path.
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it. A directory man3 will be created inside the directory specified by
+# MAN_OUTPUT.
+# The default directory is: man.
+# This tag requires that the tag GENERATE_MAN is set to YES.
MAN_OUTPUT = man
-# The MAN_EXTENSION tag determines the extension that is added to
-# the generated man pages (default is the subroutine's section .3)
+# The MAN_EXTENSION tag determines the extension that is added to the generated
+# man pages. In case the manual section does not start with a number, the number
+# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
+# optional.
+# The default value is: .3.
+# This tag requires that the tag GENERATE_MAN is set to YES.
MAN_EXTENSION = .3dbus
-# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
-# then it will generate one additional man file for each entity
-# documented in the real man page(s). These additional files
-# only source the real man page, but without them the man command
-# would be unable to find the correct page. The default is NO.
+# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
+# will generate one additional man file for each entity documented in the real
+# man page(s). These additional files only source the real man page, but without
+# them the man command would be unable to find the correct page.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_MAN is set to YES.
MAN_LINKS = YES
#---------------------------------------------------------------------------
-# configuration options related to the XML output
+# Configuration options related to the XML output
#---------------------------------------------------------------------------
-# If the GENERATE_XML tag is set to YES Doxygen will
-# generate an XML file that captures the structure of
-# the code including all documentation.
+# If the GENERATE_XML tag is set to YES doxygen will generate an XML file that
+# captures the structure of the code including all documentation.
+# The default value is: NO.
GENERATE_XML = NO
-# The XML_OUTPUT tag is used to specify where the XML pages will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `xml' will be used as the default path.
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: xml.
+# This tag requires that the tag GENERATE_XML is set to YES.
XML_OUTPUT = xml
-# The XML_SCHEMA tag can be used to specify an XML schema,
-# which can be used by a validating XML parser to check the
-# syntax of the XML files.
+# The XML_SCHEMA tag can be used to specify a XML schema, which can be used by a
+# validating XML parser to check the syntax of the XML files.
+# This tag requires that the tag GENERATE_XML is set to YES.
XML_SCHEMA =
-# The XML_DTD tag can be used to specify an XML DTD,
-# which can be used by a validating XML parser to check the
-# syntax of the XML files.
+# The XML_DTD tag can be used to specify a XML DTD, which can be used by a
+# validating XML parser to check the syntax of the XML files.
+# This tag requires that the tag GENERATE_XML is set to YES.
XML_DTD =
-# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
-# dump the program listings (including syntax highlighting
-# and cross-referencing information) to the XML output. Note that
-# enabling this will significantly increase the size of the XML output.
+# If the XML_PROGRAMLISTING tag is set to YES doxygen will dump the program
+# listings (including syntax highlighting and cross-referencing information) to
+# the XML output. Note that enabling this will significantly increase the size
+# of the XML output.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_XML is set to YES.
XML_PROGRAMLISTING = YES
#---------------------------------------------------------------------------
-# configuration options for the AutoGen Definitions output
+# Configuration options related to the DOCBOOK output
#---------------------------------------------------------------------------
-# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
-# generate an AutoGen Definitions (see autogen.sf.net) file
-# that captures the structure of the code including all
-# documentation. Note that this feature is still experimental
-# and incomplete at the moment.
+# If the GENERATE_DOCBOOK tag is set to YES doxygen will generate Docbook files
+# that can be used to generate PDF.
+# The default value is: NO.
+
+GENERATE_DOCBOOK = NO
+
+# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
+# front of it.
+# The default directory is: docbook.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_OUTPUT = docbook
+
+#---------------------------------------------------------------------------
+# Configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES doxygen will generate an AutoGen
+# Definitions (see http://autogen.sf.net) file that captures the structure of
+# the code including all documentation. Note that this feature is still
+# experimental and incomplete at the moment.
+# The default value is: NO.
GENERATE_AUTOGEN_DEF = NO
#---------------------------------------------------------------------------
-# configuration options related to the Perl module output
+# Configuration options related to the Perl module output
#---------------------------------------------------------------------------
-# If the GENERATE_PERLMOD tag is set to YES Doxygen will
-# generate a Perl module file that captures the structure of
-# the code including all documentation. Note that this
-# feature is still experimental and incomplete at the
-# moment.
+# If the GENERATE_PERLMOD tag is set to YES doxygen will generate a Perl module
+# file that captures the structure of the code including all documentation.
+#
+# Note that this feature is still experimental and incomplete at the moment.
+# The default value is: NO.
GENERATE_PERLMOD = NO
-# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
-# the necessary Makefile rules, Perl scripts and LaTeX code to be able
-# to generate PDF and DVI output from the Perl module output.
+# If the PERLMOD_LATEX tag is set to YES doxygen will generate the necessary
+# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
+# output from the Perl module output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
PERLMOD_LATEX = NO
-# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
-# nicely formatted so it can be parsed by a human reader. This is useful
-# if you want to understand what is going on. On the other hand, if this
-# tag is set to NO the size of the Perl module output will be much smaller
-# and Perl will parse it just the same.
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be nicely
+# formatted so it can be parsed by a human reader. This is useful if you want to
+# understand what is going on. On the other hand, if this tag is set to NO the
+# size of the Perl module output will be much smaller and Perl will parse it
+# just the same.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
PERLMOD_PRETTY = YES
-# The names of the make variables in the generated doxyrules.make file
-# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
-# This is useful so different doxyrules.make files included by the same
-# Makefile don't overwrite each other's variables.
+# The names of the make variables in the generated doxyrules.make file are
+# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
+# so different doxyrules.make files included by the same Makefile don't
+# overwrite each other's variables.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
PERLMOD_MAKEVAR_PREFIX =
@@ -1390,108 +1921,128 @@ PERLMOD_MAKEVAR_PREFIX =
# Configuration options related to the preprocessor
#---------------------------------------------------------------------------
-# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
-# evaluate all C-preprocessor directives found in the sources and include
-# files.
+# If the ENABLE_PREPROCESSING tag is set to YES doxygen will evaluate all
+# C-preprocessor directives found in the sources and include files.
+# The default value is: YES.
ENABLE_PREPROCESSING = YES
-# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
-# names in the source code. If set to NO (the default) only conditional
-# compilation will be performed. Macro expansion can be done in a controlled
-# way by setting EXPAND_ONLY_PREDEF to YES.
+# If the MACRO_EXPANSION tag is set to YES doxygen will expand all macro names
+# in the source code. If set to NO only conditional compilation will be
+# performed. Macro expansion can be done in a controlled way by setting
+# EXPAND_ONLY_PREDEF to YES.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
MACRO_EXPANSION = NO
-# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
-# then the macro expansion is limited to the macros specified with the
-# PREDEFINED and EXPAND_AS_DEFINED tags.
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
+# the macro expansion is limited to the macros specified with the PREDEFINED and
+# EXPAND_AS_DEFINED tags.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
EXPAND_ONLY_PREDEF = NO
-# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
-# in the INCLUDE_PATH (see below) will be search if a #include is found.
+# If the SEARCH_INCLUDES tag is set to YES the includes files in the
+# INCLUDE_PATH will be searched if a #include is found.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
SEARCH_INCLUDES = YES
-# The INCLUDE_PATH tag can be used to specify one or more directories that
-# contain include files that are not input files but should be processed by
-# the preprocessor.
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by the
+# preprocessor.
+# This tag requires that the tag SEARCH_INCLUDES is set to YES.
INCLUDE_PATH =
-# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
-# patterns (like *.h and *.hpp) to filter out the header-files in the
-# directories. If left blank, the patterns specified with FILE_PATTERNS will
-# be used.
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will be
+# used.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
INCLUDE_FILE_PATTERNS =
-# The PREDEFINED tag can be used to specify one or more macro names that
-# are defined before the preprocessor is started (similar to the -D option of
-# gcc). The argument of the tag is a list of macros of the form: name
-# or name=definition (no spaces). If the definition and the = are
-# omitted =1 is assumed. To prevent a macro definition from being
-# undefined via #undef or recursively expanded use the := operator
-# instead of the = operator.
+# The PREDEFINED tag can be used to specify one or more macro names that are
+# defined before the preprocessor is started (similar to the -D option of e.g.
+# gcc). The argument of the tag is a list of macros of the form: name or
+# name=definition (no spaces). If the definition and the "=" are omitted, "=1"
+# is assumed. To prevent a macro definition from being undefined via #undef or
+# recursively expanded use the := operator instead of the = operator.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
PREDEFINED =
-# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
-# this tag can be used to specify a list of macro names that should be expanded.
-# The macro definition that is found in the sources will be used.
-# Use the PREDEFINED tag if you want to use a different macro definition that
-# overrules the definition found in the source code.
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
+# tag can be used to specify a list of macro names that should be expanded. The
+# macro definition that is found in the sources will be used. Use the PREDEFINED
+# tag if you want to use a different macro definition that overrules the
+# definition found in the source code.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
EXPAND_AS_DEFINED =
-# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
-# doxygen's preprocessor will remove all references to function-like macros
-# that are alone on a line, have an all uppercase name, and do not end with a
-# semicolon, because these will confuse the parser if not removed.
+# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
+# remove all refrences to function-like macros that are alone on a line, have an
+# all uppercase name, and do not end with a semicolon. Such function macros are
+# typically used for boiler-plate code, and will confuse the parser if not
+# removed.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
SKIP_FUNCTION_MACROS = YES
#---------------------------------------------------------------------------
-# Configuration::additions related to external references
+# Configuration options related to external references
#---------------------------------------------------------------------------
-# The TAGFILES option can be used to specify one or more tagfiles.
-# Optionally an initial location of the external documentation
-# can be added for each tagfile. The format of a tag file without
-# this location is as follows:
-# TAGFILES = file1 file2 ...
-# Adding location for the tag files is done as follows:
-# TAGFILES = file1=loc1 "file2 = loc2" ...
-# where "loc1" and "loc2" can be relative or absolute paths or
-# URLs. If a location is present for each tag, the installdox tool
-# does not have to be run to correct the links.
-# Note that each tag file must have a unique name
-# (where the name does NOT include the path)
-# If a tag file is not located in the directory in which doxygen
-# is run, you must also specify the path to the tagfile here.
+# The TAGFILES tag can be used to specify one or more tag files. For each tag
+# file the location of the external documentation should be added. The format of
+# a tag file without this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where loc1 and loc2 can be relative or absolute paths or URLs. See the
+# section "Linking to external documentation" for more information about the use
+# of tag files.
+# Note: Each tag file must have an unique name (where the name does NOT include
+# the path). If a tag file is not located in the directory in which doxygen is
+# run, you must also specify the path to the tagfile here.
TAGFILES =
-# When a file name is specified after GENERATE_TAGFILE, doxygen will create
-# a tag file that is based on the input files it reads.
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
+# tag file that is based on the input files it reads. See section "Linking to
+# external documentation" for more information about the usage of tag files.
GENERATE_TAGFILE =
-# If the ALLEXTERNALS tag is set to YES all external classes will be listed
-# in the class index. If set to NO only the inherited external classes
-# will be listed.
+# If the ALLEXTERNALS tag is set to YES all external class will be listed in the
+# class index. If set to NO only the inherited external classes will be listed.
+# The default value is: NO.
ALLEXTERNALS = NO
-# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
-# in the modules index. If set to NO, only the current project's groups will
-# be listed.
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed in
+# the modules index. If set to NO, only the current project's groups will be
+# listed.
+# The default value is: YES.
EXTERNAL_GROUPS = YES
-# The PERL_PATH should be the absolute path and name of the perl script
-# interpreter (i.e. the result of `which perl').
+# If the EXTERNAL_PAGES tag is set to YES all external pages will be listed in
+# the related pages index. If set to NO, only the current project's pages will
+# be listed.
+# The default value is: YES.
+
+EXTERNAL_PAGES = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of 'which perl').
+# The default file (with absolute path) is: /usr/bin/perl.
PERL_PATH = /usr/bin/perl
@@ -1499,204 +2050,293 @@ PERL_PATH = /usr/bin/perl
# Configuration options related to the dot tool
#---------------------------------------------------------------------------
-# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
-# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
-# or super classes. Setting the tag to NO turns the diagrams off. Note that
-# this option also works with HAVE_DOT disabled, but it is recommended to
-# install and use dot, since it yields more powerful graphs.
+# If the CLASS_DIAGRAMS tag is set to YES doxygen will generate a class diagram
+# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
+# NO turns the diagrams off. Note that this option also works with HAVE_DOT
+# disabled, but it is recommended to install and use dot, since it yields more
+# powerful graphs.
+# The default value is: YES.
CLASS_DIAGRAMS = YES
-# You can define message sequence charts within doxygen comments using the \msc
-# command. Doxygen will then run the mscgen tool (see
-# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
-# documentation. The MSCGEN_PATH tag allows you to specify the directory where
-# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see:
+# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
# default search path.
MSCGEN_PATH =
-# If set to YES, the inheritance and collaboration graphs will hide
-# inheritance and usage relations if the target is undocumented
-# or is not a class.
+# You can include diagrams made with dia in doxygen documentation. Doxygen will
+# then run dia to produce the diagram and insert it in the documentation. The
+# DIA_PATH tag allows you to specify the directory where the dia binary resides.
+# If left empty dia is assumed to be found in the default search path.
+
+DIA_PATH =
+
+# If set to YES, the inheritance and collaboration graphs will hide inheritance
+# and usage relations if the target is undocumented or is not a class.
+# The default value is: YES.
HIDE_UNDOC_RELATIONS = YES
-# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
-# available from the path. This tool is part of Graphviz, a graph visualization
-# toolkit from AT&T and Lucent Bell Labs. The other options in this section
-# have no effect if this option is set to NO (the default)
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz (see:
+# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
+# Bell Labs. The other options in this section have no effect if this option is
+# set to NO
+# The default value is: NO.
HAVE_DOT = NO
-# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is
-# allowed to run in parallel. When set to 0 (the default) doxygen will
-# base this on the number of processors available in the system. You can set it
-# explicitly to a value larger than 0 to get control over the balance
-# between CPU load and processing speed.
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
+# to run in parallel. When set to 0 doxygen will base this on the number of
+# processors available in the system. You can set it explicitly to a value
+# larger than 0 to get control over the balance between CPU load and processing
+# speed.
+# Minimum value: 0, maximum value: 32, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
DOT_NUM_THREADS = 0
-# By default doxygen will write a font called Helvetica to the output
-# directory and reference it in all dot files that doxygen generates.
-# When you want a differently looking font you can specify the font name
-# using DOT_FONTNAME. You need to make sure dot is able to find the font,
-# which can be done by putting it in a standard location or by setting the
-# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory
-# containing the font.
+# When you want a differently looking font n the dot files that doxygen
+# generates you can specify the font name using DOT_FONTNAME. You need to make
+# sure dot is able to find the font, which can be done by putting it in a
+# standard location or by setting the DOTFONTPATH environment variable or by
+# setting DOT_FONTPATH to the directory containing the font.
+# The default value is: Helvetica.
+# This tag requires that the tag HAVE_DOT is set to YES.
DOT_FONTNAME = Helvetica
-# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
-# The default size is 10pt.
+# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
+# dot graphs.
+# Minimum value: 4, maximum value: 24, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
DOT_FONTSIZE = 10
-# By default doxygen will tell dot to use the output directory to look for the
-# FreeSans.ttf font (which doxygen will put there itself). If you specify a
-# different font using DOT_FONTNAME you can set the path where dot
-# can find it using this tag.
+# By default doxygen will tell dot to use the default font as specified with
+# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
+# the path where dot can find it using this tag.
+# This tag requires that the tag HAVE_DOT is set to YES.
DOT_FONTPATH =
-# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
-# will generate a graph for each documented class showing the direct and
-# indirect inheritance relations. Setting this tag to YES will force the
-# the CLASS_DIAGRAMS tag to NO.
+# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
+# each documented class showing the direct and indirect inheritance relations.
+# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
CLASS_GRAPH = YES
-# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
-# will generate a graph for each documented class showing the direct and
-# indirect implementation dependencies (inheritance, containment, and
-# class references variables) of the class with other documented classes.
+# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
+# graph for each documented class showing the direct and indirect implementation
+# dependencies (inheritance, containment, and class references variables) of the
+# class with other documented classes.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
COLLABORATION_GRAPH = YES
-# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
-# will generate a graph for groups, showing the direct groups dependencies
+# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
+# groups, showing the direct groups dependencies.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
GROUP_GRAPHS = YES
-# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
-# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
# Language.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
UML_LOOK = NO
-# If set to YES, the inheritance and collaboration graphs will show the
-# relations between templates and their instances.
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
+# class node. If there are many fields or methods and many nodes the graph may
+# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
+# number of items for each type to make the size more manageable. Set this to 0
+# for no limit. Note that the threshold may be exceeded by 50% before the limit
+# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
+# but if the number exceeds 15, the total amount of fields shown is limited to
+# 10.
+# Minimum value: 0, maximum value: 100, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LIMIT_NUM_FIELDS = 10
+
+# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
+# collaboration graphs will show the relations between templates and their
+# instances.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
TEMPLATE_RELATIONS = YES
-# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
-# tags are set to YES then doxygen will generate a graph for each documented
-# file showing the direct and indirect include dependencies of the file with
-# other documented files.
+# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
+# YES then doxygen will generate a graph for each documented file showing the
+# direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
INCLUDE_GRAPH = YES
-# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
-# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
-# documented header file showing the documented files that directly or
-# indirectly include this file.
+# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
+# set to YES then doxygen will generate a graph for each documented file showing
+# the direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
INCLUDED_BY_GRAPH = YES
-# If the CALL_GRAPH and HAVE_DOT options are set to YES then
-# doxygen will generate a call dependency graph for every global function
-# or class method. Note that enabling this option will significantly increase
-# the time of a run. So in most cases it will be better to enable call graphs
-# for selected functions only using the \callgraph command.
+# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
CALL_GRAPH = NO
-# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
-# doxygen will generate a caller dependency graph for every global function
-# or class method. Note that enabling this option will significantly increase
-# the time of a run. So in most cases it will be better to enable caller
-# graphs for selected functions only using the \callergraph command.
+# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
CALLER_GRAPH = NO
-# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
-# will generate a graphical hierarchy of all classes instead of a textual one.
+# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
+# hierarchy of all classes instead of a textual one.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
GRAPHICAL_HIERARCHY = YES
-# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
-# then doxygen will show the dependencies a directory has on other directories
-# in a graphical way. The dependency relations are determined by the #include
-# relations between the files in the directories.
+# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
+# dependencies a directory has on other directories in a graphical way. The
+# dependency relations are determined by the #include relations between the
+# files in the directories.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
DIRECTORY_GRAPH = YES
-# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
-# generated by dot. Possible values are png, svg, gif or svg.
-# If left blank png will be used.
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot.
+# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
+# to make the SVG files visible in IE 9+ (other browsers do not have this
+# requirement).
+# Possible values are: png, jpg, gif and svg.
+# The default value is: png.
+# This tag requires that the tag HAVE_DOT is set to YES.
DOT_IMAGE_FORMAT = png
-# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+#
+# Note that this requires a modern browser other than Internet Explorer. Tested
+# and working are Firefox, Chrome, Safari, and Opera.
+# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
+# the SVG files visible. Older versions of IE do not have SVG support.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INTERACTIVE_SVG = NO
+
+# The DOT_PATH tag can be used to specify the path where the dot tool can be
# found. If left blank, it is assumed the dot tool can be found in the path.
+# This tag requires that the tag HAVE_DOT is set to YES.
DOT_PATH =
-# The DOTFILE_DIRS tag can be used to specify one or more directories that
-# contain dot files that are included in the documentation (see the
-# \dotfile command).
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the \dotfile
+# command).
+# This tag requires that the tag HAVE_DOT is set to YES.
DOTFILE_DIRS =
-# The MSCFILE_DIRS tag can be used to specify one or more directories that
-# contain msc files that are included in the documentation (see the
-# \mscfile command).
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the \mscfile
+# command).
MSCFILE_DIRS =
-# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
-# nodes that will be shown in the graph. If the number of nodes in a graph
-# becomes larger than this value, doxygen will truncate the graph, which is
-# visualized by representing a node as a red box. Note that doxygen if the
-# number of direct children of the root node in a graph is already larger than
-# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
-# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+# The DIAFILE_DIRS tag can be used to specify one or more directories that
+# contain dia files that are included in the documentation (see the \diafile
+# command).
+
+DIAFILE_DIRS =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
+# that will be shown in the graph. If the number of nodes in a graph becomes
+# larger than this value, doxygen will truncate the graph, which is visualized
+# by representing a node as a red box. Note that doxygen if the number of direct
+# children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
+# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+# Minimum value: 0, maximum value: 10000, default value: 50.
+# This tag requires that the tag HAVE_DOT is set to YES.
DOT_GRAPH_MAX_NODES = 50
-# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
-# graphs generated by dot. A depth value of 3 means that only nodes reachable
-# from the root by following a path via at most 3 edges will be shown. Nodes
-# that lay further from the root node will be omitted. Note that setting this
-# option to 1 or 2 may greatly reduce the computation time needed for large
-# code bases. Also note that the size of a graph can be further restricted by
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
+# generated by dot. A depth value of 3 means that only nodes reachable from the
+# root by following a path via at most 3 edges will be shown. Nodes that lay
+# further from the root node will be omitted. Note that setting this option to 1
+# or 2 may greatly reduce the computation time needed for large code bases. Also
+# note that the size of a graph can be further restricted by
# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+# Minimum value: 0, maximum value: 1000, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
MAX_DOT_GRAPH_DEPTH = 0
-# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
-# background. This is disabled by default, because dot on Windows does not
-# seem to support this out of the box. Warning: Depending on the platform used,
-# enabling this option may lead to badly anti-aliased labels on the edges of
-# a graph (i.e. they become hard to read).
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not seem
+# to support this out of the box.
+#
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
DOT_TRANSPARENT = NO
-# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
-# files in one run (i.e. multiple -o and -T options on the command line). This
-# makes dot run faster, but since only newer versions of dot (>1.8.10)
-# support this, this feature is disabled by default.
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10) support
+# this, this feature is disabled by default.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
DOT_MULTI_TARGETS = NO
-# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
-# generate a legend page explaining the meaning of the various boxes and
-# arrows in the dot generated graphs.
+# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
+# explaining the meaning of the various boxes and arrows in the dot generated
+# graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
GENERATE_LEGEND = YES
-# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
-# remove the intermediate dot files that are used to generate
-# the various graphs.
+# If the DOT_CLEANUP tag is set to YES doxygen will remove the intermediate dot
+# files that are used to generate the various graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
DOT_CLEANUP = YES
From 30558a7c7d747e04bee3fb9e52c5e1a2b8127be7 Mon Sep 17 00:00:00 2001
From: Stephen Pratt
Date: Wed, 23 Apr 2014 12:12:37 -0600
Subject: [PATCH 41/49] Sync Doc Home Page Content with GitHub Home
Updates the content of the documentation home page to match the content
of the GitHub repository home page. This includes the new Recast
introduction image, premake4 based build info, etc.
---
Docs/Conceptual/mainpage_c.txt | 85 +++++++++++++++++++++++----------
Docs/Images/recast_intro.png | Bin 158510 -> 378480 bytes
2 files changed, 60 insertions(+), 25 deletions(-)
diff --git a/Docs/Conceptual/mainpage_c.txt b/Docs/Conceptual/mainpage_c.txt
index 0169494..d422470 100644
--- a/Docs/Conceptual/mainpage_c.txt
+++ b/Docs/Conceptual/mainpage_c.txt
@@ -1,31 +1,24 @@
/// @mainpage Recast Navigation
///
-/// @note A documentation effort is currently underway.
-/// This documentation is in a draft state until the effort is complete.
+/// @image html recast_intro.png
+///
+/// Recast
///
-/// @section intro_recast Recast
-///
-/// Recast is state of the art navigation mesh construction toolset for
+/// `Recast` is state of the art navigation mesh construction toolset for
/// games.
///
-/// - It is automatic , which means that you can throw any level
+/// - It is automatic, which means that you can throw any level
/// geometry at it and you will get robust mesh out.
-/// - It is fast which means swift turnaround times for level
-/// designers.
-/// - It is open source so it comes with full source and you can
+/// - It is fast which means swift turnaround times for level designers.
+/// - It is open source so it comes with full source and you can
/// customize it to your hearts content.
-///
-/// The Library is free for commercial use and open source under the
-/// ZLib License.
///
-/// Please use the latest version from the
-/// SVN .
-///
-/// @image html recast_intro.png
+/// The latest version can be found on
+/// GitHub .
///
-/// The Recast process starts with constructing a voxel mold from a level
+/// The `Recast` process starts with constructing a voxel mold from a level
/// geometry and then casting a navigation mesh over it. The process
-/// consists of three steps, building the voxel mold, partitioning the
+/// consists of three steps, building the voxel mold, partitioning the
/// mold into simple regions, peeling off the regions as simple polygons.
///
/// -# The voxel mold is build from the input triangle mesh by
@@ -41,20 +34,62 @@
/// polygons are finally converted to convex polygons which makes them
/// perfect for pathfinding and spatial reasoning about the level.
///
-/// @section intro_detour Detour
+/// Detour
///
-/// Recast is accompanied by Detour, a path-finding and spatial reasoning
-/// toolkit. You can use any navigation mesh with Detour, but of course
-/// the data generated by Recast fits perfectly.
+/// `Recast` is accompanied by `Detour`, a path-finding and spatial reasoning
+/// toolkit. You can use any navigation mesh with `Detour`, but of course
+/// the data generated by `Recast` fits perfectly.
///
-/// Detour offers a simple static navigation mesh that is suitable for
+/// `Detour` offers a simple static navigation mesh that is suitable for
/// many simple cases, as well as a tiled navigation mesh that allows you
/// to add and remove pieces of the mesh. The tiled mesh allows you to
/// create systems where you stream new navigation data in and out as
/// the player progresses the level, or regenerate tiles as the
/// world changes.
///
-/// You can discuss and give feedback for Recast and Detour in the
+/// Recast Demo
+///
+/// You can find a comprehensive demo project in RecastDemo folder. It
+/// is a kitchen sink demo containing all the functionality of the library.
+/// If you are new to `Recast` & `Detour`, check out
+///
+/// Sample_SoloMesh.cpp to get started with building navmeshes and
+///
+/// NavMeshTesterTool.cpp to see how `Detour` can be used to find paths.
+///
+/// Building RecastDemo
+///
+/// RecastDemo uses premake4
+/// to build platform specific projects, now is good time to install it if
+/// you don't have it already. To build _RecasDemo_, in your favourite terminal
+/// navigate into the `RecastDemo` folder, then:
+///
+/// - OS X: `premake4 xcode4`
+/// - Windows: `premake4 vs2010`
+/// - Linux: `premake4 gmake`
+///
+/// See the premake4 documentation for full list of supported build file types.
+/// The projects will be created in `RecastDemo/Build` folder. After you have
+/// compiled the project, the _RecastDemo_ executable will be located in
+/// `RecastDemo/Bin` folder.
+///
+/// Integrating With Your Own Project
+///
+/// It is recommended to add the source directories `DebugUtils`, `Detour`,
+/// `DetourCrowd`, `DetourTileCache`, and `Recast` into your own project
+/// depending on which parts of the project you need. For example your
+/// level building tool could include _DebugUtils_, _Recast_, and _Detour_,
+/// and your game runtime could just include _Detour_.
+///
+/// Discuss
+///
+/// - Discuss _Recast_ and _Detour_:
///
-/// discussion group .
+/// Recast Navigation Group
+/// - Development Blog:
+/// Digesting Duck
+///
+/// License
+///
+/// _Recast_ and _Detour_ is licensed under ZLib license.
///
\ No newline at end of file
diff --git a/Docs/Images/recast_intro.png b/Docs/Images/recast_intro.png
index c242d69a0873008eb1ac3c8eed3c45452b93594f..ad18ac610b32758f20616a3955160db1d493b237 100644
GIT binary patch
literal 378480
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