diff --git a/assimp-vc141-mt.dll b/assimp-vc141-mt.dll
new file mode 100644
index 0000000..86f52a0
Binary files /dev/null and b/assimp-vc141-mt.dll differ
diff --git a/assimp-vc141-mtd.dll b/assimp-vc141-mtd.dll
new file mode 100644
index 0000000..2e5fd66
Binary files /dev/null and b/assimp-vc141-mtd.dll differ
diff --git a/cw_8/assimp-vc141-mt.dll b/cw_8/assimp-vc141-mt.dll
new file mode 100644
index 0000000..86f52a0
Binary files /dev/null and b/cw_8/assimp-vc141-mt.dll differ
diff --git a/cw_8/assimp-vc141-mtd.dll b/cw_8/assimp-vc141-mtd.dll
new file mode 100644
index 0000000..2e5fd66
Binary files /dev/null and b/cw_8/assimp-vc141-mtd.dll differ
diff --git a/cw_8/freeglut.dll b/cw_8/freeglut.dll
new file mode 100644
index 0000000..c768c15
Binary files /dev/null and b/cw_8/freeglut.dll differ
diff --git a/cw_8/glew32.dll b/cw_8/glew32.dll
new file mode 100644
index 0000000..8a49a3b
Binary files /dev/null and b/cw_8/glew32.dll differ
diff --git a/cw_8/grk-cw8.vcxproj b/cw_8/grk-cw8.vcxproj
new file mode 100644
index 0000000..8d8954a
--- /dev/null
+++ b/cw_8/grk-cw8.vcxproj
@@ -0,0 +1,122 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {6D813233-7D21-4888-944E-8E3CCAC3EFD3}
+ Win32Proj
+ grk-cw8
+ 10.0
+ grk-cw8
+
+
+
+ Application
+ true
+ Unicode
+ v143
+
+
+ Application
+ false
+ true
+ Unicode
+ v143
+
+
+
+
+
+
+
+
+
+
+
+
+ false
+ $(SolutionDir)dependencies\freeglut\lib;$(SolutionDir)dependencies\glew-2.0.0\lib\Release\Win32;$(SolutionDir)dependencies\glfw-3.3.8.bin.WIN32\lib-vc2019;$(SolutionDir)dependencies\assimp;$(LibraryPath)
+ $(SolutionDir)dependencies\freeglut\include\GL;$(SolutionDir)dependencies\glew-2.0.0\include\GL;$(SolutionDir)dependencies\glm;$(SolutionDir)dependencies\glfw-3.3.8.bin.WIN32\include;$(SolutionDir)dependencies\assimp\include;$(IncludePath)
+ $(ExecutablePath)
+
+
+ false
+ $(SolutionDir)dependencies\freeglut\lib;$(SolutionDir)dependencies\glew-2.0.0\lib\Release\Win32;$(SolutionDir)dependencies\glfw-3.3.8.bin.WIN32\lib-vc2019;$(SolutionDir)dependencies\assimp;$(LibraryPath)
+ $(SolutionDir)dependencies\freeglut\include\GL;$(SolutionDir)dependencies\glew-2.0.0\include\GL;$(SolutionDir)dependencies\glm;$(SolutionDir)dependencies\glfw-3.3.8.bin.WIN32\include;$(SolutionDir)dependencies\assimp\include;$(IncludePath)
+
+
+
+
+
+ Level3
+ Disabled
+ WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
+
+
+ Console
+ true
+ opengl32.lib;freeglut.lib;glew32.lib;$(SolutionDir)\dependencies\glfw-3.3.8.bin.WIN32\lib-vc2019\glfw3.lib;$(SolutionDir)\dependencies\glfw-3.3.8.bin.WIN32\lib-vc2019\glfw3dll.lib;zlibd.lib;assimp-vc141-mtd.lib;%(AdditionalDependencies)
+ $(SolutionDir)dependencies\glfw-3.3.8.bin.WIN32\lib-vc2019;%(AdditionalLibraryDirectories)
+
+
+
+
+ Level3
+
+
+ MaxSpeed
+ true
+ true
+ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
+
+
+ Console
+ true
+ true
+ true
+ opengl32.lib;freeglut.lib;glew32.lib;$(SolutionDir)\dependencies\glfw-3.3.8.bin.WIN32\lib-vc2019\glfw3.lib;$(SolutionDir)\dependencies\glfw-3.3.8.bin.WIN32\lib-vc2019\glfw3dll.lib;zlibd.lib;assimp-vc141-mtd.lib;%(AdditionalDependencies)
+ $(SolutionDir)dependencies\glfw-3.3.8.bin.WIN32\lib-vc2019;%(AdditionalLibraryDirectories)
+
+
+
+
+
+
\ No newline at end of file
diff --git a/cw_8/grk-cw8.vcxproj.filters b/cw_8/grk-cw8.vcxproj.filters
new file mode 100644
index 0000000..c1e06a3
--- /dev/null
+++ b/cw_8/grk-cw8.vcxproj.filters
@@ -0,0 +1,101 @@
+
+
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
+
+
+ {93995380-89BD-4b04-88EB-625FBE52EBFB}
+ h;hpp;hxx;hm;inl;inc;xsd
+
+
+ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
+ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
+
+
+ {0a247bb8-2e8e-4a90-b0ef-17415b0941ba}
+
+
+ {0af44075-33f4-4953-b1d6-1d28d61d758f}
+
+
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files\SOIL
+
+
+ Source Files\SOIL
+
+
+ Source Files\SOIL
+
+
+ Source Files\SOIL
+
+
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files\SOIL
+
+
+ Source Files\SOIL
+
+
+ Source Files\SOIL
+
+
+ Source Files\SOIL
+
+
+ Source Files\SOIL
+
+
+ Source Files\SOIL
+
+
+ Source Files
+
+
+
+
+ Shader Files
+
+
+ Shader Files
+
+
+
\ No newline at end of file
diff --git a/cw_8/shaders/pbr.frag b/cw_8/shaders/pbr.frag
new file mode 100644
index 0000000..9a5dcc2
--- /dev/null
+++ b/cw_8/shaders/pbr.frag
@@ -0,0 +1,125 @@
+#version 430 core
+
+out vec4 FragColor;
+
+in vec2 TexCoords;
+in vec3 worldPos;
+in vec3 Normal;
+
+uniform vec3 cameraPos;
+
+uniform vec3 lightPos;
+uniform vec3 lightColor;
+
+uniform sampler2D albedoMap;
+uniform sampler2D normalMap;
+uniform sampler2D metallicMap;
+uniform sampler2D roughnessMap;
+uniform sampler2D aoMap;
+
+const float PI = 3.14159265359;
+
+vec3 getNormalFromMap()
+{
+ vec3 tangentNormal = texture(normalMap, TexCoords).xyz * 2.0 - 1.0;
+
+ vec3 Q1 = dFdx(worldPos);
+ vec3 Q2 = dFdy(worldPos);
+ vec2 st1 = dFdx(TexCoords);
+ vec2 st2 = dFdy(TexCoords);
+
+ vec3 N = normalize(Normal);
+ vec3 T = normalize(Q1*st2.t - Q2*st1.t);
+ vec3 B = -normalize(cross(N, T));
+ mat3 TBN = mat3(T, B, N);
+
+ return normalize(TBN * tangentNormal);
+}
+
+vec3 fresnelSchlick(float cosTheta, vec3 F0){
+ return F0 + (1.0 - F0) * pow(clamp(1.0 - cosTheta, 0.0, 1.0), 5.0);
+}
+
+float DistributionGGX(vec3 N, vec3 H, float roughness)
+{
+ float a = roughness*roughness;
+ float a2 = a*a;
+ float NdotH = max(dot(N, H), 0.0);
+ float NdotH2 = NdotH*NdotH;
+
+ float num = a2;
+ float denom = (NdotH2 * (a2 - 1.0) + 1.0);
+ denom = PI * denom * denom;
+
+ return num / denom;
+}
+
+float GeometrySchlickGGX(float NdotV, float roughness)
+{
+ float r = (roughness + 1.0);
+ float k = (r*r) / 8.0;
+
+ float num = NdotV;
+ float denom = NdotV * (1.0 - k) + k;
+
+ return num / denom;
+}
+float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness)
+{
+ float NdotV = max(dot(N, V), 0.0);
+ float NdotL = max(dot(N, L), 0.0);
+ float ggx2 = GeometrySchlickGGX(NdotV, roughness);
+ float ggx1 = GeometrySchlickGGX(NdotL, roughness);
+
+ return ggx1 * ggx2;
+}
+
+void main()
+{
+ vec3 albedo = pow(texture(albedoMap, TexCoords).rgb, vec3(2.2));
+ float metallic = texture(metallicMap, TexCoords).r;
+ float roughness = texture(roughnessMap, TexCoords).r;
+ float ao = texture(aoMap, TexCoords).r;
+
+ vec3 N = getNormalFromMap();
+ vec3 V = normalize(cameraPos - worldPos);
+
+ vec3 F0 = vec3(0.04);
+ F0 = mix(F0, albedo, metallic);
+
+
+ vec3 Lo = vec3(0.0);
+
+ vec3 L = normalize(lightPos - worldPos);
+ vec3 H = normalize(V + L);
+ float distance = length(lightPos - worldPos);
+ float attenuation = 1.0 / (distance * distance);
+ vec3 radiance = lightColor * attenuation;
+
+ float NDF = DistributionGGX(N, H, roughness);
+ float G = GeometrySmith(N, V, L, roughness);
+ vec3 F = fresnelSchlick(max(dot(H, V), 0.0), F0);
+
+ vec3 numerator = NDF * G * F;
+ float denominator = 4.0 * max(dot(N, V), 0.0) * max(dot(N, L), 0.0) + 0.0001;
+ vec3 specular = numerator / denominator;
+
+ vec3 kS = F;
+
+ vec3 kD = vec3(1.0) - kS;
+
+ kD *= 1.0 - metallic;
+
+ float NdotL = max(dot(N, L), 0.0);
+
+ Lo += (kD * albedo / PI + specular) * radiance * NdotL;
+
+ vec3 ambient = vec3(0.03) * albedo * ao;
+
+ vec3 color = ambient + Lo;
+
+ color = color / (color + vec3(1.0));
+ color = pow(color, vec3(1.0/2.2));
+
+ FragColor = vec4(color, 1.0);
+}
diff --git a/cw_8/shaders/pbr.vert b/cw_8/shaders/pbr.vert
new file mode 100644
index 0000000..5a0427a
--- /dev/null
+++ b/cw_8/shaders/pbr.vert
@@ -0,0 +1,24 @@
+#version 430 core
+
+layout(location = 0) in vec3 vertexPosition;
+layout(location = 1) in vec3 vertexNormal;
+layout(location = 2) in vec2 vertexTexCoord;
+
+uniform mat4 transformation;
+uniform mat4 modelMatrix;
+uniform vec3 cameraPos;
+
+out vec3 Normal;
+out vec3 worldPos;
+out vec2 TexCoords;
+
+void main()
+{
+
+ TexCoords = vertexTexCoord;
+ worldPos = vec3(modelMatrix * vec4(vertexPosition,1));
+ Normal = normalize((modelMatrix * vec4(vertexNormal,0)).xyz);
+
+ gl_Position = transformation * vec4(worldPos, 1.0);
+
+}
diff --git a/cw_8/src/Box.cpp b/cw_8/src/Box.cpp
new file mode 100644
index 0000000..28445d3
--- /dev/null
+++ b/cw_8/src/Box.cpp
@@ -0,0 +1,55 @@
+
+
+// dane 36 wierzcholkow i kolorow opisujace model pudelka
+const float box[] = {
+
+ // points colors
+ 0.25f, 0.25f, 0.75f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f,
+ 0.25f, -0.25f, 0.75f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f,
+ -0.25f, 0.25f, 0.75f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f,
+
+ 0.25f, -0.25f, 0.75f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f,
+ -0.25f, -0.25f, 0.75f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f,
+ -0.25f, 0.25f, 0.75f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f,
+
+ 0.25f, 0.25f, -0.75f, 1.0f, 0.8f, 0.8f, 0.8f, 1.0f,
+ -0.25f, 0.25f, -0.75f, 1.0f, 0.8f, 0.8f, 0.8f, 1.0f,
+ 0.25f, -0.25f, -0.75f, 1.0f, 0.8f, 0.8f, 0.8f, 1.0f,
+
+ 0.25f, -0.25f, -0.75f, 1.0f, 0.8f, 0.8f, 0.8f, 1.0f,
+ -0.25f, 0.25f, -0.75f, 1.0f, 0.8f, 0.8f, 0.8f, 1.0f,
+ -0.25f, -0.25f, -0.75f, 1.0f, 0.8f, 0.8f, 0.8f, 1.0f,
+
+ -0.25f, 0.25f, 0.75f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f,
+ -0.25f, -0.25f, 0.75f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f,
+ -0.25f, -0.25f, -0.75f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f,
+
+ -0.25f, 0.25f, 0.75f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f,
+ -0.25f, -0.25f, -0.75f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f,
+ -0.25f, 0.25f, -0.75f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f,
+
+ 0.25f, 0.25f, 0.75f, 1.0f, 0.5f, 0.5f, 0.0f, 1.0f,
+ 0.25f, -0.25f, -0.75f, 1.0f, 0.5f, 0.5f, 0.0f, 1.0f,
+ 0.25f, -0.25f, 0.75f, 1.0f, 0.5f, 0.5f, 0.0f, 1.0f,
+
+ 0.25f, 0.25f, 0.75f, 1.0f, 0.5f, 0.5f, 0.0f, 1.0f,
+ 0.25f, 0.25f, -0.75f, 1.0f, 0.5f, 0.5f, 0.0f, 1.0f,
+ 0.25f, -0.25f, -0.75f, 1.0f, 0.5f, 0.5f, 0.0f, 1.0f,
+
+ 0.25f, 0.25f, -0.75f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f,
+ 0.25f, 0.25f, 0.75f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f,
+ -0.25f, 0.25f, 0.75f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f,
+
+ 0.25f, 0.25f, -0.75f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f,
+ -0.25f, 0.25f, 0.75f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f,
+ -0.25f, 0.25f, -0.75f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f,
+
+ 0.25f, -0.25f, -0.75f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f,
+ -0.25f, -0.25f, 0.75f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f,
+ 0.25f, -0.25f, 0.75f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f,
+
+ 0.25f, -0.25f, -0.75f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f,
+ -0.25f, -0.25f, -0.75f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f,
+ -0.25f, -0.25f, 0.75f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f,
+};
+
diff --git a/cw_8/src/Camera.cpp b/cw_8/src/Camera.cpp
new file mode 100644
index 0000000..90cc40c
--- /dev/null
+++ b/cw_8/src/Camera.cpp
@@ -0,0 +1,31 @@
+#include "Camera.h"
+
+glm::mat4 Core::createPerspectiveMatrix(float zNear, float zFar, float frustumScale)
+{
+ glm::mat4 perspective;
+ perspective[0][0] = 1.f;
+ perspective[1][1] = frustumScale;
+ perspective[2][2] = (zFar + zNear) / (zNear - zFar);
+ perspective[3][2] = (2 * zFar * zNear) / (zNear - zFar);
+ perspective[2][3] = -1;
+ perspective[3][3] = 0;
+
+ return perspective;
+}
+
+glm::mat4 Core::createViewMatrix( glm::vec3 position, glm::vec3 forward, glm::vec3 up )
+{
+ glm::vec3 side = glm::cross(forward, up);
+
+ // Trzeba pamietac o minusie przy ustawianiu osi Z kamery.
+ // Wynika to z tego, ze standardowa macierz perspektywiczna zaklada, ze "z przodu" jest ujemna (a nie dodatnia) czesc osi Z.
+ glm::mat4 cameraRotation;
+ cameraRotation[0][0] = side.x; cameraRotation[1][0] = side.y; cameraRotation[2][0] = side.z;
+ cameraRotation[0][1] = up.x; cameraRotation[1][1] = up.y; cameraRotation[2][1] = up.z;
+ cameraRotation[0][2] = -forward.x; cameraRotation[1][2] = -forward.y; cameraRotation[2][2] = -forward.z;
+
+ glm::mat4 cameraTranslation;
+ cameraTranslation[3] = glm::vec4(-position, 1.0f);
+
+ return cameraRotation * cameraTranslation;
+}
diff --git a/cw_8/src/Camera.h b/cw_8/src/Camera.h
new file mode 100644
index 0000000..54c29b3
--- /dev/null
+++ b/cw_8/src/Camera.h
@@ -0,0 +1,14 @@
+#pragma once
+
+#include "glm.hpp"
+
+namespace Core
+{
+ glm::mat4 createPerspectiveMatrix(float zNear = 0.1f, float zFar = 100.0f, float frustumScale = 1.f);
+
+ // position - pozycja kamery
+ // forward - wektor "do przodu" kamery (jednostkowy)
+ // up - wektor "w gore" kamery (jednostkowy)
+ // up i forward musza byc ortogonalne!
+ glm::mat4 createViewMatrix(glm::vec3 position, glm::vec3 forward, glm::vec3 up);
+}
\ No newline at end of file
diff --git a/cw_8/src/Render_Utils.cpp b/cw_8/src/Render_Utils.cpp
new file mode 100644
index 0000000..18f71f9
--- /dev/null
+++ b/cw_8/src/Render_Utils.cpp
@@ -0,0 +1,128 @@
+#include "Render_Utils.h"
+
+#include
+
+#include "glew.h"
+#include "freeglut.h"
+#include
+#include
+#include
+
+
+
+void Core::RenderContext::initFromAssimpMesh(aiMesh* mesh) {
+ vertexArray = 0;
+ vertexBuffer = 0;
+ vertexIndexBuffer = 0;
+
+ std::vector textureCoord;
+ std::vector indices;
+ //tex coord must be converted to 2d vecs
+ for (unsigned int i = 0; i < mesh->mNumVertices; i++)
+ {
+ if (mesh->mTextureCoords[0] != nullptr) {
+ textureCoord.push_back(mesh->mTextureCoords[0][i].x);
+ textureCoord.push_back(mesh->mTextureCoords[0][i].y);
+ }
+ else {
+ textureCoord.push_back(0.0f);
+ textureCoord.push_back(0.0f);
+ }
+ }
+ if (mesh->mTextureCoords[0] == nullptr) {
+ std::cout << "no uv coords\n";
+ }
+ for (unsigned int i = 0; i < mesh->mNumFaces; i++)
+ {
+ aiFace face = mesh->mFaces[i];
+ // retrieve all indices of the face and store them in the indices vector
+ for (unsigned int j = 0; j < face.mNumIndices; j++)
+ indices.push_back(face.mIndices[j]);
+ }
+
+ unsigned int vertexDataBufferSize = sizeof(float) * mesh->mNumVertices * 3;
+ unsigned int vertexNormalBufferSize = sizeof(float) * mesh->mNumVertices * 3;
+ unsigned int vertexTexBufferSize = sizeof(float) * mesh->mNumVertices * 2;
+ unsigned int vertexTangentBufferSize = sizeof(float) * mesh->mNumVertices * 3;
+ unsigned int vertexBiTangentBufferSize = sizeof(float) * mesh->mNumVertices * 3;
+
+ unsigned int vertexElementBufferSize = sizeof(unsigned int) * indices.size();
+ size = indices.size();
+
+ glGenVertexArrays(1, &vertexArray);
+ glBindVertexArray(vertexArray);
+
+
+ glGenBuffers(1, &vertexIndexBuffer);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vertexIndexBuffer);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, vertexElementBufferSize, &indices[0], GL_STATIC_DRAW);
+
+ glGenBuffers(1, &vertexBuffer);
+ glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
+ //std::cout << vertexBuffer;
+ glEnableVertexAttribArray(0);
+ glEnableVertexAttribArray(1);
+ glEnableVertexAttribArray(2);
+ glEnableVertexAttribArray(3);
+ glEnableVertexAttribArray(4);
+
+ glBufferData(GL_ARRAY_BUFFER, vertexDataBufferSize + vertexNormalBufferSize + vertexTexBufferSize + vertexTangentBufferSize + vertexBiTangentBufferSize, NULL, GL_STATIC_DRAW);
+
+ glBufferSubData(GL_ARRAY_BUFFER, 0, vertexDataBufferSize, mesh->mVertices);
+
+ glBufferSubData(GL_ARRAY_BUFFER, vertexDataBufferSize, vertexNormalBufferSize, mesh->mNormals);
+
+ glBufferSubData(GL_ARRAY_BUFFER, vertexDataBufferSize + vertexNormalBufferSize, vertexTexBufferSize, &textureCoord[0]);
+
+ glBufferSubData(GL_ARRAY_BUFFER, vertexDataBufferSize + vertexNormalBufferSize + vertexTexBufferSize, vertexTangentBufferSize, mesh->mTangents);
+
+ glBufferSubData(GL_ARRAY_BUFFER, vertexDataBufferSize + vertexNormalBufferSize + vertexTexBufferSize + vertexTangentBufferSize, vertexBiTangentBufferSize, mesh->mBitangents);
+
+ glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)(0));
+ glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (void*)(vertexDataBufferSize));
+ glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, (void*)(vertexNormalBufferSize + vertexDataBufferSize));
+ glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, 0, (void*)(vertexDataBufferSize + vertexNormalBufferSize + vertexTexBufferSize));
+ glVertexAttribPointer(4, 3, GL_FLOAT, GL_FALSE, 0, (void*)(vertexDataBufferSize + vertexNormalBufferSize + vertexTexBufferSize + vertexTangentBufferSize));
+
+}
+
+void Core::DrawVertexArray(const float * vertexArray, int numVertices, int elementSize )
+{
+ glVertexAttribPointer(0, elementSize, GL_FLOAT, false, 0, vertexArray);
+ glEnableVertexAttribArray(0);
+
+ glDrawArrays(GL_TRIANGLES, 0, numVertices);
+}
+
+void Core::DrawVertexArrayIndexed( const float * vertexArray, const int * indexArray, int numIndexes, int elementSize )
+{
+ glVertexAttribPointer(0, elementSize, GL_FLOAT, false, 0, vertexArray);
+ glEnableVertexAttribArray(0);
+
+ glDrawElements(GL_TRIANGLES, numIndexes, GL_UNSIGNED_INT, indexArray);
+}
+
+
+void Core::DrawVertexArray( const VertexData & data )
+{
+ int numAttribs = std::min(VertexData::MAX_ATTRIBS, data.NumActiveAttribs);
+ for(int i = 0; i < numAttribs; i++)
+ {
+ glVertexAttribPointer(i, data.Attribs[i].Size, GL_FLOAT, false, 0, data.Attribs[i].Pointer);
+ glEnableVertexAttribArray(i);
+ }
+ glDrawArrays(GL_TRIANGLES, 0, data.NumVertices);
+}
+
+void Core::DrawContext(Core::RenderContext& context)
+{
+
+ glBindVertexArray(context.vertexArray);
+ glDrawElements(
+ GL_TRIANGLES, // mode
+ context.size, // count
+ GL_UNSIGNED_INT, // type
+ (void*)0 // element array buffer offset
+ );
+ glBindVertexArray(0);
+}
diff --git a/cw_8/src/Render_Utils.h b/cw_8/src/Render_Utils.h
new file mode 100644
index 0000000..052008d
--- /dev/null
+++ b/cw_8/src/Render_Utils.h
@@ -0,0 +1,72 @@
+#pragma once
+#include "glm.hpp"
+#include "glew.h"
+#include "objload.h"
+#include
+#include
+#include
+
+#define BUFFER_OFFSET(i) ((char *)NULL + (i))
+
+namespace Core
+{
+ struct RenderContext
+ {
+ GLuint vertexArray;
+ GLuint vertexBuffer;
+ GLuint vertexIndexBuffer;
+ int size = 0;
+
+ void initFromOBJ(obj::Model& model);
+
+ void initFromAssimpMesh(aiMesh* mesh);
+ };
+
+ // vertexArray - jednowymiarowa tablica zawierajaca wartosci opisujace pozycje kolejnych wierzcholkow w jednym ciagu (x1, y1, z1, w1, x2, y2, z2, w2, ...)
+ // numVertices - liczba wierzcholkow do narysowania
+ // elementSize - liczba wartosci opisujacych pojedynczy wierzcholek (np. 3 gdy wierzcholek opisany jest trojka (x, y, z))
+ void DrawVertexArray(const float * vertexArray, int numVertices, int elementSize);
+
+ // indexArray - jednowymiarowa tablica zawierajaca indeksy wierzcholkow kolejnych trojkatow w jednym ciagu (t1_i1, t1_i2, t1_i3, t2_i1, t2_i2, t2_i3, ...)
+ // numIndexes - liczba indeksow w tablicy indexArray
+ void DrawVertexArrayIndexed(const float * vertexArray, const int * indexArray, int numIndexes, int elementSize);
+
+
+ struct VertexAttribute
+ {
+ const void * Pointer;
+ int Size;
+ };
+
+ struct VertexData
+ {
+ static const int MAX_ATTRIBS = 8;
+ VertexAttribute Attribs[MAX_ATTRIBS];
+ int NumActiveAttribs;
+ int NumVertices;
+ };
+
+ // Ta funkcja sluzy do rysowania trojkatow, ktorych wierzcholki moga byc opisane wiecej niz jednym atrybutem.
+ // Funkcja przyjmuje jako parametr strukture, w ktorej nalezy zawrzec wszystkie potrzebne dane.
+ //
+ // Przykladowe wywolanie funkcji - narysowanie trojkata jak na pierwszych zajeciach:
+ /*
+
+ const float vertices[] = {
+ 0.25f, 0.25f, 0.0f, 1.0f,
+ 0.25f, -0.25f, 0.0f, 1.0f,
+ -0.25f, -0.25f, 0.0f, 1.0f
+ };
+
+ Core::VertexData vertexData;
+ vertexData.NumActiveAttribs = 1; // Liczba uzywanych atrybutow wierzcholka
+ vertexData.Attribs[0].Pointer = vertices; // Wskaznik na dane zerowego atrybutu
+ vertexData.Attribs[0].Size = 4; // Wielkosc zerowego atrybutu (ilosc liczb opisujacych ten atrybut w pojedynczym wierzcholku)
+ vertexData.NumVertices = 3; // Liczba wierzcholkow do narysowania
+ Core::DrawVertexArray(vertexData);
+
+ */
+ void DrawVertexArray(const VertexData & data);
+
+ void DrawContext(RenderContext& context);
+}
\ No newline at end of file
diff --git a/cw_8/src/SOIL/SOIL.c b/cw_8/src/SOIL/SOIL.c
new file mode 100644
index 0000000..1ee4daf
--- /dev/null
+++ b/cw_8/src/SOIL/SOIL.c
@@ -0,0 +1,2024 @@
+/*
+ Jonathan Dummer
+ 2007-07-26-10.36
+
+ Simple OpenGL Image Library
+
+ Public Domain
+ using Sean Barret's stb_image as a base
+
+ Thanks to:
+ * Sean Barret - for the awesome stb_image
+ * Dan Venkitachalam - for finding some non-compliant DDS files, and patching some explicit casts
+ * everybody at gamedev.net
+*/
+
+#define SOIL_CHECK_FOR_GL_ERRORS 0
+
+#ifdef WIN32
+ #define WIN32_LEAN_AND_MEAN
+ #include
+ #include
+ #include
+#elif defined(__APPLE__) || defined(__APPLE_CC__)
+ /* I can't test this Apple stuff! */
+ #include
+ #include
+ #define APIENTRY
+#else
+ #include
+ #include
+#endif
+
+#include "SOIL.h"
+#include "stb_image_aug.h"
+#include "image_helper.h"
+#include "image_DXT.h"
+
+#include
+#include
+
+/* error reporting */
+char *result_string_pointer = "SOIL initialized";
+
+/* for loading cube maps */
+enum{
+ SOIL_CAPABILITY_UNKNOWN = -1,
+ SOIL_CAPABILITY_NONE = 0,
+ SOIL_CAPABILITY_PRESENT = 1
+};
+static int has_cubemap_capability = SOIL_CAPABILITY_UNKNOWN;
+int query_cubemap_capability( void );
+#define SOIL_TEXTURE_WRAP_R 0x8072
+#define SOIL_CLAMP_TO_EDGE 0x812F
+#define SOIL_NORMAL_MAP 0x8511
+#define SOIL_REFLECTION_MAP 0x8512
+#define SOIL_TEXTURE_CUBE_MAP 0x8513
+#define SOIL_TEXTURE_BINDING_CUBE_MAP 0x8514
+#define SOIL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515
+#define SOIL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516
+#define SOIL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517
+#define SOIL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518
+#define SOIL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519
+#define SOIL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A
+#define SOIL_PROXY_TEXTURE_CUBE_MAP 0x851B
+#define SOIL_MAX_CUBE_MAP_TEXTURE_SIZE 0x851C
+/* for non-power-of-two texture */
+static int has_NPOT_capability = SOIL_CAPABILITY_UNKNOWN;
+int query_NPOT_capability( void );
+/* for texture rectangles */
+static int has_tex_rectangle_capability = SOIL_CAPABILITY_UNKNOWN;
+int query_tex_rectangle_capability( void );
+#define SOIL_TEXTURE_RECTANGLE_ARB 0x84F5
+#define SOIL_MAX_RECTANGLE_TEXTURE_SIZE_ARB 0x84F8
+/* for using DXT compression */
+static int has_DXT_capability = SOIL_CAPABILITY_UNKNOWN;
+int query_DXT_capability( void );
+#define SOIL_RGB_S3TC_DXT1 0x83F0
+#define SOIL_RGBA_S3TC_DXT1 0x83F1
+#define SOIL_RGBA_S3TC_DXT3 0x83F2
+#define SOIL_RGBA_S3TC_DXT5 0x83F3
+typedef void (APIENTRY * P_SOIL_GLCOMPRESSEDTEXIMAGE2DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid * data);
+P_SOIL_GLCOMPRESSEDTEXIMAGE2DPROC soilGlCompressedTexImage2D = NULL;
+unsigned int SOIL_direct_load_DDS(
+ const char *filename,
+ unsigned int reuse_texture_ID,
+ int flags,
+ int loading_as_cubemap );
+unsigned int SOIL_direct_load_DDS_from_memory(
+ const unsigned char *const buffer,
+ int buffer_length,
+ unsigned int reuse_texture_ID,
+ int flags,
+ int loading_as_cubemap );
+/* other functions */
+unsigned int
+ SOIL_internal_create_OGL_texture
+ (
+ const unsigned char *const data,
+ int width, int height, int channels,
+ unsigned int reuse_texture_ID,
+ unsigned int flags,
+ unsigned int opengl_texture_type,
+ unsigned int opengl_texture_target,
+ unsigned int texture_check_size_enum
+ );
+
+/* and the code magic begins here [8^) */
+unsigned int
+ SOIL_load_OGL_texture
+ (
+ const char *filename,
+ int force_channels,
+ unsigned int reuse_texture_ID,
+ unsigned int flags
+ )
+{
+ /* variables */
+ unsigned char* img;
+ int width, height, channels;
+ unsigned int tex_id;
+ /* does the user want direct uploading of the image as a DDS file? */
+ if( flags & SOIL_FLAG_DDS_LOAD_DIRECT )
+ {
+ /* 1st try direct loading of the image as a DDS file
+ note: direct uploading will only load what is in the
+ DDS file, no MIPmaps will be generated, the image will
+ not be flipped, etc. */
+ tex_id = SOIL_direct_load_DDS( filename, reuse_texture_ID, flags, 0 );
+ if( tex_id )
+ {
+ /* hey, it worked!! */
+ return tex_id;
+ }
+ }
+ /* try to load the image */
+ img = SOIL_load_image( filename, &width, &height, &channels, force_channels );
+ /* channels holds the original number of channels, which may have been forced */
+ if( (force_channels >= 1) && (force_channels <= 4) )
+ {
+ channels = force_channels;
+ }
+ if( NULL == img )
+ {
+ /* image loading failed */
+ result_string_pointer = stbi_failure_reason();
+ return 0;
+ }
+ /* OK, make it a texture! */
+ tex_id = SOIL_internal_create_OGL_texture(
+ img, width, height, channels,
+ reuse_texture_ID, flags,
+ GL_TEXTURE_2D, GL_TEXTURE_2D,
+ GL_MAX_TEXTURE_SIZE );
+ /* and nuke the image data */
+ SOIL_free_image_data( img );
+ /* and return the handle, such as it is */
+ return tex_id;
+}
+
+unsigned int
+ SOIL_load_OGL_HDR_texture
+ (
+ const char *filename,
+ int fake_HDR_format,
+ int rescale_to_max,
+ unsigned int reuse_texture_ID,
+ unsigned int flags
+ )
+{
+ /* variables */
+ unsigned char* img;
+ int width, height, channels;
+ unsigned int tex_id;
+ /* no direct uploading of the image as a DDS file */
+ /* error check */
+ if( (fake_HDR_format != SOIL_HDR_RGBE) &&
+ (fake_HDR_format != SOIL_HDR_RGBdivA) &&
+ (fake_HDR_format != SOIL_HDR_RGBdivA2) )
+ {
+ result_string_pointer = "Invalid fake HDR format specified";
+ return 0;
+ }
+ /* try to load the image (only the HDR type) */
+ img = stbi_hdr_load_rgbe( filename, &width, &height, &channels, 4 );
+ /* channels holds the original number of channels, which may have been forced */
+ if( NULL == img )
+ {
+ /* image loading failed */
+ result_string_pointer = stbi_failure_reason();
+ return 0;
+ }
+ /* the load worked, do I need to convert it? */
+ if( fake_HDR_format == SOIL_HDR_RGBdivA )
+ {
+ RGBE_to_RGBdivA( img, width, height, rescale_to_max );
+ } else if( fake_HDR_format == SOIL_HDR_RGBdivA2 )
+ {
+ RGBE_to_RGBdivA2( img, width, height, rescale_to_max );
+ }
+ /* OK, make it a texture! */
+ tex_id = SOIL_internal_create_OGL_texture(
+ img, width, height, channels,
+ reuse_texture_ID, flags,
+ GL_TEXTURE_2D, GL_TEXTURE_2D,
+ GL_MAX_TEXTURE_SIZE );
+ /* and nuke the image data */
+ SOIL_free_image_data( img );
+ /* and return the handle, such as it is */
+ return tex_id;
+}
+
+unsigned int
+ SOIL_load_OGL_texture_from_memory
+ (
+ const unsigned char *const buffer,
+ int buffer_length,
+ int force_channels,
+ unsigned int reuse_texture_ID,
+ unsigned int flags
+ )
+{
+ /* variables */
+ unsigned char* img;
+ int width, height, channels;
+ unsigned int tex_id;
+ /* does the user want direct uploading of the image as a DDS file? */
+ if( flags & SOIL_FLAG_DDS_LOAD_DIRECT )
+ {
+ /* 1st try direct loading of the image as a DDS file
+ note: direct uploading will only load what is in the
+ DDS file, no MIPmaps will be generated, the image will
+ not be flipped, etc. */
+ tex_id = SOIL_direct_load_DDS_from_memory(
+ buffer, buffer_length,
+ reuse_texture_ID, flags, 0 );
+ if( tex_id )
+ {
+ /* hey, it worked!! */
+ return tex_id;
+ }
+ }
+ /* try to load the image */
+ img = SOIL_load_image_from_memory(
+ buffer, buffer_length,
+ &width, &height, &channels,
+ force_channels );
+ /* channels holds the original number of channels, which may have been forced */
+ if( (force_channels >= 1) && (force_channels <= 4) )
+ {
+ channels = force_channels;
+ }
+ if( NULL == img )
+ {
+ /* image loading failed */
+ result_string_pointer = stbi_failure_reason();
+ return 0;
+ }
+ /* OK, make it a texture! */
+ tex_id = SOIL_internal_create_OGL_texture(
+ img, width, height, channels,
+ reuse_texture_ID, flags,
+ GL_TEXTURE_2D, GL_TEXTURE_2D,
+ GL_MAX_TEXTURE_SIZE );
+ /* and nuke the image data */
+ SOIL_free_image_data( img );
+ /* and return the handle, such as it is */
+ return tex_id;
+}
+
+unsigned int
+ SOIL_load_OGL_cubemap
+ (
+ const char *x_pos_file,
+ const char *x_neg_file,
+ const char *y_pos_file,
+ const char *y_neg_file,
+ const char *z_pos_file,
+ const char *z_neg_file,
+ int force_channels,
+ unsigned int reuse_texture_ID,
+ unsigned int flags
+ )
+{
+ /* variables */
+ unsigned char* img;
+ int width, height, channels;
+ unsigned int tex_id;
+ /* error checking */
+ if( (x_pos_file == NULL) ||
+ (x_neg_file == NULL) ||
+ (y_pos_file == NULL) ||
+ (y_neg_file == NULL) ||
+ (z_pos_file == NULL) ||
+ (z_neg_file == NULL) )
+ {
+ result_string_pointer = "Invalid cube map files list";
+ return 0;
+ }
+ /* capability checking */
+ if( query_cubemap_capability() != SOIL_CAPABILITY_PRESENT )
+ {
+ result_string_pointer = "No cube map capability present";
+ return 0;
+ }
+ /* 1st face: try to load the image */
+ img = SOIL_load_image( x_pos_file, &width, &height, &channels, force_channels );
+ /* channels holds the original number of channels, which may have been forced */
+ if( (force_channels >= 1) && (force_channels <= 4) )
+ {
+ channels = force_channels;
+ }
+ if( NULL == img )
+ {
+ /* image loading failed */
+ result_string_pointer = stbi_failure_reason();
+ return 0;
+ }
+ /* upload the texture, and create a texture ID if necessary */
+ tex_id = SOIL_internal_create_OGL_texture(
+ img, width, height, channels,
+ reuse_texture_ID, flags,
+ SOIL_TEXTURE_CUBE_MAP, SOIL_TEXTURE_CUBE_MAP_POSITIVE_X,
+ SOIL_MAX_CUBE_MAP_TEXTURE_SIZE );
+ /* and nuke the image data */
+ SOIL_free_image_data( img );
+ /* continue? */
+ if( tex_id != 0 )
+ {
+ /* 1st face: try to load the image */
+ img = SOIL_load_image( x_neg_file, &width, &height, &channels, force_channels );
+ /* channels holds the original number of channels, which may have been forced */
+ if( (force_channels >= 1) && (force_channels <= 4) )
+ {
+ channels = force_channels;
+ }
+ if( NULL == img )
+ {
+ /* image loading failed */
+ result_string_pointer = stbi_failure_reason();
+ return 0;
+ }
+ /* upload the texture, but reuse the assigned texture ID */
+ tex_id = SOIL_internal_create_OGL_texture(
+ img, width, height, channels,
+ tex_id, flags,
+ SOIL_TEXTURE_CUBE_MAP, SOIL_TEXTURE_CUBE_MAP_NEGATIVE_X,
+ SOIL_MAX_CUBE_MAP_TEXTURE_SIZE );
+ /* and nuke the image data */
+ SOIL_free_image_data( img );
+ }
+ /* continue? */
+ if( tex_id != 0 )
+ {
+ /* 1st face: try to load the image */
+ img = SOIL_load_image( y_pos_file, &width, &height, &channels, force_channels );
+ /* channels holds the original number of channels, which may have been forced */
+ if( (force_channels >= 1) && (force_channels <= 4) )
+ {
+ channels = force_channels;
+ }
+ if( NULL == img )
+ {
+ /* image loading failed */
+ result_string_pointer = stbi_failure_reason();
+ return 0;
+ }
+ /* upload the texture, but reuse the assigned texture ID */
+ tex_id = SOIL_internal_create_OGL_texture(
+ img, width, height, channels,
+ tex_id, flags,
+ SOIL_TEXTURE_CUBE_MAP, SOIL_TEXTURE_CUBE_MAP_POSITIVE_Y,
+ SOIL_MAX_CUBE_MAP_TEXTURE_SIZE );
+ /* and nuke the image data */
+ SOIL_free_image_data( img );
+ }
+ /* continue? */
+ if( tex_id != 0 )
+ {
+ /* 1st face: try to load the image */
+ img = SOIL_load_image( y_neg_file, &width, &height, &channels, force_channels );
+ /* channels holds the original number of channels, which may have been forced */
+ if( (force_channels >= 1) && (force_channels <= 4) )
+ {
+ channels = force_channels;
+ }
+ if( NULL == img )
+ {
+ /* image loading failed */
+ result_string_pointer = stbi_failure_reason();
+ return 0;
+ }
+ /* upload the texture, but reuse the assigned texture ID */
+ tex_id = SOIL_internal_create_OGL_texture(
+ img, width, height, channels,
+ tex_id, flags,
+ SOIL_TEXTURE_CUBE_MAP, SOIL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
+ SOIL_MAX_CUBE_MAP_TEXTURE_SIZE );
+ /* and nuke the image data */
+ SOIL_free_image_data( img );
+ }
+ /* continue? */
+ if( tex_id != 0 )
+ {
+ /* 1st face: try to load the image */
+ img = SOIL_load_image( z_pos_file, &width, &height, &channels, force_channels );
+ /* channels holds the original number of channels, which may have been forced */
+ if( (force_channels >= 1) && (force_channels <= 4) )
+ {
+ channels = force_channels;
+ }
+ if( NULL == img )
+ {
+ /* image loading failed */
+ result_string_pointer = stbi_failure_reason();
+ return 0;
+ }
+ /* upload the texture, but reuse the assigned texture ID */
+ tex_id = SOIL_internal_create_OGL_texture(
+ img, width, height, channels,
+ tex_id, flags,
+ SOIL_TEXTURE_CUBE_MAP, SOIL_TEXTURE_CUBE_MAP_POSITIVE_Z,
+ SOIL_MAX_CUBE_MAP_TEXTURE_SIZE );
+ /* and nuke the image data */
+ SOIL_free_image_data( img );
+ }
+ /* continue? */
+ if( tex_id != 0 )
+ {
+ /* 1st face: try to load the image */
+ img = SOIL_load_image( z_neg_file, &width, &height, &channels, force_channels );
+ /* channels holds the original number of channels, which may have been forced */
+ if( (force_channels >= 1) && (force_channels <= 4) )
+ {
+ channels = force_channels;
+ }
+ if( NULL == img )
+ {
+ /* image loading failed */
+ result_string_pointer = stbi_failure_reason();
+ return 0;
+ }
+ /* upload the texture, but reuse the assigned texture ID */
+ tex_id = SOIL_internal_create_OGL_texture(
+ img, width, height, channels,
+ tex_id, flags,
+ SOIL_TEXTURE_CUBE_MAP, SOIL_TEXTURE_CUBE_MAP_NEGATIVE_Z,
+ SOIL_MAX_CUBE_MAP_TEXTURE_SIZE );
+ /* and nuke the image data */
+ SOIL_free_image_data( img );
+ }
+ /* and return the handle, such as it is */
+ return tex_id;
+}
+
+unsigned int
+ SOIL_load_OGL_cubemap_from_memory
+ (
+ const unsigned char *const x_pos_buffer,
+ int x_pos_buffer_length,
+ const unsigned char *const x_neg_buffer,
+ int x_neg_buffer_length,
+ const unsigned char *const y_pos_buffer,
+ int y_pos_buffer_length,
+ const unsigned char *const y_neg_buffer,
+ int y_neg_buffer_length,
+ const unsigned char *const z_pos_buffer,
+ int z_pos_buffer_length,
+ const unsigned char *const z_neg_buffer,
+ int z_neg_buffer_length,
+ int force_channels,
+ unsigned int reuse_texture_ID,
+ unsigned int flags
+ )
+{
+ /* variables */
+ unsigned char* img;
+ int width, height, channels;
+ unsigned int tex_id;
+ /* error checking */
+ if( (x_pos_buffer == NULL) ||
+ (x_neg_buffer == NULL) ||
+ (y_pos_buffer == NULL) ||
+ (y_neg_buffer == NULL) ||
+ (z_pos_buffer == NULL) ||
+ (z_neg_buffer == NULL) )
+ {
+ result_string_pointer = "Invalid cube map buffers list";
+ return 0;
+ }
+ /* capability checking */
+ if( query_cubemap_capability() != SOIL_CAPABILITY_PRESENT )
+ {
+ result_string_pointer = "No cube map capability present";
+ return 0;
+ }
+ /* 1st face: try to load the image */
+ img = SOIL_load_image_from_memory(
+ x_pos_buffer, x_pos_buffer_length,
+ &width, &height, &channels, force_channels );
+ /* channels holds the original number of channels, which may have been forced */
+ if( (force_channels >= 1) && (force_channels <= 4) )
+ {
+ channels = force_channels;
+ }
+ if( NULL == img )
+ {
+ /* image loading failed */
+ result_string_pointer = stbi_failure_reason();
+ return 0;
+ }
+ /* upload the texture, and create a texture ID if necessary */
+ tex_id = SOIL_internal_create_OGL_texture(
+ img, width, height, channels,
+ reuse_texture_ID, flags,
+ SOIL_TEXTURE_CUBE_MAP, SOIL_TEXTURE_CUBE_MAP_POSITIVE_X,
+ SOIL_MAX_CUBE_MAP_TEXTURE_SIZE );
+ /* and nuke the image data */
+ SOIL_free_image_data( img );
+ /* continue? */
+ if( tex_id != 0 )
+ {
+ /* 1st face: try to load the image */
+ img = SOIL_load_image_from_memory(
+ x_neg_buffer, x_neg_buffer_length,
+ &width, &height, &channels, force_channels );
+ /* channels holds the original number of channels, which may have been forced */
+ if( (force_channels >= 1) && (force_channels <= 4) )
+ {
+ channels = force_channels;
+ }
+ if( NULL == img )
+ {
+ /* image loading failed */
+ result_string_pointer = stbi_failure_reason();
+ return 0;
+ }
+ /* upload the texture, but reuse the assigned texture ID */
+ tex_id = SOIL_internal_create_OGL_texture(
+ img, width, height, channels,
+ tex_id, flags,
+ SOIL_TEXTURE_CUBE_MAP, SOIL_TEXTURE_CUBE_MAP_NEGATIVE_X,
+ SOIL_MAX_CUBE_MAP_TEXTURE_SIZE );
+ /* and nuke the image data */
+ SOIL_free_image_data( img );
+ }
+ /* continue? */
+ if( tex_id != 0 )
+ {
+ /* 1st face: try to load the image */
+ img = SOIL_load_image_from_memory(
+ y_pos_buffer, y_pos_buffer_length,
+ &width, &height, &channels, force_channels );
+ /* channels holds the original number of channels, which may have been forced */
+ if( (force_channels >= 1) && (force_channels <= 4) )
+ {
+ channels = force_channels;
+ }
+ if( NULL == img )
+ {
+ /* image loading failed */
+ result_string_pointer = stbi_failure_reason();
+ return 0;
+ }
+ /* upload the texture, but reuse the assigned texture ID */
+ tex_id = SOIL_internal_create_OGL_texture(
+ img, width, height, channels,
+ tex_id, flags,
+ SOIL_TEXTURE_CUBE_MAP, SOIL_TEXTURE_CUBE_MAP_POSITIVE_Y,
+ SOIL_MAX_CUBE_MAP_TEXTURE_SIZE );
+ /* and nuke the image data */
+ SOIL_free_image_data( img );
+ }
+ /* continue? */
+ if( tex_id != 0 )
+ {
+ /* 1st face: try to load the image */
+ img = SOIL_load_image_from_memory(
+ y_neg_buffer, y_neg_buffer_length,
+ &width, &height, &channels, force_channels );
+ /* channels holds the original number of channels, which may have been forced */
+ if( (force_channels >= 1) && (force_channels <= 4) )
+ {
+ channels = force_channels;
+ }
+ if( NULL == img )
+ {
+ /* image loading failed */
+ result_string_pointer = stbi_failure_reason();
+ return 0;
+ }
+ /* upload the texture, but reuse the assigned texture ID */
+ tex_id = SOIL_internal_create_OGL_texture(
+ img, width, height, channels,
+ tex_id, flags,
+ SOIL_TEXTURE_CUBE_MAP, SOIL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
+ SOIL_MAX_CUBE_MAP_TEXTURE_SIZE );
+ /* and nuke the image data */
+ SOIL_free_image_data( img );
+ }
+ /* continue? */
+ if( tex_id != 0 )
+ {
+ /* 1st face: try to load the image */
+ img = SOIL_load_image_from_memory(
+ z_pos_buffer, z_pos_buffer_length,
+ &width, &height, &channels, force_channels );
+ /* channels holds the original number of channels, which may have been forced */
+ if( (force_channels >= 1) && (force_channels <= 4) )
+ {
+ channels = force_channels;
+ }
+ if( NULL == img )
+ {
+ /* image loading failed */
+ result_string_pointer = stbi_failure_reason();
+ return 0;
+ }
+ /* upload the texture, but reuse the assigned texture ID */
+ tex_id = SOIL_internal_create_OGL_texture(
+ img, width, height, channels,
+ tex_id, flags,
+ SOIL_TEXTURE_CUBE_MAP, SOIL_TEXTURE_CUBE_MAP_POSITIVE_Z,
+ SOIL_MAX_CUBE_MAP_TEXTURE_SIZE );
+ /* and nuke the image data */
+ SOIL_free_image_data( img );
+ }
+ /* continue? */
+ if( tex_id != 0 )
+ {
+ /* 1st face: try to load the image */
+ img = SOIL_load_image_from_memory(
+ z_neg_buffer, z_neg_buffer_length,
+ &width, &height, &channels, force_channels );
+ /* channels holds the original number of channels, which may have been forced */
+ if( (force_channels >= 1) && (force_channels <= 4) )
+ {
+ channels = force_channels;
+ }
+ if( NULL == img )
+ {
+ /* image loading failed */
+ result_string_pointer = stbi_failure_reason();
+ return 0;
+ }
+ /* upload the texture, but reuse the assigned texture ID */
+ tex_id = SOIL_internal_create_OGL_texture(
+ img, width, height, channels,
+ tex_id, flags,
+ SOIL_TEXTURE_CUBE_MAP, SOIL_TEXTURE_CUBE_MAP_NEGATIVE_Z,
+ SOIL_MAX_CUBE_MAP_TEXTURE_SIZE );
+ /* and nuke the image data */
+ SOIL_free_image_data( img );
+ }
+ /* and return the handle, such as it is */
+ return tex_id;
+}
+
+unsigned int
+ SOIL_load_OGL_single_cubemap
+ (
+ const char *filename,
+ const char face_order[6],
+ int force_channels,
+ unsigned int reuse_texture_ID,
+ unsigned int flags
+ )
+{
+ /* variables */
+ unsigned char* img;
+ int width, height, channels, i;
+ unsigned int tex_id = 0;
+ /* error checking */
+ if( filename == NULL )
+ {
+ result_string_pointer = "Invalid single cube map file name";
+ return 0;
+ }
+ /* does the user want direct uploading of the image as a DDS file? */
+ if( flags & SOIL_FLAG_DDS_LOAD_DIRECT )
+ {
+ /* 1st try direct loading of the image as a DDS file
+ note: direct uploading will only load what is in the
+ DDS file, no MIPmaps will be generated, the image will
+ not be flipped, etc. */
+ tex_id = SOIL_direct_load_DDS( filename, reuse_texture_ID, flags, 1 );
+ if( tex_id )
+ {
+ /* hey, it worked!! */
+ return tex_id;
+ }
+ }
+ /* face order checking */
+ for( i = 0; i < 6; ++i )
+ {
+ if( (face_order[i] != 'N') &&
+ (face_order[i] != 'S') &&
+ (face_order[i] != 'W') &&
+ (face_order[i] != 'E') &&
+ (face_order[i] != 'U') &&
+ (face_order[i] != 'D') )
+ {
+ result_string_pointer = "Invalid single cube map face order";
+ return 0;
+ };
+ }
+ /* capability checking */
+ if( query_cubemap_capability() != SOIL_CAPABILITY_PRESENT )
+ {
+ result_string_pointer = "No cube map capability present";
+ return 0;
+ }
+ /* 1st off, try to load the full image */
+ img = SOIL_load_image( filename, &width, &height, &channels, force_channels );
+ /* channels holds the original number of channels, which may have been forced */
+ if( (force_channels >= 1) && (force_channels <= 4) )
+ {
+ channels = force_channels;
+ }
+ if( NULL == img )
+ {
+ /* image loading failed */
+ result_string_pointer = stbi_failure_reason();
+ return 0;
+ }
+ /* now, does this image have the right dimensions? */
+ if( (width != 6*height) &&
+ (6*width != height) )
+ {
+ SOIL_free_image_data( img );
+ result_string_pointer = "Single cubemap image must have a 6:1 ratio";
+ return 0;
+ }
+ /* try the image split and create */
+ tex_id = SOIL_create_OGL_single_cubemap(
+ img, width, height, channels,
+ face_order, reuse_texture_ID, flags
+ );
+ /* nuke the temporary image data and return the texture handle */
+ SOIL_free_image_data( img );
+ return tex_id;
+}
+
+unsigned int
+ SOIL_load_OGL_single_cubemap_from_memory
+ (
+ const unsigned char *const buffer,
+ int buffer_length,
+ const char face_order[6],
+ int force_channels,
+ unsigned int reuse_texture_ID,
+ unsigned int flags
+ )
+{
+ /* variables */
+ unsigned char* img;
+ int width, height, channels, i;
+ unsigned int tex_id = 0;
+ /* error checking */
+ if( buffer == NULL )
+ {
+ result_string_pointer = "Invalid single cube map buffer";
+ return 0;
+ }
+ /* does the user want direct uploading of the image as a DDS file? */
+ if( flags & SOIL_FLAG_DDS_LOAD_DIRECT )
+ {
+ /* 1st try direct loading of the image as a DDS file
+ note: direct uploading will only load what is in the
+ DDS file, no MIPmaps will be generated, the image will
+ not be flipped, etc. */
+ tex_id = SOIL_direct_load_DDS_from_memory(
+ buffer, buffer_length,
+ reuse_texture_ID, flags, 1 );
+ if( tex_id )
+ {
+ /* hey, it worked!! */
+ return tex_id;
+ }
+ }
+ /* face order checking */
+ for( i = 0; i < 6; ++i )
+ {
+ if( (face_order[i] != 'N') &&
+ (face_order[i] != 'S') &&
+ (face_order[i] != 'W') &&
+ (face_order[i] != 'E') &&
+ (face_order[i] != 'U') &&
+ (face_order[i] != 'D') )
+ {
+ result_string_pointer = "Invalid single cube map face order";
+ return 0;
+ };
+ }
+ /* capability checking */
+ if( query_cubemap_capability() != SOIL_CAPABILITY_PRESENT )
+ {
+ result_string_pointer = "No cube map capability present";
+ return 0;
+ }
+ /* 1st off, try to load the full image */
+ img = SOIL_load_image_from_memory(
+ buffer, buffer_length,
+ &width, &height, &channels,
+ force_channels );
+ /* channels holds the original number of channels, which may have been forced */
+ if( (force_channels >= 1) && (force_channels <= 4) )
+ {
+ channels = force_channels;
+ }
+ if( NULL == img )
+ {
+ /* image loading failed */
+ result_string_pointer = stbi_failure_reason();
+ return 0;
+ }
+ /* now, does this image have the right dimensions? */
+ if( (width != 6*height) &&
+ (6*width != height) )
+ {
+ SOIL_free_image_data( img );
+ result_string_pointer = "Single cubemap image must have a 6:1 ratio";
+ return 0;
+ }
+ /* try the image split and create */
+ tex_id = SOIL_create_OGL_single_cubemap(
+ img, width, height, channels,
+ face_order, reuse_texture_ID, flags
+ );
+ /* nuke the temporary image data and return the texture handle */
+ SOIL_free_image_data( img );
+ return tex_id;
+}
+
+unsigned int
+ SOIL_create_OGL_single_cubemap
+ (
+ const unsigned char *const data,
+ int width, int height, int channels,
+ const char face_order[6],
+ unsigned int reuse_texture_ID,
+ unsigned int flags
+ )
+{
+ /* variables */
+ unsigned char* sub_img;
+ int dw, dh, sz, i;
+ unsigned int tex_id;
+ /* error checking */
+ if( data == NULL )
+ {
+ result_string_pointer = "Invalid single cube map image data";
+ return 0;
+ }
+ /* face order checking */
+ for( i = 0; i < 6; ++i )
+ {
+ if( (face_order[i] != 'N') &&
+ (face_order[i] != 'S') &&
+ (face_order[i] != 'W') &&
+ (face_order[i] != 'E') &&
+ (face_order[i] != 'U') &&
+ (face_order[i] != 'D') )
+ {
+ result_string_pointer = "Invalid single cube map face order";
+ return 0;
+ };
+ }
+ /* capability checking */
+ if( query_cubemap_capability() != SOIL_CAPABILITY_PRESENT )
+ {
+ result_string_pointer = "No cube map capability present";
+ return 0;
+ }
+ /* now, does this image have the right dimensions? */
+ if( (width != 6*height) &&
+ (6*width != height) )
+ {
+ result_string_pointer = "Single cubemap image must have a 6:1 ratio";
+ return 0;
+ }
+ /* which way am I stepping? */
+ if( width > height )
+ {
+ dw = height;
+ dh = 0;
+ } else
+ {
+ dw = 0;
+ dh = width;
+ }
+ sz = dw+dh;
+ sub_img = (unsigned char *)malloc( sz*sz*channels );
+ /* do the splitting and uploading */
+ tex_id = reuse_texture_ID;
+ for( i = 0; i < 6; ++i )
+ {
+ int x, y, idx = 0;
+ unsigned int cubemap_target = 0;
+ /* copy in the sub-image */
+ for( y = i*dh; y < i*dh+sz; ++y )
+ {
+ for( x = i*dw*channels; x < (i*dw+sz)*channels; ++x )
+ {
+ sub_img[idx++] = data[y*width*channels+x];
+ }
+ }
+ /* what is my texture target?
+ remember, this coordinate system is
+ LHS if viewed from inside the cube! */
+ switch( face_order[i] )
+ {
+ case 'N':
+ cubemap_target = SOIL_TEXTURE_CUBE_MAP_POSITIVE_Z;
+ break;
+ case 'S':
+ cubemap_target = SOIL_TEXTURE_CUBE_MAP_NEGATIVE_Z;
+ break;
+ case 'W':
+ cubemap_target = SOIL_TEXTURE_CUBE_MAP_NEGATIVE_X;
+ break;
+ case 'E':
+ cubemap_target = SOIL_TEXTURE_CUBE_MAP_POSITIVE_X;
+ break;
+ case 'U':
+ cubemap_target = SOIL_TEXTURE_CUBE_MAP_POSITIVE_Y;
+ break;
+ case 'D':
+ cubemap_target = SOIL_TEXTURE_CUBE_MAP_NEGATIVE_Y;
+ break;
+ }
+ /* upload it as a texture */
+ tex_id = SOIL_internal_create_OGL_texture(
+ sub_img, sz, sz, channels,
+ tex_id, flags,
+ SOIL_TEXTURE_CUBE_MAP,
+ cubemap_target,
+ SOIL_MAX_CUBE_MAP_TEXTURE_SIZE );
+ }
+ /* and nuke the image and sub-image data */
+ SOIL_free_image_data( sub_img );
+ /* and return the handle, such as it is */
+ return tex_id;
+}
+
+unsigned int
+ SOIL_create_OGL_texture
+ (
+ const unsigned char *const data,
+ int width, int height, int channels,
+ unsigned int reuse_texture_ID,
+ unsigned int flags
+ )
+{
+ /* wrapper function for 2D textures */
+ return SOIL_internal_create_OGL_texture(
+ data, width, height, channels,
+ reuse_texture_ID, flags,
+ GL_TEXTURE_2D, GL_TEXTURE_2D,
+ GL_MAX_TEXTURE_SIZE );
+}
+
+#if SOIL_CHECK_FOR_GL_ERRORS
+void check_for_GL_errors( const char *calling_location )
+{
+ /* check for errors */
+ GLenum err_code = glGetError();
+ while( GL_NO_ERROR != err_code )
+ {
+ printf( "OpenGL Error @ %s: %i", calling_location, err_code );
+ err_code = glGetError();
+ }
+}
+#else
+void check_for_GL_errors( const char *calling_location )
+{
+ /* no check for errors */
+}
+#endif
+
+unsigned int
+ SOIL_internal_create_OGL_texture
+ (
+ const unsigned char *const data,
+ int width, int height, int channels,
+ unsigned int reuse_texture_ID,
+ unsigned int flags,
+ unsigned int opengl_texture_type,
+ unsigned int opengl_texture_target,
+ unsigned int texture_check_size_enum
+ )
+{
+ /* variables */
+ unsigned char* img;
+ unsigned int tex_id;
+ unsigned int internal_texture_format = 0, original_texture_format = 0;
+ int DXT_mode = SOIL_CAPABILITY_UNKNOWN;
+ int max_supported_size;
+ /* If the user wants to use the texture rectangle I kill a few flags */
+ if( flags & SOIL_FLAG_TEXTURE_RECTANGLE )
+ {
+ /* well, the user asked for it, can we do that? */
+ if( query_tex_rectangle_capability() == SOIL_CAPABILITY_PRESENT )
+ {
+ /* only allow this if the user in _NOT_ trying to do a cubemap! */
+ if( opengl_texture_type == GL_TEXTURE_2D )
+ {
+ /* clean out the flags that cannot be used with texture rectangles */
+ flags &= ~(
+ SOIL_FLAG_POWER_OF_TWO | SOIL_FLAG_MIPMAPS |
+ SOIL_FLAG_TEXTURE_REPEATS
+ );
+ /* and change my target */
+ opengl_texture_target = SOIL_TEXTURE_RECTANGLE_ARB;
+ opengl_texture_type = SOIL_TEXTURE_RECTANGLE_ARB;
+ } else
+ {
+ /* not allowed for any other uses (yes, I'm looking at you, cubemaps!) */
+ flags &= ~SOIL_FLAG_TEXTURE_RECTANGLE;
+ }
+
+ } else
+ {
+ /* can't do it, and that is a breakable offense (uv coords use pixels instead of [0,1]!) */
+ result_string_pointer = "Texture Rectangle extension unsupported";
+ return 0;
+ }
+ }
+ /* create a copy the image data */
+ img = (unsigned char*)malloc( width*height*channels );
+ memcpy( img, data, width*height*channels );
+ /* does the user want me to invert the image? */
+ if( flags & SOIL_FLAG_INVERT_Y )
+ {
+ int i, j;
+ for( j = 0; j*2 < height; ++j )
+ {
+ int index1 = j * width * channels;
+ int index2 = (height - 1 - j) * width * channels;
+ for( i = width * channels; i > 0; --i )
+ {
+ unsigned char temp = img[index1];
+ img[index1] = img[index2];
+ img[index2] = temp;
+ ++index1;
+ ++index2;
+ }
+ }
+ }
+ /* does the user want me to scale the colors into the NTSC safe RGB range? */
+ if( flags & SOIL_FLAG_NTSC_SAFE_RGB )
+ {
+ scale_image_RGB_to_NTSC_safe( img, width, height, channels );
+ }
+ /* does the user want me to convert from straight to pre-multiplied alpha?
+ (and do we even _have_ alpha?) */
+ if( flags & SOIL_FLAG_MULTIPLY_ALPHA )
+ {
+ int i;
+ switch( channels )
+ {
+ case 2:
+ for( i = 0; i < 2*width*height; i += 2 )
+ {
+ img[i] = (img[i] * img[i+1] + 128) >> 8;
+ }
+ break;
+ case 4:
+ for( i = 0; i < 4*width*height; i += 4 )
+ {
+ img[i+0] = (img[i+0] * img[i+3] + 128) >> 8;
+ img[i+1] = (img[i+1] * img[i+3] + 128) >> 8;
+ img[i+2] = (img[i+2] * img[i+3] + 128) >> 8;
+ }
+ break;
+ default:
+ /* no other number of channels contains alpha data */
+ break;
+ }
+ }
+ /* if the user can't support NPOT textures, make sure we force the POT option */
+ if( (query_NPOT_capability() == SOIL_CAPABILITY_NONE) &&
+ !(flags & SOIL_FLAG_TEXTURE_RECTANGLE) )
+ {
+ /* add in the POT flag */
+ flags |= SOIL_FLAG_POWER_OF_TWO;
+ }
+ /* how large of a texture can this OpenGL implementation handle? */
+ /* texture_check_size_enum will be GL_MAX_TEXTURE_SIZE or SOIL_MAX_CUBE_MAP_TEXTURE_SIZE */
+ glGetIntegerv( texture_check_size_enum, &max_supported_size );
+ /* do I need to make it a power of 2? */
+ if(
+ (flags & SOIL_FLAG_POWER_OF_TWO) || /* user asked for it */
+ (flags & SOIL_FLAG_MIPMAPS) || /* need it for the MIP-maps */
+ (width > max_supported_size) || /* it's too big, (make sure it's */
+ (height > max_supported_size) ) /* 2^n for later down-sampling) */
+ {
+ int new_width = 1;
+ int new_height = 1;
+ while( new_width < width )
+ {
+ new_width *= 2;
+ }
+ while( new_height < height )
+ {
+ new_height *= 2;
+ }
+ /* still? */
+ if( (new_width != width) || (new_height != height) )
+ {
+ /* yep, resize */
+ unsigned char *resampled = (unsigned char*)malloc( channels*new_width*new_height );
+ up_scale_image(
+ img, width, height, channels,
+ resampled, new_width, new_height );
+ /* OJO this is for debug only! */
+ /*
+ SOIL_save_image( "\\showme.bmp", SOIL_SAVE_TYPE_BMP,
+ new_width, new_height, channels,
+ resampled );
+ */
+ /* nuke the old guy, then point it at the new guy */
+ SOIL_free_image_data( img );
+ img = resampled;
+ width = new_width;
+ height = new_height;
+ }
+ }
+ /* now, if it is too large... */
+ if( (width > max_supported_size) || (height > max_supported_size) )
+ {
+ /* I've already made it a power of two, so simply use the MIPmapping
+ code to reduce its size to the allowable maximum. */
+ unsigned char *resampled;
+ int reduce_block_x = 1, reduce_block_y = 1;
+ int new_width, new_height;
+ if( width > max_supported_size )
+ {
+ reduce_block_x = width / max_supported_size;
+ }
+ if( height > max_supported_size )
+ {
+ reduce_block_y = height / max_supported_size;
+ }
+ new_width = width / reduce_block_x;
+ new_height = height / reduce_block_y;
+ resampled = (unsigned char*)malloc( channels*new_width*new_height );
+ /* perform the actual reduction */
+ mipmap_image( img, width, height, channels,
+ resampled, reduce_block_x, reduce_block_y );
+ /* nuke the old guy, then point it at the new guy */
+ SOIL_free_image_data( img );
+ img = resampled;
+ width = new_width;
+ height = new_height;
+ }
+ /* does the user want us to use YCoCg color space? */
+ if( flags & SOIL_FLAG_CoCg_Y )
+ {
+ /* this will only work with RGB and RGBA images */
+ convert_RGB_to_YCoCg( img, width, height, channels );
+ /*
+ save_image_as_DDS( "CoCg_Y.dds", width, height, channels, img );
+ */
+ }
+ /* create the OpenGL texture ID handle
+ (note: allowing a forced texture ID lets me reload a texture) */
+ tex_id = reuse_texture_ID;
+ if( tex_id == 0 )
+ {
+ glGenTextures( 1, &tex_id );
+ }
+ check_for_GL_errors( "glGenTextures" );
+ /* Note: sometimes glGenTextures fails (usually no OpenGL context) */
+ if( tex_id )
+ {
+ /* and what type am I using as the internal texture format? */
+ switch( channels )
+ {
+ case 1:
+ original_texture_format = GL_LUMINANCE;
+ break;
+ case 2:
+ original_texture_format = GL_LUMINANCE_ALPHA;
+ break;
+ case 3:
+ original_texture_format = GL_RGB;
+ break;
+ case 4:
+ original_texture_format = GL_RGBA;
+ break;
+ }
+ internal_texture_format = original_texture_format;
+ /* does the user want me to, and can I, save as DXT? */
+ if( flags & SOIL_FLAG_COMPRESS_TO_DXT )
+ {
+ DXT_mode = query_DXT_capability();
+ if( DXT_mode == SOIL_CAPABILITY_PRESENT )
+ {
+ /* I can use DXT, whether I compress it or OpenGL does */
+ if( (channels & 1) == 1 )
+ {
+ /* 1 or 3 channels = DXT1 */
+ internal_texture_format = SOIL_RGB_S3TC_DXT1;
+ } else
+ {
+ /* 2 or 4 channels = DXT5 */
+ internal_texture_format = SOIL_RGBA_S3TC_DXT5;
+ }
+ }
+ }
+ /* bind an OpenGL texture ID */
+ glBindTexture( opengl_texture_type, tex_id );
+ check_for_GL_errors( "glBindTexture" );
+ /* upload the main image */
+ if( DXT_mode == SOIL_CAPABILITY_PRESENT )
+ {
+ /* user wants me to do the DXT conversion! */
+ int DDS_size;
+ unsigned char *DDS_data = NULL;
+ if( (channels & 1) == 1 )
+ {
+ /* RGB, use DXT1 */
+ DDS_data = convert_image_to_DXT1( img, width, height, channels, &DDS_size );
+ } else
+ {
+ /* RGBA, use DXT5 */
+ DDS_data = convert_image_to_DXT5( img, width, height, channels, &DDS_size );
+ }
+ if( DDS_data )
+ {
+ soilGlCompressedTexImage2D(
+ opengl_texture_target, 0,
+ internal_texture_format, width, height, 0,
+ DDS_size, DDS_data );
+ check_for_GL_errors( "glCompressedTexImage2D" );
+ SOIL_free_image_data( DDS_data );
+ /* printf( "Internal DXT compressor\n" ); */
+ } else
+ {
+ /* my compression failed, try the OpenGL driver's version */
+ glTexImage2D(
+ opengl_texture_target, 0,
+ internal_texture_format, width, height, 0,
+ original_texture_format, GL_UNSIGNED_BYTE, img );
+ check_for_GL_errors( "glTexImage2D" );
+ /* printf( "OpenGL DXT compressor\n" ); */
+ }
+ } else
+ {
+ /* user want OpenGL to do all the work! */
+ glTexImage2D(
+ opengl_texture_target, 0,
+ internal_texture_format, width, height, 0,
+ original_texture_format, GL_UNSIGNED_BYTE, img );
+ check_for_GL_errors( "glTexImage2D" );
+ /*printf( "OpenGL DXT compressor\n" ); */
+ }
+ /* are any MIPmaps desired? */
+ if( flags & SOIL_FLAG_MIPMAPS )
+ {
+ int MIPlevel = 1;
+ int MIPwidth = (width+1) / 2;
+ int MIPheight = (height+1) / 2;
+ unsigned char *resampled = (unsigned char*)malloc( channels*MIPwidth*MIPheight );
+ while( ((1< 0; --i )
+ {
+ unsigned char temp = pixel_data[index1];
+ pixel_data[index1] = pixel_data[index2];
+ pixel_data[index2] = temp;
+ ++index1;
+ ++index2;
+ }
+ }
+
+ /* save the image */
+ save_result = SOIL_save_image( filename, image_type, width, height, 3, pixel_data);
+
+ /* And free the memory */
+ SOIL_free_image_data( pixel_data );
+ return save_result;
+}
+
+unsigned char*
+ SOIL_load_image
+ (
+ const char *filename,
+ int *width, int *height, int *channels,
+ int force_channels
+ )
+{
+ unsigned char *result = stbi_load( filename,
+ width, height, channels, force_channels );
+ if( result == NULL )
+ {
+ result_string_pointer = stbi_failure_reason();
+ } else
+ {
+ result_string_pointer = "Image loaded";
+ }
+ return result;
+}
+
+unsigned char*
+ SOIL_load_image_from_memory
+ (
+ const unsigned char *const buffer,
+ int buffer_length,
+ int *width, int *height, int *channels,
+ int force_channels
+ )
+{
+ unsigned char *result = stbi_load_from_memory(
+ buffer, buffer_length,
+ width, height, channels,
+ force_channels );
+ if( result == NULL )
+ {
+ result_string_pointer = stbi_failure_reason();
+ } else
+ {
+ result_string_pointer = "Image loaded from memory";
+ }
+ return result;
+}
+
+int
+ SOIL_save_image
+ (
+ const char *filename,
+ int image_type,
+ int width, int height, int channels,
+ const unsigned char *const data
+ )
+{
+ int save_result;
+
+ /* error check */
+ if( (width < 1) || (height < 1) ||
+ (channels < 1) || (channels > 4) ||
+ (data == NULL) ||
+ (filename == NULL) )
+ {
+ return 0;
+ }
+ if( image_type == SOIL_SAVE_TYPE_BMP )
+ {
+ save_result = stbi_write_bmp( filename,
+ width, height, channels, (void*)data );
+ } else
+ if( image_type == SOIL_SAVE_TYPE_TGA )
+ {
+ save_result = stbi_write_tga( filename,
+ width, height, channels, (void*)data );
+ } else
+ if( image_type == SOIL_SAVE_TYPE_DDS )
+ {
+ save_result = save_image_as_DDS( filename,
+ width, height, channels, (const unsigned char *const)data );
+ } else
+ {
+ save_result = 0;
+ }
+ if( save_result == 0 )
+ {
+ result_string_pointer = "Saving the image failed";
+ } else
+ {
+ result_string_pointer = "Image saved";
+ }
+ return save_result;
+}
+
+void
+ SOIL_free_image_data
+ (
+ unsigned char *img_data
+ )
+{
+ free( (void*)img_data );
+}
+
+const char*
+ SOIL_last_result
+ (
+ void
+ )
+{
+ return result_string_pointer;
+}
+
+unsigned int SOIL_direct_load_DDS_from_memory(
+ const unsigned char *const buffer,
+ int buffer_length,
+ unsigned int reuse_texture_ID,
+ int flags,
+ int loading_as_cubemap )
+{
+ /* variables */
+ DDS_header header;
+ unsigned int buffer_index = 0;
+ unsigned int tex_ID = 0;
+ /* file reading variables */
+ unsigned int S3TC_type = 0;
+ unsigned char *DDS_data;
+ unsigned int DDS_main_size;
+ unsigned int DDS_full_size;
+ unsigned int width, height;
+ int mipmaps, cubemap, uncompressed, block_size = 16;
+ unsigned int flag;
+ unsigned int cf_target, ogl_target_start, ogl_target_end;
+ unsigned int opengl_texture_type;
+ int i;
+ /* 1st off, does the filename even exist? */
+ if( NULL == buffer )
+ {
+ /* we can't do it! */
+ result_string_pointer = "NULL buffer";
+ return 0;
+ }
+ if( buffer_length < sizeof( DDS_header ) )
+ {
+ /* we can't do it! */
+ result_string_pointer = "DDS file was too small to contain the DDS header";
+ return 0;
+ }
+ /* try reading in the header */
+ memcpy ( (void*)(&header), (const void *)buffer, sizeof( DDS_header ) );
+ buffer_index = sizeof( DDS_header );
+ /* guilty until proven innocent */
+ result_string_pointer = "Failed to read a known DDS header";
+ /* validate the header (warning, "goto"'s ahead, shield your eyes!!) */
+ flag = ('D'<<0)|('D'<<8)|('S'<<16)|(' '<<24);
+ if( header.dwMagic != flag ) {goto quick_exit;}
+ if( header.dwSize != 124 ) {goto quick_exit;}
+ /* I need all of these */
+ flag = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT;
+ if( (header.dwFlags & flag) != flag ) {goto quick_exit;}
+ /* According to the MSDN spec, the dwFlags should contain
+ DDSD_LINEARSIZE if it's compressed, or DDSD_PITCH if
+ uncompressed. Some DDS writers do not conform to the
+ spec, so I need to make my reader more tolerant */
+ /* I need one of these */
+ flag = DDPF_FOURCC | DDPF_RGB;
+ if( (header.sPixelFormat.dwFlags & flag) == 0 ) {goto quick_exit;}
+ if( header.sPixelFormat.dwSize != 32 ) {goto quick_exit;}
+ if( (header.sCaps.dwCaps1 & DDSCAPS_TEXTURE) == 0 ) {goto quick_exit;}
+ /* make sure it is a type we can upload */
+ if( (header.sPixelFormat.dwFlags & DDPF_FOURCC) &&
+ !(
+ (header.sPixelFormat.dwFourCC == (('D'<<0)|('X'<<8)|('T'<<16)|('1'<<24))) ||
+ (header.sPixelFormat.dwFourCC == (('D'<<0)|('X'<<8)|('T'<<16)|('3'<<24))) ||
+ (header.sPixelFormat.dwFourCC == (('D'<<0)|('X'<<8)|('T'<<16)|('5'<<24)))
+ ) )
+ {
+ goto quick_exit;
+ }
+ /* OK, validated the header, let's load the image data */
+ result_string_pointer = "DDS header loaded and validated";
+ width = header.dwWidth;
+ height = header.dwHeight;
+ uncompressed = 1 - (header.sPixelFormat.dwFlags & DDPF_FOURCC) / DDPF_FOURCC;
+ cubemap = (header.sCaps.dwCaps2 & DDSCAPS2_CUBEMAP) / DDSCAPS2_CUBEMAP;
+ if( uncompressed )
+ {
+ S3TC_type = GL_RGB;
+ block_size = 3;
+ if( header.sPixelFormat.dwFlags & DDPF_ALPHAPIXELS )
+ {
+ S3TC_type = GL_RGBA;
+ block_size = 4;
+ }
+ DDS_main_size = width * height * block_size;
+ } else
+ {
+ /* can we even handle direct uploading to OpenGL DXT compressed images? */
+ if( query_DXT_capability() != SOIL_CAPABILITY_PRESENT )
+ {
+ /* we can't do it! */
+ result_string_pointer = "Direct upload of S3TC images not supported by the OpenGL driver";
+ return 0;
+ }
+ /* well, we know it is DXT1/3/5, because we checked above */
+ switch( (header.sPixelFormat.dwFourCC >> 24) - '0' )
+ {
+ case 1:
+ S3TC_type = SOIL_RGBA_S3TC_DXT1;
+ block_size = 8;
+ break;
+ case 3:
+ S3TC_type = SOIL_RGBA_S3TC_DXT3;
+ block_size = 16;
+ break;
+ case 5:
+ S3TC_type = SOIL_RGBA_S3TC_DXT5;
+ block_size = 16;
+ break;
+ }
+ DDS_main_size = ((width+3)>>2)*((height+3)>>2)*block_size;
+ }
+ if( cubemap )
+ {
+ /* does the user want a cubemap? */
+ if( !loading_as_cubemap )
+ {
+ /* we can't do it! */
+ result_string_pointer = "DDS image was a cubemap";
+ return 0;
+ }
+ /* can we even handle cubemaps with the OpenGL driver? */
+ if( query_cubemap_capability() != SOIL_CAPABILITY_PRESENT )
+ {
+ /* we can't do it! */
+ result_string_pointer = "Direct upload of cubemap images not supported by the OpenGL driver";
+ return 0;
+ }
+ ogl_target_start = SOIL_TEXTURE_CUBE_MAP_POSITIVE_X;
+ ogl_target_end = SOIL_TEXTURE_CUBE_MAP_NEGATIVE_Z;
+ opengl_texture_type = SOIL_TEXTURE_CUBE_MAP;
+ } else
+ {
+ /* does the user want a non-cubemap? */
+ if( loading_as_cubemap )
+ {
+ /* we can't do it! */
+ result_string_pointer = "DDS image was not a cubemap";
+ return 0;
+ }
+ ogl_target_start = GL_TEXTURE_2D;
+ ogl_target_end = GL_TEXTURE_2D;
+ opengl_texture_type = GL_TEXTURE_2D;
+ }
+ if( (header.sCaps.dwCaps1 & DDSCAPS_MIPMAP) && (header.dwMipMapCount > 1) )
+ {
+ int shift_offset;
+ mipmaps = header.dwMipMapCount - 1;
+ DDS_full_size = DDS_main_size;
+ if( uncompressed )
+ {
+ /* uncompressed DDS, simple MIPmap size calculation */
+ shift_offset = 0;
+ } else
+ {
+ /* compressed DDS, MIPmap size calculation is block based */
+ shift_offset = 2;
+ }
+ for( i = 1; i <= mipmaps; ++ i )
+ {
+ int w, h;
+ w = width >> (shift_offset + i);
+ h = height >> (shift_offset + i);
+ if( w < 1 )
+ {
+ w = 1;
+ }
+ if( h < 1 )
+ {
+ h = 1;
+ }
+ DDS_full_size += w*h*block_size;
+ }
+ } else
+ {
+ mipmaps = 0;
+ DDS_full_size = DDS_main_size;
+ }
+ DDS_data = (unsigned char*)malloc( DDS_full_size );
+ /* got the image data RAM, create or use an existing OpenGL texture handle */
+ tex_ID = reuse_texture_ID;
+ if( tex_ID == 0 )
+ {
+ glGenTextures( 1, &tex_ID );
+ }
+ /* bind an OpenGL texture ID */
+ glBindTexture( opengl_texture_type, tex_ID );
+ /* do this for each face of the cubemap! */
+ for( cf_target = ogl_target_start; cf_target <= ogl_target_end; ++cf_target )
+ {
+ if( buffer_index + DDS_full_size <= buffer_length )
+ {
+ unsigned int byte_offset = DDS_main_size;
+ memcpy( (void*)DDS_data, (const void*)(&buffer[buffer_index]), DDS_full_size );
+ buffer_index += DDS_full_size;
+ /* upload the main chunk */
+ if( uncompressed )
+ {
+ /* and remember, DXT uncompressed uses BGR(A),
+ so swap to RGB(A) for ALL MIPmap levels */
+ for( i = 0; i < DDS_full_size; i += block_size )
+ {
+ unsigned char temp = DDS_data[i];
+ DDS_data[i] = DDS_data[i+2];
+ DDS_data[i+2] = temp;
+ }
+ glTexImage2D(
+ cf_target, 0,
+ S3TC_type, width, height, 0,
+ S3TC_type, GL_UNSIGNED_BYTE, DDS_data );
+ } else
+ {
+ soilGlCompressedTexImage2D(
+ cf_target, 0,
+ S3TC_type, width, height, 0,
+ DDS_main_size, DDS_data );
+ }
+ /* upload the mipmaps, if we have them */
+ for( i = 1; i <= mipmaps; ++i )
+ {
+ int w, h, mip_size;
+ w = width >> i;
+ h = height >> i;
+ if( w < 1 )
+ {
+ w = 1;
+ }
+ if( h < 1 )
+ {
+ h = 1;
+ }
+ /* upload this mipmap */
+ if( uncompressed )
+ {
+ mip_size = w*h*block_size;
+ glTexImage2D(
+ cf_target, i,
+ S3TC_type, w, h, 0,
+ S3TC_type, GL_UNSIGNED_BYTE, &DDS_data[byte_offset] );
+ } else
+ {
+ mip_size = ((w+3)/4)*((h+3)/4)*block_size;
+ soilGlCompressedTexImage2D(
+ cf_target, i,
+ S3TC_type, w, h, 0,
+ mip_size, &DDS_data[byte_offset] );
+ }
+ /* and move to the next mipmap */
+ byte_offset += mip_size;
+ }
+ /* it worked! */
+ result_string_pointer = "DDS file loaded";
+ } else
+ {
+ glDeleteTextures( 1, & tex_ID );
+ tex_ID = 0;
+ cf_target = ogl_target_end + 1;
+ result_string_pointer = "DDS file was too small for expected image data";
+ }
+ }/* end reading each face */
+ SOIL_free_image_data( DDS_data );
+ if( tex_ID )
+ {
+ /* did I have MIPmaps? */
+ if( mipmaps > 0 )
+ {
+ /* instruct OpenGL to use the MIPmaps */
+ glTexParameteri( opengl_texture_type, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
+ glTexParameteri( opengl_texture_type, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
+ } else
+ {
+ /* instruct OpenGL _NOT_ to use the MIPmaps */
+ glTexParameteri( opengl_texture_type, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
+ glTexParameteri( opengl_texture_type, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
+ }
+ /* does the user want clamping, or wrapping? */
+ if( flags & SOIL_FLAG_TEXTURE_REPEATS )
+ {
+ glTexParameteri( opengl_texture_type, GL_TEXTURE_WRAP_S, GL_REPEAT );
+ glTexParameteri( opengl_texture_type, GL_TEXTURE_WRAP_T, GL_REPEAT );
+ glTexParameteri( opengl_texture_type, SOIL_TEXTURE_WRAP_R, GL_REPEAT );
+ } else
+ {
+ /* unsigned int clamp_mode = SOIL_CLAMP_TO_EDGE; */
+ unsigned int clamp_mode = GL_CLAMP;
+ glTexParameteri( opengl_texture_type, GL_TEXTURE_WRAP_S, clamp_mode );
+ glTexParameteri( opengl_texture_type, GL_TEXTURE_WRAP_T, clamp_mode );
+ glTexParameteri( opengl_texture_type, SOIL_TEXTURE_WRAP_R, clamp_mode );
+ }
+ }
+
+quick_exit:
+ /* report success or failure */
+ return tex_ID;
+}
+
+unsigned int SOIL_direct_load_DDS(
+ const char *filename,
+ unsigned int reuse_texture_ID,
+ int flags,
+ int loading_as_cubemap )
+{
+ FILE *f;
+ unsigned char *buffer;
+ size_t buffer_length, bytes_read;
+ unsigned int tex_ID = 0;
+ /* error checks */
+ if( NULL == filename )
+ {
+ result_string_pointer = "NULL filename";
+ return 0;
+ }
+ f = fopen( filename, "rb" );
+ if( NULL == f )
+ {
+ /* the file doesn't seem to exist (or be open-able) */
+ result_string_pointer = "Can not find DDS file";
+ return 0;
+ }
+ fseek( f, 0, SEEK_END );
+ buffer_length = ftell( f );
+ fseek( f, 0, SEEK_SET );
+ buffer = (unsigned char *) malloc( buffer_length );
+ if( NULL == buffer )
+ {
+ result_string_pointer = "malloc failed";
+ fclose( f );
+ return 0;
+ }
+ bytes_read = fread( (void*)buffer, 1, buffer_length, f );
+ fclose( f );
+ if( bytes_read < buffer_length )
+ {
+ /* huh? */
+ buffer_length = bytes_read;
+ }
+ /* now try to do the loading */
+ tex_ID = SOIL_direct_load_DDS_from_memory(
+ (const unsigned char *const)buffer, buffer_length,
+ reuse_texture_ID, flags, loading_as_cubemap );
+ SOIL_free_image_data( buffer );
+ return tex_ID;
+}
+
+int query_NPOT_capability( void )
+{
+ /* check for the capability */
+ if( has_NPOT_capability == SOIL_CAPABILITY_UNKNOWN )
+ {
+ /* we haven't yet checked for the capability, do so */
+ if(
+ (NULL == strstr( (char const*)glGetString( GL_EXTENSIONS ),
+ "GL_ARB_texture_non_power_of_two" ) )
+ )
+ {
+ /* not there, flag the failure */
+ has_NPOT_capability = SOIL_CAPABILITY_NONE;
+ } else
+ {
+ /* it's there! */
+ has_NPOT_capability = SOIL_CAPABILITY_PRESENT;
+ }
+ }
+ /* let the user know if we can do non-power-of-two textures or not */
+ return has_NPOT_capability;
+}
+
+int query_tex_rectangle_capability( void )
+{
+ /* check for the capability */
+ if( has_tex_rectangle_capability == SOIL_CAPABILITY_UNKNOWN )
+ {
+ /* we haven't yet checked for the capability, do so */
+ if(
+ (NULL == strstr( (char const*)glGetString( GL_EXTENSIONS ),
+ "GL_ARB_texture_rectangle" ) )
+ &&
+ (NULL == strstr( (char const*)glGetString( GL_EXTENSIONS ),
+ "GL_EXT_texture_rectangle" ) )
+ &&
+ (NULL == strstr( (char const*)glGetString( GL_EXTENSIONS ),
+ "GL_NV_texture_rectangle" ) )
+ )
+ {
+ /* not there, flag the failure */
+ has_tex_rectangle_capability = SOIL_CAPABILITY_NONE;
+ } else
+ {
+ /* it's there! */
+ has_tex_rectangle_capability = SOIL_CAPABILITY_PRESENT;
+ }
+ }
+ /* let the user know if we can do texture rectangles or not */
+ return has_tex_rectangle_capability;
+}
+
+int query_cubemap_capability( void )
+{
+ /* check for the capability */
+ if( has_cubemap_capability == SOIL_CAPABILITY_UNKNOWN )
+ {
+ /* we haven't yet checked for the capability, do so */
+ if(
+ (NULL == strstr( (char const*)glGetString( GL_EXTENSIONS ),
+ "GL_ARB_texture_cube_map" ) )
+ &&
+ (NULL == strstr( (char const*)glGetString( GL_EXTENSIONS ),
+ "GL_EXT_texture_cube_map" ) )
+ )
+ {
+ /* not there, flag the failure */
+ has_cubemap_capability = SOIL_CAPABILITY_NONE;
+ } else
+ {
+ /* it's there! */
+ has_cubemap_capability = SOIL_CAPABILITY_PRESENT;
+ }
+ }
+ /* let the user know if we can do cubemaps or not */
+ return has_cubemap_capability;
+}
+
+int query_DXT_capability( void )
+{
+ /* check for the capability */
+ if( has_DXT_capability == SOIL_CAPABILITY_UNKNOWN )
+ {
+ /* we haven't yet checked for the capability, do so */
+ if( NULL == strstr(
+ (char const*)glGetString( GL_EXTENSIONS ),
+ "GL_EXT_texture_compression_s3tc" ) )
+ {
+ /* not there, flag the failure */
+ has_DXT_capability = SOIL_CAPABILITY_NONE;
+ } else
+ {
+ /* and find the address of the extension function */
+ P_SOIL_GLCOMPRESSEDTEXIMAGE2DPROC ext_addr = NULL;
+ #ifdef WIN32
+ ext_addr = (P_SOIL_GLCOMPRESSEDTEXIMAGE2DPROC)
+ wglGetProcAddress
+ (
+ "glCompressedTexImage2DARB"
+ );
+ #elif defined(__APPLE__) || defined(__APPLE_CC__)
+ /* I can't test this Apple stuff! */
+ CFBundleRef bundle;
+ CFURLRef bundleURL =
+ CFURLCreateWithFileSystemPath(
+ kCFAllocatorDefault,
+ CFSTR("/System/Library/Frameworks/OpenGL.framework"),
+ kCFURLPOSIXPathStyle,
+ true );
+ CFStringRef extensionName =
+ CFStringCreateWithCString(
+ kCFAllocatorDefault,
+ "glCompressedTexImage2DARB",
+ kCFStringEncodingASCII );
+ bundle = CFBundleCreate( kCFAllocatorDefault, bundleURL );
+ assert( bundle != NULL );
+ ext_addr = (P_SOIL_GLCOMPRESSEDTEXIMAGE2DPROC)
+ CFBundleGetFunctionPointerForName
+ (
+ bundle, extensionName
+ );
+ CFRelease( bundleURL );
+ CFRelease( extensionName );
+ CFRelease( bundle );
+ #else
+ ext_addr = (P_SOIL_GLCOMPRESSEDTEXIMAGE2DPROC)
+ glXGetProcAddressARB
+ (
+ (const GLubyte *)"glCompressedTexImage2DARB"
+ );
+ #endif
+ /* Flag it so no checks needed later */
+ if( NULL == ext_addr )
+ {
+ /* hmm, not good!! This should not happen, but does on my
+ laptop's VIA chipset. The GL_EXT_texture_compression_s3tc
+ spec requires that ARB_texture_compression be present too.
+ this means I can upload and have the OpenGL drive do the
+ conversion, but I can't use my own routines or load DDS files
+ from disk and upload them directly [8^( */
+ has_DXT_capability = SOIL_CAPABILITY_NONE;
+ } else
+ {
+ /* all's well! */
+ soilGlCompressedTexImage2D = ext_addr;
+ has_DXT_capability = SOIL_CAPABILITY_PRESENT;
+ }
+ }
+ }
+ /* let the user know if we can do DXT or not */
+ return has_DXT_capability;
+}
diff --git a/cw_8/src/SOIL/SOIL.h b/cw_8/src/SOIL/SOIL.h
new file mode 100644
index 0000000..43f634f
--- /dev/null
+++ b/cw_8/src/SOIL/SOIL.h
@@ -0,0 +1,433 @@
+/**
+ @mainpage SOIL
+
+ Jonathan Dummer
+ 2007-07-26-10.36
+
+ Simple OpenGL Image Library
+
+ A tiny c library for uploading images as
+ textures into OpenGL. Also saving and
+ loading of images is supported.
+
+ I'm using Sean's Tool Box image loader as a base:
+ http://www.nothings.org/
+
+ I'm upgrading it to load TGA and DDS files, and a direct
+ path for loading DDS files straight into OpenGL textures,
+ when applicable.
+
+ Image Formats:
+ - BMP load & save
+ - TGA load & save
+ - DDS load & save
+ - PNG load
+ - JPG load
+
+ OpenGL Texture Features:
+ - resample to power-of-two sizes
+ - MIPmap generation
+ - compressed texture S3TC formats (if supported)
+ - can pre-multiply alpha for you, for better compositing
+ - can flip image about the y-axis (except pre-compressed DDS files)
+
+ Thanks to:
+ * Sean Barret - for the awesome stb_image
+ * Dan Venkitachalam - for finding some non-compliant DDS files, and patching some explicit casts
+ * everybody at gamedev.net
+**/
+
+#ifndef HEADER_SIMPLE_OPENGL_IMAGE_LIBRARY
+#define HEADER_SIMPLE_OPENGL_IMAGE_LIBRARY
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ The format of images that may be loaded (force_channels).
+ SOIL_LOAD_AUTO leaves the image in whatever format it was found.
+ SOIL_LOAD_L forces the image to load as Luminous (greyscale)
+ SOIL_LOAD_LA forces the image to load as Luminous with Alpha
+ SOIL_LOAD_RGB forces the image to load as Red Green Blue
+ SOIL_LOAD_RGBA forces the image to load as Red Green Blue Alpha
+**/
+enum
+{
+ SOIL_LOAD_AUTO = 0,
+ SOIL_LOAD_L = 1,
+ SOIL_LOAD_LA = 2,
+ SOIL_LOAD_RGB = 3,
+ SOIL_LOAD_RGBA = 4
+};
+
+/**
+ Passed in as reuse_texture_ID, will cause SOIL to
+ register a new texture ID using glGenTextures().
+ If the value passed into reuse_texture_ID > 0 then
+ SOIL will just re-use that texture ID (great for
+ reloading image assets in-game!)
+**/
+enum
+{
+ SOIL_CREATE_NEW_ID = 0
+};
+
+/**
+ flags you can pass into SOIL_load_OGL_texture()
+ and SOIL_create_OGL_texture().
+ (note that if SOIL_FLAG_DDS_LOAD_DIRECT is used
+ the rest of the flags with the exception of
+ SOIL_FLAG_TEXTURE_REPEATS will be ignored while
+ loading already-compressed DDS files.)
+
+ SOIL_FLAG_POWER_OF_TWO: force the image to be POT
+ SOIL_FLAG_MIPMAPS: generate mipmaps for the texture
+ SOIL_FLAG_TEXTURE_REPEATS: otherwise will clamp
+ SOIL_FLAG_MULTIPLY_ALPHA: for using (GL_ONE,GL_ONE_MINUS_SRC_ALPHA) blending
+ SOIL_FLAG_INVERT_Y: flip the image vertically
+ SOIL_FLAG_COMPRESS_TO_DXT: if the card can display them, will convert RGB to DXT1, RGBA to DXT5
+ SOIL_FLAG_DDS_LOAD_DIRECT: will load DDS files directly without _ANY_ additional processing
+ SOIL_FLAG_NTSC_SAFE_RGB: clamps RGB components to the range [16,235]
+ SOIL_FLAG_CoCg_Y: Google YCoCg; RGB=>CoYCg, RGBA=>CoCgAY
+ SOIL_FLAG_TEXTURE_RECTANGE: uses ARB_texture_rectangle ; pixel indexed & no repeat or MIPmaps or cubemaps
+**/
+enum
+{
+ SOIL_FLAG_POWER_OF_TWO = 1,
+ SOIL_FLAG_MIPMAPS = 2,
+ SOIL_FLAG_TEXTURE_REPEATS = 4,
+ SOIL_FLAG_MULTIPLY_ALPHA = 8,
+ SOIL_FLAG_INVERT_Y = 16,
+ SOIL_FLAG_COMPRESS_TO_DXT = 32,
+ SOIL_FLAG_DDS_LOAD_DIRECT = 64,
+ SOIL_FLAG_NTSC_SAFE_RGB = 128,
+ SOIL_FLAG_CoCg_Y = 256,
+ SOIL_FLAG_TEXTURE_RECTANGLE = 512
+};
+
+/**
+ The types of images that may be saved.
+ (TGA supports uncompressed RGB / RGBA)
+ (BMP supports uncompressed RGB)
+ (DDS supports DXT1 and DXT5)
+**/
+enum
+{
+ SOIL_SAVE_TYPE_TGA = 0,
+ SOIL_SAVE_TYPE_BMP = 1,
+ SOIL_SAVE_TYPE_DDS = 2
+};
+
+/**
+ Defines the order of faces in a DDS cubemap.
+ I recommend that you use the same order in single
+ image cubemap files, so they will be interchangeable
+ with DDS cubemaps when using SOIL.
+**/
+#define SOIL_DDS_CUBEMAP_FACE_ORDER "EWUDNS"
+
+/**
+ The types of internal fake HDR representations
+
+ SOIL_HDR_RGBE: RGB * pow( 2.0, A - 128.0 )
+ SOIL_HDR_RGBdivA: RGB / A
+ SOIL_HDR_RGBdivA2: RGB / (A*A)
+**/
+enum
+{
+ SOIL_HDR_RGBE = 0,
+ SOIL_HDR_RGBdivA = 1,
+ SOIL_HDR_RGBdivA2 = 2
+};
+
+/**
+ Loads an image from disk into an OpenGL texture.
+ \param filename the name of the file to upload as a texture
+ \param force_channels 0-image format, 1-luminous, 2-luminous/alpha, 3-RGB, 4-RGBA
+ \param reuse_texture_ID 0-generate a new texture ID, otherwise reuse the texture ID (overwriting the old texture)
+ \param flags can be any of SOIL_FLAG_POWER_OF_TWO | SOIL_FLAG_MIPMAPS | SOIL_FLAG_TEXTURE_REPEATS | SOIL_FLAG_MULTIPLY_ALPHA | SOIL_FLAG_INVERT_Y | SOIL_FLAG_COMPRESS_TO_DXT | SOIL_FLAG_DDS_LOAD_DIRECT
+ \return 0-failed, otherwise returns the OpenGL texture handle
+**/
+unsigned int
+ SOIL_load_OGL_texture
+ (
+ const char *filename,
+ int force_channels,
+ unsigned int reuse_texture_ID,
+ unsigned int flags
+ );
+
+/**
+ Loads 6 images from disk into an OpenGL cubemap texture.
+ \param x_pos_file the name of the file to upload as the +x cube face
+ \param x_neg_file the name of the file to upload as the -x cube face
+ \param y_pos_file the name of the file to upload as the +y cube face
+ \param y_neg_file the name of the file to upload as the -y cube face
+ \param z_pos_file the name of the file to upload as the +z cube face
+ \param z_neg_file the name of the file to upload as the -z cube face
+ \param force_channels 0-image format, 1-luminous, 2-luminous/alpha, 3-RGB, 4-RGBA
+ \param reuse_texture_ID 0-generate a new texture ID, otherwise reuse the texture ID (overwriting the old texture)
+ \param flags can be any of SOIL_FLAG_POWER_OF_TWO | SOIL_FLAG_MIPMAPS | SOIL_FLAG_TEXTURE_REPEATS | SOIL_FLAG_MULTIPLY_ALPHA | SOIL_FLAG_INVERT_Y | SOIL_FLAG_COMPRESS_TO_DXT | SOIL_FLAG_DDS_LOAD_DIRECT
+ \return 0-failed, otherwise returns the OpenGL texture handle
+**/
+unsigned int
+ SOIL_load_OGL_cubemap
+ (
+ const char *x_pos_file,
+ const char *x_neg_file,
+ const char *y_pos_file,
+ const char *y_neg_file,
+ const char *z_pos_file,
+ const char *z_neg_file,
+ int force_channels,
+ unsigned int reuse_texture_ID,
+ unsigned int flags
+ );
+
+/**
+ Loads 1 image from disk and splits it into an OpenGL cubemap texture.
+ \param filename the name of the file to upload as a texture
+ \param face_order the order of the faces in the file, any combination of NSWEUD, for North, South, Up, etc.
+ \param force_channels 0-image format, 1-luminous, 2-luminous/alpha, 3-RGB, 4-RGBA
+ \param reuse_texture_ID 0-generate a new texture ID, otherwise reuse the texture ID (overwriting the old texture)
+ \param flags can be any of SOIL_FLAG_POWER_OF_TWO | SOIL_FLAG_MIPMAPS | SOIL_FLAG_TEXTURE_REPEATS | SOIL_FLAG_MULTIPLY_ALPHA | SOIL_FLAG_INVERT_Y | SOIL_FLAG_COMPRESS_TO_DXT | SOIL_FLAG_DDS_LOAD_DIRECT
+ \return 0-failed, otherwise returns the OpenGL texture handle
+**/
+unsigned int
+ SOIL_load_OGL_single_cubemap
+ (
+ const char *filename,
+ const char face_order[6],
+ int force_channels,
+ unsigned int reuse_texture_ID,
+ unsigned int flags
+ );
+
+/**
+ Loads an HDR image from disk into an OpenGL texture.
+ \param filename the name of the file to upload as a texture
+ \param fake_HDR_format SOIL_HDR_RGBE, SOIL_HDR_RGBdivA, SOIL_HDR_RGBdivA2
+ \param reuse_texture_ID 0-generate a new texture ID, otherwise reuse the texture ID (overwriting the old texture)
+ \param flags can be any of SOIL_FLAG_POWER_OF_TWO | SOIL_FLAG_MIPMAPS | SOIL_FLAG_TEXTURE_REPEATS | SOIL_FLAG_MULTIPLY_ALPHA | SOIL_FLAG_INVERT_Y | SOIL_FLAG_COMPRESS_TO_DXT
+ \return 0-failed, otherwise returns the OpenGL texture handle
+**/
+unsigned int
+ SOIL_load_OGL_HDR_texture
+ (
+ const char *filename,
+ int fake_HDR_format,
+ int rescale_to_max,
+ unsigned int reuse_texture_ID,
+ unsigned int flags
+ );
+
+/**
+ Loads an image from RAM into an OpenGL texture.
+ \param buffer the image data in RAM just as if it were still in a file
+ \param buffer_length the size of the buffer in bytes
+ \param force_channels 0-image format, 1-luminous, 2-luminous/alpha, 3-RGB, 4-RGBA
+ \param reuse_texture_ID 0-generate a new texture ID, otherwise reuse the texture ID (overwriting the old texture)
+ \param flags can be any of SOIL_FLAG_POWER_OF_TWO | SOIL_FLAG_MIPMAPS | SOIL_FLAG_TEXTURE_REPEATS | SOIL_FLAG_MULTIPLY_ALPHA | SOIL_FLAG_INVERT_Y | SOIL_FLAG_COMPRESS_TO_DXT | SOIL_FLAG_DDS_LOAD_DIRECT
+ \return 0-failed, otherwise returns the OpenGL texture handle
+**/
+unsigned int
+ SOIL_load_OGL_texture_from_memory
+ (
+ const unsigned char *const buffer,
+ int buffer_length,
+ int force_channels,
+ unsigned int reuse_texture_ID,
+ unsigned int flags
+ );
+
+/**
+ Loads 6 images from memory into an OpenGL cubemap texture.
+ \param x_pos_buffer the image data in RAM to upload as the +x cube face
+ \param x_pos_buffer_length the size of the above buffer
+ \param x_neg_buffer the image data in RAM to upload as the +x cube face
+ \param x_neg_buffer_length the size of the above buffer
+ \param y_pos_buffer the image data in RAM to upload as the +x cube face
+ \param y_pos_buffer_length the size of the above buffer
+ \param y_neg_buffer the image data in RAM to upload as the +x cube face
+ \param y_neg_buffer_length the size of the above buffer
+ \param z_pos_buffer the image data in RAM to upload as the +x cube face
+ \param z_pos_buffer_length the size of the above buffer
+ \param z_neg_buffer the image data in RAM to upload as the +x cube face
+ \param z_neg_buffer_length the size of the above buffer
+ \param force_channels 0-image format, 1-luminous, 2-luminous/alpha, 3-RGB, 4-RGBA
+ \param reuse_texture_ID 0-generate a new texture ID, otherwise reuse the texture ID (overwriting the old texture)
+ \param flags can be any of SOIL_FLAG_POWER_OF_TWO | SOIL_FLAG_MIPMAPS | SOIL_FLAG_TEXTURE_REPEATS | SOIL_FLAG_MULTIPLY_ALPHA | SOIL_FLAG_INVERT_Y | SOIL_FLAG_COMPRESS_TO_DXT | SOIL_FLAG_DDS_LOAD_DIRECT
+ \return 0-failed, otherwise returns the OpenGL texture handle
+**/
+unsigned int
+ SOIL_load_OGL_cubemap_from_memory
+ (
+ const unsigned char *const x_pos_buffer,
+ int x_pos_buffer_length,
+ const unsigned char *const x_neg_buffer,
+ int x_neg_buffer_length,
+ const unsigned char *const y_pos_buffer,
+ int y_pos_buffer_length,
+ const unsigned char *const y_neg_buffer,
+ int y_neg_buffer_length,
+ const unsigned char *const z_pos_buffer,
+ int z_pos_buffer_length,
+ const unsigned char *const z_neg_buffer,
+ int z_neg_buffer_length,
+ int force_channels,
+ unsigned int reuse_texture_ID,
+ unsigned int flags
+ );
+
+/**
+ Loads 1 image from RAM and splits it into an OpenGL cubemap texture.
+ \param buffer the image data in RAM just as if it were still in a file
+ \param buffer_length the size of the buffer in bytes
+ \param face_order the order of the faces in the file, any combination of NSWEUD, for North, South, Up, etc.
+ \param force_channels 0-image format, 1-luminous, 2-luminous/alpha, 3-RGB, 4-RGBA
+ \param reuse_texture_ID 0-generate a new texture ID, otherwise reuse the texture ID (overwriting the old texture)
+ \param flags can be any of SOIL_FLAG_POWER_OF_TWO | SOIL_FLAG_MIPMAPS | SOIL_FLAG_TEXTURE_REPEATS | SOIL_FLAG_MULTIPLY_ALPHA | SOIL_FLAG_INVERT_Y | SOIL_FLAG_COMPRESS_TO_DXT | SOIL_FLAG_DDS_LOAD_DIRECT
+ \return 0-failed, otherwise returns the OpenGL texture handle
+**/
+unsigned int
+ SOIL_load_OGL_single_cubemap_from_memory
+ (
+ const unsigned char *const buffer,
+ int buffer_length,
+ const char face_order[6],
+ int force_channels,
+ unsigned int reuse_texture_ID,
+ unsigned int flags
+ );
+
+/**
+ Creates a 2D OpenGL texture from raw image data. Note that the raw data is
+ _NOT_ freed after the upload (so the user can load various versions).
+ \param data the raw data to be uploaded as an OpenGL texture
+ \param width the width of the image in pixels
+ \param height the height of the image in pixels
+ \param channels the number of channels: 1-luminous, 2-luminous/alpha, 3-RGB, 4-RGBA
+ \param reuse_texture_ID 0-generate a new texture ID, otherwise reuse the texture ID (overwriting the old texture)
+ \param flags can be any of SOIL_FLAG_POWER_OF_TWO | SOIL_FLAG_MIPMAPS | SOIL_FLAG_TEXTURE_REPEATS | SOIL_FLAG_MULTIPLY_ALPHA | SOIL_FLAG_INVERT_Y | SOIL_FLAG_COMPRESS_TO_DXT
+ \return 0-failed, otherwise returns the OpenGL texture handle
+**/
+unsigned int
+ SOIL_create_OGL_texture
+ (
+ const unsigned char *const data,
+ int width, int height, int channels,
+ unsigned int reuse_texture_ID,
+ unsigned int flags
+ );
+
+/**
+ Creates an OpenGL cubemap texture by splitting up 1 image into 6 parts.
+ \param data the raw data to be uploaded as an OpenGL texture
+ \param width the width of the image in pixels
+ \param height the height of the image in pixels
+ \param channels the number of channels: 1-luminous, 2-luminous/alpha, 3-RGB, 4-RGBA
+ \param face_order the order of the faces in the file, and combination of NSWEUD, for North, South, Up, etc.
+ \param reuse_texture_ID 0-generate a new texture ID, otherwise reuse the texture ID (overwriting the old texture)
+ \param flags can be any of SOIL_FLAG_POWER_OF_TWO | SOIL_FLAG_MIPMAPS | SOIL_FLAG_TEXTURE_REPEATS | SOIL_FLAG_MULTIPLY_ALPHA | SOIL_FLAG_INVERT_Y | SOIL_FLAG_COMPRESS_TO_DXT | SOIL_FLAG_DDS_LOAD_DIRECT
+ \return 0-failed, otherwise returns the OpenGL texture handle
+**/
+unsigned int
+ SOIL_create_OGL_single_cubemap
+ (
+ const unsigned char *const data,
+ int width, int height, int channels,
+ const char face_order[6],
+ unsigned int reuse_texture_ID,
+ unsigned int flags
+ );
+
+/**
+ Captures the OpenGL window (RGB) and saves it to disk
+ \return 0 if it failed, otherwise returns 1
+**/
+int
+ SOIL_save_screenshot
+ (
+ const char *filename,
+ int image_type,
+ int x, int y,
+ int width, int height
+ );
+
+/**
+ Loads an image from disk into an array of unsigned chars.
+ Note that *channels return the original channel count of the
+ image. If force_channels was other than SOIL_LOAD_AUTO,
+ the resulting image has force_channels, but *channels may be
+ different (if the original image had a different channel
+ count).
+ \return 0 if failed, otherwise returns 1
+**/
+unsigned char*
+ SOIL_load_image
+ (
+ const char *filename,
+ int *width, int *height, int *channels,
+ int force_channels
+ );
+
+/**
+ Loads an image from memory into an array of unsigned chars.
+ Note that *channels return the original channel count of the
+ image. If force_channels was other than SOIL_LOAD_AUTO,
+ the resulting image has force_channels, but *channels may be
+ different (if the original image had a different channel
+ count).
+ \return 0 if failed, otherwise returns 1
+**/
+unsigned char*
+ SOIL_load_image_from_memory
+ (
+ const unsigned char *const buffer,
+ int buffer_length,
+ int *width, int *height, int *channels,
+ int force_channels
+ );
+
+/**
+ Saves an image from an array of unsigned chars (RGBA) to disk
+ \return 0 if failed, otherwise returns 1
+**/
+int
+ SOIL_save_image
+ (
+ const char *filename,
+ int image_type,
+ int width, int height, int channels,
+ const unsigned char *const data
+ );
+
+/**
+ Frees the image data (note, this is just C's "free()"...this function is
+ present mostly so C++ programmers don't forget to use "free()" and call
+ "delete []" instead [8^)
+**/
+void
+ SOIL_free_image_data
+ (
+ unsigned char *img_data
+ );
+
+/**
+ This function resturn a pointer to a string describing the last thing
+ that happened inside SOIL. It can be used to determine why an image
+ failed to load.
+**/
+const char*
+ SOIL_last_result
+ (
+ void
+ );
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* HEADER_SIMPLE_OPENGL_IMAGE_LIBRARY */
diff --git a/cw_8/src/SOIL/image_DXT.c b/cw_8/src/SOIL/image_DXT.c
new file mode 100644
index 0000000..4206a1b
--- /dev/null
+++ b/cw_8/src/SOIL/image_DXT.c
@@ -0,0 +1,632 @@
+/*
+ Jonathan Dummer
+ 2007-07-31-10.32
+
+ simple DXT compression / decompression code
+
+ public domain
+*/
+
+#include "image_DXT.h"
+#include
+#include
+#include
+#include
+
+/* set this =1 if you want to use the covarince matrix method...
+ which is better than my method of using standard deviations
+ overall, except on the infintesimal chance that the power
+ method fails for finding the largest eigenvector */
+#define USE_COV_MAT 1
+
+/********* Function Prototypes *********/
+/*
+ Takes a 4x4 block of pixels and compresses it into 8 bytes
+ in DXT1 format (color only, no alpha). Speed is valued
+ over prettyness, at least for now.
+*/
+void compress_DDS_color_block(
+ int channels,
+ const unsigned char *const uncompressed,
+ unsigned char compressed[8] );
+/*
+ Takes a 4x4 block of pixels and compresses the alpha
+ component it into 8 bytes for use in DXT5 DDS files.
+ Speed is valued over prettyness, at least for now.
+*/
+void compress_DDS_alpha_block(
+ const unsigned char *const uncompressed,
+ unsigned char compressed[8] );
+
+/********* Actual Exposed Functions *********/
+int
+ save_image_as_DDS
+ (
+ const char *filename,
+ int width, int height, int channels,
+ const unsigned char *const data
+ )
+{
+ /* variables */
+ FILE *fout;
+ unsigned char *DDS_data;
+ DDS_header header;
+ int DDS_size;
+ /* error check */
+ if( (NULL == filename) ||
+ (width < 1) || (height < 1) ||
+ (channels < 1) || (channels > 4) ||
+ (data == NULL ) )
+ {
+ return 0;
+ }
+ /* Convert the image */
+ if( (channels & 1) == 1 )
+ {
+ /* no alpha, just use DXT1 */
+ DDS_data = convert_image_to_DXT1( data, width, height, channels, &DDS_size );
+ } else
+ {
+ /* has alpha, so use DXT5 */
+ DDS_data = convert_image_to_DXT5( data, width, height, channels, &DDS_size );
+ }
+ /* save it */
+ memset( &header, 0, sizeof( DDS_header ) );
+ header.dwMagic = ('D' << 0) | ('D' << 8) | ('S' << 16) | (' ' << 24);
+ header.dwSize = 124;
+ header.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT | DDSD_LINEARSIZE;
+ header.dwWidth = width;
+ header.dwHeight = height;
+ header.dwPitchOrLinearSize = DDS_size;
+ header.sPixelFormat.dwSize = 32;
+ header.sPixelFormat.dwFlags = DDPF_FOURCC;
+ if( (channels & 1) == 1 )
+ {
+ header.sPixelFormat.dwFourCC = ('D' << 0) | ('X' << 8) | ('T' << 16) | ('1' << 24);
+ } else
+ {
+ header.sPixelFormat.dwFourCC = ('D' << 0) | ('X' << 8) | ('T' << 16) | ('5' << 24);
+ }
+ header.sCaps.dwCaps1 = DDSCAPS_TEXTURE;
+ /* write it out */
+ fout = fopen( filename, "wb");
+ fwrite( &header, sizeof( DDS_header ), 1, fout );
+ fwrite( DDS_data, 1, DDS_size, fout );
+ fclose( fout );
+ /* done */
+ free( DDS_data );
+ return 1;
+}
+
+unsigned char* convert_image_to_DXT1(
+ const unsigned char *const uncompressed,
+ int width, int height, int channels,
+ int *out_size )
+{
+ unsigned char *compressed;
+ int i, j, x, y;
+ unsigned char ublock[16*3];
+ unsigned char cblock[8];
+ int index = 0, chan_step = 1;
+ int block_count = 0;
+ /* error check */
+ *out_size = 0;
+ if( (width < 1) || (height < 1) ||
+ (NULL == uncompressed) ||
+ (channels < 1) || (channels > 4) )
+ {
+ return NULL;
+ }
+ /* for channels == 1 or 2, I do not step forward for R,G,B values */
+ if( channels < 3 )
+ {
+ chan_step = 0;
+ }
+ /* get the RAM for the compressed image
+ (8 bytes per 4x4 pixel block) */
+ *out_size = ((width+3) >> 2) * ((height+3) >> 2) * 8;
+ compressed = (unsigned char*)malloc( *out_size );
+ /* go through each block */
+ for( j = 0; j < height; j += 4 )
+ {
+ for( i = 0; i < width; i += 4 )
+ {
+ /* copy this block into a new one */
+ int idx = 0;
+ int mx = 4, my = 4;
+ if( j+4 >= height )
+ {
+ my = height - j;
+ }
+ if( i+4 >= width )
+ {
+ mx = width - i;
+ }
+ for( y = 0; y < my; ++y )
+ {
+ for( x = 0; x < mx; ++x )
+ {
+ ublock[idx++] = uncompressed[(j+y)*width*channels+(i+x)*channels];
+ ublock[idx++] = uncompressed[(j+y)*width*channels+(i+x)*channels+chan_step];
+ ublock[idx++] = uncompressed[(j+y)*width*channels+(i+x)*channels+chan_step+chan_step];
+ }
+ for( x = mx; x < 4; ++x )
+ {
+ ublock[idx++] = ublock[0];
+ ublock[idx++] = ublock[1];
+ ublock[idx++] = ublock[2];
+ }
+ }
+ for( y = my; y < 4; ++y )
+ {
+ for( x = 0; x < 4; ++x )
+ {
+ ublock[idx++] = ublock[0];
+ ublock[idx++] = ublock[1];
+ ublock[idx++] = ublock[2];
+ }
+ }
+ /* compress the block */
+ ++block_count;
+ compress_DDS_color_block( 3, ublock, cblock );
+ /* copy the data from the block into the main block */
+ for( x = 0; x < 8; ++x )
+ {
+ compressed[index++] = cblock[x];
+ }
+ }
+ }
+ return compressed;
+}
+
+unsigned char* convert_image_to_DXT5(
+ const unsigned char *const uncompressed,
+ int width, int height, int channels,
+ int *out_size )
+{
+ unsigned char *compressed;
+ int i, j, x, y;
+ unsigned char ublock[16*4];
+ unsigned char cblock[8];
+ int index = 0, chan_step = 1;
+ int block_count = 0, has_alpha;
+ /* error check */
+ *out_size = 0;
+ if( (width < 1) || (height < 1) ||
+ (NULL == uncompressed) ||
+ (channels < 1) || ( channels > 4) )
+ {
+ return NULL;
+ }
+ /* for channels == 1 or 2, I do not step forward for R,G,B vales */
+ if( channels < 3 )
+ {
+ chan_step = 0;
+ }
+ /* # channels = 1 or 3 have no alpha, 2 & 4 do have alpha */
+ has_alpha = 1 - (channels & 1);
+ /* get the RAM for the compressed image
+ (16 bytes per 4x4 pixel block) */
+ *out_size = ((width+3) >> 2) * ((height+3) >> 2) * 16;
+ compressed = (unsigned char*)malloc( *out_size );
+ /* go through each block */
+ for( j = 0; j < height; j += 4 )
+ {
+ for( i = 0; i < width; i += 4 )
+ {
+ /* local variables, and my block counter */
+ int idx = 0;
+ int mx = 4, my = 4;
+ if( j+4 >= height )
+ {
+ my = height - j;
+ }
+ if( i+4 >= width )
+ {
+ mx = width - i;
+ }
+ for( y = 0; y < my; ++y )
+ {
+ for( x = 0; x < mx; ++x )
+ {
+ ublock[idx++] = uncompressed[(j+y)*width*channels+(i+x)*channels];
+ ublock[idx++] = uncompressed[(j+y)*width*channels+(i+x)*channels+chan_step];
+ ublock[idx++] = uncompressed[(j+y)*width*channels+(i+x)*channels+chan_step+chan_step];
+ ublock[idx++] =
+ has_alpha * uncompressed[(j+y)*width*channels+(i+x)*channels+channels-1]
+ + (1-has_alpha)*255;
+ }
+ for( x = mx; x < 4; ++x )
+ {
+ ublock[idx++] = ublock[0];
+ ublock[idx++] = ublock[1];
+ ublock[idx++] = ublock[2];
+ ublock[idx++] = ublock[3];
+ }
+ }
+ for( y = my; y < 4; ++y )
+ {
+ for( x = 0; x < 4; ++x )
+ {
+ ublock[idx++] = ublock[0];
+ ublock[idx++] = ublock[1];
+ ublock[idx++] = ublock[2];
+ ublock[idx++] = ublock[3];
+ }
+ }
+ /* now compress the alpha block */
+ compress_DDS_alpha_block( ublock, cblock );
+ /* copy the data from the compressed alpha block into the main buffer */
+ for( x = 0; x < 8; ++x )
+ {
+ compressed[index++] = cblock[x];
+ }
+ /* then compress the color block */
+ ++block_count;
+ compress_DDS_color_block( 4, ublock, cblock );
+ /* copy the data from the compressed color block into the main buffer */
+ for( x = 0; x < 8; ++x )
+ {
+ compressed[index++] = cblock[x];
+ }
+ }
+ }
+ return compressed;
+}
+
+/********* Helper Functions *********/
+int convert_bit_range( int c, int from_bits, int to_bits )
+{
+ int b = (1 << (from_bits - 1)) + c * ((1 << to_bits) - 1);
+ return (b + (b >> from_bits)) >> from_bits;
+}
+
+int rgb_to_565( int r, int g, int b )
+{
+ return
+ (convert_bit_range( r, 8, 5 ) << 11) |
+ (convert_bit_range( g, 8, 6 ) << 05) |
+ (convert_bit_range( b, 8, 5 ) << 00);
+}
+
+void rgb_888_from_565( unsigned int c, int *r, int *g, int *b )
+{
+ *r = convert_bit_range( (c >> 11) & 31, 5, 8 );
+ *g = convert_bit_range( (c >> 05) & 63, 6, 8 );
+ *b = convert_bit_range( (c >> 00) & 31, 5, 8 );
+}
+
+void compute_color_line_STDEV(
+ const unsigned char *const uncompressed,
+ int channels,
+ float point[3], float direction[3] )
+{
+ const float inv_16 = 1.0f / 16.0f;
+ int i;
+ float sum_r = 0.0f, sum_g = 0.0f, sum_b = 0.0f;
+ float sum_rr = 0.0f, sum_gg = 0.0f, sum_bb = 0.0f;
+ float sum_rg = 0.0f, sum_rb = 0.0f, sum_gb = 0.0f;
+ /* calculate all data needed for the covariance matrix
+ ( to compare with _rygdxt code) */
+ for( i = 0; i < 16*channels; i += channels )
+ {
+ sum_r += uncompressed[i+0];
+ sum_rr += uncompressed[i+0] * uncompressed[i+0];
+ sum_g += uncompressed[i+1];
+ sum_gg += uncompressed[i+1] * uncompressed[i+1];
+ sum_b += uncompressed[i+2];
+ sum_bb += uncompressed[i+2] * uncompressed[i+2];
+ sum_rg += uncompressed[i+0] * uncompressed[i+1];
+ sum_rb += uncompressed[i+0] * uncompressed[i+2];
+ sum_gb += uncompressed[i+1] * uncompressed[i+2];
+ }
+ /* convert the sums to averages */
+ sum_r *= inv_16;
+ sum_g *= inv_16;
+ sum_b *= inv_16;
+ /* and convert the squares to the squares of the value - avg_value */
+ sum_rr -= 16.0f * sum_r * sum_r;
+ sum_gg -= 16.0f * sum_g * sum_g;
+ sum_bb -= 16.0f * sum_b * sum_b;
+ sum_rg -= 16.0f * sum_r * sum_g;
+ sum_rb -= 16.0f * sum_r * sum_b;
+ sum_gb -= 16.0f * sum_g * sum_b;
+ /* the point on the color line is the average */
+ point[0] = sum_r;
+ point[1] = sum_g;
+ point[2] = sum_b;
+ #if USE_COV_MAT
+ /*
+ The following idea was from ryg.
+ (https://mollyrocket.com/forums/viewtopic.php?t=392)
+ The method worked great (less RMSE than mine) most of
+ the time, but had some issues handling some simple
+ boundary cases, like full green next to full red,
+ which would generate a covariance matrix like this:
+
+ | 1 -1 0 |
+ | -1 1 0 |
+ | 0 0 0 |
+
+ For a given starting vector, the power method can
+ generate all zeros! So no starting with {1,1,1}
+ as I was doing! This kind of error is still a
+ slight posibillity, but will be very rare.
+ */
+ /* use the covariance matrix directly
+ (1st iteration, don't use all 1.0 values!) */
+ sum_r = 1.0f;
+ sum_g = 2.718281828f;
+ sum_b = 3.141592654f;
+ direction[0] = sum_r*sum_rr + sum_g*sum_rg + sum_b*sum_rb;
+ direction[1] = sum_r*sum_rg + sum_g*sum_gg + sum_b*sum_gb;
+ direction[2] = sum_r*sum_rb + sum_g*sum_gb + sum_b*sum_bb;
+ /* 2nd iteration, use results from the 1st guy */
+ sum_r = direction[0];
+ sum_g = direction[1];
+ sum_b = direction[2];
+ direction[0] = sum_r*sum_rr + sum_g*sum_rg + sum_b*sum_rb;
+ direction[1] = sum_r*sum_rg + sum_g*sum_gg + sum_b*sum_gb;
+ direction[2] = sum_r*sum_rb + sum_g*sum_gb + sum_b*sum_bb;
+ /* 3rd iteration, use results from the 2nd guy */
+ sum_r = direction[0];
+ sum_g = direction[1];
+ sum_b = direction[2];
+ direction[0] = sum_r*sum_rr + sum_g*sum_rg + sum_b*sum_rb;
+ direction[1] = sum_r*sum_rg + sum_g*sum_gg + sum_b*sum_gb;
+ direction[2] = sum_r*sum_rb + sum_g*sum_gb + sum_b*sum_bb;
+ #else
+ /* use my standard deviation method
+ (very robust, a tiny bit slower and less accurate) */
+ direction[0] = sqrt( sum_rr );
+ direction[1] = sqrt( sum_gg );
+ direction[2] = sqrt( sum_bb );
+ /* which has a greater component */
+ if( sum_gg > sum_rr )
+ {
+ /* green has greater component, so base the other signs off of green */
+ if( sum_rg < 0.0f )
+ {
+ direction[0] = -direction[0];
+ }
+ if( sum_gb < 0.0f )
+ {
+ direction[2] = -direction[2];
+ }
+ } else
+ {
+ /* red has a greater component */
+ if( sum_rg < 0.0f )
+ {
+ direction[1] = -direction[1];
+ }
+ if( sum_rb < 0.0f )
+ {
+ direction[2] = -direction[2];
+ }
+ }
+ #endif
+}
+
+void LSE_master_colors_max_min(
+ int *cmax, int *cmin,
+ int channels,
+ const unsigned char *const uncompressed )
+{
+ int i, j;
+ /* the master colors */
+ int c0[3], c1[3];
+ /* used for fitting the line */
+ float sum_x[] = { 0.0f, 0.0f, 0.0f };
+ float sum_x2[] = { 0.0f, 0.0f, 0.0f };
+ float dot_max = 1.0f, dot_min = -1.0f;
+ float vec_len2 = 0.0f;
+ float dot;
+ /* error check */
+ if( (channels < 3) || (channels > 4) )
+ {
+ return;
+ }
+ compute_color_line_STDEV( uncompressed, channels, sum_x, sum_x2 );
+ vec_len2 = 1.0f / ( 0.00001f +
+ sum_x2[0]*sum_x2[0] + sum_x2[1]*sum_x2[1] + sum_x2[2]*sum_x2[2] );
+ /* finding the max and min vector values */
+ dot_max =
+ (
+ sum_x2[0] * uncompressed[0] +
+ sum_x2[1] * uncompressed[1] +
+ sum_x2[2] * uncompressed[2]
+ );
+ dot_min = dot_max;
+ for( i = 1; i < 16; ++i )
+ {
+ dot =
+ (
+ sum_x2[0] * uncompressed[i*channels+0] +
+ sum_x2[1] * uncompressed[i*channels+1] +
+ sum_x2[2] * uncompressed[i*channels+2]
+ );
+ if( dot < dot_min )
+ {
+ dot_min = dot;
+ } else if( dot > dot_max )
+ {
+ dot_max = dot;
+ }
+ }
+ /* and the offset (from the average location) */
+ dot = sum_x2[0]*sum_x[0] + sum_x2[1]*sum_x[1] + sum_x2[2]*sum_x[2];
+ dot_min -= dot;
+ dot_max -= dot;
+ /* post multiply by the scaling factor */
+ dot_min *= vec_len2;
+ dot_max *= vec_len2;
+ /* OK, build the master colors */
+ for( i = 0; i < 3; ++i )
+ {
+ /* color 0 */
+ c0[i] = (int)(0.5f + sum_x[i] + dot_max * sum_x2[i]);
+ if( c0[i] < 0 )
+ {
+ c0[i] = 0;
+ } else if( c0[i] > 255 )
+ {
+ c0[i] = 255;
+ }
+ /* color 1 */
+ c1[i] = (int)(0.5f + sum_x[i] + dot_min * sum_x2[i]);
+ if( c1[i] < 0 )
+ {
+ c1[i] = 0;
+ } else if( c1[i] > 255 )
+ {
+ c1[i] = 255;
+ }
+ }
+ /* down_sample (with rounding?) */
+ i = rgb_to_565( c0[0], c0[1], c0[2] );
+ j = rgb_to_565( c1[0], c1[1], c1[2] );
+ if( i > j )
+ {
+ *cmax = i;
+ *cmin = j;
+ } else
+ {
+ *cmax = j;
+ *cmin = i;
+ }
+}
+
+void
+ compress_DDS_color_block
+ (
+ int channels,
+ const unsigned char *const uncompressed,
+ unsigned char compressed[8]
+ )
+{
+ /* variables */
+ int i;
+ int next_bit;
+ int enc_c0, enc_c1;
+ int c0[4], c1[4];
+ float color_line[] = { 0.0f, 0.0f, 0.0f, 0.0f };
+ float vec_len2 = 0.0f, dot_offset = 0.0f;
+ /* stupid order */
+ int swizzle4[] = { 0, 2, 3, 1 };
+ /* get the master colors */
+ LSE_master_colors_max_min( &enc_c0, &enc_c1, channels, uncompressed );
+ /* store the 565 color 0 and color 1 */
+ compressed[0] = (enc_c0 >> 0) & 255;
+ compressed[1] = (enc_c0 >> 8) & 255;
+ compressed[2] = (enc_c1 >> 0) & 255;
+ compressed[3] = (enc_c1 >> 8) & 255;
+ /* zero out the compressed data */
+ compressed[4] = 0;
+ compressed[5] = 0;
+ compressed[6] = 0;
+ compressed[7] = 0;
+ /* reconstitute the master color vectors */
+ rgb_888_from_565( enc_c0, &c0[0], &c0[1], &c0[2] );
+ rgb_888_from_565( enc_c1, &c1[0], &c1[1], &c1[2] );
+ /* the new vector */
+ vec_len2 = 0.0f;
+ for( i = 0; i < 3; ++i )
+ {
+ color_line[i] = (float)(c1[i] - c0[i]);
+ vec_len2 += color_line[i] * color_line[i];
+ }
+ if( vec_len2 > 0.0f )
+ {
+ vec_len2 = 1.0f / vec_len2;
+ }
+ /* pre-proform the scaling */
+ color_line[0] *= vec_len2;
+ color_line[1] *= vec_len2;
+ color_line[2] *= vec_len2;
+ /* compute the offset (constant) portion of the dot product */
+ dot_offset = color_line[0]*c0[0] + color_line[1]*c0[1] + color_line[2]*c0[2];
+ /* store the rest of the bits */
+ next_bit = 8*4;
+ for( i = 0; i < 16; ++i )
+ {
+ /* find the dot product of this color, to place it on the line
+ (should be [-1,1]) */
+ int next_value = 0;
+ float dot_product =
+ color_line[0] * uncompressed[i*channels+0] +
+ color_line[1] * uncompressed[i*channels+1] +
+ color_line[2] * uncompressed[i*channels+2] -
+ dot_offset;
+ /* map to [0,3] */
+ next_value = (int)( dot_product * 3.0f + 0.5f );
+ if( next_value > 3 )
+ {
+ next_value = 3;
+ } else if( next_value < 0 )
+ {
+ next_value = 0;
+ }
+ /* OK, store this value */
+ compressed[next_bit >> 3] |= swizzle4[ next_value ] << (next_bit & 7);
+ next_bit += 2;
+ }
+ /* done compressing to DXT1 */
+}
+
+void
+ compress_DDS_alpha_block
+ (
+ const unsigned char *const uncompressed,
+ unsigned char compressed[8]
+ )
+{
+ /* variables */
+ int i;
+ int next_bit;
+ int a0, a1;
+ float scale_me;
+ /* stupid order */
+ int swizzle8[] = { 1, 7, 6, 5, 4, 3, 2, 0 };
+ /* get the alpha limits (a0 > a1) */
+ a0 = a1 = uncompressed[3];
+ for( i = 4+3; i < 16*4; i += 4 )
+ {
+ if( uncompressed[i] > a0 )
+ {
+ a0 = uncompressed[i];
+ } else if( uncompressed[i] < a1 )
+ {
+ a1 = uncompressed[i];
+ }
+ }
+ /* store those limits, and zero the rest of the compressed dataset */
+ compressed[0] = a0;
+ compressed[1] = a1;
+ /* zero out the compressed data */
+ compressed[2] = 0;
+ compressed[3] = 0;
+ compressed[4] = 0;
+ compressed[5] = 0;
+ compressed[6] = 0;
+ compressed[7] = 0;
+ /* store the all of the alpha values */
+ next_bit = 8*2;
+ scale_me = 7.9999f / (a0 - a1);
+ for( i = 3; i < 16*4; i += 4 )
+ {
+ /* convert this alpha value to a 3 bit number */
+ int svalue;
+ int value = (int)((uncompressed[i] - a1) * scale_me);
+ svalue = swizzle8[ value&7 ];
+ /* OK, store this value, start with the 1st byte */
+ compressed[next_bit >> 3] |= svalue << (next_bit & 7);
+ if( (next_bit & 7) > 5 )
+ {
+ /* spans 2 bytes, fill in the start of the 2nd byte */
+ compressed[1 + (next_bit >> 3)] |= svalue >> (8 - (next_bit & 7) );
+ }
+ next_bit += 3;
+ }
+ /* done compressing to DXT1 */
+}
diff --git a/cw_8/src/SOIL/image_DXT.h b/cw_8/src/SOIL/image_DXT.h
new file mode 100644
index 0000000..75f604f
--- /dev/null
+++ b/cw_8/src/SOIL/image_DXT.h
@@ -0,0 +1,123 @@
+/*
+ Jonathan Dummer
+ 2007-07-31-10.32
+
+ simple DXT compression / decompression code
+
+ public domain
+*/
+
+#ifndef HEADER_IMAGE_DXT
+#define HEADER_IMAGE_DXT
+
+/**
+ Converts an image from an array of unsigned chars (RGB or RGBA) to
+ DXT1 or DXT5, then saves the converted image to disk.
+ \return 0 if failed, otherwise returns 1
+**/
+int
+save_image_as_DDS
+(
+ const char *filename,
+ int width, int height, int channels,
+ const unsigned char *const data
+);
+
+/**
+ take an image and convert it to DXT1 (no alpha)
+**/
+unsigned char*
+convert_image_to_DXT1
+(
+ const unsigned char *const uncompressed,
+ int width, int height, int channels,
+ int *out_size
+);
+
+/**
+ take an image and convert it to DXT5 (with alpha)
+**/
+unsigned char*
+convert_image_to_DXT5
+(
+ const unsigned char *const uncompressed,
+ int width, int height, int channels,
+ int *out_size
+);
+
+/** A bunch of DirectDraw Surface structures and flags **/
+typedef struct
+{
+ unsigned int dwMagic;
+ unsigned int dwSize;
+ unsigned int dwFlags;
+ unsigned int dwHeight;
+ unsigned int dwWidth;
+ unsigned int dwPitchOrLinearSize;
+ unsigned int dwDepth;
+ unsigned int dwMipMapCount;
+ unsigned int dwReserved1[ 11 ];
+
+ /* DDPIXELFORMAT */
+ struct
+ {
+ unsigned int dwSize;
+ unsigned int dwFlags;
+ unsigned int dwFourCC;
+ unsigned int dwRGBBitCount;
+ unsigned int dwRBitMask;
+ unsigned int dwGBitMask;
+ unsigned int dwBBitMask;
+ unsigned int dwAlphaBitMask;
+ }
+ sPixelFormat;
+
+ /* DDCAPS2 */
+ struct
+ {
+ unsigned int dwCaps1;
+ unsigned int dwCaps2;
+ unsigned int dwDDSX;
+ unsigned int dwReserved;
+ }
+ sCaps;
+ unsigned int dwReserved2;
+}
+DDS_header ;
+
+/* the following constants were copied directly off the MSDN website */
+
+/* The dwFlags member of the original DDSURFACEDESC2 structure
+ can be set to one or more of the following values. */
+#define DDSD_CAPS 0x00000001
+#define DDSD_HEIGHT 0x00000002
+#define DDSD_WIDTH 0x00000004
+#define DDSD_PITCH 0x00000008
+#define DDSD_PIXELFORMAT 0x00001000
+#define DDSD_MIPMAPCOUNT 0x00020000
+#define DDSD_LINEARSIZE 0x00080000
+#define DDSD_DEPTH 0x00800000
+
+/* DirectDraw Pixel Format */
+#define DDPF_ALPHAPIXELS 0x00000001
+#define DDPF_FOURCC 0x00000004
+#define DDPF_RGB 0x00000040
+
+/* The dwCaps1 member of the DDSCAPS2 structure can be
+ set to one or more of the following values. */
+#define DDSCAPS_COMPLEX 0x00000008
+#define DDSCAPS_TEXTURE 0x00001000
+#define DDSCAPS_MIPMAP 0x00400000
+
+/* The dwCaps2 member of the DDSCAPS2 structure can be
+ set to one or more of the following values. */
+#define DDSCAPS2_CUBEMAP 0x00000200
+#define DDSCAPS2_CUBEMAP_POSITIVEX 0x00000400
+#define DDSCAPS2_CUBEMAP_NEGATIVEX 0x00000800
+#define DDSCAPS2_CUBEMAP_POSITIVEY 0x00001000
+#define DDSCAPS2_CUBEMAP_NEGATIVEY 0x00002000
+#define DDSCAPS2_CUBEMAP_POSITIVEZ 0x00004000
+#define DDSCAPS2_CUBEMAP_NEGATIVEZ 0x00008000
+#define DDSCAPS2_VOLUME 0x00200000
+
+#endif /* HEADER_IMAGE_DXT */
diff --git a/cw_8/src/SOIL/image_helper.c b/cw_8/src/SOIL/image_helper.c
new file mode 100644
index 0000000..d22340f
--- /dev/null
+++ b/cw_8/src/SOIL/image_helper.c
@@ -0,0 +1,435 @@
+/*
+ Jonathan Dummer
+
+ image helper functions
+
+ MIT license
+*/
+
+#include "image_helper.h"
+#include
+#include
+
+/* Upscaling the image uses simple bilinear interpolation */
+int
+ up_scale_image
+ (
+ const unsigned char* const orig,
+ int width, int height, int channels,
+ unsigned char* resampled,
+ int resampled_width, int resampled_height
+ )
+{
+ float dx, dy;
+ int x, y, c;
+
+ /* error(s) check */
+ if ( (width < 1) || (height < 1) ||
+ (resampled_width < 2) || (resampled_height < 2) ||
+ (channels < 1) ||
+ (NULL == orig) || (NULL == resampled) )
+ {
+ /* signify badness */
+ return 0;
+ }
+ /*
+ for each given pixel in the new map, find the exact location
+ from the original map which would contribute to this guy
+ */
+ dx = (width - 1.0f) / (resampled_width - 1.0f);
+ dy = (height - 1.0f) / (resampled_height - 1.0f);
+ for ( y = 0; y < resampled_height; ++y )
+ {
+ /* find the base y index and fractional offset from that */
+ float sampley = y * dy;
+ int inty = (int)sampley;
+ /* if( inty < 0 ) { inty = 0; } else */
+ if( inty > height - 2 ) { inty = height - 2; }
+ sampley -= inty;
+ for ( x = 0; x < resampled_width; ++x )
+ {
+ float samplex = x * dx;
+ int intx = (int)samplex;
+ int base_index;
+ /* find the base x index and fractional offset from that */
+ /* if( intx < 0 ) { intx = 0; } else */
+ if( intx > width - 2 ) { intx = width - 2; }
+ samplex -= intx;
+ /* base index into the original image */
+ base_index = (inty * width + intx) * channels;
+ for ( c = 0; c < channels; ++c )
+ {
+ /* do the sampling */
+ float value = 0.5f;
+ value += orig[base_index]
+ *(1.0f-samplex)*(1.0f-sampley);
+ value += orig[base_index+channels]
+ *(samplex)*(1.0f-sampley);
+ value += orig[base_index+width*channels]
+ *(1.0f-samplex)*(sampley);
+ value += orig[base_index+width*channels+channels]
+ *(samplex)*(sampley);
+ /* move to the next channel */
+ ++base_index;
+ /* save the new value */
+ resampled[y*resampled_width*channels+x*channels+c] =
+ (unsigned char)(value);
+ }
+ }
+ }
+ /* done */
+ return 1;
+}
+
+int
+ mipmap_image
+ (
+ const unsigned char* const orig,
+ int width, int height, int channels,
+ unsigned char* resampled,
+ int block_size_x, int block_size_y
+ )
+{
+ int mip_width, mip_height;
+ int i, j, c;
+
+ /* error check */
+ if( (width < 1) || (height < 1) ||
+ (channels < 1) || (orig == NULL) ||
+ (resampled == NULL) ||
+ (block_size_x < 1) || (block_size_y < 1) )
+ {
+ /* nothing to do */
+ return 0;
+ }
+ mip_width = width / block_size_x;
+ mip_height = height / block_size_y;
+ if( mip_width < 1 )
+ {
+ mip_width = 1;
+ }
+ if( mip_height < 1 )
+ {
+ mip_height = 1;
+ }
+ for( j = 0; j < mip_height; ++j )
+ {
+ for( i = 0; i < mip_width; ++i )
+ {
+ for( c = 0; c < channels; ++c )
+ {
+ const int index = (j*block_size_y)*width*channels + (i*block_size_x)*channels + c;
+ int sum_value;
+ int u,v;
+ int u_block = block_size_x;
+ int v_block = block_size_y;
+ int block_area;
+ /* do a bit of checking so we don't over-run the boundaries
+ (necessary for non-square textures!) */
+ if( block_size_x * (i+1) > width )
+ {
+ u_block = width - i*block_size_y;
+ }
+ if( block_size_y * (j+1) > height )
+ {
+ v_block = height - j*block_size_y;
+ }
+ block_area = u_block*v_block;
+ /* for this pixel, see what the average
+ of all the values in the block are.
+ note: start the sum at the rounding value, not at 0 */
+ sum_value = block_area >> 1;
+ for( v = 0; v < v_block; ++v )
+ for( u = 0; u < u_block; ++u )
+ {
+ sum_value += orig[index + v*width*channels + u*channels];
+ }
+ resampled[j*mip_width*channels + i*channels + c] = sum_value / block_area;
+ }
+ }
+ }
+ return 1;
+}
+
+int
+ scale_image_RGB_to_NTSC_safe
+ (
+ unsigned char* orig,
+ int width, int height, int channels
+ )
+{
+ const float scale_lo = 16.0f - 0.499f;
+ const float scale_hi = 235.0f + 0.499f;
+ int i, j;
+ int nc = channels;
+ unsigned char scale_LUT[256];
+ /* error check */
+ if( (width < 1) || (height < 1) ||
+ (channels < 1) || (orig == NULL) )
+ {
+ /* nothing to do */
+ return 0;
+ }
+ /* set up the scaling Look Up Table */
+ for( i = 0; i < 256; ++i )
+ {
+ scale_LUT[i] = (unsigned char)((scale_hi - scale_lo) * i / 255.0f + scale_lo);
+ }
+ /* for channels = 2 or 4, ignore the alpha component */
+ nc -= 1 - (channels & 1);
+ /* OK, go through the image and scale any non-alpha components */
+ for( i = 0; i < width*height*channels; i += channels )
+ {
+ for( j = 0; j < nc; ++j )
+ {
+ orig[i+j] = scale_LUT[orig[i+j]];
+ }
+ }
+ return 1;
+}
+
+unsigned char clamp_byte( int x ) { return ( (x) < 0 ? (0) : ( (x) > 255 ? 255 : (x) ) ); }
+
+/*
+ This function takes the RGB components of the image
+ and converts them into YCoCg. 3 components will be
+ re-ordered to CoYCg (for optimum DXT1 compression),
+ while 4 components will be ordered CoCgAY (for DXT5
+ compression).
+*/
+int
+ convert_RGB_to_YCoCg
+ (
+ unsigned char* orig,
+ int width, int height, int channels
+ )
+{
+ int i;
+ /* error check */
+ if( (width < 1) || (height < 1) ||
+ (channels < 3) || (channels > 4) ||
+ (orig == NULL) )
+ {
+ /* nothing to do */
+ return -1;
+ }
+ /* do the conversion */
+ if( channels == 3 )
+ {
+ for( i = 0; i < width*height*3; i += 3 )
+ {
+ int r = orig[i+0];
+ int g = (orig[i+1] + 1) >> 1;
+ int b = orig[i+2];
+ int tmp = (2 + r + b) >> 2;
+ /* Co */
+ orig[i+0] = clamp_byte( 128 + ((r - b + 1) >> 1) );
+ /* Y */
+ orig[i+1] = clamp_byte( g + tmp );
+ /* Cg */
+ orig[i+2] = clamp_byte( 128 + g - tmp );
+ }
+ } else
+ {
+ for( i = 0; i < width*height*4; i += 4 )
+ {
+ int r = orig[i+0];
+ int g = (orig[i+1] + 1) >> 1;
+ int b = orig[i+2];
+ unsigned char a = orig[i+3];
+ int tmp = (2 + r + b) >> 2;
+ /* Co */
+ orig[i+0] = clamp_byte( 128 + ((r - b + 1) >> 1) );
+ /* Cg */
+ orig[i+1] = clamp_byte( 128 + g - tmp );
+ /* Alpha */
+ orig[i+2] = a;
+ /* Y */
+ orig[i+3] = clamp_byte( g + tmp );
+ }
+ }
+ /* done */
+ return 0;
+}
+
+/*
+ This function takes the YCoCg components of the image
+ and converts them into RGB. See above.
+*/
+int
+ convert_YCoCg_to_RGB
+ (
+ unsigned char* orig,
+ int width, int height, int channels
+ )
+{
+ int i;
+ /* error check */
+ if( (width < 1) || (height < 1) ||
+ (channels < 3) || (channels > 4) ||
+ (orig == NULL) )
+ {
+ /* nothing to do */
+ return -1;
+ }
+ /* do the conversion */
+ if( channels == 3 )
+ {
+ for( i = 0; i < width*height*3; i += 3 )
+ {
+ int co = orig[i+0] - 128;
+ int y = orig[i+1];
+ int cg = orig[i+2] - 128;
+ /* R */
+ orig[i+0] = clamp_byte( y + co - cg );
+ /* G */
+ orig[i+1] = clamp_byte( y + cg );
+ /* B */
+ orig[i+2] = clamp_byte( y - co - cg );
+ }
+ } else
+ {
+ for( i = 0; i < width*height*4; i += 4 )
+ {
+ int co = orig[i+0] - 128;
+ int cg = orig[i+1] - 128;
+ unsigned char a = orig[i+2];
+ int y = orig[i+3];
+ /* R */
+ orig[i+0] = clamp_byte( y + co - cg );
+ /* G */
+ orig[i+1] = clamp_byte( y + cg );
+ /* B */
+ orig[i+2] = clamp_byte( y - co - cg );
+ /* A */
+ orig[i+3] = a;
+ }
+ }
+ /* done */
+ return 0;
+}
+
+float
+find_max_RGBE
+(
+ unsigned char *image,
+ int width, int height
+)
+{
+ float max_val = 0.0f;
+ unsigned char *img = image;
+ int i, j;
+ for( i = width * height; i > 0; --i )
+ {
+ /* float scale = powf( 2.0f, img[3] - 128.0f ) / 255.0f; */
+ float scale = ldexp( 1.0f / 255.0f, (int)(img[3]) - 128 );
+ for( j = 0; j < 3; ++j )
+ {
+ if( img[j] * scale > max_val )
+ {
+ max_val = img[j] * scale;
+ }
+ }
+ /* next pixel */
+ img += 4;
+ }
+ return max_val;
+}
+
+int
+RGBE_to_RGBdivA
+(
+ unsigned char *image,
+ int width, int height,
+ int rescale_to_max
+)
+{
+ /* local variables */
+ int i, iv;
+ unsigned char *img = image;
+ float scale = 1.0f;
+ /* error check */
+ if( (!image) || (width < 1) || (height < 1) )
+ {
+ return 0;
+ }
+ /* convert (note: no negative numbers, but 0.0 is possible) */
+ if( rescale_to_max )
+ {
+ scale = 255.0f / find_max_RGBE( image, width, height );
+ }
+ for( i = width * height; i > 0; --i )
+ {
+ /* decode this pixel, and find the max */
+ float r,g,b,e, m;
+ /* e = scale * powf( 2.0f, img[3] - 128.0f ) / 255.0f; */
+ e = scale * ldexp( 1.0f / 255.0f, (int)(img[3]) - 128 );
+ r = e * img[0];
+ g = e * img[1];
+ b = e * img[2];
+ m = (r > g) ? r : g;
+ m = (b > m) ? b : m;
+ /* and encode it into RGBdivA */
+ iv = (m != 0.0f) ? (int)(255.0f / m) : 1.0f;
+ iv = (iv < 1) ? 1 : iv;
+ img[3] = (iv > 255) ? 255 : iv;
+ iv = (int)(img[3] * r + 0.5f);
+ img[0] = (iv > 255) ? 255 : iv;
+ iv = (int)(img[3] * g + 0.5f);
+ img[1] = (iv > 255) ? 255 : iv;
+ iv = (int)(img[3] * b + 0.5f);
+ img[2] = (iv > 255) ? 255 : iv;
+ /* and on to the next pixel */
+ img += 4;
+ }
+ return 1;
+}
+
+int
+RGBE_to_RGBdivA2
+(
+ unsigned char *image,
+ int width, int height,
+ int rescale_to_max
+)
+{
+ /* local variables */
+ int i, iv;
+ unsigned char *img = image;
+ float scale = 1.0f;
+ /* error check */
+ if( (!image) || (width < 1) || (height < 1) )
+ {
+ return 0;
+ }
+ /* convert (note: no negative numbers, but 0.0 is possible) */
+ if( rescale_to_max )
+ {
+ scale = 255.0f * 255.0f / find_max_RGBE( image, width, height );
+ }
+ for( i = width * height; i > 0; --i )
+ {
+ /* decode this pixel, and find the max */
+ float r,g,b,e, m;
+ /* e = scale * powf( 2.0f, img[3] - 128.0f ) / 255.0f; */
+ e = scale * ldexp( 1.0f / 255.0f, (int)(img[3]) - 128 );
+ r = e * img[0];
+ g = e * img[1];
+ b = e * img[2];
+ m = (r > g) ? r : g;
+ m = (b > m) ? b : m;
+ /* and encode it into RGBdivA */
+ iv = (m != 0.0f) ? (int)sqrtf( 255.0f * 255.0f / m ) : 1.0f;
+ iv = (iv < 1) ? 1 : iv;
+ img[3] = (iv > 255) ? 255 : iv;
+ iv = (int)(img[3] * img[3] * r / 255.0f + 0.5f);
+ img[0] = (iv > 255) ? 255 : iv;
+ iv = (int)(img[3] * img[3] * g / 255.0f + 0.5f);
+ img[1] = (iv > 255) ? 255 : iv;
+ iv = (int)(img[3] * img[3] * b / 255.0f + 0.5f);
+ img[2] = (iv > 255) ? 255 : iv;
+ /* and on to the next pixel */
+ img += 4;
+ }
+ return 1;
+}
diff --git a/cw_8/src/SOIL/image_helper.h b/cw_8/src/SOIL/image_helper.h
new file mode 100644
index 0000000..3fa2662
--- /dev/null
+++ b/cw_8/src/SOIL/image_helper.h
@@ -0,0 +1,115 @@
+/*
+ Jonathan Dummer
+
+ Image helper functions
+
+ MIT license
+*/
+
+#ifndef HEADER_IMAGE_HELPER
+#define HEADER_IMAGE_HELPER
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ This function upscales an image.
+ Not to be used to create MIPmaps,
+ but to make it square,
+ or to make it a power-of-two sized.
+**/
+int
+ up_scale_image
+ (
+ const unsigned char* const orig,
+ int width, int height, int channels,
+ unsigned char* resampled,
+ int resampled_width, int resampled_height
+ );
+
+/**
+ This function downscales an image.
+ Used for creating MIPmaps,
+ the incoming image should be a
+ power-of-two sized.
+**/
+int
+ mipmap_image
+ (
+ const unsigned char* const orig,
+ int width, int height, int channels,
+ unsigned char* resampled,
+ int block_size_x, int block_size_y
+ );
+
+/**
+ This function takes the RGB components of the image
+ and scales each channel from [0,255] to [16,235].
+ This makes the colors "Safe" for display on NTSC
+ displays. Note that this is _NOT_ a good idea for
+ loading images like normal- or height-maps!
+**/
+int
+ scale_image_RGB_to_NTSC_safe
+ (
+ unsigned char* orig,
+ int width, int height, int channels
+ );
+
+/**
+ This function takes the RGB components of the image
+ and converts them into YCoCg. 3 components will be
+ re-ordered to CoYCg (for optimum DXT1 compression),
+ while 4 components will be ordered CoCgAY (for DXT5
+ compression).
+**/
+int
+ convert_RGB_to_YCoCg
+ (
+ unsigned char* orig,
+ int width, int height, int channels
+ );
+
+/**
+ This function takes the YCoCg components of the image
+ and converts them into RGB. See above.
+**/
+int
+ convert_YCoCg_to_RGB
+ (
+ unsigned char* orig,
+ int width, int height, int channels
+ );
+
+/**
+ Converts an HDR image from an array
+ of unsigned chars (RGBE) to RGBdivA
+ \return 0 if failed, otherwise returns 1
+**/
+int
+ RGBE_to_RGBdivA
+ (
+ unsigned char *image,
+ int width, int height,
+ int rescale_to_max
+ );
+
+/**
+ Converts an HDR image from an array
+ of unsigned chars (RGBE) to RGBdivA2
+ \return 0 if failed, otherwise returns 1
+**/
+int
+ RGBE_to_RGBdivA2
+ (
+ unsigned char *image,
+ int width, int height,
+ int rescale_to_max
+ );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* HEADER_IMAGE_HELPER */
diff --git a/cw_8/src/SOIL/stb_image_aug.c b/cw_8/src/SOIL/stb_image_aug.c
new file mode 100644
index 0000000..bb088fc
--- /dev/null
+++ b/cw_8/src/SOIL/stb_image_aug.c
@@ -0,0 +1,3682 @@
+/* stbi-1.16 - public domain JPEG/PNG reader - http://nothings.org/stb_image.c
+ when you control the images you're loading
+
+ QUICK NOTES:
+ Primarily of interest to game developers and other people who can
+ avoid problematic images and only need the trivial interface
+
+ JPEG baseline (no JPEG progressive, no oddball channel decimations)
+ PNG non-interlaced
+ BMP non-1bpp, non-RLE
+ TGA (not sure what subset, if a subset)
+ PSD (composited view only, no extra channels)
+ HDR (radiance rgbE format)
+ writes BMP,TGA (define STBI_NO_WRITE to remove code)
+ decoded from memory or through stdio FILE (define STBI_NO_STDIO to remove code)
+ supports installable dequantizing-IDCT, YCbCr-to-RGB conversion (define STBI_SIMD)
+
+ TODO:
+ stbi_info_*
+
+ history:
+ 1.16 major bugfix - convert_format converted one too many pixels
+ 1.15 initialize some fields for thread safety
+ 1.14 fix threadsafe conversion bug; header-file-only version (#define STBI_HEADER_FILE_ONLY before including)
+ 1.13 threadsafe
+ 1.12 const qualifiers in the API
+ 1.11 Support installable IDCT, colorspace conversion routines
+ 1.10 Fixes for 64-bit (don't use "unsigned long")
+ optimized upsampling by Fabian "ryg" Giesen
+ 1.09 Fix format-conversion for PSD code (bad global variables!)
+ 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz
+ 1.07 attempt to fix C++ warning/errors again
+ 1.06 attempt to fix C++ warning/errors again
+ 1.05 fix TGA loading to return correct *comp and use good luminance calc
+ 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free
+ 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR
+ 1.02 support for (subset of) HDR files, float interface for preferred access to them
+ 1.01 fix bug: possible bug in handling right-side up bmps... not sure
+ fix bug: the stbi_bmp_load() and stbi_tga_load() functions didn't work at all
+ 1.00 interface to zlib that skips zlib header
+ 0.99 correct handling of alpha in palette
+ 0.98 TGA loader by lonesock; dynamically add loaders (untested)
+ 0.97 jpeg errors on too large a file; also catch another malloc failure
+ 0.96 fix detection of invalid v value - particleman@mollyrocket forum
+ 0.95 during header scan, seek to markers in case of padding
+ 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same
+ 0.93 handle jpegtran output; verbose errors
+ 0.92 read 4,8,16,24,32-bit BMP files of several formats
+ 0.91 output 24-bit Windows 3.0 BMP files
+ 0.90 fix a few more warnings; bump version number to approach 1.0
+ 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd
+ 0.60 fix compiling as c++
+ 0.59 fix warnings: merge Dave Moore's -Wall fixes
+ 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian
+ 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less
+ than 16 available
+ 0.56 fix bug: zlib uncompressed mode len vs. nlen
+ 0.55 fix bug: restart_interval not initialized to 0
+ 0.54 allow NULL for 'int *comp'
+ 0.53 fix bug in png 3->4; speedup png decoding
+ 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments
+ 0.51 obey req_comp requests, 1-component jpegs return as 1-component,
+ on 'test' only check type, not whether we support this variant
+*/
+
+#include "stb_image_aug.h"
+
+#ifndef STBI_NO_HDR
+#include // ldexp
+#include // strcmp
+#endif
+
+#ifndef STBI_NO_STDIO
+#include
+#endif
+#include
+#include
+#include
+#include
+
+#ifndef _MSC_VER
+ #ifdef __cplusplus
+ #define __forceinline inline
+ #else
+ #define __forceinline
+ #endif
+#endif
+
+
+// implementation:
+typedef unsigned char uint8;
+typedef unsigned short uint16;
+typedef signed short int16;
+typedef unsigned int uint32;
+typedef signed int int32;
+typedef unsigned int uint;
+
+// should produce compiler error if size is wrong
+typedef unsigned char validate_uint32[sizeof(uint32)==4];
+
+#if defined(STBI_NO_STDIO) && !defined(STBI_NO_WRITE)
+#define STBI_NO_WRITE
+#endif
+
+#ifndef STBI_NO_DDS
+#include "stbi_DDS_aug.h"
+#endif
+
+// I (JLD) want full messages for SOIL
+#define STBI_FAILURE_USERMSG 1
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Generic API that works on all image types
+//
+
+// this is not threadsafe
+static char *failure_reason;
+
+char *stbi_failure_reason(void)
+{
+ return failure_reason;
+}
+
+static int e(char *str)
+{
+ failure_reason = str;
+ return 0;
+}
+
+#ifdef STBI_NO_FAILURE_STRINGS
+ #define e(x,y) 0
+#elif defined(STBI_FAILURE_USERMSG)
+ #define e(x,y) e(y)
+#else
+ #define e(x,y) e(x)
+#endif
+
+#define epf(x,y) ((float *) (e(x,y)?NULL:NULL))
+#define epuc(x,y) ((unsigned char *) (e(x,y)?NULL:NULL))
+
+void stbi_image_free(void *retval_from_stbi_load)
+{
+ free(retval_from_stbi_load);
+}
+
+#define MAX_LOADERS 32
+stbi_loader *loaders[MAX_LOADERS];
+static int max_loaders = 0;
+
+int stbi_register_loader(stbi_loader *loader)
+{
+ int i;
+ for (i=0; i < MAX_LOADERS; ++i) {
+ // already present?
+ if (loaders[i] == loader)
+ return 1;
+ // end of the list?
+ if (loaders[i] == NULL) {
+ loaders[i] = loader;
+ max_loaders = i+1;
+ return 1;
+ }
+ }
+ // no room for it
+ return 0;
+}
+
+#ifndef STBI_NO_HDR
+static float *ldr_to_hdr(stbi_uc *data, int x, int y, int comp);
+static stbi_uc *hdr_to_ldr(float *data, int x, int y, int comp);
+#endif
+
+#ifndef STBI_NO_STDIO
+unsigned char *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp)
+{
+ FILE *f = fopen(filename, "rb");
+ unsigned char *result;
+ if (!f) return epuc("can't fopen", "Unable to open file");
+ result = stbi_load_from_file(f,x,y,comp,req_comp);
+ fclose(f);
+ return result;
+}
+
+unsigned char *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp)
+{
+ int i;
+ if (stbi_jpeg_test_file(f))
+ return stbi_jpeg_load_from_file(f,x,y,comp,req_comp);
+ if (stbi_png_test_file(f))
+ return stbi_png_load_from_file(f,x,y,comp,req_comp);
+ if (stbi_bmp_test_file(f))
+ return stbi_bmp_load_from_file(f,x,y,comp,req_comp);
+ if (stbi_psd_test_file(f))
+ return stbi_psd_load_from_file(f,x,y,comp,req_comp);
+ #ifndef STBI_NO_DDS
+ if (stbi_dds_test_file(f))
+ return stbi_dds_load_from_file(f,x,y,comp,req_comp);
+ #endif
+ #ifndef STBI_NO_HDR
+ if (stbi_hdr_test_file(f)) {
+ float *hdr = stbi_hdr_load_from_file(f, x,y,comp,req_comp);
+ return hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp);
+ }
+ #endif
+ for (i=0; i < max_loaders; ++i)
+ if (loaders[i]->test_file(f))
+ return loaders[i]->load_from_file(f,x,y,comp,req_comp);
+ // test tga last because it's a crappy test!
+ if (stbi_tga_test_file(f))
+ return stbi_tga_load_from_file(f,x,y,comp,req_comp);
+ return epuc("unknown image type", "Image not of any known type, or corrupt");
+}
+#endif
+
+unsigned char *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp)
+{
+ int i;
+ if (stbi_jpeg_test_memory(buffer,len))
+ return stbi_jpeg_load_from_memory(buffer,len,x,y,comp,req_comp);
+ if (stbi_png_test_memory(buffer,len))
+ return stbi_png_load_from_memory(buffer,len,x,y,comp,req_comp);
+ if (stbi_bmp_test_memory(buffer,len))
+ return stbi_bmp_load_from_memory(buffer,len,x,y,comp,req_comp);
+ if (stbi_psd_test_memory(buffer,len))
+ return stbi_psd_load_from_memory(buffer,len,x,y,comp,req_comp);
+ #ifndef STBI_NO_DDS
+ if (stbi_dds_test_memory(buffer,len))
+ return stbi_dds_load_from_memory(buffer,len,x,y,comp,req_comp);
+ #endif
+ #ifndef STBI_NO_HDR
+ if (stbi_hdr_test_memory(buffer, len)) {
+ float *hdr = stbi_hdr_load_from_memory(buffer, len,x,y,comp,req_comp);
+ return hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp);
+ }
+ #endif
+ for (i=0; i < max_loaders; ++i)
+ if (loaders[i]->test_memory(buffer,len))
+ return loaders[i]->load_from_memory(buffer,len,x,y,comp,req_comp);
+ // test tga last because it's a crappy test!
+ if (stbi_tga_test_memory(buffer,len))
+ return stbi_tga_load_from_memory(buffer,len,x,y,comp,req_comp);
+ return epuc("unknown image type", "Image not of any known type, or corrupt");
+}
+
+#ifndef STBI_NO_HDR
+
+#ifndef STBI_NO_STDIO
+float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp)
+{
+ FILE *f = fopen(filename, "rb");
+ float *result;
+ if (!f) return epf("can't fopen", "Unable to open file");
+ result = stbi_loadf_from_file(f,x,y,comp,req_comp);
+ fclose(f);
+ return result;
+}
+
+float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp)
+{
+ unsigned char *data;
+ #ifndef STBI_NO_HDR
+ if (stbi_hdr_test_file(f))
+ return stbi_hdr_load_from_file(f,x,y,comp,req_comp);
+ #endif
+ data = stbi_load_from_file(f, x, y, comp, req_comp);
+ if (data)
+ return ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp);
+ return epf("unknown image type", "Image not of any known type, or corrupt");
+}
+#endif
+
+float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp)
+{
+ stbi_uc *data;
+ #ifndef STBI_NO_HDR
+ if (stbi_hdr_test_memory(buffer, len))
+ return stbi_hdr_load_from_memory(buffer, len,x,y,comp,req_comp);
+ #endif
+ data = stbi_load_from_memory(buffer, len, x, y, comp, req_comp);
+ if (data)
+ return ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp);
+ return epf("unknown image type", "Image not of any known type, or corrupt");
+}
+#endif
+
+// these is-hdr-or-not is defined independent of whether STBI_NO_HDR is
+// defined, for API simplicity; if STBI_NO_HDR is defined, it always
+// reports false!
+
+int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len)
+{
+ #ifndef STBI_NO_HDR
+ return stbi_hdr_test_memory(buffer, len);
+ #else
+ return 0;
+ #endif
+}
+
+#ifndef STBI_NO_STDIO
+extern int stbi_is_hdr (char const *filename)
+{
+ FILE *f = fopen(filename, "rb");
+ int result=0;
+ if (f) {
+ result = stbi_is_hdr_from_file(f);
+ fclose(f);
+ }
+ return result;
+}
+
+extern int stbi_is_hdr_from_file(FILE *f)
+{
+ #ifndef STBI_NO_HDR
+ return stbi_hdr_test_file(f);
+ #else
+ return 0;
+ #endif
+}
+
+#endif
+
+// @TODO: get image dimensions & components without fully decoding
+#ifndef STBI_NO_STDIO
+extern int stbi_info (char const *filename, int *x, int *y, int *comp);
+extern int stbi_info_from_file (FILE *f, int *x, int *y, int *comp);
+#endif
+extern int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp);
+
+#ifndef STBI_NO_HDR
+static float h2l_gamma_i=1.0f/2.2f, h2l_scale_i=1.0f;
+static float l2h_gamma=2.2f, l2h_scale=1.0f;
+
+void stbi_hdr_to_ldr_gamma(float gamma) { h2l_gamma_i = 1/gamma; }
+void stbi_hdr_to_ldr_scale(float scale) { h2l_scale_i = 1/scale; }
+
+void stbi_ldr_to_hdr_gamma(float gamma) { l2h_gamma = gamma; }
+void stbi_ldr_to_hdr_scale(float scale) { l2h_scale = scale; }
+#endif
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Common code used by all image loaders
+//
+
+enum
+{
+ SCAN_load=0,
+ SCAN_type,
+ SCAN_header,
+};
+
+typedef struct
+{
+ uint32 img_x, img_y;
+ int img_n, img_out_n;
+
+ #ifndef STBI_NO_STDIO
+ FILE *img_file;
+ #endif
+ uint8 *img_buffer, *img_buffer_end;
+} stbi;
+
+#ifndef STBI_NO_STDIO
+static void start_file(stbi *s, FILE *f)
+{
+ s->img_file = f;
+}
+#endif
+
+static void start_mem(stbi *s, uint8 const *buffer, int len)
+{
+#ifndef STBI_NO_STDIO
+ s->img_file = NULL;
+#endif
+ s->img_buffer = (uint8 *) buffer;
+ s->img_buffer_end = (uint8 *) buffer+len;
+}
+
+__forceinline static int get8(stbi *s)
+{
+#ifndef STBI_NO_STDIO
+ if (s->img_file) {
+ int c = fgetc(s->img_file);
+ return c == EOF ? 0 : c;
+ }
+#endif
+ if (s->img_buffer < s->img_buffer_end)
+ return *s->img_buffer++;
+ return 0;
+}
+
+__forceinline static int at_eof(stbi *s)
+{
+#ifndef STBI_NO_STDIO
+ if (s->img_file)
+ return feof(s->img_file);
+#endif
+ return s->img_buffer >= s->img_buffer_end;
+}
+
+__forceinline static uint8 get8u(stbi *s)
+{
+ return (uint8) get8(s);
+}
+
+static void skip(stbi *s, int n)
+{
+#ifndef STBI_NO_STDIO
+ if (s->img_file)
+ fseek(s->img_file, n, SEEK_CUR);
+ else
+#endif
+ s->img_buffer += n;
+}
+
+static int get16(stbi *s)
+{
+ int z = get8(s);
+ return (z << 8) + get8(s);
+}
+
+static uint32 get32(stbi *s)
+{
+ uint32 z = get16(s);
+ return (z << 16) + get16(s);
+}
+
+static int get16le(stbi *s)
+{
+ int z = get8(s);
+ return z + (get8(s) << 8);
+}
+
+static uint32 get32le(stbi *s)
+{
+ uint32 z = get16le(s);
+ return z + (get16le(s) << 16);
+}
+
+static void getn(stbi *s, stbi_uc *buffer, int n)
+{
+#ifndef STBI_NO_STDIO
+ if (s->img_file) {
+ fread(buffer, 1, n, s->img_file);
+ return;
+ }
+#endif
+ memcpy(buffer, s->img_buffer, n);
+ s->img_buffer += n;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// generic converter from built-in img_n to req_comp
+// individual types do this automatically as much as possible (e.g. jpeg
+// does all cases internally since it needs to colorspace convert anyway,
+// and it never has alpha, so very few cases ). png can automatically
+// interleave an alpha=255 channel, but falls back to this for other cases
+//
+// assume data buffer is malloced, so malloc a new one and free that one
+// only failure mode is malloc failing
+
+static uint8 compute_y(int r, int g, int b)
+{
+ return (uint8) (((r*77) + (g*150) + (29*b)) >> 8);
+}
+
+static unsigned char *convert_format(unsigned char *data, int img_n, int req_comp, uint x, uint y)
+{
+ int i,j;
+ unsigned char *good;
+
+ if (req_comp == img_n) return data;
+ assert(req_comp >= 1 && req_comp <= 4);
+
+ good = (unsigned char *) malloc(req_comp * x * y);
+ if (good == NULL) {
+ free(data);
+ return epuc("outofmem", "Out of memory");
+ }
+
+ for (j=0; j < (int) y; ++j) {
+ unsigned char *src = data + j * x * img_n ;
+ unsigned char *dest = good + j * x * req_comp;
+
+ #define COMBO(a,b) ((a)*8+(b))
+ #define CASE(a,b) case COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b)
+ // convert source image with img_n components to one with req_comp components;
+ // avoid switch per pixel, so use switch per scanline and massive macros
+ switch(COMBO(img_n, req_comp)) {
+ CASE(1,2) dest[0]=src[0], dest[1]=255; break;
+ CASE(1,3) dest[0]=dest[1]=dest[2]=src[0]; break;
+ CASE(1,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; break;
+ CASE(2,1) dest[0]=src[0]; break;
+ CASE(2,3) dest[0]=dest[1]=dest[2]=src[0]; break;
+ CASE(2,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; break;
+ CASE(3,4) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; break;
+ CASE(3,1) dest[0]=compute_y(src[0],src[1],src[2]); break;
+ CASE(3,2) dest[0]=compute_y(src[0],src[1],src[2]), dest[1] = 255; break;
+ CASE(4,1) dest[0]=compute_y(src[0],src[1],src[2]); break;
+ CASE(4,2) dest[0]=compute_y(src[0],src[1],src[2]), dest[1] = src[3]; break;
+ CASE(4,3) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; break;
+ default: assert(0);
+ }
+ #undef CASE
+ }
+
+ free(data);
+ return good;
+}
+
+#ifndef STBI_NO_HDR
+static float *ldr_to_hdr(stbi_uc *data, int x, int y, int comp)
+{
+ int i,k,n;
+ float *output = (float *) malloc(x * y * comp * sizeof(float));
+ if (output == NULL) { free(data); return epf("outofmem", "Out of memory"); }
+ // compute number of non-alpha components
+ if (comp & 1) n = comp; else n = comp-1;
+ for (i=0; i < x*y; ++i) {
+ for (k=0; k < n; ++k) {
+ output[i*comp + k] = (float) pow(data[i*comp+k]/255.0f, l2h_gamma) * l2h_scale;
+ }
+ if (k < comp) output[i*comp + k] = data[i*comp+k]/255.0f;
+ }
+ free(data);
+ return output;
+}
+
+#define float2int(x) ((int) (x))
+static stbi_uc *hdr_to_ldr(float *data, int x, int y, int comp)
+{
+ int i,k,n;
+ stbi_uc *output = (stbi_uc *) malloc(x * y * comp);
+ if (output == NULL) { free(data); return epuc("outofmem", "Out of memory"); }
+ // compute number of non-alpha components
+ if (comp & 1) n = comp; else n = comp-1;
+ for (i=0; i < x*y; ++i) {
+ for (k=0; k < n; ++k) {
+ float z = (float) pow(data[i*comp+k]*h2l_scale_i, h2l_gamma_i) * 255 + 0.5f;
+ if (z < 0) z = 0;
+ if (z > 255) z = 255;
+ output[i*comp + k] = float2int(z);
+ }
+ if (k < comp) {
+ float z = data[i*comp+k] * 255 + 0.5f;
+ if (z < 0) z = 0;
+ if (z > 255) z = 255;
+ output[i*comp + k] = float2int(z);
+ }
+ }
+ free(data);
+ return output;
+}
+#endif
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// "baseline" JPEG/JFIF decoder (not actually fully baseline implementation)
+//
+// simple implementation
+// - channel subsampling of at most 2 in each dimension
+// - doesn't support delayed output of y-dimension
+// - simple interface (only one output format: 8-bit interleaved RGB)
+// - doesn't try to recover corrupt jpegs
+// - doesn't allow partial loading, loading multiple at once
+// - still fast on x86 (copying globals into locals doesn't help x86)
+// - allocates lots of intermediate memory (full size of all components)
+// - non-interleaved case requires this anyway
+// - allows good upsampling (see next)
+// high-quality
+// - upsampled channels are bilinearly interpolated, even across blocks
+// - quality integer IDCT derived from IJG's 'slow'
+// performance
+// - fast huffman; reasonable integer IDCT
+// - uses a lot of intermediate memory, could cache poorly
+// - load http://nothings.org/remote/anemones.jpg 3 times on 2.8Ghz P4
+// stb_jpeg: 1.34 seconds (MSVC6, default release build)
+// stb_jpeg: 1.06 seconds (MSVC6, processor = Pentium Pro)
+// IJL11.dll: 1.08 seconds (compiled by intel)
+// IJG 1998: 0.98 seconds (MSVC6, makefile provided by IJG)
+// IJG 1998: 0.95 seconds (MSVC6, makefile + proc=PPro)
+
+// huffman decoding acceleration
+#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache
+
+typedef struct
+{
+ uint8 fast[1 << FAST_BITS];
+ // weirdly, repacking this into AoS is a 10% speed loss, instead of a win
+ uint16 code[256];
+ uint8 values[256];
+ uint8 size[257];
+ unsigned int maxcode[18];
+ int delta[17]; // old 'firstsymbol' - old 'firstcode'
+} huffman;
+
+typedef struct
+{
+ #if STBI_SIMD
+ unsigned short dequant2[4][64];
+ #endif
+ stbi s;
+ huffman huff_dc[4];
+ huffman huff_ac[4];
+ uint8 dequant[4][64];
+
+// sizes for components, interleaved MCUs
+ int img_h_max, img_v_max;
+ int img_mcu_x, img_mcu_y;
+ int img_mcu_w, img_mcu_h;
+
+// definition of jpeg image component
+ struct
+ {
+ int id;
+ int h,v;
+ int tq;
+ int hd,ha;
+ int dc_pred;
+
+ int x,y,w2,h2;
+ uint8 *data;
+ void *raw_data;
+ uint8 *linebuf;
+ } img_comp[4];
+
+ uint32 code_buffer; // jpeg entropy-coded buffer
+ int code_bits; // number of valid bits
+ unsigned char marker; // marker seen while filling entropy buffer
+ int nomore; // flag if we saw a marker so must stop
+
+ int scan_n, order[4];
+ int restart_interval, todo;
+} jpeg;
+
+static int build_huffman(huffman *h, int *count)
+{
+ int i,j,k=0,code;
+ // build size list for each symbol (from JPEG spec)
+ for (i=0; i < 16; ++i)
+ for (j=0; j < count[i]; ++j)
+ h->size[k++] = (uint8) (i+1);
+ h->size[k] = 0;
+
+ // compute actual symbols (from jpeg spec)
+ code = 0;
+ k = 0;
+ for(j=1; j <= 16; ++j) {
+ // compute delta to add to code to compute symbol id
+ h->delta[j] = k - code;
+ if (h->size[k] == j) {
+ while (h->size[k] == j)
+ h->code[k++] = (uint16) (code++);
+ if (code-1 >= (1 << j)) return e("bad code lengths","Corrupt JPEG");
+ }
+ // compute largest code + 1 for this size, preshifted as needed later
+ h->maxcode[j] = code << (16-j);
+ code <<= 1;
+ }
+ h->maxcode[j] = 0xffffffff;
+
+ // build non-spec acceleration table; 255 is flag for not-accelerated
+ memset(h->fast, 255, 1 << FAST_BITS);
+ for (i=0; i < k; ++i) {
+ int s = h->size[i];
+ if (s <= FAST_BITS) {
+ int c = h->code[i] << (FAST_BITS-s);
+ int m = 1 << (FAST_BITS-s);
+ for (j=0; j < m; ++j) {
+ h->fast[c+j] = (uint8) i;
+ }
+ }
+ }
+ return 1;
+}
+
+static void grow_buffer_unsafe(jpeg *j)
+{
+ do {
+ int b = j->nomore ? 0 : get8(&j->s);
+ if (b == 0xff) {
+ int c = get8(&j->s);
+ if (c != 0) {
+ j->marker = (unsigned char) c;
+ j->nomore = 1;
+ return;
+ }
+ }
+ j->code_buffer = (j->code_buffer << 8) | b;
+ j->code_bits += 8;
+ } while (j->code_bits <= 24);
+}
+
+// (1 << n) - 1
+static uint32 bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535};
+
+// decode a jpeg huffman value from the bitstream
+__forceinline static int decode(jpeg *j, huffman *h)
+{
+ unsigned int temp;
+ int c,k;
+
+ if (j->code_bits < 16) grow_buffer_unsafe(j);
+
+ // look at the top FAST_BITS and determine what symbol ID it is,
+ // if the code is <= FAST_BITS
+ c = (j->code_buffer >> (j->code_bits - FAST_BITS)) & ((1 << FAST_BITS)-1);
+ k = h->fast[c];
+ if (k < 255) {
+ if (h->size[k] > j->code_bits)
+ return -1;
+ j->code_bits -= h->size[k];
+ return h->values[k];
+ }
+
+ // naive test is to shift the code_buffer down so k bits are
+ // valid, then test against maxcode. To speed this up, we've
+ // preshifted maxcode left so that it has (16-k) 0s at the
+ // end; in other words, regardless of the number of bits, it
+ // wants to be compared against something shifted to have 16;
+ // that way we don't need to shift inside the loop.
+ if (j->code_bits < 16)
+ temp = (j->code_buffer << (16 - j->code_bits)) & 0xffff;
+ else
+ temp = (j->code_buffer >> (j->code_bits - 16)) & 0xffff;
+ for (k=FAST_BITS+1 ; ; ++k)
+ if (temp < h->maxcode[k])
+ break;
+ if (k == 17) {
+ // error! code not found
+ j->code_bits -= 16;
+ return -1;
+ }
+
+ if (k > j->code_bits)
+ return -1;
+
+ // convert the huffman code to the symbol id
+ c = ((j->code_buffer >> (j->code_bits - k)) & bmask[k]) + h->delta[k];
+ assert((((j->code_buffer) >> (j->code_bits - h->size[c])) & bmask[h->size[c]]) == h->code[c]);
+
+ // convert the id to a symbol
+ j->code_bits -= k;
+ return h->values[c];
+}
+
+// combined JPEG 'receive' and JPEG 'extend', since baseline
+// always extends everything it receives.
+__forceinline static int extend_receive(jpeg *j, int n)
+{
+ unsigned int m = 1 << (n-1);
+ unsigned int k;
+ if (j->code_bits < n) grow_buffer_unsafe(j);
+ k = (j->code_buffer >> (j->code_bits - n)) & bmask[n];
+ j->code_bits -= n;
+ // the following test is probably a random branch that won't
+ // predict well. I tried to table accelerate it but failed.
+ // maybe it's compiling as a conditional move?
+ if (k < m)
+ return (-1 << n) + k + 1;
+ else
+ return k;
+}
+
+// given a value that's at position X in the zigzag stream,
+// where does it appear in the 8x8 matrix coded as row-major?
+static uint8 dezigzag[64+15] =
+{
+ 0, 1, 8, 16, 9, 2, 3, 10,
+ 17, 24, 32, 25, 18, 11, 4, 5,
+ 12, 19, 26, 33, 40, 48, 41, 34,
+ 27, 20, 13, 6, 7, 14, 21, 28,
+ 35, 42, 49, 56, 57, 50, 43, 36,
+ 29, 22, 15, 23, 30, 37, 44, 51,
+ 58, 59, 52, 45, 38, 31, 39, 46,
+ 53, 60, 61, 54, 47, 55, 62, 63,
+ // let corrupt input sample past end
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63
+};
+
+// decode one 64-entry block--
+static int decode_block(jpeg *j, short data[64], huffman *hdc, huffman *hac, int b)
+{
+ int diff,dc,k;
+ int t = decode(j, hdc);
+ if (t < 0) return e("bad huffman code","Corrupt JPEG");
+
+ // 0 all the ac values now so we can do it 32-bits at a time
+ memset(data,0,64*sizeof(data[0]));
+
+ diff = t ? extend_receive(j, t) : 0;
+ dc = j->img_comp[b].dc_pred + diff;
+ j->img_comp[b].dc_pred = dc;
+ data[0] = (short) dc;
+
+ // decode AC components, see JPEG spec
+ k = 1;
+ do {
+ int r,s;
+ int rs = decode(j, hac);
+ if (rs < 0) return e("bad huffman code","Corrupt JPEG");
+ s = rs & 15;
+ r = rs >> 4;
+ if (s == 0) {
+ if (rs != 0xf0) break; // end block
+ k += 16;
+ } else {
+ k += r;
+ // decode into unzigzag'd location
+ data[dezigzag[k++]] = (short) extend_receive(j,s);
+ }
+ } while (k < 64);
+ return 1;
+}
+
+// take a -128..127 value and clamp it and convert to 0..255
+__forceinline static uint8 clamp(int x)
+{
+ x += 128;
+ // trick to use a single test to catch both cases
+ if ((unsigned int) x > 255) {
+ if (x < 0) return 0;
+ if (x > 255) return 255;
+ }
+ return (uint8) x;
+}
+
+#define f2f(x) (int) (((x) * 4096 + 0.5))
+#define fsh(x) ((x) << 12)
+
+// derived from jidctint -- DCT_ISLOW
+#define IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \
+ int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \
+ p2 = s2; \
+ p3 = s6; \
+ p1 = (p2+p3) * f2f(0.5411961f); \
+ t2 = p1 + p3*f2f(-1.847759065f); \
+ t3 = p1 + p2*f2f( 0.765366865f); \
+ p2 = s0; \
+ p3 = s4; \
+ t0 = fsh(p2+p3); \
+ t1 = fsh(p2-p3); \
+ x0 = t0+t3; \
+ x3 = t0-t3; \
+ x1 = t1+t2; \
+ x2 = t1-t2; \
+ t0 = s7; \
+ t1 = s5; \
+ t2 = s3; \
+ t3 = s1; \
+ p3 = t0+t2; \
+ p4 = t1+t3; \
+ p1 = t0+t3; \
+ p2 = t1+t2; \
+ p5 = (p3+p4)*f2f( 1.175875602f); \
+ t0 = t0*f2f( 0.298631336f); \
+ t1 = t1*f2f( 2.053119869f); \
+ t2 = t2*f2f( 3.072711026f); \
+ t3 = t3*f2f( 1.501321110f); \
+ p1 = p5 + p1*f2f(-0.899976223f); \
+ p2 = p5 + p2*f2f(-2.562915447f); \
+ p3 = p3*f2f(-1.961570560f); \
+ p4 = p4*f2f(-0.390180644f); \
+ t3 += p1+p4; \
+ t2 += p2+p3; \
+ t1 += p2+p4; \
+ t0 += p1+p3;
+
+#if !STBI_SIMD
+// .344 seconds on 3*anemones.jpg
+static void idct_block(uint8 *out, int out_stride, short data[64], uint8 *dequantize)
+{
+ int i,val[64],*v=val;
+ uint8 *o,*dq = dequantize;
+ short *d = data;
+
+ // columns
+ for (i=0; i < 8; ++i,++d,++dq, ++v) {
+ // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing
+ if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0
+ && d[40]==0 && d[48]==0 && d[56]==0) {
+ // no shortcut 0 seconds
+ // (1|2|3|4|5|6|7)==0 0 seconds
+ // all separate -0.047 seconds
+ // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds
+ int dcterm = d[0] * dq[0] << 2;
+ v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm;
+ } else {
+ IDCT_1D(d[ 0]*dq[ 0],d[ 8]*dq[ 8],d[16]*dq[16],d[24]*dq[24],
+ d[32]*dq[32],d[40]*dq[40],d[48]*dq[48],d[56]*dq[56])
+ // constants scaled things up by 1<<12; let's bring them back
+ // down, but keep 2 extra bits of precision
+ x0 += 512; x1 += 512; x2 += 512; x3 += 512;
+ v[ 0] = (x0+t3) >> 10;
+ v[56] = (x0-t3) >> 10;
+ v[ 8] = (x1+t2) >> 10;
+ v[48] = (x1-t2) >> 10;
+ v[16] = (x2+t1) >> 10;
+ v[40] = (x2-t1) >> 10;
+ v[24] = (x3+t0) >> 10;
+ v[32] = (x3-t0) >> 10;
+ }
+ }
+
+ for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) {
+ // no fast case since the first 1D IDCT spread components out
+ IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7])
+ // constants scaled things up by 1<<12, plus we had 1<<2 from first
+ // loop, plus horizontal and vertical each scale by sqrt(8) so together
+ // we've got an extra 1<<3, so 1<<17 total we need to remove.
+ x0 += 65536; x1 += 65536; x2 += 65536; x3 += 65536;
+ o[0] = clamp((x0+t3) >> 17);
+ o[7] = clamp((x0-t3) >> 17);
+ o[1] = clamp((x1+t2) >> 17);
+ o[6] = clamp((x1-t2) >> 17);
+ o[2] = clamp((x2+t1) >> 17);
+ o[5] = clamp((x2-t1) >> 17);
+ o[3] = clamp((x3+t0) >> 17);
+ o[4] = clamp((x3-t0) >> 17);
+ }
+}
+#else
+static void idct_block(uint8 *out, int out_stride, short data[64], unsigned short *dequantize)
+{
+ int i,val[64],*v=val;
+ uint8 *o;
+ unsigned short *dq = dequantize;
+ short *d = data;
+
+ // columns
+ for (i=0; i < 8; ++i,++d,++dq, ++v) {
+ // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing
+ if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0
+ && d[40]==0 && d[48]==0 && d[56]==0) {
+ // no shortcut 0 seconds
+ // (1|2|3|4|5|6|7)==0 0 seconds
+ // all separate -0.047 seconds
+ // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds
+ int dcterm = d[0] * dq[0] << 2;
+ v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm;
+ } else {
+ IDCT_1D(d[ 0]*dq[ 0],d[ 8]*dq[ 8],d[16]*dq[16],d[24]*dq[24],
+ d[32]*dq[32],d[40]*dq[40],d[48]*dq[48],d[56]*dq[56])
+ // constants scaled things up by 1<<12; let's bring them back
+ // down, but keep 2 extra bits of precision
+ x0 += 512; x1 += 512; x2 += 512; x3 += 512;
+ v[ 0] = (x0+t3) >> 10;
+ v[56] = (x0-t3) >> 10;
+ v[ 8] = (x1+t2) >> 10;
+ v[48] = (x1-t2) >> 10;
+ v[16] = (x2+t1) >> 10;
+ v[40] = (x2-t1) >> 10;
+ v[24] = (x3+t0) >> 10;
+ v[32] = (x3-t0) >> 10;
+ }
+ }
+
+ for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) {
+ // no fast case since the first 1D IDCT spread components out
+ IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7])
+ // constants scaled things up by 1<<12, plus we had 1<<2 from first
+ // loop, plus horizontal and vertical each scale by sqrt(8) so together
+ // we've got an extra 1<<3, so 1<<17 total we need to remove.
+ x0 += 65536; x1 += 65536; x2 += 65536; x3 += 65536;
+ o[0] = clamp((x0+t3) >> 17);
+ o[7] = clamp((x0-t3) >> 17);
+ o[1] = clamp((x1+t2) >> 17);
+ o[6] = clamp((x1-t2) >> 17);
+ o[2] = clamp((x2+t1) >> 17);
+ o[5] = clamp((x2-t1) >> 17);
+ o[3] = clamp((x3+t0) >> 17);
+ o[4] = clamp((x3-t0) >> 17);
+ }
+}
+static stbi_idct_8x8 stbi_idct_installed = idct_block;
+
+extern void stbi_install_idct(stbi_idct_8x8 func)
+{
+ stbi_idct_installed = func;
+}
+#endif
+
+#define MARKER_none 0xff
+// if there's a pending marker from the entropy stream, return that
+// otherwise, fetch from the stream and get a marker. if there's no
+// marker, return 0xff, which is never a valid marker value
+static uint8 get_marker(jpeg *j)
+{
+ uint8 x;
+ if (j->marker != MARKER_none) { x = j->marker; j->marker = MARKER_none; return x; }
+ x = get8u(&j->s);
+ if (x != 0xff) return MARKER_none;
+ while (x == 0xff)
+ x = get8u(&j->s);
+ return x;
+}
+
+// in each scan, we'll have scan_n components, and the order
+// of the components is specified by order[]
+#define RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7)
+
+// after a restart interval, reset the entropy decoder and
+// the dc prediction
+static void reset(jpeg *j)
+{
+ j->code_bits = 0;
+ j->code_buffer = 0;
+ j->nomore = 0;
+ j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = 0;
+ j->marker = MARKER_none;
+ j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff;
+ // no more than 1<<31 MCUs if no restart_interal? that's plenty safe,
+ // since we don't even allow 1<<30 pixels
+}
+
+static int parse_entropy_coded_data(jpeg *z)
+{
+ reset(z);
+ if (z->scan_n == 1) {
+ int i,j;
+ #if STBI_SIMD
+ __declspec(align(16))
+ #endif
+ short data[64];
+ int n = z->order[0];
+ // non-interleaved data, we just need to process one block at a time,
+ // in trivial scanline order
+ // number of blocks to do just depends on how many actual "pixels" this
+ // component has, independent of interleaved MCU blocking and such
+ int w = (z->img_comp[n].x+7) >> 3;
+ int h = (z->img_comp[n].y+7) >> 3;
+ for (j=0; j < h; ++j) {
+ for (i=0; i < w; ++i) {
+ if (!decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+z->img_comp[n].ha, n)) return 0;
+ #if STBI_SIMD
+ stbi_idct_installed(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data, z->dequant2[z->img_comp[n].tq]);
+ #else
+ idct_block(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data, z->dequant[z->img_comp[n].tq]);
+ #endif
+ // every data block is an MCU, so countdown the restart interval
+ if (--z->todo <= 0) {
+ if (z->code_bits < 24) grow_buffer_unsafe(z);
+ // if it's NOT a restart, then just bail, so we get corrupt data
+ // rather than no data
+ if (!RESTART(z->marker)) return 1;
+ reset(z);
+ }
+ }
+ }
+ } else { // interleaved!
+ int i,j,k,x,y;
+ short data[64];
+ for (j=0; j < z->img_mcu_y; ++j) {
+ for (i=0; i < z->img_mcu_x; ++i) {
+ // scan an interleaved mcu... process scan_n components in order
+ for (k=0; k < z->scan_n; ++k) {
+ int n = z->order[k];
+ // scan out an mcu's worth of this component; that's just determined
+ // by the basic H and V specified for the component
+ for (y=0; y < z->img_comp[n].v; ++y) {
+ for (x=0; x < z->img_comp[n].h; ++x) {
+ int x2 = (i*z->img_comp[n].h + x)*8;
+ int y2 = (j*z->img_comp[n].v + y)*8;
+ if (!decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+z->img_comp[n].ha, n)) return 0;
+ #if STBI_SIMD
+ stbi_idct_installed(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data, z->dequant2[z->img_comp[n].tq]);
+ #else
+ idct_block(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data, z->dequant[z->img_comp[n].tq]);
+ #endif
+ }
+ }
+ }
+ // after all interleaved components, that's an interleaved MCU,
+ // so now count down the restart interval
+ if (--z->todo <= 0) {
+ if (z->code_bits < 24) grow_buffer_unsafe(z);
+ // if it's NOT a restart, then just bail, so we get corrupt data
+ // rather than no data
+ if (!RESTART(z->marker)) return 1;
+ reset(z);
+ }
+ }
+ }
+ }
+ return 1;
+}
+
+static int process_marker(jpeg *z, int m)
+{
+ int L;
+ switch (m) {
+ case MARKER_none: // no marker found
+ return e("expected marker","Corrupt JPEG");
+
+ case 0xC2: // SOF - progressive
+ return e("progressive jpeg","JPEG format not supported (progressive)");
+
+ case 0xDD: // DRI - specify restart interval
+ if (get16(&z->s) != 4) return e("bad DRI len","Corrupt JPEG");
+ z->restart_interval = get16(&z->s);
+ return 1;
+
+ case 0xDB: // DQT - define quantization table
+ L = get16(&z->s)-2;
+ while (L > 0) {
+ int q = get8(&z->s);
+ int p = q >> 4;
+ int t = q & 15,i;
+ if (p != 0) return e("bad DQT type","Corrupt JPEG");
+ if (t > 3) return e("bad DQT table","Corrupt JPEG");
+ for (i=0; i < 64; ++i)
+ z->dequant[t][dezigzag[i]] = get8u(&z->s);
+ #if STBI_SIMD
+ for (i=0; i < 64; ++i)
+ z->dequant2[t][i] = dequant[t][i];
+ #endif
+ L -= 65;
+ }
+ return L==0;
+
+ case 0xC4: // DHT - define huffman table
+ L = get16(&z->s)-2;
+ while (L > 0) {
+ uint8 *v;
+ int sizes[16],i,m=0;
+ int q = get8(&z->s);
+ int tc = q >> 4;
+ int th = q & 15;
+ if (tc > 1 || th > 3) return e("bad DHT header","Corrupt JPEG");
+ for (i=0; i < 16; ++i) {
+ sizes[i] = get8(&z->s);
+ m += sizes[i];
+ }
+ L -= 17;
+ if (tc == 0) {
+ if (!build_huffman(z->huff_dc+th, sizes)) return 0;
+ v = z->huff_dc[th].values;
+ } else {
+ if (!build_huffman(z->huff_ac+th, sizes)) return 0;
+ v = z->huff_ac[th].values;
+ }
+ for (i=0; i < m; ++i)
+ v[i] = get8u(&z->s);
+ L -= m;
+ }
+ return L==0;
+ }
+ // check for comment block or APP blocks
+ if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) {
+ skip(&z->s, get16(&z->s)-2);
+ return 1;
+ }
+ return 0;
+}
+
+// after we see SOS
+static int process_scan_header(jpeg *z)
+{
+ int i;
+ int Ls = get16(&z->s);
+ z->scan_n = get8(&z->s);
+ if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s.img_n) return e("bad SOS component count","Corrupt JPEG");
+ if (Ls != 6+2*z->scan_n) return e("bad SOS len","Corrupt JPEG");
+ for (i=0; i < z->scan_n; ++i) {
+ int id = get8(&z->s), which;
+ int q = get8(&z->s);
+ for (which = 0; which < z->s.img_n; ++which)
+ if (z->img_comp[which].id == id)
+ break;
+ if (which == z->s.img_n) return 0;
+ z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return e("bad DC huff","Corrupt JPEG");
+ z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return e("bad AC huff","Corrupt JPEG");
+ z->order[i] = which;
+ }
+ if (get8(&z->s) != 0) return e("bad SOS","Corrupt JPEG");
+ get8(&z->s); // should be 63, but might be 0
+ if (get8(&z->s) != 0) return e("bad SOS","Corrupt JPEG");
+
+ return 1;
+}
+
+static int process_frame_header(jpeg *z, int scan)
+{
+ stbi *s = &z->s;
+ int Lf,p,i,q, h_max=1,v_max=1,c;
+ Lf = get16(s); if (Lf < 11) return e("bad SOF len","Corrupt JPEG"); // JPEG
+ p = get8(s); if (p != 8) return e("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline
+ s->img_y = get16(s); if (s->img_y == 0) return e("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG
+ s->img_x = get16(s); if (s->img_x == 0) return e("0 width","Corrupt JPEG"); // JPEG requires
+ c = get8(s);
+ if (c != 3 && c != 1) return e("bad component count","Corrupt JPEG"); // JFIF requires
+ s->img_n = c;
+ for (i=0; i < c; ++i) {
+ z->img_comp[i].data = NULL;
+ z->img_comp[i].linebuf = NULL;
+ }
+
+ if (Lf != 8+3*s->img_n) return e("bad SOF len","Corrupt JPEG");
+
+ for (i=0; i < s->img_n; ++i) {
+ z->img_comp[i].id = get8(s);
+ if (z->img_comp[i].id != i+1) // JFIF requires
+ if (z->img_comp[i].id != i) // some version of jpegtran outputs non-JFIF-compliant files!
+ return e("bad component ID","Corrupt JPEG");
+ q = get8(s);
+ z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return e("bad H","Corrupt JPEG");
+ z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return e("bad V","Corrupt JPEG");
+ z->img_comp[i].tq = get8(s); if (z->img_comp[i].tq > 3) return e("bad TQ","Corrupt JPEG");
+ }
+
+ if (scan != SCAN_load) return 1;
+
+ if ((1 << 30) / s->img_x / s->img_n < s->img_y) return e("too large", "Image too large to decode");
+
+ for (i=0; i < s->img_n; ++i) {
+ if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h;
+ if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v;
+ }
+
+ // compute interleaved mcu info
+ z->img_h_max = h_max;
+ z->img_v_max = v_max;
+ z->img_mcu_w = h_max * 8;
+ z->img_mcu_h = v_max * 8;
+ z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w;
+ z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h;
+
+ for (i=0; i < s->img_n; ++i) {
+ // number of effective pixels (e.g. for non-interleaved MCU)
+ z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max;
+ z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max;
+ // to simplify generation, we'll allocate enough memory to decode
+ // the bogus oversized data from using interleaved MCUs and their
+ // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't
+ // discard the extra data until colorspace conversion
+ z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8;
+ z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8;
+ z->img_comp[i].raw_data = malloc(z->img_comp[i].w2 * z->img_comp[i].h2+15);
+ if (z->img_comp[i].raw_data == NULL) {
+ for(--i; i >= 0; --i) {
+ free(z->img_comp[i].raw_data);
+ z->img_comp[i].data = NULL;
+ }
+ return e("outofmem", "Out of memory");
+ }
+ // align blocks for installable-idct using mmx/sse
+ z->img_comp[i].data = (uint8*) (((size_t) z->img_comp[i].raw_data + 15) & ~15);
+ z->img_comp[i].linebuf = NULL;
+ }
+
+ return 1;
+}
+
+// use comparisons since in some cases we handle more than one case (e.g. SOF)
+#define DNL(x) ((x) == 0xdc)
+#define SOI(x) ((x) == 0xd8)
+#define EOI(x) ((x) == 0xd9)
+#define SOF(x) ((x) == 0xc0 || (x) == 0xc1)
+#define SOS(x) ((x) == 0xda)
+
+static int decode_jpeg_header(jpeg *z, int scan)
+{
+ int m;
+ z->marker = MARKER_none; // initialize cached marker to empty
+ m = get_marker(z);
+ if (!SOI(m)) return e("no SOI","Corrupt JPEG");
+ if (scan == SCAN_type) return 1;
+ m = get_marker(z);
+ while (!SOF(m)) {
+ if (!process_marker(z,m)) return 0;
+ m = get_marker(z);
+ while (m == MARKER_none) {
+ // some files have extra padding after their blocks, so ok, we'll scan
+ if (at_eof(&z->s)) return e("no SOF", "Corrupt JPEG");
+ m = get_marker(z);
+ }
+ }
+ if (!process_frame_header(z, scan)) return 0;
+ return 1;
+}
+
+static int decode_jpeg_image(jpeg *j)
+{
+ int m;
+ j->restart_interval = 0;
+ if (!decode_jpeg_header(j, SCAN_load)) return 0;
+ m = get_marker(j);
+ while (!EOI(m)) {
+ if (SOS(m)) {
+ if (!process_scan_header(j)) return 0;
+ if (!parse_entropy_coded_data(j)) return 0;
+ } else {
+ if (!process_marker(j, m)) return 0;
+ }
+ m = get_marker(j);
+ }
+ return 1;
+}
+
+// static jfif-centered resampling (across block boundaries)
+
+typedef uint8 *(*resample_row_func)(uint8 *out, uint8 *in0, uint8 *in1,
+ int w, int hs);
+
+#define div4(x) ((uint8) ((x) >> 2))
+
+static uint8 *resample_row_1(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs)
+{
+ return in_near;
+}
+
+static uint8* resample_row_v_2(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs)
+{
+ // need to generate two samples vertically for every one in input
+ int i;
+ for (i=0; i < w; ++i)
+ out[i] = div4(3*in_near[i] + in_far[i] + 2);
+ return out;
+}
+
+static uint8* resample_row_h_2(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs)
+{
+ // need to generate two samples horizontally for every one in input
+ int i;
+ uint8 *input = in_near;
+ if (w == 1) {
+ // if only one sample, can't do any interpolation
+ out[0] = out[1] = input[0];
+ return out;
+ }
+
+ out[0] = input[0];
+ out[1] = div4(input[0]*3 + input[1] + 2);
+ for (i=1; i < w-1; ++i) {
+ int n = 3*input[i]+2;
+ out[i*2+0] = div4(n+input[i-1]);
+ out[i*2+1] = div4(n+input[i+1]);
+ }
+ out[i*2+0] = div4(input[w-2]*3 + input[w-1] + 2);
+ out[i*2+1] = input[w-1];
+ return out;
+}
+
+#define div16(x) ((uint8) ((x) >> 4))
+
+static uint8 *resample_row_hv_2(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs)
+{
+ // need to generate 2x2 samples for every one in input
+ int i,t0,t1;
+ if (w == 1) {
+ out[0] = out[1] = div4(3*in_near[0] + in_far[0] + 2);
+ return out;
+ }
+
+ t1 = 3*in_near[0] + in_far[0];
+ out[0] = div4(t1+2);
+ for (i=1; i < w; ++i) {
+ t0 = t1;
+ t1 = 3*in_near[i]+in_far[i];
+ out[i*2-1] = div16(3*t0 + t1 + 8);
+ out[i*2 ] = div16(3*t1 + t0 + 8);
+ }
+ out[w*2-1] = div4(t1+2);
+ return out;
+}
+
+static uint8 *resample_row_generic(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs)
+{
+ // resample with nearest-neighbor
+ int i,j;
+ for (i=0; i < w; ++i)
+ for (j=0; j < hs; ++j)
+ out[i*hs+j] = in_near[i];
+ return out;
+}
+
+#define float2fixed(x) ((int) ((x) * 65536 + 0.5))
+
+// 0.38 seconds on 3*anemones.jpg (0.25 with processor = Pro)
+// VC6 without processor=Pro is generating multiple LEAs per multiply!
+static void YCbCr_to_RGB_row(uint8 *out, uint8 *y, uint8 *pcb, uint8 *pcr, int count, int step)
+{
+ int i;
+ for (i=0; i < count; ++i) {
+ int y_fixed = (y[i] << 16) + 32768; // rounding
+ int r,g,b;
+ int cr = pcr[i] - 128;
+ int cb = pcb[i] - 128;
+ r = y_fixed + cr*float2fixed(1.40200f);
+ g = y_fixed - cr*float2fixed(0.71414f) - cb*float2fixed(0.34414f);
+ b = y_fixed + cb*float2fixed(1.77200f);
+ r >>= 16;
+ g >>= 16;
+ b >>= 16;
+ if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; }
+ if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; }
+ if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; }
+ out[0] = (uint8)r;
+ out[1] = (uint8)g;
+ out[2] = (uint8)b;
+ out[3] = 255;
+ out += step;
+ }
+}
+
+#if STBI_SIMD
+static stbi_YCbCr_to_RGB_run stbi_YCbCr_installed = YCbCr_to_RGB_row;
+
+void stbi_install_YCbCr_to_RGB(stbi_YCbCr_to_RGB_run func)
+{
+ stbi_YCbCr_installed = func;
+}
+#endif
+
+
+// clean up the temporary component buffers
+static void cleanup_jpeg(jpeg *j)
+{
+ int i;
+ for (i=0; i < j->s.img_n; ++i) {
+ if (j->img_comp[i].data) {
+ free(j->img_comp[i].raw_data);
+ j->img_comp[i].data = NULL;
+ }
+ if (j->img_comp[i].linebuf) {
+ free(j->img_comp[i].linebuf);
+ j->img_comp[i].linebuf = NULL;
+ }
+ }
+}
+
+typedef struct
+{
+ resample_row_func resample;
+ uint8 *line0,*line1;
+ int hs,vs; // expansion factor in each axis
+ int w_lores; // horizontal pixels pre-expansion
+ int ystep; // how far through vertical expansion we are
+ int ypos; // which pre-expansion row we're on
+} stbi_resample;
+
+static uint8 *load_jpeg_image(jpeg *z, int *out_x, int *out_y, int *comp, int req_comp)
+{
+ int n, decode_n;
+ // validate req_comp
+ if (req_comp < 0 || req_comp > 4) return epuc("bad req_comp", "Internal error");
+ z->s.img_n = 0;
+
+ // load a jpeg image from whichever source
+ if (!decode_jpeg_image(z)) { cleanup_jpeg(z); return NULL; }
+
+ // determine actual number of components to generate
+ n = req_comp ? req_comp : z->s.img_n;
+
+ if (z->s.img_n == 3 && n < 3)
+ decode_n = 1;
+ else
+ decode_n = z->s.img_n;
+
+ // resample and color-convert
+ {
+ int k;
+ uint i,j;
+ uint8 *output;
+ uint8 *coutput[4];
+
+ stbi_resample res_comp[4];
+
+ for (k=0; k < decode_n; ++k) {
+ stbi_resample *r = &res_comp[k];
+
+ // allocate line buffer big enough for upsampling off the edges
+ // with upsample factor of 4
+ z->img_comp[k].linebuf = (uint8 *) malloc(z->s.img_x + 3);
+ if (!z->img_comp[k].linebuf) { cleanup_jpeg(z); return epuc("outofmem", "Out of memory"); }
+
+ r->hs = z->img_h_max / z->img_comp[k].h;
+ r->vs = z->img_v_max / z->img_comp[k].v;
+ r->ystep = r->vs >> 1;
+ r->w_lores = (z->s.img_x + r->hs-1) / r->hs;
+ r->ypos = 0;
+ r->line0 = r->line1 = z->img_comp[k].data;
+
+ if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1;
+ else if (r->hs == 1 && r->vs == 2) r->resample = resample_row_v_2;
+ else if (r->hs == 2 && r->vs == 1) r->resample = resample_row_h_2;
+ else if (r->hs == 2 && r->vs == 2) r->resample = resample_row_hv_2;
+ else r->resample = resample_row_generic;
+ }
+
+ // can't error after this so, this is safe
+ output = (uint8 *) malloc(n * z->s.img_x * z->s.img_y + 1);
+ if (!output) { cleanup_jpeg(z); return epuc("outofmem", "Out of memory"); }
+
+ // now go ahead and resample
+ for (j=0; j < z->s.img_y; ++j) {
+ uint8 *out = output + n * z->s.img_x * j;
+ for (k=0; k < decode_n; ++k) {
+ stbi_resample *r = &res_comp[k];
+ int y_bot = r->ystep >= (r->vs >> 1);
+ coutput[k] = r->resample(z->img_comp[k].linebuf,
+ y_bot ? r->line1 : r->line0,
+ y_bot ? r->line0 : r->line1,
+ r->w_lores, r->hs);
+ if (++r->ystep >= r->vs) {
+ r->ystep = 0;
+ r->line0 = r->line1;
+ if (++r->ypos < z->img_comp[k].y)
+ r->line1 += z->img_comp[k].w2;
+ }
+ }
+ if (n >= 3) {
+ uint8 *y = coutput[0];
+ if (z->s.img_n == 3) {
+ #if STBI_SIMD
+ stbi_YCbCr_installed(out, y, coutput[1], coutput[2], z->s.img_x, n);
+ #else
+ YCbCr_to_RGB_row(out, y, coutput[1], coutput[2], z->s.img_x, n);
+ #endif
+ } else
+ for (i=0; i < z->s.img_x; ++i) {
+ out[0] = out[1] = out[2] = y[i];
+ out[3] = 255; // not used if n==3
+ out += n;
+ }
+ } else {
+ uint8 *y = coutput[0];
+ if (n == 1)
+ for (i=0; i < z->s.img_x; ++i) out[i] = y[i];
+ else
+ for (i=0; i < z->s.img_x; ++i) *out++ = y[i], *out++ = 255;
+ }
+ }
+ cleanup_jpeg(z);
+ *out_x = z->s.img_x;
+ *out_y = z->s.img_y;
+ if (comp) *comp = z->s.img_n; // report original components, not output
+ return output;
+ }
+}
+
+#ifndef STBI_NO_STDIO
+unsigned char *stbi_jpeg_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp)
+{
+ jpeg j;
+ start_file(&j.s, f);
+ return load_jpeg_image(&j, x,y,comp,req_comp);
+}
+
+unsigned char *stbi_jpeg_load(char const *filename, int *x, int *y, int *comp, int req_comp)
+{
+ unsigned char *data;
+ FILE *f = fopen(filename, "rb");
+ if (!f) return NULL;
+ data = stbi_jpeg_load_from_file(f,x,y,comp,req_comp);
+ fclose(f);
+ return data;
+}
+#endif
+
+unsigned char *stbi_jpeg_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp)
+{
+ jpeg j;
+ start_mem(&j.s, buffer,len);
+ return load_jpeg_image(&j, x,y,comp,req_comp);
+}
+
+#ifndef STBI_NO_STDIO
+int stbi_jpeg_test_file(FILE *f)
+{
+ int n,r;
+ jpeg j;
+ n = ftell(f);
+ start_file(&j.s, f);
+ r = decode_jpeg_header(&j, SCAN_type);
+ fseek(f,n,SEEK_SET);
+ return r;
+}
+#endif
+
+int stbi_jpeg_test_memory(stbi_uc const *buffer, int len)
+{
+ jpeg j;
+ start_mem(&j.s, buffer,len);
+ return decode_jpeg_header(&j, SCAN_type);
+}
+
+// @TODO:
+#ifndef STBI_NO_STDIO
+extern int stbi_jpeg_info (char const *filename, int *x, int *y, int *comp);
+extern int stbi_jpeg_info_from_file (FILE *f, int *x, int *y, int *comp);
+#endif
+extern int stbi_jpeg_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp);
+
+// public domain zlib decode v0.2 Sean Barrett 2006-11-18
+// simple implementation
+// - all input must be provided in an upfront buffer
+// - all output is written to a single output buffer (can malloc/realloc)
+// performance
+// - fast huffman
+
+// fast-way is faster to check than jpeg huffman, but slow way is slower
+#define ZFAST_BITS 9 // accelerate all cases in default tables
+#define ZFAST_MASK ((1 << ZFAST_BITS) - 1)
+
+// zlib-style huffman encoding
+// (jpegs packs from left, zlib from right, so can't share code)
+typedef struct
+{
+ uint16 fast[1 << ZFAST_BITS];
+ uint16 firstcode[16];
+ int maxcode[17];
+ uint16 firstsymbol[16];
+ uint8 size[288];
+ uint16 value[288];
+} zhuffman;
+
+__forceinline static int bitreverse16(int n)
+{
+ n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1);
+ n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2);
+ n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4);
+ n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8);
+ return n;
+}
+
+__forceinline static int bit_reverse(int v, int bits)
+{
+ assert(bits <= 16);
+ // to bit reverse n bits, reverse 16 and shift
+ // e.g. 11 bits, bit reverse and shift away 5
+ return bitreverse16(v) >> (16-bits);
+}
+
+static int zbuild_huffman(zhuffman *z, uint8 *sizelist, int num)
+{
+ int i,k=0;
+ int code, next_code[16], sizes[17];
+
+ // DEFLATE spec for generating codes
+ memset(sizes, 0, sizeof(sizes));
+ memset(z->fast, 255, sizeof(z->fast));
+ for (i=0; i < num; ++i)
+ ++sizes[sizelist[i]];
+ sizes[0] = 0;
+ for (i=1; i < 16; ++i)
+ assert(sizes[i] <= (1 << i));
+ code = 0;
+ for (i=1; i < 16; ++i) {
+ next_code[i] = code;
+ z->firstcode[i] = (uint16) code;
+ z->firstsymbol[i] = (uint16) k;
+ code = (code + sizes[i]);
+ if (sizes[i])
+ if (code-1 >= (1 << i)) return e("bad codelengths","Corrupt JPEG");
+ z->maxcode[i] = code << (16-i); // preshift for inner loop
+ code <<= 1;
+ k += sizes[i];
+ }
+ z->maxcode[16] = 0x10000; // sentinel
+ for (i=0; i < num; ++i) {
+ int s = sizelist[i];
+ if (s) {
+ int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s];
+ z->size[c] = (uint8)s;
+ z->value[c] = (uint16)i;
+ if (s <= ZFAST_BITS) {
+ int k = bit_reverse(next_code[s],s);
+ while (k < (1 << ZFAST_BITS)) {
+ z->fast[k] = (uint16) c;
+ k += (1 << s);
+ }
+ }
+ ++next_code[s];
+ }
+ }
+ return 1;
+}
+
+// zlib-from-memory implementation for PNG reading
+// because PNG allows splitting the zlib stream arbitrarily,
+// and it's annoying structurally to have PNG call ZLIB call PNG,
+// we require PNG read all the IDATs and combine them into a single
+// memory buffer
+
+typedef struct
+{
+ uint8 *zbuffer, *zbuffer_end;
+ int num_bits;
+ uint32 code_buffer;
+
+ char *zout;
+ char *zout_start;
+ char *zout_end;
+ int z_expandable;
+
+ zhuffman z_length, z_distance;
+} zbuf;
+
+__forceinline static int zget8(zbuf *z)
+{
+ if (z->zbuffer >= z->zbuffer_end) return 0;
+ return *z->zbuffer++;
+}
+
+static void fill_bits(zbuf *z)
+{
+ do {
+ assert(z->code_buffer < (1U << z->num_bits));
+ z->code_buffer |= zget8(z) << z->num_bits;
+ z->num_bits += 8;
+ } while (z->num_bits <= 24);
+}
+
+__forceinline static unsigned int zreceive(zbuf *z, int n)
+{
+ unsigned int k;
+ if (z->num_bits < n) fill_bits(z);
+ k = z->code_buffer & ((1 << n) - 1);
+ z->code_buffer >>= n;
+ z->num_bits -= n;
+ return k;
+}
+
+__forceinline static int zhuffman_decode(zbuf *a, zhuffman *z)
+{
+ int b,s,k;
+ if (a->num_bits < 16) fill_bits(a);
+ b = z->fast[a->code_buffer & ZFAST_MASK];
+ if (b < 0xffff) {
+ s = z->size[b];
+ a->code_buffer >>= s;
+ a->num_bits -= s;
+ return z->value[b];
+ }
+
+ // not resolved by fast table, so compute it the slow way
+ // use jpeg approach, which requires MSbits at top
+ k = bit_reverse(a->code_buffer, 16);
+ for (s=ZFAST_BITS+1; ; ++s)
+ if (k < z->maxcode[s])
+ break;
+ if (s == 16) return -1; // invalid code!
+ // code size is s, so:
+ b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s];
+ assert(z->size[b] == s);
+ a->code_buffer >>= s;
+ a->num_bits -= s;
+ return z->value[b];
+}
+
+static int expand(zbuf *z, int n) // need to make room for n bytes
+{
+ char *q;
+ int cur, limit;
+ if (!z->z_expandable) return e("output buffer limit","Corrupt PNG");
+ cur = (int) (z->zout - z->zout_start);
+ limit = (int) (z->zout_end - z->zout_start);
+ while (cur + n > limit)
+ limit *= 2;
+ q = (char *) realloc(z->zout_start, limit);
+ if (q == NULL) return e("outofmem", "Out of memory");
+ z->zout_start = q;
+ z->zout = q + cur;
+ z->zout_end = q + limit;
+ return 1;
+}
+
+static int length_base[31] = {
+ 3,4,5,6,7,8,9,10,11,13,
+ 15,17,19,23,27,31,35,43,51,59,
+ 67,83,99,115,131,163,195,227,258,0,0 };
+
+static int length_extra[31]=
+{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 };
+
+static int dist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,
+257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0};
+
+static int dist_extra[32] =
+{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13};
+
+static int parse_huffman_block(zbuf *a)
+{
+ for(;;) {
+ int z = zhuffman_decode(a, &a->z_length);
+ if (z < 256) {
+ if (z < 0) return e("bad huffman code","Corrupt PNG"); // error in huffman codes
+ if (a->zout >= a->zout_end) if (!expand(a, 1)) return 0;
+ *a->zout++ = (char) z;
+ } else {
+ uint8 *p;
+ int len,dist;
+ if (z == 256) return 1;
+ z -= 257;
+ len = length_base[z];
+ if (length_extra[z]) len += zreceive(a, length_extra[z]);
+ z = zhuffman_decode(a, &a->z_distance);
+ if (z < 0) return e("bad huffman code","Corrupt PNG");
+ dist = dist_base[z];
+ if (dist_extra[z]) dist += zreceive(a, dist_extra[z]);
+ if (a->zout - a->zout_start < dist) return e("bad dist","Corrupt PNG");
+ if (a->zout + len > a->zout_end) if (!expand(a, len)) return 0;
+ p = (uint8 *) (a->zout - dist);
+ while (len--)
+ *a->zout++ = *p++;
+ }
+ }
+}
+
+static int compute_huffman_codes(zbuf *a)
+{
+ static uint8 length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 };
+ static zhuffman z_codelength; // static just to save stack space
+ uint8 lencodes[286+32+137];//padding for maximum single op
+ uint8 codelength_sizes[19];
+ int i,n;
+
+ int hlit = zreceive(a,5) + 257;
+ int hdist = zreceive(a,5) + 1;
+ int hclen = zreceive(a,4) + 4;
+
+ memset(codelength_sizes, 0, sizeof(codelength_sizes));
+ for (i=0; i < hclen; ++i) {
+ int s = zreceive(a,3);
+ codelength_sizes[length_dezigzag[i]] = (uint8) s;
+ }
+ if (!zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0;
+
+ n = 0;
+ while (n < hlit + hdist) {
+ int c = zhuffman_decode(a, &z_codelength);
+ assert(c >= 0 && c < 19);
+ if (c < 16)
+ lencodes[n++] = (uint8) c;
+ else if (c == 16) {
+ c = zreceive(a,2)+3;
+ memset(lencodes+n, lencodes[n-1], c);
+ n += c;
+ } else if (c == 17) {
+ c = zreceive(a,3)+3;
+ memset(lencodes+n, 0, c);
+ n += c;
+ } else {
+ assert(c == 18);
+ c = zreceive(a,7)+11;
+ memset(lencodes+n, 0, c);
+ n += c;
+ }
+ }
+ if (n != hlit+hdist) return e("bad codelengths","Corrupt PNG");
+ if (!zbuild_huffman(&a->z_length, lencodes, hlit)) return 0;
+ if (!zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0;
+ return 1;
+}
+
+static int parse_uncompressed_block(zbuf *a)
+{
+ uint8 header[4];
+ int len,nlen,k;
+ if (a->num_bits & 7)
+ zreceive(a, a->num_bits & 7); // discard
+ // drain the bit-packed data into header
+ k = 0;
+ while (a->num_bits > 0) {
+ header[k++] = (uint8) (a->code_buffer & 255); // wtf this warns?
+ a->code_buffer >>= 8;
+ a->num_bits -= 8;
+ }
+ assert(a->num_bits == 0);
+ // now fill header the normal way
+ while (k < 4)
+ header[k++] = (uint8) zget8(a);
+ len = header[1] * 256 + header[0];
+ nlen = header[3] * 256 + header[2];
+ if (nlen != (len ^ 0xffff)) return e("zlib corrupt","Corrupt PNG");
+ if (a->zbuffer + len > a->zbuffer_end) return e("read past buffer","Corrupt PNG");
+ if (a->zout + len > a->zout_end)
+ if (!expand(a, len)) return 0;
+ memcpy(a->zout, a->zbuffer, len);
+ a->zbuffer += len;
+ a->zout += len;
+ return 1;
+}
+
+static int parse_zlib_header(zbuf *a)
+{
+ int cmf = zget8(a);
+ int cm = cmf & 15;
+ /* int cinfo = cmf >> 4; */
+ int flg = zget8(a);
+ if ((cmf*256+flg) % 31 != 0) return e("bad zlib header","Corrupt PNG"); // zlib spec
+ if (flg & 32) return e("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png
+ if (cm != 8) return e("bad compression","Corrupt PNG"); // DEFLATE required for png
+ // window = 1 << (8 + cinfo)... but who cares, we fully buffer output
+ return 1;
+}
+
+// @TODO: should statically initialize these for optimal thread safety
+static uint8 default_length[288], default_distance[32];
+static void init_defaults(void)
+{
+ int i; // use <= to match clearly with spec
+ for (i=0; i <= 143; ++i) default_length[i] = 8;
+ for ( ; i <= 255; ++i) default_length[i] = 9;
+ for ( ; i <= 279; ++i) default_length[i] = 7;
+ for ( ; i <= 287; ++i) default_length[i] = 8;
+
+ for (i=0; i <= 31; ++i) default_distance[i] = 5;
+}
+
+static int parse_zlib(zbuf *a, int parse_header)
+{
+ int final, type;
+ if (parse_header)
+ if (!parse_zlib_header(a)) return 0;
+ a->num_bits = 0;
+ a->code_buffer = 0;
+ do {
+ final = zreceive(a,1);
+ type = zreceive(a,2);
+ if (type == 0) {
+ if (!parse_uncompressed_block(a)) return 0;
+ } else if (type == 3) {
+ return 0;
+ } else {
+ if (type == 1) {
+ // use fixed code lengths
+ if (!default_distance[31]) init_defaults();
+ if (!zbuild_huffman(&a->z_length , default_length , 288)) return 0;
+ if (!zbuild_huffman(&a->z_distance, default_distance, 32)) return 0;
+ } else {
+ if (!compute_huffman_codes(a)) return 0;
+ }
+ if (!parse_huffman_block(a)) return 0;
+ }
+ } while (!final);
+ return 1;
+}
+
+static int do_zlib(zbuf *a, char *obuf, int olen, int exp, int parse_header)
+{
+ a->zout_start = obuf;
+ a->zout = obuf;
+ a->zout_end = obuf + olen;
+ a->z_expandable = exp;
+
+ return parse_zlib(a, parse_header);
+}
+
+char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen)
+{
+ zbuf a;
+ char *p = (char *) malloc(initial_size);
+ if (p == NULL) return NULL;
+ a.zbuffer = (uint8 *) buffer;
+ a.zbuffer_end = (uint8 *) buffer + len;
+ if (do_zlib(&a, p, initial_size, 1, 1)) {
+ if (outlen) *outlen = (int) (a.zout - a.zout_start);
+ return a.zout_start;
+ } else {
+ free(a.zout_start);
+ return NULL;
+ }
+}
+
+char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen)
+{
+ return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen);
+}
+
+int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen)
+{
+ zbuf a;
+ a.zbuffer = (uint8 *) ibuffer;
+ a.zbuffer_end = (uint8 *) ibuffer + ilen;
+ if (do_zlib(&a, obuffer, olen, 0, 1))
+ return (int) (a.zout - a.zout_start);
+ else
+ return -1;
+}
+
+char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen)
+{
+ zbuf a;
+ char *p = (char *) malloc(16384);
+ if (p == NULL) return NULL;
+ a.zbuffer = (uint8 *) buffer;
+ a.zbuffer_end = (uint8 *) buffer+len;
+ if (do_zlib(&a, p, 16384, 1, 0)) {
+ if (outlen) *outlen = (int) (a.zout - a.zout_start);
+ return a.zout_start;
+ } else {
+ free(a.zout_start);
+ return NULL;
+ }
+}
+
+int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen)
+{
+ zbuf a;
+ a.zbuffer = (uint8 *) ibuffer;
+ a.zbuffer_end = (uint8 *) ibuffer + ilen;
+ if (do_zlib(&a, obuffer, olen, 0, 0))
+ return (int) (a.zout - a.zout_start);
+ else
+ return -1;
+}
+
+// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18
+// simple implementation
+// - only 8-bit samples
+// - no CRC checking
+// - allocates lots of intermediate memory
+// - avoids problem of streaming data between subsystems
+// - avoids explicit window management
+// performance
+// - uses stb_zlib, a PD zlib implementation with fast huffman decoding
+
+
+typedef struct
+{
+ uint32 length;
+ uint32 type;
+} chunk;
+
+#define PNG_TYPE(a,b,c,d) (((a) << 24) + ((b) << 16) + ((c) << 8) + (d))
+
+static chunk get_chunk_header(stbi *s)
+{
+ chunk c;
+ c.length = get32(s);
+ c.type = get32(s);
+ return c;
+}
+
+static int check_png_header(stbi *s)
+{
+ static uint8 png_sig[8] = { 137,80,78,71,13,10,26,10 };
+ int i;
+ for (i=0; i < 8; ++i)
+ if (get8(s) != png_sig[i]) return e("bad png sig","Not a PNG");
+ return 1;
+}
+
+typedef struct
+{
+ stbi s;
+ uint8 *idata, *expanded, *out;
+} png;
+
+
+enum {
+ F_none=0, F_sub=1, F_up=2, F_avg=3, F_paeth=4,
+ F_avg_first, F_paeth_first,
+};
+
+static uint8 first_row_filter[5] =
+{
+ F_none, F_sub, F_none, F_avg_first, F_paeth_first
+};
+
+static int paeth(int a, int b, int c)
+{
+ int p = a + b - c;
+ int pa = abs(p-a);
+ int pb = abs(p-b);
+ int pc = abs(p-c);
+ if (pa <= pb && pa <= pc) return a;
+ if (pb <= pc) return b;
+ return c;
+}
+
+// create the png data from post-deflated data
+static int create_png_image(png *a, uint8 *raw, uint32 raw_len, int out_n)
+{
+ stbi *s = &a->s;
+ uint32 i,j,stride = s->img_x*out_n;
+ int k;
+ int img_n = s->img_n; // copy it into a local for later
+ assert(out_n == s->img_n || out_n == s->img_n+1);
+ a->out = (uint8 *) malloc(s->img_x * s->img_y * out_n);
+ if (!a->out) return e("outofmem", "Out of memory");
+ if (raw_len != (img_n * s->img_x + 1) * s->img_y) return e("not enough pixels","Corrupt PNG");
+ for (j=0; j < s->img_y; ++j) {
+ uint8 *cur = a->out + stride*j;
+ uint8 *prior = cur - stride;
+ int filter = *raw++;
+ if (filter > 4) return e("invalid filter","Corrupt PNG");
+ // if first row, use special filter that doesn't sample previous row
+ if (j == 0) filter = first_row_filter[filter];
+ // handle first pixel explicitly
+ for (k=0; k < img_n; ++k) {
+ switch(filter) {
+ case F_none : cur[k] = raw[k]; break;
+ case F_sub : cur[k] = raw[k]; break;
+ case F_up : cur[k] = raw[k] + prior[k]; break;
+ case F_avg : cur[k] = raw[k] + (prior[k]>>1); break;
+ case F_paeth : cur[k] = (uint8) (raw[k] + paeth(0,prior[k],0)); break;
+ case F_avg_first : cur[k] = raw[k]; break;
+ case F_paeth_first: cur[k] = raw[k]; break;
+ }
+ }
+ if (img_n != out_n) cur[img_n] = 255;
+ raw += img_n;
+ cur += out_n;
+ prior += out_n;
+ // this is a little gross, so that we don't switch per-pixel or per-component
+ if (img_n == out_n) {
+ #define CASE(f) \
+ case f: \
+ for (i=s->img_x-1; i >= 1; --i, raw+=img_n,cur+=img_n,prior+=img_n) \
+ for (k=0; k < img_n; ++k)
+ switch(filter) {
+ CASE(F_none) cur[k] = raw[k]; break;
+ CASE(F_sub) cur[k] = raw[k] + cur[k-img_n]; break;
+ CASE(F_up) cur[k] = raw[k] + prior[k]; break;
+ CASE(F_avg) cur[k] = raw[k] + ((prior[k] + cur[k-img_n])>>1); break;
+ CASE(F_paeth) cur[k] = (uint8) (raw[k] + paeth(cur[k-img_n],prior[k],prior[k-img_n])); break;
+ CASE(F_avg_first) cur[k] = raw[k] + (cur[k-img_n] >> 1); break;
+ CASE(F_paeth_first) cur[k] = (uint8) (raw[k] + paeth(cur[k-img_n],0,0)); break;
+ }
+ #undef CASE
+ } else {
+ assert(img_n+1 == out_n);
+ #define CASE(f) \
+ case f: \
+ for (i=s->img_x-1; i >= 1; --i, cur[img_n]=255,raw+=img_n,cur+=out_n,prior+=out_n) \
+ for (k=0; k < img_n; ++k)
+ switch(filter) {
+ CASE(F_none) cur[k] = raw[k]; break;
+ CASE(F_sub) cur[k] = raw[k] + cur[k-out_n]; break;
+ CASE(F_up) cur[k] = raw[k] + prior[k]; break;
+ CASE(F_avg) cur[k] = raw[k] + ((prior[k] + cur[k-out_n])>>1); break;
+ CASE(F_paeth) cur[k] = (uint8) (raw[k] + paeth(cur[k-out_n],prior[k],prior[k-out_n])); break;
+ CASE(F_avg_first) cur[k] = raw[k] + (cur[k-out_n] >> 1); break;
+ CASE(F_paeth_first) cur[k] = (uint8) (raw[k] + paeth(cur[k-out_n],0,0)); break;
+ }
+ #undef CASE
+ }
+ }
+ return 1;
+}
+
+static int compute_transparency(png *z, uint8 tc[3], int out_n)
+{
+ stbi *s = &z->s;
+ uint32 i, pixel_count = s->img_x * s->img_y;
+ uint8 *p = z->out;
+
+ // compute color-based transparency, assuming we've
+ // already got 255 as the alpha value in the output
+ assert(out_n == 2 || out_n == 4);
+
+ if (out_n == 2) {
+ for (i=0; i < pixel_count; ++i) {
+ p[1] = (p[0] == tc[0] ? 0 : 255);
+ p += 2;
+ }
+ } else {
+ for (i=0; i < pixel_count; ++i) {
+ if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2])
+ p[3] = 0;
+ p += 4;
+ }
+ }
+ return 1;
+}
+
+static int expand_palette(png *a, uint8 *palette, int len, int pal_img_n)
+{
+ uint32 i, pixel_count = a->s.img_x * a->s.img_y;
+ uint8 *p, *temp_out, *orig = a->out;
+
+ p = (uint8 *) malloc(pixel_count * pal_img_n);
+ if (p == NULL) return e("outofmem", "Out of memory");
+
+ // between here and free(out) below, exitting would leak
+ temp_out = p;
+
+ if (pal_img_n == 3) {
+ for (i=0; i < pixel_count; ++i) {
+ int n = orig[i]*4;
+ p[0] = palette[n ];
+ p[1] = palette[n+1];
+ p[2] = palette[n+2];
+ p += 3;
+ }
+ } else {
+ for (i=0; i < pixel_count; ++i) {
+ int n = orig[i]*4;
+ p[0] = palette[n ];
+ p[1] = palette[n+1];
+ p[2] = palette[n+2];
+ p[3] = palette[n+3];
+ p += 4;
+ }
+ }
+ free(a->out);
+ a->out = temp_out;
+ return 1;
+}
+
+static int parse_png_file(png *z, int scan, int req_comp)
+{
+ uint8 palette[1024], pal_img_n=0;
+ uint8 has_trans=0, tc[3];
+ uint32 ioff=0, idata_limit=0, i, pal_len=0;
+ int first=1,k;
+ stbi *s = &z->s;
+
+ if (!check_png_header(s)) return 0;
+
+ if (scan == SCAN_type) return 1;
+
+ for(;;first=0) {
+ chunk c = get_chunk_header(s);
+ if (first && c.type != PNG_TYPE('I','H','D','R'))
+ return e("first not IHDR","Corrupt PNG");
+ switch (c.type) {
+ case PNG_TYPE('I','H','D','R'): {
+ int depth,color,interlace,comp,filter;
+ if (!first) return e("multiple IHDR","Corrupt PNG");
+ if (c.length != 13) return e("bad IHDR len","Corrupt PNG");
+ s->img_x = get32(s); if (s->img_x > (1 << 24)) return e("too large","Very large image (corrupt?)");
+ s->img_y = get32(s); if (s->img_y > (1 << 24)) return e("too large","Very large image (corrupt?)");
+ depth = get8(s); if (depth != 8) return e("8bit only","PNG not supported: 8-bit only");
+ color = get8(s); if (color > 6) return e("bad ctype","Corrupt PNG");
+ if (color == 3) pal_img_n = 3; else if (color & 1) return e("bad ctype","Corrupt PNG");
+ comp = get8(s); if (comp) return e("bad comp method","Corrupt PNG");
+ filter= get8(s); if (filter) return e("bad filter method","Corrupt PNG");
+ interlace = get8(s); if (interlace) return e("interlaced","PNG not supported: interlaced mode");
+ if (!s->img_x || !s->img_y) return e("0-pixel image","Corrupt PNG");
+ if (!pal_img_n) {
+ s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0);
+ if ((1 << 30) / s->img_x / s->img_n < s->img_y) return e("too large", "Image too large to decode");
+ if (scan == SCAN_header) return 1;
+ } else {
+ // if paletted, then pal_n is our final components, and
+ // img_n is # components to decompress/filter.
+ s->img_n = 1;
+ if ((1 << 30) / s->img_x / 4 < s->img_y) return e("too large","Corrupt PNG");
+ // if SCAN_header, have to scan to see if we have a tRNS
+ }
+ break;
+ }
+
+ case PNG_TYPE('P','L','T','E'): {
+ if (c.length > 256*3) return e("invalid PLTE","Corrupt PNG");
+ pal_len = c.length / 3;
+ if (pal_len * 3 != c.length) return e("invalid PLTE","Corrupt PNG");
+ for (i=0; i < pal_len; ++i) {
+ palette[i*4+0] = get8u(s);
+ palette[i*4+1] = get8u(s);
+ palette[i*4+2] = get8u(s);
+ palette[i*4+3] = 255;
+ }
+ break;
+ }
+
+ case PNG_TYPE('t','R','N','S'): {
+ if (z->idata) return e("tRNS after IDAT","Corrupt PNG");
+ if (pal_img_n) {
+ if (scan == SCAN_header) { s->img_n = 4; return 1; }
+ if (pal_len == 0) return e("tRNS before PLTE","Corrupt PNG");
+ if (c.length > pal_len) return e("bad tRNS len","Corrupt PNG");
+ pal_img_n = 4;
+ for (i=0; i < c.length; ++i)
+ palette[i*4+3] = get8u(s);
+ } else {
+ if (!(s->img_n & 1)) return e("tRNS with alpha","Corrupt PNG");
+ if (c.length != (uint32) s->img_n*2) return e("bad tRNS len","Corrupt PNG");
+ has_trans = 1;
+ for (k=0; k < s->img_n; ++k)
+ tc[k] = (uint8) get16(s); // non 8-bit images will be larger
+ }
+ break;
+ }
+
+ case PNG_TYPE('I','D','A','T'): {
+ if (pal_img_n && !pal_len) return e("no PLTE","Corrupt PNG");
+ if (scan == SCAN_header) { s->img_n = pal_img_n; return 1; }
+ if (ioff + c.length > idata_limit) {
+ uint8 *p;
+ if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096;
+ while (ioff + c.length > idata_limit)
+ idata_limit *= 2;
+ p = (uint8 *) realloc(z->idata, idata_limit); if (p == NULL) return e("outofmem", "Out of memory");
+ z->idata = p;
+ }
+ #ifndef STBI_NO_STDIO
+ if (s->img_file)
+ {
+ if (fread(z->idata+ioff,1,c.length,s->img_file) != c.length) return e("outofdata","Corrupt PNG");
+ }
+ else
+ #endif
+ {
+ memcpy(z->idata+ioff, s->img_buffer, c.length);
+ s->img_buffer += c.length;
+ }
+ ioff += c.length;
+ break;
+ }
+
+ case PNG_TYPE('I','E','N','D'): {
+ uint32 raw_len;
+ if (scan != SCAN_load) return 1;
+ if (z->idata == NULL) return e("no IDAT","Corrupt PNG");
+ z->expanded = (uint8 *) stbi_zlib_decode_malloc((char *) z->idata, ioff, (int *) &raw_len);
+ if (z->expanded == NULL) return 0; // zlib should set error
+ free(z->idata); z->idata = NULL;
+ if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans)
+ s->img_out_n = s->img_n+1;
+ else
+ s->img_out_n = s->img_n;
+ if (!create_png_image(z, z->expanded, raw_len, s->img_out_n)) return 0;
+ if (has_trans)
+ if (!compute_transparency(z, tc, s->img_out_n)) return 0;
+ if (pal_img_n) {
+ // pal_img_n == 3 or 4
+ s->img_n = pal_img_n; // record the actual colors we had
+ s->img_out_n = pal_img_n;
+ if (req_comp >= 3) s->img_out_n = req_comp;
+ if (!expand_palette(z, palette, pal_len, s->img_out_n))
+ return 0;
+ }
+ free(z->expanded); z->expanded = NULL;
+ return 1;
+ }
+
+ default:
+ // if critical, fail
+ if ((c.type & (1 << 29)) == 0) {
+ #ifndef STBI_NO_FAILURE_STRINGS
+ // not threadsafe
+ static char invalid_chunk[] = "XXXX chunk not known";
+ invalid_chunk[0] = (uint8) (c.type >> 24);
+ invalid_chunk[1] = (uint8) (c.type >> 16);
+ invalid_chunk[2] = (uint8) (c.type >> 8);
+ invalid_chunk[3] = (uint8) (c.type >> 0);
+ #endif
+ return e(invalid_chunk, "PNG not supported: unknown chunk type");
+ }
+ skip(s, c.length);
+ break;
+ }
+ // end of chunk, read and skip CRC
+ get32(s);
+ }
+}
+
+static unsigned char *do_png(png *p, int *x, int *y, int *n, int req_comp)
+{
+ unsigned char *result=NULL;
+ p->expanded = NULL;
+ p->idata = NULL;
+ p->out = NULL;
+ if (req_comp < 0 || req_comp > 4) return epuc("bad req_comp", "Internal error");
+ if (parse_png_file(p, SCAN_load, req_comp)) {
+ result = p->out;
+ p->out = NULL;
+ if (req_comp && req_comp != p->s.img_out_n) {
+ result = convert_format(result, p->s.img_out_n, req_comp, p->s.img_x, p->s.img_y);
+ p->s.img_out_n = req_comp;
+ if (result == NULL) return result;
+ }
+ *x = p->s.img_x;
+ *y = p->s.img_y;
+ if (n) *n = p->s.img_n;
+ }
+ free(p->out); p->out = NULL;
+ free(p->expanded); p->expanded = NULL;
+ free(p->idata); p->idata = NULL;
+
+ return result;
+}
+
+#ifndef STBI_NO_STDIO
+unsigned char *stbi_png_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp)
+{
+ png p;
+ start_file(&p.s, f);
+ return do_png(&p, x,y,comp,req_comp);
+}
+
+unsigned char *stbi_png_load(char const *filename, int *x, int *y, int *comp, int req_comp)
+{
+ unsigned char *data;
+ FILE *f = fopen(filename, "rb");
+ if (!f) return NULL;
+ data = stbi_png_load_from_file(f,x,y,comp,req_comp);
+ fclose(f);
+ return data;
+}
+#endif
+
+unsigned char *stbi_png_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp)
+{
+ png p;
+ start_mem(&p.s, buffer,len);
+ return do_png(&p, x,y,comp,req_comp);
+}
+
+#ifndef STBI_NO_STDIO
+int stbi_png_test_file(FILE *f)
+{
+ png p;
+ int n,r;
+ n = ftell(f);
+ start_file(&p.s, f);
+ r = parse_png_file(&p, SCAN_type,STBI_default);
+ fseek(f,n,SEEK_SET);
+ return r;
+}
+#endif
+
+int stbi_png_test_memory(stbi_uc const *buffer, int len)
+{
+ png p;
+ start_mem(&p.s, buffer, len);
+ return parse_png_file(&p, SCAN_type,STBI_default);
+}
+
+// TODO: load header from png
+#ifndef STBI_NO_STDIO
+extern int stbi_png_info (char const *filename, int *x, int *y, int *comp);
+extern int stbi_png_info_from_file (FILE *f, int *x, int *y, int *comp);
+#endif
+extern int stbi_png_info_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp);
+
+// Microsoft/Windows BMP image
+
+static int bmp_test(stbi *s)
+{
+ int sz;
+ if (get8(s) != 'B') return 0;
+ if (get8(s) != 'M') return 0;
+ get32le(s); // discard filesize
+ get16le(s); // discard reserved
+ get16le(s); // discard reserved
+ get32le(s); // discard data offset
+ sz = get32le(s);
+ if (sz == 12 || sz == 40 || sz == 56 || sz == 108) return 1;
+ return 0;
+}
+
+#ifndef STBI_NO_STDIO
+int stbi_bmp_test_file (FILE *f)
+{
+ stbi s;
+ int r,n = ftell(f);
+ start_file(&s,f);
+ r = bmp_test(&s);
+ fseek(f,n,SEEK_SET);
+ return r;
+}
+#endif
+
+int stbi_bmp_test_memory (stbi_uc const *buffer, int len)
+{
+ stbi s;
+ start_mem(&s, buffer, len);
+ return bmp_test(&s);
+}
+
+// returns 0..31 for the highest set bit
+static int high_bit(unsigned int z)
+{
+ int n=0;
+ if (z == 0) return -1;
+ if (z >= 0x10000) n += 16, z >>= 16;
+ if (z >= 0x00100) n += 8, z >>= 8;
+ if (z >= 0x00010) n += 4, z >>= 4;
+ if (z >= 0x00004) n += 2, z >>= 2;
+ if (z >= 0x00002) n += 1, z >>= 1;
+ return n;
+}
+
+static int bitcount(unsigned int a)
+{
+ a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2
+ a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4
+ a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits
+ a = (a + (a >> 8)); // max 16 per 8 bits
+ a = (a + (a >> 16)); // max 32 per 8 bits
+ return a & 0xff;
+}
+
+static int shiftsigned(int v, int shift, int bits)
+{
+ int result;
+ int z=0;
+
+ if (shift < 0) v <<= -shift;
+ else v >>= shift;
+ result = v;
+
+ z = bits;
+ while (z < 8) {
+ result += v >> z;
+ z += bits;
+ }
+ return result;
+}
+
+static stbi_uc *bmp_load(stbi *s, int *x, int *y, int *comp, int req_comp)
+{
+ uint8 *out;
+ unsigned int mr=0,mg=0,mb=0,ma=0;
+ stbi_uc pal[256][4];
+ int psize=0,i,j,compress=0,width;
+ int bpp, flip_vertically, pad, target, offset, hsz;
+ if (get8(s) != 'B' || get8(s) != 'M') return epuc("not BMP", "Corrupt BMP");
+ get32le(s); // discard filesize
+ get16le(s); // discard reserved
+ get16le(s); // discard reserved
+ offset = get32le(s);
+ hsz = get32le(s);
+ if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108) return epuc("unknown BMP", "BMP type not supported: unknown");
+ failure_reason = "bad BMP";
+ if (hsz == 12) {
+ s->img_x = get16le(s);
+ s->img_y = get16le(s);
+ } else {
+ s->img_x = get32le(s);
+ s->img_y = get32le(s);
+ }
+ if (get16le(s) != 1) return 0;
+ bpp = get16le(s);
+ if (bpp == 1) return epuc("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 = get32le(s);
+ if (compress == 1 || compress == 2) return epuc("BMP RLE", "BMP type not supported: RLE");
+ get32le(s); // discard sizeof
+ get32le(s); // discard hres
+ get32le(s); // discard vres
+ get32le(s); // discard colorsused
+ get32le(s); // discard max important
+ if (hsz == 40 || hsz == 56) {
+ if (hsz == 56) {
+ get32le(s);
+ get32le(s);
+ get32le(s);
+ get32le(s);
+ }
+ if (bpp == 16 || bpp == 32) {
+ mr = mg = mb = 0;
+ if (compress == 0) {
+ if (bpp == 32) {
+ mr = 0xff << 16;
+ mg = 0xff << 8;
+ mb = 0xff << 0;
+ } else {
+ mr = 31 << 10;
+ mg = 31 << 5;
+ mb = 31 << 0;
+ }
+ } else if (compress == 3) {
+ mr = get32le(s);
+ mg = get32le(s);
+ mb = get32le(s);
+ // not documented, but generated by photoshop and handled by mspaint
+ if (mr == mg && mg == mb) {
+ // ?!?!?
+ return NULL;
+ }
+ } else
+ return NULL;
+ }
+ } else {
+ assert(hsz == 108);
+ mr = get32le(s);
+ mg = get32le(s);
+ mb = get32le(s);
+ ma = get32le(s);
+ get32le(s); // discard color space
+ for (i=0; i < 12; ++i)
+ get32le(s); // discard color space parameters
+ }
+ if (bpp < 16)
+ psize = (offset - 14 - 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 *) malloc(target * s->img_x * s->img_y);
+ if (!out) return epuc("outofmem", "Out of memory");
+ if (bpp < 16) {
+ int z=0;
+ if (psize == 0 || psize > 256) { free(out); return epuc("invalid", "Corrupt BMP"); }
+ for (i=0; i < psize; ++i) {
+ pal[i][2] = get8(s);
+ pal[i][1] = get8(s);
+ pal[i][0] = get8(s);
+ if (hsz != 12) get8(s);
+ pal[i][3] = 255;
+ }
+ 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;
+ else { free(out); return epuc("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=get8(s),v2=0;
+ if (bpp == 4) {
+ v2 = v & 15;
+ v >>= 4;
+ }
+ out[z++] = pal[v][0];
+ out[z++] = pal[v][1];
+ out[z++] = pal[v][2];
+ if (target == 4) out[z++] = 255;
+ if (i+1 == (int) s->img_x) break;
+ v = (bpp == 8) ? get8(s) : v2;
+ out[z++] = pal[v][0];
+ out[z++] = pal[v][1];
+ out[z++] = pal[v][2];
+ if (target == 4) out[z++] = 255;
+ }
+ skip(s, pad);
+ }
+ } else {
+ int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0;
+ int z = 0;
+ int easy=0;
+ skip(s, offset - 14 - hsz);
+ if (bpp == 24) width = 3 * s->img_x;
+ else if (bpp == 16) width = 2*s->img_x;
+ else /* bpp = 32 and pad = 0 */ width=0;
+ pad = (-width) & 3;
+ if (bpp == 24) {
+ easy = 1;
+ } else if (bpp == 32) {
+ if (mb == 0xff && mg == 0xff00 && mr == 0xff000000 && ma == 0xff000000)
+ easy = 2;
+ }
+ if (!easy) {
+ if (!mr || !mg || !mb) return epuc("bad masks", "Corrupt BMP");
+ // right shift amt to put high bit in position #7
+ rshift = high_bit(mr)-7; rcount = bitcount(mr);
+ gshift = high_bit(mg)-7; gcount = bitcount(mr);
+ bshift = high_bit(mb)-7; bcount = bitcount(mr);
+ ashift = high_bit(ma)-7; acount = bitcount(mr);
+ }
+ for (j=0; j < (int) s->img_y; ++j) {
+ if (easy) {
+ for (i=0; i < (int) s->img_x; ++i) {
+ int a;
+ out[z+2] = get8(s);
+ out[z+1] = get8(s);
+ out[z+0] = get8(s);
+ z += 3;
+ a = (easy == 2 ? get8(s) : 255);
+ if (target == 4) out[z++] = a;
+ }
+ } else {
+ for (i=0; i < (int) s->img_x; ++i) {
+ uint32 v = (bpp == 16 ? get16le(s) : get32le(s));
+ int a;
+ out[z++] = shiftsigned(v & mr, rshift, rcount);
+ out[z++] = shiftsigned(v & mg, gshift, gcount);
+ out[z++] = shiftsigned(v & mb, bshift, bcount);
+ a = (ma ? shiftsigned(v & ma, ashift, acount) : 255);
+ if (target == 4) out[z++] = a;
+ }
+ }
+ skip(s, pad);
+ }
+ }
+ if (flip_vertically) {
+ stbi_uc t;
+ for (j=0; j < (int) s->img_y>>1; ++j) {
+ stbi_uc *p1 = out + j *s->img_x*target;
+ stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target;
+ for (i=0; i < (int) s->img_x*target; ++i) {
+ t = p1[i], p1[i] = p2[i], p2[i] = t;
+ }
+ }
+ }
+
+ if (req_comp && req_comp != target) {
+ out = convert_format(out, target, req_comp, s->img_x, s->img_y);
+ if (out == NULL) return out; // convert_format frees input on failure
+ }
+
+ *x = s->img_x;
+ *y = s->img_y;
+ if (comp) *comp = target;
+ return out;
+}
+
+#ifndef STBI_NO_STDIO
+stbi_uc *stbi_bmp_load (char const *filename, int *x, int *y, int *comp, int req_comp)
+{
+ stbi_uc *data;
+ FILE *f = fopen(filename, "rb");
+ if (!f) return NULL;
+ data = stbi_bmp_load_from_file(f, x,y,comp,req_comp);
+ fclose(f);
+ return data;
+}
+
+stbi_uc *stbi_bmp_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp)
+{
+ stbi s;
+ start_file(&s, f);
+ return bmp_load(&s, x,y,comp,req_comp);
+}
+#endif
+
+stbi_uc *stbi_bmp_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp)
+{
+ stbi s;
+ start_mem(&s, buffer, len);
+ return bmp_load(&s, x,y,comp,req_comp);
+}
+
+// Targa Truevision - TGA
+// by Jonathan Dummer
+
+static int tga_test(stbi *s)
+{
+ int sz;
+ get8u(s); // discard Offset
+ sz = get8u(s); // color type
+ if( sz > 1 ) return 0; // only RGB or indexed allowed
+ sz = get8u(s); // image type
+ if( (sz != 1) && (sz != 2) && (sz != 3) && (sz != 9) && (sz != 10) && (sz != 11) ) return 0; // only RGB or grey allowed, +/- RLE
+ get16(s); // discard palette start
+ get16(s); // discard palette length
+ get8(s); // discard bits per palette color entry
+ get16(s); // discard x origin
+ get16(s); // discard y origin
+ if( get16(s) < 1 ) return 0; // test width
+ if( get16(s) < 1 ) return 0; // test height
+ sz = get8(s); // bits per pixel
+ if( (sz != 8) && (sz != 16) && (sz != 24) && (sz != 32) ) return 0; // only RGB or RGBA or grey allowed
+ return 1; // seems to have passed everything
+}
+
+#ifndef STBI_NO_STDIO
+int stbi_tga_test_file (FILE *f)
+{
+ stbi s;
+ int r,n = ftell(f);
+ start_file(&s, f);
+ r = tga_test(&s);
+ fseek(f,n,SEEK_SET);
+ return r;
+}
+#endif
+
+int stbi_tga_test_memory (stbi_uc const *buffer, int len)
+{
+ stbi s;
+ start_mem(&s, buffer, len);
+ return tga_test(&s);
+}
+
+static stbi_uc *tga_load(stbi *s, int *x, int *y, int *comp, int req_comp)
+{
+ // read in the TGA header stuff
+ int tga_offset = get8u(s);
+ int tga_indexed = get8u(s);
+ int tga_image_type = get8u(s);
+ int tga_is_RLE = 0;
+ int tga_palette_start = get16le(s);
+ int tga_palette_len = get16le(s);
+ int tga_palette_bits = get8u(s);
+ int tga_x_origin = get16le(s);
+ int tga_y_origin = get16le(s);
+ int tga_width = get16le(s);
+ int tga_height = get16le(s);
+ int tga_bits_per_pixel = get8u(s);
+ int tga_inverted = get8u(s);
+ // image data
+ unsigned char *tga_data;
+ unsigned char *tga_palette = NULL;
+ int i, j;
+ unsigned char raw_data[4];
+ unsigned char trans_data[] = { 0,0,0,0 };
+ int RLE_count = 0;
+ int RLE_repeating = 0;
+ int read_next_pixel = 1;
+ // do a tiny bit of precessing
+ if( tga_image_type >= 8 )
+ {
+ tga_image_type -= 8;
+ tga_is_RLE = 1;
+ }
+ /* int tga_alpha_bits = tga_inverted & 15; */
+ tga_inverted = 1 - ((tga_inverted >> 5) & 1);
+
+ // 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;
+ }
+
+ // If I'm paletted, then I'll use the number of bits from the palette
+ if( tga_indexed )
+ {
+ tga_bits_per_pixel = tga_palette_bits;
+ }
+
+ // tga info
+ *x = tga_width;
+ *y = tga_height;
+ if( (req_comp < 1) || (req_comp > 4) )
+ {
+ // just use whatever the file was
+ req_comp = tga_bits_per_pixel / 8;
+ *comp = req_comp;
+ } else
+ {
+ // force a new number of components
+ *comp = tga_bits_per_pixel/8;
+ }
+ tga_data = (unsigned char*)malloc( tga_width * tga_height * req_comp );
+
+ // skip to the data's starting position (offset usually = 0)
+ skip(s, tga_offset );
+ // do I need to load a palette?
+ if( tga_indexed )
+ {
+ // any data to skip? (offset usually = 0)
+ skip(s, tga_palette_start );
+ // load the palette
+ tga_palette = (unsigned char*)malloc( tga_palette_len * tga_palette_bits / 8 );
+ getn(s, tga_palette, tga_palette_len * tga_palette_bits / 8 );
+ }
+ // load the data
+ for( i = 0; i < tga_width * tga_height; ++i )
+ {
+ // if I'm in RLE mode, do I need to get a RLE chunk?
+ if( tga_is_RLE )
+ {
+ if( RLE_count == 0 )
+ {
+ // yep, get the next byte as a RLE command
+ int RLE_cmd = get8u(s);
+ RLE_count = 1 + (RLE_cmd & 127);
+ RLE_repeating = RLE_cmd >> 7;
+ read_next_pixel = 1;
+ } else if( !RLE_repeating )
+ {
+ read_next_pixel = 1;
+ }
+ } else
+ {
+ read_next_pixel = 1;
+ }
+ // OK, if I need to read a pixel, do it now
+ if( read_next_pixel )
+ {
+ // load however much data we did have
+ if( tga_indexed )
+ {
+ // read in 1 byte, then perform the lookup
+ int pal_idx = get8u(s);
+ if( pal_idx >= tga_palette_len )
+ {
+ // invalid index
+ pal_idx = 0;
+ }
+ pal_idx *= tga_bits_per_pixel / 8;
+ for( j = 0; j*8 < tga_bits_per_pixel; ++j )
+ {
+ raw_data[j] = tga_palette[pal_idx+j];
+ }
+ } else
+ {
+ // read in the data raw
+ for( j = 0; j*8 < tga_bits_per_pixel; ++j )
+ {
+ raw_data[j] = get8u(s);
+ }
+ }
+ // convert raw to the intermediate format
+ switch( tga_bits_per_pixel )
+ {
+ case 8:
+ // Luminous => RGBA
+ trans_data[0] = raw_data[0];
+ trans_data[1] = raw_data[0];
+ trans_data[2] = raw_data[0];
+ trans_data[3] = 255;
+ break;
+ case 16:
+ // Luminous,Alpha => RGBA
+ trans_data[0] = raw_data[0];
+ trans_data[1] = raw_data[0];
+ trans_data[2] = raw_data[0];
+ trans_data[3] = raw_data[1];
+ break;
+ case 24:
+ // BGR => RGBA
+ trans_data[0] = raw_data[2];
+ trans_data[1] = raw_data[1];
+ trans_data[2] = raw_data[0];
+ trans_data[3] = 255;
+ break;
+ case 32:
+ // BGRA => RGBA
+ trans_data[0] = raw_data[2];
+ trans_data[1] = raw_data[1];
+ trans_data[2] = raw_data[0];
+ trans_data[3] = raw_data[3];
+ break;
+ }
+ // clear the reading flag for the next pixel
+ read_next_pixel = 0;
+ } // end of reading a pixel
+ // convert to final format
+ switch( req_comp )
+ {
+ case 1:
+ // RGBA => Luminance
+ tga_data[i*req_comp+0] = compute_y(trans_data[0],trans_data[1],trans_data[2]);
+ break;
+ case 2:
+ // RGBA => Luminance,Alpha
+ tga_data[i*req_comp+0] = compute_y(trans_data[0],trans_data[1],trans_data[2]);
+ tga_data[i*req_comp+1] = trans_data[3];
+ break;
+ case 3:
+ // RGBA => RGB
+ tga_data[i*req_comp+0] = trans_data[0];
+ tga_data[i*req_comp+1] = trans_data[1];
+ tga_data[i*req_comp+2] = trans_data[2];
+ break;
+ case 4:
+ // RGBA => RGBA
+ tga_data[i*req_comp+0] = trans_data[0];
+ tga_data[i*req_comp+1] = trans_data[1];
+ tga_data[i*req_comp+2] = trans_data[2];
+ tga_data[i*req_comp+3] = trans_data[3];
+ break;
+ }
+ // in case we're in RLE mode, keep counting down
+ --RLE_count;
+ }
+ // do I need to invert the image?
+ if( tga_inverted )
+ {
+ for( j = 0; j*2 < tga_height; ++j )
+ {
+ int index1 = j * tga_width * req_comp;
+ int index2 = (tga_height - 1 - j) * tga_width * req_comp;
+ for( i = tga_width * req_comp; i > 0; --i )
+ {
+ unsigned char temp = tga_data[index1];
+ tga_data[index1] = tga_data[index2];
+ tga_data[index2] = temp;
+ ++index1;
+ ++index2;
+ }
+ }
+ }
+ // clear my palette, if I had one
+ if( tga_palette != NULL )
+ {
+ free( tga_palette );
+ }
+ // the things I do to get rid of an error message, and yet keep
+ // Microsoft's C compilers happy... [8^(
+ tga_palette_start = tga_palette_len = tga_palette_bits =
+ tga_x_origin = tga_y_origin = 0;
+ // OK, done
+ return tga_data;
+}
+
+#ifndef STBI_NO_STDIO
+stbi_uc *stbi_tga_load (char const *filename, int *x, int *y, int *comp, int req_comp)
+{
+ stbi_uc *data;
+ FILE *f = fopen(filename, "rb");
+ if (!f) return NULL;
+ data = stbi_tga_load_from_file(f, x,y,comp,req_comp);
+ fclose(f);
+ return data;
+}
+
+stbi_uc *stbi_tga_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp)
+{
+ stbi s;
+ start_file(&s, f);
+ return tga_load(&s, x,y,comp,req_comp);
+}
+#endif
+
+stbi_uc *stbi_tga_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp)
+{
+ stbi s;
+ start_mem(&s, buffer, len);
+ return tga_load(&s, x,y,comp,req_comp);
+}
+
+
+// *************************************************************************************************
+// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicholas Schulz, tweaked by STB
+
+static int psd_test(stbi *s)
+{
+ if (get32(s) != 0x38425053) return 0; // "8BPS"
+ else return 1;
+}
+
+#ifndef STBI_NO_STDIO
+int stbi_psd_test_file(FILE *f)
+{
+ stbi s;
+ int r,n = ftell(f);
+ start_file(&s, f);
+ r = psd_test(&s);
+ fseek(f,n,SEEK_SET);
+ return r;
+}
+#endif
+
+int stbi_psd_test_memory(stbi_uc const *buffer, int len)
+{
+ stbi s;
+ start_mem(&s, buffer, len);
+ return psd_test(&s);
+}
+
+static stbi_uc *psd_load(stbi *s, int *x, int *y, int *comp, int req_comp)
+{
+ int pixelCount;
+ int channelCount, compression;
+ int channel, i, count, len;
+ int w,h;
+ uint8 *out;
+
+ // Check identifier
+ if (get32(s) != 0x38425053) // "8BPS"
+ return epuc("not PSD", "Corrupt PSD image");
+
+ // Check file type version.
+ if (get16(s) != 1)
+ return epuc("wrong version", "Unsupported version of PSD image");
+
+ // Skip 6 reserved bytes.
+ skip(s, 6 );
+
+ // Read the number of channels (R, G, B, A, etc).
+ channelCount = get16(s);
+ if (channelCount < 0 || channelCount > 16)
+ return epuc("wrong channel count", "Unsupported number of channels in PSD image");
+
+ // Read the rows and columns of the image.
+ h = get32(s);
+ w = get32(s);
+
+ // Make sure the depth is 8 bits.
+ if (get16(s) != 8)
+ return epuc("unsupported bit depth", "PSD bit depth is not 8 bit");
+
+ // Make sure the color mode is RGB.
+ // Valid options are:
+ // 0: Bitmap
+ // 1: Grayscale
+ // 2: Indexed color
+ // 3: RGB color
+ // 4: CMYK color
+ // 7: Multichannel
+ // 8: Duotone
+ // 9: Lab color
+ if (get16(s) != 3)
+ return epuc("wrong color format", "PSD is not in RGB color format");
+
+ // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.)
+ skip(s,get32(s) );
+
+ // Skip the image resources. (resolution, pen tool paths, etc)
+ skip(s, get32(s) );
+
+ // Skip the reserved data.
+ skip(s, get32(s) );
+
+ // Find out if the data is compressed.
+ // Known values:
+ // 0: no compression
+ // 1: RLE compressed
+ compression = get16(s);
+ if (compression > 1)
+ return epuc("bad compression", "PSD has an unknown compression format");
+
+ // Create the destination image.
+ out = (stbi_uc *) malloc(4 * w*h);
+ if (!out) return epuc("outofmem", "Out of memory");
+ pixelCount = w*h;
+
+ // Initialize the data to zero.
+ //memset( out, 0, pixelCount * 4 );
+
+ // Finally, the image data.
+ if (compression) {
+ // RLE as used by .PSD and .TIFF
+ // Loop until you get the number of unpacked bytes you are expecting:
+ // Read the next source byte into n.
+ // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally.
+ // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times.
+ // Else if n is 128, noop.
+ // Endloop
+
+ // The RLE-compressed data is preceeded by a 2-byte data count for each row in the data,
+ // which we're going to just skip.
+ skip(s, h * channelCount * 2 );
+
+ // Read the RLE data by channel.
+ for (channel = 0; channel < 4; channel++) {
+ uint8 *p;
+
+ p = out+channel;
+ if (channel >= channelCount) {
+ // Fill this channel with default data.
+ for (i = 0; i < pixelCount; i++) *p = (channel == 3 ? 255 : 0), p += 4;
+ } else {
+ // Read the RLE data.
+ count = 0;
+ while (count < pixelCount) {
+ len = get8(s);
+ if (len == 128) {
+ // No-op.
+ } else if (len < 128) {
+ // Copy next len+1 bytes literally.
+ len++;
+ count += len;
+ while (len) {
+ *p = get8(s);
+ p += 4;
+ len--;
+ }
+ } else if (len > 128) {
+ uint32 val;
+ // Next -len+1 bytes in the dest are replicated from next source byte.
+ // (Interpret len as a negative 8-bit int.)
+ len ^= 0x0FF;
+ len += 2;
+ val = get8(s);
+ count += len;
+ while (len) {
+ *p = val;
+ p += 4;
+ len--;
+ }
+ }
+ }
+ }
+ }
+
+ } else {
+ // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...)
+ // where each channel consists of an 8-bit value for each pixel in the image.
+
+ // Read the data by channel.
+ for (channel = 0; channel < 4; channel++) {
+ uint8 *p;
+
+ p = out + channel;
+ if (channel > channelCount) {
+ // Fill this channel with default data.
+ for (i = 0; i < pixelCount; i++) *p = channel == 3 ? 255 : 0, p += 4;
+ } else {
+ // Read the data.
+ count = 0;
+ for (i = 0; i < pixelCount; i++)
+ *p = get8(s), p += 4;
+ }
+ }
+ }
+
+ if (req_comp && req_comp != 4) {
+ out = convert_format(out, 4, req_comp, w, h);
+ if (out == NULL) return out; // convert_format frees input on failure
+ }
+
+ if (comp) *comp = channelCount;
+ *y = h;
+ *x = w;
+
+ return out;
+}
+
+#ifndef STBI_NO_STDIO
+stbi_uc *stbi_psd_load(char const *filename, int *x, int *y, int *comp, int req_comp)
+{
+ stbi_uc *data;
+ FILE *f = fopen(filename, "rb");
+ if (!f) return NULL;
+ data = stbi_psd_load_from_file(f, x,y,comp,req_comp);
+ fclose(f);
+ return data;
+}
+
+stbi_uc *stbi_psd_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp)
+{
+ stbi s;
+ start_file(&s, f);
+ return psd_load(&s, x,y,comp,req_comp);
+}
+#endif
+
+stbi_uc *stbi_psd_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp)
+{
+ stbi s;
+ start_mem(&s, buffer, len);
+ return psd_load(&s, x,y,comp,req_comp);
+}
+
+
+// *************************************************************************************************
+// Radiance RGBE HDR loader
+// originally by Nicolas Schulz
+#ifndef STBI_NO_HDR
+static int hdr_test(stbi *s)
+{
+ char *signature = "#?RADIANCE\n";
+ int i;
+ for (i=0; signature[i]; ++i)
+ if (get8(s) != signature[i])
+ return 0;
+ return 1;
+}
+
+int stbi_hdr_test_memory(stbi_uc const *buffer, int len)
+{
+ stbi s;
+ start_mem(&s, buffer, len);
+ return hdr_test(&s);
+}
+
+#ifndef STBI_NO_STDIO
+int stbi_hdr_test_file(FILE *f)
+{
+ stbi s;
+ int r,n = ftell(f);
+ start_file(&s, f);
+ r = hdr_test(&s);
+ fseek(f,n,SEEK_SET);
+ return r;
+}
+#endif
+
+#define HDR_BUFLEN 1024
+static char *hdr_gettoken(stbi *z, char *buffer)
+{
+ int len=0;
+ //char *s = buffer,
+ char c = '\0';
+
+ c = get8(z);
+
+ while (!at_eof(z) && c != '\n') {
+ buffer[len++] = c;
+ if (len == HDR_BUFLEN-1) {
+ // flush to end of line
+ while (!at_eof(z) && get8(z) != '\n')
+ ;
+ break;
+ }
+ c = get8(z);
+ }
+
+ buffer[len] = 0;
+ return buffer;
+}
+
+static void hdr_convert(float *output, stbi_uc *input, int req_comp)
+{
+ if( input[3] != 0 ) {
+ float f1;
+ // Exponent
+ f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8));
+ if (req_comp <= 2)
+ output[0] = (input[0] + input[1] + input[2]) * f1 / 3;
+ else {
+ output[0] = input[0] * f1;
+ output[1] = input[1] * f1;
+ output[2] = input[2] * f1;
+ }
+ if (req_comp == 2) output[1] = 1;
+ if (req_comp == 4) output[3] = 1;
+ } else {
+ switch (req_comp) {
+ case 4: output[3] = 1; /* fallthrough */
+ case 3: output[0] = output[1] = output[2] = 0;
+ break;
+ case 2: output[1] = 1; /* fallthrough */
+ case 1: output[0] = 0;
+ break;
+ }
+ }
+}
+
+
+static float *hdr_load(stbi *s, int *x, int *y, int *comp, int req_comp)
+{
+ char buffer[HDR_BUFLEN];
+ char *token;
+ int valid = 0;
+ int width, height;
+ stbi_uc *scanline;
+ float *hdr_data;
+ int len;
+ unsigned char count, value;
+ int i, j, k, c1,c2, z;
+
+
+ // Check identifier
+ if (strcmp(hdr_gettoken(s,buffer), "#?RADIANCE") != 0)
+ return epf("not HDR", "Corrupt HDR image");
+
+ // Parse header
+ while(1) {
+ token = hdr_gettoken(s,buffer);
+ if (token[0] == 0) break;
+ if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1;
+ }
+
+ if (!valid) return epf("unsupported format", "Unsupported HDR format");
+
+ // Parse width and height
+ // can't use sscanf() if we're not using stdio!
+ token = hdr_gettoken(s,buffer);
+ if (strncmp(token, "-Y ", 3)) return epf("unsupported data layout", "Unsupported HDR format");
+ token += 3;
+ height = strtol(token, &token, 10);
+ while (*token == ' ') ++token;
+ if (strncmp(token, "+X ", 3)) return epf("unsupported data layout", "Unsupported HDR format");
+ token += 3;
+ width = strtol(token, NULL, 10);
+
+ *x = width;
+ *y = height;
+
+ *comp = 3;
+ if (req_comp == 0) req_comp = 3;
+
+ // Read data
+ hdr_data = (float *) malloc(height * width * req_comp * sizeof(float));
+
+ // Load image data
+ // image data is stored as some number of sca
+ if( width < 8 || width >= 32768) {
+ // Read flat data
+ for (j=0; j < height; ++j) {
+ for (i=0; i < width; ++i) {
+ stbi_uc rgbe[4];
+ main_decode_loop:
+ getn(s, rgbe, 4);
+ hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp);
+ }
+ }
+ } else {
+ // Read RLE-encoded data
+ scanline = NULL;
+
+ for (j = 0; j < height; ++j) {
+ c1 = get8(s);
+ c2 = get8(s);
+ len = get8(s);
+ if (c1 != 2 || c2 != 2 || (len & 0x80)) {
+ // not run-length encoded, so we have to actually use THIS data as a decoded
+ // pixel (note this can't be a valid pixel--one of RGB must be >= 128)
+ stbi_uc rgbe[4] = { c1,c2,len, get8(s) };
+ hdr_convert(hdr_data, rgbe, req_comp);
+ i = 1;
+ j = 0;
+ free(scanline);
+ goto main_decode_loop; // yes, this is fucking insane; blame the fucking insane format
+ }
+ len <<= 8;
+ len |= get8(s);
+ if (len != width) { free(hdr_data); free(scanline); return epf("invalid decoded scanline length", "corrupt HDR"); }
+ if (scanline == NULL) scanline = (stbi_uc *) malloc(width * 4);
+
+ for (k = 0; k < 4; ++k) {
+ i = 0;
+ while (i < width) {
+ count = get8(s);
+ if (count > 128) {
+ // Run
+ value = get8(s);
+ count -= 128;
+ for (z = 0; z < count; ++z)
+ scanline[i++ * 4 + k] = value;
+ } else {
+ // Dump
+ for (z = 0; z < count; ++z)
+ scanline[i++ * 4 + k] = get8(s);
+ }
+ }
+ }
+ for (i=0; i < width; ++i)
+ hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp);
+ }
+ free(scanline);
+ }
+
+ return hdr_data;
+}
+
+static stbi_uc *hdr_load_rgbe(stbi *s, int *x, int *y, int *comp, int req_comp)
+{
+ char buffer[HDR_BUFLEN];
+ char *token;
+ int valid = 0;
+ int width, height;
+ stbi_uc *scanline;
+ stbi_uc *rgbe_data;
+ int len;
+ unsigned char count, value;
+ int i, j, k, c1,c2, z;
+
+
+ // Check identifier
+ if (strcmp(hdr_gettoken(s,buffer), "#?RADIANCE") != 0)
+ return epuc("not HDR", "Corrupt HDR image");
+
+ // Parse header
+ while(1) {
+ token = hdr_gettoken(s,buffer);
+ if (token[0] == 0) break;
+ if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1;
+ }
+
+ if (!valid) return epuc("unsupported format", "Unsupported HDR format");
+
+ // Parse width and height
+ // can't use sscanf() if we're not using stdio!
+ token = hdr_gettoken(s,buffer);
+ if (strncmp(token, "-Y ", 3)) return epuc("unsupported data layout", "Unsupported HDR format");
+ token += 3;
+ height = strtol(token, &token, 10);
+ while (*token == ' ') ++token;
+ if (strncmp(token, "+X ", 3)) return epuc("unsupported data layout", "Unsupported HDR format");
+ token += 3;
+ width = strtol(token, NULL, 10);
+
+ *x = width;
+ *y = height;
+
+ // RGBE _MUST_ come out as 4 components
+ *comp = 4;
+ req_comp = 4;
+
+ // Read data
+ rgbe_data = (stbi_uc *) malloc(height * width * req_comp * sizeof(stbi_uc));
+ // point to the beginning
+ scanline = rgbe_data;
+
+ // Load image data
+ // image data is stored as some number of scan lines
+ if( width < 8 || width >= 32768) {
+ // Read flat data
+ for (j=0; j < height; ++j) {
+ for (i=0; i < width; ++i) {
+ main_decode_loop:
+ //getn(rgbe, 4);
+ getn(s,scanline, 4);
+ scanline += 4;
+ }
+ }
+ } else {
+ // Read RLE-encoded data
+ for (j = 0; j < height; ++j) {
+ c1 = get8(s);
+ c2 = get8(s);
+ len = get8(s);
+ if (c1 != 2 || c2 != 2 || (len & 0x80)) {
+ // not run-length encoded, so we have to actually use THIS data as a decoded
+ // pixel (note this can't be a valid pixel--one of RGB must be >= 128)
+ scanline[0] = c1;
+ scanline[1] = c2;
+ scanline[2] = len;
+ scanline[3] = get8(s);
+ scanline += 4;
+ i = 1;
+ j = 0;
+ goto main_decode_loop; // yes, this is insane; blame the insane format
+ }
+ len <<= 8;
+ len |= get8(s);
+ if (len != width) { free(rgbe_data); return epuc("invalid decoded scanline length", "corrupt HDR"); }
+ for (k = 0; k < 4; ++k) {
+ i = 0;
+ while (i < width) {
+ count = get8(s);
+ if (count > 128) {
+ // Run
+ value = get8(s);
+ count -= 128;
+ for (z = 0; z < count; ++z)
+ scanline[i++ * 4 + k] = value;
+ } else {
+ // Dump
+ for (z = 0; z < count; ++z)
+ scanline[i++ * 4 + k] = get8(s);
+ }
+ }
+ }
+ // move the scanline on
+ scanline += 4 * width;
+ }
+ }
+
+ return rgbe_data;
+}
+
+#ifndef STBI_NO_STDIO
+float *stbi_hdr_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp)
+{
+ stbi s;
+ start_file(&s,f);
+ return hdr_load(&s,x,y,comp,req_comp);
+}
+
+stbi_uc *stbi_hdr_load_rgbe_file(FILE *f, int *x, int *y, int *comp, int req_comp)
+{
+ stbi s;
+ start_file(&s,f);
+ return hdr_load_rgbe(&s,x,y,comp,req_comp);
+}
+
+stbi_uc *stbi_hdr_load_rgbe (char const *filename, int *x, int *y, int *comp, int req_comp)
+{
+ FILE *f = fopen(filename, "rb");
+ unsigned char *result;
+ if (!f) return epuc("can't fopen", "Unable to open file");
+ result = stbi_hdr_load_rgbe_file(f,x,y,comp,req_comp);
+ fclose(f);
+ return result;
+}
+#endif
+
+float *stbi_hdr_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp)
+{
+ stbi s;
+ start_mem(&s,buffer, len);
+ return hdr_load(&s,x,y,comp,req_comp);
+}
+
+stbi_uc *stbi_hdr_load_rgbe_memory(stbi_uc *buffer, int len, int *x, int *y, int *comp, int req_comp)
+{
+ stbi s;
+ start_mem(&s,buffer, len);
+ return hdr_load_rgbe(&s,x,y,comp,req_comp);
+}
+
+#endif // STBI_NO_HDR
+
+/////////////////////// write image ///////////////////////
+
+#ifndef STBI_NO_WRITE
+
+static void write8(FILE *f, int x) { uint8 z = (uint8) x; fwrite(&z,1,1,f); }
+
+static void writefv(FILE *f, char *fmt, va_list v)
+{
+ while (*fmt) {
+ switch (*fmt++) {
+ case ' ': break;
+ case '1': { uint8 x = va_arg(v, int); write8(f,x); break; }
+ case '2': { int16 x = va_arg(v, int); write8(f,x); write8(f,x>>8); break; }
+ case '4': { int32 x = va_arg(v, int); write8(f,x); write8(f,x>>8); write8(f,x>>16); write8(f,x>>24); break; }
+ default:
+ assert(0);
+ va_end(v);
+ return;
+ }
+ }
+}
+
+static void writef(FILE *f, char *fmt, ...)
+{
+ va_list v;
+ va_start(v, fmt);
+ writefv(f,fmt,v);
+ va_end(v);
+}
+
+static void write_pixels(FILE *f, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad)
+{
+ uint8 bg[3] = { 255, 0, 255}, px[3];
+ uint32 zero = 0;
+ int i,j,k, j_end;
+
+ if (vdir < 0)
+ j_end = -1, j = y-1;
+ else
+ j_end = y, j = 0;
+
+ for (; j != j_end; j += vdir) {
+ for (i=0; i < x; ++i) {
+ uint8 *d = (uint8 *) data + (j*x+i)*comp;
+ if (write_alpha < 0)
+ fwrite(&d[comp-1], 1, 1, f);
+ switch (comp) {
+ case 1:
+ case 2: writef(f, "111", d[0],d[0],d[0]);
+ break;
+ case 4:
+ if (!write_alpha) {
+ for (k=0; k < 3; ++k)
+ px[k] = bg[k] + ((d[k] - bg[k]) * d[3])/255;
+ writef(f, "111", px[1-rgb_dir],px[1],px[1+rgb_dir]);
+ break;
+ }
+ /* FALLTHROUGH */
+ case 3:
+ writef(f, "111", d[1-rgb_dir],d[1],d[1+rgb_dir]);
+ break;
+ }
+ if (write_alpha > 0)
+ fwrite(&d[comp-1], 1, 1, f);
+ }
+ fwrite(&zero,scanline_pad,1,f);
+ }
+}
+
+static int outfile(char const *filename, int rgb_dir, int vdir, int x, int y, int comp, void *data, int alpha, int pad, char *fmt, ...)
+{
+ FILE *f = fopen(filename, "wb");
+ if (f) {
+ va_list v;
+ va_start(v, fmt);
+ writefv(f, fmt, v);
+ va_end(v);
+ write_pixels(f,rgb_dir,vdir,x,y,comp,data,alpha,pad);
+ fclose(f);
+ }
+ return f != NULL;
+}
+
+int stbi_write_bmp(char const *filename, int x, int y, int comp, void *data)
+{
+ int pad = (-x*3) & 3;
+ return outfile(filename,-1,-1,x,y,comp,data,0,pad,
+ "11 4 22 4" "4 44 22 444444",
+ 'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header
+ 40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header
+}
+
+int stbi_write_tga(char const *filename, int x, int y, int comp, void *data)
+{
+ int has_alpha = !(comp & 1);
+ return outfile(filename, -1,-1, x, y, comp, data, has_alpha, 0,
+ "111 221 2222 11", 0,0,2, 0,0,0, 0,0,x,y, 24+8*has_alpha, 8*has_alpha);
+}
+
+// any other image formats that do interleaved rgb data?
+// PNG: requires adler32,crc32 -- significant amount of code
+// PSD: no, channels output separately
+// TIFF: no, stripwise-interleaved... i think
+
+#endif // STBI_NO_WRITE
+
+// add in my DDS loading support
+#ifndef STBI_NO_DDS
+#include "stbi_DDS_aug_c.h"
+#endif
diff --git a/cw_8/src/SOIL/stb_image_aug.h b/cw_8/src/SOIL/stb_image_aug.h
new file mode 100644
index 0000000..e59f2eb
--- /dev/null
+++ b/cw_8/src/SOIL/stb_image_aug.h
@@ -0,0 +1,354 @@
+/* stbi-1.16 - public domain JPEG/PNG reader - http://nothings.org/stb_image.c
+ when you control the images you're loading
+
+ QUICK NOTES:
+ Primarily of interest to game developers and other people who can
+ avoid problematic images and only need the trivial interface
+
+ JPEG baseline (no JPEG progressive, no oddball channel decimations)
+ PNG non-interlaced
+ BMP non-1bpp, non-RLE
+ TGA (not sure what subset, if a subset)
+ PSD (composited view only, no extra channels)
+ HDR (radiance rgbE format)
+ writes BMP,TGA (define STBI_NO_WRITE to remove code)
+ decoded from memory or through stdio FILE (define STBI_NO_STDIO to remove code)
+ supports installable dequantizing-IDCT, YCbCr-to-RGB conversion (define STBI_SIMD)
+
+ TODO:
+ stbi_info_*
+
+ history:
+ 1.16 major bugfix - convert_format converted one too many pixels
+ 1.15 initialize some fields for thread safety
+ 1.14 fix threadsafe conversion bug; header-file-only version (#define STBI_HEADER_FILE_ONLY before including)
+ 1.13 threadsafe
+ 1.12 const qualifiers in the API
+ 1.11 Support installable IDCT, colorspace conversion routines
+ 1.10 Fixes for 64-bit (don't use "unsigned long")
+ optimized upsampling by Fabian "ryg" Giesen
+ 1.09 Fix format-conversion for PSD code (bad global variables!)
+ 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz
+ 1.07 attempt to fix C++ warning/errors again
+ 1.06 attempt to fix C++ warning/errors again
+ 1.05 fix TGA loading to return correct *comp and use good luminance calc
+ 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free
+ 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR
+ 1.02 support for (subset of) HDR files, float interface for preferred access to them
+ 1.01 fix bug: possible bug in handling right-side up bmps... not sure
+ fix bug: the stbi_bmp_load() and stbi_tga_load() functions didn't work at all
+ 1.00 interface to zlib that skips zlib header
+ 0.99 correct handling of alpha in palette
+ 0.98 TGA loader by lonesock; dynamically add loaders (untested)
+ 0.97 jpeg errors on too large a file; also catch another malloc failure
+ 0.96 fix detection of invalid v value - particleman@mollyrocket forum
+ 0.95 during header scan, seek to markers in case of padding
+ 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same
+ 0.93 handle jpegtran output; verbose errors
+ 0.92 read 4,8,16,24,32-bit BMP files of several formats
+ 0.91 output 24-bit Windows 3.0 BMP files
+ 0.90 fix a few more warnings; bump version number to approach 1.0
+ 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd
+ 0.60 fix compiling as c++
+ 0.59 fix warnings: merge Dave Moore's -Wall fixes
+ 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian
+ 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less
+ than 16 available
+ 0.56 fix bug: zlib uncompressed mode len vs. nlen
+ 0.55 fix bug: restart_interval not initialized to 0
+ 0.54 allow NULL for 'int *comp'
+ 0.53 fix bug in png 3->4; speedup png decoding
+ 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments
+ 0.51 obey req_comp requests, 1-component jpegs return as 1-component,
+ on 'test' only check type, not whether we support this variant
+*/
+
+#ifndef HEADER_STB_IMAGE_AUGMENTED
+#define HEADER_STB_IMAGE_AUGMENTED
+
+//// begin header file ////////////////////////////////////////////////////
+//
+// Limitations:
+// - no progressive/interlaced support (jpeg, png)
+// - 8-bit samples only (jpeg, png)
+// - not threadsafe
+// - channel subsampling of at most 2 in each dimension (jpeg)
+// - no delayed line count (jpeg) -- IJG doesn't support either
+//
+// Basic usage (see HDR discussion below):
+// int x,y,n;
+// unsigned char *data = stbi_load(filename, &x, &y, &n, 0);
+// // ... process data if not NULL ...
+// // ... x = width, y = height, n = # 8-bit components per pixel ...
+// // ... replace '0' with '1'..'4' to force that many components per pixel
+// stbi_image_free(data)
+//
+// Standard parameters:
+// int *x -- outputs image width in pixels
+// int *y -- outputs image height in pixels
+// int *comp -- outputs # of image components in image file
+// int req_comp -- if non-zero, # of image components requested in result
+//
+// The return value from an image loader is an 'unsigned char *' which points
+// to the pixel data. The pixel data consists of *y scanlines of *x pixels,
+// with each pixel consisting of N interleaved 8-bit components; the first
+// pixel pointed to is top-left-most in the image. There is no padding between
+// image scanlines or between pixels, regardless of format. The number of
+// components N is 'req_comp' if req_comp is non-zero, or *comp otherwise.
+// If req_comp is non-zero, *comp has the number of components that _would_
+// have been output otherwise. E.g. if you set req_comp to 4, you will always
+// get RGBA output, but you can check *comp to easily see if it's opaque.
+//
+// An output image with N components has the following components interleaved
+// in this order in each pixel:
+//
+// N=#comp components
+// 1 grey
+// 2 grey, alpha
+// 3 red, green, blue
+// 4 red, green, blue, alpha
+//
+// If image loading fails for any reason, the return value will be NULL,
+// and *x, *y, *comp will be unchanged. The function stbi_failure_reason()
+// can be queried for an extremely brief, end-user unfriendly explanation
+// of why the load failed. Define STBI_NO_FAILURE_STRINGS to avoid
+// compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly
+// more user-friendly ones.
+//
+// Paletted PNG and BMP images are automatically depalettized.
+//
+//
+// ===========================================================================
+//
+// HDR image support (disable by defining STBI_NO_HDR)
+//
+// stb_image now supports loading HDR images in general, and currently
+// the Radiance .HDR file format, although the support is provided
+// generically. You can still load any file through the existing interface;
+// if you attempt to load an HDR file, it will be automatically remapped to
+// LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1;
+// both of these constants can be reconfigured through this interface:
+//
+// stbi_hdr_to_ldr_gamma(2.2f);
+// stbi_hdr_to_ldr_scale(1.0f);
+//
+// (note, do not use _inverse_ constants; stbi_image will invert them
+// appropriately).
+//
+// Additionally, there is a new, parallel interface for loading files as
+// (linear) floats to preserve the full dynamic range:
+//
+// float *data = stbi_loadf(filename, &x, &y, &n, 0);
+//
+// If you load LDR images through this interface, those images will
+// be promoted to floating point values, run through the inverse of
+// constants corresponding to the above:
+//
+// stbi_ldr_to_hdr_scale(1.0f);
+// stbi_ldr_to_hdr_gamma(2.2f);
+//
+// Finally, given a filename (or an open file or memory block--see header
+// file for details) containing image data, you can query for the "most
+// appropriate" interface to use (that is, whether the image is HDR or
+// not), using:
+//
+// stbi_is_hdr(char *filename);
+
+#ifndef STBI_NO_STDIO
+#include
+#endif
+
+#define STBI_VERSION 1
+
+enum
+{
+ STBI_default = 0, // only used for req_comp
+
+ STBI_grey = 1,
+ STBI_grey_alpha = 2,
+ STBI_rgb = 3,
+ STBI_rgb_alpha = 4,
+};
+
+typedef unsigned char stbi_uc;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// WRITING API
+
+#if !defined(STBI_NO_WRITE) && !defined(STBI_NO_STDIO)
+// write a BMP/TGA file given tightly packed 'comp' channels (no padding, nor bmp-stride-padding)
+// (you must include the appropriate extension in the filename).
+// returns TRUE on success, FALSE if couldn't open file, error writing file
+extern int stbi_write_bmp (char const *filename, int x, int y, int comp, void *data);
+extern int stbi_write_tga (char const *filename, int x, int y, int comp, void *data);
+#endif
+
+// PRIMARY API - works on images of any type
+
+// load image by filename, open file, or memory buffer
+#ifndef STBI_NO_STDIO
+extern stbi_uc *stbi_load (char const *filename, int *x, int *y, int *comp, int req_comp);
+extern stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp);
+extern int stbi_info_from_file (FILE *f, int *x, int *y, int *comp);
+#endif
+extern stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp);
+// for stbi_load_from_file, file pointer is left pointing immediately after image
+
+#ifndef STBI_NO_HDR
+#ifndef STBI_NO_STDIO
+extern float *stbi_loadf (char const *filename, int *x, int *y, int *comp, int req_comp);
+extern float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *comp, int req_comp);
+#endif
+extern float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp);
+
+extern void stbi_hdr_to_ldr_gamma(float gamma);
+extern void stbi_hdr_to_ldr_scale(float scale);
+
+extern void stbi_ldr_to_hdr_gamma(float gamma);
+extern void stbi_ldr_to_hdr_scale(float scale);
+
+#endif // STBI_NO_HDR
+
+// get a VERY brief reason for failure
+// NOT THREADSAFE
+extern char *stbi_failure_reason (void);
+
+// free the loaded image -- this is just free()
+extern void stbi_image_free (void *retval_from_stbi_load);
+
+// get image dimensions & components without fully decoding
+extern int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp);
+extern int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len);
+#ifndef STBI_NO_STDIO
+extern int stbi_info (char const *filename, int *x, int *y, int *comp);
+extern int stbi_is_hdr (char const *filename);
+extern int stbi_is_hdr_from_file(FILE *f);
+#endif
+
+// ZLIB client - used by PNG, available for other purposes
+
+extern char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen);
+extern char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen);
+extern int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen);
+
+extern char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen);
+extern int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen);
+
+// TYPE-SPECIFIC ACCESS
+
+// is it a jpeg?
+extern int stbi_jpeg_test_memory (stbi_uc const *buffer, int len);
+extern stbi_uc *stbi_jpeg_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp);
+extern int stbi_jpeg_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp);
+
+#ifndef STBI_NO_STDIO
+extern stbi_uc *stbi_jpeg_load (char const *filename, int *x, int *y, int *comp, int req_comp);
+extern int stbi_jpeg_test_file (FILE *f);
+extern stbi_uc *stbi_jpeg_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp);
+
+extern int stbi_jpeg_info (char const *filename, int *x, int *y, int *comp);
+extern int stbi_jpeg_info_from_file (FILE *f, int *x, int *y, int *comp);
+#endif
+
+// is it a png?
+extern int stbi_png_test_memory (stbi_uc const *buffer, int len);
+extern stbi_uc *stbi_png_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp);
+extern int stbi_png_info_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp);
+
+#ifndef STBI_NO_STDIO
+extern stbi_uc *stbi_png_load (char const *filename, int *x, int *y, int *comp, int req_comp);
+extern int stbi_png_info (char const *filename, int *x, int *y, int *comp);
+extern int stbi_png_test_file (FILE *f);
+extern stbi_uc *stbi_png_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp);
+extern int stbi_png_info_from_file (FILE *f, int *x, int *y, int *comp);
+#endif
+
+// is it a bmp?
+extern int stbi_bmp_test_memory (stbi_uc const *buffer, int len);
+
+extern stbi_uc *stbi_bmp_load (char const *filename, int *x, int *y, int *comp, int req_comp);
+extern stbi_uc *stbi_bmp_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp);
+#ifndef STBI_NO_STDIO
+extern int stbi_bmp_test_file (FILE *f);
+extern stbi_uc *stbi_bmp_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp);
+#endif
+
+// is it a tga?
+extern int stbi_tga_test_memory (stbi_uc const *buffer, int len);
+
+extern stbi_uc *stbi_tga_load (char const *filename, int *x, int *y, int *comp, int req_comp);
+extern stbi_uc *stbi_tga_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp);
+#ifndef STBI_NO_STDIO
+extern int stbi_tga_test_file (FILE *f);
+extern stbi_uc *stbi_tga_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp);
+#endif
+
+// is it a psd?
+extern int stbi_psd_test_memory (stbi_uc const *buffer, int len);
+
+extern stbi_uc *stbi_psd_load (char const *filename, int *x, int *y, int *comp, int req_comp);
+extern stbi_uc *stbi_psd_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp);
+#ifndef STBI_NO_STDIO
+extern int stbi_psd_test_file (FILE *f);
+extern stbi_uc *stbi_psd_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp);
+#endif
+
+// is it an hdr?
+extern int stbi_hdr_test_memory (stbi_uc const *buffer, int len);
+
+extern float * stbi_hdr_load (char const *filename, int *x, int *y, int *comp, int req_comp);
+extern float * stbi_hdr_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp);
+extern stbi_uc *stbi_hdr_load_rgbe (char const *filename, int *x, int *y, int *comp, int req_comp);
+extern float * stbi_hdr_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp);
+#ifndef STBI_NO_STDIO
+extern int stbi_hdr_test_file (FILE *f);
+extern float * stbi_hdr_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp);
+extern stbi_uc *stbi_hdr_load_rgbe_file (FILE *f, int *x, int *y, int *comp, int req_comp);
+#endif
+
+// define new loaders
+typedef struct
+{
+ int (*test_memory)(stbi_uc const *buffer, int len);
+ stbi_uc * (*load_from_memory)(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp);
+ #ifndef STBI_NO_STDIO
+ int (*test_file)(FILE *f);
+ stbi_uc * (*load_from_file)(FILE *f, int *x, int *y, int *comp, int req_comp);
+ #endif
+} stbi_loader;
+
+// register a loader by filling out the above structure (you must defined ALL functions)
+// returns 1 if added or already added, 0 if not added (too many loaders)
+// NOT THREADSAFE
+extern int stbi_register_loader(stbi_loader *loader);
+
+// define faster low-level operations (typically SIMD support)
+#if STBI_SIMD
+typedef void (*stbi_idct_8x8)(uint8 *out, int out_stride, short data[64], unsigned short *dequantize);
+// compute an integer IDCT on "input"
+// input[x] = data[x] * dequantize[x]
+// write results to 'out': 64 samples, each run of 8 spaced by 'out_stride'
+// CLAMP results to 0..255
+typedef void (*stbi_YCbCr_to_RGB_run)(uint8 *output, uint8 const *y, uint8 const *cb, uint8 const *cr, int count, int step);
+// compute a conversion from YCbCr to RGB
+// 'count' pixels
+// write pixels to 'output'; each pixel is 'step' bytes (either 3 or 4; if 4, write '255' as 4th), order R,G,B
+// y: Y input channel
+// cb: Cb input channel; scale/biased to be 0..255
+// cr: Cr input channel; scale/biased to be 0..255
+
+extern void stbi_install_idct(stbi_idct_8x8 func);
+extern void stbi_install_YCbCr_to_RGB(stbi_YCbCr_to_RGB_run func);
+#endif // STBI_SIMD
+
+#ifdef __cplusplus
+}
+#endif
+
+//
+//
+//// end header file /////////////////////////////////////////////////////
+#endif // STBI_INCLUDE_STB_IMAGE_H
diff --git a/cw_8/src/SOIL/stbi_DDS_aug.h b/cw_8/src/SOIL/stbi_DDS_aug.h
new file mode 100644
index 0000000..c7da9f7
--- /dev/null
+++ b/cw_8/src/SOIL/stbi_DDS_aug.h
@@ -0,0 +1,21 @@
+/*
+ adding DDS loading support to stbi
+*/
+
+#ifndef HEADER_STB_IMAGE_DDS_AUGMENTATION
+#define HEADER_STB_IMAGE_DDS_AUGMENTATION
+
+// is it a DDS file?
+extern int stbi_dds_test_memory (stbi_uc const *buffer, int len);
+
+extern stbi_uc *stbi_dds_load (char *filename, int *x, int *y, int *comp, int req_comp);
+extern stbi_uc *stbi_dds_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp);
+#ifndef STBI_NO_STDIO
+extern int stbi_dds_test_file (FILE *f);
+extern stbi_uc *stbi_dds_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp);
+#endif
+
+//
+//
+//// end header file /////////////////////////////////////////////////////
+#endif // HEADER_STB_IMAGE_DDS_AUGMENTATION
diff --git a/cw_8/src/SOIL/stbi_DDS_aug_c.h b/cw_8/src/SOIL/stbi_DDS_aug_c.h
new file mode 100644
index 0000000..f49407a
--- /dev/null
+++ b/cw_8/src/SOIL/stbi_DDS_aug_c.h
@@ -0,0 +1,511 @@
+
+/// DDS file support, does decoding, _not_ direct uploading
+/// (use SOIL for that ;-)
+
+/// A bunch of DirectDraw Surface structures and flags
+typedef struct {
+ unsigned int dwMagic;
+ unsigned int dwSize;
+ unsigned int dwFlags;
+ unsigned int dwHeight;
+ unsigned int dwWidth;
+ unsigned int dwPitchOrLinearSize;
+ unsigned int dwDepth;
+ unsigned int dwMipMapCount;
+ unsigned int dwReserved1[ 11 ];
+
+ // DDPIXELFORMAT
+ struct {
+ unsigned int dwSize;
+ unsigned int dwFlags;
+ unsigned int dwFourCC;
+ unsigned int dwRGBBitCount;
+ unsigned int dwRBitMask;
+ unsigned int dwGBitMask;
+ unsigned int dwBBitMask;
+ unsigned int dwAlphaBitMask;
+ } sPixelFormat;
+
+ // DDCAPS2
+ struct {
+ unsigned int dwCaps1;
+ unsigned int dwCaps2;
+ unsigned int dwDDSX;
+ unsigned int dwReserved;
+ } sCaps;
+ unsigned int dwReserved2;
+} DDS_header ;
+
+// the following constants were copied directly off the MSDN website
+
+// The dwFlags member of the original DDSURFACEDESC2 structure
+// can be set to one or more of the following values.
+#define DDSD_CAPS 0x00000001
+#define DDSD_HEIGHT 0x00000002
+#define DDSD_WIDTH 0x00000004
+#define DDSD_PITCH 0x00000008
+#define DDSD_PIXELFORMAT 0x00001000
+#define DDSD_MIPMAPCOUNT 0x00020000
+#define DDSD_LINEARSIZE 0x00080000
+#define DDSD_DEPTH 0x00800000
+
+// DirectDraw Pixel Format
+#define DDPF_ALPHAPIXELS 0x00000001
+#define DDPF_FOURCC 0x00000004
+#define DDPF_RGB 0x00000040
+
+// The dwCaps1 member of the DDSCAPS2 structure can be
+// set to one or more of the following values.
+#define DDSCAPS_COMPLEX 0x00000008
+#define DDSCAPS_TEXTURE 0x00001000
+#define DDSCAPS_MIPMAP 0x00400000
+
+// The dwCaps2 member of the DDSCAPS2 structure can be
+// set to one or more of the following values.
+#define DDSCAPS2_CUBEMAP 0x00000200
+#define DDSCAPS2_CUBEMAP_POSITIVEX 0x00000400
+#define DDSCAPS2_CUBEMAP_NEGATIVEX 0x00000800
+#define DDSCAPS2_CUBEMAP_POSITIVEY 0x00001000
+#define DDSCAPS2_CUBEMAP_NEGATIVEY 0x00002000
+#define DDSCAPS2_CUBEMAP_POSITIVEZ 0x00004000
+#define DDSCAPS2_CUBEMAP_NEGATIVEZ 0x00008000
+#define DDSCAPS2_VOLUME 0x00200000
+
+static int dds_test(stbi *s)
+{
+ // check the magic number
+ if (get8(s) != 'D') return 0;
+ if (get8(s) != 'D') return 0;
+ if (get8(s) != 'S') return 0;
+ if (get8(s) != ' ') return 0;
+ // check header size
+ if (get32le(s) != 124) return 0;
+ return 1;
+}
+#ifndef STBI_NO_STDIO
+int stbi_dds_test_file (FILE *f)
+{
+ stbi s;
+ int r,n = ftell(f);
+ start_file(&s,f);
+ r = dds_test(&s);
+ fseek(f,n,SEEK_SET);
+ return r;
+}
+#endif
+
+int stbi_dds_test_memory (stbi_uc const *buffer, int len)
+{
+ stbi s;
+ start_mem(&s,buffer, len);
+ return dds_test(&s);
+}
+
+// helper functions
+int stbi_convert_bit_range( int c, int from_bits, int to_bits )
+{
+ int b = (1 << (from_bits - 1)) + c * ((1 << to_bits) - 1);
+ return (b + (b >> from_bits)) >> from_bits;
+}
+void stbi_rgb_888_from_565( unsigned int c, int *r, int *g, int *b )
+{
+ *r = stbi_convert_bit_range( (c >> 11) & 31, 5, 8 );
+ *g = stbi_convert_bit_range( (c >> 05) & 63, 6, 8 );
+ *b = stbi_convert_bit_range( (c >> 00) & 31, 5, 8 );
+}
+void stbi_decode_DXT1_block(
+ unsigned char uncompressed[16*4],
+ unsigned char compressed[8] )
+{
+ int next_bit = 4*8;
+ int i, r, g, b;
+ int c0, c1;
+ unsigned char decode_colors[4*4];
+ // find the 2 primary colors
+ c0 = compressed[0] + (compressed[1] << 8);
+ c1 = compressed[2] + (compressed[3] << 8);
+ stbi_rgb_888_from_565( c0, &r, &g, &b );
+ decode_colors[0] = r;
+ decode_colors[1] = g;
+ decode_colors[2] = b;
+ decode_colors[3] = 255;
+ stbi_rgb_888_from_565( c1, &r, &g, &b );
+ decode_colors[4] = r;
+ decode_colors[5] = g;
+ decode_colors[6] = b;
+ decode_colors[7] = 255;
+ if( c0 > c1 )
+ {
+ // no alpha, 2 interpolated colors
+ decode_colors[8] = (2*decode_colors[0] + decode_colors[4]) / 3;
+ decode_colors[9] = (2*decode_colors[1] + decode_colors[5]) / 3;
+ decode_colors[10] = (2*decode_colors[2] + decode_colors[6]) / 3;
+ decode_colors[11] = 255;
+ decode_colors[12] = (decode_colors[0] + 2*decode_colors[4]) / 3;
+ decode_colors[13] = (decode_colors[1] + 2*decode_colors[5]) / 3;
+ decode_colors[14] = (decode_colors[2] + 2*decode_colors[6]) / 3;
+ decode_colors[15] = 255;
+ } else
+ {
+ // 1 interpolated color, alpha
+ decode_colors[8] = (decode_colors[0] + decode_colors[4]) / 2;
+ decode_colors[9] = (decode_colors[1] + decode_colors[5]) / 2;
+ decode_colors[10] = (decode_colors[2] + decode_colors[6]) / 2;
+ decode_colors[11] = 255;
+ decode_colors[12] = 0;
+ decode_colors[13] = 0;
+ decode_colors[14] = 0;
+ decode_colors[15] = 0;
+ }
+ // decode the block
+ for( i = 0; i < 16*4; i += 4 )
+ {
+ int idx = ((compressed[next_bit>>3] >> (next_bit & 7)) & 3) * 4;
+ next_bit += 2;
+ uncompressed[i+0] = decode_colors[idx+0];
+ uncompressed[i+1] = decode_colors[idx+1];
+ uncompressed[i+2] = decode_colors[idx+2];
+ uncompressed[i+3] = decode_colors[idx+3];
+ }
+ // done
+}
+void stbi_decode_DXT23_alpha_block(
+ unsigned char uncompressed[16*4],
+ unsigned char compressed[8] )
+{
+ int i, next_bit = 0;
+ // each alpha value gets 4 bits
+ for( i = 3; i < 16*4; i += 4 )
+ {
+ uncompressed[i] = stbi_convert_bit_range(
+ (compressed[next_bit>>3] >> (next_bit&7)) & 15,
+ 4, 8 );
+ next_bit += 4;
+ }
+}
+void stbi_decode_DXT45_alpha_block(
+ unsigned char uncompressed[16*4],
+ unsigned char compressed[8] )
+{
+ int i, next_bit = 8*2;
+ unsigned char decode_alpha[8];
+ // each alpha value gets 3 bits, and the 1st 2 bytes are the range
+ decode_alpha[0] = compressed[0];
+ decode_alpha[1] = compressed[1];
+ if( decode_alpha[0] > decode_alpha[1] )
+ {
+ // 6 step intermediate
+ decode_alpha[2] = (6*decode_alpha[0] + 1*decode_alpha[1]) / 7;
+ decode_alpha[3] = (5*decode_alpha[0] + 2*decode_alpha[1]) / 7;
+ decode_alpha[4] = (4*decode_alpha[0] + 3*decode_alpha[1]) / 7;
+ decode_alpha[5] = (3*decode_alpha[0] + 4*decode_alpha[1]) / 7;
+ decode_alpha[6] = (2*decode_alpha[0] + 5*decode_alpha[1]) / 7;
+ decode_alpha[7] = (1*decode_alpha[0] + 6*decode_alpha[1]) / 7;
+ } else
+ {
+ // 4 step intermediate, pluss full and none
+ decode_alpha[2] = (4*decode_alpha[0] + 1*decode_alpha[1]) / 5;
+ decode_alpha[3] = (3*decode_alpha[0] + 2*decode_alpha[1]) / 5;
+ decode_alpha[4] = (2*decode_alpha[0] + 3*decode_alpha[1]) / 5;
+ decode_alpha[5] = (1*decode_alpha[0] + 4*decode_alpha[1]) / 5;
+ decode_alpha[6] = 0;
+ decode_alpha[7] = 255;
+ }
+ for( i = 3; i < 16*4; i += 4 )
+ {
+ int idx = 0, bit;
+ bit = (compressed[next_bit>>3] >> (next_bit&7)) & 1;
+ idx += bit << 0;
+ ++next_bit;
+ bit = (compressed[next_bit>>3] >> (next_bit&7)) & 1;
+ idx += bit << 1;
+ ++next_bit;
+ bit = (compressed[next_bit>>3] >> (next_bit&7)) & 1;
+ idx += bit << 2;
+ ++next_bit;
+ uncompressed[i] = decode_alpha[idx & 7];
+ }
+ // done
+}
+void stbi_decode_DXT_color_block(
+ unsigned char uncompressed[16*4],
+ unsigned char compressed[8] )
+{
+ int next_bit = 4*8;
+ int i, r, g, b;
+ int c0, c1;
+ unsigned char decode_colors[4*3];
+ // find the 2 primary colors
+ c0 = compressed[0] + (compressed[1] << 8);
+ c1 = compressed[2] + (compressed[3] << 8);
+ stbi_rgb_888_from_565( c0, &r, &g, &b );
+ decode_colors[0] = r;
+ decode_colors[1] = g;
+ decode_colors[2] = b;
+ stbi_rgb_888_from_565( c1, &r, &g, &b );
+ decode_colors[3] = r;
+ decode_colors[4] = g;
+ decode_colors[5] = b;
+ // Like DXT1, but no choicees:
+ // no alpha, 2 interpolated colors
+ decode_colors[6] = (2*decode_colors[0] + decode_colors[3]) / 3;
+ decode_colors[7] = (2*decode_colors[1] + decode_colors[4]) / 3;
+ decode_colors[8] = (2*decode_colors[2] + decode_colors[5]) / 3;
+ decode_colors[9] = (decode_colors[0] + 2*decode_colors[3]) / 3;
+ decode_colors[10] = (decode_colors[1] + 2*decode_colors[4]) / 3;
+ decode_colors[11] = (decode_colors[2] + 2*decode_colors[5]) / 3;
+ // decode the block
+ for( i = 0; i < 16*4; i += 4 )
+ {
+ int idx = ((compressed[next_bit>>3] >> (next_bit & 7)) & 3) * 3;
+ next_bit += 2;
+ uncompressed[i+0] = decode_colors[idx+0];
+ uncompressed[i+1] = decode_colors[idx+1];
+ uncompressed[i+2] = decode_colors[idx+2];
+ }
+ // done
+}
+static stbi_uc *dds_load(stbi *s, int *x, int *y, int *comp, int req_comp)
+{
+ // all variables go up front
+ stbi_uc *dds_data = NULL;
+ stbi_uc block[16*4];
+ stbi_uc compressed[8];
+ int flags, DXT_family;
+ int has_alpha, has_mipmap;
+ int is_compressed, cubemap_faces;
+ int block_pitch, num_blocks;
+ DDS_header header;
+ int i, sz, cf;
+ // load the header
+ if( sizeof( DDS_header ) != 128 )
+ {
+ return NULL;
+ }
+ getn( s, (stbi_uc*)(&header), 128 );
+ // and do some checking
+ if( header.dwMagic != (('D' << 0) | ('D' << 8) | ('S' << 16) | (' ' << 24)) ) return NULL;
+ if( header.dwSize != 124 ) return NULL;
+ flags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT;
+ if( (header.dwFlags & flags) != flags ) return NULL;
+ /* According to the MSDN spec, the dwFlags should contain
+ DDSD_LINEARSIZE if it's compressed, or DDSD_PITCH if
+ uncompressed. Some DDS writers do not conform to the
+ spec, so I need to make my reader more tolerant */
+ if( header.sPixelFormat.dwSize != 32 ) return NULL;
+ flags = DDPF_FOURCC | DDPF_RGB;
+ if( (header.sPixelFormat.dwFlags & flags) == 0 ) return NULL;
+ if( (header.sCaps.dwCaps1 & DDSCAPS_TEXTURE) == 0 ) return NULL;
+ // get the image data
+ s->img_x = header.dwWidth;
+ s->img_y = header.dwHeight;
+ s->img_n = 4;
+ is_compressed = (header.sPixelFormat.dwFlags & DDPF_FOURCC) / DDPF_FOURCC;
+ has_alpha = (header.sPixelFormat.dwFlags & DDPF_ALPHAPIXELS) / DDPF_ALPHAPIXELS;
+ has_mipmap = (header.sCaps.dwCaps1 & DDSCAPS_MIPMAP) && (header.dwMipMapCount > 1);
+ cubemap_faces = (header.sCaps.dwCaps2 & DDSCAPS2_CUBEMAP) / DDSCAPS2_CUBEMAP;
+ /* I need cubemaps to have square faces */
+ cubemap_faces &= (s->img_x == s->img_y);
+ cubemap_faces *= 5;
+ cubemap_faces += 1;
+ block_pitch = (s->img_x+3) >> 2;
+ num_blocks = block_pitch * ((s->img_y+3) >> 2);
+ /* let the user know what's going on */
+ *x = s->img_x;
+ *y = s->img_y;
+ *comp = s->img_n;
+ /* is this uncompressed? */
+ if( is_compressed )
+ {
+ /* compressed */
+ // note: header.sPixelFormat.dwFourCC is something like (('D'<<0)|('X'<<8)|('T'<<16)|('1'<<24))
+ DXT_family = 1 + (header.sPixelFormat.dwFourCC >> 24) - '1';
+ if( (DXT_family < 1) || (DXT_family > 5) ) return NULL;
+ /* check the expected size...oops, nevermind...
+ those non-compliant writers leave
+ dwPitchOrLinearSize == 0 */
+ // passed all the tests, get the RAM for decoding
+ sz = (s->img_x)*(s->img_y)*4*cubemap_faces;
+ dds_data = (unsigned char*)malloc( sz );
+ /* do this once for each face */
+ for( cf = 0; cf < cubemap_faces; ++ cf )
+ {
+ // now read and decode all the blocks
+ for( i = 0; i < num_blocks; ++i )
+ {
+ // where are we?
+ int bx, by, bw=4, bh=4;
+ int ref_x = 4 * (i % block_pitch);
+ int ref_y = 4 * (i / block_pitch);
+ // get the next block's worth of compressed data, and decompress it
+ if( DXT_family == 1 )
+ {
+ // DXT1
+ getn( s, compressed, 8 );
+ stbi_decode_DXT1_block( block, compressed );
+ } else if( DXT_family < 4 )
+ {
+ // DXT2/3
+ getn( s, compressed, 8 );
+ stbi_decode_DXT23_alpha_block ( block, compressed );
+ getn( s, compressed, 8 );
+ stbi_decode_DXT_color_block ( block, compressed );
+ } else
+ {
+ // DXT4/5
+ getn( s, compressed, 8 );
+ stbi_decode_DXT45_alpha_block ( block, compressed );
+ getn( s, compressed, 8 );
+ stbi_decode_DXT_color_block ( block, compressed );
+ }
+ // is this a partial block?
+ if( ref_x + 4 > s->img_x )
+ {
+ bw = s->img_x - ref_x;
+ }
+ if( ref_y + 4 > s->img_y )
+ {
+ bh = s->img_y - ref_y;
+ }
+ // now drop our decompressed data into the buffer
+ for( by = 0; by < bh; ++by )
+ {
+ int idx = 4*((ref_y+by+cf*s->img_x)*s->img_x + ref_x);
+ for( bx = 0; bx < bw*4; ++bx )
+ {
+
+ dds_data[idx+bx] = block[by*16+bx];
+ }
+ }
+ }
+ /* done reading and decoding the main image...
+ skip MIPmaps if present */
+ if( has_mipmap )
+ {
+ int block_size = 16;
+ if( DXT_family == 1 )
+ {
+ block_size = 8;
+ }
+ for( i = 1; i < header.dwMipMapCount; ++i )
+ {
+ int mx = s->img_x >> (i + 2);
+ int my = s->img_y >> (i + 2);
+ if( mx < 1 )
+ {
+ mx = 1;
+ }
+ if( my < 1 )
+ {
+ my = 1;
+ }
+ skip( s, mx*my*block_size );
+ }
+ }
+ }/* per cubemap face */
+ } else
+ {
+ /* uncompressed */
+ DXT_family = 0;
+ s->img_n = 3;
+ if( has_alpha )
+ {
+ s->img_n = 4;
+ }
+ *comp = s->img_n;
+ sz = s->img_x*s->img_y*s->img_n*cubemap_faces;
+ dds_data = (unsigned char*)malloc( sz );
+ /* do this once for each face */
+ for( cf = 0; cf < cubemap_faces; ++ cf )
+ {
+ /* read the main image for this face */
+ getn( s, &dds_data[cf*s->img_x*s->img_y*s->img_n], s->img_x*s->img_y*s->img_n );
+ /* done reading and decoding the main image...
+ skip MIPmaps if present */
+ if( has_mipmap )
+ {
+ for( i = 1; i < header.dwMipMapCount; ++i )
+ {
+ int mx = s->img_x >> i;
+ int my = s->img_y >> i;
+ if( mx < 1 )
+ {
+ mx = 1;
+ }
+ if( my < 1 )
+ {
+ my = 1;
+ }
+ skip( s, mx*my*s->img_n );
+ }
+ }
+ }
+ /* data was BGR, I need it RGB */
+ for( i = 0; i < sz; i += s->img_n )
+ {
+ unsigned char temp = dds_data[i];
+ dds_data[i] = dds_data[i+2];
+ dds_data[i+2] = temp;
+ }
+ }
+ /* finished decompressing into RGBA,
+ adjust the y size if we have a cubemap
+ note: sz is already up to date */
+ s->img_y *= cubemap_faces;
+ *y = s->img_y;
+ // did the user want something else, or
+ // see if all the alpha values are 255 (i.e. no transparency)
+ has_alpha = 0;
+ if( s->img_n == 4)
+ {
+ for( i = 3; (i < sz) && (has_alpha == 0); i += 4 )
+ {
+ has_alpha |= (dds_data[i] < 255);
+ }
+ }
+ if( (req_comp <= 4) && (req_comp >= 1) )
+ {
+ // user has some requirements, meet them
+ if( req_comp != s->img_n )
+ {
+ dds_data = convert_format( dds_data, s->img_n, req_comp, s->img_x, s->img_y );
+ *comp = s->img_n;
+ }
+ } else
+ {
+ // user had no requirements, only drop to RGB is no alpha
+ if( (has_alpha == 0) && (s->img_n == 4) )
+ {
+ dds_data = convert_format( dds_data, 4, 3, s->img_x, s->img_y );
+ *comp = 3;
+ }
+ }
+ // OK, done
+ return dds_data;
+}
+
+#ifndef STBI_NO_STDIO
+stbi_uc *stbi_dds_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp)
+{
+ stbi s;
+ start_file(&s,f);
+ return dds_load(&s,x,y,comp,req_comp);
+}
+
+stbi_uc *stbi_dds_load (char *filename, int *x, int *y, int *comp, int req_comp)
+{
+ stbi_uc *data;
+ FILE *f = fopen(filename, "rb");
+ if (!f) return NULL;
+ data = stbi_dds_load_from_file(f,x,y,comp,req_comp);
+ fclose(f);
+ return data;
+}
+#endif
+
+stbi_uc *stbi_dds_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp)
+{
+ stbi s;
+ start_mem(&s,buffer, len);
+ return dds_load(&s,x,y,comp,req_comp);
+}
diff --git a/cw_8/src/Shader_Loader.cpp b/cw_8/src/Shader_Loader.cpp
new file mode 100644
index 0000000..6e118e9
--- /dev/null
+++ b/cw_8/src/Shader_Loader.cpp
@@ -0,0 +1,102 @@
+#include "Shader_Loader.h"
+#include
+#include
+#include
+
+using namespace Core;
+
+Shader_Loader::Shader_Loader(void){}
+Shader_Loader::~Shader_Loader(void){}
+
+std::string Shader_Loader::ReadShader(char *filename)
+{
+
+ std::string shaderCode;
+ std::ifstream file(filename, std::ios::in);
+
+ if (!file.good())
+ {
+ std::cout << "Can't read file " << filename << std::endl;
+ std::terminate();
+ }
+
+ file.seekg(0, std::ios::end);
+ shaderCode.resize((unsigned int)file.tellg());
+ file.seekg(0, std::ios::beg);
+ file.read(&shaderCode[0], shaderCode.size());
+ file.close();
+ return shaderCode;
+}
+
+GLuint Shader_Loader::CreateShader(GLenum shaderType, std::string
+ source, char* shaderName)
+{
+
+ int compile_result = 0;
+
+ GLuint shader = glCreateShader(shaderType);
+ const char *shader_code_ptr = source.c_str();
+ const int shader_code_size = source.size();
+
+ glShaderSource(shader, 1, &shader_code_ptr, &shader_code_size);
+ glCompileShader(shader);
+ glGetShaderiv(shader, GL_COMPILE_STATUS, &compile_result);
+
+ //sprawdz bledy
+ if (compile_result == GL_FALSE)
+ {
+
+ int info_log_length = 0;
+ glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &info_log_length);
+ std::vector shader_log(info_log_length);
+ glGetShaderInfoLog(shader, info_log_length, NULL, &shader_log[0]);
+ std::cout << "ERROR compiling shader: " << shaderName << std::endl << &shader_log[0] << std::endl;
+ return 0;
+ }
+
+ return shader;
+}
+
+GLuint Shader_Loader::CreateProgram(char* vertexShaderFilename,
+ char* fragmentShaderFilename)
+{
+
+ //wczytaj shadery
+ std::string vertex_shader_code = ReadShader(vertexShaderFilename);
+ std::string fragment_shader_code = ReadShader(fragmentShaderFilename);
+
+ GLuint vertex_shader = CreateShader(GL_VERTEX_SHADER, vertex_shader_code, "vertex shader");
+ GLuint fragment_shader = CreateShader(GL_FRAGMENT_SHADER, fragment_shader_code, "fragment shader");
+
+ int link_result = 0;
+ //stworz shader
+ GLuint program = glCreateProgram();
+ glAttachShader(program, vertex_shader);
+ glAttachShader(program, fragment_shader);
+
+ glLinkProgram(program);
+ glGetProgramiv(program, GL_LINK_STATUS, &link_result);
+ //sprawdz bledy w linkerze
+ if (link_result == GL_FALSE)
+ {
+
+ int info_log_length = 0;
+ glGetProgramiv(program, GL_INFO_LOG_LENGTH, &info_log_length);
+ std::vector program_log(info_log_length);
+ glGetProgramInfoLog(program, info_log_length, NULL, &program_log[0]);
+ std::cout << "Shader Loader : LINK ERROR" << std::endl << &program_log[0] << std::endl;
+ return 0;
+ }
+
+ glDetachShader(program, vertex_shader);
+ glDetachShader(program, fragment_shader);
+ glDeleteShader(vertex_shader);
+ glDeleteShader(fragment_shader);
+
+ return program;
+}
+
+void Shader_Loader::DeleteProgram( GLuint program )
+{
+ glDeleteProgram(program);
+}
diff --git a/cw_8/src/Shader_Loader.h b/cw_8/src/Shader_Loader.h
new file mode 100644
index 0000000..1fb9757
--- /dev/null
+++ b/cw_8/src/Shader_Loader.h
@@ -0,0 +1,29 @@
+#pragma once
+
+#include "glew.h"
+#include "freeglut.h"
+#include
+
+namespace Core
+{
+
+ class Shader_Loader
+ {
+ private:
+
+ std::string ReadShader(char *filename);
+ GLuint CreateShader(GLenum shaderType,
+ std::string source,
+ char* shaderName);
+
+ public:
+
+ Shader_Loader(void);
+ ~Shader_Loader(void);
+ GLuint CreateProgram(char* VertexShaderFilename,
+ char* FragmentShaderFilename);
+
+ void DeleteProgram(GLuint program);
+
+ };
+}
\ No newline at end of file
diff --git a/cw_8/src/Texture.cpp b/cw_8/src/Texture.cpp
new file mode 100644
index 0000000..2548b13
--- /dev/null
+++ b/cw_8/src/Texture.cpp
@@ -0,0 +1,37 @@
+#include "Texture.h"
+
+#include
+#include
+#include
+#include
+#include "SOIL/SOIL.h"
+
+typedef unsigned char byte;
+
+GLuint Core::LoadTexture( const char * filepath )
+{
+ GLuint id;
+ glGenTextures(1, &id);
+ glBindTexture(GL_TEXTURE_2D, id);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+
+ int w, h;
+ unsigned char* image = SOIL_load_image(filepath, &w, &h, 0, SOIL_LOAD_RGBA);
+
+
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, image);
+ glGenerateMipmap(GL_TEXTURE_2D);
+ SOIL_free_image_data(image);
+
+ return id;
+}
+
+void Core::SetActiveTexture(GLuint textureID, const char * shaderVariableName, GLuint programID, int textureUnit)
+{
+ glUniform1i(glGetUniformLocation(programID, shaderVariableName), textureUnit);
+ glActiveTexture(GL_TEXTURE0 + textureUnit);
+ glBindTexture(GL_TEXTURE_2D, textureID);
+}
diff --git a/cw_8/src/Texture.h b/cw_8/src/Texture.h
new file mode 100644
index 0000000..910228e
--- /dev/null
+++ b/cw_8/src/Texture.h
@@ -0,0 +1,15 @@
+#pragma once
+
+#include "glew.h"
+#include "freeglut.h"
+
+namespace Core
+{
+ GLuint LoadTexture(const char * filepath);
+
+ // textureID - identyfikator tekstury otrzymany z funkcji LoadTexture
+ // shaderVariableName - nazwa zmiennej typu 'sampler2D' w shaderze, z ktora ma zostac powiazana tekstura
+ // programID - identyfikator aktualnego programu karty graficznej
+ // textureUnit - indeks jednostki teksturujacej - liczba od 0 do 7. Jezeli uzywa sie wielu tekstur w jednym shaderze, to kazda z nich nalezy powiazac z inna jednostka.
+ void SetActiveTexture(GLuint textureID, const char * shaderVariableName, GLuint programID, int textureUnit);
+}
\ No newline at end of file
diff --git a/cw_8/src/ex_8_1.hpp b/cw_8/src/ex_8_1.hpp
new file mode 100644
index 0000000..2589088
--- /dev/null
+++ b/cw_8/src/ex_8_1.hpp
@@ -0,0 +1,243 @@
+#include "glew.h"
+#include
+#include "glm.hpp"
+#include "ext.hpp"
+#include
+#include
+
+#include "Shader_Loader.h"
+#include "Render_Utils.h"
+//#include "Texture.h"
+
+#include "Box.cpp"
+#include
+#include
+#include
+#include
+
+
+namespace texture {
+ GLuint earth;
+ GLuint clouds;
+ GLuint moon;
+ GLuint ship;
+
+ GLuint grid;
+
+ GLuint earthNormal;
+ GLuint asteroidNormal;
+ GLuint shipNormal;
+}
+
+
+GLuint program;
+GLuint programSun;
+GLuint programTex;
+Core::Shader_Loader shaderLoader;
+
+Core::RenderContext shipContext;
+Core::RenderContext sphereContext;
+
+glm::vec3 cameraPos = glm::vec3(-4.f, 0, 0);
+glm::vec3 cameraDir = glm::vec3(1.f, 0.f, 0.f);
+
+
+glm::vec3 spaceshipPos = glm::vec3(0.479490f, 1.000000f, -2.124680f);
+glm::vec3 spaceshipDir = glm::vec3(-0.354510f, 0.000000f, 0.935054f);
+
+GLuint VAO,VBO;
+
+float aspectRatio = 1.f;
+
+float exposition = 1.f;
+
+glm::vec3 lightPos = glm::vec3(-8, 4, 2);
+glm::vec3 lightColor = glm::vec3(0.9, 0.7, 0.8)*100;
+
+glm::vec3 spotlightPos = glm::vec3(0, 0, 0);
+glm::vec3 spotlightConeDir = glm::vec3(0, 0, 0);
+glm::vec3 spotlightColor = glm::vec3(0.5, 0.9, 0.8)*10;
+float spotlightPhi = 3.14 / 3;
+
+
+glm::mat4 createCameraMatrix()
+{
+ glm::vec3 cameraSide = glm::normalize(glm::cross(cameraDir,glm::vec3(0.f,1.f,0.f)));
+ glm::vec3 cameraUp = glm::normalize(glm::cross(cameraSide,cameraDir));
+ glm::mat4 cameraRotrationMatrix = glm::mat4({
+ cameraSide.x,cameraSide.y,cameraSide.z,0,
+ cameraUp.x,cameraUp.y,cameraUp.z ,0,
+ -cameraDir.x,-cameraDir.y,-cameraDir.z,0,
+ 0.,0.,0.,1.,
+ });
+ cameraRotrationMatrix = glm::transpose(cameraRotrationMatrix);
+ glm::mat4 cameraMatrix = cameraRotrationMatrix * glm::translate(-cameraPos);
+
+ return cameraMatrix;
+}
+
+glm::mat4 createPerspectiveMatrix()
+{
+
+ glm::mat4 perspectiveMatrix;
+ float n = 0.05;
+ float f = 20.;
+ float a1 = glm::min(aspectRatio, 1.f);
+ float a2 = glm::min(1 / aspectRatio, 1.f);
+ perspectiveMatrix = glm::mat4({
+ 1,0.,0.,0.,
+ 0.,aspectRatio,0.,0.,
+ 0.,0.,(f+n) / (n - f),2*f * n / (n - f),
+ 0.,0.,-1.,0.,
+ });
+
+
+ perspectiveMatrix=glm::transpose(perspectiveMatrix);
+
+ return perspectiveMatrix;
+}
+
+void drawObjectColor(Core::RenderContext& context, glm::mat4 modelMatrix, glm::vec3 color) {
+
+ glm::mat4 viewProjectionMatrix = createPerspectiveMatrix() * createCameraMatrix();
+ glm::mat4 transformation = viewProjectionMatrix * modelMatrix;
+ glUniformMatrix4fv(glGetUniformLocation(program, "transformation"), 1, GL_FALSE, (float*)&transformation);
+ glUniformMatrix4fv(glGetUniformLocation(program, "modelMatrix"), 1, GL_FALSE, (float*)&modelMatrix);
+
+ glUniform1f(glGetUniformLocation(program, "exposition"), exposition);
+
+ glUniform3f(glGetUniformLocation(program, "color"), color.x, color.y, color.z);
+
+ glUniform3f(glGetUniformLocation(program, "cameraPos"), cameraPos.x, cameraPos.y, cameraPos.z);
+
+ glUniform3f(glGetUniformLocation(program, "lightPos"), lightPos.x, lightPos.y, lightPos.z);
+ glUniform3f(glGetUniformLocation(program, "lightColor"), lightColor.x, lightColor.y, lightColor.z);
+
+ glUniform3f(glGetUniformLocation(program, "spotlightConeDir"), spotlightConeDir.x, spotlightConeDir.y, spotlightConeDir.z);
+ glUniform3f(glGetUniformLocation(program, "spotlightPos"), spotlightPos.x, spotlightPos.y, spotlightPos.z);
+ glUniform3f(glGetUniformLocation(program, "spotlightColor"), spotlightColor.x, spotlightColor.y, spotlightColor.z);
+ glUniform1f(glGetUniformLocation(program, "spotlightPhi"), spotlightPhi);
+ Core::DrawContext(context);
+
+}
+void renderScene(GLFWwindow* window)
+{
+ glClearColor(0.0f, 0.3f, 0.3f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ glm::mat4 transformation;
+ float time = glfwGetTime();
+
+
+ glUseProgram(program);
+ drawObjectColor(sphereContext, glm::mat4(), glm::vec3(0.95, 0.1, 0.1));
+
+ glm::vec3 spaceshipSide = glm::normalize(glm::cross(spaceshipDir, glm::vec3(0.f, 1.f, 0.f)));
+ glm::vec3 spaceshipUp = glm::normalize(glm::cross(spaceshipSide, spaceshipDir));
+ glm::mat4 specshipCameraRotrationMatrix = glm::mat4({
+ spaceshipSide.x,spaceshipSide.y,spaceshipSide.z,0,
+ spaceshipUp.x,spaceshipUp.y,spaceshipUp.z ,0,
+ -spaceshipDir.x,-spaceshipDir.y,-spaceshipDir.z,0,
+ 0.,0.,0.,1.,
+ });
+
+
+ //drawObjectColor(shipContext,
+ // glm::translate(cameraPos + 1.5 * cameraDir + cameraUp * -0.5f) * inveseCameraRotrationMatrix * glm::eulerAngleY(glm::pi()),
+ // glm::vec3(0.3, 0.3, 0.5)
+ // );
+ drawObjectColor(shipContext,
+ glm::translate(spaceshipPos) * specshipCameraRotrationMatrix * glm::eulerAngleY(glm::pi()) * glm::scale(glm::vec3(0.1)),
+ glm::vec3(0.3, 0.3, 0.5)
+ );
+
+ spotlightPos = spaceshipPos + 0.5 * spaceshipDir;
+ spotlightConeDir = spaceshipDir;
+
+ glUseProgram(0);
+ glfwSwapBuffers(window);
+}
+void framebuffer_size_callback(GLFWwindow* window, int width, int height)
+{
+ aspectRatio = width / float(height);
+ glViewport(0, 0, width, height);
+}
+void loadModelToContext(std::string path, Core::RenderContext& context)
+{
+ Assimp::Importer import;
+ const aiScene* scene = import.ReadFile(path, aiProcess_Triangulate | aiProcess_CalcTangentSpace);
+
+ if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode)
+ {
+ std::cout << "ERROR::ASSIMP::" << import.GetErrorString() << std::endl;
+ return;
+ }
+ context.initFromAssimpMesh(scene->mMeshes[0]);
+}
+
+void init(GLFWwindow* window)
+{
+ glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
+
+ glEnable(GL_DEPTH_TEST);
+ program = shaderLoader.CreateProgram("shaders/shader_8_1.vert", "shaders/shader_8_1.frag");
+ programSun = shaderLoader.CreateProgram("shaders/shader_8_sun.vert", "shaders/shader_8_sun.frag");
+
+ loadModelToContext("./models/sphere.obj", sphereContext);
+ loadModelToContext("./models/spaceship.obj", shipContext);
+
+}
+
+void shutdown(GLFWwindow* window)
+{
+ shaderLoader.DeleteProgram(program);
+}
+
+//obsluga wejscia
+void processInput(GLFWwindow* window)
+{
+ glm::vec3 spaceshipSide = glm::normalize(glm::cross(spaceshipDir, glm::vec3(0.f, 1.f, 0.f)));
+ glm::vec3 spaceshipUp = glm::vec3(0.f, 1.f, 0.f);
+ float angleSpeed = 0.05f;
+ float moveSpeed = 0.05f;
+ if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) {
+ glfwSetWindowShouldClose(window, true);
+ }
+ if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
+ spaceshipPos += spaceshipDir * moveSpeed;
+ if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
+ spaceshipPos -= spaceshipDir * moveSpeed;
+ if (glfwGetKey(window, GLFW_KEY_X) == GLFW_PRESS)
+ spaceshipPos += spaceshipSide * moveSpeed;
+ if (glfwGetKey(window, GLFW_KEY_Z) == GLFW_PRESS)
+ spaceshipPos -= spaceshipSide * moveSpeed;
+ if (glfwGetKey(window, GLFW_KEY_Q) == GLFW_PRESS)
+ spaceshipPos += spaceshipUp * moveSpeed;
+ if (glfwGetKey(window, GLFW_KEY_E) == GLFW_PRESS)
+ spaceshipPos -= spaceshipUp * moveSpeed;
+ if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
+ spaceshipDir = glm::vec3(glm::eulerAngleY(angleSpeed) * glm::vec4(spaceshipDir, 0));
+ if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
+ spaceshipDir = glm::vec3(glm::eulerAngleY(-angleSpeed) * glm::vec4(spaceshipDir, 0));
+
+ cameraPos = spaceshipPos - 1.5 * spaceshipDir + glm::vec3(0, 1, 0) * 0.5f;
+ cameraDir = spaceshipDir;
+ if (glfwGetKey(window, GLFW_KEY_1) == GLFW_PRESS)
+ exposition -= 0.05;
+ if (glfwGetKey(window, GLFW_KEY_2) == GLFW_PRESS)
+ exposition += 0.05;
+
+ //cameraDir = glm::normalize(-cameraPos);
+
+}
+
+// funkcja jest glowna petla
+void renderLoop(GLFWwindow* window) {
+ while (!glfwWindowShouldClose(window))
+ {
+ processInput(window);
+
+ renderScene(window);
+ glfwPollEvents();
+ }
+}
+//}
\ No newline at end of file
diff --git a/cw_8/src/ex_8_2.hpp b/cw_8/src/ex_8_2.hpp
new file mode 100644
index 0000000..0c0392f
--- /dev/null
+++ b/cw_8/src/ex_8_2.hpp
@@ -0,0 +1,270 @@
+#include "glew.h"
+#include
+#include "glm.hpp"
+#include "ext.hpp"
+#include
+#include
+
+#include "Shader_Loader.h"
+#include "Render_Utils.h"
+//#include "Texture.h"
+
+#include "Box.cpp"
+#include
+#include
+#include
+#include
+
+
+namespace texture {
+ GLuint earth;
+ GLuint clouds;
+ GLuint moon;
+ GLuint ship;
+
+ GLuint grid;
+
+ GLuint earthNormal;
+ GLuint asteroidNormal;
+ GLuint shipNormal;
+}
+
+
+GLuint program;
+GLuint programSun;
+GLuint programTex;
+Core::Shader_Loader shaderLoader;
+
+Core::RenderContext shipContext;
+Core::RenderContext sphereContext;
+
+glm::vec3 cameraPos = glm::vec3(-4.f, 0, 0);
+glm::vec3 cameraDir = glm::vec3(1.f, 0.f, 0.f);
+
+glm::vec3 spaceshipPos = glm::vec3(-4.f, 0, 0);
+glm::vec3 spaceshipDir = glm::vec3(1.f, 0.f, 0.f);
+
+GLuint VAO,VBO;
+
+float aspectRatio = 1.f;
+
+float exposition = 1.f;
+
+glm::vec3 lightPos = glm::vec3(0, 0, 0);
+glm::vec3 lightColor = glm::vec3(0.9, 0.7, 0.8)*100;
+
+glm::vec3 spotlightPos = glm::vec3(0, 0, 0);
+glm::vec3 spotlightConeDir = glm::vec3(0, 0, 0);
+glm::vec3 spotlightColor = glm::vec3(0.5, 0.9, 0.8)*10;
+float spotlightPhi = 3.14 / 3;
+
+
+float lastTime = -1.f;
+float deltaTime = 0.f;
+
+void updateDeltaTime(float time) {
+ if (lastTime < 0) {
+ lastTime = time;
+ return;
+ }
+
+ deltaTime = time - lastTime;
+ if (deltaTime > 0.1) deltaTime = 0.1;
+ lastTime = time;
+}
+glm::mat4 createCameraMatrix()
+{
+ glm::vec3 cameraSide = glm::normalize(glm::cross(cameraDir,glm::vec3(0.f,1.f,0.f)));
+ glm::vec3 cameraUp = glm::normalize(glm::cross(cameraSide,cameraDir));
+ glm::mat4 cameraRotrationMatrix = glm::mat4({
+ cameraSide.x,cameraSide.y,cameraSide.z,0,
+ cameraUp.x,cameraUp.y,cameraUp.z ,0,
+ -cameraDir.x,-cameraDir.y,-cameraDir.z,0,
+ 0.,0.,0.,1.,
+ });
+ cameraRotrationMatrix = glm::transpose(cameraRotrationMatrix);
+ glm::mat4 cameraMatrix = cameraRotrationMatrix * glm::translate(-cameraPos);
+
+ return cameraMatrix;
+}
+
+glm::mat4 createPerspectiveMatrix()
+{
+
+ glm::mat4 perspectiveMatrix;
+ float n = 0.05;
+ float f = 20.;
+ float a1 = glm::min(aspectRatio, 1.f);
+ float a2 = glm::min(1 / aspectRatio, 1.f);
+ perspectiveMatrix = glm::mat4({
+ 1,0.,0.,0.,
+ 0.,aspectRatio,0.,0.,
+ 0.,0.,(f+n) / (n - f),2*f * n / (n - f),
+ 0.,0.,-1.,0.,
+ });
+
+
+ perspectiveMatrix=glm::transpose(perspectiveMatrix);
+
+ return perspectiveMatrix;
+}
+
+void drawObjectColor(Core::RenderContext& context, glm::mat4 modelMatrix, glm::vec3 color) {
+
+ glm::mat4 viewProjectionMatrix = createPerspectiveMatrix() * createCameraMatrix();
+ glm::mat4 transformation = viewProjectionMatrix * modelMatrix;
+ glUniformMatrix4fv(glGetUniformLocation(program, "transformation"), 1, GL_FALSE, (float*)&transformation);
+ glUniformMatrix4fv(glGetUniformLocation(program, "modelMatrix"), 1, GL_FALSE, (float*)&modelMatrix);
+
+ glUniform1f(glGetUniformLocation(program, "exposition"), exposition);
+
+ glUniform3f(glGetUniformLocation(program, "color"), color.x, color.y, color.z);
+
+ glUniform3f(glGetUniformLocation(program, "cameraPos"), cameraPos.x, cameraPos.y, cameraPos.z);
+
+ glUniform3f(glGetUniformLocation(program, "lightPos"), lightPos.x, lightPos.y, lightPos.z);
+ glUniform3f(glGetUniformLocation(program, "lightColor"), lightColor.x, lightColor.y, lightColor.z);
+
+ glUniform3f(glGetUniformLocation(program, "spotlightConeDir"), spotlightConeDir.x, spotlightConeDir.y, spotlightConeDir.z);
+ glUniform3f(glGetUniformLocation(program, "spotlightPos"), spotlightPos.x, spotlightPos.y, spotlightPos.z);
+ glUniform3f(glGetUniformLocation(program, "spotlightColor"), spotlightColor.x, spotlightColor.y, spotlightColor.z);
+ glUniform1f(glGetUniformLocation(program, "spotlightPhi"), spotlightPhi);
+ Core::DrawContext(context);
+
+}
+void renderScene(GLFWwindow* window)
+{
+ glClearColor(0.0f, 0.3f, 0.3f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ glm::mat4 transformation;
+ float time = glfwGetTime();
+ updateDeltaTime(time);
+
+
+
+ glUseProgram(programSun);
+ transformation = createPerspectiveMatrix() * createCameraMatrix();
+ glUniformMatrix4fv(glGetUniformLocation(programSun, "transformation"), 1, GL_FALSE, (float*)&transformation);
+ glUniform3f(glGetUniformLocation(programSun, "color"), 0.9,0.8,0.4);
+ Core::DrawContext(sphereContext);
+
+ glUseProgram(program);
+ drawObjectColor(sphereContext, glm::eulerAngleY(time / 3) * glm::translate(glm::vec3(4.f, 0, 0)) * glm::scale(glm::vec3(0.3f)), glm::vec3(0.2, 0.7, 0.3));
+
+ drawObjectColor(sphereContext,
+ glm::eulerAngleY(time / 3) * glm::translate(glm::vec3(4.f, 0, 0)) * glm::eulerAngleY(time) * glm::translate(glm::vec3(1.f, 0, 0)) * glm::scale(glm::vec3(0.1f)),
+ glm::vec3(0.5, 0.5, 0.5));
+
+
+
+ glm::vec3 spaceshipSide = glm::normalize(glm::cross(spaceshipDir, glm::vec3(0.f, 1.f, 0.f)));
+ glm::vec3 spaceshipUp = glm::normalize(glm::cross(spaceshipSide, spaceshipDir));
+ glm::mat4 specshipCameraRotrationMatrix = glm::mat4({
+ spaceshipSide.x,spaceshipSide.y,spaceshipSide.z,0,
+ spaceshipUp.x,spaceshipUp.y,spaceshipUp.z ,0,
+ -spaceshipDir.x,-spaceshipDir.y,-spaceshipDir.z,0,
+ 0.,0.,0.,1.,
+ });
+
+
+ //drawObjectColor(shipContext,
+ // glm::translate(cameraPos + 1.5 * cameraDir + cameraUp * -0.5f) * inveseCameraRotrationMatrix * glm::eulerAngleY(glm::pi()),
+ // glm::vec3(0.3, 0.3, 0.5)
+ // );
+ drawObjectColor(shipContext,
+ glm::translate(spaceshipPos) * specshipCameraRotrationMatrix * glm::eulerAngleY(glm::pi())*glm::scale(glm::vec3(0.1)),
+ glm::vec3(0.3, 0.3, 0.5)
+ );
+
+ spotlightPos = spaceshipPos + 0.5 * spaceshipDir;
+ spotlightConeDir = spaceshipDir;
+
+ glUseProgram(0);
+ glfwSwapBuffers(window);
+}
+void framebuffer_size_callback(GLFWwindow* window, int width, int height)
+{
+ aspectRatio = width / float(height);
+ glViewport(0, 0, width, height);
+}
+void loadModelToContext(std::string path, Core::RenderContext& context)
+{
+ Assimp::Importer import;
+ const aiScene* scene = import.ReadFile(path, aiProcess_Triangulate | aiProcess_CalcTangentSpace);
+
+ if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode)
+ {
+ std::cout << "ERROR::ASSIMP::" << import.GetErrorString() << std::endl;
+ return;
+ }
+ context.initFromAssimpMesh(scene->mMeshes[0]);
+}
+
+void init(GLFWwindow* window)
+{
+ glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
+
+ glEnable(GL_DEPTH_TEST);
+ program = shaderLoader.CreateProgram("shaders/shader_8_1.vert", "shaders/shader_8_1.frag");
+ programSun = shaderLoader.CreateProgram("shaders/shader_8_sun.vert", "shaders/shader_8_sun.frag");
+
+ loadModelToContext("./models/sphere.obj", sphereContext);
+ loadModelToContext("./models/spaceship.obj", shipContext);
+
+}
+
+void shutdown(GLFWwindow* window)
+{
+ shaderLoader.DeleteProgram(program);
+}
+
+//obsluga wejscia
+void processInput(GLFWwindow* window)
+{
+ glm::vec3 spaceshipSide = glm::normalize(glm::cross(spaceshipDir, glm::vec3(0.f, 1.f, 0.f)));
+ glm::vec3 spaceshipUp = glm::vec3(0.f, 1.f, 0.f);
+ float angleSpeed = 0.05f * deltaTime * 60;
+ float moveSpeed = 0.05f * deltaTime * 60;
+ if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) {
+ glfwSetWindowShouldClose(window, true);
+ }
+ if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
+ spaceshipPos += spaceshipDir * moveSpeed;
+ if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
+ spaceshipPos -= spaceshipDir * moveSpeed;
+ if (glfwGetKey(window, GLFW_KEY_X) == GLFW_PRESS)
+ spaceshipPos += spaceshipSide * moveSpeed;
+ if (glfwGetKey(window, GLFW_KEY_Z) == GLFW_PRESS)
+ spaceshipPos -= spaceshipSide * moveSpeed;
+ if (glfwGetKey(window, GLFW_KEY_Q) == GLFW_PRESS)
+ spaceshipPos += spaceshipUp * moveSpeed;
+ if (glfwGetKey(window, GLFW_KEY_E) == GLFW_PRESS)
+ spaceshipPos -= spaceshipUp * moveSpeed;
+ if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
+ spaceshipDir = glm::vec3(glm::eulerAngleY(angleSpeed) * glm::vec4(spaceshipDir, 0));
+ if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
+ spaceshipDir = glm::vec3(glm::eulerAngleY(-angleSpeed) * glm::vec4(spaceshipDir, 0));
+
+ cameraPos = spaceshipPos - 1.5 * spaceshipDir + glm::vec3(0, 1, 0) * 0.5f;
+ cameraDir = spaceshipDir;
+
+ if (glfwGetKey(window, GLFW_KEY_1) == GLFW_PRESS)
+ exposition -= 0.05;
+ if (glfwGetKey(window, GLFW_KEY_2) == GLFW_PRESS)
+ exposition += 0.05;
+
+ //cameraDir = glm::normalize(-cameraPos);
+
+}
+
+// funkcja jest glowna petla
+void renderLoop(GLFWwindow* window) {
+ while (!glfwWindowShouldClose(window))
+ {
+ processInput(window);
+
+ renderScene(window);
+ glfwPollEvents();
+ }
+}
+//}
\ No newline at end of file
diff --git a/cw_8/src/main.cpp b/cw_8/src/main.cpp
new file mode 100644
index 0000000..97a3bb4
--- /dev/null
+++ b/cw_8/src/main.cpp
@@ -0,0 +1,47 @@
+#include "glew.h"
+
+#include
+#include "glm.hpp"
+#include "ext.hpp"
+#include
+#include
+
+#include "projekt.hpp"
+
+
+
+int main(int argc, char** argv)
+{
+ // inicjalizacja glfw
+ glfwInit();
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
+ glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
+
+#ifdef __APPLE__
+ glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
+#endif
+
+ // tworzenie okna za pomoca glfw
+ GLFWwindow* window = glfwCreateWindow(500, 500, "FirstWindow", NULL, NULL);
+ if (window == NULL)
+ {
+ std::cout << "Failed to create GLFW window" << std::endl;
+ glfwTerminate();
+ return -1;
+ }
+ glfwMakeContextCurrent(window);
+
+ // ladowanie OpenGL za pomoca glew
+ glewInit();
+ glViewport(0, 0, 500, 500);
+
+ init(window);
+
+ // uruchomienie glownej petli
+ renderLoop(window);
+
+ shutdown(window);
+ glfwTerminate();
+ return 0;
+}
diff --git a/cw_8/src/objload.h b/cw_8/src/objload.h
new file mode 100644
index 0000000..004ab4e
--- /dev/null
+++ b/cw_8/src/objload.h
@@ -0,0 +1,291 @@
+/* Copyright (c) 2012, Gerhard Reitmayr
+ All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+
+#ifndef OBJLOAD_H_
+#define OBJLOAD_H_
+
+#include
+#include
+#include
+#include
+#include
+#include