| /* |
| 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; |
| } |