blob: b128d25ab29185e393492f60f62723adc4eb1b8b [file] [log] [blame]
/*
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 <windows.h>
#include <wingdi.h>
#include <GL/gl.h>
#elif defined(__APPLE__) || defined(__APPLE_CC__)
/* I can't test this Apple stuff! */
#include <OpenGL/gl.h>
#include <Carbon/Carbon.h>
#define APIENTRY
#else
#ifdef USE_GLES1
#include <GLES/gl.h>
#define APIENTRY
#else
#include <GL/gl.h>
#include <GL/glx.h>
#endif
#endif
#include "SOIL.h"
#include "stb_image_aug.h"
#include "image_helper.h"
#include "image_DXT.h"
#include <stdlib.h>
#include <string.h>
/* 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
)
{
int width, height;
return SOIL_load_OGL_texture_size(filename,force_channels,reuse_texture_ID,flags,&width, &height);
}
unsigned int
SOIL_load_OGL_texture_size
(
const char *filename,
int force_channels,
unsigned int reuse_texture_ID,
unsigned int flags,
int *width,
int *height
)
{
/* variables */
unsigned char* img;
int 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<<MIPlevel) <= width) || ((1<<MIPlevel) <= height) )
{
/* do this MIPmap level */
mipmap_image(
img, width, height, channels,
resampled,
(1 << MIPlevel), (1 << MIPlevel) );
/* upload the MIPmaps */
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(
resampled, MIPwidth, MIPheight, channels, &DDS_size );
} else
{
/* RGBA, use DXT5 */
DDS_data = convert_image_to_DXT5(
resampled, MIPwidth, MIPheight, channels, &DDS_size );
}
if( DDS_data )
{
soilGlCompressedTexImage2D(
opengl_texture_target, MIPlevel,
internal_texture_format, MIPwidth, MIPheight, 0,
DDS_size, DDS_data );
check_for_GL_errors( "glCompressedTexImage2D" );
SOIL_free_image_data( DDS_data );
} else
{
/* my compression failed, try the OpenGL driver's version */
glTexImage2D(
opengl_texture_target, MIPlevel,
internal_texture_format, MIPwidth, MIPheight, 0,
original_texture_format, GL_UNSIGNED_BYTE, resampled );
check_for_GL_errors( "glTexImage2D" );
}
} else
{
/* user want OpenGL to do all the work! */
glTexImage2D(
opengl_texture_target, MIPlevel,
internal_texture_format, MIPwidth, MIPheight, 0,
original_texture_format, GL_UNSIGNED_BYTE, resampled );
check_for_GL_errors( "glTexImage2D" );
}
/* prep for the next level */
++MIPlevel;
MIPwidth = (MIPwidth + 1) / 2;
MIPheight = (MIPheight + 1) / 2;
}
SOIL_free_image_data( resampled );
/* 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 );
check_for_GL_errors( "GL_TEXTURE_MIN/MAG_FILTER" );
} 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 );
check_for_GL_errors( "GL_TEXTURE_MIN/MAG_FILTER" );
}
/* 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 );
if( opengl_texture_type == SOIL_TEXTURE_CUBE_MAP )
{
/* SOIL_TEXTURE_WRAP_R is invalid if cubemaps aren't supported */
glTexParameteri( opengl_texture_type, SOIL_TEXTURE_WRAP_R, GL_REPEAT );
}
check_for_GL_errors( "GL_TEXTURE_WRAP_*" );
} else
{
/* unsigned int clamp_mode = SOIL_CLAMP_TO_EDGE; */
unsigned int clamp_mode = GL_CLAMP_TO_EDGE;
glTexParameteri( opengl_texture_type, GL_TEXTURE_WRAP_S, clamp_mode );
glTexParameteri( opengl_texture_type, GL_TEXTURE_WRAP_T, clamp_mode );
if( opengl_texture_type == SOIL_TEXTURE_CUBE_MAP )
{
/* SOIL_TEXTURE_WRAP_R is invalid if cubemaps aren't supported */
glTexParameteri( opengl_texture_type, SOIL_TEXTURE_WRAP_R, clamp_mode );
}
check_for_GL_errors( "GL_TEXTURE_WRAP_*" );
}
/* done */
result_string_pointer = "Image loaded as an OpenGL texture";
} else
{
/* failed */
result_string_pointer = "Failed to generate an OpenGL texture name; missing OpenGL context?";
}
SOIL_free_image_data( img );
return tex_id;
}
int
SOIL_save_screenshot
(
const char *filename,
int image_type,
int x, int y,
int width, int height
)
{
unsigned char *pixel_data;
int i, j;
int save_result;
/* error checks */
if( (width < 1) || (height < 1) )
{
result_string_pointer = "Invalid screenshot dimensions";
return 0;
}
if( (x < 0) || (y < 0) )
{
result_string_pointer = "Invalid screenshot location";
return 0;
}
if( filename == NULL )
{
result_string_pointer = "Invalid screenshot filename";
return 0;
}
/* Get the data from OpenGL */
pixel_data = (unsigned char*)malloc( 3*width*height );
glReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, pixel_data);
/* invert the image */
for( j = 0; j*2 < height; ++j )
{
int index1 = j * width * 3;
int index2 = (height - 1 - j) * width * 3;
for( i = width * 3; i > 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_TO_EDGE;
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 )
{
#ifdef USE_GLES1
/*
* GLES1 has no S3-style texture compression extension.
* GLES2 has GL_EXT_texture_compression_dxt1 available which only supports,
* obviously enough, DXT1 compression.
*/
has_DXT_capability = SOIL_CAPABILITY_NONE;
#else
/* 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;
}
}
}
#endif
/* let the user know if we can do DXT or not */
return has_DXT_capability;
}