/* Fork by Martin Lucas Golini Original author Jonathan Dummer 2007-07-26-10.36 Simple OpenGL Image Library 2 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 #if defined( __APPLE_CC__ ) || defined ( __APPLE__ ) #include #if defined( __IPHONE__ ) || ( defined( TARGET_OS_IPHONE ) && TARGET_OS_IPHONE ) || ( defined( TARGET_IPHONE_SIMULATOR ) && TARGET_IPHONE_SIMULATOR ) #define SOIL_PLATFORM_IOS #include #else #define SOIL_PLATFORM_OSX #endif #elif defined( __ANDROID__ ) || defined( ANDROID ) #define SOIL_PLATFORM_ANDROID #elif ( defined ( linux ) || defined( __linux__ ) || defined( __FreeBSD__ ) || defined(__OpenBSD__) || defined( __NetBSD__ ) || defined( __DragonFly__ ) || defined( __SVR4 ) ) #define SOIL_X11_PLATFORM #endif #if ( defined( SOIL_PLATFORM_IOS ) || defined( SOIL_PLATFORM_ANDROID ) ) && ( !defined( SOIL_GLES1 ) && !defined( SOIL_GLES2 ) ) #define SOIL_GLES2 #endif #if ( defined( SOIL_GLES2 ) || defined( SOIL_GLES1 ) ) && !defined( SOIL_NO_EGL ) && !defined( SOIL_PLATFORM_IOS ) #include #endif #if defined( SOIL_GLES3 ) #include #define APIENTRY GL_APIENTRY #endif #if defined( SOIL_GLES2 ) #ifdef SOIL_PLATFORM_IOS #include #include #else #include #include #endif #define APIENTRY GL_APIENTRY #elif defined( SOIL_GLES1 ) #ifndef GL_GLEXT_PROTOTYPES #define GL_GLEXT_PROTOTYPES #endif #ifdef SOIL_PLATFORM_IOS #include #include #else #include #include #endif #define APIENTRY GL_APIENTRY #else #if defined( __WIN32__ ) || defined( _WIN32 ) || defined( WIN32 ) #define SOIL_PLATFORM_WIN32 #include #include #include #ifndef GL_UNSIGNED_SHORT_4_4_4_4 #define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 #endif #ifndef GL_UNSIGNED_SHORT_5_5_5_1 #define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 #endif #ifndef GL_UNSIGNED_SHORT_5_6_5 #define GL_UNSIGNED_SHORT_5_6_5 0x8363 #endif #elif defined(__APPLE__) || defined(__APPLE_CC__) /* I can't test this Apple stuff! */ #include #include #define APIENTRY #elif defined( SOIL_X11_PLATFORM ) #include #include #else #include #endif #endif #ifndef GL_BGRA #define GL_BGRA 0x80E1 #endif #ifndef GL_RG #define GL_RG 0x8227 #endif #include "SOIL2.h" #define STB_IMAGE_IMPLEMENTATION #include "stb_image.h" #define STB_IMAGE_WRITE_IMPLEMENTATION #include "stb_image_write.h" #include "image_helper.h" #include "image_DXT.h" #include "pvr_helper.h" #include "pkm_helper.h" #include #include /* error reporting */ const 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 */ #define SOIL_IS_POW2( v ) ( ( v & ( v - 1 ) ) == 0 ) 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); static P_SOIL_GLCOMPRESSEDTEXIMAGE2DPROC soilGlCompressedTexImage2D = NULL; typedef void (APIENTRY *P_SOIL_GLGENERATEMIPMAPPROC)(GLenum target); static P_SOIL_GLGENERATEMIPMAPPROC soilGlGenerateMipmap = NULL; static int has_gen_mipmap_capability = SOIL_CAPABILITY_UNKNOWN; static int query_gen_mipmap_capability( void ); static int has_PVR_capability = SOIL_CAPABILITY_UNKNOWN; int query_PVR_capability( void ); static int has_BGRA8888_capability = SOIL_CAPABILITY_UNKNOWN; int query_BGRA8888_capability( void ); static int has_ETC1_capability = SOIL_CAPABILITY_UNKNOWN; int query_ETC1_capability( void ); /* GL_IMG_texture_compression_pvrtc */ #define SOIL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG 0x8C00 #define SOIL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG 0x8C01 #define SOIL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG 0x8C02 #define SOIL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG 0x8C03 #define SOIL_GL_ETC1_RGB8_OES 0x8D64 #if defined( SOIL_X11_PLATFORM ) || defined( SOIL_PLATFORM_WIN32 ) || defined( SOIL_PLATFORM_OSX ) typedef const GLubyte *(APIENTRY * P_SOIL_glGetStringiFunc) (GLenum, GLuint); static P_SOIL_glGetStringiFunc soilGlGetStringiFunc = NULL; static int isAtLeastGL3() { static int is_gl3 = SOIL_CAPABILITY_UNKNOWN; if ( SOIL_CAPABILITY_UNKNOWN == is_gl3 ) { const char * verstr = (const char *) glGetString( GL_VERSION ); is_gl3 = ( verstr && ( atoi(verstr) >= 3 ) ); } return is_gl3; } #endif #ifdef SOIL_PLATFORM_WIN32 static int soilTestWinProcPointer(const PROC pTest) { ptrdiff_t iTest; if(!pTest) return 0; iTest = (ptrdiff_t)pTest; if(iTest == 1 || iTest == 2 || iTest == 3 || iTest == -1) return 0; return 1; } #endif void * SOIL_GL_GetProcAddress(const char *proc) { void *func = NULL; #if defined( SOIL_PLATFORM_IOS ) func = dlsym( RTLD_DEFAULT, proc ); #elif defined( SOIL_GLES2 ) || defined( SOIL_GLES1 ) || defined( SOIL_GLES3 ) #ifndef SOIL_NO_EGL func = eglGetProcAddress( proc ); #else func = NULL; #endif #elif defined( SOIL_PLATFORM_WIN32 ) func = wglGetProcAddress( proc ); if (!soilTestWinProcPointer((const PROC)func)) func = NULL; #elif defined( SOIL_PLATFORM_OSX ) /* 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, proc, kCFStringEncodingASCII ); bundle = CFBundleCreate( kCFAllocatorDefault, bundleURL ); assert( bundle != NULL ); func = CFBundleGetFunctionPointerForName( bundle, extensionName ); CFRelease( bundleURL ); CFRelease( extensionName ); CFRelease( bundle ); #elif defined( SOIL_X11_PLATFORM ) func = #if !defined(GLX_VERSION_1_4) glXGetProcAddressARB #else glXGetProcAddress #endif ( (const GLubyte *)proc ); #endif return func; } /* Based on the SDL2 implementation */ int SOIL_GL_ExtensionSupported(const char *extension) { const char *extensions; const char *start; const char *where, *terminator; /* Extension names should not have spaces. */ where = strchr(extension, ' '); if (where || *extension == '\0') { return 0; } #if defined( SOIL_X11_PLATFORM ) || defined( SOIL_PLATFORM_WIN32 ) || defined( SOIL_PLATFORM_OSX ) /* Lookup the available extensions */ if ( isAtLeastGL3() ) { GLint num_exts = 0; GLint i; if ( NULL == soilGlGetStringiFunc ) { soilGlGetStringiFunc = (P_SOIL_glGetStringiFunc)SOIL_GL_GetProcAddress("glGetStringi"); if ( NULL == soilGlGetStringiFunc ) { return 0; } } #ifndef GL_NUM_EXTENSIONS #define GL_NUM_EXTENSIONS 0x821D #endif glGetIntegerv(GL_NUM_EXTENSIONS, &num_exts); for (i = 0; i < num_exts; i++) { const char *thisext = (const char *) soilGlGetStringiFunc(GL_EXTENSIONS, i); if (strcmp(thisext, extension) == 0) { return 1; } } return 0; } #endif /* Try the old way with glGetString(GL_EXTENSIONS) ... */ extensions = (const char *) glGetString(GL_EXTENSIONS); if (!extensions) { return 0; } /* * It takes a bit of care to be fool-proof about parsing the OpenGL * extensions string. Don't be fooled by sub-strings, etc. */ start = extensions; for (;;) { where = strstr(start, extension); if (!where) break; terminator = where + strlen(extension); if (where == start || *(where - 1) == ' ') if (*terminator == ' ' || *terminator == '\0') return 1; start = terminator; } return 0; } /* 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, 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; } } if( flags & SOIL_FLAG_PVR_LOAD_DIRECT ) { tex_id = SOIL_direct_load_PVR( filename, reuse_texture_ID, flags, 0 ); if( tex_id ) { /* hey, it worked!! */ return tex_id; } } if( flags & SOIL_FLAG_ETC1_LOAD_DIRECT ) { tex_id = SOIL_direct_load_ETC1( filename, reuse_texture_ID, flags ); 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 = NULL; 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; } /* check if the image is HDR */ if ( stbi_is_hdr( filename ) ) { /* try to load the image (only the HDR type) */ img = stbi_load( 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, unsigned int buffer_length, 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_from_memory( buffer, buffer_length, reuse_texture_ID, flags, 0 ); if( tex_id ) { /* hey, it worked!! */ return tex_id; } } if( flags & SOIL_FLAG_PVR_LOAD_DIRECT ) { tex_id = SOIL_direct_load_PVR_from_memory( buffer, buffer_length, reuse_texture_ID, flags, 0 ); if( tex_id ) { /* hey, it worked!! */ return tex_id; } } if( flags & SOIL_FLAG_ETC1_LOAD_DIRECT ) { tex_id = SOIL_direct_load_ETC1_from_memory( buffer, buffer_length, reuse_texture_ID, flags ); 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; } } if ( flags & SOIL_FLAG_PVR_LOAD_DIRECT ) { tex_id = SOIL_direct_load_PVR( filename, reuse_texture_ID, flags, 1 ); if( tex_id ) { /* hey, it worked!! */ return tex_id; } } if ( flags & SOIL_FLAG_ETC1_LOAD_DIRECT ) { 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; } /* 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; } } if ( flags & SOIL_FLAG_PVR_LOAD_DIRECT ) { tex_id = SOIL_direct_load_PVR_from_memory( buffer, buffer_length, reuse_texture_ID, flags, 1 ); if ( tex_id ) { /* hey, it worked!! */ return tex_id; } } if ( flags & SOIL_FLAG_ETC1_LOAD_DIRECT ) { 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; } /* 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 static void createMipmaps(const unsigned char *const img, int width, int height, int channels, unsigned int flags, unsigned int opengl_texture_target, unsigned int internal_texture_format, unsigned int original_texture_format, int DXT_mode) { if ( ( flags & SOIL_FLAG_GL_MIPMAPS ) && query_gen_mipmap_capability() == SOIL_CAPABILITY_PRESENT ) { soilGlGenerateMipmap(opengl_texture_target); } else { int MIPlevel = 1; int MIPwidth = (width+1) / 2; int MIPheight = (height+1) / 2; unsigned char *resampled = (unsigned char*)malloc( channels*MIPwidth*MIPheight ); while( ((1< 0; --i ) { unsigned char temp = 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, iwidth, iheight, 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*iwidth*iheight; i += 2 ) { img[i] = (img[i] * img[i+1] + 128) >> 8; } break; case 4: for( i = 0; i < 4*iwidth*iheight; 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; } } /* do I need to make it a power of 2? */ if( ( ( flags & SOIL_FLAG_POWER_OF_TWO) && ( !SOIL_IS_POW2(iwidth) || !SOIL_IS_POW2(iheight) ) ) || /* user asked for it and the texture is not power of 2 */ ( (flags & SOIL_FLAG_MIPMAPS)&& !( ( flags & SOIL_FLAG_GL_MIPMAPS ) && query_gen_mipmap_capability() == SOIL_CAPABILITY_PRESENT && query_NPOT_capability() == SOIL_CAPABILITY_PRESENT ) ) || /* need it for the MIP-maps when mipmaps required and not GL mipmaps required and supported */ (iwidth > max_supported_size) || /* it's too big, (make sure it's */ (iheight > max_supported_size) ) /* 2^n for later down-sampling) */ { int new_width = 1; int new_height = 1; while( new_width < iwidth ) { new_width *= 2; } while( new_height < iheight ) { new_height *= 2; } /* still? */ if( (new_width != iwidth) || (new_height != iheight) ) { /* yep, resize */ unsigned char *resampled = (unsigned char*)malloc( channels*new_width*new_height ); up_scale_image( NULL != img ? img : data, iwidth, iheight, channels, resampled, new_width, new_height ); /* nuke the old guy ( if a copy exists ), then point it at the new guy */ SOIL_free_image_data( img ); img = resampled; *width = new_width; *height = new_height; iwidth = new_width; iheight = new_height; } } /* now, if it is too large... */ if( (iwidth > max_supported_size) || (iheight > 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( iwidth > max_supported_size ) { reduce_block_x = iwidth / max_supported_size; } if( iheight > max_supported_size ) { reduce_block_y = iheight / max_supported_size; } new_width = iwidth / reduce_block_x; new_height = iheight / reduce_block_y; resampled = (unsigned char*)malloc( channels*new_width*new_height ); /* perform the actual reduction */ mipmap_image( NULL != img ? img : data, iwidth, iheight, 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; iwidth = new_width; iheight = 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, iwidth, iheight, channels ); } /* 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" ); /* set the unpack aligment */ glGetIntegerv(GL_UNPACK_ALIGNMENT, &unpack_aligment); if ( 1 != unpack_aligment ) { glPixelStorei(GL_UNPACK_ALIGNMENT,1); } /* 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( NULL != img ? img : data, iwidth, iheight, channels, &DDS_size ); } else { /* RGBA, use DXT5 */ DDS_data = convert_image_to_DXT5( NULL != img ? img : data, iwidth, iheight, channels, &DDS_size ); } if( DDS_data ) { soilGlCompressedTexImage2D( opengl_texture_target, 0, internal_texture_format, iwidth, iheight, 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, iwidth, iheight, 0, original_texture_format, GL_UNSIGNED_BYTE, NULL != img ? img : data ); 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, iwidth, iheight, 0, original_texture_format, GL_UNSIGNED_BYTE, NULL != img ? img : data ); check_for_GL_errors( "glTexImage2D" ); /*printf( "OpenGL DXT compressor\n" ); */ } /* are any MIPmaps desired? */ if( flags & SOIL_FLAG_MIPMAPS || flags & SOIL_FLAG_GL_MIPMAPS ) { createMipmaps( NULL != img ? img : data, iwidth, iheight, channels, flags, opengl_texture_target, internal_texture_format, original_texture_format, DXT_mode ); /* 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" ); } /* recover the unpack aligment */ if ( 1 != unpack_aligment ) { glPixelStorei(GL_UNPACK_ALIGNMENT, unpack_aligment); } /* 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; */ 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 if( image_type == SOIL_SAVE_TYPE_PNG ) { save_result = stbi_write_png( filename, width, height, channels, (const unsigned char *const)data, 0 ); } 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 ) { if ( 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, unsigned 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 < (int)DDS_full_size; i += block_size ) { unsigned char temp = DDS_data[i]; DDS_data[i] = DDS_data[i+2]; DDS_data[i+2] = temp; } glTexImage2D( cf_target, 0, S3TC_type, width, height, 0, S3TC_type, GL_UNSIGNED_BYTE, DDS_data ); } else { soilGlCompressedTexImage2D( cf_target, 0, S3TC_type, width, height, 0, DDS_main_size, DDS_data ); } /* upload the mipmaps, if we have them */ for( i = 1; i <= mipmaps; ++i ) { int w, h, mip_size; w = width >> i; h = height >> i; if( w < 1 ) { w = 1; } if( h < 1 ) { h = 1; } /* upload this mipmap */ if( uncompressed ) { mip_size = w*h*block_size; glTexImage2D( cf_target, i, S3TC_type, w, h, 0, S3TC_type, GL_UNSIGNED_BYTE, &DDS_data[byte_offset] ); } else { mip_size = ((w+3)/4)*((h+3)/4)*block_size; soilGlCompressedTexImage2D( cf_target, i, S3TC_type, w, h, 0, mip_size, &DDS_data[byte_offset] ); } /* and move to the next mipmap */ byte_offset += mip_size; } /* it worked! */ result_string_pointer = "DDS file loaded"; } else { glDeleteTextures( 1, & tex_ID ); tex_ID = 0; cf_target = ogl_target_end + 1; result_string_pointer = "DDS file was too small for expected image data"; } }/* end reading each face */ SOIL_free_image_data( DDS_data ); if( tex_ID ) { /* did I have MIPmaps? */ if( mipmaps > 0 ) { /* instruct OpenGL to use the MIPmaps */ glTexParameteri( opengl_texture_type, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); glTexParameteri( opengl_texture_type, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR ); } else { /* instruct OpenGL _NOT_ to use the MIPmaps */ glTexParameteri( opengl_texture_type, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); glTexParameteri( opengl_texture_type, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); } /* does the user want clamping, or wrapping? */ if( flags & SOIL_FLAG_TEXTURE_REPEATS ) { glTexParameteri( opengl_texture_type, GL_TEXTURE_WRAP_S, GL_REPEAT ); glTexParameteri( opengl_texture_type, GL_TEXTURE_WRAP_T, GL_REPEAT ); glTexParameteri( opengl_texture_type, SOIL_TEXTURE_WRAP_R, GL_REPEAT ); } else { unsigned int clamp_mode = SOIL_CLAMP_TO_EDGE; /* unsigned int clamp_mode = GL_CLAMP; */ glTexParameteri( opengl_texture_type, GL_TEXTURE_WRAP_S, clamp_mode ); glTexParameteri( opengl_texture_type, GL_TEXTURE_WRAP_T, clamp_mode ); glTexParameteri( opengl_texture_type, SOIL_TEXTURE_WRAP_R, clamp_mode ); } } quick_exit: /* report success or failure */ return tex_ID; } unsigned int SOIL_direct_load_DDS( const char *filename, unsigned int reuse_texture_ID, int flags, int loading_as_cubemap ) { FILE *f; unsigned char *buffer; size_t buffer_length, bytes_read; unsigned int tex_ID = 0; /* error checks */ if( NULL == filename ) { result_string_pointer = "NULL filename"; return 0; } f = fopen( filename, "rb" ); if( NULL == f ) { /* the file doesn't seem to exist (or be open-able) */ result_string_pointer = "Can not find DDS file"; return 0; } fseek( f, 0, SEEK_END ); buffer_length = ftell( f ); fseek( f, 0, SEEK_SET ); buffer = (unsigned char *) malloc( buffer_length ); if( NULL == buffer ) { result_string_pointer = "malloc failed"; fclose( f ); return 0; } bytes_read = fread( (void*)buffer, 1, buffer_length, f ); fclose( f ); if( bytes_read < buffer_length ) { /* huh? */ buffer_length = bytes_read; } /* now try to do the loading */ tex_ID = SOIL_direct_load_DDS_from_memory( (const unsigned char *const)buffer, (int)buffer_length, reuse_texture_ID, flags, loading_as_cubemap ); SOIL_free_image_data( buffer ); return tex_ID; } unsigned int SOIL_direct_load_PVR_from_memory( const unsigned char *const buffer, int buffer_length, unsigned int reuse_texture_ID, int flags, int loading_as_cubemap ) { PVR_Texture_Header* header = (PVR_Texture_Header*)buffer; int num_surfs = 1; GLuint tex_ID = 0; GLenum PVR_format = 0; GLenum PVR_type = GL_RGB; unsigned int opengl_texture_type = loading_as_cubemap ? SOIL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D; int is_PVRTC_supported = query_PVR_capability() == SOIL_CAPABILITY_PRESENT; int is_BGRA8888_supported = query_BGRA8888_capability() == SOIL_CAPABILITY_PRESENT; int is_compressed_format_supported = 0; int is_compressed_format = 0; int mipmaps = 0; int i; GLint unpack_aligment; // Check the header size if ( header->dwHeaderSize != sizeof(PVR_Texture_Header) ) { if ( header->dwHeaderSize == PVRTEX_V1_HEADER_SIZE ) { result_string_pointer = "this is an old pvr ( update the PVR file )"; if ( loading_as_cubemap ) { if( header->dwpfFlags & PVRTEX_CUBEMAP ) { num_surfs = 6; } else { result_string_pointer = "tried to load a non-cubemap PVR as cubemap"; return 0; } } } else { result_string_pointer = "invalid PVR header"; return 0; } } else { if ( loading_as_cubemap ) { // Header V2 if( header->dwNumSurfs < 1 ) { if( header->dwpfFlags & PVRTEX_CUBEMAP ) { num_surfs = 6; } else { result_string_pointer = "tried to load a non-cubemap PVR as cubemap"; return 0; } } else { num_surfs = header->dwNumSurfs; } } } // Check the magic identifier if ( header->dwPVR != PVRTEX_IDENTIFIER ) { result_string_pointer = "invalid PVR header"; return 0; } /* Only accept untwiddled data UNLESS texture format is PVRTC */ if ( ((header->dwpfFlags & PVRTEX_TWIDDLE) == PVRTEX_TWIDDLE) && ((header->dwpfFlags & PVRTEX_PIXELTYPE)!=OGL_PVRTC2) && ((header->dwpfFlags & PVRTEX_PIXELTYPE)!=OGL_PVRTC4) ) { // We need to load untwiddled textures -- hw will twiddle for us. result_string_pointer = "pvr is not compressed ( untwiddled texture )"; return 0; } switch( header->dwpfFlags & PVRTEX_PIXELTYPE ) { case OGL_RGBA_4444: PVR_format = GL_UNSIGNED_SHORT_4_4_4_4; PVR_type = GL_RGBA; break; case OGL_RGBA_5551: PVR_format = GL_UNSIGNED_SHORT_5_5_5_1; PVR_type = GL_RGBA; break; case OGL_RGBA_8888: PVR_format = GL_UNSIGNED_BYTE; PVR_type = GL_RGBA; break; case OGL_RGB_565: PVR_format = GL_UNSIGNED_SHORT_5_6_5; PVR_type = GL_RGB; break; case OGL_RGB_555: result_string_pointer = "failed: pixel type OGL_RGB_555 not supported."; return 0; case OGL_RGB_888: PVR_format = GL_UNSIGNED_BYTE; PVR_type = GL_RGB; break; case OGL_I_8: PVR_format = GL_UNSIGNED_BYTE; PVR_type = GL_LUMINANCE; break; case OGL_AI_88: PVR_format = GL_UNSIGNED_BYTE; PVR_type = GL_LUMINANCE_ALPHA; break; case MGLPT_PVRTC2: case OGL_PVRTC2: if(is_PVRTC_supported) { is_compressed_format_supported = is_compressed_format = 1; PVR_format = header->dwAlphaBitMask==0 ? SOIL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG : SOIL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG ; // PVRTC2 } else { result_string_pointer = "error: PVRTC2 not supported.Decompress the texture first."; return 0; } break; case MGLPT_PVRTC4: case OGL_PVRTC4: if(is_PVRTC_supported) { is_compressed_format_supported = is_compressed_format = 1; PVR_format = header->dwAlphaBitMask==0 ? SOIL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG : SOIL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG ; // PVRTC4 } else { result_string_pointer = "error: PVRTC4 not supported. Decompress the texture first."; return 0; } break; case OGL_BGRA_8888: if(is_BGRA8888_supported) { PVR_format = GL_UNSIGNED_BYTE; PVR_type = GL_BGRA; break; } else { result_string_pointer = "Unable to load GL_BGRA texture as extension GL_IMG_texture_format_BGRA8888 is unsupported."; return 0; } default: // NOT SUPPORTED result_string_pointer = "failed: pixel type not supported."; return 0; } #ifdef SOIL_GLES1 // check that this data is cube map data or not. if( loading_as_cubemap ) { result_string_pointer = "cube map textures are not available in GLES1.x."; return 0; } #endif // load the texture up tex_ID = reuse_texture_ID; if( tex_ID == 0 ) { glGenTextures( 1, &tex_ID ); } glBindTexture( opengl_texture_type, tex_ID ); if( glGetError() ) { result_string_pointer = "failed: glBindTexture() failed."; return 0; } glGetIntegerv(GL_UNPACK_ALIGNMENT, &unpack_aligment); if ( 1 != unpack_aligment ) { glPixelStorei(GL_UNPACK_ALIGNMENT,1); // Never have row-aligned in headers } #define _MAX( a, b ) (( a <= b )? b : a) for(i=0; idwHeaderSize + header->dwTextureDataSize * i; char *cur_texture_ptr = 0; int mipmap_level; unsigned int width= header->dwWidth, height = header->dwHeight; unsigned int compressed_image_size = 0; mipmaps = ( ( flags & SOIL_FLAG_MIPMAPS ) && (header->dwpfFlags & PVRTEX_MIPMAP) ) ? header->dwMipMapCount : 0; for(mipmap_level = 0; mipmap_level <= mipmaps; width = _MAX(width/2, (unsigned int)1), height = _MAX(height/2, (unsigned int)1), mipmap_level++ ) { // Do Alpha-swap if needed cur_texture_ptr = texture_ptr; // Load the Texture /* If the texture is PVRTC then use GLCompressedTexImage2D */ if( is_compressed_format ) { /* Calculate how many bytes this MIP level occupies */ if ((header->dwpfFlags & PVRTEX_PIXELTYPE)==OGL_PVRTC2) { compressed_image_size = ( _MAX(width, PVRTC2_MIN_TEXWIDTH) * _MAX(height, PVRTC2_MIN_TEXHEIGHT) * header->dwBitCount + 7 ) / 8; } else {// PVRTC4 case compressed_image_size = ( _MAX(width, PVRTC4_MIN_TEXWIDTH) * _MAX(height, PVRTC4_MIN_TEXHEIGHT) * header->dwBitCount + 7 ) / 8; } if ( is_compressed_format_supported ) { /* Load compressed texture data at selected MIP level */ if ( loading_as_cubemap ) { soilGlCompressedTexImage2D( SOIL_TEXTURE_CUBE_MAP_POSITIVE_X + i, mipmap_level, PVR_format, width, height, 0, compressed_image_size, cur_texture_ptr ); } else { soilGlCompressedTexImage2D( opengl_texture_type, mipmap_level, PVR_format, width, height, 0, compressed_image_size, cur_texture_ptr ); } } else { result_string_pointer = "failed: GPU doesnt support compressed textures"; } } else { /* Load uncompressed texture data at selected MIP level */ if ( loading_as_cubemap ) { glTexImage2D( SOIL_TEXTURE_CUBE_MAP_POSITIVE_X + i, mipmap_level, PVR_type, width, height, 0, PVR_type, PVR_format, cur_texture_ptr ); } else { glTexImage2D( opengl_texture_type, mipmap_level, PVR_type, width, height, 0, PVR_type, PVR_format, cur_texture_ptr ); } } if( glGetError() ) { result_string_pointer = "failed: glCompressedTexImage2D() failed."; if ( 1 != unpack_aligment ) { glPixelStorei(GL_UNPACK_ALIGNMENT, unpack_aligment); } return 0; } // offset the texture pointer by one mip-map level /* PVRTC case */ if ( is_compressed_format ) { texture_ptr += compressed_image_size; } else { /* New formula that takes into account bit counts inferior to 8 (e.g. 1 bpp) */ texture_ptr += (width * height * header->dwBitCount + 7) / 8; } } } #undef _MAX if ( 1 != unpack_aligment ) { glPixelStorei(GL_UNPACK_ALIGNMENT, unpack_aligment); } if( tex_ID ) { /* did I have MIPmaps? */ if( mipmaps ) { /* instruct OpenGL to use the MIPmaps */ glTexParameteri( opengl_texture_type, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); glTexParameteri( opengl_texture_type, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR ); } else { /* instruct OpenGL _NOT_ to use the MIPmaps */ glTexParameteri( opengl_texture_type, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); glTexParameteri( opengl_texture_type, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); } /* does the user want clamping, or wrapping? */ if( flags & SOIL_FLAG_TEXTURE_REPEATS ) { glTexParameteri( opengl_texture_type, GL_TEXTURE_WRAP_S, GL_REPEAT ); glTexParameteri( opengl_texture_type, GL_TEXTURE_WRAP_T, GL_REPEAT ); glTexParameteri( opengl_texture_type, SOIL_TEXTURE_WRAP_R, GL_REPEAT ); } else { unsigned int clamp_mode = SOIL_CLAMP_TO_EDGE; /* unsigned int clamp_mode = GL_CLAMP; */ glTexParameteri( opengl_texture_type, GL_TEXTURE_WRAP_S, clamp_mode ); glTexParameteri( opengl_texture_type, GL_TEXTURE_WRAP_T, clamp_mode ); glTexParameteri( opengl_texture_type, SOIL_TEXTURE_WRAP_R, clamp_mode ); } } return tex_ID; } unsigned int SOIL_direct_load_PVR( 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 PVR 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_PVR_from_memory( (const unsigned char *const)buffer, (int)buffer_length, reuse_texture_ID, flags, loading_as_cubemap ); SOIL_free_image_data( buffer ); return tex_ID; } unsigned int SOIL_direct_load_ETC1_from_memory( const unsigned char *const buffer, int buffer_length, unsigned int reuse_texture_ID, int flags ) { GLuint tex_ID = 0; PKMHeader* header = (PKMHeader*)buffer; unsigned int opengl_texture_type = GL_TEXTURE_2D; unsigned int width; unsigned int height; unsigned long compressed_image_size = buffer_length - PKM_HEADER_SIZE; char *texture_ptr = (char*)buffer + PKM_HEADER_SIZE; GLint unpack_aligment; if ( query_ETC1_capability() != SOIL_CAPABILITY_PRESENT ) { result_string_pointer = "error: ETC1 not supported. Decompress the texture first."; return 0; } if ( 0 != strcmp( header->aName, "PKM 10" ) ) { result_string_pointer = "error: PKM 10 header not found."; return 0; } width = (header->iWidthMSB << 8) | header->iWidthLSB; height = (header->iHeightMSB << 8) | header->iHeightLSB; compressed_image_size = (((width + 3) & ~3) * ((height + 3) & ~3)) >> 1; // load the texture up tex_ID = reuse_texture_ID; if( tex_ID == 0 ) { glGenTextures( 1, &tex_ID ); } glBindTexture( opengl_texture_type, tex_ID ); if( glGetError() ) { result_string_pointer = "failed: glBindTexture() failed."; return 0; } glGetIntegerv(GL_UNPACK_ALIGNMENT, &unpack_aligment); if ( 1 != unpack_aligment ) { glPixelStorei(GL_UNPACK_ALIGNMENT,1); // Never have row-aligned in headers } soilGlCompressedTexImage2D( opengl_texture_type, 0, SOIL_GL_ETC1_RGB8_OES, width, height, 0, compressed_image_size, texture_ptr ); if( glGetError() ) { result_string_pointer = "failed: glCompressedTexImage2D() failed."; if ( 1 != unpack_aligment ) { glPixelStorei(GL_UNPACK_ALIGNMENT, unpack_aligment); } return 0; } if ( 1 != unpack_aligment ) { glPixelStorei(GL_UNPACK_ALIGNMENT, unpack_aligment); } if( tex_ID ) { /* No MIPmaps for ETC1 */ glTexParameteri( opengl_texture_type, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); glTexParameteri( opengl_texture_type, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); /* does the user want clamping, or wrapping? */ if( flags & SOIL_FLAG_TEXTURE_REPEATS ) { glTexParameteri( opengl_texture_type, GL_TEXTURE_WRAP_S, GL_REPEAT ); glTexParameteri( opengl_texture_type, GL_TEXTURE_WRAP_T, GL_REPEAT ); glTexParameteri( opengl_texture_type, SOIL_TEXTURE_WRAP_R, GL_REPEAT ); } else { unsigned int clamp_mode = SOIL_CLAMP_TO_EDGE; /* unsigned int clamp_mode = GL_CLAMP; */ glTexParameteri( opengl_texture_type, GL_TEXTURE_WRAP_S, clamp_mode ); glTexParameteri( opengl_texture_type, GL_TEXTURE_WRAP_T, clamp_mode ); glTexParameteri( opengl_texture_type, SOIL_TEXTURE_WRAP_R, clamp_mode ); } } return tex_ID; } unsigned int SOIL_direct_load_ETC1(const char *filename, unsigned int reuse_texture_ID, int flags ) { 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 PVR 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_ETC1_from_memory( (const unsigned char *const)buffer, (int)buffer_length, reuse_texture_ID, flags ); 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( (0 == SOIL_GL_ExtensionSupported( "GL_ARB_texture_non_power_of_two" ) ) && (0 == SOIL_GL_ExtensionSupported( "GL_OES_texture_npot" ) ) ) { /* 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( (0 == SOIL_GL_ExtensionSupported( "GL_ARB_texture_rectangle" ) ) && (0 == SOIL_GL_ExtensionSupported( "GL_EXT_texture_rectangle" ) ) && (0 == SOIL_GL_ExtensionSupported( "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( (0 == SOIL_GL_ExtensionSupported( "GL_ARB_texture_cube_map" ) ) && (0 == SOIL_GL_ExtensionSupported( "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; } static P_SOIL_GLCOMPRESSEDTEXIMAGE2DPROC get_glCompressedTexImage2D_addr() { /* and find the address of the extension function */ P_SOIL_GLCOMPRESSEDTEXIMAGE2DPROC ext_addr = NULL; #if defined( SOIL_PLATFORM_WIN32 ) || defined( SOIL_PLATFORM_OSX ) || defined( SOIL_X11_PLATFORM ) ext_addr = (P_SOIL_GLCOMPRESSEDTEXIMAGE2DPROC)SOIL_GL_GetProcAddress( "glCompressedTexImage2D" ); #else ext_addr = (P_SOIL_GLCOMPRESSEDTEXIMAGE2DPROC)&glCompressedTexImage2D; #endif return ext_addr; } int query_DXT_capability( void ) { /* check for the capability */ if( has_DXT_capability == SOIL_CAPABILITY_UNKNOWN ) { /* we haven't yet checked for the capability, do so */ if ( 0 == SOIL_GL_ExtensionSupported( "GL_EXT_texture_compression_s3tc" ) && 0 == SOIL_GL_ExtensionSupported( "WEBGL_compressed_texture_s3tc ") && 0 == SOIL_GL_ExtensionSupported( "WEBKIT_WEBGL_compressed_texture_s3tc") && 0 == SOIL_GL_ExtensionSupported( "MOZ_WEBGL_compressed_texture_s3tc" ) ) { /* not there, flag the failure */ has_DXT_capability = SOIL_CAPABILITY_NONE; } else { P_SOIL_GLCOMPRESSEDTEXIMAGE2DPROC ext_addr = get_glCompressedTexImage2D_addr(); /* Flag it so no checks needed later */ if( NULL == ext_addr ) { /* hmm, not good!! This should not happen, but does on my laptop's VIA chipset. The GL_EXT_texture_compression_s3tc spec requires that ARB_texture_compression be present too. this means I can upload and have the OpenGL drive do the conversion, but I can't use my own routines or load DDS files from disk and upload them directly [8^( */ has_DXT_capability = SOIL_CAPABILITY_NONE; } else { /* all's well! */ soilGlCompressedTexImage2D = ext_addr; has_DXT_capability = SOIL_CAPABILITY_PRESENT; } } } /* let the user know if we can do DXT or not */ return has_DXT_capability; } int query_PVR_capability( void ) { /* check for the capability */ if( has_PVR_capability == SOIL_CAPABILITY_UNKNOWN ) { /* we haven't yet checked for the capability, do so */ if (0 == SOIL_GL_ExtensionSupported( "GL_IMG_texture_compression_pvrtc" ) ) { /* not there, flag the failure */ has_PVR_capability = SOIL_CAPABILITY_NONE; } else { if ( NULL == soilGlCompressedTexImage2D ) { soilGlCompressedTexImage2D = get_glCompressedTexImage2D_addr(); } /* it's there! */ has_PVR_capability = SOIL_CAPABILITY_PRESENT; } } /* let the user know if we can do cubemaps or not */ return has_PVR_capability; } int query_BGRA8888_capability( void ) { /* check for the capability */ if( has_BGRA8888_capability == SOIL_CAPABILITY_UNKNOWN ) { /* we haven't yet checked for the capability, do so */ if (0 == SOIL_GL_ExtensionSupported( "GL_IMG_texture_format_BGRA8888" ) ) { /* not there, flag the failure */ has_BGRA8888_capability = SOIL_CAPABILITY_NONE; } else { /* it's there! */ has_BGRA8888_capability = SOIL_CAPABILITY_PRESENT; } } /* let the user know if we can do cubemaps or not */ return has_BGRA8888_capability; } int query_ETC1_capability( void ) { /* check for the capability */ if( has_ETC1_capability == SOIL_CAPABILITY_UNKNOWN ) { /* we haven't yet checked for the capability, do so */ if (0 == SOIL_GL_ExtensionSupported( "GL_OES_compressed_ETC1_RGB8_texture" ) ) { /* not there, flag the failure */ has_ETC1_capability = SOIL_CAPABILITY_NONE; } else { if ( NULL == soilGlCompressedTexImage2D ) { soilGlCompressedTexImage2D = get_glCompressedTexImage2D_addr(); } /* it's there! */ has_ETC1_capability = SOIL_CAPABILITY_PRESENT; } } /* let the user know if we can do cubemaps or not */ return has_ETC1_capability; } int query_gen_mipmap_capability( void ) { /* check for the capability */ P_SOIL_GLGENERATEMIPMAPPROC ext_addr = NULL; if( has_gen_mipmap_capability == SOIL_CAPABILITY_UNKNOWN ) { if ( 0 == SOIL_GL_ExtensionSupported( "GL_ARB_framebuffer_object" ) && 0 == SOIL_GL_ExtensionSupported( "GL_EXT_framebuffer_object" ) && 0 == SOIL_GL_ExtensionSupported( "GL_OES_framebuffer_object" ) ) { /* not there, flag the failure */ has_gen_mipmap_capability = SOIL_CAPABILITY_NONE; } else { #if !defined( SOIL_GLES1 ) && !defined( SOIL_GLES2 ) ext_addr = (P_SOIL_GLGENERATEMIPMAPPROC)SOIL_GL_GetProcAddress("glGenerateMipmap"); if(ext_addr == NULL) { ext_addr = (P_SOIL_GLGENERATEMIPMAPPROC)SOIL_GL_GetProcAddress("glGenerateMipmapEXT"); } #elif defined( SOIL_GLES2 ) ext_addr = &glGenerateMipmap; #else /** SOIL_GLES1 */ ext_addr = &glGenerateMipmapOES; #endif } if(ext_addr == NULL) { /* this should never happen */ has_gen_mipmap_capability = SOIL_CAPABILITY_NONE; } else { /* it's there! */ has_gen_mipmap_capability = SOIL_CAPABILITY_PRESENT; soilGlGenerateMipmap = ext_addr; } } return has_gen_mipmap_capability; }