aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core.c45
-rw-r--r--src/easings.h72
-rw-r--r--src/models.c6
-rw-r--r--src/physac.c32
-rw-r--r--src/physac.h7
-rw-r--r--src/raylib.h12
-rw-r--r--src/raymath.h414
-rw-r--r--src/rlgl.c2
-rw-r--r--src/rlgl.h9
-rw-r--r--src/stb_image.h717
-rw-r--r--src/stb_image_write.h578
-rw-r--r--src/stb_truetype.h56
-rw-r--r--src/stb_vorbis.c844
-rw-r--r--src/stb_vorbis.h65
14 files changed, 1740 insertions, 1119 deletions
diff --git a/src/core.c b/src/core.c
index 7b7d65fc..f55dba50 100644
--- a/src/core.c
+++ b/src/core.c
@@ -38,9 +38,12 @@
#include "raylib.h" // raylib main header
#include "rlgl.h" // raylib OpenGL abstraction layer to OpenGL 1.1, 3.3+ or ES2
-#include "raymath.h" // Required for data type Matrix and Matrix functions
#include "utils.h" // TraceLog() function
// NOTE: Includes Android fopen map, InitAssetManager()
+
+#define RAYMATH_IMPLEMENTATION // Use raymath as a header-only library (includes implementation)
+#define RAYMATH_EXTERN_INLINE // Compile raymath functions as static inline (remember, it's a compiler hint)
+#include "raymath.h" // Required for Vector3 and Matrix functions
#include <stdio.h> // Standard input / output lib
#include <stdlib.h> // Declares malloc() and free() for memory management, rand(), atexit()
@@ -643,6 +646,46 @@ float *ColorToFloat(Color color)
return buffer;
}
+// Converts Vector3 to float array
+float *VectorToFloat(Vector3 vec)
+{
+ static float buffer[3];
+
+ buffer[0] = vec.x;
+ buffer[1] = vec.y;
+ buffer[2] = vec.z;
+
+ return buffer;
+}
+
+// Converts Matrix to float array
+// NOTE: Returned vector is a transposed version of the Matrix struct,
+// it should be this way because, despite raymath use OpenGL column-major convention,
+// Matrix struct memory alignment and variables naming are not coherent
+float *MatrixToFloat(Matrix mat)
+{
+ static float buffer[16];
+
+ buffer[0] = mat.m0;
+ buffer[1] = mat.m4;
+ buffer[2] = mat.m8;
+ buffer[3] = mat.m12;
+ buffer[4] = mat.m1;
+ buffer[5] = mat.m5;
+ buffer[6] = mat.m9;
+ buffer[7] = mat.m13;
+ buffer[8] = mat.m2;
+ buffer[9] = mat.m6;
+ buffer[10] = mat.m10;
+ buffer[11] = mat.m14;
+ buffer[12] = mat.m3;
+ buffer[13] = mat.m7;
+ buffer[14] = mat.m11;
+ buffer[15] = mat.m15;
+
+ return buffer;
+}
+
// Returns a Color struct from hexadecimal value
Color GetColor(int hexValue)
{
diff --git a/src/easings.h b/src/easings.h
index 55dce0df..a198be4d 100644
--- a/src/easings.h
+++ b/src/easings.h
@@ -1,6 +1,12 @@
/*******************************************************************************************
*
-* raylib Easings - Useful easing functions for values animation
+* raylib easings (header only file)
+*
+* Useful easing functions for values animation
+*
+* This header uses:
+* #define EASINGS_STATIC_INLINE // Inlines all functions code, so it runs faster.
+* // This requires lots of memory on system.
*
* A port of Robert Penner's easing equations to C (http://robertpenner.com/easing/)
*
@@ -56,6 +62,14 @@
#ifndef EASINGS_H
#define EASINGS_H
+#define EASINGS_STATIC_INLINE // NOTE: By default, compile functions as static inline
+
+#if defined(EASINGS_STATIC_INLINE)
+ #define EASEDEF static inline
+#else
+ #define EASEDEF extern
+#endif
+
#include <math.h>
#ifdef __cplusplus
@@ -63,47 +77,47 @@ extern "C" { // Prevents name mangling of functions
#endif
// Linear Easing functions
-float EaseLinearNone(float t, float b, float c, float d) { return (c*t/d + b); }
-float EaseLinearIn(float t, float b, float c, float d) { return (c*t/d + b); }
-float EaseLinearOut(float t, float b, float c, float d) { return (c*t/d + b); }
-float EaseLinearInOut(float t,float b, float c, float d) { return (c*t/d + b); }
+EASEDEF float EaseLinearNone(float t, float b, float c, float d) { return (c*t/d + b); }
+EASEDEF float EaseLinearIn(float t, float b, float c, float d) { return (c*t/d + b); }
+EASEDEF float EaseLinearOut(float t, float b, float c, float d) { return (c*t/d + b); }
+EASEDEF float EaseLinearInOut(float t,float b, float c, float d) { return (c*t/d + b); }
// Sine Easing functions
-float EaseSineIn(float t, float b, float c, float d) { return (-c*cos(t/d*(PI/2)) + c + b); }
-float EaseSineOut(float t, float b, float c, float d) { return (c*sin(t/d*(PI/2)) + b); }
-float EaseSineInOut(float t, float b, float c, float d) { return (-c/2*(cos(PI*t/d) - 1) + b); }
+EASEDEF float EaseSineIn(float t, float b, float c, float d) { return (-c*cos(t/d*(PI/2)) + c + b); }
+EASEDEF float EaseSineOut(float t, float b, float c, float d) { return (c*sin(t/d*(PI/2)) + b); }
+EASEDEF float EaseSineInOut(float t, float b, float c, float d) { return (-c/2*(cos(PI*t/d) - 1) + b); }
// Circular Easing functions
-float EaseCircIn(float t, float b, float c, float d) { return (-c*(sqrt(1 - (t/=d)*t) - 1) + b); }
-float EaseCircOut(float t, float b, float c, float d) { return (c*sqrt(1 - (t=t/d-1)*t) + b); }
-float EaseCircInOut(float t, float b, float c, float d)
+EASEDEF float EaseCircIn(float t, float b, float c, float d) { return (-c*(sqrt(1 - (t/=d)*t) - 1) + b); }
+EASEDEF float EaseCircOut(float t, float b, float c, float d) { return (c*sqrt(1 - (t=t/d-1)*t) + b); }
+EASEDEF float EaseCircInOut(float t, float b, float c, float d)
{
if ((t/=d/2) < 1) return (-c/2*(sqrt(1 - t*t) - 1) + b);
return (c/2*(sqrt(1 - t*(t-=2)) + 1) + b);
}
// Cubic Easing functions
-float EaseCubicIn(float t, float b, float c, float d) { return (c*(t/=d)*t*t + b); }
-float EaseCubicOut(float t, float b, float c, float d) { return (c*((t=t/d-1)*t*t + 1) + b); }
-float EaseCubicInOut(float t, float b, float c, float d)
+EASEDEF float EaseCubicIn(float t, float b, float c, float d) { return (c*(t/=d)*t*t + b); }
+EASEDEF float EaseCubicOut(float t, float b, float c, float d) { return (c*((t=t/d-1)*t*t + 1) + b); }
+EASEDEF float EaseCubicInOut(float t, float b, float c, float d)
{
if ((t/=d/2) < 1) return (c/2*t*t*t + b);
return (c/2*((t-=2)*t*t + 2) + b);
}
// Quadratic Easing functions
-float EaseQuadIn(float t, float b, float c, float d) { return (c*(t/=d)*t + b); }
-float EaseQuadOut(float t, float b, float c, float d) { return (-c*(t/=d)*(t-2) + b); }
-float EaseQuadInOut(float t, float b, float c, float d)
+EASEDEF float EaseQuadIn(float t, float b, float c, float d) { return (c*(t/=d)*t + b); }
+EASEDEF float EaseQuadOut(float t, float b, float c, float d) { return (-c*(t/=d)*(t-2) + b); }
+EASEDEF float EaseQuadInOut(float t, float b, float c, float d)
{
if ((t/=d/2) < 1) return (((c/2)*(t*t)) + b);
return (-c/2*(((t-2)*(--t)) - 1) + b);
}
// Exponential Easing functions
-float EaseExpoIn(float t, float b, float c, float d) { return (t == 0) ? b : (c*pow(2, 10*(t/d - 1)) + b); }
-float EaseExpoOut(float t, float b, float c, float d) { return (t == d) ? (b + c) : (c*(-pow(2, -10*t/d) + 1) + b); }
-float EaseExpoInOut(float t, float b, float c, float d)
+EASEDEF float EaseExpoIn(float t, float b, float c, float d) { return (t == 0) ? b : (c*pow(2, 10*(t/d - 1)) + b); }
+EASEDEF float EaseExpoOut(float t, float b, float c, float d) { return (t == d) ? (b + c) : (c*(-pow(2, -10*t/d) + 1) + b); }
+EASEDEF float EaseExpoInOut(float t, float b, float c, float d)
{
if (t == 0) return b;
if (t == d) return (b + c);
@@ -113,20 +127,20 @@ float EaseExpoInOut(float t, float b, float c, float d)
}
// Back Easing functions
-float EaseBackIn(float t, float b, float c, float d)
+EASEDEF float EaseBackIn(float t, float b, float c, float d)
{
float s = 1.70158f;
float postFix = t/=d;
return (c*(postFix)*t*((s + 1)*t - s) + b);
}
-float EaseBackOut(float t, float b, float c, float d)
+EASEDEF float EaseBackOut(float t, float b, float c, float d)
{
float s = 1.70158f;
return (c*((t=t/d-1)*t*((s + 1)*t + s) + 1) + b);
}
-float EaseBackInOut(float t, float b, float c, float d)
+EASEDEF float EaseBackInOut(float t, float b, float c, float d)
{
float s = 1.70158f;
if ((t/=d/2) < 1) return (c/2*(t*t*(((s*=(1.525f)) + 1)*t - s)) + b);
@@ -136,8 +150,7 @@ float EaseBackInOut(float t, float b, float c, float d)
}
// Bounce Easing functions
-float EaseBounceIn(float t, float b, float c, float d) { return (c - EaseBounceOut(d-t, 0, c, d) + b); }
-float EaseBounceOut(float t, float b, float c, float d)
+EASEDEF float EaseBounceOut(float t, float b, float c, float d)
{
if ((t/=d) < (1/2.75f))
{
@@ -160,14 +173,15 @@ float EaseBounceOut(float t, float b, float c, float d)
}
}
-float EaseBounceInOut(float t, float b, float c, float d)
+EASEDEF float EaseBounceIn(float t, float b, float c, float d) { return (c - EaseBounceOut(d-t, 0, c, d) + b); }
+EASEDEF float EaseBounceInOut(float t, float b, float c, float d)
{
if (t < d/2) return (EaseBounceIn(t*2, 0, c, d)*0.5f + b);
else return (EaseBounceOut(t*2-d, 0, c, d)*0.5f + c*0.5f + b);
}
// Elastic Easing functions
-float EaseElasticIn(float t, float b, float c, float d)
+EASEDEF float EaseElasticIn(float t, float b, float c, float d)
{
if (t == 0) return b;
if ((t/=d) == 1) return (b + c);
@@ -180,7 +194,7 @@ float EaseElasticIn(float t, float b, float c, float d)
return (-(postFix*sin((t*d-s)*(2*PI)/p )) + b);
}
-float EaseElasticOut(float t, float b, float c, float d)
+EASEDEF float EaseElasticOut(float t, float b, float c, float d)
{
if (t == 0) return b;
if ((t/=d) == 1) return (b + c);
@@ -192,7 +206,7 @@ float EaseElasticOut(float t, float b, float c, float d)
return (a*pow(2,-10*t)*sin((t*d-s)*(2*PI)/p) + c + b);
}
-float EaseElasticInOut(float t, float b, float c, float d)
+EASEDEF float EaseElasticInOut(float t, float b, float c, float d)
{
if (t == 0) return b;
if ((t/=d/2) == 2) return (b + c);
diff --git a/src/models.c b/src/models.c
index 06044820..3d228a30 100644
--- a/src/models.c
+++ b/src/models.c
@@ -34,8 +34,8 @@
#include <string.h> // Required for strcmp()
#include <math.h> // Used for sin, cos, tan
-#include "raymath.h" // Required for data type Matrix and Matrix functions
#include "rlgl.h" // raylib OpenGL abstraction layer to OpenGL 1.1, 3.3+ or ES2
+#include "raymath.h" // Required for data type Matrix and Matrix functions
//----------------------------------------------------------------------------------
// Defines and Macros
@@ -1373,8 +1373,8 @@ bool CheckCollisionRayBox(Ray ray, Vector3 minBBox, Vector3 maxBBox)
BoundingBox CalculateBoundingBox(Mesh mesh)
{
// Get min and max vertex to construct bounds (AABB)
- Vector3 minVertex = mesh.vertices[0];
- Vector3 maxVertex = mesh.vertices[0];
+ Vector3 minVertex = (Vector3){ mesh.vertices[0], mesh.vertices[1], mesh.vertices[2] };
+ Vector3 maxVertex = (Vector3){ mesh.vertices[0], mesh.vertices[1], mesh.vertices[2] };
for (int i = 1; i < mesh.vertexCount; i++)
{
diff --git a/src/physac.c b/src/physac.c
index 6e3b6e61..891f0123 100644
--- a/src/physac.c
+++ b/src/physac.c
@@ -55,8 +55,8 @@ static bool collisionChecker = false;
// Module specific Functions Declarations
//----------------------------------------------------------------------------------
static float Vector2Length(Vector2 vector);
-static float Vector2LengthPoints(Vector2 a, Vector2 b);
-static Vector2 Vector2Normalize(Vector2 vector);
+static float Vector2Distance(Vector2 a, Vector2 b);
+static void Vector2Normalize(Vector2 *vector);
//----------------------------------------------------------------------------------
// Module Functions Definitions
@@ -183,9 +183,9 @@ void ApplyPhysics(int index, Vector2 *position)
{
if (colliders[index].enabled && colliders[j].enabled)
{
- if (colliders[index].type == RectangleCollider)
+ if (colliders[index].type == COLLIDER_RECTANGLE)
{
- if (colliders[j].type == RectangleCollider)
+ if (colliders[j].type == COLLIDER_RECTANGLE)
{
if (CheckCollisionRecs(colliders[index].bounds, colliders[j].bounds))
{
@@ -207,7 +207,7 @@ void ApplyPhysics(int index, Vector2 *position)
}
else
{
- if (colliders[j].type == RectangleCollider)
+ if (colliders[j].type == COLLIDER_RECTANGLE)
{
if (CheckCollisionCircleRec((Vector2){colliders[index].bounds.x, colliders[index].bounds.y}, colliders[index].radius, colliders[j].bounds))
{
@@ -277,7 +277,7 @@ void AddForceAtPosition(Vector2 position, float intensity, float radius)
Vector2 pos = {colliders[i].bounds.x, colliders[i].bounds.y};
// Get distance between rigidbody position and target position
- float distance = Vector2LengthPoints(position, pos);
+ float distance = Vector2Distance(position, pos);
if(distance <= radius)
{
@@ -285,7 +285,7 @@ void AddForceAtPosition(Vector2 position, float intensity, float radius)
Vector2 force = {colliders[i].bounds.x - position.x, colliders[i].bounds.y - position.y};
// Normalize the direction vector
- force = Vector2Normalize(force);
+ Vector2Normalize(&force);
// Invert y value
force.y *= -1;
@@ -323,20 +323,24 @@ static float Vector2Length(Vector2 vector)
return sqrt((vector.x * vector.x) + (vector.y * vector.y));
}
-static float Vector2LengthPoints(Vector2 a, Vector2 b)
+static float Vector2Distance(Vector2 a, Vector2 b)
{
Vector2 vector = {b.x - a.x, b.y - a.y};
return sqrt((vector.x * vector.x) + (vector.y * vector.y));
}
-static Vector2 Vector2Normalize(Vector2 vector)
+static void Vector2Normalize(Vector2 *vector)
{
- float length = Vector2Length(vector);
+ float length = Vector2Length(*vector);
- if(length != 0)
+ if (length != 0.0f)
{
- return (Vector2){vector.x / length, vector.y / length};
+ vector->x /= length;
+ vector->y /= length;
+ }
+ else
+ {
+ vector->x = 0.0f;
+ vector->y = 0.0f;
}
-
- return (Vector2){0, 0};
}
diff --git a/src/physac.h b/src/physac.h
index 558673ef..12209987 100644
--- a/src/physac.h
+++ b/src/physac.h
@@ -32,7 +32,8 @@
//----------------------------------------------------------------------------------
// Types and Structures Definition
//----------------------------------------------------------------------------------
-typedef enum { RectangleCollider, CircleCollider } ColliderType;
+// Collider types
+typedef enum { COLLIDER_CIRCLE, COLLIDER_RECTANGLE, COLLIDER_CAPSULE } ColliderType;
// Physics struct
typedef struct Physics {
@@ -65,8 +66,8 @@ typedef struct Rigidbody {
typedef struct Collider {
bool enabled;
ColliderType type;
- Rectangle bounds; // Just used for RectangleCollider type
- int radius; // Just used for CircleCollider type
+ Rectangle bounds; // Used for COLLIDER_RECTANGLE and COLLIDER_CAPSULE
+ int radius; // Used for COLLIDER_CIRCLE and COLLIDER_CAPSULE
} Collider;
#ifdef __cplusplus
diff --git a/src/raylib.h b/src/raylib.h
index 16311df8..5798d907 100644
--- a/src/raylib.h
+++ b/src/raylib.h
@@ -463,12 +463,12 @@ typedef struct {
typedef enum { CAMERA_CUSTOM = 0, CAMERA_FREE, CAMERA_ORBITAL, CAMERA_FIRST_PERSON, CAMERA_THIRD_PERSON } CameraMode;
// Collider types
-typedef enum { RectangleCollider, CircleCollider } ColliderType;
+typedef enum { COLLIDER_CIRCLE, COLLIDER_RECTANGLE, COLLIDER_CAPSULE } ColliderType;
// Physics struct
typedef struct Physics {
bool enabled;
- bool debug; // Should be used by programmer for testing purposes
+ bool debug; // Should be used by programmer for testing purposes
Vector2 gravity;
} Physics;
@@ -496,8 +496,8 @@ typedef struct Rigidbody {
typedef struct Collider {
bool enabled;
ColliderType type;
- Rectangle bounds; // Just used for RectangleCollider type
- int radius; // Just used for CircleCollider type
+ Rectangle bounds; // Used for COLLIDER_RECTANGLE and COLLIDER_CAPSULE
+ int radius; // Used for COLLIDER_CIRCLE and COLLIDER_CAPSULE
} Collider;
#ifdef __cplusplus
@@ -547,8 +547,8 @@ float GetFrameTime(void); // Returns time in s
Color GetColor(int hexValue); // Returns a Color struct from hexadecimal value
int GetHexValue(Color color); // Returns hexadecimal value for a Color
float *ColorToFloat(Color color); // Converts Color to float array and normalizes
-float *VectorToFloat(Vector3 vec); // Converts Vector3 to float array (defined in raymath module)
-float *MatrixToVector(Matrix mat); // Converts Matrix to float array (defined in raymath module)
+float *VectorToFloat(Vector3 vec); // Converts Vector3 to float array
+float *MatrixToFloat(Matrix mat); // Converts Matrix to float array
int GetRandomValue(int min, int max); // Returns a random value between min and max (both included)
Color Fade(Color color, float alpha); // Color fade-in or fade-out, alpha goes from 0.0f to 1.0f
diff --git a/src/raymath.h b/src/raymath.h
index f5912795..8ad32528 100644
--- a/src/raymath.h
+++ b/src/raymath.h
@@ -1,9 +1,23 @@
/**********************************************************************************************
*
-* raymath
+* raymath (header only file)
*
* Some useful functions to work with Vector3, Matrix and Quaternions
*
+* You must:
+* #define RAYMATH_IMPLEMENTATION
+* before you include this file in *only one* C or C++ file to create the implementation.
+*
+* Example:
+* #define RAYMATH_IMPLEMENTATION
+* #include "raymath.h"
+*
+* You can also use:
+* #define RAYMATH_EXTERN_INLINE // Inlines all functions code, so it runs faster.
+* // This requires lots of memory on system.
+* #define RAYMATH_STANDALONE // Not dependent on raylib.h structs: Vector3, Matrix.
+*
+*
* Copyright (c) 2015 Ramon Santamaria (@raysan5)
*
* This software is provided "as-is", without any express or implied warranty. In no event
@@ -22,37 +36,21 @@
* 3. This notice may not be removed or altered from any source distribution.
*
**********************************************************************************************/
-//============================================================================
-// YOU MUST
-//
-// #define RAYMATH_DEFINE
-//
-// Like:
-//
-// #define RAYMATH_DEFINE
-// #include "raymath.h"
-//
-// YOU CAN:
-// #define RAYMATH_INLINE //inlines all code, so it runs faster. This requires lots of memory on system.
-// AND
-// #define RAYMATH_STANDALONE //not dependent on outside libs
-//
-// This needs to be done for every library/source file.
-//============================================================================
-
-#ifdef RAYMATH_INLINE
- #define RMDEF static inline
-#else
- #define RMDEF static
-#endif
#ifndef RAYMATH_H
#define RAYMATH_H
-//#define RAYMATH_STANDALONE // NOTE: To use raymath as standalone lib, just uncomment this line
+//#define RAYMATH_STANDALONE // NOTE: To use raymath as standalone lib, just uncomment this line
+//#define RAYMATH_EXTERN_INLINE // NOTE: To compile functions as static inline, uncomment this line
#ifndef RAYMATH_STANDALONE
- #include "raylib.h" // Required for typedef: Vector3
+ #include "raylib.h" // Required for structs: Vector3, Matrix
+#endif
+
+#if defined(RAYMATH_EXTERN_INLINE)
+ #define RMDEF extern inline
+#else
+ #define RMDEF extern
#endif
//----------------------------------------------------------------------------------
@@ -63,18 +61,18 @@
#endif
#ifndef DEG2RAD
- #define DEG2RAD (PI / 180.0f)
+ #define DEG2RAD (PI/180.0f)
#endif
#ifndef RAD2DEG
- #define RAD2DEG (180.0f / PI)
+ #define RAD2DEG (180.0f/PI)
#endif
//----------------------------------------------------------------------------------
// Types and Structures Definition
//----------------------------------------------------------------------------------
-#ifdef RAYMATH_STANDALONE
+#if defined(RAYMATH_STANDALONE)
// Vector2 type
typedef struct Vector2 {
float x;
@@ -105,7 +103,77 @@ typedef struct Quaternion {
float w;
} Quaternion;
-#ifdef RAYMATH_DEFINE
+#ifndef RAYMATH_EXTERN_INLINE
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//------------------------------------------------------------------------------------
+// Functions Declaration to work with Vector3
+//------------------------------------------------------------------------------------
+RMDEF Vector3 VectorAdd(Vector3 v1, Vector3 v2); // Add two vectors
+RMDEF Vector3 VectorSubtract(Vector3 v1, Vector3 v2); // Substract two vectors
+RMDEF Vector3 VectorCrossProduct(Vector3 v1, Vector3 v2); // Calculate two vectors cross product
+RMDEF Vector3 VectorPerpendicular(Vector3 v); // Calculate one vector perpendicular vector
+RMDEF float VectorDotProduct(Vector3 v1, Vector3 v2); // Calculate two vectors dot product
+RMDEF float VectorLength(const Vector3 v); // Calculate vector lenght
+RMDEF void VectorScale(Vector3 *v, float scale); // Scale provided vector
+RMDEF void VectorNegate(Vector3 *v); // Negate provided vector (invert direction)
+RMDEF void VectorNormalize(Vector3 *v); // Normalize provided vector
+RMDEF float VectorDistance(Vector3 v1, Vector3 v2); // Calculate distance between two points
+RMDEF Vector3 VectorLerp(Vector3 v1, Vector3 v2, float amount); // Calculate linear interpolation between two vectors
+RMDEF Vector3 VectorReflect(Vector3 vector, Vector3 normal); // Calculate reflected vector to normal
+RMDEF void VectorTransform(Vector3 *v, Matrix mat); // Transforms a Vector3 by a given Matrix
+RMDEF Vector3 VectorZero(void); // Return a Vector3 init to zero
+
+//------------------------------------------------------------------------------------
+// Functions Declaration to work with Matrix
+//------------------------------------------------------------------------------------
+RMDEF float MatrixDeterminant(Matrix mat); // Compute matrix determinant
+RMDEF float MatrixTrace(Matrix mat); // Returns the trace of the matrix (sum of the values along the diagonal)
+RMDEF void MatrixTranspose(Matrix *mat); // Transposes provided matrix
+RMDEF void MatrixInvert(Matrix *mat); // Invert provided matrix
+RMDEF void MatrixNormalize(Matrix *mat); // Normalize provided matrix
+RMDEF Matrix MatrixIdentity(void); // Returns identity matrix
+RMDEF Matrix MatrixAdd(Matrix left, Matrix right); // Add two matrices
+RMDEF Matrix MatrixSubstract(Matrix left, Matrix right); // Substract two matrices (left - right)
+RMDEF Matrix MatrixTranslate(float x, float y, float z); // Returns translation matrix
+RMDEF Matrix MatrixRotate(float angle, Vector3 axis); // Returns rotation matrix for an angle around an specified axis (angle in radians)
+RMDEF Matrix MatrixRotateX(float angle); // Returns x-rotation matrix (angle in radians)
+RMDEF Matrix MatrixRotateY(float angle); // Returns y-rotation matrix (angle in radians)
+RMDEF Matrix MatrixRotateZ(float angle); // Returns z-rotation matrix (angle in radians)
+RMDEF Matrix MatrixScale(float x, float y, float z); // Returns scaling matrix
+RMDEF Matrix MatrixMultiply(Matrix left, Matrix right); // Returns two matrix multiplication
+RMDEF Matrix MatrixFrustum(double left, double right, double bottom, double top, double near, double far); // Returns perspective projection matrix
+RMDEF Matrix MatrixPerspective(double fovy, double aspect, double near, double far); // Returns perspective projection matrix
+RMDEF Matrix MatrixOrtho(double left, double right, double bottom, double top, double near, double far); // Returns orthographic projection matrix
+RMDEF Matrix MatrixLookAt(Vector3 position, Vector3 target, Vector3 up); // Returns camera look-at matrix (view matrix)
+RMDEF void PrintMatrix(Matrix m); // Print matrix utility
+
+//------------------------------------------------------------------------------------
+// Functions Declaration to work with Quaternions
+//------------------------------------------------------------------------------------
+RMDEF float QuaternionLength(Quaternion quat); // Compute the length of a quaternion
+RMDEF void QuaternionNormalize(Quaternion *q); // Normalize provided quaternion
+RMDEF Quaternion QuaternionMultiply(Quaternion q1, Quaternion q2); // Calculate two quaternion multiplication
+RMDEF Quaternion QuaternionSlerp(Quaternion q1, Quaternion q2, float slerp); // Calculates spherical linear interpolation between two quaternions
+RMDEF Quaternion QuaternionFromMatrix(Matrix matrix); // Returns a quaternion for a given rotation matrix
+RMDEF Matrix QuaternionToMatrix(Quaternion q); // Returns a matrix for a given quaternion
+RMDEF Quaternion QuaternionFromAxisAngle(float angle, Vector3 axis); // Returns rotation quaternion for an angle and axis
+RMDEF void QuaternionToAxisAngle(Quaternion q, float *outAngle, Vector3 *outAxis); // Returns the rotation angle and axis for a given quaternion
+RMDEF void QuaternionTransform(Quaternion *q, Matrix mat); // Transform a quaternion given a transformation matrix
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // notdef RAYMATH_EXTERN_INLINE
+
+//////////////////////////////////////////////////////////////////// end of header file
+
+#if defined(RAYMATH_IMPLEMENTATION) || defined(RAYMATH_EXTERN_INLINE)
+
#include <stdio.h> // Used only on PrintMatrix()
#include <math.h> // Standard math libary: sin(), cos(), tan()...
#include <stdlib.h> // Used for abs()
@@ -114,18 +182,6 @@ typedef struct Quaternion {
// Module Functions Definition - Vector3 math
//----------------------------------------------------------------------------------
-// Converts Vector3 to float array
-RMDEF float *VectorToFloat(Vector3 vec)
-{
- static float buffer[3];
-
- buffer[0] = vec.x;
- buffer[1] = vec.y;
- buffer[2] = vec.z;
-
- return buffer;
-}
-
// Add two vectors
RMDEF Vector3 VectorAdd(Vector3 v1, Vector3 v2)
{
@@ -229,9 +285,9 @@ RMDEF void VectorNormalize(Vector3 *v)
length = VectorLength(*v);
- if (length == 0) length = 1;
+ if (length == 0) length = 1.0f;
- ilength = 1.0/length;
+ ilength = 1.0f/length;
v->x *= ilength;
v->y *= ilength;
@@ -257,9 +313,9 @@ RMDEF Vector3 VectorLerp(Vector3 v1, Vector3 v2, float amount)
{
Vector3 result;
- result.x = v1.x + amount * (v2.x - v1.x);
- result.y = v1.y + amount * (v2.y - v1.y);
- result.z = v1.z + amount * (v2.z - v1.z);
+ result.x = v1.x + amount*(v2.x - v1.x);
+ result.y = v1.y + amount*(v2.y - v1.y);
+ result.z = v1.z + amount*(v2.z - v1.z);
return result;
}
@@ -269,15 +325,15 @@ RMDEF Vector3 VectorReflect(Vector3 vector, Vector3 normal)
{
// I is the original vector
// N is the normal of the incident plane
- // R = I - (2 * N * ( DotProduct[ I,N] ))
+ // R = I - (2*N*( DotProduct[ I,N] ))
Vector3 result;
float dotProduct = VectorDotProduct(vector, normal);
- result.x = vector.x - (2.0 * normal.x) * dotProduct;
- result.y = vector.y - (2.0 * normal.y) * dotProduct;
- result.z = vector.z - (2.0 * normal.z) * dotProduct;
+ result.x = vector.x - (2.0f*normal.x)*dotProduct;
+ result.y = vector.y - (2.0f*normal.y)*dotProduct;
+ result.z = vector.z - (2.0f*normal.z)*dotProduct;
return result;
}
@@ -308,34 +364,6 @@ RMDEF Vector3 VectorZero(void)
// Module Functions Definition - Matrix math
//----------------------------------------------------------------------------------
-// Converts Matrix to float array
-// NOTE: Returned vector is a transposed version of the Matrix struct,
-// it should be this way because, despite raymath use OpenGL column-major convention,
-// Matrix struct memory alignment and variables naming are not coherent
-RMDEF float *MatrixToFloat(Matrix mat)
-{
- static float buffer[16];
-
- buffer[0] = mat.m0;
- buffer[1] = mat.m4;
- buffer[2] = mat.m8;
- buffer[3] = mat.m12;
- buffer[4] = mat.m1;
- buffer[5] = mat.m5;
- buffer[6] = mat.m9;
- buffer[7] = mat.m13;
- buffer[8] = mat.m2;
- buffer[9] = mat.m6;
- buffer[10] = mat.m10;
- buffer[11] = mat.m14;
- buffer[12] = mat.m3;
- buffer[13] = mat.m7;
- buffer[14] = mat.m11;
- buffer[15] = mat.m15;
-
- return buffer;
-}
-
// Compute matrix determinant
RMDEF float MatrixDeterminant(Matrix mat)
{
@@ -413,7 +441,7 @@ RMDEF void MatrixInvert(Matrix *mat)
float b11 = a22*a33 - a23*a32;
// Calculate the invert determinant (inlined to avoid double-caching)
- float invDet = 1/(b00*b11 - b01*b10 + b02*b09 + b03*b08 - b04*b07 + b05*b06);
+ float invDet = 1.0f/(b00*b11 - b01*b10 + b02*b09 + b03*b08 - b04*b07 + b05*b06);
temp.m0 = (a11*b11 - a12*b10 + a13*b09)*invDet;
temp.m1 = (-a01*b11 + a02*b10 - a03*b09)*invDet;
@@ -461,7 +489,10 @@ RMDEF void MatrixNormalize(Matrix *mat)
// Returns identity matrix
RMDEF Matrix MatrixIdentity(void)
{
- Matrix result = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 };
+ Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f, 0.0f,
+ 0.0f, 0.0f, 0.0f, 1.0f };
return result;
}
@@ -519,7 +550,10 @@ RMDEF Matrix MatrixSubstract(Matrix left, Matrix right)
// Returns translation matrix
RMDEF Matrix MatrixTranslate(float x, float y, float z)
{
- Matrix result = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, x, y, z, 1 };
+ Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f, 0.0f,
+ x, y, z, 1.0f };
return result;
}
@@ -536,9 +570,9 @@ RMDEF Matrix MatrixRotate(float angle, Vector3 axis)
float length = sqrt(x*x + y*y + z*z);
- if ((length != 1) && (length != 0))
+ if ((length != 1.0f) && (length != 0.0f))
{
- length = 1/length;
+ length = 1.0f/length;
x *= length;
y *= length;
z *= length;
@@ -594,15 +628,15 @@ RMDEF Matrix MatrixRotate(float angle, float x, float y, float z)
m2 = result.m2, m6 = result.m6, m10 = result.m10, m14 = result.m14;
// build rotation matrix
- float r0 = x * x * c1 + c;
- float r1 = x * y * c1 + z * s;
- float r2 = x * z * c1 - y * s;
- float r4 = x * y * c1 - z * s;
- float r5 = y * y * c1 + c;
- float r6 = y * z * c1 + x * s;
- float r8 = x * z * c1 + y * s;
- float r9 = y * z * c1 - x * s;
- float r10= z * z * c1 + c;
+ float r0 = x*x*c1 + c;
+ float r1 = x*y*c1 + z*s;
+ float r2 = x*z*c1 - y*s;
+ float r4 = x*y*c1 - z*s;
+ float r5 = y*y*c1 + c;
+ float r6 = y*z*c1 + x*s;
+ float r8 = x*z*c1 + y*s;
+ float r9 = y*z*c1 - x*s;
+ float r10= z*z*c1 + c;
// multiply rotation matrix
result.m0 = r0*m0 + r4*m1 + r8*m2;
@@ -673,7 +707,10 @@ RMDEF Matrix MatrixRotateZ(float angle)
// Returns scaling matrix
RMDEF Matrix MatrixScale(float x, float y, float z)
{
- Matrix result = { x, 0, 0, 0, 0, y, 0, 0, 0, 0, z, 0, 0, 0, 0, 1 };
+ Matrix result = { x, 0.0f, 0.0f, 0.0f,
+ 0.0f, y, 0.0f, 0.0f,
+ 0.0f, 0.0f, z, 0.0f,
+ 0.0f, 0.0f, 0.0f, 1.0f };
return result;
}
@@ -713,25 +750,25 @@ RMDEF Matrix MatrixFrustum(double left, double right, double bottom, double top,
float tb = (top - bottom);
float fn = (far - near);
- result.m0 = (near*2.0f) / rl;
- result.m1 = 0;
- result.m2 = 0;
- result.m3 = 0;
+ result.m0 = (near*2.0f)/rl;
+ result.m1 = 0.0f;
+ result.m2 = 0.0f;
+ result.m3 = 0.0f;
- result.m4 = 0;
- result.m5 = (near*2.0f) / tb;
- result.m6 = 0;
- result.m7 = 0;
+ result.m4 = 0.0f;
+ result.m5 = (near*2.0f)/tb;
+ result.m6 = 0.0f;
+ result.m7 = 0.0f;
- result.m8 = (right + left) / rl;
- result.m9 = (top + bottom) / tb;
- result.m10 = -(far + near) / fn;
+ result.m8 = (right + left)/rl;
+ result.m9 = (top + bottom)/tb;
+ result.m10 = -(far + near)/fn;
result.m11 = -1.0f;
- result.m12 = 0;
- result.m13 = 0;
- result.m14 = -(far*near*2.0f) / fn;
- result.m15 = 0;
+ result.m12 = 0.0f;
+ result.m13 = 0.0f;
+ result.m14 = -(far*near*2.0f)/fn;
+ result.m15 = 0.0f;
return result;
}
@@ -739,7 +776,7 @@ RMDEF Matrix MatrixFrustum(double left, double right, double bottom, double top,
// Returns perspective projection matrix
RMDEF Matrix MatrixPerspective(double fovy, double aspect, double near, double far)
{
- double top = near*tanf(fovy*PI / 360.0f);
+ double top = near*tanf(fovy*PI/360.0f);
double right = top*aspect;
return MatrixFrustum(-right, right, -top, top, near, far);
@@ -754,22 +791,22 @@ RMDEF Matrix MatrixOrtho(double left, double right, double bottom, double top, d
float tb = (top - bottom);
float fn = (far - near);
- result.m0 = 2 / rl;
- result.m1 = 0;
- result.m2 = 0;
- result.m3 = 0;
- result.m4 = 0;
- result.m5 = 2 / tb;
- result.m6 = 0;
- result.m7 = 0;
- result.m8 = 0;
- result.m9 = 0;
- result.m10 = -2 / fn;
- result.m11 = 0;
- result.m12 = -(left + right) / rl;
- result.m13 = -(top + bottom) / tb;
- result.m14 = -(far + near) / fn;
- result.m15 = 1;
+ result.m0 = 2.0f/rl;
+ result.m1 = 0.0f;
+ result.m2 = 0.0f;
+ result.m3 = 0.0f;
+ result.m4 = 0.0f;
+ result.m5 = 2.0f/tb;
+ result.m6 = 0.0f;
+ result.m7 = 0.0f;
+ result.m8 = 0.0f;
+ result.m9 = 0.0f;
+ result.m10 = -2.0f/fn;
+ result.m11 = 0.0f;
+ result.m12 = -(left + right)/rl;
+ result.m13 = -(top + bottom)/tb;
+ result.m14 = -(far + near)/fn;
+ result.m15 = 1.0f;
return result;
}
@@ -789,19 +826,19 @@ RMDEF Matrix MatrixLookAt(Vector3 eye, Vector3 target, Vector3 up)
result.m0 = x.x;
result.m1 = x.y;
result.m2 = x.z;
- result.m3 = -((x.x * eye.x) + (x.y * eye.y) + (x.z * eye.z));
+ result.m3 = -((x.x*eye.x) + (x.y*eye.y) + (x.z*eye.z));
result.m4 = y.x;
result.m5 = y.y;
result.m6 = y.z;
- result.m7 = -((y.x * eye.x) + (y.y * eye.y) + (y.z * eye.z));
+ result.m7 = -((y.x*eye.x) + (y.y*eye.y) + (y.z*eye.z));
result.m8 = z.x;
result.m9 = z.y;
result.m10 = z.z;
- result.m11 = -((z.x * eye.x) + (z.y * eye.y) + (z.z * eye.z));
- result.m12 = 0;
- result.m13 = 0;
- result.m14 = 0;
- result.m15 = 1;
+ result.m11 = -((z.x*eye.x) + (z.y*eye.y) + (z.z*eye.z));
+ result.m12 = 0.0f;
+ result.m13 = 0.0f;
+ result.m14 = 0.0f;
+ result.m15 = 1.0f;
return result;
}
@@ -834,9 +871,9 @@ RMDEF void QuaternionNormalize(Quaternion *q)
length = QuaternionLength(*q);
- if (length == 0) length = 1;
+ if (length == 0.0f) length = 1.0f;
- ilength = 1.0/length;
+ ilength = 1.0f/length;
q->x *= ilength;
q->y *= ilength;
@@ -882,8 +919,8 @@ RMDEF Quaternion QuaternionSlerp(Quaternion q1, Quaternion q2, float amount)
}
else
{
- float ratioA = sin((1 - amount)*halfTheta) / sinHalfTheta;
- float ratioB = sin(amount*halfTheta) / sinHalfTheta;
+ float ratioA = sin((1 - amount)*halfTheta)/sinHalfTheta;
+ float ratioB = sin(amount*halfTheta)/sinHalfTheta;
result.x = (q1.x*ratioA + q2.x*ratioB);
result.y = (q1.y*ratioA + q2.y*ratioB);
@@ -902,15 +939,15 @@ RMDEF Quaternion QuaternionFromMatrix(Matrix matrix)
float trace = MatrixTrace(matrix);
- if (trace > 0)
+ if (trace > 0.0f)
{
- float s = (float)sqrt(trace + 1) * 2;
- float invS = 1 / s;
+ float s = (float)sqrt(trace + 1)*2.0f;
+ float invS = 1.0f/s;
- result.w = s * 0.25;
- result.x = (matrix.m6 - matrix.m9) * invS;
- result.y = (matrix.m8 - matrix.m2) * invS;
- result.z = (matrix.m1 - matrix.m4) * invS;
+ result.w = s*0.25f;
+ result.x = (matrix.m6 - matrix.m9)*invS;
+ result.y = (matrix.m8 - matrix.m2)*invS;
+ result.z = (matrix.m1 - matrix.m4)*invS;
}
else
{
@@ -918,33 +955,33 @@ RMDEF Quaternion QuaternionFromMatrix(Matrix matrix)
if (m00 > m11 && m00 > m22)
{
- float s = (float)sqrt(1 + m00 - m11 - m22) * 2;
- float invS = 1 / s;
+ float s = (float)sqrt(1.0f + m00 - m11 - m22)*2.0f;
+ float invS = 1.0f/s;
- result.w = (matrix.m6 - matrix.m9) * invS;
- result.x = s * 0.25;
- result.y = (matrix.m4 + matrix.m1) * invS;
- result.z = (matrix.m8 + matrix.m2) * invS;
+ result.w = (matrix.m6 - matrix.m9)*invS;
+ result.x = s*0.25f;
+ result.y = (matrix.m4 + matrix.m1)*invS;
+ result.z = (matrix.m8 + matrix.m2)*invS;
}
else if (m11 > m22)
{
- float s = (float)sqrt(1 + m11 - m00 - m22) * 2;
- float invS = 1 / s;
+ float s = (float)sqrt(1.0f + m11 - m00 - m22)*2.0f;
+ float invS = 1.0f/s;
- result.w = (matrix.m8 - matrix.m2) * invS;
- result.x = (matrix.m4 + matrix.m1) * invS;
- result.y = s * 0.25;
- result.z = (matrix.m9 + matrix.m6) * invS;
+ result.w = (matrix.m8 - matrix.m2)*invS;
+ result.x = (matrix.m4 + matrix.m1)*invS;
+ result.y = s*0.25f;
+ result.z = (matrix.m9 + matrix.m6)*invS;
}
else
{
- float s = (float)sqrt(1 + m22 - m00 - m11) * 2;
- float invS = 1 / s;
+ float s = (float)sqrt(1.0f + m22 - m00 - m11)*2.0f;
+ float invS = 1.0f/s;
- result.w = (matrix.m1 - matrix.m4) * invS;
- result.x = (matrix.m8 + matrix.m2) * invS;
- result.y = (matrix.m9 + matrix.m6) * invS;
- result.z = s * 0.25;
+ result.w = (matrix.m1 - matrix.m4)*invS;
+ result.x = (matrix.m8 + matrix.m2)*invS;
+ result.y = (matrix.m9 + matrix.m6)*invS;
+ result.z = s*0.25f;
}
}
@@ -974,22 +1011,22 @@ RMDEF Matrix QuaternionToMatrix(Quaternion q)
float wy = w*y2;
float wz = w*z2;
- result.m0 = 1 - (yy + zz);
+ result.m0 = 1.0f - (yy + zz);
result.m1 = xy - wz;
result.m2 = xz + wy;
- result.m3 = 0;
+ result.m3 = 0.0f;
result.m4 = xy + wz;
- result.m5 = 1 - (xx + zz);
+ result.m5 = 1.0f - (xx + zz);
result.m6 = yz - wx;
- result.m7 = 0;
+ result.m7 = 0.0f;
result.m8 = xz - wy;
result.m9 = yz + wx;
- result.m10 = 1 - (xx + yy);
- result.m11 = 0;
- result.m12 = 0;
- result.m13 = 0;
- result.m14 = 0;
- result.m15 = 1;
+ result.m10 = 1.0f - (xx + yy);
+ result.m11 = 0.0f;
+ result.m12 = 0.0f;
+ result.m13 = 0.0f;
+ result.m14 = 0.0f;
+ result.m15 = 1.0f;
return result;
}
@@ -998,17 +1035,17 @@ RMDEF Matrix QuaternionToMatrix(Quaternion q)
// NOTE: angle must be provided in radians
RMDEF Quaternion QuaternionFromAxisAngle(float angle, Vector3 axis)
{
- Quaternion result = { 0, 0, 0, 1 };
+ Quaternion result = { 0.0f, 0.0f, 0.0f, 1.0f };
- if (VectorLength(axis) != 0.0)
+ if (VectorLength(axis) != 0.0f)
- angle *= 0.5;
+ angle *= 0.5f;
VectorNormalize(&axis);
- result.x = axis.x * (float)sin(angle);
- result.y = axis.y * (float)sin(angle);
- result.z = axis.z * (float)sin(angle);
+ result.x = axis.x*(float)sin(angle);
+ result.y = axis.y*(float)sin(angle);
+ result.z = axis.z*(float)sin(angle);
result.w = (float)cos(angle);
QuaternionNormalize(&result);
@@ -1021,23 +1058,23 @@ RMDEF void QuaternionToAxisAngle(Quaternion q, float *outAngle, Vector3 *outAxis
{
if (fabs(q.w) > 1.0f) QuaternionNormalize(&q);
- Vector3 resAxis = { 0, 0, 0 };
- float resAngle = 0;
+ Vector3 resAxis = { 0.0f, 0.0f, 0.0f };
+ float resAngle = 0.0f;
- resAngle = 2.0f * (float)acos(q.w);
- float den = (float)sqrt(1.0 - q.w * q.w);
+ resAngle = 2.0f*(float)acos(q.w);
+ float den = (float)sqrt(1.0f - q.w*q.w);
if (den > 0.0001f)
{
- resAxis.x = q.x / den;
- resAxis.y = q.y / den;
- resAxis.z = q.z / den;
+ resAxis.x = q.x/den;
+ resAxis.y = q.y/den;
+ resAxis.z = q.z/den;
}
else
{
// This occurs when the angle is zero.
// Not a problem: just set an arbitrary normalized axis.
- resAxis.x = 1.0;
+ resAxis.x = 1.0f;
}
*outAxis = resAxis;
@@ -1058,5 +1095,6 @@ RMDEF void QuaternionTransform(Quaternion *q, Matrix mat)
q->w = mat.m3*x + mat.m7*y + mat.m11*z + mat.m15*w;
}
-#endif // RAYMATH_DEFINE
-#endif // RAYMATH_H \ No newline at end of file
+#endif // RAYMATH_IMPLEMENTATION
+
+#endif // RAYMATH_H \ No newline at end of file
diff --git a/src/rlgl.c b/src/rlgl.c
index ca08e1a2..dbcbc35f 100644
--- a/src/rlgl.c
+++ b/src/rlgl.c
@@ -32,6 +32,8 @@
#include <stdlib.h> // Declares malloc() and free() for memory management, rand()
#include <string.h> // Declares strcmp(), strlen(), strtok()
+#include "raymath.h" // Required for Vector3 and Matrix functions
+
#if defined(GRAPHICS_API_OPENGL_11)
#ifdef __APPLE__ // OpenGL include for OSX
#include <OpenGL/gl.h>
diff --git a/src/rlgl.h b/src/rlgl.h
index 066e0339..fbece962 100644
--- a/src/rlgl.h
+++ b/src/rlgl.h
@@ -37,11 +37,12 @@
#endif
#if defined(RLGL_STANDALONE)
- #define RAYMATH_STANDALONE
+ #define RAYMATH_IMPLEMENTATION // Use raymath as a header-only library (includes implementation)
+ #define RAYMATH_EXTERN_INLINE // Compile raymath functions as static inline (remember, it's a compiler hint)
+ #define RAYMATH_STANDALONE // Not dependent on raylib.h structs: Vector3, Matrix
+ #include "raymath.h" // Required for Vector3 and Matrix functions
#endif
-#include "raymath.h" // Required for data type Matrix and Matrix functions
-
// Select desired OpenGL version
// NOTE: Those preprocessor defines are only used on rlgl module,
// if OpenGL version is required by any other module, it uses rlGetVersion()
@@ -92,7 +93,7 @@ typedef enum { RL_LINES, RL_TRIANGLES, RL_QUADS } DrawMode;
typedef enum { OPENGL_11 = 1, OPENGL_33, OPENGL_ES_20 } GlVersion;
-#ifdef RLGL_STANDALONE
+#if defined(RLGL_STANDALONE)
#ifndef __cplusplus
// Boolean type
typedef enum { false, true } bool;
diff --git a/src/stb_image.h b/src/stb_image.h
index 81d1ecf6..c28b5286 100644
--- a/src/stb_image.h
+++ b/src/stb_image.h
@@ -1,4 +1,4 @@
-/* stb_image - v2.06 - public domain image loader - http://nothings.org/stb_image.h
+/* stb_image - v2.09 - public domain image loader - http://nothings.org/stb_image.h
no warranty implied; use at your own risk
Do this:
@@ -25,13 +25,16 @@
TGA (not sure what subset, if a subset)
BMP non-1bpp, non-RLE
- PSD (composited view only, no extra channels)
+ PSD (composited view only, no extra channels, 8/16 bit-per-channel)
GIF (*comp always reports as 4-channel)
HDR (radiance rgbE format)
PIC (Softimage PIC)
PNM (PPM and PGM binary only)
+ Animated GIF still needs a proper API, but here's one way to do it:
+ http://gist.github.com/urraka/685d9a6340b26b830d49
+
- decode from memory or through FILE (define STBI_NO_STDIO to remove code)
- decode from arbitrary I/O callbacks
- SIMD acceleration on x86/x64 (SSE2) and ARM (NEON)
@@ -143,6 +146,11 @@
Latest revision history:
+ 2.09 (2016-01-16) 16-bit TGA; comments in PNM files; STBI_REALLOC_SIZED
+ 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA
+ 2.07 (2015-09-13) partial animated GIF support
+ limited 16-bit PSD support
+ minor bugs, code cleanup, and compiler warnings
2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value
2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning
2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit
@@ -168,46 +176,41 @@
============================ Contributors =========================
- Image formats Bug fixes & warning fixes
- Sean Barrett (jpeg, png, bmp) Marc LeBlanc
- Nicolas Schulz (hdr, psd) Christpher Lloyd
- Jonathan Dummer (tga) Dave Moore
- Jean-Marc Lienher (gif) Won Chun
- Tom Seddon (pic) the Horde3D community
- Thatcher Ulrich (psd) Janez Zemva
- Ken Miller (pgm, ppm) Jonathan Blow
- Laurent Gomila
- Aruelien Pocheville
- Extensions, features Ryamond Barbiero
- Jetro Lauha (stbi_info) David Woo
- Martin "SpartanJ" Golini (stbi_info) Martin Golini
- James "moose2000" Brown (iPhone PNG) Roy Eltham
- Ben "Disch" Wenger (io callbacks) Luke Graham
- Omar Cornut (1/2/4-bit PNG) Thomas Ruf
- Nicolas Guillemot (vertical flip) John Bartholomew
- Ken Hamada
- Optimizations & bugfixes Cort Stratton
- Fabian "ryg" Giesen Blazej Dariusz Roszkowski
- Arseny Kapoulkine Thibault Reuille
- Paul Du Bois
- Guillaume George
- If your name should be here but Jerry Jansson
- isn't, let Sean know. Hayaki Saito
- Johan Duparc
- Ronny Chevalier
- Michal Cichon
- Tero Hanninen
- Sergio Gonzalez
- Cass Everitt
- Engin Manap
- Martins Mozeiko
- Joseph Thomson
- Phil Jordan
-
-License:
- This software is in the public domain. Where that dedication is not
- recognized, you are granted a perpetual, irrevocable license to copy
- and modify this file however you want.
+ Image formats Extensions, features
+ Sean Barrett (jpeg, png, bmp) Jetro Lauha (stbi_info)
+ Nicolas Schulz (hdr, psd) Martin "SpartanJ" Golini (stbi_info)
+ Jonathan Dummer (tga) James "moose2000" Brown (iPhone PNG)
+ Jean-Marc Lienher (gif) Ben "Disch" Wenger (io callbacks)
+ Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG)
+ Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip)
+ Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD)
+ urraka@github (animated gif) Junggon Kim (PNM comments)
+ Daniel Gibson (16-bit TGA)
+
+ Optimizations & bugfixes
+ Fabian "ryg" Giesen
+ Arseny Kapoulkine
+
+ Bug & warning fixes
+ Marc LeBlanc David Woo Guillaume George Martins Mozeiko
+ Christpher Lloyd Martin Golini Jerry Jansson Joseph Thomson
+ Dave Moore Roy Eltham Hayaki Saito Phil Jordan
+ Won Chun Luke Graham Johan Duparc Nathan Reed
+ the Horde3D community Thomas Ruf Ronny Chevalier Nick Verigakis
+ Janez Zemva John Bartholomew Michal Cichon svdijk@github
+ Jonathan Blow Ken Hamada Tero Hanninen Baldur Karlsson
+ Laurent Gomila Cort Stratton Sergio Gonzalez romigrou@github
+ Aruelien Pocheville Thibault Reuille Cass Everitt
+ Ryamond Barbiero Paul Du Bois Engin Manap
+ Blazej Dariusz Roszkowski
+ Michaelangel007@github
+
+
+LICENSE
+
+This software is in the public domain. Where that dedication is not
+recognized, you are granted a perpetual, irrevocable license to copy,
+distribute, and modify this file as you see fit.
*/
@@ -458,12 +461,12 @@ STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y,
#ifndef STBI_NO_HDR
STBIDEF void stbi_hdr_to_ldr_gamma(float gamma);
STBIDEF void stbi_hdr_to_ldr_scale(float scale);
-#endif
+#endif // STBI_NO_HDR
#ifndef STBI_NO_LINEAR
STBIDEF void stbi_ldr_to_hdr_gamma(float gamma);
STBIDEF void stbi_ldr_to_hdr_scale(float scale);
-#endif // STBI_NO_HDR
+#endif // STBI_NO_LINEAR
// stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR
STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user);
@@ -627,18 +630,22 @@ typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1];
#define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (32 - (y))))
#endif
-#if defined(STBI_MALLOC) && defined(STBI_FREE) && defined(STBI_REALLOC)
+#if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED))
// ok
-#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC)
+#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) && !defined(STBI_REALLOC_SIZED)
// ok
#else
-#error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC."
+#error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC (or STBI_REALLOC_SIZED)."
#endif
#ifndef STBI_MALLOC
-#define STBI_MALLOC(sz) malloc(sz)
-#define STBI_REALLOC(p,sz) realloc(p,sz)
-#define STBI_FREE(p) free(p)
+#define STBI_MALLOC(sz) malloc(sz)
+#define STBI_REALLOC(p,newsz) realloc(p,newsz)
+#define STBI_FREE(p) free(p)
+#endif
+
+#ifndef STBI_REALLOC_SIZED
+#define STBI_REALLOC_SIZED(p,oldsz,newsz) STBI_REALLOC(p,newsz)
#endif
// x86/x64 detection
@@ -757,7 +764,7 @@ typedef struct
stbi_uc buffer_start[128];
stbi_uc *img_buffer, *img_buffer_end;
- stbi_uc *img_buffer_original;
+ stbi_uc *img_buffer_original, *img_buffer_original_end;
} stbi__context;
@@ -769,7 +776,7 @@ static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len)
s->io.read = NULL;
s->read_from_callbacks = 0;
s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer;
- s->img_buffer_end = (stbi_uc *) buffer+len;
+ s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len;
}
// initialize a callback-based context
@@ -781,6 +788,7 @@ static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *
s->read_from_callbacks = 1;
s->img_buffer_original = s->buffer_start;
stbi__refill_buffer(s);
+ s->img_buffer_original_end = s->img_buffer_end;
}
#ifndef STBI_NO_STDIO
@@ -822,6 +830,7 @@ static void stbi__rewind(stbi__context *s)
// 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;
+ s->img_buffer_end = s->img_buffer_original_end;
}
#ifndef STBI_NO_JPEG
@@ -909,8 +918,8 @@ static void *stbi__malloc(size_t size)
#define stbi__err(x,y) stbi__err(x)
#endif
-#define stbi__errpf(x,y) ((float *) (stbi__err(x,y)?NULL:NULL))
-#define stbi__errpuc(x,y) ((unsigned char *) (stbi__err(x,y)?NULL:NULL))
+#define stbi__errpf(x,y) ((float *)(size_t) (stbi__err(x,y)?NULL:NULL))
+#define stbi__errpuc(x,y) ((unsigned char *)(size_t) (stbi__err(x,y)?NULL:NULL))
STBIDEF void stbi_image_free(void *retval_from_stbi_load)
{
@@ -997,6 +1006,7 @@ static unsigned char *stbi__load_flip(stbi__context *s, int *x, int *y, int *com
return result;
}
+#ifndef STBI_NO_HDR
static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp)
{
if (stbi__vertically_flip_on_load && result != NULL) {
@@ -1017,7 +1027,7 @@ static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, in
}
}
}
-
+#endif
#ifndef STBI_NO_STDIO
@@ -1161,6 +1171,7 @@ STBIDEF int stbi_is_hdr_from_file(FILE *f)
stbi__start_file(&s,f);
return stbi__hdr_test(&s);
#else
+ STBI_NOTUSED(f);
return 0;
#endif
}
@@ -1173,18 +1184,21 @@ STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void
stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user);
return stbi__hdr_test(&s);
#else
+ STBI_NOTUSED(clbk);
+ STBI_NOTUSED(user);
return 0;
#endif
}
-static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f;
+#ifndef STBI_NO_LINEAR
static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f;
-#ifndef STBI_NO_LINEAR
STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; }
STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; }
#endif
+static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f;
+
STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; }
STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; }
@@ -1293,17 +1307,23 @@ static stbi__uint32 stbi__get32be(stbi__context *s)
return (z << 16) + stbi__get16be(s);
}
+#if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF)
+// nothing
+#else
static int stbi__get16le(stbi__context *s)
{
int z = stbi__get8(s);
return z + (stbi__get8(s) << 8);
}
+#endif
+#ifndef STBI_NO_BMP
static stbi__uint32 stbi__get32le(stbi__context *s)
{
stbi__uint32 z = stbi__get16le(s);
return z + (stbi__get16le(s) << 16);
}
+#endif
#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings
@@ -2747,7 +2767,7 @@ static int stbi__process_frame_header(stbi__jpeg *z, int scan)
if (z->img_comp[i].raw_data == NULL) {
for(--i; i >= 0; --i) {
STBI_FREE(z->img_comp[i].raw_data);
- z->img_comp[i].data = NULL;
+ z->img_comp[i].raw_data = NULL;
}
return stbi__err("outofmem", "Out of memory");
}
@@ -3509,10 +3529,10 @@ static int stbi__zbuild_huffman(stbi__zhuffman *z, stbi_uc *sizelist, int num)
z->size [c] = (stbi_uc ) s;
z->value[c] = (stbi__uint16) i;
if (s <= STBI__ZFAST_BITS) {
- int k = stbi__bit_reverse(next_code[s],s);
- while (k < (1 << STBI__ZFAST_BITS)) {
- z->fast[k] = fastv;
- k += (1 << s);
+ int j = stbi__bit_reverse(next_code[s],s);
+ while (j < (1 << STBI__ZFAST_BITS)) {
+ z->fast[j] = fastv;
+ j += (1 << s);
}
}
++next_code[s];
@@ -3551,7 +3571,7 @@ static void stbi__fill_bits(stbi__zbuf *z)
{
do {
STBI_ASSERT(z->code_buffer < (1U << z->num_bits));
- z->code_buffer |= stbi__zget8(z) << z->num_bits;
+ z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits;
z->num_bits += 8;
} while (z->num_bits <= 24);
}
@@ -3601,14 +3621,14 @@ stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z)
static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes
{
char *q;
- int cur, limit;
+ int cur, limit, old_limit;
z->zout = zout;
if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG");
cur = (int) (z->zout - z->zout_start);
- limit = (int) (z->zout_end - z->zout_start);
+ limit = old_limit = (int) (z->zout_end - z->zout_start);
while (cur + n > limit)
limit *= 2;
- q = (char *) STBI_REALLOC(z->zout_start, limit);
+ q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit);
if (q == NULL) return stbi__err("outofmem", "Out of memory");
z->zout_start = q;
z->zout = q + cur;
@@ -4117,21 +4137,21 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r
if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01);
}
if (img_n != out_n) {
+ int q;
// insert alpha = 255
- stbi_uc *cur = a->out + stride*j;
- int i;
+ cur = a->out + stride*j;
if (img_n == 1) {
- for (i=x-1; i >= 0; --i) {
- cur[i*2+1] = 255;
- cur[i*2+0] = cur[i];
+ for (q=x-1; q >= 0; --q) {
+ cur[q*2+1] = 255;
+ cur[q*2+0] = cur[q];
}
} else {
STBI_ASSERT(img_n == 3);
- for (i=x-1; i >= 0; --i) {
- cur[i*4+3] = 255;
- cur[i*4+2] = cur[i*3+2];
- cur[i*4+1] = cur[i*3+1];
- cur[i*4+0] = cur[i*3+0];
+ for (q=x-1; q >= 0; --q) {
+ cur[q*4+3] = 255;
+ cur[q*4+2] = cur[q*3+2];
+ cur[q*4+1] = cur[q*3+1];
+ cur[q*4+0] = cur[q*3+0];
}
}
}
@@ -4393,11 +4413,12 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp)
if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; }
if ((int)(ioff + c.length) < (int)ioff) return 0;
if (ioff + c.length > idata_limit) {
+ stbi__uint32 idata_limit_old = idata_limit;
stbi_uc *p;
if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096;
while (ioff + c.length > idata_limit)
idata_limit *= 2;
- p = (stbi_uc *) STBI_REALLOC(z->idata, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory");
+ p = (stbi_uc *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory");
z->idata = p;
}
if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG");
@@ -4583,19 +4604,22 @@ static int stbi__shiftsigned(int v, int shift, int bits)
return result;
}
-static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp)
+typedef struct
{
- stbi_uc *out;
- unsigned int mr=0,mg=0,mb=0,ma=0, fake_a=0;
- stbi_uc pal[256][4];
- int psize=0,i,j,compress=0,width;
- int bpp, flip_vertically, pad, target, offset, hsz;
+ int bpp, offset, hsz;
+ unsigned int mr,mg,mb,ma, all_a;
+} stbi__bmp_data;
+
+static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info)
+{
+ int hsz;
if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc("not BMP", "Corrupt BMP");
stbi__get32le(s); // discard filesize
stbi__get16le(s); // discard reserved
stbi__get16le(s); // discard reserved
- offset = stbi__get32le(s);
- hsz = stbi__get32le(s);
+ info->offset = stbi__get32le(s);
+ info->hsz = hsz = stbi__get32le(s);
+
if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown");
if (hsz == 12) {
s->img_x = stbi__get16le(s);
@@ -4605,15 +4629,10 @@ static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int
s->img_y = stbi__get32le(s);
}
if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP");
- bpp = stbi__get16le(s);
- if (bpp == 1) return stbi__errpuc("monochrome", "BMP type not supported: 1-bit");
- flip_vertically = ((int) s->img_y) > 0;
- s->img_y = abs((int) s->img_y);
- if (hsz == 12) {
- if (bpp < 24)
- psize = (offset - 14 - 24) / 3;
- } else {
- compress = stbi__get32le(s);
+ info->bpp = stbi__get16le(s);
+ if (info->bpp == 1) return stbi__errpuc("monochrome", "BMP type not supported: 1-bit");
+ if (hsz != 12) {
+ int compress = stbi__get32le(s);
if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE");
stbi__get32le(s); // discard sizeof
stbi__get32le(s); // discard hres
@@ -4627,27 +4646,26 @@ static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int
stbi__get32le(s);
stbi__get32le(s);
}
- if (bpp == 16 || bpp == 32) {
- mr = mg = mb = 0;
+ if (info->bpp == 16 || info->bpp == 32) {
+ info->mr = info->mg = info->mb = 0;
if (compress == 0) {
- if (bpp == 32) {
- mr = 0xffu << 16;
- mg = 0xffu << 8;
- mb = 0xffu << 0;
- ma = 0xffu << 24;
- fake_a = 1; // @TODO: check for cases like alpha value is all 0 and switch it to 255
- STBI_NOTUSED(fake_a);
+ if (info->bpp == 32) {
+ info->mr = 0xffu << 16;
+ info->mg = 0xffu << 8;
+ info->mb = 0xffu << 0;
+ info->ma = 0xffu << 24;
+ info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0
} else {
- mr = 31u << 10;
- mg = 31u << 5;
- mb = 31u << 0;
+ info->mr = 31u << 10;
+ info->mg = 31u << 5;
+ info->mb = 31u << 0;
}
} else if (compress == 3) {
- mr = stbi__get32le(s);
- mg = stbi__get32le(s);
- mb = stbi__get32le(s);
+ info->mr = stbi__get32le(s);
+ info->mg = stbi__get32le(s);
+ info->mb = stbi__get32le(s);
// not documented, but generated by photoshop and handled by mspaint
- if (mr == mg && mg == mb) {
+ if (info->mr == info->mg && info->mg == info->mb) {
// ?!?!?
return stbi__errpuc("bad BMP", "bad BMP");
}
@@ -4655,11 +4673,13 @@ static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int
return stbi__errpuc("bad BMP", "bad BMP");
}
} else {
- STBI_ASSERT(hsz == 108 || hsz == 124);
- mr = stbi__get32le(s);
- mg = stbi__get32le(s);
- mb = stbi__get32le(s);
- ma = stbi__get32le(s);
+ int i;
+ if (hsz != 108 && hsz != 124)
+ return stbi__errpuc("bad BMP", "bad BMP");
+ info->mr = stbi__get32le(s);
+ info->mg = stbi__get32le(s);
+ info->mb = stbi__get32le(s);
+ info->ma = stbi__get32le(s);
stbi__get32le(s); // discard color space
for (i=0; i < 12; ++i)
stbi__get32le(s); // discard color space parameters
@@ -4670,35 +4690,68 @@ static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int
stbi__get32le(s); // discard reserved
}
}
- if (bpp < 16)
- psize = (offset - 14 - hsz) >> 2;
}
+ return (void *) 1;
+}
+
+
+static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp)
+{
+ stbi_uc *out;
+ unsigned int mr=0,mg=0,mb=0,ma=0, all_a;
+ stbi_uc pal[256][4];
+ int psize=0,i,j,width;
+ int flip_vertically, pad, target;
+ stbi__bmp_data info;
+
+ info.all_a = 255;
+ if (stbi__bmp_parse_header(s, &info) == NULL)
+ return NULL; // error code already set
+
+ flip_vertically = ((int) s->img_y) > 0;
+ s->img_y = abs((int) s->img_y);
+
+ mr = info.mr;
+ mg = info.mg;
+ mb = info.mb;
+ ma = info.ma;
+ all_a = info.all_a;
+
+ if (info.hsz == 12) {
+ if (info.bpp < 24)
+ psize = (info.offset - 14 - 24) / 3;
+ } else {
+ if (info.bpp < 16)
+ psize = (info.offset - 14 - info.hsz) >> 2;
+ }
+
s->img_n = ma ? 4 : 3;
if (req_comp && req_comp >= 3) // we can directly decode 3 or 4
target = req_comp;
else
target = s->img_n; // if they want monochrome, we'll post-convert
+
out = (stbi_uc *) stbi__malloc(target * s->img_x * s->img_y);
if (!out) return stbi__errpuc("outofmem", "Out of memory");
- if (bpp < 16) {
+ if (info.bpp < 16) {
int z=0;
if (psize == 0 || psize > 256) { STBI_FREE(out); return stbi__errpuc("invalid", "Corrupt BMP"); }
for (i=0; i < psize; ++i) {
pal[i][2] = stbi__get8(s);
pal[i][1] = stbi__get8(s);
pal[i][0] = stbi__get8(s);
- if (hsz != 12) stbi__get8(s);
+ if (info.hsz != 12) stbi__get8(s);
pal[i][3] = 255;
}
- stbi__skip(s, offset - 14 - hsz - psize * (hsz == 12 ? 3 : 4));
- if (bpp == 4) width = (s->img_x + 1) >> 1;
- else if (bpp == 8) width = s->img_x;
+ stbi__skip(s, info.offset - 14 - info.hsz - psize * (info.hsz == 12 ? 3 : 4));
+ if (info.bpp == 4) width = (s->img_x + 1) >> 1;
+ else if (info.bpp == 8) width = s->img_x;
else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); }
pad = (-width)&3;
for (j=0; j < (int) s->img_y; ++j) {
for (i=0; i < (int) s->img_x; i += 2) {
int v=stbi__get8(s),v2=0;
- if (bpp == 4) {
+ if (info.bpp == 4) {
v2 = v & 15;
v >>= 4;
}
@@ -4707,7 +4760,7 @@ static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int
out[z++] = pal[v][2];
if (target == 4) out[z++] = 255;
if (i+1 == (int) s->img_x) break;
- v = (bpp == 8) ? stbi__get8(s) : v2;
+ v = (info.bpp == 8) ? stbi__get8(s) : v2;
out[z++] = pal[v][0];
out[z++] = pal[v][1];
out[z++] = pal[v][2];
@@ -4719,14 +4772,14 @@ static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int
int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0;
int z = 0;
int easy=0;
- stbi__skip(s, offset - 14 - hsz);
- if (bpp == 24) width = 3 * s->img_x;
- else if (bpp == 16) width = 2*s->img_x;
+ stbi__skip(s, info.offset - 14 - info.hsz);
+ if (info.bpp == 24) width = 3 * s->img_x;
+ else if (info.bpp == 16) width = 2*s->img_x;
else /* bpp = 32 and pad = 0 */ width=0;
pad = (-width) & 3;
- if (bpp == 24) {
+ if (info.bpp == 24) {
easy = 1;
- } else if (bpp == 32) {
+ } else if (info.bpp == 32) {
if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000)
easy = 2;
}
@@ -4747,9 +4800,11 @@ static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int
out[z+0] = stbi__get8(s);
z += 3;
a = (easy == 2 ? stbi__get8(s) : 255);
+ all_a |= a;
if (target == 4) out[z++] = a;
}
} else {
+ int bpp = info.bpp;
for (i=0; i < (int) s->img_x; ++i) {
stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s));
int a;
@@ -4757,12 +4812,19 @@ static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int
out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount));
out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount));
a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255);
+ all_a |= a;
if (target == 4) out[z++] = STBI__BYTECAST(a);
}
}
stbi__skip(s, pad);
}
}
+
+ // if alpha channel is all 0s, replace with all 255s
+ if (target == 4 && all_a == 0)
+ for (i=4*s->img_x*s->img_y-1; i >= 0; i -= 4)
+ out[i] = 255;
+
if (flip_vertically) {
stbi_uc t;
for (j=0; j < (int) s->img_y>>1; ++j) {
@@ -4789,20 +4851,55 @@ static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int
// Targa Truevision - TGA
// by Jonathan Dummer
#ifndef STBI_NO_TGA
+// returns STBI_rgb or whatever, 0 on error
+static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16)
+{
+ // only RGB or RGBA (incl. 16bit) or grey allowed
+ if(is_rgb16) *is_rgb16 = 0;
+ switch(bits_per_pixel) {
+ case 8: return STBI_grey;
+ case 16: if(is_grey) return STBI_grey_alpha;
+ // else: fall-through
+ case 15: if(is_rgb16) *is_rgb16 = 1;
+ return STBI_rgb;
+ case 24: // fall-through
+ case 32: return bits_per_pixel/8;
+ default: return 0;
+ }
+}
+
static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp)
{
- int tga_w, tga_h, tga_comp;
- int sz;
+ int tga_w, tga_h, tga_comp, tga_image_type, tga_bits_per_pixel, tga_colormap_bpp;
+ int sz, tga_colormap_type;
stbi__get8(s); // discard Offset
- sz = stbi__get8(s); // color type
- if( sz > 1 ) {
+ tga_colormap_type = stbi__get8(s); // colormap type
+ if( tga_colormap_type > 1 ) {
stbi__rewind(s);
return 0; // only RGB or indexed allowed
}
- sz = stbi__get8(s); // image type
- // only RGB or grey allowed, +/- RLE
- if ((sz != 1) && (sz != 2) && (sz != 3) && (sz != 9) && (sz != 10) && (sz != 11)) return 0;
- stbi__skip(s,9);
+ tga_image_type = stbi__get8(s); // image type
+ if ( tga_colormap_type == 1 ) { // colormapped (paletted) image
+ if (tga_image_type != 1 && tga_image_type != 9) {
+ stbi__rewind(s);
+ return 0;
+ }
+ stbi__skip(s,4); // skip index of first colormap entry and number of entries
+ sz = stbi__get8(s); // check bits per palette color entry
+ if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) {
+ stbi__rewind(s);
+ return 0;
+ }
+ stbi__skip(s,4); // skip image x and y origin
+ tga_colormap_bpp = sz;
+ } else { // "normal" image w/o colormap - only RGB or grey allowed, +/- RLE
+ if ( (tga_image_type != 2) && (tga_image_type != 3) && (tga_image_type != 10) && (tga_image_type != 11) ) {
+ stbi__rewind(s);
+ return 0; // only RGB or grey allowed, +/- RLE
+ }
+ stbi__skip(s,9); // skip colormap specification and image x/y origin
+ tga_colormap_bpp = 0;
+ }
tga_w = stbi__get16le(s);
if( tga_w < 1 ) {
stbi__rewind(s);
@@ -4813,44 +4910,80 @@ static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp)
stbi__rewind(s);
return 0; // test height
}
- sz = stbi__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_bits_per_pixel = stbi__get8(s); // bits per pixel
+ stbi__get8(s); // ignore alpha bits
+ if (tga_colormap_bpp != 0) {
+ if((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16)) {
+ // when using a colormap, tga_bits_per_pixel is the size of the indexes
+ // I don't think anything but 8 or 16bit indexes makes sense
+ stbi__rewind(s);
+ return 0;
+ }
+ tga_comp = stbi__tga_get_comp(tga_colormap_bpp, 0, NULL);
+ } else {
+ tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3) || (tga_image_type == 11), NULL);
+ }
+ if(!tga_comp) {
+ stbi__rewind(s);
+ return 0;
}
- tga_comp = sz;
if (x) *x = tga_w;
if (y) *y = tga_h;
- if (comp) *comp = tga_comp / 8;
+ if (comp) *comp = tga_comp;
return 1; // seems to have passed everything
}
static int stbi__tga_test(stbi__context *s)
{
- int res;
- int sz;
+ int res = 0;
+ int sz, tga_color_type;
stbi__get8(s); // discard Offset
- sz = stbi__get8(s); // color type
- if ( sz > 1 ) return 0; // only RGB or indexed allowed
+ tga_color_type = stbi__get8(s); // color type
+ if ( tga_color_type > 1 ) goto errorEnd; // only RGB or indexed allowed
sz = stbi__get8(s); // image type
- if ( (sz != 1) && (sz != 2) && (sz != 3) && (sz != 9) && (sz != 10) && (sz != 11) ) return 0; // only RGB or grey allowed, +/- RLE
- stbi__get16be(s); // discard palette start
- stbi__get16be(s); // discard palette length
- stbi__get8(s); // discard bits per palette color entry
- stbi__get16be(s); // discard x origin
- stbi__get16be(s); // discard y origin
- if ( stbi__get16be(s) < 1 ) return 0; // test width
- if ( stbi__get16be(s) < 1 ) return 0; // test height
+ if ( tga_color_type == 1 ) { // colormapped (paletted) image
+ if (sz != 1 && sz != 9) goto errorEnd; // colortype 1 demands image type 1 or 9
+ stbi__skip(s,4); // skip index of first colormap entry and number of entries
+ sz = stbi__get8(s); // check bits per palette color entry
+ if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd;
+ stbi__skip(s,4); // skip image x and y origin
+ } else { // "normal" image w/o colormap
+ if ( (sz != 2) && (sz != 3) && (sz != 10) && (sz != 11) ) goto errorEnd; // only RGB or grey allowed, +/- RLE
+ stbi__skip(s,9); // skip colormap specification and image x/y origin
+ }
+ if ( stbi__get16le(s) < 1 ) goto errorEnd; // test width
+ if ( stbi__get16le(s) < 1 ) goto errorEnd; // test height
sz = stbi__get8(s); // bits per pixel
- if ( (sz != 8) && (sz != 16) && (sz != 24) && (sz != 32) )
- res = 0;
- else
- res = 1;
+ if ( (tga_color_type == 1) && (sz != 8) && (sz != 16) ) goto errorEnd; // for colormapped images, bpp is size of an index
+ if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd;
+
+ res = 1; // if we got this far, everything's good and we can return 1 instead of 0
+
+errorEnd:
stbi__rewind(s);
return res;
}
+// read 16bit value and convert to 24bit RGB
+void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out)
+{
+ stbi__uint16 px = stbi__get16le(s);
+ stbi__uint16 fiveBitMask = 31;
+ // we have 3 channels with 5bits each
+ int r = (px >> 10) & fiveBitMask;
+ int g = (px >> 5) & fiveBitMask;
+ int b = px & fiveBitMask;
+ // Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later
+ out[0] = (r * 255)/31;
+ out[1] = (g * 255)/31;
+ out[2] = (b * 255)/31;
+
+ // some people claim that the most significant bit might be used for alpha
+ // (possibly if an alpha-bit is set in the "image descriptor byte")
+ // but that only made 16bit test images completely translucent..
+ // so let's treat all 15 and 16bit TGAs as RGB with no alpha.
+}
+
static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp)
{
// read in the TGA header stuff
@@ -4866,8 +4999,9 @@ static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int
int tga_width = stbi__get16le(s);
int tga_height = stbi__get16le(s);
int tga_bits_per_pixel = stbi__get8(s);
- int tga_comp = tga_bits_per_pixel / 8;
+ int tga_comp, tga_rgb16=0;
int tga_inverted = stbi__get8(s);
+ // int tga_alpha_bits = tga_inverted & 15; // the 4 lowest bits - unused (useless?)
// image data
unsigned char *tga_data;
unsigned char *tga_palette = NULL;
@@ -4883,25 +5017,14 @@ static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int
tga_image_type -= 8;
tga_is_RLE = 1;
}
- /* int tga_alpha_bits = tga_inverted & 15; */
tga_inverted = 1 - ((tga_inverted >> 5) & 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; // we don't report this as a bad TGA because we don't even know if it's TGA
- }
-
// If I'm paletted, then I'll use the number of bits from the palette
- if ( tga_indexed )
- {
- tga_comp = tga_palette_bits / 8;
- }
+ if ( tga_indexed ) tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16);
+ else tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3), &tga_rgb16);
+
+ if(!tga_comp) // shouldn't really happen, stbi__tga_test() should have ensured basic consistency
+ return stbi__errpuc("bad format", "Can't find out TGA pixelformat");
// tga info
*x = tga_width;
@@ -4914,10 +5037,10 @@ static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int
// skip to the data's starting position (offset usually = 0)
stbi__skip(s, tga_offset );
- if ( !tga_indexed && !tga_is_RLE) {
+ if ( !tga_indexed && !tga_is_RLE && !tga_rgb16 ) {
for (i=0; i < tga_height; ++i) {
- int y = tga_inverted ? tga_height -i - 1 : i;
- stbi_uc *tga_row = tga_data + y*tga_width*tga_comp;
+ int row = tga_inverted ? tga_height -i - 1 : i;
+ stbi_uc *tga_row = tga_data + row*tga_width*tga_comp;
stbi__getn(s, tga_row, tga_width * tga_comp);
}
} else {
@@ -4927,15 +5050,22 @@ static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int
// any data to skip? (offset usually = 0)
stbi__skip(s, tga_palette_start );
// load the palette
- tga_palette = (unsigned char*)stbi__malloc( tga_palette_len * tga_palette_bits / 8 );
+ tga_palette = (unsigned char*)stbi__malloc( tga_palette_len * tga_comp );
if (!tga_palette) {
STBI_FREE(tga_data);
return stbi__errpuc("outofmem", "Out of memory");
}
- if (!stbi__getn(s, tga_palette, tga_palette_len * tga_palette_bits / 8 )) {
- STBI_FREE(tga_data);
- STBI_FREE(tga_palette);
- return stbi__errpuc("bad palette", "Corrupt TGA");
+ if (tga_rgb16) {
+ stbi_uc *pal_entry = tga_palette;
+ STBI_ASSERT(tga_comp == STBI_rgb);
+ for (i=0; i < tga_palette_len; ++i) {
+ stbi__tga_read_rgb16(s, pal_entry);
+ pal_entry += tga_comp;
+ }
+ } else if (!stbi__getn(s, tga_palette, tga_palette_len * tga_comp)) {
+ STBI_FREE(tga_data);
+ STBI_FREE(tga_palette);
+ return stbi__errpuc("bad palette", "Corrupt TGA");
}
}
// load the data
@@ -4965,23 +5095,22 @@ static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int
// load however much data we did have
if ( tga_indexed )
{
- // read in 1 byte, then perform the lookup
- int pal_idx = stbi__get8(s);
- if ( pal_idx >= tga_palette_len )
- {
- // invalid index
+ // read in index, then perform the lookup
+ int pal_idx = (tga_bits_per_pixel == 8) ? stbi__get8(s) : stbi__get16le(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)
- {
+ pal_idx *= tga_comp;
+ for (j = 0; j < tga_comp; ++j) {
raw_data[j] = tga_palette[pal_idx+j];
}
- } else
- {
+ } else if(tga_rgb16) {
+ STBI_ASSERT(tga_comp == STBI_rgb);
+ stbi__tga_read_rgb16(s, raw_data);
+ } else {
// read in the data raw
- for (j = 0; j*8 < tga_bits_per_pixel; ++j)
- {
+ for (j = 0; j < tga_comp; ++j) {
raw_data[j] = stbi__get8(s);
}
}
@@ -5020,8 +5149,8 @@ static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int
}
}
- // swap RGB
- if (tga_comp >= 3)
+ // swap RGB - if the source data was RGB16, it already is in the right order
+ if (tga_comp >= 3 && !tga_rgb16)
{
unsigned char* tga_pixel = tga_data;
for (i=0; i < tga_width * tga_height; ++i)
@@ -5062,6 +5191,7 @@ static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int
int pixelCount;
int channelCount, compression;
int channel, i, count, len;
+ int bitdepth;
int w,h;
stbi_uc *out;
@@ -5086,8 +5216,9 @@ static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int
w = stbi__get32be(s);
// Make sure the depth is 8 bits.
- if (stbi__get16be(s) != 8)
- return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 bit");
+ bitdepth = stbi__get16be(s);
+ if (bitdepth != 8 && bitdepth != 16)
+ return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 or 16 bit");
// Make sure the color mode is RGB.
// Valid options are:
@@ -5193,14 +5324,20 @@ static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int
stbi_uc *p;
p = out + channel;
- if (channel > channelCount) {
+ if (channel >= channelCount) {
// Fill this channel with default data.
+ stbi_uc val = channel == 3 ? 255 : 0;
for (i = 0; i < pixelCount; i++, p += 4)
- *p = channel == 3 ? 255 : 0;
+ *p = val;
} else {
// Read the data.
- for (i = 0; i < pixelCount; i++, p += 4)
- *p = stbi__get8(s);
+ if (bitdepth == 16) {
+ for (i = 0; i < pixelCount; i++, p += 4)
+ *p = (stbi_uc) (stbi__get16be(s) >> 8);
+ } else {
+ for (i = 0; i < pixelCount; i++, p += 4)
+ *p = stbi__get8(s);
+ }
}
}
}
@@ -5358,7 +5495,6 @@ static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *c
if (count >= 128) { // Repeated
stbi_uc value[4];
- int i;
if (count==128)
count = stbi__get16be(s);
@@ -5446,8 +5582,8 @@ typedef struct
typedef struct
{
int w,h;
- stbi_uc *out; // output buffer (always 4 components)
- int flags, bgindex, ratio, transparent, eflags;
+ stbi_uc *out, *old_out; // output buffer (always 4 components)
+ int flags, bgindex, ratio, transparent, eflags, delay;
stbi_uc pal[256][4];
stbi_uc lpal[256][4];
stbi__gif_lzw codes[4096];
@@ -5565,7 +5701,7 @@ static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code)
static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g)
{
stbi_uc lzw_cs;
- stbi__int32 len, code;
+ stbi__int32 len, init_code;
stbi__uint32 first;
stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear;
stbi__gif_lzw *p;
@@ -5578,10 +5714,10 @@ static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g)
codemask = (1 << codesize) - 1;
bits = 0;
valid_bits = 0;
- for (code = 0; code < clear; code++) {
- g->codes[code].prefix = -1;
- g->codes[code].first = (stbi_uc) code;
- g->codes[code].suffix = (stbi_uc) code;
+ for (init_code = 0; init_code < clear; init_code++) {
+ g->codes[init_code].prefix = -1;
+ g->codes[init_code].first = (stbi_uc) init_code;
+ g->codes[init_code].suffix = (stbi_uc) init_code;
}
// support no starting clear code
@@ -5642,17 +5778,18 @@ static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g)
}
}
-static void stbi__fill_gif_background(stbi__gif *g)
+static void stbi__fill_gif_background(stbi__gif *g, int x0, int y0, int x1, int y1)
{
- int i;
+ int x, y;
stbi_uc *c = g->pal[g->bgindex];
- // @OPTIMIZE: write a dword at a time
- for (i = 0; i < g->w * g->h * 4; i += 4) {
- stbi_uc *p = &g->out[i];
- p[0] = c[2];
- p[1] = c[1];
- p[2] = c[0];
- p[3] = c[3];
+ for (y = y0; y < y1; y += 4 * g->w) {
+ for (x = x0; x < x1; x += 4) {
+ stbi_uc *p = &g->out[y + x];
+ p[0] = c[2];
+ p[1] = c[1];
+ p[2] = c[0];
+ p[3] = 0;
+ }
}
}
@@ -5660,27 +5797,40 @@ static void stbi__fill_gif_background(stbi__gif *g)
static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp)
{
int i;
- stbi_uc *old_out = 0;
+ stbi_uc *prev_out = 0;
- if (g->out == 0) {
- if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header
- g->out = (stbi_uc *) stbi__malloc(4 * g->w * g->h);
- if (g->out == 0) return stbi__errpuc("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 = (stbi_uc *) stbi__malloc(4 * g->w * g->h);
- if (g->out == 0) return stbi__errpuc("outofmem", "Out of memory");
- memcpy(g->out, old_out, g->w*g->h*4);
- }
+ if (g->out == 0 && !stbi__gif_header(s, g, comp,0))
+ return 0; // stbi__g_failure_reason set by stbi__gif_header
+
+ prev_out = g->out;
+ g->out = (stbi_uc *) stbi__malloc(4 * g->w * g->h);
+ if (g->out == 0) return stbi__errpuc("outofmem", "Out of memory");
+
+ switch ((g->eflags & 0x1C) >> 2) {
+ case 0: // unspecified (also always used on 1st frame)
+ stbi__fill_gif_background(g, 0, 0, 4 * g->w, 4 * g->w * g->h);
+ break;
+ case 1: // do not dispose
+ if (prev_out) memcpy(g->out, prev_out, 4 * g->w * g->h);
+ g->old_out = prev_out;
+ break;
+ case 2: // dispose to background
+ if (prev_out) memcpy(g->out, prev_out, 4 * g->w * g->h);
+ stbi__fill_gif_background(g, g->start_x, g->start_y, g->max_x, g->max_y);
+ break;
+ case 3: // dispose to previous
+ if (g->old_out) {
+ for (i = g->start_y; i < g->max_y; i += 4 * g->w)
+ memcpy(&g->out[i + g->start_x], &g->old_out[i + g->start_x], g->max_x - g->start_x);
+ }
+ break;
}
for (;;) {
switch (stbi__get8(s)) {
case 0x2C: /* Image Descriptor */
{
+ int prev_trans = -1;
stbi__int32 x, y, w, h;
stbi_uc *o;
@@ -5713,10 +5863,10 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i
stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1);
g->color_table = (stbi_uc *) g->lpal;
} else if (g->flags & 0x80) {
- for (i=0; i < 256; ++i) // @OPTIMIZE: stbi__jpeg_reset only the previous transparent
- g->pal[i][3] = 255;
- if (g->transparent >= 0 && (g->eflags & 0x01))
+ if (g->transparent >= 0 && (g->eflags & 0x01)) {
+ prev_trans = g->pal[g->transparent][3];
g->pal[g->transparent][3] = 0;
+ }
g->color_table = (stbi_uc *) g->pal;
} else
return stbi__errpuc("missing color table", "Corrupt GIF");
@@ -5724,8 +5874,9 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i
o = stbi__process_gif_raster(s, g);
if (o == NULL) return NULL;
- if (req_comp && req_comp != 4)
- o = stbi__convert_format(o, 4, req_comp, g->w, g->h);
+ if (prev_trans != -1)
+ g->pal[g->transparent][3] = (stbi_uc) prev_trans;
+
return o;
}
@@ -5736,7 +5887,7 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i
len = stbi__get8(s);
if (len == 4) {
g->eflags = stbi__get8(s);
- stbi__get16le(s); // delay
+ g->delay = stbi__get16le(s);
g->transparent = stbi__get8(s);
} else {
stbi__skip(s, len);
@@ -5755,6 +5906,8 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i
return stbi__errpuc("unknown code", "Corrupt GIF");
}
}
+
+ STBI_NOTUSED(req_comp);
}
static stbi_uc *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp)
@@ -5768,7 +5921,11 @@ static stbi_uc *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int
if (u) {
*x = g.w;
*y = g.h;
+ if (req_comp && req_comp != 4)
+ u = stbi__convert_format(u, 4, req_comp, g.w, g.h);
}
+ else if (g.out)
+ STBI_FREE(g.out);
return u;
}
@@ -5967,7 +6124,7 @@ static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp)
char *token;
int valid = 0;
- if (strcmp(stbi__hdr_gettoken(s,buffer), "#?RADIANCE") != 0) {
+ if (stbi__hdr_test(s) == 0) {
stbi__rewind( s );
return 0;
}
@@ -6004,29 +6161,17 @@ static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp)
#ifndef STBI_NO_BMP
static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp)
{
- int hsz;
- if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') {
- stbi__rewind( s );
- return 0;
- }
- stbi__skip(s,12);
- hsz = stbi__get32le(s);
- if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) {
- stbi__rewind( s );
- return 0;
- }
- if (hsz == 12) {
- *x = stbi__get16le(s);
- *y = stbi__get16le(s);
- } else {
- *x = stbi__get32le(s);
- *y = stbi__get32le(s);
- }
- if (stbi__get16le(s) != 1) {
- stbi__rewind( s );
- return 0;
- }
- *comp = stbi__get16le(s) / 8;
+ void *p;
+ stbi__bmp_data info;
+
+ info.all_a = 255;
+ p = stbi__bmp_parse_header(s, &info);
+ stbi__rewind( s );
+ if (p == NULL)
+ return 0;
+ *x = s->img_x;
+ *y = s->img_y;
+ *comp = info.ma ? 4 : 3;
return 1;
}
#endif
@@ -6070,14 +6215,22 @@ static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp)
int act_comp=0,num_packets=0,chained;
stbi__pic_packet packets[10];
- stbi__skip(s, 92);
+ if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) {
+ stbi__rewind(s);
+ return 0;
+ }
+
+ stbi__skip(s, 88);
*x = stbi__get16be(s);
*y = stbi__get16be(s);
- if (stbi__at_eof(s)) return 0;
+ if (stbi__at_eof(s)) {
+ stbi__rewind( s);
+ return 0;
+ }
if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) {
- stbi__rewind( s );
- return 0;
+ stbi__rewind( s );
+ return 0;
}
stbi__skip(s, 8);
@@ -6164,8 +6317,16 @@ static int stbi__pnm_isspace(char c)
static void stbi__pnm_skip_whitespace(stbi__context *s, char *c)
{
- while (!stbi__at_eof(s) && stbi__pnm_isspace(*c))
- *c = (char) stbi__get8(s);
+ for (;;) {
+ while (!stbi__at_eof(s) && stbi__pnm_isspace(*c))
+ *c = (char) stbi__get8(s);
+
+ if (stbi__at_eof(s) || *c != '#')
+ break;
+
+ while (!stbi__at_eof(s) && *c != '\n' && *c != '\r' )
+ *c = (char) stbi__get8(s);
+ }
}
static int stbi__pnm_isdigit(char c)
@@ -6303,6 +6464,18 @@ STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int
/*
revision history:
+ 2.09 (2016-01-16) allow comments in PNM files
+ 16-bit-per-pixel TGA (not bit-per-component)
+ info() for TGA could break due to .hdr handling
+ info() for BMP to shares code instead of sloppy parse
+ can use STBI_REALLOC_SIZED if allocator doesn't support realloc
+ code cleanup
+ 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA
+ 2.07 (2015-09-13) fix compiler warnings
+ partial animated GIF support
+ limited 16-bpc PSD support
+ #ifdef unused functions
+ bug with < 92 byte PIC,PNM,HDR,TGA
2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value
2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning
2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit
diff --git a/src/stb_image_write.h b/src/stb_image_write.h
index 10489707..98fa4103 100644
--- a/src/stb_image_write.h
+++ b/src/stb_image_write.h
@@ -1,7 +1,6 @@
-/* stb_image_write - v0.98 - public domain - http://nothings.org/stb/stb_image_write.h
- writes out PNG/BMP/TGA images to C stdio - Sean Barrett 2010
- no warranty implied; use at your own risk
-
+/* stb_image_write - v1.01 - public domain - http://nothings.org/stb/stb_image_write.h
+ writes out PNG/BMP/TGA images to C stdio - Sean Barrett 2010-2015
+ no warranty implied; use at your own risk
Before #including,
@@ -18,7 +17,7 @@ ABOUT:
The PNG output is not optimal; it is 20-50% larger than the file
written by a decent optimizing implementation. This library is designed
- for source code compactness and simplicitly, not optimal image file size
+ for source code compactness and simplicity, not optimal image file size
or run-time performance.
BUILDING:
@@ -35,7 +34,22 @@ USAGE:
int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes);
int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data);
int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data);
- int stbi_write_hdr(char const *filename, int w, int h, int comp, const void *data);
+ int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data);
+
+ There are also four equivalent functions that use an arbitrary write function. You are
+ expected to open/close your file-equivalent before and after calling these:
+
+ int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes);
+ int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data);
+ int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data);
+ int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data);
+
+ where the callback is:
+ void stbi_write_func(void *context, void *data, int size);
+
+ You can define STBI_WRITE_NO_STDIO to disable the file variant of these
+ functions, so the library will not use stdio.h at all. However, this will
+ also disable HDR writing, because it requires stdio for formatted output.
Each function returns 0 on failure and non-0 on success.
@@ -63,6 +77,9 @@ USAGE:
data, alpha (if provided) is discarded, and for monochrome data it is
replicated across all three channels.
+ TGA supports RLE or non-RLE compressed data. To use non-RLE-compressed
+ data, set the global variable 'stbi_write_tga_with_rle' to 0.
+
CREDITS:
PNG/BMP/TGA
@@ -73,8 +90,25 @@ CREDITS:
Jean-Sebastien Guay
misc enhancements:
Tim Kelsey
+ TGA RLE
+ Alan Hickman
+ initial file IO callback implementation
+ Emmanuel Julien
bugfixes:
github:Chribba
+ Guillaume Chereau
+ github:jry2
+ github:romigrou
+ Sergio Gonzalez
+ Jonas Karlsson
+ Filip Wasil
+
+LICENSE
+
+This software is in the public domain. Where that dedication is not
+recognized, you are granted a perpetual, irrevocable license to copy,
+distribute, and modify this file as you see fit.
+
*/
#ifndef INCLUDE_STB_IMAGE_WRITE_H
@@ -84,10 +118,26 @@ CREDITS:
extern "C" {
#endif
-extern int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes);
-extern int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data);
-extern int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data);
-extern int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data);
+#ifdef STB_IMAGE_WRITE_STATIC
+#define STBIWDEF static
+#else
+#define STBIWDEF extern
+extern int stbi_write_tga_with_rle;
+#endif
+
+#ifndef STBI_WRITE_NO_STDIO
+STBIWDEF int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes);
+STBIWDEF int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data);
+STBIWDEF int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data);
+STBIWDEF int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data);
+#endif
+
+typedef void stbi_write_func(void *context, void *data, int size);
+
+STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes);
+STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data);
+STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data);
+STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data);
#ifdef __cplusplus
}
@@ -97,25 +147,43 @@ extern int stbi_write_hdr(char const *filename, int w, int h, int comp, const fl
#ifdef STB_IMAGE_WRITE_IMPLEMENTATION
+#ifdef _WIN32
+ #ifndef _CRT_SECURE_NO_WARNINGS
+ #define _CRT_SECURE_NO_WARNINGS
+ #endif
+ #ifndef _CRT_NONSTDC_NO_DEPRECATE
+ #define _CRT_NONSTDC_NO_DEPRECATE
+ #endif
+#endif
+
+#ifndef STBI_WRITE_NO_STDIO
+#include <stdio.h>
+#endif // STBI_WRITE_NO_STDIO
+
#include <stdarg.h>
#include <stdlib.h>
-#include <stdio.h>
#include <string.h>
#include <math.h>
-#if defined(STBIW_MALLOC) && defined(STBIW_FREE) && defined(STBIW_REALLOC)
+#if defined(STBIW_MALLOC) && defined(STBIW_FREE) && (defined(STBIW_REALLOC) || defined(STBIW_REALLOC_SIZED))
// ok
-#elif !defined(STBIW_MALLOC) && !defined(STBIW_FREE) && !defined(STBIW_REALLOC)
+#elif !defined(STBIW_MALLOC) && !defined(STBIW_FREE) && !defined(STBIW_REALLOC) && !defined(STBIW_REALLOC_SIZED)
// ok
#else
-#error "Must define all or none of STBIW_MALLOC, STBIW_FREE, and STBIW_REALLOC."
+#error "Must define all or none of STBIW_MALLOC, STBIW_FREE, and STBIW_REALLOC (or STBIW_REALLOC_SIZED)."
#endif
#ifndef STBIW_MALLOC
-#define STBIW_MALLOC(sz) malloc(sz)
-#define STBIW_REALLOC(p,sz) realloc(p,sz)
-#define STBIW_FREE(p) free(p)
+#define STBIW_MALLOC(sz) malloc(sz)
+#define STBIW_REALLOC(p,newsz) realloc(p,newsz)
+#define STBIW_FREE(p) free(p)
#endif
+
+#ifndef STBIW_REALLOC_SIZED
+#define STBIW_REALLOC_SIZED(p,oldsz,newsz) STBIW_REALLOC(p,newsz)
+#endif
+
+
#ifndef STBIW_MEMMOVE
#define STBIW_MEMMOVE(a,b,sz) memmove(a,b,sz)
#endif
@@ -126,22 +194,73 @@ extern int stbi_write_hdr(char const *filename, int w, int h, int comp, const fl
#define STBIW_ASSERT(x) assert(x)
#endif
+#define STBIW_UCHAR(x) (unsigned char) ((x) & 0xff)
+
+typedef struct
+{
+ stbi_write_func *func;
+ void *context;
+} stbi__write_context;
+
+// initialize a callback-based context
+static void stbi__start_write_callbacks(stbi__write_context *s, stbi_write_func *c, void *context)
+{
+ s->func = c;
+ s->context = context;
+}
+
+#ifndef STBI_WRITE_NO_STDIO
+
+static void stbi__stdio_write(void *context, void *data, int size)
+{
+ fwrite(data,1,size,(FILE*) context);
+}
+
+static int stbi__start_write_file(stbi__write_context *s, const char *filename)
+{
+ FILE *f = fopen(filename, "wb");
+ stbi__start_write_callbacks(s, stbi__stdio_write, (void *) f);
+ return f != NULL;
+}
+
+static void stbi__end_write_file(stbi__write_context *s)
+{
+ fclose((FILE *)s->context);
+}
+
+#endif // !STBI_WRITE_NO_STDIO
+
typedef unsigned int stbiw_uint32;
typedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1];
-static void writefv(FILE *f, const char *fmt, va_list v)
+#ifdef STB_IMAGE_WRITE_STATIC
+static int stbi_write_tga_with_rle = 1;
+#else
+int stbi_write_tga_with_rle = 1;
+#endif
+
+static void stbiw__writefv(stbi__write_context *s, const char *fmt, va_list v)
{
while (*fmt) {
switch (*fmt++) {
case ' ': break;
- case '1': { unsigned char x = (unsigned char) va_arg(v, int); fputc(x,f); break; }
- case '2': { int x = va_arg(v,int); unsigned char b[2];
- b[0] = (unsigned char) x; b[1] = (unsigned char) (x>>8);
- fwrite(b,2,1,f); break; }
- case '4': { stbiw_uint32 x = va_arg(v,int); unsigned char b[4];
- b[0]=(unsigned char)x; b[1]=(unsigned char)(x>>8);
- b[2]=(unsigned char)(x>>16); b[3]=(unsigned char)(x>>24);
- fwrite(b,4,1,f); break; }
+ case '1': { unsigned char x = STBIW_UCHAR(va_arg(v, int));
+ s->func(s->context,&x,1);
+ break; }
+ case '2': { int x = va_arg(v,int);
+ unsigned char b[2];
+ b[0] = STBIW_UCHAR(x);
+ b[1] = STBIW_UCHAR(x>>8);
+ s->func(s->context,b,2);
+ break; }
+ case '4': { stbiw_uint32 x = va_arg(v,int);
+ unsigned char b[4];
+ b[0]=STBIW_UCHAR(x);
+ b[1]=STBIW_UCHAR(x>>8);
+ b[2]=STBIW_UCHAR(x>>16);
+ b[3]=STBIW_UCHAR(x>>24);
+ s->func(s->context,b,4);
+ break; }
default:
STBIW_ASSERT(0);
return;
@@ -149,18 +268,60 @@ static void writefv(FILE *f, const char *fmt, va_list v)
}
}
-static void write3(FILE *f, unsigned char a, unsigned char b, unsigned char c)
+static void stbiw__writef(stbi__write_context *s, const char *fmt, ...)
+{
+ va_list v;
+ va_start(v, fmt);
+ stbiw__writefv(s, fmt, v);
+ va_end(v);
+}
+
+static void stbiw__write3(stbi__write_context *s, unsigned char a, unsigned char b, unsigned char c)
{
unsigned char arr[3];
arr[0] = a, arr[1] = b, arr[2] = c;
- fwrite(arr, 3, 1, f);
+ s->func(s->context, arr, 3);
}
-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, int expand_mono)
+static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, int write_alpha, int expand_mono, unsigned char *d)
{
unsigned char bg[3] = { 255, 0, 255}, px[3];
+ int k;
+
+ if (write_alpha < 0)
+ s->func(s->context, &d[comp - 1], 1);
+
+ switch (comp) {
+ case 1:
+ s->func(s->context,d,1);
+ break;
+ case 2:
+ if (expand_mono)
+ stbiw__write3(s, d[0], d[0], d[0]); // monochrome bmp
+ else
+ s->func(s->context, d, 1); // monochrome TGA
+ break;
+ case 4:
+ if (!write_alpha) {
+ // composite against pink background
+ for (k = 0; k < 3; ++k)
+ px[k] = bg[k] + ((d[k] - bg[k]) * d[3]) / 255;
+ stbiw__write3(s, px[1 - rgb_dir], px[1], px[1 + rgb_dir]);
+ break;
+ }
+ /* FALLTHROUGH */
+ case 3:
+ stbiw__write3(s, d[1 - rgb_dir], d[1], d[1 + rgb_dir]);
+ break;
+ }
+ if (write_alpha > 0)
+ s->func(s->context, &d[comp - 1], 1);
+}
+
+static void stbiw__write_pixels(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad, int expand_mono)
+{
stbiw_uint32 zero = 0;
- int i,j,k, j_end;
+ int i,j, j_end;
if (y <= 0)
return;
@@ -173,73 +334,148 @@ static void write_pixels(FILE *f, int rgb_dir, int vdir, int x, int y, int comp,
for (; j != j_end; j += vdir) {
for (i=0; i < x; ++i) {
unsigned char *d = (unsigned char *) data + (j*x+i)*comp;
- if (write_alpha < 0)
- fwrite(&d[comp-1], 1, 1, f);
- switch (comp) {
- case 1: fwrite(d, 1, 1, f);
- break;
- case 2: if (expand_mono)
- write3(f, d[0],d[0],d[0]); // monochrome bmp
- else
- fwrite(d, 1, 1, f); // monochrome TGA
- break;
- case 4:
- if (!write_alpha) {
- // composite against pink background
- for (k=0; k < 3; ++k)
- px[k] = bg[k] + ((d[k] - bg[k]) * d[3])/255;
- write3(f, px[1-rgb_dir],px[1],px[1+rgb_dir]);
- break;
- }
- /* FALLTHROUGH */
- case 3:
- write3(f, d[1-rgb_dir],d[1],d[1+rgb_dir]);
- break;
- }
- if (write_alpha > 0)
- fwrite(&d[comp-1], 1, 1, f);
+ stbiw__write_pixel(s, rgb_dir, comp, write_alpha, expand_mono, d);
}
- fwrite(&zero,scanline_pad,1,f);
+ s->func(s->context, &zero, scanline_pad);
}
}
-static int outfile(char const *filename, int rgb_dir, int vdir, int x, int y, int comp, int expand_mono, void *data, int alpha, int pad, const char *fmt, ...)
+static int stbiw__outfile(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, int expand_mono, void *data, int alpha, int pad, const char *fmt, ...)
{
- FILE *f;
- if (y < 0 || x < 0) return 0;
- f = fopen(filename, "wb");
- if (f) {
+ if (y < 0 || x < 0) {
+ return 0;
+ } else {
va_list v;
va_start(v, fmt);
- writefv(f, fmt, v);
+ stbiw__writefv(s, fmt, v);
va_end(v);
- write_pixels(f,rgb_dir,vdir,x,y,comp,data,alpha,pad,expand_mono);
- fclose(f);
+ stbiw__write_pixels(s,rgb_dir,vdir,x,y,comp,data,alpha,pad, expand_mono);
+ return 1;
}
- return f != NULL;
}
-int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data)
+static int stbi_write_bmp_core(stbi__write_context *s, int x, int y, int comp, const void *data)
{
int pad = (-x*3) & 3;
- return outfile(filename,-1,-1,x,y,comp,1,(void *) data,0,pad,
+ return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *) 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
}
-int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data)
+STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data)
+{
+ stbi__write_context s;
+ stbi__start_write_callbacks(&s, func, context);
+ return stbi_write_bmp_core(&s, x, y, comp, data);
+}
+
+#ifndef STBI_WRITE_NO_STDIO
+STBIWDEF int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data)
+{
+ stbi__write_context s;
+ if (stbi__start_write_file(&s,filename)) {
+ int r = stbi_write_bmp_core(&s, x, y, comp, data);
+ stbi__end_write_file(&s);
+ return r;
+ } else
+ return 0;
+}
+#endif //!STBI_WRITE_NO_STDIO
+
+static int stbi_write_tga_core(stbi__write_context *s, int x, int y, int comp, void *data)
{
int has_alpha = (comp == 2 || comp == 4);
int colorbytes = has_alpha ? comp-1 : comp;
int format = colorbytes < 2 ? 3 : 2; // 3 color channels (RGB/RGBA) = 2, 1 color channel (Y/YA) = 3
- return outfile(filename, -1,-1, x, y, comp, 0, (void *) data, has_alpha, 0,
- "111 221 2222 11", 0,0,format, 0,0,0, 0,0,x,y, (colorbytes+has_alpha)*8, has_alpha*8);
+
+ if (y < 0 || x < 0)
+ return 0;
+
+ if (!stbi_write_tga_with_rle) {
+ return stbiw__outfile(s, -1, -1, x, y, comp, 0, (void *) data, has_alpha, 0,
+ "111 221 2222 11", 0, 0, format, 0, 0, 0, 0, 0, x, y, (colorbytes + has_alpha) * 8, has_alpha * 8);
+ } else {
+ int i,j,k;
+
+ stbiw__writef(s, "111 221 2222 11", 0,0,format+8, 0,0,0, 0,0,x,y, (colorbytes + has_alpha) * 8, has_alpha * 8);
+
+ for (j = y - 1; j >= 0; --j) {
+ unsigned char *row = (unsigned char *) data + j * x * comp;
+ int len;
+
+ for (i = 0; i < x; i += len) {
+ unsigned char *begin = row + i * comp;
+ int diff = 1;
+ len = 1;
+
+ if (i < x - 1) {
+ ++len;
+ diff = memcmp(begin, row + (i + 1) * comp, comp);
+ if (diff) {
+ const unsigned char *prev = begin;
+ for (k = i + 2; k < x && len < 128; ++k) {
+ if (memcmp(prev, row + k * comp, comp)) {
+ prev += comp;
+ ++len;
+ } else {
+ --len;
+ break;
+ }
+ }
+ } else {
+ for (k = i + 2; k < x && len < 128; ++k) {
+ if (!memcmp(begin, row + k * comp, comp)) {
+ ++len;
+ } else {
+ break;
+ }
+ }
+ }
+ }
+
+ if (diff) {
+ unsigned char header = STBIW_UCHAR(len - 1);
+ s->func(s->context, &header, 1);
+ for (k = 0; k < len; ++k) {
+ stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin + k * comp);
+ }
+ } else {
+ unsigned char header = STBIW_UCHAR(len - 129);
+ s->func(s->context, &header, 1);
+ stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin);
+ }
+ }
+ }
+ }
+ return 1;
+}
+
+int stbi_write_tga_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data)
+{
+ stbi__write_context s;
+ stbi__start_write_callbacks(&s, func, context);
+ return stbi_write_tga_core(&s, x, y, comp, (void *) data);
}
+#ifndef STBI_WRITE_NO_STDIO
+int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data)
+{
+ stbi__write_context s;
+ if (stbi__start_write_file(&s,filename)) {
+ int r = stbi_write_tga_core(&s, x, y, comp, (void *) data);
+ stbi__end_write_file(&s);
+ return r;
+ } else
+ return 0;
+}
+#endif
+
// *************************************************************************************************
// Radiance RGBE HDR writer
// by Baldur Karlsson
+#ifndef STBI_WRITE_NO_STDIO
+
#define stbiw__max(a, b) ((a) > (b) ? (a) : (b))
void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear)
@@ -247,7 +483,7 @@ void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear)
int exponent;
float maxcomp = stbiw__max(linear[0], stbiw__max(linear[1], linear[2]));
- if (maxcomp < 1e-32) {
+ if (maxcomp < 1e-32f) {
rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0;
} else {
float normalize = (float) frexp(maxcomp, &exponent) * 256.0f/maxcomp;
@@ -259,23 +495,23 @@ void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear)
}
}
-void stbiw__write_run_data(FILE *f, int length, unsigned char databyte)
+void stbiw__write_run_data(stbi__write_context *s, int length, unsigned char databyte)
{
- unsigned char lengthbyte = (unsigned char) (length+128);
+ unsigned char lengthbyte = STBIW_UCHAR(length+128);
STBIW_ASSERT(length+128 <= 255);
- fwrite(&lengthbyte, 1, 1, f);
- fwrite(&databyte, 1, 1, f);
+ s->func(s->context, &lengthbyte, 1);
+ s->func(s->context, &databyte, 1);
}
-void stbiw__write_dump_data(FILE *f, int length, unsigned char *data)
+void stbiw__write_dump_data(stbi__write_context *s, int length, unsigned char *data)
{
- unsigned char lengthbyte = (unsigned char )(length & 0xff);
+ unsigned char lengthbyte = STBIW_UCHAR(length);
STBIW_ASSERT(length <= 128); // inconsistent with spec but consistent with official code
- fwrite(&lengthbyte, 1, 1, f);
- fwrite(data, length, 1, f);
+ s->func(s->context, &lengthbyte, 1);
+ s->func(s->context, data, length);
}
-void stbiw__write_hdr_scanline(FILE *f, int width, int comp, unsigned char *scratch, const float *scanline)
+void stbiw__write_hdr_scanline(stbi__write_context *s, int width, int ncomp, unsigned char *scratch, float *scanline)
{
unsigned char scanlineheader[4] = { 2, 2, 0, 0 };
unsigned char rgbe[4];
@@ -288,31 +524,31 @@ void stbiw__write_hdr_scanline(FILE *f, int width, int comp, unsigned char *scra
/* skip RLE for images too small or large */
if (width < 8 || width >= 32768) {
for (x=0; x < width; x++) {
- switch (comp) {
+ switch (ncomp) {
case 4: /* fallthrough */
- case 3: linear[2] = scanline[x*comp + 2];
- linear[1] = scanline[x*comp + 1];
- linear[0] = scanline[x*comp + 0];
+ case 3: linear[2] = scanline[x*ncomp + 2];
+ linear[1] = scanline[x*ncomp + 1];
+ linear[0] = scanline[x*ncomp + 0];
break;
- case 2: /* fallthrough */
- case 1: linear[0] = linear[1] = linear[2] = scanline[x*comp + 0];
+ default:
+ linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0];
break;
}
stbiw__linear_to_rgbe(rgbe, linear);
- fwrite(rgbe, 4, 1, f);
+ s->func(s->context, rgbe, 4);
}
} else {
int c,r;
/* encode into scratch buffer */
for (x=0; x < width; x++) {
- switch(comp) {
+ switch(ncomp) {
case 4: /* fallthrough */
- case 3: linear[2] = scanline[x*comp + 2];
- linear[1] = scanline[x*comp + 1];
- linear[0] = scanline[x*comp + 0];
+ case 3: linear[2] = scanline[x*ncomp + 2];
+ linear[1] = scanline[x*ncomp + 1];
+ linear[0] = scanline[x*ncomp + 0];
break;
- case 2: /* fallthrough */
- case 1: linear[0] = linear[1] = linear[2] = scanline[x*comp + 0];
+ default:
+ linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0];
break;
}
stbiw__linear_to_rgbe(rgbe, linear);
@@ -322,7 +558,7 @@ void stbiw__write_hdr_scanline(FILE *f, int width, int comp, unsigned char *scra
scratch[x + width*3] = rgbe[3];
}
- fwrite(scanlineheader, 4, 1, f);
+ s->func(s->context, scanlineheader, 4);
/* RLE each component separately */
for (c=0; c < 4; c++) {
@@ -343,7 +579,7 @@ void stbiw__write_hdr_scanline(FILE *f, int width, int comp, unsigned char *scra
while (x < r) {
int len = r-x;
if (len > 128) len = 128;
- stbiw__write_dump_data(f, len, &comp[x]);
+ stbiw__write_dump_data(s, len, &comp[x]);
x += len;
}
// if there's a run, output it
@@ -355,7 +591,7 @@ void stbiw__write_hdr_scanline(FILE *f, int width, int comp, unsigned char *scra
while (x < r) {
int len = r-x;
if (len > 127) len = 127;
- stbiw__write_run_data(f, len, comp[x]);
+ stbiw__write_run_data(s, len, comp[x]);
x += len;
}
}
@@ -364,27 +600,52 @@ void stbiw__write_hdr_scanline(FILE *f, int width, int comp, unsigned char *scra
}
}
-int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data)
+static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, float *data)
{
- int i;
- FILE *f;
- if (y <= 0 || x <= 0 || data == NULL) return 0;
- f = fopen(filename, "wb");
- if (f) {
- /* Each component is stored separately. Allocate scratch space for full output scanline. */
+ if (y <= 0 || x <= 0 || data == NULL)
+ return 0;
+ else {
+ // Each component is stored separately. Allocate scratch space for full output scanline.
unsigned char *scratch = (unsigned char *) STBIW_MALLOC(x*4);
- fprintf(f, "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n" );
- fprintf(f, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n" , y, x);
+ int i, len;
+ char buffer[128];
+ char header[] = "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n";
+ s->func(s->context, header, sizeof(header)-1);
+
+ len = sprintf(buffer, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x);
+ s->func(s->context, buffer, len);
+
for(i=0; i < y; i++)
- stbiw__write_hdr_scanline(f, x, comp, scratch, data + comp*i*x);
+ stbiw__write_hdr_scanline(s, x, comp, scratch, data + comp*i*x);
STBIW_FREE(scratch);
- fclose(f);
+ return 1;
}
- return f != NULL;
}
-/////////////////////////////////////////////////////////
-// PNG
+int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const float *data)
+{
+ stbi__write_context s;
+ stbi__start_write_callbacks(&s, func, context);
+ return stbi_write_hdr_core(&s, x, y, comp, (float *) data);
+}
+
+int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data)
+{
+ stbi__write_context s;
+ if (stbi__start_write_file(&s,filename)) {
+ int r = stbi_write_hdr_core(&s, x, y, comp, (float *) data);
+ stbi__end_write_file(&s);
+ return r;
+ } else
+ return 0;
+}
+#endif // STBI_WRITE_NO_STDIO
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// PNG writer
+//
// stretchy buffer; stbiw__sbpush() == vector<>::push_back() -- stbiw__sbcount() == vector<>::size()
#define stbiw__sbraw(a) ((int *) (a) - 2)
@@ -402,7 +663,7 @@ int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *da
static void *stbiw__sbgrowf(void **arr, int increment, int itemsize)
{
int m = *arr ? 2*stbiw__sbm(*arr)+increment : increment+1;
- void *p = STBIW_REALLOC(*arr ? stbiw__sbraw(*arr) : 0, itemsize * m + sizeof(int)*2);
+ void *p = STBIW_REALLOC_SIZED(*arr ? stbiw__sbraw(*arr) : 0, *arr ? (stbiw__sbm(*arr)*itemsize + sizeof(int)*2) : 0, itemsize * m + sizeof(int)*2);
STBIW_ASSERT(p);
if (p) {
if (!*arr) ((int *) p)[1] = 0;
@@ -415,7 +676,7 @@ static void *stbiw__sbgrowf(void **arr, int increment, int itemsize)
static unsigned char *stbiw__zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount)
{
while (*bitcount >= 8) {
- stbiw__sbpush(data, (unsigned char) *bitbuffer);
+ stbiw__sbpush(data, STBIW_UCHAR(*bitbuffer));
*bitbuffer >>= 8;
*bitcount -= 8;
}
@@ -550,18 +811,19 @@ unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_l
{
// compute adler32 on input
- unsigned int i=0, s1=1, s2=0, blocklen = data_len % 5552;
- int j=0;
+ unsigned int s1=1, s2=0;
+ int blocklen = (int) (data_len % 5552);
+ j=0;
while (j < data_len) {
for (i=0; i < blocklen; ++i) s1 += data[j+i], s2 += s1;
s1 %= 65521, s2 %= 65521;
j += blocklen;
blocklen = 5552;
}
- stbiw__sbpush(out, (unsigned char) (s2 >> 8));
- stbiw__sbpush(out, (unsigned char) s2);
- stbiw__sbpush(out, (unsigned char) (s1 >> 8));
- stbiw__sbpush(out, (unsigned char) s1);
+ stbiw__sbpush(out, STBIW_UCHAR(s2 >> 8));
+ stbiw__sbpush(out, STBIW_UCHAR(s2));
+ stbiw__sbpush(out, STBIW_UCHAR(s1 >> 8));
+ stbiw__sbpush(out, STBIW_UCHAR(s1));
}
*out_len = stbiw__sbn(out);
// make returned pointer freeable
@@ -569,21 +831,52 @@ unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_l
return (unsigned char *) stbiw__sbraw(out);
}
-unsigned int stbiw__crc32(unsigned char *buffer, int len)
+static unsigned int stbiw__crc32(unsigned char *buffer, int len)
{
- static unsigned int crc_table[256];
+ static unsigned int crc_table[256] =
+ {
+ 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
+ 0x0eDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
+ 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
+ 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
+ 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
+ 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
+ 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
+ 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
+ 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
+ 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
+ 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
+ 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
+ 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
+ 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
+ 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
+ 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
+ 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
+ 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
+ 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
+ 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
+ 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
+ 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
+ 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
+ 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
+ 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
+ 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
+ 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
+ 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
+ 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
+ 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
+ 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
+ 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
+ };
+
unsigned int crc = ~0u;
- int i,j;
- if (crc_table[1] == 0)
- for(i=0; i < 256; i++)
- for (crc_table[i]=i, j=0; j < 8; ++j)
- crc_table[i] = (crc_table[i] >> 1) ^ (crc_table[i] & 1 ? 0xedb88320 : 0);
+ int i;
for (i=0; i < len; ++i)
crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)];
return ~crc;
}
-#define stbiw__wpng4(o,a,b,c,d) ((o)[0]=(unsigned char)(a),(o)[1]=(unsigned char)(b),(o)[2]=(unsigned char)(c),(o)[3]=(unsigned char)(d),(o)+=4)
+#define stbiw__wpng4(o,a,b,c,d) ((o)[0]=STBIW_UCHAR(a),(o)[1]=STBIW_UCHAR(b),(o)[2]=STBIW_UCHAR(c),(o)[3]=STBIW_UCHAR(d),(o)+=4)
#define stbiw__wp32(data,v) stbiw__wpng4(data, (v)>>24,(v)>>16,(v)>>8,(v));
#define stbiw__wptag(data,s) stbiw__wpng4(data, s[0],s[1],s[2],s[3])
@@ -596,9 +889,9 @@ static void stbiw__wpcrc(unsigned char **data, int len)
static unsigned char stbiw__paeth(int a, int b, int c)
{
int p = a + b - c, pa = abs(p-a), pb = abs(p-b), pc = abs(p-c);
- if (pa <= pb && pa <= pc) return (unsigned char) a;
- if (pb <= pc) return (unsigned char) b;
- return (unsigned char) c;
+ if (pa <= pb && pa <= pc) return STBIW_UCHAR(a);
+ if (pb <= pc) return STBIW_UCHAR(b);
+ return STBIW_UCHAR(c);
}
unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len)
@@ -671,7 +964,7 @@ unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, in
stbiw__wp32(o, x);
stbiw__wp32(o, y);
*o++ = 8;
- *o++ = (unsigned char) ctype[n];
+ *o++ = STBIW_UCHAR(ctype[n]);
*o++ = 0;
*o++ = 0;
*o++ = 0;
@@ -693,12 +986,13 @@ unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, in
return out;
}
-int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes)
+#ifndef STBI_WRITE_NO_STDIO
+STBIWDEF int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes)
{
FILE *f;
int len;
unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len);
- if (!png) return 0;
+ if (png == NULL) return 0;
f = fopen(filename, "wb");
if (!f) { STBIW_FREE(png); return 0; }
fwrite(png, 1, len, f);
@@ -706,9 +1000,29 @@ int stbi_write_png(char const *filename, int x, int y, int comp, const void *dat
STBIW_FREE(png);
return 1;
}
+#endif
+
+STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int stride_bytes)
+{
+ int len;
+ unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len);
+ if (png == NULL) return 0;
+ func(context, png, len);
+ STBIW_FREE(png);
+ return 1;
+}
+
#endif // STB_IMAGE_WRITE_IMPLEMENTATION
/* Revision history
+ 1.01 (2016-01-16)
+ STBIW_REALLOC_SIZED: support allocators with no realloc support
+ avoid race-condition in crc initialization
+ minor compile issues
+ 1.00 (2015-09-14)
+ installable file IO function
+ 0.99 (2015-09-13)
+ warning fixes; TGA rle support
0.98 (2015-04-08)
added STBIW_MALLOC, STBIW_ASSERT etc
0.97 (2015-01-18)
diff --git a/src/stb_truetype.h b/src/stb_truetype.h
index 00d22eb6..bfb1841f 100644
--- a/src/stb_truetype.h
+++ b/src/stb_truetype.h
@@ -1,4 +1,4 @@
-// stb_truetype.h - v1.08 - public domain
+// stb_truetype.h - v1.09 - public domain
// authored from 2009-2015 by Sean Barrett / RAD Game Tools
//
// This library processes TrueType files:
@@ -42,12 +42,15 @@
// Sergey Popov
// Giumo X. Clanjor
// Higor Euripedes
+// Thomas Fields
+// Derek Vinyard
//
// Misc other:
// Ryan Gordon
//
// VERSION HISTORY
//
+// 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use allocation userdata properly
// 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges
// 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints;
// variant PackFontRanges to pack and render in separate phases;
@@ -1556,7 +1559,7 @@ STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v)
STBTT_DEF 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;
+ int x0=0,y0=0,x1,y1; // =0 suppresses compiler warning
if (!stbtt_GetGlyphBox(font, glyph, &x0,&y0,&x1,&y1)) {
// e.g. space character
if (ix0) *ix0 = 0;
@@ -1672,6 +1675,7 @@ static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, i
{
stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata);
float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0);
+ STBTT_assert(z != NULL);
if (!z) return z;
// round dx down to avoid overshooting
@@ -1693,6 +1697,7 @@ static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, i
{
stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata);
float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0);
+ STBTT_assert(z != NULL);
//STBTT_assert(e->y0 <= start_point);
if (!z) return z;
z->fdx = dxdy;
@@ -1817,21 +1822,23 @@ static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e,
while (e->y0 <= scan_y) {
if (e->y1 > scan_y) {
stbtt__active_edge *z = stbtt__new_active(&hh, 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;
+ if (z != NULL) {
+ // 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;
@@ -2101,10 +2108,12 @@ static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e,
while (e->y0 <= scan_y_bottom) {
if (e->y0 != e->y1) {
stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y_top, userdata);
- STBTT_assert(z->ey >= scan_y_top);
- // insert at front
- z->next = active;
- active = z;
+ if (z != NULL) {
+ STBTT_assert(z->ey >= scan_y_top);
+ // insert at front
+ z->next = active;
+ active = z;
+ }
}
++e;
}
@@ -2513,6 +2522,7 @@ STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // fo
float scale;
int x,y,bottom_y, i;
stbtt_fontinfo f;
+ f.userdata = NULL;
if (!stbtt_InitFont(&f, data, offset))
return -1;
STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels
@@ -2706,6 +2716,7 @@ static void stbtt__h_prefilter(unsigned char *pixels, int w, int h, int stride_i
unsigned char buffer[STBTT_MAX_OVERSAMPLE];
int safe_w = w - kernel_width;
int j;
+ STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze
for (j=0; j < h; ++j) {
int i;
unsigned int total;
@@ -2767,6 +2778,7 @@ static void stbtt__v_prefilter(unsigned char *pixels, int w, int h, int stride_i
unsigned char buffer[STBTT_MAX_OVERSAMPLE];
int safe_h = h - kernel_width;
int j;
+ STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze
for (j=0; j < w; ++j) {
int i;
unsigned int total;
@@ -2975,6 +2987,7 @@ STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, unsigned char *fontd
if (rects == NULL)
return 0;
+ info.userdata = spc->user_allocator_context;
stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata,font_index));
n = stbtt_PackFontRangesGatherRects(spc, &info, ranges, num_ranges, rects);
@@ -3192,6 +3205,7 @@ STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *font_collection, const
// FULL VERSION HISTORY
//
+// 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use alloc userdata for PackFontRanges
// 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges
// 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints;
// allow PackFontRanges to pack and render in separate phases;
diff --git a/src/stb_vorbis.c b/src/stb_vorbis.c
index e2700157..0510edc7 100644
--- a/src/stb_vorbis.c
+++ b/src/stb_vorbis.c
@@ -107,14 +107,8 @@
// trade off storage for speed.
//#define STB_VORBIS_DIVIDES_IN_CODEBOOK
-// STB_VORBIS_CODEBOOK_SHORTS
-// The vorbis file format encodes VQ codebook floats as ax+b where a and
-// b are floating point per-codebook constants, and x is a 16-bit int.
-// Normally, stb_vorbis decodes them to floats rather than leaving them
-// as 16-bit ints and computing ax+b while decoding. This is a speed/space
-// tradeoff; you can save space by defining this flag.
-#ifndef STB_VORBIS_CODEBOOK_SHORTS
-#define STB_VORBIS_CODEBOOK_FLOATS
+#ifdef STB_VORBIS_CODEBOOK_SHORTS
+#error "STB_VORBIS_CODEBOOK_SHORTS is no longer supported as it produced incorrect results for some input formats"
#endif
// STB_VORBIS_DIVIDE_TABLE
@@ -175,11 +169,26 @@
#if !(defined(__APPLE__) || defined(MACOSX) || defined(macintosh) || defined(Macintosh))
#include <malloc.h>
#endif
-#else
+#else // STB_VORBIS_NO_CRT
#define NULL 0
-#endif
-
-#if !defined(_MSC_VER) && !(defined(__MINGW32__) && defined(__forceinline))
+#define malloc(s) 0
+#define free(s) ((void) 0)
+#define realloc(s) 0
+#endif // STB_VORBIS_NO_CRT
+
+#include <limits.h>
+
+#ifdef __MINGW32__
+ // eff you mingw:
+ // "fixed":
+ // http://sourceforge.net/p/mingw-w64/mailman/message/32882927/
+ // "no that broke the build, reverted, who cares about C":
+ // http://sourceforge.net/p/mingw-w64/mailman/message/32890381/
+ #ifdef __forceinline
+ #undef __forceinline
+ #endif
+ #define __forceinline
+#elif !defined(_MSC_VER)
#if __GNUC__
#define __forceinline inline
#else
@@ -196,6 +205,13 @@
#endif
+#if 0
+#include <crtdbg.h>
+#define CHECK(f) _CrtIsValidHeapPointer(f->channel_buffers[1])
+#else
+#define CHECK(f) ((void) 0)
+#endif
+
#define MAX_BLOCKSIZE_LOG 13 // from specification
#define MAX_BLOCKSIZE (1 << MAX_BLOCKSIZE_LOG)
@@ -212,11 +228,7 @@ typedef signed int int32;
#define FALSE 0
#endif
-#ifdef STB_VORBIS_CODEBOOK_FLOATS
typedef float codetype;
-#else
-typedef uint16 codetype;
-#endif
// @NOTE
//
@@ -336,8 +348,6 @@ typedef struct
typedef struct
{
uint32 page_start, page_end;
- uint32 after_previous_page_start;
- uint32 first_decoded_sample;
uint32 last_decoded_sample;
} ProbedPage;
@@ -452,13 +462,6 @@ struct stb_vorbis
int channel_buffer_end;
};
-extern int my_prof(int slot);
-//#define stb_prof my_prof
-
-#ifndef stb_prof
-#define stb_prof(x) ((void) 0)
-#endif
-
#if defined(STB_VORBIS_NO_PUSHDATA_API)
#define IS_PUSH_MODE(f) FALSE
#elif defined(STB_VORBIS_NO_PULLDATA_API)
@@ -525,7 +528,7 @@ static void *setup_malloc(vorb *f, int sz)
static void setup_free(vorb *f, void *p)
{
- if (f->alloc.alloc_buffer) return; // do nothing; setup mem is not a stack
+ if (f->alloc.alloc_buffer) return; // do nothing; setup mem is a stack
free(p);
}
@@ -557,7 +560,7 @@ static void crc32_init(void)
int i,j;
uint32 s;
for(i=0; i < 256; i++) {
- for (s=i<<24, j=0; j < 8; ++j)
+ for (s=(uint32) i << 24, j=0; j < 8; ++j)
s = (s << 1) ^ (s >= (1U<<31) ? CRC32_POLY : 0);
crc_table[i] = s;
}
@@ -658,7 +661,7 @@ static int compute_codewords(Codebook *c, uint8 *len, int n, uint32 *values)
add_entry(c, 0, k, m++, len[k], values);
// add all available leaves
for (i=1; i <= len[k]; ++i)
- available[i] = 1 << (32-i);
+ available[i] = 1U << (32-i);
// note that the above code treats the first case specially,
// but it's really the same as the following code, so they
// could probably be combined (except the initial code is 0,
@@ -674,12 +677,14 @@ static int compute_codewords(Codebook *c, uint8 *len, int n, uint32 *values)
// trivial to prove, but it seems true and the assert never
// fires, so!
while (z > 0 && !available[z]) --z;
- if (z == 0) { assert(0); return FALSE; }
+ if (z == 0) { return FALSE; }
res = available[z];
+ assert(z >= 0 && z < 32);
available[z] = 0;
add_entry(c, bit_reverse(res), i, m++, len[i], values);
// propogate availability up the tree
if (z != len[i]) {
+ assert(len[i] >= 0 && len[i] < 32);
for (y=len[i]; y > z; --y) {
assert(available[y] == 0);
available[y] = res + (1 << (32-y));
@@ -914,7 +919,7 @@ static uint32 get32(vorb *f)
x = get8(f);
x += get8(f) << 8;
x += get8(f) << 16;
- x += get8(f) << 24;
+ x += (uint32) get8(f) << 24;
return x;
}
@@ -1045,8 +1050,6 @@ static int start_page_no_capturepattern(vorb *f)
len += 27 + f->segment_count;
p.page_start = f->first_audio_page_offset;
p.page_end = p.page_start + len;
- p.after_previous_page_start = p.page_start;
- p.first_decoded_sample = 0;
p.last_decoded_sample = loc0;
f->p_first = p;
}
@@ -1189,7 +1192,7 @@ static __forceinline void prep_huffman(vorb *f)
if (f->last_seg && !f->bytes_in_seg) return;
z = get8_packet_raw(f);
if (z == EOP) return;
- f->acc += z << f->valid_bits;
+ f->acc += (unsigned) z << f->valid_bits;
f->valid_bits += 8;
} while (f->valid_bits <= 24);
}
@@ -1199,7 +1202,7 @@ enum
{
VORBIS_packet_id = 1,
VORBIS_packet_comment = 3,
- VORBIS_packet_setup = 5,
+ VORBIS_packet_setup = 5
};
static int codebook_decode_scalar_raw(vorb *f, Codebook *c)
@@ -1207,7 +1210,9 @@ static int codebook_decode_scalar_raw(vorb *f, Codebook *c)
int i;
prep_huffman(f);
- assert(c->sorted_codewords || c->codewords);
+ if (c->codewords == NULL && c->sorted_codewords == NULL)
+ return -1;
+
// cases to use binary search: sorted_codewords && !c->codewords
// sorted_codewords && c->entries > 8
if (c->entries > 8 ? c->sorted_codewords!=NULL : !c->codewords) {
@@ -1315,15 +1320,9 @@ static int codebook_decode_scalar(vorb *f, Codebook *c)
// CODEBOOK_ELEMENT_FAST is an optimization for the CODEBOOK_FLOATS case
// where we avoid one addition
-#ifndef STB_VORBIS_CODEBOOK_FLOATS
- #define CODEBOOK_ELEMENT(c,off) (c->multiplicands[off] * c->delta_value + c->minimum_value)
- #define CODEBOOK_ELEMENT_FAST(c,off) (c->multiplicands[off] * c->delta_value)
- #define CODEBOOK_ELEMENT_BASE(c) (c->minimum_value)
-#else
- #define CODEBOOK_ELEMENT(c,off) (c->multiplicands[off])
- #define CODEBOOK_ELEMENT_FAST(c,off) (c->multiplicands[off])
- #define CODEBOOK_ELEMENT_BASE(c) (0)
-#endif
+#define CODEBOOK_ELEMENT(c,off) (c->multiplicands[off])
+#define CODEBOOK_ELEMENT_FAST(c,off) (c->multiplicands[off])
+#define CODEBOOK_ELEMENT_BASE(c) (0)
static int codebook_decode_start(vorb *f, Codebook *c)
{
@@ -1515,7 +1514,6 @@ static int codebook_decode_deinterleave_repeat_2(vorb *f, Codebook *c, float **o
{
z *= c->dimensions;
- stb_prof(11);
if (c->sequence_p) {
// haven't optimized this case because I don't have any examples
for (i=0; i < effective; ++i) {
@@ -1527,7 +1525,7 @@ static int codebook_decode_deinterleave_repeat_2(vorb *f, Codebook *c, float **o
}
} else {
i=0;
- if (c_inter == 1) {
+ if (c_inter == 1 && i < effective) {
float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last;
if (outputs[c_inter])
outputs[c_inter][p_inter] += val;
@@ -1699,15 +1697,17 @@ static __forceinline void draw_line(float *output, int x0, int y0, int x1, int y
#endif
ady -= abs(base) * adx;
if (x1 > n) x1 = n;
- LINE_OP(output[x], inverse_db_table[y]);
- for (++x; x < x1; ++x) {
- err += ady;
- if (err >= adx) {
- err -= adx;
- y += sy;
- } else
- y += base;
+ if (x < x1) {
LINE_OP(output[x], inverse_db_table[y]);
+ for (++x; x < x1; ++x) {
+ err += ady;
+ if (err >= adx) {
+ err -= adx;
+ y += sy;
+ } else
+ y += base;
+ LINE_OP(output[x], inverse_db_table[y]);
+ }
}
}
@@ -1746,7 +1746,8 @@ static void decode_residue(vorb *f, float *residue_buffers[], int ch, int n, int
int **classifications = (int **) temp_block_array(f,f->channels, part_read * sizeof(**classifications));
#endif
- stb_prof(2);
+ CHECK(f);
+
for (i=0; i < ch; ++i)
if (!do_not_decode[i])
memset(residue_buffers[i], 0, sizeof(float) * n);
@@ -1758,11 +1759,9 @@ static void decode_residue(vorb *f, float *residue_buffers[], int ch, int n, int
if (j == ch)
goto done;
- stb_prof(3);
for (pass=0; pass < 8; ++pass) {
int pcount = 0, class_set = 0;
if (ch == 2) {
- stb_prof(13);
while (pcount < part_read) {
int z = r->begin + pcount*r->part_size;
int c_inter = (z & 1), p_inter = z>>1;
@@ -1780,7 +1779,6 @@ static void decode_residue(vorb *f, float *residue_buffers[], int ch, int n, int
}
#endif
}
- stb_prof(5);
for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) {
int z = r->begin + pcount*r->part_size;
#ifndef STB_VORBIS_DIVIDES_IN_RESIDUE
@@ -1791,23 +1789,20 @@ static void decode_residue(vorb *f, float *residue_buffers[], int ch, int n, int
int b = r->residue_books[c][pass];
if (b >= 0) {
Codebook *book = f->codebooks + b;
- stb_prof(20); // accounts for X time
#ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK
if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size))
goto done;
#else
// saves 1%
- if (!codebook_decode_deinterleave_repeat_2(f, book, residue_buffers, &c_inter, &p_inter, n, r->part_size))
+ if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size))
goto done;
#endif
- stb_prof(7);
} else {
z += r->part_size;
c_inter = z & 1;
p_inter = z >> 1;
}
}
- stb_prof(8);
#ifndef STB_VORBIS_DIVIDES_IN_RESIDUE
++class_set;
#endif
@@ -1840,10 +1835,8 @@ static void decode_residue(vorb *f, float *residue_buffers[], int ch, int n, int
int b = r->residue_books[c][pass];
if (b >= 0) {
Codebook *book = f->codebooks + b;
- stb_prof(22);
if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size))
goto done;
- stb_prof(3);
} else {
z += r->part_size;
c_inter = 0;
@@ -1882,10 +1875,8 @@ static void decode_residue(vorb *f, float *residue_buffers[], int ch, int n, int
int b = r->residue_books[c][pass];
if (b >= 0) {
Codebook *book = f->codebooks + b;
- stb_prof(22);
if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size))
goto done;
- stb_prof(3);
} else {
z += r->part_size;
c_inter = z % ch;
@@ -1900,7 +1891,7 @@ static void decode_residue(vorb *f, float *residue_buffers[], int ch, int n, int
}
goto done;
}
- stb_prof(9);
+ CHECK(f);
for (pass=0; pass < 8; ++pass) {
int pcount = 0, class_set=0;
@@ -1949,7 +1940,7 @@ static void decode_residue(vorb *f, float *residue_buffers[], int ch, int n, int
}
}
done:
- stb_prof(0);
+ CHECK(f);
temp_alloc_restore(f,temp_alloc_point);
}
@@ -2761,18 +2752,36 @@ static int do_floor(vorb *f, Mapping *map, int i, int n, float *target, YTYPE *f
{
int hy = finalY[j] * g->floor1_multiplier;
int hx = g->Xlist[j];
- draw_line(target, lx,ly, hx,hy, n2);
+ if (lx != hx)
+ draw_line(target, lx,ly, hx,hy, n2);
+ CHECK(f);
lx = hx, ly = hy;
}
}
- if (lx < n2)
+ if (lx < n2) {
// optimization of: draw_line(target, lx,ly, n,ly, n2);
for (j=lx; j < n2; ++j)
LINE_OP(target[j], inverse_db_table[ly]);
+ CHECK(f);
+ }
}
return TRUE;
}
+// The meaning of "left" and "right"
+//
+// For a given frame:
+// we compute samples from 0..n
+// window_center is n/2
+// we'll window and mix the samples from left_start to left_end with data from the previous frame
+// all of the samples from left_end to right_start can be output without mixing; however,
+// this interval is 0-length except when transitioning between short and long frames
+// all of the samples from right_start to right_end need to be mixed with the next frame,
+// which we don't have, so those get saved in a buffer
+// frame N's right_end-right_start, the number of samples to mix with the next frame,
+// has to be the same as frame N+1's left_end-left_start (which they are by
+// construction)
+
static int vorbis_decode_initial(vorb *f, int *p_left_start, int *p_left_end, int *p_right_start, int *p_right_end, int *mode)
{
Mode *m;
@@ -2825,6 +2834,7 @@ static int vorbis_decode_initial(vorb *f, int *p_left_start, int *p_left_end, in
*p_right_start = window_center;
*p_right_end = n;
}
+
return TRUE;
}
@@ -2843,7 +2853,8 @@ static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start,
// FLOORS
n2 = n >> 1;
- stb_prof(1);
+ CHECK(f);
+
for (i=0; i < f->channels; ++i) {
int s = map->chan[i].mux, floor;
zero_channel[i] = FALSE;
@@ -2935,7 +2946,7 @@ static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start,
// at this point we've decoded the floor into buffer
}
}
- stb_prof(0);
+ CHECK(f);
// at this point we've decoded all floors
if (f->alloc.alloc_buffer)
@@ -2948,6 +2959,7 @@ static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start,
zero_channel[map->chan[i].magnitude] = zero_channel[map->chan[i].angle] = FALSE;
}
+ CHECK(f);
// RESIDUE DECODE
for (i=0; i < map->submaps; ++i) {
float *residue_buffers[STB_VORBIS_MAX_CHANNELS];
@@ -2972,9 +2984,9 @@ static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start,
if (f->alloc.alloc_buffer)
assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset);
+ CHECK(f);
// INVERSE COUPLING
- stb_prof(14);
for (i = map->coupling_steps-1; i >= 0; --i) {
int n2 = n >> 1;
float *m = f->channel_buffers[map->chan[i].magnitude];
@@ -2995,10 +3007,10 @@ static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start,
a[j] = a2;
}
}
+ CHECK(f);
// finish decoding the floors
#ifndef STB_VORBIS_NO_DEFER_FLOOR
- stb_prof(15);
for (i=0; i < f->channels; ++i) {
if (really_zero_channel[i]) {
memset(f->channel_buffers[i], 0, sizeof(*f->channel_buffers[i]) * n2);
@@ -3018,10 +3030,10 @@ static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start,
#endif
// INVERSE MDCT
- stb_prof(16);
+ CHECK(f);
for (i=0; i < f->channels; ++i)
inverse_mdct(f->channel_buffers[i], n, f, m->blockflag);
- stb_prof(0);
+ CHECK(f);
// this shouldn't be necessary, unless we exited on an error
// and want to flush to get to the next packet
@@ -3039,9 +3051,15 @@ static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start,
f->current_loc_valid = TRUE;
f->first_decode = FALSE;
} else if (f->discard_samples_deferred) {
- left_start += f->discard_samples_deferred;
- *p_left = left_start;
- f->discard_samples_deferred = 0;
+ if (f->discard_samples_deferred >= right_start - left_start) {
+ f->discard_samples_deferred -= (right_start - left_start);
+ left_start = right_start;
+ *p_left = left_start;
+ } else {
+ left_start += f->discard_samples_deferred;
+ *p_left = left_start;
+ f->discard_samples_deferred = 0;
+ }
} else if (f->previous_length == 0 && f->current_loc_valid) {
// we're recovering from a seek... that means we're going to discard
// the samples from this packet even though we know our position from
@@ -3057,7 +3075,7 @@ static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start,
if (f->current_loc_valid && (f->page_flag & PAGEFLAG_last_page)) {
uint32 current_end = f->known_loc_for_packet - (n-right_end);
// then let's infer the size of the (probably) short final frame
- if (current_end < f->current_loc + right_end) {
+ if (current_end < f->current_loc + (right_end-left_start)) {
if (current_end < f->current_loc) {
// negative truncation, that's impossible!
*len = 0;
@@ -3065,6 +3083,7 @@ static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start,
*len = current_end - f->current_loc;
}
*len += left_start;
+ if (*len > right_end) *len = right_end; // this should never happen
f->current_loc += *len;
return TRUE;
}
@@ -3082,6 +3101,8 @@ static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start,
if (f->alloc.alloc_buffer)
assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset);
*len = right_end; // ignore samples after the window goes to 0
+ CHECK(f);
+
return TRUE;
}
@@ -3247,14 +3268,15 @@ static int start_decoder(vorb *f)
get32(f); // bitrate_nominal
get32(f); // bitrate_minimum
x = get8(f);
- { int log0,log1;
- log0 = x & 15;
- log1 = x >> 4;
- f->blocksize_0 = 1 << log0;
- f->blocksize_1 = 1 << log1;
- if (log0 < 6 || log0 > 13) return error(f, VORBIS_invalid_setup);
- if (log1 < 6 || log1 > 13) return error(f, VORBIS_invalid_setup);
- if (log0 > log1) return error(f, VORBIS_invalid_setup);
+ {
+ int log0,log1;
+ log0 = x & 15;
+ log1 = x >> 4;
+ f->blocksize_0 = 1 << log0;
+ f->blocksize_1 = 1 << log1;
+ if (log0 < 6 || log0 > 13) return error(f, VORBIS_invalid_setup);
+ if (log1 < 6 || log1 > 13) return error(f, VORBIS_invalid_setup);
+ if (log0 > log1) return error(f, VORBIS_invalid_setup);
}
// framing_flag
@@ -3303,6 +3325,7 @@ static int start_decoder(vorb *f)
int total=0;
uint8 *lengths;
Codebook *c = f->codebooks+i;
+ CHECK(f);
x = get_bits(f, 8); if (x != 0x42) return error(f, VORBIS_invalid_setup);
x = get_bits(f, 8); if (x != 0x43) return error(f, VORBIS_invalid_setup);
x = get_bits(f, 8); if (x != 0x56) return error(f, VORBIS_invalid_setup);
@@ -3314,6 +3337,8 @@ static int start_decoder(vorb *f)
ordered = get_bits(f,1);
c->sparse = ordered ? 0 : get_bits(f,1);
+ if (c->dimensions == 0 && c->entries != 0) return error(f, VORBIS_invalid_setup);
+
if (c->sparse)
lengths = (uint8 *) setup_temp_malloc(f, c->entries);
else
@@ -3338,6 +3363,8 @@ static int start_decoder(vorb *f)
if (present) {
lengths[j] = get_bits(f, 5) + 1;
++total;
+ if (lengths[j] == 32)
+ return error(f, VORBIS_invalid_setup);
} else {
lengths[j] = NO_CODE;
}
@@ -3350,6 +3377,7 @@ static int start_decoder(vorb *f)
f->setup_temp_memory_required = c->entries;
c->codeword_lengths = (uint8 *) setup_malloc(f, c->entries);
+ if (c->codeword_lengths == NULL) return error(f, VORBIS_outofmem);
memcpy(c->codeword_lengths, lengths, c->entries);
setup_temp_free(f, lengths, c->entries); // note this is only safe if there have been no intervening temp mallocs!
lengths = c->codeword_lengths;
@@ -3371,6 +3399,7 @@ static int start_decoder(vorb *f)
c->sorted_entries = sorted_count;
values = NULL;
+ CHECK(f);
if (!c->sparse) {
c->codewords = (uint32 *) setup_malloc(f, sizeof(c->codewords[0]) * c->entries);
if (!c->codewords) return error(f, VORBIS_outofmem);
@@ -3397,10 +3426,13 @@ static int start_decoder(vorb *f)
if (c->sorted_entries) {
// allocate an extra slot for sentinels
c->sorted_codewords = (uint32 *) setup_malloc(f, sizeof(*c->sorted_codewords) * (c->sorted_entries+1));
+ if (c->sorted_codewords == NULL) return error(f, VORBIS_outofmem);
// allocate an extra slot at the front so that c->sorted_values[-1] is defined
// so that we can catch that case without an extra if
c->sorted_values = ( int *) setup_malloc(f, sizeof(*c->sorted_values ) * (c->sorted_entries+1));
- if (c->sorted_values) { ++c->sorted_values; c->sorted_values[-1] = -1; }
+ if (c->sorted_values == NULL) return error(f, VORBIS_outofmem);
+ ++c->sorted_values;
+ c->sorted_values[-1] = -1;
compute_sorted_huffman(c, lengths, values);
}
@@ -3413,6 +3445,7 @@ static int start_decoder(vorb *f)
compute_accelerated_huffman(c);
+ CHECK(f);
c->lookup_type = get_bits(f, 4);
if (c->lookup_type > 2) return error(f, VORBIS_invalid_setup);
if (c->lookup_type > 0) {
@@ -3426,6 +3459,7 @@ static int start_decoder(vorb *f)
} else {
c->lookup_values = c->entries * c->dimensions;
}
+ if (c->lookup_values == 0) return error(f, VORBIS_invalid_setup);
mults = (uint16 *) setup_temp_malloc(f, sizeof(mults[0]) * c->lookup_values);
if (mults == NULL) return error(f, VORBIS_outofmem);
for (j=0; j < (int) c->lookup_values; ++j) {
@@ -3437,6 +3471,7 @@ static int start_decoder(vorb *f)
#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK
if (c->lookup_type == 1) {
int len, sparse = c->sparse;
+ float last=0;
// pre-expand the lookup1-style multiplicands, to avoid a divide in the inner loop
if (sparse) {
if (c->sorted_entries == 0) goto skip;
@@ -3446,21 +3481,22 @@ static int start_decoder(vorb *f)
if (c->multiplicands == NULL) { setup_temp_free(f,mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_outofmem); }
len = sparse ? c->sorted_entries : c->entries;
for (j=0; j < len; ++j) {
- int z = sparse ? c->sorted_values[j] : j, div=1;
+ unsigned int z = sparse ? c->sorted_values[j] : j;
+ unsigned int div=1;
for (k=0; k < c->dimensions; ++k) {
int off = (z / div) % c->lookup_values;
- c->multiplicands[j*c->dimensions + k] =
- #ifndef STB_VORBIS_CODEBOOK_FLOATS
- mults[off];
- #else
- mults[off]*c->delta_value + c->minimum_value;
- // in this case (and this case only) we could pre-expand c->sequence_p,
- // and throw away the decode logic for it; have to ALSO do
- // it in the case below, but it can only be done if
- // STB_VORBIS_CODEBOOK_FLOATS
- // !STB_VORBIS_DIVIDES_IN_CODEBOOK
- #endif
- div *= c->lookup_values;
+ float val = mults[off];
+ val = mults[off]*c->delta_value + c->minimum_value + last;
+ c->multiplicands[j*c->dimensions + k] = val;
+ if (c->sequence_p)
+ last = val;
+ if (k+1 < c->dimensions) {
+ if (div > UINT_MAX / (unsigned int) c->lookup_values) {
+ setup_temp_free(f, mults,sizeof(mults[0])*c->lookup_values);
+ return error(f, VORBIS_invalid_setup);
+ }
+ div *= c->lookup_values;
+ }
}
}
setup_temp_free(f, mults,sizeof(mults[0])*c->lookup_values);
@@ -3469,27 +3505,25 @@ static int start_decoder(vorb *f)
else
#endif
{
+ float last=0;
+ CHECK(f);
c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->lookup_values);
- #ifndef STB_VORBIS_CODEBOOK_FLOATS
- memcpy(c->multiplicands, mults, sizeof(c->multiplicands[0]) * c->lookup_values);
- #else
- for (j=0; j < (int) c->lookup_values; ++j)
- c->multiplicands[j] = mults[j] * c->delta_value + c->minimum_value;
- #endif
+ if (c->multiplicands == NULL) { setup_temp_free(f, mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_outofmem); }
+ for (j=0; j < (int) c->lookup_values; ++j) {
+ float val = mults[j] * c->delta_value + c->minimum_value + last;
+ c->multiplicands[j] = val;
+ if (c->sequence_p)
+ last = val;
+ }
setup_temp_free(f, mults,sizeof(mults[0])*c->lookup_values);
}
#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK
skip:;
#endif
- #ifdef STB_VORBIS_CODEBOOK_FLOATS
- if (c->lookup_type == 2 && c->sequence_p) {
- for (j=1; j < (int) c->lookup_values; ++j)
- c->multiplicands[j] = c->multiplicands[j-1];
- c->sequence_p = 0;
- }
- #endif
+ CHECK(f);
}
+ CHECK(f);
}
// time domain transfers (notused)
@@ -3503,6 +3537,7 @@ static int start_decoder(vorb *f)
// Floors
f->floor_count = get_bits(f, 6)+1;
f->floor_config = (Floor *) setup_malloc(f, f->floor_count * sizeof(*f->floor_config));
+ if (f->floor_config == NULL) return error(f, VORBIS_outofmem);
for (i=0; i < f->floor_count; ++i) {
f->floor_types[i] = get_bits(f, 16);
if (f->floor_types[i] > 1) return error(f, VORBIS_invalid_setup);
@@ -3574,7 +3609,9 @@ static int start_decoder(vorb *f)
// Residue
f->residue_count = get_bits(f, 6)+1;
- f->residue_config = (Residue *) setup_malloc(f, f->residue_count * sizeof(*f->residue_config));
+ f->residue_config = (Residue *) setup_malloc(f, f->residue_count * sizeof(f->residue_config[0]));
+ if (f->residue_config == NULL) return error(f, VORBIS_outofmem);
+ memset(f->residue_config, 0, f->residue_count * sizeof(f->residue_config[0]));
for (i=0; i < f->residue_count; ++i) {
uint8 residue_cascade[64];
Residue *r = f->residue_config+i;
@@ -3582,9 +3619,11 @@ static int start_decoder(vorb *f)
if (f->residue_types[i] > 2) return error(f, VORBIS_invalid_setup);
r->begin = get_bits(f, 24);
r->end = get_bits(f, 24);
+ if (r->end < r->begin) return error(f, VORBIS_invalid_setup);
r->part_size = get_bits(f,24)+1;
r->classifications = get_bits(f,6)+1;
r->classbook = get_bits(f,8);
+ if (r->classbook >= f->codebook_count) return error(f, VORBIS_invalid_setup);
for (j=0; j < r->classifications; ++j) {
uint8 high_bits=0;
uint8 low_bits=get_bits(f,3);
@@ -3593,6 +3632,7 @@ static int start_decoder(vorb *f)
residue_cascade[j] = high_bits*8 + low_bits;
}
r->residue_books = (short (*)[8]) setup_malloc(f, sizeof(r->residue_books[0]) * r->classifications);
+ if (r->residue_books == NULL) return error(f, VORBIS_outofmem);
for (j=0; j < r->classifications; ++j) {
for (k=0; k < 8; ++k) {
if (residue_cascade[j] & (1 << k)) {
@@ -3612,6 +3652,7 @@ static int start_decoder(vorb *f)
int classwords = f->codebooks[r->classbook].dimensions;
int temp = j;
r->classdata[j] = (uint8 *) setup_malloc(f, sizeof(r->classdata[j][0]) * classwords);
+ if (r->classdata[j] == NULL) return error(f, VORBIS_outofmem);
for (k=classwords-1; k >= 0; --k) {
r->classdata[j][k] = temp % r->classifications;
temp /= r->classifications;
@@ -3621,11 +3662,14 @@ static int start_decoder(vorb *f)
f->mapping_count = get_bits(f,6)+1;
f->mapping = (Mapping *) setup_malloc(f, f->mapping_count * sizeof(*f->mapping));
+ if (f->mapping == NULL) return error(f, VORBIS_outofmem);
+ memset(f->mapping, 0, f->mapping_count * sizeof(*f->mapping));
for (i=0; i < f->mapping_count; ++i) {
Mapping *m = f->mapping + i;
int mapping_type = get_bits(f,16);
if (mapping_type != 0) return error(f, VORBIS_invalid_setup);
m->chan = (MappingChannel *) setup_malloc(f, f->channels * sizeof(*m->chan));
+ if (m->chan == NULL) return error(f, VORBIS_outofmem);
if (get_bits(f,1))
m->submaps = get_bits(f,4)+1;
else
@@ -3686,8 +3730,10 @@ static int start_decoder(vorb *f)
f->channel_buffers[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1);
f->previous_window[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1/2);
f->finalY[i] = (int16 *) setup_malloc(f, sizeof(int16) * longest_floorlist);
+ if (f->channel_buffers[i] == NULL || f->previous_window[i] == NULL || f->finalY[i] == NULL) return error(f, VORBIS_outofmem);
#ifdef STB_VORBIS_NO_DEFER_FLOOR
f->floor_buffers[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1/2);
+ if (f->floor_buffers[i] == NULL) return error(f, VORBIS_outofmem);
#endif
}
@@ -3745,17 +3791,20 @@ static int start_decoder(vorb *f)
static void vorbis_deinit(stb_vorbis *p)
{
int i,j;
- for (i=0; i < p->residue_count; ++i) {
- Residue *r = p->residue_config+i;
- if (r->classdata) {
- for (j=0; j < p->codebooks[r->classbook].entries; ++j)
- setup_free(p, r->classdata[j]);
- setup_free(p, r->classdata);
+ if (p->residue_config) {
+ for (i=0; i < p->residue_count; ++i) {
+ Residue *r = p->residue_config+i;
+ if (r->classdata) {
+ for (j=0; j < p->codebooks[r->classbook].entries; ++j)
+ setup_free(p, r->classdata[j]);
+ setup_free(p, r->classdata);
+ }
+ setup_free(p, r->residue_books);
}
- setup_free(p, r->residue_books);
}
if (p->codebooks) {
+ CHECK(p);
for (i=0; i < p->codebook_count; ++i) {
Codebook *c = p->codebooks + i;
setup_free(p, c->codeword_lengths);
@@ -3769,10 +3818,13 @@ static void vorbis_deinit(stb_vorbis *p)
}
setup_free(p, p->floor_config);
setup_free(p, p->residue_config);
- for (i=0; i < p->mapping_count; ++i)
- setup_free(p, p->mapping[i].chan);
- setup_free(p, p->mapping);
- for (i=0; i < p->channels; ++i) {
+ if (p->mapping) {
+ for (i=0; i < p->mapping_count; ++i)
+ setup_free(p, p->mapping[i].chan);
+ setup_free(p, p->mapping);
+ }
+ CHECK(p);
+ for (i=0; i < p->channels && i < STB_VORBIS_MAX_CHANNELS; ++i) {
setup_free(p, p->channel_buffers[i]);
setup_free(p, p->previous_window[i]);
#ifdef STB_VORBIS_NO_DEFER_FLOOR
@@ -3799,7 +3851,7 @@ void stb_vorbis_close(stb_vorbis *p)
setup_free(p,p);
}
-static void vorbis_init(stb_vorbis *p, stb_vorbis_alloc *z)
+static void vorbis_init(stb_vorbis *p, const stb_vorbis_alloc *z)
{
memset(p, 0, sizeof(*p)); // NULL out all malloc'd pointers to start
if (z) {
@@ -3957,11 +4009,11 @@ static int vorbis_search_for_page_pushdata(vorb *f, uint8 *data, int data_len)
// return value: number of bytes we used
int stb_vorbis_decode_frame_pushdata(
- stb_vorbis *f, // the file we're decoding
- uint8 *data, int data_len, // the memory available for decoding
- int *channels, // place to write number of float * buffers
- float ***output, // place to write float ** array of float * buffers
- int *samples // place to write number of output samples
+ stb_vorbis *f, // the file we're decoding
+ const uint8 *data, int data_len, // the memory available for decoding
+ int *channels, // place to write number of float * buffers
+ float ***output, // place to write float ** array of float * buffers
+ int *samples // place to write number of output samples
)
{
int i;
@@ -3971,11 +4023,11 @@ int stb_vorbis_decode_frame_pushdata(
if (f->page_crc_tests >= 0) {
*samples = 0;
- return vorbis_search_for_page_pushdata(f, data, data_len);
+ return vorbis_search_for_page_pushdata(f, (uint8 *) data, data_len);
}
- f->stream = data;
- f->stream_end = data + data_len;
+ f->stream = (uint8 *) data;
+ f->stream_end = (uint8 *) data + data_len;
f->error = VORBIS__no_error;
// check that we have the entire packet in memory
@@ -4027,14 +4079,14 @@ int stb_vorbis_decode_frame_pushdata(
}
stb_vorbis *stb_vorbis_open_pushdata(
- unsigned char *data, int data_len, // the memory available for decoding
+ const unsigned char *data, int data_len, // the memory available for decoding
int *data_used, // only defined if result is not NULL
- int *error, stb_vorbis_alloc *alloc)
+ int *error, const stb_vorbis_alloc *alloc)
{
stb_vorbis *f, p;
vorbis_init(&p, alloc);
- p.stream = data;
- p.stream_end = data + data_len;
+ p.stream = (uint8 *) data;
+ p.stream_end = (uint8 *) data + data_len;
p.push_mode = TRUE;
if (!start_decoder(&p)) {
if (p.eof)
@@ -4078,7 +4130,7 @@ static uint32 vorbis_find_page(stb_vorbis *f, uint32 *end, uint32 *last)
int n;
if (f->eof) return 0;
n = get8(f);
- if (n == 0x4f) { // page header
+ if (n == 0x4f) { // page header candidate
unsigned int retry_loc = stb_vorbis_get_file_offset(f);
int i;
// check if we're off the end of a file_section stream
@@ -4142,37 +4194,30 @@ static uint32 vorbis_find_page(stb_vorbis *f, uint32 *end, uint32 *last)
}
}
-// seek is implemented with 'interpolation search'--this is like
-// binary search, but we use the data values to estimate the likely
-// location of the data item (plus a bit of a bias so when the
-// estimation is wrong we don't waste overly much time)
#define SAMPLE_unknown 0xffffffff
+// seeking is implemented with a binary search, which narrows down the range to
+// 64K, before using a linear search (because finding the synchronization
+// pattern can be expensive, and the chance we'd find the end page again is
+// relatively high for small ranges)
+//
+// two initial interpolation-style probes are used at the start of the search
+// to try to bound either side of the binary search sensibly, while still
+// working in O(log n) time if they fail.
-// ogg vorbis, in its insane infinite wisdom, only provides
-// information about the sample at the END of the page.
-// therefore we COULD have the data we need in the current
-// page, and not know it. we could just use the end location
-// as our only knowledge for bounds, seek back, and eventually
-// the binary search finds it. or we can try to be smart and
-// not waste time trying to locate more pages. we try to be
-// smart, since this data is already in memory anyway, so
-// doing needless I/O would be crazy!
-static int vorbis_analyze_page(stb_vorbis *f, ProbedPage *z)
+static int get_seek_page_info(stb_vorbis *f, ProbedPage *z)
{
uint8 header[27], lacing[255];
- uint8 packet_type[255];
- int num_packet, packet_start;
int i,len;
- uint32 samples;
// record where the page starts
z->page_start = stb_vorbis_get_file_offset(f);
// parse the header
getn(f, header, 27);
- assert(header[0] == 'O' && header[1] == 'g' && header[2] == 'g' && header[3] == 'S');
+ if (header[0] != 'O' || header[1] != 'g' || header[2] != 'g' || header[3] != 'S')
+ return 0;
getn(f, lacing, header[26]);
// determine the length of the payload
@@ -4184,304 +4229,265 @@ static int vorbis_analyze_page(stb_vorbis *f, ProbedPage *z)
z->page_end = z->page_start + 27 + header[26] + len;
// read the last-decoded sample out of the data
- z->last_decoded_sample = header[6] + (header[7] << 8) + (header[8] << 16) + (header[9] << 16);
+ z->last_decoded_sample = header[6] + (header[7] << 8) + (header[8] << 16) + (header[9] << 24);
- if (header[5] & 4) {
- // if this is the last page, it's not possible to work
- // backwards to figure out the first sample! whoops! fuck.
- z->first_decoded_sample = SAMPLE_unknown;
- set_file_offset(f, z->page_start);
- return 1;
- }
-
- // scan through the frames to determine the sample-count of each one...
- // our goal is the sample # of the first fully-decoded sample on the
- // page, which is the first decoded sample of the 2nd packet
+ // restore file state to where we were
+ set_file_offset(f, z->page_start);
+ return 1;
+}
- num_packet=0;
+// rarely used function to seek back to the preceeding page while finding the
+// start of a packet
+static int go_to_page_before(stb_vorbis *f, unsigned int limit_offset)
+{
+ unsigned int previous_safe, end;
- packet_start = ((header[5] & 1) == 0);
+ // now we want to seek back 64K from the limit
+ if (limit_offset >= 65536 && limit_offset-65536 >= f->first_audio_page_offset)
+ previous_safe = limit_offset - 65536;
+ else
+ previous_safe = f->first_audio_page_offset;
- for (i=0; i < header[26]; ++i) {
- if (packet_start) {
- uint8 n,b;
- if (lacing[i] == 0) goto bail; // trying to read from zero-length packet
- n = get8(f);
- // if bottom bit is non-zero, we've got corruption
- if (n & 1) goto bail;
- n >>= 1;
- b = ilog(f->mode_count-1);
- n &= (1 << b)-1;
- if (n >= f->mode_count) goto bail;
- packet_type[num_packet++] = f->mode_config[n].blockflag;
- skip(f, lacing[i]-1);
- } else
- skip(f, lacing[i]);
- packet_start = (lacing[i] < 255);
- }
-
- // now that we know the sizes of all the pages, we can start determining
- // how much sample data there is.
-
- samples = 0;
-
- // for the last packet, we step by its whole length, because the definition
- // is that we encoded the end sample loc of the 'last packet completed',
- // where 'completed' refers to packets being split, and we are left to guess
- // what 'end sample loc' means. we assume it means ignoring the fact that
- // the last half of the data is useless without windowing against the next
- // packet... (so it's not REALLY complete in that sense)
- if (num_packet > 1)
- samples += f->blocksize[packet_type[num_packet-1]];
-
- for (i=num_packet-2; i >= 1; --i) {
- // now, for this packet, how many samples do we have that
- // do not overlap the following packet?
- if (packet_type[i] == 1)
- if (packet_type[i+1] == 1)
- samples += f->blocksize_1 >> 1;
- else
- samples += ((f->blocksize_1 - f->blocksize_0) >> 2) + (f->blocksize_0 >> 1);
- else
- samples += f->blocksize_0 >> 1;
- }
- // now, at this point, we've rewound to the very beginning of the
- // _second_ packet. if we entirely discard the first packet after
- // a seek, this will be exactly the right sample number. HOWEVER!
- // we can't as easily compute this number for the LAST page. The
- // only way to get the sample offset of the LAST page is to use
- // the end loc from the previous page. But what that returns us
- // is _exactly_ the place where we get our first non-overlapped
- // sample. (I think. Stupid spec for being ambiguous.) So for
- // consistency it's better to do that here, too. However, that
- // will then require us to NOT discard all of the first frame we
- // decode, in some cases, which means an even weirder frame size
- // and extra code. what a fucking pain.
-
- // we're going to discard the first packet if we
- // start the seek here, so we don't care about it. (we could actually
- // do better; if the first packet is long, and the previous packet
- // is short, there's actually data in the first half of the first
- // packet that doesn't need discarding... but not worth paying the
- // effort of tracking that of that here and in the seeking logic)
- // except crap, if we infer it from the _previous_ packet's end
- // location, we DO need to use that definition... and we HAVE to
- // infer the start loc of the LAST packet from the previous packet's
- // end location. fuck you, ogg vorbis.
-
- z->first_decoded_sample = z->last_decoded_sample - samples;
+ set_file_offset(f, previous_safe);
- // restore file state to where we were
- set_file_offset(f, z->page_start);
- return 1;
+ while (vorbis_find_page(f, &end, NULL)) {
+ if (end >= limit_offset && stb_vorbis_get_file_offset(f) < limit_offset)
+ return 1;
+ set_file_offset(f, end);
+ }
- // restore file state to where we were
- bail:
- set_file_offset(f, z->page_start);
return 0;
}
-static int vorbis_seek_frame_from_page(stb_vorbis *f, uint32 page_start, uint32 first_sample, uint32 target_sample, int fine)
-{
- int left_start, left_end, right_start, right_end, mode,i;
- int frame=0;
- uint32 frame_start;
- int frames_to_skip, data_to_skip;
+// implements the search logic for finding a page and starting decoding. if
+// the function succeeds, current_loc_valid will be true and current_loc will
+// be less than or equal to the provided sample number (the closer the
+// better).
+static int seek_to_sample_coarse(stb_vorbis *f, uint32 sample_number)
+{
+ ProbedPage left, right, mid;
+ int i, start_seg_with_known_loc, end_pos, page_start;
+ uint32 delta, stream_length, padding;
+ double offset, bytes_per_sample;
+ int probe = 0;
+
+ // find the last page and validate the target sample
+ stream_length = stb_vorbis_stream_length_in_samples(f);
+ if (stream_length == 0) return error(f, VORBIS_seek_without_length);
+ if (sample_number > stream_length) return error(f, VORBIS_seek_invalid);
+
+ // this is the maximum difference between the window-center (which is the
+ // actual granule position value), and the right-start (which the spec
+ // indicates should be the granule position (give or take one)).
+ padding = ((f->blocksize_1 - f->blocksize_0) >> 2);
+ if (sample_number < padding)
+ sample_number = 0;
+ else
+ sample_number -= padding;
- // first_sample is the sample # of the first sample that doesn't
- // overlap the previous page... note that this requires us to
- // _partially_ discard the first packet! bleh.
- set_file_offset(f, page_start);
+ left = f->p_first;
+ while (left.last_decoded_sample == ~0U) {
+ // (untested) the first page does not have a 'last_decoded_sample'
+ set_file_offset(f, left.page_end);
+ if (!get_seek_page_info(f, &left)) goto error;
+ }
- f->next_seg = -1; // force page resync
+ right = f->p_last;
+ assert(right.last_decoded_sample != ~0U);
- frame_start = first_sample;
- // frame start is where the previous packet's last decoded sample
- // was, which corresponds to left_end... EXCEPT if the previous
- // packet was long and this packet is short? Probably a bug here.
+ // starting from the start is handled differently
+ if (sample_number <= left.last_decoded_sample) {
+ stb_vorbis_seek_start(f);
+ return 1;
+ }
+ while (left.page_end != right.page_start) {
+ assert(left.page_end < right.page_start);
+ // search range in bytes
+ delta = right.page_start - left.page_end;
+ if (delta <= 65536) {
+ // there's only 64K left to search - handle it linearly
+ set_file_offset(f, left.page_end);
+ } else {
+ if (probe < 2) {
+ if (probe == 0) {
+ // first probe (interpolate)
+ double data_bytes = right.page_end - left.page_start;
+ bytes_per_sample = data_bytes / right.last_decoded_sample;
+ offset = left.page_start + bytes_per_sample * (sample_number - left.last_decoded_sample);
+ } else {
+ // second probe (try to bound the other side)
+ double error = ((double) sample_number - mid.last_decoded_sample) * bytes_per_sample;
+ if (error >= 0 && error < 8000) error = 8000;
+ if (error < 0 && error > -8000) error = -8000;
+ offset += error * 2;
+ }
- // now, we can start decoding frames... we'll only FAKE decode them,
- // until we find the frame that contains our sample; then we'll rewind,
- // and try again
- for (;;) {
- int start;
+ // ensure the offset is valid
+ if (offset < left.page_end)
+ offset = left.page_end;
+ if (offset > right.page_start - 65536)
+ offset = right.page_start - 65536;
- if (!vorbis_decode_initial(f, &left_start, &left_end, &right_start, &right_end, &mode))
- return error(f, VORBIS_seek_failed);
+ set_file_offset(f, (unsigned int) offset);
+ } else {
+ // binary search for large ranges (offset by 32K to ensure
+ // we don't hit the right page)
+ set_file_offset(f, left.page_end + (delta / 2) - 32768);
+ }
- if (frame == 0)
- start = left_end;
- else
- start = left_start;
+ if (!vorbis_find_page(f, NULL, NULL)) goto error;
+ }
- // the window starts at left_start; the last valid sample we generate
- // before the next frame's window start is right_start-1
- if (target_sample < frame_start + right_start-start)
- break;
+ for (;;) {
+ if (!get_seek_page_info(f, &mid)) goto error;
+ if (mid.last_decoded_sample != ~0U) break;
+ // (untested) no frames end on this page
+ set_file_offset(f, mid.page_end);
+ assert(mid.page_start < right.page_start);
+ }
- flush_packet(f);
- if (f->eof)
- return error(f, VORBIS_seek_failed);
+ // if we've just found the last page again then we're in a tricky file,
+ // and we're close enough.
+ if (mid.page_start == right.page_start)
+ break;
- frame_start += right_start - start;
+ if (sample_number < mid.last_decoded_sample)
+ right = mid;
+ else
+ left = mid;
- ++frame;
+ ++probe;
}
- // ok, at this point, the sample we want is contained in frame #'frame'
+ // seek back to start of the last packet
+ page_start = left.page_start;
+ set_file_offset(f, page_start);
+ if (!start_page(f)) return error(f, VORBIS_seek_failed);
+ end_pos = f->end_seg_with_known_loc;
+ assert(end_pos >= 0);
- // to decode frame #'frame' normally, we have to decode the
- // previous frame first... but if it's the FIRST frame of the page
- // we can't. if it's the first frame, it means it falls in the part
- // of the first frame that doesn't overlap either of the other frames.
- // so, if we have to handle that case for the first frame, we might
- // as well handle it for all of them, so:
- if (target_sample > frame_start + (left_end - left_start)) {
- // so what we want to do is go ahead and just immediately decode
- // this frame, but then make it so the next get_frame_float() uses
- // this already-decoded data? or do we want to go ahead and rewind,
- // and leave a flag saying to skip the first N data? let's do that
- frames_to_skip = frame; // if this is frame #1, skip 1 frame (#0)
- data_to_skip = left_end - left_start;
- } else {
- // otherwise, we want to skip frames 0, 1, 2, ... frame-2
- // (which means frame-2+1 total frames) then decode frame-1,
- // then leave frame pending
- frames_to_skip = frame - 1;
- assert(frames_to_skip >= 0);
- data_to_skip = -1;
- }
+ for (;;) {
+ for (i = end_pos; i > 0; --i)
+ if (f->segments[i-1] != 255)
+ break;
- set_file_offset(f, page_start);
- f->next_seg = - 1; // force page resync
+ start_seg_with_known_loc = i;
- for (i=0; i < frames_to_skip; ++i) {
- maybe_start_packet(f);
- flush_packet(f);
- }
+ if (start_seg_with_known_loc > 0 || !(f->page_flag & PAGEFLAG_continued_packet))
+ break;
- if (data_to_skip >= 0) {
- int i,j,n = f->blocksize_0 >> 1;
- f->discard_samples_deferred = data_to_skip;
- for (i=0; i < f->channels; ++i)
- for (j=0; j < n; ++j)
- f->previous_window[i][j] = 0;
- f->previous_length = n;
- frame_start += data_to_skip;
- } else {
- f->previous_length = 0;
- vorbis_pump_first_frame(f);
- }
-
- // at this point, the NEXT decoded frame will generate the desired sample
- if (fine) {
- // so if we're doing sample accurate streaming, we want to go ahead and decode it!
- if (target_sample != frame_start) {
- int n;
- stb_vorbis_get_frame_float(f, &n, NULL);
- assert(target_sample > frame_start);
- assert(f->channel_buffer_start + (int) (target_sample-frame_start) < f->channel_buffer_end);
- f->channel_buffer_start += (target_sample - frame_start);
- }
+ // (untested) the final packet begins on an earlier page
+ if (!go_to_page_before(f, page_start))
+ goto error;
+
+ page_start = stb_vorbis_get_file_offset(f);
+ if (!start_page(f)) goto error;
+ end_pos = f->segment_count - 1;
}
- return 0;
+ // prepare to start decoding
+ f->current_loc_valid = FALSE;
+ f->last_seg = FALSE;
+ f->valid_bits = 0;
+ f->packet_bytes = 0;
+ f->bytes_in_seg = 0;
+ f->previous_length = 0;
+ f->next_seg = start_seg_with_known_loc;
+
+ for (i = 0; i < start_seg_with_known_loc; i++)
+ skip(f, f->segments[i]);
+
+ // start decoding (optimizable - this frame is generally discarded)
+ vorbis_pump_first_frame(f);
+ return 1;
+
+error:
+ // try to restore the file to a valid state
+ stb_vorbis_seek_start(f);
+ return error(f, VORBIS_seek_failed);
}
-static int vorbis_seek_base(stb_vorbis *f, unsigned int sample_number, int fine)
+// the same as vorbis_decode_initial, but without advancing
+static int peek_decode_initial(vorb *f, int *p_left_start, int *p_left_end, int *p_right_start, int *p_right_end, int *mode)
{
- ProbedPage p[2],q;
- if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing);
+ int bits_read, bytes_read;
- // do we know the location of the last page?
- if (f->p_last.page_start == 0) {
- uint32 z = stb_vorbis_stream_length_in_samples(f);
- if (z == 0) return error(f, VORBIS_cant_find_last_page);
- }
+ if (!vorbis_decode_initial(f, p_left_start, p_left_end, p_right_start, p_right_end, mode))
+ return 0;
- p[0] = f->p_first;
- p[1] = f->p_last;
+ // either 1 or 2 bytes were read, figure out which so we can rewind
+ bits_read = 1 + ilog(f->mode_count-1);
+ if (f->mode_config[*mode].blockflag)
+ bits_read += 2;
+ bytes_read = (bits_read + 7) / 8;
+
+ f->bytes_in_seg += bytes_read;
+ f->packet_bytes -= bytes_read;
+ skip(f, -bytes_read);
+ if (f->next_seg == -1)
+ f->next_seg = f->segment_count - 1;
+ else
+ f->next_seg--;
+ f->valid_bits = 0;
- if (sample_number >= f->p_last.last_decoded_sample)
- sample_number = f->p_last.last_decoded_sample-1;
+ return 1;
+}
- if (sample_number < f->p_first.last_decoded_sample) {
- vorbis_seek_frame_from_page(f, p[0].page_start, 0, sample_number, fine);
- return 0;
- } else {
- int attempts=0;
- while (p[0].page_end < p[1].page_start) {
- uint32 probe;
- uint32 start_offset, end_offset;
- uint32 start_sample, end_sample;
-
- // copy these into local variables so we can tweak them
- // if any are unknown
- start_offset = p[0].page_end;
- end_offset = p[1].after_previous_page_start; // an address known to seek to page p[1]
- start_sample = p[0].last_decoded_sample;
- end_sample = p[1].last_decoded_sample;
-
- // currently there is no such tweaking logic needed/possible?
- if (start_sample == SAMPLE_unknown || end_sample == SAMPLE_unknown)
- return error(f, VORBIS_seek_failed);
-
- // now we want to lerp between these for the target samples...
-
- // step 1: we need to bias towards the page start...
- if (start_offset + 4000 < end_offset)
- end_offset -= 4000;
-
- // now compute an interpolated search loc
- probe = start_offset + (int) floor((float) (end_offset - start_offset) / (end_sample - start_sample) * (sample_number - start_sample));
-
- // next we need to bias towards binary search...
- // code is a little wonky to allow for full 32-bit unsigned values
- if (attempts >= 4) {
- uint32 probe2 = start_offset + ((end_offset - start_offset) >> 1);
- if (attempts >= 8)
- probe = probe2;
- else if (probe < probe2)
- probe = probe + ((probe2 - probe) >> 1);
- else
- probe = probe2 + ((probe - probe2) >> 1);
- }
- ++attempts;
+int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number)
+{
+ uint32 max_frame_samples;
- set_file_offset(f, probe);
- if (!vorbis_find_page(f, NULL, NULL)) return error(f, VORBIS_seek_failed);
- if (!vorbis_analyze_page(f, &q)) return error(f, VORBIS_seek_failed);
- q.after_previous_page_start = probe;
+ if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing);
- // it's possible we've just found the last page again
- if (q.page_start == p[1].page_start) {
- p[1] = q;
- continue;
- }
+ // fast page-level search
+ if (!seek_to_sample_coarse(f, sample_number))
+ return 0;
- if (sample_number < q.last_decoded_sample)
- p[1] = q;
- else
- p[0] = q;
- }
+ assert(f->current_loc_valid);
+ assert(f->current_loc <= sample_number);
- if (p[0].last_decoded_sample <= sample_number && sample_number < p[1].last_decoded_sample) {
- vorbis_seek_frame_from_page(f, p[1].page_start, p[0].last_decoded_sample, sample_number, fine);
- return 0;
+ // linear search for the relevant packet
+ max_frame_samples = (f->blocksize_1*3 - f->blocksize_0) >> 2;
+ while (f->current_loc < sample_number) {
+ int left_start, left_end, right_start, right_end, mode, frame_samples;
+ if (!peek_decode_initial(f, &left_start, &left_end, &right_start, &right_end, &mode))
+ return error(f, VORBIS_seek_failed);
+ // calculate the number of samples returned by the next frame
+ frame_samples = right_start - left_start;
+ if (f->current_loc + frame_samples > sample_number) {
+ return 1; // the next frame will contain the sample
+ } else if (f->current_loc + frame_samples + max_frame_samples > sample_number) {
+ // there's a chance the frame after this could contain the sample
+ vorbis_pump_first_frame(f);
+ } else {
+ // this frame is too early to be relevant
+ f->current_loc += frame_samples;
+ f->previous_length = 0;
+ maybe_start_packet(f);
+ flush_packet(f);
}
- return error(f, VORBIS_seek_failed);
}
-}
-
-int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number)
-{
- return vorbis_seek_base(f, sample_number, FALSE);
+ // the next frame will start with the sample
+ assert(f->current_loc == sample_number);
+ return 1;
}
int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number)
{
- return vorbis_seek_base(f, sample_number, TRUE);
+ if (!stb_vorbis_seek_frame(f, sample_number))
+ return 0;
+
+ if (sample_number != f->current_loc) {
+ int n;
+ uint32 frame_start = f->current_loc;
+ stb_vorbis_get_frame_float(f, &n, NULL);
+ assert(sample_number > frame_start);
+ assert(f->channel_buffer_start + (int) (sample_number-frame_start) <= f->channel_buffer_end);
+ f->channel_buffer_start += (sample_number - frame_start);
+ }
+
+ return 1;
}
void stb_vorbis_seek_start(stb_vorbis *f)
@@ -4562,8 +4568,6 @@ unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f)
f->p_last.page_start = last_page_loc;
f->p_last.page_end = end;
f->p_last.last_decoded_sample = lo;
- f->p_last.first_decoded_sample = SAMPLE_unknown;
- f->p_last.after_previous_page_start = previous_safe;
done:
set_file_offset(f, restore_offset);
@@ -4602,7 +4606,7 @@ int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output)
#ifndef STB_VORBIS_NO_STDIO
-stb_vorbis * stb_vorbis_open_file_section(FILE *file, int close_on_free, int *error, stb_vorbis_alloc *alloc, unsigned int length)
+stb_vorbis * stb_vorbis_open_file_section(FILE *file, int close_on_free, int *error, const stb_vorbis_alloc *alloc, unsigned int length)
{
stb_vorbis *f, p;
vorbis_init(&p, alloc);
@@ -4623,7 +4627,7 @@ stb_vorbis * stb_vorbis_open_file_section(FILE *file, int close_on_free, int *er
return NULL;
}
-stb_vorbis * stb_vorbis_open_file(FILE *file, int close_on_free, int *error, stb_vorbis_alloc *alloc)
+stb_vorbis * stb_vorbis_open_file(FILE *file, int close_on_free, int *error, const stb_vorbis_alloc *alloc)
{
unsigned int len, start;
start = ftell(file);
@@ -4633,7 +4637,7 @@ stb_vorbis * stb_vorbis_open_file(FILE *file, int close_on_free, int *error, stb
return stb_vorbis_open_file_section(file, close_on_free, error, alloc, len);
}
-stb_vorbis * stb_vorbis_open_filename(const char *filename, int *error, stb_vorbis_alloc *alloc)
+stb_vorbis * stb_vorbis_open_filename(const char *filename, int *error, const stb_vorbis_alloc *alloc)
{
FILE *f = fopen(filename, "rb");
if (f)
@@ -4643,7 +4647,7 @@ stb_vorbis * stb_vorbis_open_filename(const char *filename, int *error, stb_vorb
}
#endif // STB_VORBIS_NO_STDIO
-stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len, int *error, stb_vorbis_alloc *alloc)
+stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len, int *error, const stb_vorbis_alloc *alloc)
{
stb_vorbis *f, p;
if (data == NULL) return NULL;
@@ -5023,6 +5027,10 @@ int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, in
#endif // STB_VORBIS_NO_PULLDATA_API
/* Version history
+ 1.07 - 2015/01/16 - fixed some warnings, fix mingw, const-correct API
+ some more crash fixes when out of memory or with corrupt files
+ 1.06 - 2015/08/31 - full, correct support for seeking API (Dougall Johnson)
+ some crash fixes when out of memory or with corrupt files
1.05 - 2015/04/19 - don't define __forceinline if it's redundant
1.04 - 2014/08/27 - fix missing const-correct case in API
1.03 - 2014/08/07 - Warning fixes
diff --git a/src/stb_vorbis.h b/src/stb_vorbis.h
index 2a1460e1..bd318972 100644
--- a/src/stb_vorbis.h
+++ b/src/stb_vorbis.h
@@ -1,35 +1,45 @@
-// Ogg Vorbis audio decoder - v1.05 - public domain
+// Ogg Vorbis audio decoder - v1.07 - public domain
// http://nothings.org/stb_vorbis/
//
-// Written by Sean Barrett in 2007, last updated in 2014
-// Sponsored by RAD Game Tools.
+// Original version written by Sean Barrett in 2007.
//
-// Placed in the public domain April 2007 by the author: no copyright
-// is claimed, and you may use it for any purpose you like.
+// Originally sponsored by RAD Game Tools. Seeking sponsored
+// by Phillip Bennefall, Marc Andersen, Aaron Baker, Elias Software,
+// Aras Pranckevicius, and Sean Barrett.
+//
+// LICENSE
+//
+// This software is in the public domain. Where that dedication is not
+// recognized, you are granted a perpetual, irrevocable license to copy,
+// distribute, and modify this file as you see fit.
//
// No warranty for any purpose is expressed or implied by the author (nor
// by RAD Game Tools). Report bugs and send enhancements to the author.
//
// Limitations:
//
-// - seeking not supported except manually via PUSHDATA api
// - floor 0 not supported (used in old ogg vorbis files pre-2004)
// - lossless sample-truncation at beginning ignored
// - cannot concatenate multiple vorbis streams
// - sample positions are 32-bit, limiting seekable 192Khz
// files to around 6 hours (Ogg supports 64-bit)
//
+// Feature contributors:
+// Dougall Johnson (sample-exact seeking)
+//
// Bugfix/warning contributors:
// Terje Mathisen Niklas Frykholm Andy Hill
// Casey Muratori John Bolton Gargaj
// Laurent Gomila Marc LeBlanc Ronny Chevalier
-// Bernhard Wodo Evan Balster "alxprd"@github
+// Bernhard Wodo Evan Balster "alxprd"@github
// Tom Beaumont Ingo Leitgeb Nicolas Guillemot
-// (If you reported a bug but do not appear in this list, it is because
-// someone else reported the bug before you. There were too many of you to
-// list them all because I was lax about updating for a long time, sorry.)
+// Phillip Bennefall Rohit
//
// Partial history:
+// 1.07 - 2015/01/16 - fixes for crashes on invalid files; warning fixes; const
+// 1.06 - 2015/08/31 - full, correct support for seeking API (Dougall Johnson)
+// some crash fixes when out of memory or with corrupt files
+// fix some inappropriately signed shifts
// 1.05 - 2015/04/19 - don't define __forceinline if it's redundant
// 1.04 - 2014/08/27 - fix missing const-correct case in API
// 1.03 - 2014/08/07 - warning fixes
@@ -37,8 +47,6 @@
// 1.01 - 2014/06/18 - fix stb_vorbis_get_samples_float (interleaved was correct)
// 1.0 - 2014/05/26 - fix memory leaks; fix warnings; fix bugs in >2-channel;
// (API change) report sample rate for decode-full-file funcs
-// 0.99996 - - bracket #include <malloc.h> for macintosh compilation
-// 0.99995 - - avoid alias-optimization issue in float-to-int conversion
//
// See end of file for full version history.
@@ -157,10 +165,10 @@ extern unsigned int stb_vorbis_get_file_offset(stb_vorbis *f);
// specification does not bound the size of an individual frame.
extern stb_vorbis *stb_vorbis_open_pushdata(
- unsigned char *datablock, int datablock_length_in_bytes,
+ const unsigned char * datablock, int datablock_length_in_bytes,
int *datablock_memory_consumed_in_bytes,
int *error,
- stb_vorbis_alloc *alloc_buffer);
+ const stb_vorbis_alloc *alloc_buffer);
// create a vorbis decoder by passing in the initial data block containing
// the ogg&vorbis headers (you don't need to do parse them, just provide
// the first N bytes of the file--you're told if it's not enough, see below)
@@ -171,7 +179,8 @@ extern stb_vorbis *stb_vorbis_open_pushdata(
// incomplete and you need to pass in a larger block from the start of the file
extern int stb_vorbis_decode_frame_pushdata(
- stb_vorbis *f, unsigned char *datablock, int datablock_length_in_bytes,
+ stb_vorbis *f,
+ const unsigned char *datablock, int datablock_length_in_bytes,
int *channels, // place to write number of float * buffers
float ***output, // place to write float ** array of float * buffers
int *samples // place to write number of output samples
@@ -235,18 +244,18 @@ extern int stb_vorbis_decode_memory(const unsigned char *mem, int len, int *chan
// When you're done with it, just free() the pointer returned in *output.
extern stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len,
- int *error, stb_vorbis_alloc *alloc_buffer);
+ int *error, const stb_vorbis_alloc *alloc_buffer);
// create an ogg vorbis decoder from an ogg vorbis stream in memory (note
// this must be the entire stream!). on failure, returns NULL and sets *error
#ifndef STB_VORBIS_NO_STDIO
extern stb_vorbis * stb_vorbis_open_filename(const char *filename,
- int *error, stb_vorbis_alloc *alloc_buffer);
+ int *error, const stb_vorbis_alloc *alloc_buffer);
// create an ogg vorbis decoder from a filename via fopen(). on failure,
// returns NULL and sets *error (possibly to VORBIS_file_open_failure).
extern stb_vorbis * stb_vorbis_open_file(FILE *f, int close_handle_on_close,
- int *error, stb_vorbis_alloc *alloc_buffer);
+ int *error, const stb_vorbis_alloc *alloc_buffer);
// create an ogg vorbis decoder from an open FILE *, looking for a stream at
// the _current_ seek point (ftell). on failure, returns NULL and sets *error.
// note that stb_vorbis must "own" this stream; if you seek it in between
@@ -256,7 +265,7 @@ extern stb_vorbis * stb_vorbis_open_file(FILE *f, int close_handle_on_close,
// function, stb_vorbis_open_file_section(), to limit it.
extern stb_vorbis * stb_vorbis_open_file_section(FILE *f, int close_handle_on_close,
- int *error, stb_vorbis_alloc *alloc_buffer, unsigned int len);
+ int *error, const stb_vorbis_alloc *alloc_buffer, unsigned int len);
// create an ogg vorbis decoder from an open FILE *, looking for a stream at
// the _current_ seek point (ftell); the stream will be of length 'len' bytes.
// on failure, returns NULL and sets *error. note that stb_vorbis must "own"
@@ -266,7 +275,6 @@ extern stb_vorbis * stb_vorbis_open_file_section(FILE *f, int close_handle_on_cl
extern int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number);
extern int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number);
-// NOT WORKING YET
// these functions seek in the Vorbis file to (approximately) 'sample_number'.
// after calling seek_frame(), the next call to get_frame_*() will include
// the specified sample. after calling stb_vorbis_seek(), the next call to
@@ -275,8 +283,7 @@ extern int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number);
// you can also use seek_frame().
extern void stb_vorbis_seek_start(stb_vorbis *f);
-// this function is equivalent to stb_vorbis_seek(f,0), but it
-// actually works
+// this function is equivalent to stb_vorbis_seek(f,0)
extern unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f);
extern float stb_vorbis_stream_length_in_seconds(stb_vorbis *f);
@@ -296,15 +303,17 @@ extern int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***out
extern int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts);
extern int stb_vorbis_get_frame_short (stb_vorbis *f, int num_c, short **buffer, int num_samples);
#endif
-// decode the next frame and return the number of samples per channel. the
-// data is coerced to the number of channels you request according to the
+// decode the next frame and return the number of *samples* per channel.
+// Note that for interleaved data, you pass in the number of shorts (the
+// size of your array), but the return value is the number of samples per
+// channel, not the total number of samples.
+//
+// The data is coerced to the number of channels you request according to the
// channel coercion rules (see below). You must pass in the size of your
// buffer(s) so that stb_vorbis will not overwrite the end of the buffer.
// The maximum buffer size needed can be gotten from get_info(); however,
// the Vorbis I specification implies an absolute maximum of 4096 samples
-// per channel. Note that for interleaved data, you pass in the number of
-// shorts (the size of your array), but the return value is the number of
-// samples per channel, not the total number of samples.
+// per channel.
// Channel coercion rules:
// Let M be the number of channels requested, and N the number of channels present,
@@ -371,7 +380,7 @@ enum STBVorbisError
VORBIS_invalid_first_page,
VORBIS_bad_packet_type,
VORBIS_cant_find_last_page,
- VORBIS_seek_failed,
+ VORBIS_seek_failed
};