├── CMakeLists.txt ├── inc └── SOIL │ ├── stbi_DDS_aug.h │ ├── image_helper.h │ ├── image_DXT.h │ ├── SOIL.h │ ├── stbi_DDS_aug_c.h │ └── stb_image_aug.h ├── .gitignore ├── LICENSE.txt ├── README.md └── src ├── image_helper.c ├── image_DXT.c └── SOIL.c /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.6) 2 | 3 | include_directories(inc) 4 | 5 | ADD_LIBRARY( soil STATIC 6 | src/image_helper.c 7 | src/stb_image_aug.c 8 | src/image_DXT.c 9 | src/SOIL.c 10 | ) 11 | -------------------------------------------------------------------------------- /inc/SOIL/stbi_DDS_aug.h: -------------------------------------------------------------------------------- 1 | /* 2 | adding DDS loading support to stbi 3 | */ 4 | 5 | #ifndef HEADER_STB_IMAGE_DDS_AUGMENTATION 6 | #define HEADER_STB_IMAGE_DDS_AUGMENTATION 7 | 8 | // is it a DDS file? 9 | extern int stbi_dds_test_memory (stbi_uc const *buffer, int len); 10 | 11 | extern stbi_uc *stbi_dds_load (char *filename, int *x, int *y, int *comp, int req_comp); 12 | extern stbi_uc *stbi_dds_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); 13 | #ifndef STBI_NO_STDIO 14 | extern int stbi_dds_test_file (FILE *f); 15 | extern stbi_uc *stbi_dds_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); 16 | #endif 17 | 18 | // 19 | // 20 | //// end header file ///////////////////////////////////////////////////// 21 | #endif // HEADER_STB_IMAGE_DDS_AUGMENTATION 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by http://www.gitignore.io 2 | 3 | ### Linux ### 4 | *~ 5 | 6 | 7 | ### Windows ### 8 | # Windows image file caches 9 | Thumbs.db 10 | ehthumbs.db 11 | 12 | # Folder config file 13 | Desktop.ini 14 | 15 | # Recycle Bin used on file shares 16 | $RECYCLE.BIN/ 17 | 18 | # Windows Installer files 19 | *.cab 20 | *.msi 21 | *.msm 22 | *.msp 23 | 24 | 25 | ### C++ ### 26 | # Compiled Object files 27 | *.slo 28 | *.lo 29 | *.o 30 | *.obj 31 | 32 | # Compiled Dynamic libraries 33 | *.so 34 | *.dylib 35 | *.dll 36 | 37 | # Compiled Static libraries 38 | *.lai 39 | *.la 40 | *.a 41 | *.lib 42 | 43 | # Executables 44 | *.exe 45 | *.out 46 | *.app 47 | 48 | 49 | ### vim ### 50 | [._]*.s[a-w][a-z] 51 | [._]s[a-w][a-z] 52 | *.un~ 53 | Session.vim 54 | .netrwhist 55 | *~ 56 | 57 | 58 | ### CMake ### 59 | CMakeCache.txt 60 | CMakeFiles 61 | Makefile 62 | cmake_install.cmake 63 | install_manifest.txt 64 | 65 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any person obtaining 2 | a copy of this software and associated documentation files (the 3 | "Software"), to deal in the Software without restriction, including 4 | without limitation the rights to use, copy, modify, merge, publish, 5 | distribute, sublicense, and/or sell copies of the Software, and to 6 | permit persons to whom the Software is furnished to do so, subject to 7 | the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be 10 | included in all copies or substantial portions of the Software. 11 | 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 13 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 14 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 15 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 16 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 17 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 18 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /inc/SOIL/image_helper.h: -------------------------------------------------------------------------------- 1 | /* 2 | Jonathan Dummer 3 | 4 | Image helper functions 5 | 6 | MIT license 7 | */ 8 | 9 | #ifndef HEADER_IMAGE_HELPER 10 | #define HEADER_IMAGE_HELPER 11 | 12 | #ifdef __cplusplus 13 | extern "C" { 14 | #endif 15 | 16 | /** 17 | This function upscales an image. 18 | Not to be used to create MIPmaps, 19 | but to make it square, 20 | or to make it a power-of-two sized. 21 | **/ 22 | int 23 | up_scale_image 24 | ( 25 | const unsigned char* const orig, 26 | int width, int height, int channels, 27 | unsigned char* resampled, 28 | int resampled_width, int resampled_height 29 | ); 30 | 31 | /** 32 | This function downscales an image. 33 | Used for creating MIPmaps, 34 | the incoming image should be a 35 | power-of-two sized. 36 | **/ 37 | int 38 | mipmap_image 39 | ( 40 | const unsigned char* const orig, 41 | int width, int height, int channels, 42 | unsigned char* resampled, 43 | int block_size_x, int block_size_y 44 | ); 45 | 46 | /** 47 | This function takes the RGB components of the image 48 | and scales each channel from [0,255] to [16,235]. 49 | This makes the colors "Safe" for display on NTSC 50 | displays. Note that this is _NOT_ a good idea for 51 | loading images like normal- or height-maps! 52 | **/ 53 | int 54 | scale_image_RGB_to_NTSC_safe 55 | ( 56 | unsigned char* orig, 57 | int width, int height, int channels 58 | ); 59 | 60 | /** 61 | This function takes the RGB components of the image 62 | and converts them into YCoCg. 3 components will be 63 | re-ordered to CoYCg (for optimum DXT1 compression), 64 | while 4 components will be ordered CoCgAY (for DXT5 65 | compression). 66 | **/ 67 | int 68 | convert_RGB_to_YCoCg 69 | ( 70 | unsigned char* orig, 71 | int width, int height, int channels 72 | ); 73 | 74 | /** 75 | This function takes the YCoCg components of the image 76 | and converts them into RGB. See above. 77 | **/ 78 | int 79 | convert_YCoCg_to_RGB 80 | ( 81 | unsigned char* orig, 82 | int width, int height, int channels 83 | ); 84 | 85 | /** 86 | Converts an HDR image from an array 87 | of unsigned chars (RGBE) to RGBdivA 88 | \return 0 if failed, otherwise returns 1 89 | **/ 90 | int 91 | RGBE_to_RGBdivA 92 | ( 93 | unsigned char *image, 94 | int width, int height, 95 | int rescale_to_max 96 | ); 97 | 98 | /** 99 | Converts an HDR image from an array 100 | of unsigned chars (RGBE) to RGBdivA2 101 | \return 0 if failed, otherwise returns 1 102 | **/ 103 | int 104 | RGBE_to_RGBdivA2 105 | ( 106 | unsigned char *image, 107 | int width, int height, 108 | int rescale_to_max 109 | ); 110 | 111 | #ifdef __cplusplus 112 | } 113 | #endif 114 | 115 | #endif /* HEADER_IMAGE_HELPER */ 116 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **Simple OpenGL Image Library** 2 | 3 | 4 | **Introduction:** 5 | 6 | SOIL is a tiny C library used primarily for uploading textures into OpenGL. It is based on stb_image version 1.16, the public domain code from Sean Barrett (found here). It has been extended to load TGA and DDS files, and to perform common functions needed in loading OpenGL textures. SOIL can also be used to save and load images in a variety of formats. 7 | 8 | **Features:** 9 | 10 | * Readable Image Formats: 11 | * BMP - non-1bpp, non-RLE (from stb_image documentation) 12 | * PNG - non-interlaced (from stb_image documentation) 13 | * JPG - JPEG baseline (from stb_image documentation) 14 | * TGA - greyscale or RGB or RGBA or indexed, uncompressed or RLE 15 | * DDS - DXT1/2/3/4/5, uncompressed, cubemaps (can't read 3D DDS files yet) 16 | * PSD - (from stb_image documentation) 17 | * HDR - converted to LDR, unless loaded with \*HDR\* functions (RGBE or RGBdivA or RGBdivA2) 18 | 19 | Writeable Image Formats: 20 | * TGA - Greyscale or RGB or RGBA, uncompressed 21 | * BMP - RGB, uncompressed 22 | * DDS - RGB as DXT1, or RGBA as DXT5 23 | * Can load an image file directly into a 2D OpenGL texture, optionally performing the following functions: 24 | * Can generate a new texture handle, or reuse one specified 25 | * Can automatically rescale the image to the next largest power-of-two size 26 | * Can automatically create MIPmaps 27 | * Can scale (not simply clamp) the RGB values into the "safe range" for NTSC displays (16 to 235, as recommended [here][1]) 28 | * Can multiply alpha on load (for more correct blending / compositing) 29 | * Can flip the image vertically 30 | * Can compress and upload any image as DXT1 or DXT5 (if EXT\_texture\_compression_s3tc is available), using an internal (very fast!) compressor 31 | * Can convert the RGB to YCoCg color space (useful with DXT5 compression: see [this link][2] from NVIDIA) 32 | * Will automatically downsize a texture if it is larger than GL\_MAX\_TEXTURE_SIZE 33 | * Can directly upload DDS files (DXT1/3/5/uncompressed/cubemap, with or without MIPmaps). Note: directly uploading the compressed DDS image will disable the other options (no flipping, no pre-multiplying alpha, no rescaling, no creation of MIPmaps, no auto-downsizing) 34 | * Can load rectangluar textures for GUI elements or splash screens (requires GL\_ARB/EXT/NV\_texture_rectangle) 35 | * Can decompress images from RAM (e.g. via [PhysicsFS][3] or similar) into an OpenGL texture (same features as regular 2D textures, above) 36 | * Can load cube maps directly into an OpenGL texture (same features as regular 2D textures, above) 37 | * Can take six image files directly into an OpenGL cube map texture 38 | * Can take a single image file where width = 6*height (or vice versa), split it into an OpenGL cube map texture 39 | * No external dependencies 40 | * Tiny 41 | * Cross platform (Windows, *nix, Mac OS X) 42 | * Public Domain 43 | 44 | **Usage:** 45 | 46 | SOIL is meant to be used as a static library (as it's tiny and in the public domain). 47 | 48 | Simply include SOIL.h in your C or C++ file, link in the static library, and then use any of SOIL's functions. The file SOIL.h contains simple doxygen style documentation. (If you use the static library, no other header files are needed besides SOIL.h) 49 | 50 | You can compile it like any other CMAKE project, simply make a folder named "build" and run 51 | ``` 52 | cmake .. 53 | make 54 | 55 | ``` 56 | 57 | [1]: http://msdn2.microsoft.com/en-us/library/bb174608.aspx#NTSC_Suggestions 58 | [2]: http://developer.nvidia.com/object/real-time-ycocg-dxt-compression.html 59 | [3]: http://icculus.org/physfs/ 60 | -------------------------------------------------------------------------------- /inc/SOIL/image_DXT.h: -------------------------------------------------------------------------------- 1 | /* 2 | Jonathan Dummer 3 | 2007-07-31-10.32 4 | 5 | simple DXT compression / decompression code 6 | 7 | public domain 8 | */ 9 | 10 | #ifndef HEADER_IMAGE_DXT 11 | #define HEADER_IMAGE_DXT 12 | 13 | /** 14 | Converts an image from an array of unsigned chars (RGB or RGBA) to 15 | DXT1 or DXT5, then saves the converted image to disk. 16 | \return 0 if failed, otherwise returns 1 17 | **/ 18 | int 19 | save_image_as_DDS 20 | ( 21 | const char *filename, 22 | int width, int height, int channels, 23 | const unsigned char *const data 24 | ); 25 | 26 | /** 27 | take an image and convert it to DXT1 (no alpha) 28 | **/ 29 | unsigned char* 30 | convert_image_to_DXT1 31 | ( 32 | const unsigned char *const uncompressed, 33 | int width, int height, int channels, 34 | int *out_size 35 | ); 36 | 37 | /** 38 | take an image and convert it to DXT5 (with alpha) 39 | **/ 40 | unsigned char* 41 | convert_image_to_DXT5 42 | ( 43 | const unsigned char *const uncompressed, 44 | int width, int height, int channels, 45 | int *out_size 46 | ); 47 | 48 | /** A bunch of DirectDraw Surface structures and flags **/ 49 | typedef struct 50 | { 51 | unsigned int dwMagic; 52 | unsigned int dwSize; 53 | unsigned int dwFlags; 54 | unsigned int dwHeight; 55 | unsigned int dwWidth; 56 | unsigned int dwPitchOrLinearSize; 57 | unsigned int dwDepth; 58 | unsigned int dwMipMapCount; 59 | unsigned int dwReserved1[ 11 ]; 60 | 61 | /* DDPIXELFORMAT */ 62 | struct 63 | { 64 | unsigned int dwSize; 65 | unsigned int dwFlags; 66 | unsigned int dwFourCC; 67 | unsigned int dwRGBBitCount; 68 | unsigned int dwRBitMask; 69 | unsigned int dwGBitMask; 70 | unsigned int dwBBitMask; 71 | unsigned int dwAlphaBitMask; 72 | } 73 | sPixelFormat; 74 | 75 | /* DDCAPS2 */ 76 | struct 77 | { 78 | unsigned int dwCaps1; 79 | unsigned int dwCaps2; 80 | unsigned int dwDDSX; 81 | unsigned int dwReserved; 82 | } 83 | sCaps; 84 | unsigned int dwReserved2; 85 | } 86 | DDS_header ; 87 | 88 | /* the following constants were copied directly off the MSDN website */ 89 | 90 | /* The dwFlags member of the original DDSURFACEDESC2 structure 91 | can be set to one or more of the following values. */ 92 | #define DDSD_CAPS 0x00000001 93 | #define DDSD_HEIGHT 0x00000002 94 | #define DDSD_WIDTH 0x00000004 95 | #define DDSD_PITCH 0x00000008 96 | #define DDSD_PIXELFORMAT 0x00001000 97 | #define DDSD_MIPMAPCOUNT 0x00020000 98 | #define DDSD_LINEARSIZE 0x00080000 99 | #define DDSD_DEPTH 0x00800000 100 | 101 | /* DirectDraw Pixel Format */ 102 | #define DDPF_ALPHAPIXELS 0x00000001 103 | #define DDPF_FOURCC 0x00000004 104 | #define DDPF_RGB 0x00000040 105 | 106 | /* The dwCaps1 member of the DDSCAPS2 structure can be 107 | set to one or more of the following values. */ 108 | #define DDSCAPS_COMPLEX 0x00000008 109 | #define DDSCAPS_TEXTURE 0x00001000 110 | #define DDSCAPS_MIPMAP 0x00400000 111 | 112 | /* The dwCaps2 member of the DDSCAPS2 structure can be 113 | set to one or more of the following values. */ 114 | #define DDSCAPS2_CUBEMAP 0x00000200 115 | #define DDSCAPS2_CUBEMAP_POSITIVEX 0x00000400 116 | #define DDSCAPS2_CUBEMAP_NEGATIVEX 0x00000800 117 | #define DDSCAPS2_CUBEMAP_POSITIVEY 0x00001000 118 | #define DDSCAPS2_CUBEMAP_NEGATIVEY 0x00002000 119 | #define DDSCAPS2_CUBEMAP_POSITIVEZ 0x00004000 120 | #define DDSCAPS2_CUBEMAP_NEGATIVEZ 0x00008000 121 | #define DDSCAPS2_VOLUME 0x00200000 122 | 123 | #endif /* HEADER_IMAGE_DXT */ 124 | -------------------------------------------------------------------------------- /src/image_helper.c: -------------------------------------------------------------------------------- 1 | /* 2 | Jonathan Dummer 3 | 4 | image helper functions 5 | 6 | MIT license 7 | */ 8 | 9 | #include "SOIL/image_helper.h" 10 | #include 11 | #include 12 | 13 | /* Upscaling the image uses simple bilinear interpolation */ 14 | int 15 | up_scale_image 16 | ( 17 | const unsigned char* const orig, 18 | int width, int height, int channels, 19 | unsigned char* resampled, 20 | int resampled_width, int resampled_height 21 | ) 22 | { 23 | float dx, dy; 24 | int x, y, c; 25 | 26 | /* error(s) check */ 27 | if ( (width < 1) || (height < 1) || 28 | (resampled_width < 2) || (resampled_height < 2) || 29 | (channels < 1) || 30 | (NULL == orig) || (NULL == resampled) ) 31 | { 32 | /* signify badness */ 33 | return 0; 34 | } 35 | /* 36 | for each given pixel in the new map, find the exact location 37 | from the original map which would contribute to this guy 38 | */ 39 | dx = (width - 1.0f) / (resampled_width - 1.0f); 40 | dy = (height - 1.0f) / (resampled_height - 1.0f); 41 | for ( y = 0; y < resampled_height; ++y ) 42 | { 43 | /* find the base y index and fractional offset from that */ 44 | float sampley = y * dy; 45 | int inty = (int)sampley; 46 | /* if( inty < 0 ) { inty = 0; } else */ 47 | if( inty > height - 2 ) { inty = height - 2; } 48 | sampley -= inty; 49 | for ( x = 0; x < resampled_width; ++x ) 50 | { 51 | float samplex = x * dx; 52 | int intx = (int)samplex; 53 | int base_index; 54 | /* find the base x index and fractional offset from that */ 55 | /* if( intx < 0 ) { intx = 0; } else */ 56 | if( intx > width - 2 ) { intx = width - 2; } 57 | samplex -= intx; 58 | /* base index into the original image */ 59 | base_index = (inty * width + intx) * channels; 60 | for ( c = 0; c < channels; ++c ) 61 | { 62 | /* do the sampling */ 63 | float value = 0.5f; 64 | value += orig[base_index] 65 | *(1.0f-samplex)*(1.0f-sampley); 66 | value += orig[base_index+channels] 67 | *(samplex)*(1.0f-sampley); 68 | value += orig[base_index+width*channels] 69 | *(1.0f-samplex)*(sampley); 70 | value += orig[base_index+width*channels+channels] 71 | *(samplex)*(sampley); 72 | /* move to the next channel */ 73 | ++base_index; 74 | /* save the new value */ 75 | resampled[y*resampled_width*channels+x*channels+c] = 76 | (unsigned char)(value); 77 | } 78 | } 79 | } 80 | /* done */ 81 | return 1; 82 | } 83 | 84 | int 85 | mipmap_image 86 | ( 87 | const unsigned char* const orig, 88 | int width, int height, int channels, 89 | unsigned char* resampled, 90 | int block_size_x, int block_size_y 91 | ) 92 | { 93 | int mip_width, mip_height; 94 | int i, j, c; 95 | 96 | /* error check */ 97 | if( (width < 1) || (height < 1) || 98 | (channels < 1) || (orig == NULL) || 99 | (resampled == NULL) || 100 | (block_size_x < 1) || (block_size_y < 1) ) 101 | { 102 | /* nothing to do */ 103 | return 0; 104 | } 105 | mip_width = width / block_size_x; 106 | mip_height = height / block_size_y; 107 | if( mip_width < 1 ) 108 | { 109 | mip_width = 1; 110 | } 111 | if( mip_height < 1 ) 112 | { 113 | mip_height = 1; 114 | } 115 | for( j = 0; j < mip_height; ++j ) 116 | { 117 | for( i = 0; i < mip_width; ++i ) 118 | { 119 | for( c = 0; c < channels; ++c ) 120 | { 121 | const int index = (j*block_size_y)*width*channels + (i*block_size_x)*channels + c; 122 | int sum_value; 123 | int u,v; 124 | int u_block = block_size_x; 125 | int v_block = block_size_y; 126 | int block_area; 127 | /* do a bit of checking so we don't over-run the boundaries 128 | (necessary for non-square textures!) */ 129 | if( block_size_x * (i+1) > width ) 130 | { 131 | u_block = width - i*block_size_y; 132 | } 133 | if( block_size_y * (j+1) > height ) 134 | { 135 | v_block = height - j*block_size_y; 136 | } 137 | block_area = u_block*v_block; 138 | /* for this pixel, see what the average 139 | of all the values in the block are. 140 | note: start the sum at the rounding value, not at 0 */ 141 | sum_value = block_area >> 1; 142 | for( v = 0; v < v_block; ++v ) 143 | for( u = 0; u < u_block; ++u ) 144 | { 145 | sum_value += orig[index + v*width*channels + u*channels]; 146 | } 147 | resampled[j*mip_width*channels + i*channels + c] = sum_value / block_area; 148 | } 149 | } 150 | } 151 | return 1; 152 | } 153 | 154 | int 155 | scale_image_RGB_to_NTSC_safe 156 | ( 157 | unsigned char* orig, 158 | int width, int height, int channels 159 | ) 160 | { 161 | const float scale_lo = 16.0f - 0.499f; 162 | const float scale_hi = 235.0f + 0.499f; 163 | int i, j; 164 | int nc = channels; 165 | unsigned char scale_LUT[256]; 166 | /* error check */ 167 | if( (width < 1) || (height < 1) || 168 | (channels < 1) || (orig == NULL) ) 169 | { 170 | /* nothing to do */ 171 | return 0; 172 | } 173 | /* set up the scaling Look Up Table */ 174 | for( i = 0; i < 256; ++i ) 175 | { 176 | scale_LUT[i] = (unsigned char)((scale_hi - scale_lo) * i / 255.0f + scale_lo); 177 | } 178 | /* for channels = 2 or 4, ignore the alpha component */ 179 | nc -= 1 - (channels & 1); 180 | /* OK, go through the image and scale any non-alpha components */ 181 | for( i = 0; i < width*height*channels; i += channels ) 182 | { 183 | for( j = 0; j < nc; ++j ) 184 | { 185 | orig[i+j] = scale_LUT[orig[i+j]]; 186 | } 187 | } 188 | return 1; 189 | } 190 | 191 | unsigned char clamp_byte( int x ) { return ( (x) < 0 ? (0) : ( (x) > 255 ? 255 : (x) ) ); } 192 | 193 | /* 194 | This function takes the RGB components of the image 195 | and converts them into YCoCg. 3 components will be 196 | re-ordered to CoYCg (for optimum DXT1 compression), 197 | while 4 components will be ordered CoCgAY (for DXT5 198 | compression). 199 | */ 200 | int 201 | convert_RGB_to_YCoCg 202 | ( 203 | unsigned char* orig, 204 | int width, int height, int channels 205 | ) 206 | { 207 | int i; 208 | /* error check */ 209 | if( (width < 1) || (height < 1) || 210 | (channels < 3) || (channels > 4) || 211 | (orig == NULL) ) 212 | { 213 | /* nothing to do */ 214 | return -1; 215 | } 216 | /* do the conversion */ 217 | if( channels == 3 ) 218 | { 219 | for( i = 0; i < width*height*3; i += 3 ) 220 | { 221 | int r = orig[i+0]; 222 | int g = (orig[i+1] + 1) >> 1; 223 | int b = orig[i+2]; 224 | int tmp = (2 + r + b) >> 2; 225 | /* Co */ 226 | orig[i+0] = clamp_byte( 128 + ((r - b + 1) >> 1) ); 227 | /* Y */ 228 | orig[i+1] = clamp_byte( g + tmp ); 229 | /* Cg */ 230 | orig[i+2] = clamp_byte( 128 + g - tmp ); 231 | } 232 | } else 233 | { 234 | for( i = 0; i < width*height*4; i += 4 ) 235 | { 236 | int r = orig[i+0]; 237 | int g = (orig[i+1] + 1) >> 1; 238 | int b = orig[i+2]; 239 | unsigned char a = orig[i+3]; 240 | int tmp = (2 + r + b) >> 2; 241 | /* Co */ 242 | orig[i+0] = clamp_byte( 128 + ((r - b + 1) >> 1) ); 243 | /* Cg */ 244 | orig[i+1] = clamp_byte( 128 + g - tmp ); 245 | /* Alpha */ 246 | orig[i+2] = a; 247 | /* Y */ 248 | orig[i+3] = clamp_byte( g + tmp ); 249 | } 250 | } 251 | /* done */ 252 | return 0; 253 | } 254 | 255 | /* 256 | This function takes the YCoCg components of the image 257 | and converts them into RGB. See above. 258 | */ 259 | int 260 | convert_YCoCg_to_RGB 261 | ( 262 | unsigned char* orig, 263 | int width, int height, int channels 264 | ) 265 | { 266 | int i; 267 | /* error check */ 268 | if( (width < 1) || (height < 1) || 269 | (channels < 3) || (channels > 4) || 270 | (orig == NULL) ) 271 | { 272 | /* nothing to do */ 273 | return -1; 274 | } 275 | /* do the conversion */ 276 | if( channels == 3 ) 277 | { 278 | for( i = 0; i < width*height*3; i += 3 ) 279 | { 280 | int co = orig[i+0] - 128; 281 | int y = orig[i+1]; 282 | int cg = orig[i+2] - 128; 283 | /* R */ 284 | orig[i+0] = clamp_byte( y + co - cg ); 285 | /* G */ 286 | orig[i+1] = clamp_byte( y + cg ); 287 | /* B */ 288 | orig[i+2] = clamp_byte( y - co - cg ); 289 | } 290 | } else 291 | { 292 | for( i = 0; i < width*height*4; i += 4 ) 293 | { 294 | int co = orig[i+0] - 128; 295 | int cg = orig[i+1] - 128; 296 | unsigned char a = orig[i+2]; 297 | int y = orig[i+3]; 298 | /* R */ 299 | orig[i+0] = clamp_byte( y + co - cg ); 300 | /* G */ 301 | orig[i+1] = clamp_byte( y + cg ); 302 | /* B */ 303 | orig[i+2] = clamp_byte( y - co - cg ); 304 | /* A */ 305 | orig[i+3] = a; 306 | } 307 | } 308 | /* done */ 309 | return 0; 310 | } 311 | 312 | float 313 | find_max_RGBE 314 | ( 315 | unsigned char *image, 316 | int width, int height 317 | ) 318 | { 319 | float max_val = 0.0f; 320 | unsigned char *img = image; 321 | int i, j; 322 | for( i = width * height; i > 0; --i ) 323 | { 324 | /* float scale = powf( 2.0f, img[3] - 128.0f ) / 255.0f; */ 325 | float scale = ldexp( 1.0f / 255.0f, (int)(img[3]) - 128 ); 326 | for( j = 0; j < 3; ++j ) 327 | { 328 | if( img[j] * scale > max_val ) 329 | { 330 | max_val = img[j] * scale; 331 | } 332 | } 333 | /* next pixel */ 334 | img += 4; 335 | } 336 | return max_val; 337 | } 338 | 339 | int 340 | RGBE_to_RGBdivA 341 | ( 342 | unsigned char *image, 343 | int width, int height, 344 | int rescale_to_max 345 | ) 346 | { 347 | /* local variables */ 348 | int i, iv; 349 | unsigned char *img = image; 350 | float scale = 1.0f; 351 | /* error check */ 352 | if( (!image) || (width < 1) || (height < 1) ) 353 | { 354 | return 0; 355 | } 356 | /* convert (note: no negative numbers, but 0.0 is possible) */ 357 | if( rescale_to_max ) 358 | { 359 | scale = 255.0f / find_max_RGBE( image, width, height ); 360 | } 361 | for( i = width * height; i > 0; --i ) 362 | { 363 | /* decode this pixel, and find the max */ 364 | float r,g,b,e, m; 365 | /* e = scale * powf( 2.0f, img[3] - 128.0f ) / 255.0f; */ 366 | e = scale * ldexp( 1.0f / 255.0f, (int)(img[3]) - 128 ); 367 | r = e * img[0]; 368 | g = e * img[1]; 369 | b = e * img[2]; 370 | m = (r > g) ? r : g; 371 | m = (b > m) ? b : m; 372 | /* and encode it into RGBdivA */ 373 | iv = (m != 0.0f) ? (int)(255.0f / m) : 1.0f; 374 | iv = (iv < 1) ? 1 : iv; 375 | img[3] = (iv > 255) ? 255 : iv; 376 | iv = (int)(img[3] * r + 0.5f); 377 | img[0] = (iv > 255) ? 255 : iv; 378 | iv = (int)(img[3] * g + 0.5f); 379 | img[1] = (iv > 255) ? 255 : iv; 380 | iv = (int)(img[3] * b + 0.5f); 381 | img[2] = (iv > 255) ? 255 : iv; 382 | /* and on to the next pixel */ 383 | img += 4; 384 | } 385 | return 1; 386 | } 387 | 388 | int 389 | RGBE_to_RGBdivA2 390 | ( 391 | unsigned char *image, 392 | int width, int height, 393 | int rescale_to_max 394 | ) 395 | { 396 | /* local variables */ 397 | int i, iv; 398 | unsigned char *img = image; 399 | float scale = 1.0f; 400 | /* error check */ 401 | if( (!image) || (width < 1) || (height < 1) ) 402 | { 403 | return 0; 404 | } 405 | /* convert (note: no negative numbers, but 0.0 is possible) */ 406 | if( rescale_to_max ) 407 | { 408 | scale = 255.0f * 255.0f / find_max_RGBE( image, width, height ); 409 | } 410 | for( i = width * height; i > 0; --i ) 411 | { 412 | /* decode this pixel, and find the max */ 413 | float r,g,b,e, m; 414 | /* e = scale * powf( 2.0f, img[3] - 128.0f ) / 255.0f; */ 415 | e = scale * ldexp( 1.0f / 255.0f, (int)(img[3]) - 128 ); 416 | r = e * img[0]; 417 | g = e * img[1]; 418 | b = e * img[2]; 419 | m = (r > g) ? r : g; 420 | m = (b > m) ? b : m; 421 | /* and encode it into RGBdivA */ 422 | iv = (m != 0.0f) ? (int)sqrtf( 255.0f * 255.0f / m ) : 1.0f; 423 | iv = (iv < 1) ? 1 : iv; 424 | img[3] = (iv > 255) ? 255 : iv; 425 | iv = (int)(img[3] * img[3] * r / 255.0f + 0.5f); 426 | img[0] = (iv > 255) ? 255 : iv; 427 | iv = (int)(img[3] * img[3] * g / 255.0f + 0.5f); 428 | img[1] = (iv > 255) ? 255 : iv; 429 | iv = (int)(img[3] * img[3] * b / 255.0f + 0.5f); 430 | img[2] = (iv > 255) ? 255 : iv; 431 | /* and on to the next pixel */ 432 | img += 4; 433 | } 434 | return 1; 435 | } 436 | -------------------------------------------------------------------------------- /inc/SOIL/SOIL.h: -------------------------------------------------------------------------------- 1 | /** 2 | @mainpage SOIL 3 | 4 | Jonathan Dummer 5 | 2007-07-26-10.36 6 | 7 | Simple OpenGL Image Library 8 | 9 | A tiny c library for uploading images as 10 | textures into OpenGL. Also saving and 11 | loading of images is supported. 12 | 13 | I'm using Sean's Tool Box image loader as a base: 14 | http://www.nothings.org/ 15 | 16 | I'm upgrading it to load TGA and DDS files, and a direct 17 | path for loading DDS files straight into OpenGL textures, 18 | when applicable. 19 | 20 | Image Formats: 21 | - BMP load & save 22 | - TGA load & save 23 | - DDS load & save 24 | - PNG load 25 | - JPG load 26 | 27 | OpenGL Texture Features: 28 | - resample to power-of-two sizes 29 | - MIPmap generation 30 | - compressed texture S3TC formats (if supported) 31 | - can pre-multiply alpha for you, for better compositing 32 | - can flip image about the y-axis (except pre-compressed DDS files) 33 | 34 | Thanks to: 35 | * Sean Barret - for the awesome stb_image 36 | * Dan Venkitachalam - for finding some non-compliant DDS files, and patching some explicit casts 37 | * everybody at gamedev.net 38 | **/ 39 | 40 | #ifndef HEADER_SIMPLE_OPENGL_IMAGE_LIBRARY 41 | #define HEADER_SIMPLE_OPENGL_IMAGE_LIBRARY 42 | 43 | #ifdef __cplusplus 44 | extern "C" { 45 | #endif 46 | 47 | /** 48 | The format of images that may be loaded (force_channels). 49 | SOIL_LOAD_AUTO leaves the image in whatever format it was found. 50 | SOIL_LOAD_L forces the image to load as Luminous (greyscale) 51 | SOIL_LOAD_LA forces the image to load as Luminous with Alpha 52 | SOIL_LOAD_RGB forces the image to load as Red Green Blue 53 | SOIL_LOAD_RGBA forces the image to load as Red Green Blue Alpha 54 | **/ 55 | enum 56 | { 57 | SOIL_LOAD_AUTO = 0, 58 | SOIL_LOAD_L = 1, 59 | SOIL_LOAD_LA = 2, 60 | SOIL_LOAD_RGB = 3, 61 | SOIL_LOAD_RGBA = 4 62 | }; 63 | 64 | /** 65 | Passed in as reuse_texture_ID, will cause SOIL to 66 | register a new texture ID using glGenTextures(). 67 | If the value passed into reuse_texture_ID > 0 then 68 | SOIL will just re-use that texture ID (great for 69 | reloading image assets in-game!) 70 | **/ 71 | enum 72 | { 73 | SOIL_CREATE_NEW_ID = 0 74 | }; 75 | 76 | /** 77 | flags you can pass into SOIL_load_OGL_texture() 78 | and SOIL_create_OGL_texture(). 79 | (note that if SOIL_FLAG_DDS_LOAD_DIRECT is used 80 | the rest of the flags with the exception of 81 | SOIL_FLAG_TEXTURE_REPEATS will be ignored while 82 | loading already-compressed DDS files.) 83 | 84 | SOIL_FLAG_POWER_OF_TWO: force the image to be POT 85 | SOIL_FLAG_MIPMAPS: generate mipmaps for the texture 86 | SOIL_FLAG_TEXTURE_REPEATS: otherwise will clamp 87 | SOIL_FLAG_MULTIPLY_ALPHA: for using (GL_ONE,GL_ONE_MINUS_SRC_ALPHA) blending 88 | SOIL_FLAG_INVERT_Y: flip the image vertically 89 | SOIL_FLAG_COMPRESS_TO_DXT: if the card can display them, will convert RGB to DXT1, RGBA to DXT5 90 | SOIL_FLAG_DDS_LOAD_DIRECT: will load DDS files directly without _ANY_ additional processing 91 | SOIL_FLAG_NTSC_SAFE_RGB: clamps RGB components to the range [16,235] 92 | SOIL_FLAG_CoCg_Y: Google YCoCg; RGB=>CoYCg, RGBA=>CoCgAY 93 | SOIL_FLAG_TEXTURE_RECTANGE: uses ARB_texture_rectangle ; pixel indexed & no repeat or MIPmaps or cubemaps 94 | **/ 95 | enum 96 | { 97 | SOIL_FLAG_POWER_OF_TWO = 1, 98 | SOIL_FLAG_MIPMAPS = 2, 99 | SOIL_FLAG_TEXTURE_REPEATS = 4, 100 | SOIL_FLAG_MULTIPLY_ALPHA = 8, 101 | SOIL_FLAG_INVERT_Y = 16, 102 | SOIL_FLAG_COMPRESS_TO_DXT = 32, 103 | SOIL_FLAG_DDS_LOAD_DIRECT = 64, 104 | SOIL_FLAG_NTSC_SAFE_RGB = 128, 105 | SOIL_FLAG_CoCg_Y = 256, 106 | SOIL_FLAG_TEXTURE_RECTANGLE = 512 107 | }; 108 | 109 | /** 110 | The types of images that may be saved. 111 | (TGA supports uncompressed RGB / RGBA) 112 | (BMP supports uncompressed RGB) 113 | (DDS supports DXT1 and DXT5) 114 | **/ 115 | enum 116 | { 117 | SOIL_SAVE_TYPE_TGA = 0, 118 | SOIL_SAVE_TYPE_BMP = 1, 119 | SOIL_SAVE_TYPE_DDS = 2 120 | }; 121 | 122 | /** 123 | Defines the order of faces in a DDS cubemap. 124 | I recommend that you use the same order in single 125 | image cubemap files, so they will be interchangeable 126 | with DDS cubemaps when using SOIL. 127 | **/ 128 | #define SOIL_DDS_CUBEMAP_FACE_ORDER "EWUDNS" 129 | 130 | /** 131 | The types of internal fake HDR representations 132 | 133 | SOIL_HDR_RGBE: RGB * pow( 2.0, A - 128.0 ) 134 | SOIL_HDR_RGBdivA: RGB / A 135 | SOIL_HDR_RGBdivA2: RGB / (A*A) 136 | **/ 137 | enum 138 | { 139 | SOIL_HDR_RGBE = 0, 140 | SOIL_HDR_RGBdivA = 1, 141 | SOIL_HDR_RGBdivA2 = 2 142 | }; 143 | 144 | /** 145 | Loads an image from disk into an OpenGL texture. 146 | \param filename the name of the file to upload as a texture 147 | \param force_channels 0-image format, 1-luminous, 2-luminous/alpha, 3-RGB, 4-RGBA 148 | \param reuse_texture_ID 0-generate a new texture ID, otherwise reuse the texture ID (overwriting the old texture) 149 | \param flags can be any of SOIL_FLAG_POWER_OF_TWO | SOIL_FLAG_MIPMAPS | SOIL_FLAG_TEXTURE_REPEATS | SOIL_FLAG_MULTIPLY_ALPHA | SOIL_FLAG_INVERT_Y | SOIL_FLAG_COMPRESS_TO_DXT | SOIL_FLAG_DDS_LOAD_DIRECT 150 | \return 0-failed, otherwise returns the OpenGL texture handle 151 | **/ 152 | unsigned int 153 | SOIL_load_OGL_texture 154 | ( 155 | const char *filename, 156 | int force_channels, 157 | unsigned int reuse_texture_ID, 158 | unsigned int flags 159 | ); 160 | 161 | /** 162 | Loads 6 images from disk into an OpenGL cubemap texture. 163 | \param x_pos_file the name of the file to upload as the +x cube face 164 | \param x_neg_file the name of the file to upload as the -x cube face 165 | \param y_pos_file the name of the file to upload as the +y cube face 166 | \param y_neg_file the name of the file to upload as the -y cube face 167 | \param z_pos_file the name of the file to upload as the +z cube face 168 | \param z_neg_file the name of the file to upload as the -z cube face 169 | \param force_channels 0-image format, 1-luminous, 2-luminous/alpha, 3-RGB, 4-RGBA 170 | \param reuse_texture_ID 0-generate a new texture ID, otherwise reuse the texture ID (overwriting the old texture) 171 | \param flags can be any of SOIL_FLAG_POWER_OF_TWO | SOIL_FLAG_MIPMAPS | SOIL_FLAG_TEXTURE_REPEATS | SOIL_FLAG_MULTIPLY_ALPHA | SOIL_FLAG_INVERT_Y | SOIL_FLAG_COMPRESS_TO_DXT | SOIL_FLAG_DDS_LOAD_DIRECT 172 | \return 0-failed, otherwise returns the OpenGL texture handle 173 | **/ 174 | unsigned int 175 | SOIL_load_OGL_cubemap 176 | ( 177 | const char *x_pos_file, 178 | const char *x_neg_file, 179 | const char *y_pos_file, 180 | const char *y_neg_file, 181 | const char *z_pos_file, 182 | const char *z_neg_file, 183 | int force_channels, 184 | unsigned int reuse_texture_ID, 185 | unsigned int flags 186 | ); 187 | 188 | /** 189 | Loads 1 image from disk and splits it into an OpenGL cubemap texture. 190 | \param filename the name of the file to upload as a texture 191 | \param face_order the order of the faces in the file, any combination of NSWEUD, for North, South, Up, etc. 192 | \param force_channels 0-image format, 1-luminous, 2-luminous/alpha, 3-RGB, 4-RGBA 193 | \param reuse_texture_ID 0-generate a new texture ID, otherwise reuse the texture ID (overwriting the old texture) 194 | \param flags can be any of SOIL_FLAG_POWER_OF_TWO | SOIL_FLAG_MIPMAPS | SOIL_FLAG_TEXTURE_REPEATS | SOIL_FLAG_MULTIPLY_ALPHA | SOIL_FLAG_INVERT_Y | SOIL_FLAG_COMPRESS_TO_DXT | SOIL_FLAG_DDS_LOAD_DIRECT 195 | \return 0-failed, otherwise returns the OpenGL texture handle 196 | **/ 197 | unsigned int 198 | SOIL_load_OGL_single_cubemap 199 | ( 200 | const char *filename, 201 | const char face_order[6], 202 | int force_channels, 203 | unsigned int reuse_texture_ID, 204 | unsigned int flags 205 | ); 206 | 207 | /** 208 | Loads an HDR image from disk into an OpenGL texture. 209 | \param filename the name of the file to upload as a texture 210 | \param fake_HDR_format SOIL_HDR_RGBE, SOIL_HDR_RGBdivA, SOIL_HDR_RGBdivA2 211 | \param reuse_texture_ID 0-generate a new texture ID, otherwise reuse the texture ID (overwriting the old texture) 212 | \param flags can be any of SOIL_FLAG_POWER_OF_TWO | SOIL_FLAG_MIPMAPS | SOIL_FLAG_TEXTURE_REPEATS | SOIL_FLAG_MULTIPLY_ALPHA | SOIL_FLAG_INVERT_Y | SOIL_FLAG_COMPRESS_TO_DXT 213 | \return 0-failed, otherwise returns the OpenGL texture handle 214 | **/ 215 | unsigned int 216 | SOIL_load_OGL_HDR_texture 217 | ( 218 | const char *filename, 219 | int fake_HDR_format, 220 | int rescale_to_max, 221 | unsigned int reuse_texture_ID, 222 | unsigned int flags 223 | ); 224 | 225 | /** 226 | Loads an image from RAM into an OpenGL texture. 227 | \param buffer the image data in RAM just as if it were still in a file 228 | \param buffer_length the size of the buffer in bytes 229 | \param force_channels 0-image format, 1-luminous, 2-luminous/alpha, 3-RGB, 4-RGBA 230 | \param reuse_texture_ID 0-generate a new texture ID, otherwise reuse the texture ID (overwriting the old texture) 231 | \param flags can be any of SOIL_FLAG_POWER_OF_TWO | SOIL_FLAG_MIPMAPS | SOIL_FLAG_TEXTURE_REPEATS | SOIL_FLAG_MULTIPLY_ALPHA | SOIL_FLAG_INVERT_Y | SOIL_FLAG_COMPRESS_TO_DXT | SOIL_FLAG_DDS_LOAD_DIRECT 232 | \return 0-failed, otherwise returns the OpenGL texture handle 233 | **/ 234 | unsigned int 235 | SOIL_load_OGL_texture_from_memory 236 | ( 237 | const unsigned char *const buffer, 238 | int buffer_length, 239 | int force_channels, 240 | unsigned int reuse_texture_ID, 241 | unsigned int flags 242 | ); 243 | 244 | /** 245 | Loads 6 images from memory into an OpenGL cubemap texture. 246 | \param x_pos_buffer the image data in RAM to upload as the +x cube face 247 | \param x_pos_buffer_length the size of the above buffer 248 | \param x_neg_buffer the image data in RAM to upload as the +x cube face 249 | \param x_neg_buffer_length the size of the above buffer 250 | \param y_pos_buffer the image data in RAM to upload as the +x cube face 251 | \param y_pos_buffer_length the size of the above buffer 252 | \param y_neg_buffer the image data in RAM to upload as the +x cube face 253 | \param y_neg_buffer_length the size of the above buffer 254 | \param z_pos_buffer the image data in RAM to upload as the +x cube face 255 | \param z_pos_buffer_length the size of the above buffer 256 | \param z_neg_buffer the image data in RAM to upload as the +x cube face 257 | \param z_neg_buffer_length the size of the above buffer 258 | \param force_channels 0-image format, 1-luminous, 2-luminous/alpha, 3-RGB, 4-RGBA 259 | \param reuse_texture_ID 0-generate a new texture ID, otherwise reuse the texture ID (overwriting the old texture) 260 | \param flags can be any of SOIL_FLAG_POWER_OF_TWO | SOIL_FLAG_MIPMAPS | SOIL_FLAG_TEXTURE_REPEATS | SOIL_FLAG_MULTIPLY_ALPHA | SOIL_FLAG_INVERT_Y | SOIL_FLAG_COMPRESS_TO_DXT | SOIL_FLAG_DDS_LOAD_DIRECT 261 | \return 0-failed, otherwise returns the OpenGL texture handle 262 | **/ 263 | unsigned int 264 | SOIL_load_OGL_cubemap_from_memory 265 | ( 266 | const unsigned char *const x_pos_buffer, 267 | int x_pos_buffer_length, 268 | const unsigned char *const x_neg_buffer, 269 | int x_neg_buffer_length, 270 | const unsigned char *const y_pos_buffer, 271 | int y_pos_buffer_length, 272 | const unsigned char *const y_neg_buffer, 273 | int y_neg_buffer_length, 274 | const unsigned char *const z_pos_buffer, 275 | int z_pos_buffer_length, 276 | const unsigned char *const z_neg_buffer, 277 | int z_neg_buffer_length, 278 | int force_channels, 279 | unsigned int reuse_texture_ID, 280 | unsigned int flags 281 | ); 282 | 283 | /** 284 | Loads 1 image from RAM and splits it into an OpenGL cubemap texture. 285 | \param buffer the image data in RAM just as if it were still in a file 286 | \param buffer_length the size of the buffer in bytes 287 | \param face_order the order of the faces in the file, any combination of NSWEUD, for North, South, Up, etc. 288 | \param force_channels 0-image format, 1-luminous, 2-luminous/alpha, 3-RGB, 4-RGBA 289 | \param reuse_texture_ID 0-generate a new texture ID, otherwise reuse the texture ID (overwriting the old texture) 290 | \param flags can be any of SOIL_FLAG_POWER_OF_TWO | SOIL_FLAG_MIPMAPS | SOIL_FLAG_TEXTURE_REPEATS | SOIL_FLAG_MULTIPLY_ALPHA | SOIL_FLAG_INVERT_Y | SOIL_FLAG_COMPRESS_TO_DXT | SOIL_FLAG_DDS_LOAD_DIRECT 291 | \return 0-failed, otherwise returns the OpenGL texture handle 292 | **/ 293 | unsigned int 294 | SOIL_load_OGL_single_cubemap_from_memory 295 | ( 296 | const unsigned char *const buffer, 297 | int buffer_length, 298 | const char face_order[6], 299 | int force_channels, 300 | unsigned int reuse_texture_ID, 301 | unsigned int flags 302 | ); 303 | 304 | /** 305 | Creates a 2D OpenGL texture from raw image data. Note that the raw data is 306 | _NOT_ freed after the upload (so the user can load various versions). 307 | \param data the raw data to be uploaded as an OpenGL texture 308 | \param width the width of the image in pixels 309 | \param height the height of the image in pixels 310 | \param channels the number of channels: 1-luminous, 2-luminous/alpha, 3-RGB, 4-RGBA 311 | \param reuse_texture_ID 0-generate a new texture ID, otherwise reuse the texture ID (overwriting the old texture) 312 | \param flags can be any of SOIL_FLAG_POWER_OF_TWO | SOIL_FLAG_MIPMAPS | SOIL_FLAG_TEXTURE_REPEATS | SOIL_FLAG_MULTIPLY_ALPHA | SOIL_FLAG_INVERT_Y | SOIL_FLAG_COMPRESS_TO_DXT 313 | \return 0-failed, otherwise returns the OpenGL texture handle 314 | **/ 315 | unsigned int 316 | SOIL_create_OGL_texture 317 | ( 318 | const unsigned char *const data, 319 | int width, int height, int channels, 320 | unsigned int reuse_texture_ID, 321 | unsigned int flags 322 | ); 323 | 324 | /** 325 | Creates an OpenGL cubemap texture by splitting up 1 image into 6 parts. 326 | \param data the raw data to be uploaded as an OpenGL texture 327 | \param width the width of the image in pixels 328 | \param height the height of the image in pixels 329 | \param channels the number of channels: 1-luminous, 2-luminous/alpha, 3-RGB, 4-RGBA 330 | \param face_order the order of the faces in the file, and combination of NSWEUD, for North, South, Up, etc. 331 | \param reuse_texture_ID 0-generate a new texture ID, otherwise reuse the texture ID (overwriting the old texture) 332 | \param flags can be any of SOIL_FLAG_POWER_OF_TWO | SOIL_FLAG_MIPMAPS | SOIL_FLAG_TEXTURE_REPEATS | SOIL_FLAG_MULTIPLY_ALPHA | SOIL_FLAG_INVERT_Y | SOIL_FLAG_COMPRESS_TO_DXT | SOIL_FLAG_DDS_LOAD_DIRECT 333 | \return 0-failed, otherwise returns the OpenGL texture handle 334 | **/ 335 | unsigned int 336 | SOIL_create_OGL_single_cubemap 337 | ( 338 | const unsigned char *const data, 339 | int width, int height, int channels, 340 | const char face_order[6], 341 | unsigned int reuse_texture_ID, 342 | unsigned int flags 343 | ); 344 | 345 | /** 346 | Captures the OpenGL window (RGB) and saves it to disk 347 | \return 0 if it failed, otherwise returns 1 348 | **/ 349 | int 350 | SOIL_save_screenshot 351 | ( 352 | const char *filename, 353 | int image_type, 354 | int x, int y, 355 | int width, int height 356 | ); 357 | 358 | /** 359 | Loads an image from disk into an array of unsigned chars. 360 | Note that *channels return the original channel count of the 361 | image. If force_channels was other than SOIL_LOAD_AUTO, 362 | the resulting image has force_channels, but *channels may be 363 | different (if the original image had a different channel 364 | count). 365 | \return 0 if failed, otherwise returns 1 366 | **/ 367 | unsigned char* 368 | SOIL_load_image 369 | ( 370 | const char *filename, 371 | int *width, int *height, int *channels, 372 | int force_channels 373 | ); 374 | 375 | /** 376 | Loads an image from memory into an array of unsigned chars. 377 | Note that *channels return the original channel count of the 378 | image. If force_channels was other than SOIL_LOAD_AUTO, 379 | the resulting image has force_channels, but *channels may be 380 | different (if the original image had a different channel 381 | count). 382 | \return 0 if failed, otherwise returns 1 383 | **/ 384 | unsigned char* 385 | SOIL_load_image_from_memory 386 | ( 387 | const unsigned char *const buffer, 388 | int buffer_length, 389 | int *width, int *height, int *channels, 390 | int force_channels 391 | ); 392 | 393 | /** 394 | Saves an image from an array of unsigned chars (RGBA) to disk 395 | \return 0 if failed, otherwise returns 1 396 | **/ 397 | int 398 | SOIL_save_image 399 | ( 400 | const char *filename, 401 | int image_type, 402 | int width, int height, int channels, 403 | const unsigned char *const data 404 | ); 405 | 406 | /** 407 | Frees the image data (note, this is just C's "free()"...this function is 408 | present mostly so C++ programmers don't forget to use "free()" and call 409 | "delete []" instead [8^) 410 | **/ 411 | void 412 | SOIL_free_image_data 413 | ( 414 | unsigned char *img_data 415 | ); 416 | 417 | /** 418 | This function resturn a pointer to a string describing the last thing 419 | that happened inside SOIL. It can be used to determine why an image 420 | failed to load. 421 | **/ 422 | const char* 423 | SOIL_last_result 424 | ( 425 | void 426 | ); 427 | 428 | 429 | #ifdef __cplusplus 430 | } 431 | #endif 432 | 433 | #endif /* HEADER_SIMPLE_OPENGL_IMAGE_LIBRARY */ 434 | -------------------------------------------------------------------------------- /inc/SOIL/stbi_DDS_aug_c.h: -------------------------------------------------------------------------------- 1 | 2 | /// DDS file support, does decoding, _not_ direct uploading 3 | /// (use SOIL for that ;-) 4 | 5 | /// A bunch of DirectDraw Surface structures and flags 6 | typedef struct { 7 | unsigned int dwMagic; 8 | unsigned int dwSize; 9 | unsigned int dwFlags; 10 | unsigned int dwHeight; 11 | unsigned int dwWidth; 12 | unsigned int dwPitchOrLinearSize; 13 | unsigned int dwDepth; 14 | unsigned int dwMipMapCount; 15 | unsigned int dwReserved1[ 11 ]; 16 | 17 | // DDPIXELFORMAT 18 | struct { 19 | unsigned int dwSize; 20 | unsigned int dwFlags; 21 | unsigned int dwFourCC; 22 | unsigned int dwRGBBitCount; 23 | unsigned int dwRBitMask; 24 | unsigned int dwGBitMask; 25 | unsigned int dwBBitMask; 26 | unsigned int dwAlphaBitMask; 27 | } sPixelFormat; 28 | 29 | // DDCAPS2 30 | struct { 31 | unsigned int dwCaps1; 32 | unsigned int dwCaps2; 33 | unsigned int dwDDSX; 34 | unsigned int dwReserved; 35 | } sCaps; 36 | unsigned int dwReserved2; 37 | } DDS_header ; 38 | 39 | // the following constants were copied directly off the MSDN website 40 | 41 | // The dwFlags member of the original DDSURFACEDESC2 structure 42 | // can be set to one or more of the following values. 43 | #define DDSD_CAPS 0x00000001 44 | #define DDSD_HEIGHT 0x00000002 45 | #define DDSD_WIDTH 0x00000004 46 | #define DDSD_PITCH 0x00000008 47 | #define DDSD_PIXELFORMAT 0x00001000 48 | #define DDSD_MIPMAPCOUNT 0x00020000 49 | #define DDSD_LINEARSIZE 0x00080000 50 | #define DDSD_DEPTH 0x00800000 51 | 52 | // DirectDraw Pixel Format 53 | #define DDPF_ALPHAPIXELS 0x00000001 54 | #define DDPF_FOURCC 0x00000004 55 | #define DDPF_RGB 0x00000040 56 | 57 | // The dwCaps1 member of the DDSCAPS2 structure can be 58 | // set to one or more of the following values. 59 | #define DDSCAPS_COMPLEX 0x00000008 60 | #define DDSCAPS_TEXTURE 0x00001000 61 | #define DDSCAPS_MIPMAP 0x00400000 62 | 63 | // The dwCaps2 member of the DDSCAPS2 structure can be 64 | // set to one or more of the following values. 65 | #define DDSCAPS2_CUBEMAP 0x00000200 66 | #define DDSCAPS2_CUBEMAP_POSITIVEX 0x00000400 67 | #define DDSCAPS2_CUBEMAP_NEGATIVEX 0x00000800 68 | #define DDSCAPS2_CUBEMAP_POSITIVEY 0x00001000 69 | #define DDSCAPS2_CUBEMAP_NEGATIVEY 0x00002000 70 | #define DDSCAPS2_CUBEMAP_POSITIVEZ 0x00004000 71 | #define DDSCAPS2_CUBEMAP_NEGATIVEZ 0x00008000 72 | #define DDSCAPS2_VOLUME 0x00200000 73 | 74 | static int dds_test(stbi *s) 75 | { 76 | // check the magic number 77 | if (get8(s) != 'D') return 0; 78 | if (get8(s) != 'D') return 0; 79 | if (get8(s) != 'S') return 0; 80 | if (get8(s) != ' ') return 0; 81 | // check header size 82 | if (get32le(s) != 124) return 0; 83 | return 1; 84 | } 85 | #ifndef STBI_NO_STDIO 86 | int stbi_dds_test_file (FILE *f) 87 | { 88 | stbi s; 89 | int r,n = ftell(f); 90 | start_file(&s,f); 91 | r = dds_test(&s); 92 | fseek(f,n,SEEK_SET); 93 | return r; 94 | } 95 | #endif 96 | 97 | int stbi_dds_test_memory (stbi_uc const *buffer, int len) 98 | { 99 | stbi s; 100 | start_mem(&s,buffer, len); 101 | return dds_test(&s); 102 | } 103 | 104 | // helper functions 105 | int stbi_convert_bit_range( int c, int from_bits, int to_bits ) 106 | { 107 | int b = (1 << (from_bits - 1)) + c * ((1 << to_bits) - 1); 108 | return (b + (b >> from_bits)) >> from_bits; 109 | } 110 | void stbi_rgb_888_from_565( unsigned int c, int *r, int *g, int *b ) 111 | { 112 | *r = stbi_convert_bit_range( (c >> 11) & 31, 5, 8 ); 113 | *g = stbi_convert_bit_range( (c >> 05) & 63, 6, 8 ); 114 | *b = stbi_convert_bit_range( (c >> 00) & 31, 5, 8 ); 115 | } 116 | void stbi_decode_DXT1_block( 117 | unsigned char uncompressed[16*4], 118 | unsigned char compressed[8] ) 119 | { 120 | int next_bit = 4*8; 121 | int i, r, g, b; 122 | int c0, c1; 123 | unsigned char decode_colors[4*4]; 124 | // find the 2 primary colors 125 | c0 = compressed[0] + (compressed[1] << 8); 126 | c1 = compressed[2] + (compressed[3] << 8); 127 | stbi_rgb_888_from_565( c0, &r, &g, &b ); 128 | decode_colors[0] = r; 129 | decode_colors[1] = g; 130 | decode_colors[2] = b; 131 | decode_colors[3] = 255; 132 | stbi_rgb_888_from_565( c1, &r, &g, &b ); 133 | decode_colors[4] = r; 134 | decode_colors[5] = g; 135 | decode_colors[6] = b; 136 | decode_colors[7] = 255; 137 | if( c0 > c1 ) 138 | { 139 | // no alpha, 2 interpolated colors 140 | decode_colors[8] = (2*decode_colors[0] + decode_colors[4]) / 3; 141 | decode_colors[9] = (2*decode_colors[1] + decode_colors[5]) / 3; 142 | decode_colors[10] = (2*decode_colors[2] + decode_colors[6]) / 3; 143 | decode_colors[11] = 255; 144 | decode_colors[12] = (decode_colors[0] + 2*decode_colors[4]) / 3; 145 | decode_colors[13] = (decode_colors[1] + 2*decode_colors[5]) / 3; 146 | decode_colors[14] = (decode_colors[2] + 2*decode_colors[6]) / 3; 147 | decode_colors[15] = 255; 148 | } else 149 | { 150 | // 1 interpolated color, alpha 151 | decode_colors[8] = (decode_colors[0] + decode_colors[4]) / 2; 152 | decode_colors[9] = (decode_colors[1] + decode_colors[5]) / 2; 153 | decode_colors[10] = (decode_colors[2] + decode_colors[6]) / 2; 154 | decode_colors[11] = 255; 155 | decode_colors[12] = 0; 156 | decode_colors[13] = 0; 157 | decode_colors[14] = 0; 158 | decode_colors[15] = 0; 159 | } 160 | // decode the block 161 | for( i = 0; i < 16*4; i += 4 ) 162 | { 163 | int idx = ((compressed[next_bit>>3] >> (next_bit & 7)) & 3) * 4; 164 | next_bit += 2; 165 | uncompressed[i+0] = decode_colors[idx+0]; 166 | uncompressed[i+1] = decode_colors[idx+1]; 167 | uncompressed[i+2] = decode_colors[idx+2]; 168 | uncompressed[i+3] = decode_colors[idx+3]; 169 | } 170 | // done 171 | } 172 | void stbi_decode_DXT23_alpha_block( 173 | unsigned char uncompressed[16*4], 174 | unsigned char compressed[8] ) 175 | { 176 | int i, next_bit = 0; 177 | // each alpha value gets 4 bits 178 | for( i = 3; i < 16*4; i += 4 ) 179 | { 180 | uncompressed[i] = stbi_convert_bit_range( 181 | (compressed[next_bit>>3] >> (next_bit&7)) & 15, 182 | 4, 8 ); 183 | next_bit += 4; 184 | } 185 | } 186 | void stbi_decode_DXT45_alpha_block( 187 | unsigned char uncompressed[16*4], 188 | unsigned char compressed[8] ) 189 | { 190 | int i, next_bit = 8*2; 191 | unsigned char decode_alpha[8]; 192 | // each alpha value gets 3 bits, and the 1st 2 bytes are the range 193 | decode_alpha[0] = compressed[0]; 194 | decode_alpha[1] = compressed[1]; 195 | if( decode_alpha[0] > decode_alpha[1] ) 196 | { 197 | // 6 step intermediate 198 | decode_alpha[2] = (6*decode_alpha[0] + 1*decode_alpha[1]) / 7; 199 | decode_alpha[3] = (5*decode_alpha[0] + 2*decode_alpha[1]) / 7; 200 | decode_alpha[4] = (4*decode_alpha[0] + 3*decode_alpha[1]) / 7; 201 | decode_alpha[5] = (3*decode_alpha[0] + 4*decode_alpha[1]) / 7; 202 | decode_alpha[6] = (2*decode_alpha[0] + 5*decode_alpha[1]) / 7; 203 | decode_alpha[7] = (1*decode_alpha[0] + 6*decode_alpha[1]) / 7; 204 | } else 205 | { 206 | // 4 step intermediate, pluss full and none 207 | decode_alpha[2] = (4*decode_alpha[0] + 1*decode_alpha[1]) / 5; 208 | decode_alpha[3] = (3*decode_alpha[0] + 2*decode_alpha[1]) / 5; 209 | decode_alpha[4] = (2*decode_alpha[0] + 3*decode_alpha[1]) / 5; 210 | decode_alpha[5] = (1*decode_alpha[0] + 4*decode_alpha[1]) / 5; 211 | decode_alpha[6] = 0; 212 | decode_alpha[7] = 255; 213 | } 214 | for( i = 3; i < 16*4; i += 4 ) 215 | { 216 | int idx = 0, bit; 217 | bit = (compressed[next_bit>>3] >> (next_bit&7)) & 1; 218 | idx += bit << 0; 219 | ++next_bit; 220 | bit = (compressed[next_bit>>3] >> (next_bit&7)) & 1; 221 | idx += bit << 1; 222 | ++next_bit; 223 | bit = (compressed[next_bit>>3] >> (next_bit&7)) & 1; 224 | idx += bit << 2; 225 | ++next_bit; 226 | uncompressed[i] = decode_alpha[idx & 7]; 227 | } 228 | // done 229 | } 230 | void stbi_decode_DXT_color_block( 231 | unsigned char uncompressed[16*4], 232 | unsigned char compressed[8] ) 233 | { 234 | int next_bit = 4*8; 235 | int i, r, g, b; 236 | int c0, c1; 237 | unsigned char decode_colors[4*3]; 238 | // find the 2 primary colors 239 | c0 = compressed[0] + (compressed[1] << 8); 240 | c1 = compressed[2] + (compressed[3] << 8); 241 | stbi_rgb_888_from_565( c0, &r, &g, &b ); 242 | decode_colors[0] = r; 243 | decode_colors[1] = g; 244 | decode_colors[2] = b; 245 | stbi_rgb_888_from_565( c1, &r, &g, &b ); 246 | decode_colors[3] = r; 247 | decode_colors[4] = g; 248 | decode_colors[5] = b; 249 | // Like DXT1, but no choicees: 250 | // no alpha, 2 interpolated colors 251 | decode_colors[6] = (2*decode_colors[0] + decode_colors[3]) / 3; 252 | decode_colors[7] = (2*decode_colors[1] + decode_colors[4]) / 3; 253 | decode_colors[8] = (2*decode_colors[2] + decode_colors[5]) / 3; 254 | decode_colors[9] = (decode_colors[0] + 2*decode_colors[3]) / 3; 255 | decode_colors[10] = (decode_colors[1] + 2*decode_colors[4]) / 3; 256 | decode_colors[11] = (decode_colors[2] + 2*decode_colors[5]) / 3; 257 | // decode the block 258 | for( i = 0; i < 16*4; i += 4 ) 259 | { 260 | int idx = ((compressed[next_bit>>3] >> (next_bit & 7)) & 3) * 3; 261 | next_bit += 2; 262 | uncompressed[i+0] = decode_colors[idx+0]; 263 | uncompressed[i+1] = decode_colors[idx+1]; 264 | uncompressed[i+2] = decode_colors[idx+2]; 265 | } 266 | // done 267 | } 268 | static stbi_uc *dds_load(stbi *s, int *x, int *y, int *comp, int req_comp) 269 | { 270 | // all variables go up front 271 | stbi_uc *dds_data = NULL; 272 | stbi_uc block[16*4]; 273 | stbi_uc compressed[8]; 274 | int flags, DXT_family; 275 | int has_alpha, has_mipmap; 276 | int is_compressed, cubemap_faces; 277 | int block_pitch, num_blocks; 278 | DDS_header header; 279 | int i, sz, cf; 280 | // load the header 281 | if( sizeof( DDS_header ) != 128 ) 282 | { 283 | return NULL; 284 | } 285 | getn( s, (stbi_uc*)(&header), 128 ); 286 | // and do some checking 287 | if( header.dwMagic != (('D' << 0) | ('D' << 8) | ('S' << 16) | (' ' << 24)) ) return NULL; 288 | if( header.dwSize != 124 ) return NULL; 289 | flags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT; 290 | if( (header.dwFlags & flags) != flags ) return NULL; 291 | /* According to the MSDN spec, the dwFlags should contain 292 | DDSD_LINEARSIZE if it's compressed, or DDSD_PITCH if 293 | uncompressed. Some DDS writers do not conform to the 294 | spec, so I need to make my reader more tolerant */ 295 | if( header.sPixelFormat.dwSize != 32 ) return NULL; 296 | flags = DDPF_FOURCC | DDPF_RGB; 297 | if( (header.sPixelFormat.dwFlags & flags) == 0 ) return NULL; 298 | if( (header.sCaps.dwCaps1 & DDSCAPS_TEXTURE) == 0 ) return NULL; 299 | // get the image data 300 | s->img_x = header.dwWidth; 301 | s->img_y = header.dwHeight; 302 | s->img_n = 4; 303 | is_compressed = (header.sPixelFormat.dwFlags & DDPF_FOURCC) / DDPF_FOURCC; 304 | has_alpha = (header.sPixelFormat.dwFlags & DDPF_ALPHAPIXELS) / DDPF_ALPHAPIXELS; 305 | has_mipmap = (header.sCaps.dwCaps1 & DDSCAPS_MIPMAP) && (header.dwMipMapCount > 1); 306 | cubemap_faces = (header.sCaps.dwCaps2 & DDSCAPS2_CUBEMAP) / DDSCAPS2_CUBEMAP; 307 | /* I need cubemaps to have square faces */ 308 | cubemap_faces &= (s->img_x == s->img_y); 309 | cubemap_faces *= 5; 310 | cubemap_faces += 1; 311 | block_pitch = (s->img_x+3) >> 2; 312 | num_blocks = block_pitch * ((s->img_y+3) >> 2); 313 | /* let the user know what's going on */ 314 | *x = s->img_x; 315 | *y = s->img_y; 316 | *comp = s->img_n; 317 | /* is this uncompressed? */ 318 | if( is_compressed ) 319 | { 320 | /* compressed */ 321 | // note: header.sPixelFormat.dwFourCC is something like (('D'<<0)|('X'<<8)|('T'<<16)|('1'<<24)) 322 | DXT_family = 1 + (header.sPixelFormat.dwFourCC >> 24) - '1'; 323 | if( (DXT_family < 1) || (DXT_family > 5) ) return NULL; 324 | /* check the expected size...oops, nevermind... 325 | those non-compliant writers leave 326 | dwPitchOrLinearSize == 0 */ 327 | // passed all the tests, get the RAM for decoding 328 | sz = (s->img_x)*(s->img_y)*4*cubemap_faces; 329 | dds_data = (unsigned char*)malloc( sz ); 330 | /* do this once for each face */ 331 | for( cf = 0; cf < cubemap_faces; ++ cf ) 332 | { 333 | // now read and decode all the blocks 334 | for( i = 0; i < num_blocks; ++i ) 335 | { 336 | // where are we? 337 | int bx, by, bw=4, bh=4; 338 | int ref_x = 4 * (i % block_pitch); 339 | int ref_y = 4 * (i / block_pitch); 340 | // get the next block's worth of compressed data, and decompress it 341 | if( DXT_family == 1 ) 342 | { 343 | // DXT1 344 | getn( s, compressed, 8 ); 345 | stbi_decode_DXT1_block( block, compressed ); 346 | } else if( DXT_family < 4 ) 347 | { 348 | // DXT2/3 349 | getn( s, compressed, 8 ); 350 | stbi_decode_DXT23_alpha_block ( block, compressed ); 351 | getn( s, compressed, 8 ); 352 | stbi_decode_DXT_color_block ( block, compressed ); 353 | } else 354 | { 355 | // DXT4/5 356 | getn( s, compressed, 8 ); 357 | stbi_decode_DXT45_alpha_block ( block, compressed ); 358 | getn( s, compressed, 8 ); 359 | stbi_decode_DXT_color_block ( block, compressed ); 360 | } 361 | // is this a partial block? 362 | if( ref_x + 4 > s->img_x ) 363 | { 364 | bw = s->img_x - ref_x; 365 | } 366 | if( ref_y + 4 > s->img_y ) 367 | { 368 | bh = s->img_y - ref_y; 369 | } 370 | // now drop our decompressed data into the buffer 371 | for( by = 0; by < bh; ++by ) 372 | { 373 | int idx = 4*((ref_y+by+cf*s->img_x)*s->img_x + ref_x); 374 | for( bx = 0; bx < bw*4; ++bx ) 375 | { 376 | 377 | dds_data[idx+bx] = block[by*16+bx]; 378 | } 379 | } 380 | } 381 | /* done reading and decoding the main image... 382 | skip MIPmaps if present */ 383 | if( has_mipmap ) 384 | { 385 | int block_size = 16; 386 | if( DXT_family == 1 ) 387 | { 388 | block_size = 8; 389 | } 390 | for( i = 1; i < header.dwMipMapCount; ++i ) 391 | { 392 | int mx = s->img_x >> (i + 2); 393 | int my = s->img_y >> (i + 2); 394 | if( mx < 1 ) 395 | { 396 | mx = 1; 397 | } 398 | if( my < 1 ) 399 | { 400 | my = 1; 401 | } 402 | skip( s, mx*my*block_size ); 403 | } 404 | } 405 | }/* per cubemap face */ 406 | } else 407 | { 408 | /* uncompressed */ 409 | DXT_family = 0; 410 | s->img_n = 3; 411 | if( has_alpha ) 412 | { 413 | s->img_n = 4; 414 | } 415 | *comp = s->img_n; 416 | sz = s->img_x*s->img_y*s->img_n*cubemap_faces; 417 | dds_data = (unsigned char*)malloc( sz ); 418 | /* do this once for each face */ 419 | for( cf = 0; cf < cubemap_faces; ++ cf ) 420 | { 421 | /* read the main image for this face */ 422 | getn( s, &dds_data[cf*s->img_x*s->img_y*s->img_n], s->img_x*s->img_y*s->img_n ); 423 | /* done reading and decoding the main image... 424 | skip MIPmaps if present */ 425 | if( has_mipmap ) 426 | { 427 | for( i = 1; i < header.dwMipMapCount; ++i ) 428 | { 429 | int mx = s->img_x >> i; 430 | int my = s->img_y >> i; 431 | if( mx < 1 ) 432 | { 433 | mx = 1; 434 | } 435 | if( my < 1 ) 436 | { 437 | my = 1; 438 | } 439 | skip( s, mx*my*s->img_n ); 440 | } 441 | } 442 | } 443 | /* data was BGR, I need it RGB */ 444 | for( i = 0; i < sz; i += s->img_n ) 445 | { 446 | unsigned char temp = dds_data[i]; 447 | dds_data[i] = dds_data[i+2]; 448 | dds_data[i+2] = temp; 449 | } 450 | } 451 | /* finished decompressing into RGBA, 452 | adjust the y size if we have a cubemap 453 | note: sz is already up to date */ 454 | s->img_y *= cubemap_faces; 455 | *y = s->img_y; 456 | // did the user want something else, or 457 | // see if all the alpha values are 255 (i.e. no transparency) 458 | has_alpha = 0; 459 | if( s->img_n == 4) 460 | { 461 | for( i = 3; (i < sz) && (has_alpha == 0); i += 4 ) 462 | { 463 | has_alpha |= (dds_data[i] < 255); 464 | } 465 | } 466 | if( (req_comp <= 4) && (req_comp >= 1) ) 467 | { 468 | // user has some requirements, meet them 469 | if( req_comp != s->img_n ) 470 | { 471 | dds_data = convert_format( dds_data, s->img_n, req_comp, s->img_x, s->img_y ); 472 | *comp = s->img_n; 473 | } 474 | } else 475 | { 476 | // user had no requirements, only drop to RGB is no alpha 477 | if( (has_alpha == 0) && (s->img_n == 4) ) 478 | { 479 | dds_data = convert_format( dds_data, 4, 3, s->img_x, s->img_y ); 480 | *comp = 3; 481 | } 482 | } 483 | // OK, done 484 | return dds_data; 485 | } 486 | 487 | #ifndef STBI_NO_STDIO 488 | stbi_uc *stbi_dds_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp) 489 | { 490 | stbi s; 491 | start_file(&s,f); 492 | return dds_load(&s,x,y,comp,req_comp); 493 | } 494 | 495 | stbi_uc *stbi_dds_load (char *filename, int *x, int *y, int *comp, int req_comp) 496 | { 497 | stbi_uc *data; 498 | FILE *f = fopen(filename, "rb"); 499 | if (!f) return NULL; 500 | data = stbi_dds_load_from_file(f,x,y,comp,req_comp); 501 | fclose(f); 502 | return data; 503 | } 504 | #endif 505 | 506 | stbi_uc *stbi_dds_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) 507 | { 508 | stbi s; 509 | start_mem(&s,buffer, len); 510 | return dds_load(&s,x,y,comp,req_comp); 511 | } 512 | -------------------------------------------------------------------------------- /inc/SOIL/stb_image_aug.h: -------------------------------------------------------------------------------- 1 | /* stbi-1.16 - public domain JPEG/PNG reader - http://nothings.org/stb_image.c 2 | when you control the images you're loading 3 | 4 | QUICK NOTES: 5 | Primarily of interest to game developers and other people who can 6 | avoid problematic images and only need the trivial interface 7 | 8 | JPEG baseline (no JPEG progressive, no oddball channel decimations) 9 | PNG non-interlaced 10 | BMP non-1bpp, non-RLE 11 | TGA (not sure what subset, if a subset) 12 | PSD (composited view only, no extra channels) 13 | HDR (radiance rgbE format) 14 | writes BMP,TGA (define STBI_NO_WRITE to remove code) 15 | decoded from memory or through stdio FILE (define STBI_NO_STDIO to remove code) 16 | supports installable dequantizing-IDCT, YCbCr-to-RGB conversion (define STBI_SIMD) 17 | 18 | TODO: 19 | stbi_info_* 20 | 21 | history: 22 | 1.16 major bugfix - convert_format converted one too many pixels 23 | 1.15 initialize some fields for thread safety 24 | 1.14 fix threadsafe conversion bug; header-file-only version (#define STBI_HEADER_FILE_ONLY before including) 25 | 1.13 threadsafe 26 | 1.12 const qualifiers in the API 27 | 1.11 Support installable IDCT, colorspace conversion routines 28 | 1.10 Fixes for 64-bit (don't use "unsigned long") 29 | optimized upsampling by Fabian "ryg" Giesen 30 | 1.09 Fix format-conversion for PSD code (bad global variables!) 31 | 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz 32 | 1.07 attempt to fix C++ warning/errors again 33 | 1.06 attempt to fix C++ warning/errors again 34 | 1.05 fix TGA loading to return correct *comp and use good luminance calc 35 | 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free 36 | 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR 37 | 1.02 support for (subset of) HDR files, float interface for preferred access to them 38 | 1.01 fix bug: possible bug in handling right-side up bmps... not sure 39 | fix bug: the stbi_bmp_load() and stbi_tga_load() functions didn't work at all 40 | 1.00 interface to zlib that skips zlib header 41 | 0.99 correct handling of alpha in palette 42 | 0.98 TGA loader by lonesock; dynamically add loaders (untested) 43 | 0.97 jpeg errors on too large a file; also catch another malloc failure 44 | 0.96 fix detection of invalid v value - particleman@mollyrocket forum 45 | 0.95 during header scan, seek to markers in case of padding 46 | 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same 47 | 0.93 handle jpegtran output; verbose errors 48 | 0.92 read 4,8,16,24,32-bit BMP files of several formats 49 | 0.91 output 24-bit Windows 3.0 BMP files 50 | 0.90 fix a few more warnings; bump version number to approach 1.0 51 | 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd 52 | 0.60 fix compiling as c++ 53 | 0.59 fix warnings: merge Dave Moore's -Wall fixes 54 | 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian 55 | 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less 56 | than 16 available 57 | 0.56 fix bug: zlib uncompressed mode len vs. nlen 58 | 0.55 fix bug: restart_interval not initialized to 0 59 | 0.54 allow NULL for 'int *comp' 60 | 0.53 fix bug in png 3->4; speedup png decoding 61 | 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments 62 | 0.51 obey req_comp requests, 1-component jpegs return as 1-component, 63 | on 'test' only check type, not whether we support this variant 64 | */ 65 | 66 | #ifndef HEADER_STB_IMAGE_AUGMENTED 67 | #define HEADER_STB_IMAGE_AUGMENTED 68 | 69 | //// begin header file //////////////////////////////////////////////////// 70 | // 71 | // Limitations: 72 | // - no progressive/interlaced support (jpeg, png) 73 | // - 8-bit samples only (jpeg, png) 74 | // - not threadsafe 75 | // - channel subsampling of at most 2 in each dimension (jpeg) 76 | // - no delayed line count (jpeg) -- IJG doesn't support either 77 | // 78 | // Basic usage (see HDR discussion below): 79 | // int x,y,n; 80 | // unsigned char *data = stbi_load(filename, &x, &y, &n, 0); 81 | // // ... process data if not NULL ... 82 | // // ... x = width, y = height, n = # 8-bit components per pixel ... 83 | // // ... replace '0' with '1'..'4' to force that many components per pixel 84 | // stbi_image_free(data) 85 | // 86 | // Standard parameters: 87 | // int *x -- outputs image width in pixels 88 | // int *y -- outputs image height in pixels 89 | // int *comp -- outputs # of image components in image file 90 | // int req_comp -- if non-zero, # of image components requested in result 91 | // 92 | // The return value from an image loader is an 'unsigned char *' which points 93 | // to the pixel data. The pixel data consists of *y scanlines of *x pixels, 94 | // with each pixel consisting of N interleaved 8-bit components; the first 95 | // pixel pointed to is top-left-most in the image. There is no padding between 96 | // image scanlines or between pixels, regardless of format. The number of 97 | // components N is 'req_comp' if req_comp is non-zero, or *comp otherwise. 98 | // If req_comp is non-zero, *comp has the number of components that _would_ 99 | // have been output otherwise. E.g. if you set req_comp to 4, you will always 100 | // get RGBA output, but you can check *comp to easily see if it's opaque. 101 | // 102 | // An output image with N components has the following components interleaved 103 | // in this order in each pixel: 104 | // 105 | // N=#comp components 106 | // 1 grey 107 | // 2 grey, alpha 108 | // 3 red, green, blue 109 | // 4 red, green, blue, alpha 110 | // 111 | // If image loading fails for any reason, the return value will be NULL, 112 | // and *x, *y, *comp will be unchanged. The function stbi_failure_reason() 113 | // can be queried for an extremely brief, end-user unfriendly explanation 114 | // of why the load failed. Define STBI_NO_FAILURE_STRINGS to avoid 115 | // compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly 116 | // more user-friendly ones. 117 | // 118 | // Paletted PNG and BMP images are automatically depalettized. 119 | // 120 | // 121 | // =========================================================================== 122 | // 123 | // HDR image support (disable by defining STBI_NO_HDR) 124 | // 125 | // stb_image now supports loading HDR images in general, and currently 126 | // the Radiance .HDR file format, although the support is provided 127 | // generically. You can still load any file through the existing interface; 128 | // if you attempt to load an HDR file, it will be automatically remapped to 129 | // LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; 130 | // both of these constants can be reconfigured through this interface: 131 | // 132 | // stbi_hdr_to_ldr_gamma(2.2f); 133 | // stbi_hdr_to_ldr_scale(1.0f); 134 | // 135 | // (note, do not use _inverse_ constants; stbi_image will invert them 136 | // appropriately). 137 | // 138 | // Additionally, there is a new, parallel interface for loading files as 139 | // (linear) floats to preserve the full dynamic range: 140 | // 141 | // float *data = stbi_loadf(filename, &x, &y, &n, 0); 142 | // 143 | // If you load LDR images through this interface, those images will 144 | // be promoted to floating point values, run through the inverse of 145 | // constants corresponding to the above: 146 | // 147 | // stbi_ldr_to_hdr_scale(1.0f); 148 | // stbi_ldr_to_hdr_gamma(2.2f); 149 | // 150 | // Finally, given a filename (or an open file or memory block--see header 151 | // file for details) containing image data, you can query for the "most 152 | // appropriate" interface to use (that is, whether the image is HDR or 153 | // not), using: 154 | // 155 | // stbi_is_hdr(char *filename); 156 | 157 | #ifndef STBI_NO_STDIO 158 | #include 159 | #endif 160 | 161 | #define STBI_VERSION 1 162 | 163 | enum 164 | { 165 | STBI_default = 0, // only used for req_comp 166 | 167 | STBI_grey = 1, 168 | STBI_grey_alpha = 2, 169 | STBI_rgb = 3, 170 | STBI_rgb_alpha = 4, 171 | }; 172 | 173 | typedef unsigned char stbi_uc; 174 | 175 | #ifdef __cplusplus 176 | extern "C" { 177 | #endif 178 | 179 | // WRITING API 180 | 181 | #if !defined(STBI_NO_WRITE) && !defined(STBI_NO_STDIO) 182 | // write a BMP/TGA file given tightly packed 'comp' channels (no padding, nor bmp-stride-padding) 183 | // (you must include the appropriate extension in the filename). 184 | // returns TRUE on success, FALSE if couldn't open file, error writing file 185 | extern int stbi_write_bmp (char const *filename, int x, int y, int comp, void *data); 186 | extern int stbi_write_tga (char const *filename, int x, int y, int comp, void *data); 187 | #endif 188 | 189 | // PRIMARY API - works on images of any type 190 | 191 | // load image by filename, open file, or memory buffer 192 | #ifndef STBI_NO_STDIO 193 | extern stbi_uc *stbi_load (char const *filename, int *x, int *y, int *comp, int req_comp); 194 | extern stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); 195 | extern int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); 196 | #endif 197 | extern stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); 198 | // for stbi_load_from_file, file pointer is left pointing immediately after image 199 | 200 | #ifndef STBI_NO_HDR 201 | #ifndef STBI_NO_STDIO 202 | extern float *stbi_loadf (char const *filename, int *x, int *y, int *comp, int req_comp); 203 | extern float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); 204 | #endif 205 | extern float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); 206 | 207 | extern void stbi_hdr_to_ldr_gamma(float gamma); 208 | extern void stbi_hdr_to_ldr_scale(float scale); 209 | 210 | extern void stbi_ldr_to_hdr_gamma(float gamma); 211 | extern void stbi_ldr_to_hdr_scale(float scale); 212 | 213 | #endif // STBI_NO_HDR 214 | 215 | // get a VERY brief reason for failure 216 | // NOT THREADSAFE 217 | extern char *stbi_failure_reason (void); 218 | 219 | // free the loaded image -- this is just free() 220 | extern void stbi_image_free (void *retval_from_stbi_load); 221 | 222 | // get image dimensions & components without fully decoding 223 | extern int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); 224 | extern int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); 225 | #ifndef STBI_NO_STDIO 226 | extern int stbi_info (char const *filename, int *x, int *y, int *comp); 227 | extern int stbi_is_hdr (char const *filename); 228 | extern int stbi_is_hdr_from_file(FILE *f); 229 | #endif 230 | 231 | // ZLIB client - used by PNG, available for other purposes 232 | 233 | extern char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); 234 | extern char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); 235 | extern int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); 236 | 237 | extern char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); 238 | extern int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); 239 | 240 | // TYPE-SPECIFIC ACCESS 241 | 242 | // is it a jpeg? 243 | extern int stbi_jpeg_test_memory (stbi_uc const *buffer, int len); 244 | extern stbi_uc *stbi_jpeg_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); 245 | extern int stbi_jpeg_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); 246 | 247 | #ifndef STBI_NO_STDIO 248 | extern stbi_uc *stbi_jpeg_load (char const *filename, int *x, int *y, int *comp, int req_comp); 249 | extern int stbi_jpeg_test_file (FILE *f); 250 | extern stbi_uc *stbi_jpeg_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); 251 | 252 | extern int stbi_jpeg_info (char const *filename, int *x, int *y, int *comp); 253 | extern int stbi_jpeg_info_from_file (FILE *f, int *x, int *y, int *comp); 254 | #endif 255 | 256 | // is it a png? 257 | extern int stbi_png_test_memory (stbi_uc const *buffer, int len); 258 | extern stbi_uc *stbi_png_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); 259 | extern int stbi_png_info_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp); 260 | 261 | #ifndef STBI_NO_STDIO 262 | extern stbi_uc *stbi_png_load (char const *filename, int *x, int *y, int *comp, int req_comp); 263 | extern int stbi_png_info (char const *filename, int *x, int *y, int *comp); 264 | extern int stbi_png_test_file (FILE *f); 265 | extern stbi_uc *stbi_png_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); 266 | extern int stbi_png_info_from_file (FILE *f, int *x, int *y, int *comp); 267 | #endif 268 | 269 | // is it a bmp? 270 | extern int stbi_bmp_test_memory (stbi_uc const *buffer, int len); 271 | 272 | extern stbi_uc *stbi_bmp_load (char const *filename, int *x, int *y, int *comp, int req_comp); 273 | extern stbi_uc *stbi_bmp_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); 274 | #ifndef STBI_NO_STDIO 275 | extern int stbi_bmp_test_file (FILE *f); 276 | extern stbi_uc *stbi_bmp_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); 277 | #endif 278 | 279 | // is it a tga? 280 | extern int stbi_tga_test_memory (stbi_uc const *buffer, int len); 281 | 282 | extern stbi_uc *stbi_tga_load (char const *filename, int *x, int *y, int *comp, int req_comp); 283 | extern stbi_uc *stbi_tga_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); 284 | #ifndef STBI_NO_STDIO 285 | extern int stbi_tga_test_file (FILE *f); 286 | extern stbi_uc *stbi_tga_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); 287 | #endif 288 | 289 | // is it a psd? 290 | extern int stbi_psd_test_memory (stbi_uc const *buffer, int len); 291 | 292 | extern stbi_uc *stbi_psd_load (char const *filename, int *x, int *y, int *comp, int req_comp); 293 | extern stbi_uc *stbi_psd_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); 294 | #ifndef STBI_NO_STDIO 295 | extern int stbi_psd_test_file (FILE *f); 296 | extern stbi_uc *stbi_psd_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); 297 | #endif 298 | 299 | // is it an hdr? 300 | extern int stbi_hdr_test_memory (stbi_uc const *buffer, int len); 301 | 302 | extern float * stbi_hdr_load (char const *filename, int *x, int *y, int *comp, int req_comp); 303 | extern float * stbi_hdr_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); 304 | extern stbi_uc *stbi_hdr_load_rgbe (char const *filename, int *x, int *y, int *comp, int req_comp); 305 | extern float * stbi_hdr_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); 306 | #ifndef STBI_NO_STDIO 307 | extern int stbi_hdr_test_file (FILE *f); 308 | extern float * stbi_hdr_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); 309 | extern stbi_uc *stbi_hdr_load_rgbe_file (FILE *f, int *x, int *y, int *comp, int req_comp); 310 | #endif 311 | 312 | // define new loaders 313 | typedef struct 314 | { 315 | int (*test_memory)(stbi_uc const *buffer, int len); 316 | stbi_uc * (*load_from_memory)(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); 317 | #ifndef STBI_NO_STDIO 318 | int (*test_file)(FILE *f); 319 | stbi_uc * (*load_from_file)(FILE *f, int *x, int *y, int *comp, int req_comp); 320 | #endif 321 | } stbi_loader; 322 | 323 | // register a loader by filling out the above structure (you must defined ALL functions) 324 | // returns 1 if added or already added, 0 if not added (too many loaders) 325 | // NOT THREADSAFE 326 | extern int stbi_register_loader(stbi_loader *loader); 327 | 328 | // define faster low-level operations (typically SIMD support) 329 | #if STBI_SIMD 330 | typedef void (*stbi_idct_8x8)(uint8 *out, int out_stride, short data[64], unsigned short *dequantize); 331 | // compute an integer IDCT on "input" 332 | // input[x] = data[x] * dequantize[x] 333 | // write results to 'out': 64 samples, each run of 8 spaced by 'out_stride' 334 | // CLAMP results to 0..255 335 | typedef void (*stbi_YCbCr_to_RGB_run)(uint8 *output, uint8 const *y, uint8 const *cb, uint8 const *cr, int count, int step); 336 | // compute a conversion from YCbCr to RGB 337 | // 'count' pixels 338 | // write pixels to 'output'; each pixel is 'step' bytes (either 3 or 4; if 4, write '255' as 4th), order R,G,B 339 | // y: Y input channel 340 | // cb: Cb input channel; scale/biased to be 0..255 341 | // cr: Cr input channel; scale/biased to be 0..255 342 | 343 | extern void stbi_install_idct(stbi_idct_8x8 func); 344 | extern void stbi_install_YCbCr_to_RGB(stbi_YCbCr_to_RGB_run func); 345 | #endif // STBI_SIMD 346 | 347 | #ifdef __cplusplus 348 | } 349 | #endif 350 | 351 | // 352 | // 353 | //// end header file ///////////////////////////////////////////////////// 354 | #endif // STBI_INCLUDE_STB_IMAGE_H 355 | -------------------------------------------------------------------------------- /src/image_DXT.c: -------------------------------------------------------------------------------- 1 | /* 2 | Jonathan Dummer 3 | 2007-07-31-10.32 4 | 5 | simple DXT compression / decompression code 6 | 7 | public domain 8 | */ 9 | 10 | #include "SOIL/image_DXT.h" 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | /* set this =1 if you want to use the covarince matrix method... 17 | which is better than my method of using standard deviations 18 | overall, except on the infintesimal chance that the power 19 | method fails for finding the largest eigenvector */ 20 | #define USE_COV_MAT 1 21 | 22 | /********* Function Prototypes *********/ 23 | /* 24 | Takes a 4x4 block of pixels and compresses it into 8 bytes 25 | in DXT1 format (color only, no alpha). Speed is valued 26 | over prettyness, at least for now. 27 | */ 28 | void compress_DDS_color_block( 29 | int channels, 30 | const unsigned char *const uncompressed, 31 | unsigned char compressed[8] ); 32 | /* 33 | Takes a 4x4 block of pixels and compresses the alpha 34 | component it into 8 bytes for use in DXT5 DDS files. 35 | Speed is valued over prettyness, at least for now. 36 | */ 37 | void compress_DDS_alpha_block( 38 | const unsigned char *const uncompressed, 39 | unsigned char compressed[8] ); 40 | 41 | /********* Actual Exposed Functions *********/ 42 | int 43 | save_image_as_DDS 44 | ( 45 | const char *filename, 46 | int width, int height, int channels, 47 | const unsigned char *const data 48 | ) 49 | { 50 | /* variables */ 51 | FILE *fout; 52 | unsigned char *DDS_data; 53 | DDS_header header; 54 | int DDS_size; 55 | /* error check */ 56 | if( (NULL == filename) || 57 | (width < 1) || (height < 1) || 58 | (channels < 1) || (channels > 4) || 59 | (data == NULL ) ) 60 | { 61 | return 0; 62 | } 63 | /* Convert the image */ 64 | if( (channels & 1) == 1 ) 65 | { 66 | /* no alpha, just use DXT1 */ 67 | DDS_data = convert_image_to_DXT1( data, width, height, channels, &DDS_size ); 68 | } else 69 | { 70 | /* has alpha, so use DXT5 */ 71 | DDS_data = convert_image_to_DXT5( data, width, height, channels, &DDS_size ); 72 | } 73 | /* save it */ 74 | memset( &header, 0, sizeof( DDS_header ) ); 75 | header.dwMagic = ('D' << 0) | ('D' << 8) | ('S' << 16) | (' ' << 24); 76 | header.dwSize = 124; 77 | header.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT | DDSD_LINEARSIZE; 78 | header.dwWidth = width; 79 | header.dwHeight = height; 80 | header.dwPitchOrLinearSize = DDS_size; 81 | header.sPixelFormat.dwSize = 32; 82 | header.sPixelFormat.dwFlags = DDPF_FOURCC; 83 | if( (channels & 1) == 1 ) 84 | { 85 | header.sPixelFormat.dwFourCC = ('D' << 0) | ('X' << 8) | ('T' << 16) | ('1' << 24); 86 | } else 87 | { 88 | header.sPixelFormat.dwFourCC = ('D' << 0) | ('X' << 8) | ('T' << 16) | ('5' << 24); 89 | } 90 | header.sCaps.dwCaps1 = DDSCAPS_TEXTURE; 91 | /* write it out */ 92 | fout = fopen( filename, "wb"); 93 | fwrite( &header, sizeof( DDS_header ), 1, fout ); 94 | fwrite( DDS_data, 1, DDS_size, fout ); 95 | fclose( fout ); 96 | /* done */ 97 | free( DDS_data ); 98 | return 1; 99 | } 100 | 101 | unsigned char* convert_image_to_DXT1( 102 | const unsigned char *const uncompressed, 103 | int width, int height, int channels, 104 | int *out_size ) 105 | { 106 | unsigned char *compressed; 107 | int i, j, x, y; 108 | unsigned char ublock[16*3]; 109 | unsigned char cblock[8]; 110 | int index = 0, chan_step = 1; 111 | int block_count = 0; 112 | /* error check */ 113 | *out_size = 0; 114 | if( (width < 1) || (height < 1) || 115 | (NULL == uncompressed) || 116 | (channels < 1) || (channels > 4) ) 117 | { 118 | return NULL; 119 | } 120 | /* for channels == 1 or 2, I do not step forward for R,G,B values */ 121 | if( channels < 3 ) 122 | { 123 | chan_step = 0; 124 | } 125 | /* get the RAM for the compressed image 126 | (8 bytes per 4x4 pixel block) */ 127 | *out_size = ((width+3) >> 2) * ((height+3) >> 2) * 8; 128 | compressed = (unsigned char*)malloc( *out_size ); 129 | /* go through each block */ 130 | for( j = 0; j < height; j += 4 ) 131 | { 132 | for( i = 0; i < width; i += 4 ) 133 | { 134 | /* copy this block into a new one */ 135 | int idx = 0; 136 | int mx = 4, my = 4; 137 | if( j+4 >= height ) 138 | { 139 | my = height - j; 140 | } 141 | if( i+4 >= width ) 142 | { 143 | mx = width - i; 144 | } 145 | for( y = 0; y < my; ++y ) 146 | { 147 | for( x = 0; x < mx; ++x ) 148 | { 149 | ublock[idx++] = uncompressed[(j+y)*width*channels+(i+x)*channels]; 150 | ublock[idx++] = uncompressed[(j+y)*width*channels+(i+x)*channels+chan_step]; 151 | ublock[idx++] = uncompressed[(j+y)*width*channels+(i+x)*channels+chan_step+chan_step]; 152 | } 153 | for( x = mx; x < 4; ++x ) 154 | { 155 | ublock[idx++] = ublock[0]; 156 | ublock[idx++] = ublock[1]; 157 | ublock[idx++] = ublock[2]; 158 | } 159 | } 160 | for( y = my; y < 4; ++y ) 161 | { 162 | for( x = 0; x < 4; ++x ) 163 | { 164 | ublock[idx++] = ublock[0]; 165 | ublock[idx++] = ublock[1]; 166 | ublock[idx++] = ublock[2]; 167 | } 168 | } 169 | /* compress the block */ 170 | ++block_count; 171 | compress_DDS_color_block( 3, ublock, cblock ); 172 | /* copy the data from the block into the main block */ 173 | for( x = 0; x < 8; ++x ) 174 | { 175 | compressed[index++] = cblock[x]; 176 | } 177 | } 178 | } 179 | return compressed; 180 | } 181 | 182 | unsigned char* convert_image_to_DXT5( 183 | const unsigned char *const uncompressed, 184 | int width, int height, int channels, 185 | int *out_size ) 186 | { 187 | unsigned char *compressed; 188 | int i, j, x, y; 189 | unsigned char ublock[16*4]; 190 | unsigned char cblock[8]; 191 | int index = 0, chan_step = 1; 192 | int block_count = 0, has_alpha; 193 | /* error check */ 194 | *out_size = 0; 195 | if( (width < 1) || (height < 1) || 196 | (NULL == uncompressed) || 197 | (channels < 1) || ( channels > 4) ) 198 | { 199 | return NULL; 200 | } 201 | /* for channels == 1 or 2, I do not step forward for R,G,B vales */ 202 | if( channels < 3 ) 203 | { 204 | chan_step = 0; 205 | } 206 | /* # channels = 1 or 3 have no alpha, 2 & 4 do have alpha */ 207 | has_alpha = 1 - (channels & 1); 208 | /* get the RAM for the compressed image 209 | (16 bytes per 4x4 pixel block) */ 210 | *out_size = ((width+3) >> 2) * ((height+3) >> 2) * 16; 211 | compressed = (unsigned char*)malloc( *out_size ); 212 | /* go through each block */ 213 | for( j = 0; j < height; j += 4 ) 214 | { 215 | for( i = 0; i < width; i += 4 ) 216 | { 217 | /* local variables, and my block counter */ 218 | int idx = 0; 219 | int mx = 4, my = 4; 220 | if( j+4 >= height ) 221 | { 222 | my = height - j; 223 | } 224 | if( i+4 >= width ) 225 | { 226 | mx = width - i; 227 | } 228 | for( y = 0; y < my; ++y ) 229 | { 230 | for( x = 0; x < mx; ++x ) 231 | { 232 | ublock[idx++] = uncompressed[(j+y)*width*channels+(i+x)*channels]; 233 | ublock[idx++] = uncompressed[(j+y)*width*channels+(i+x)*channels+chan_step]; 234 | ublock[idx++] = uncompressed[(j+y)*width*channels+(i+x)*channels+chan_step+chan_step]; 235 | ublock[idx++] = 236 | has_alpha * uncompressed[(j+y)*width*channels+(i+x)*channels+channels-1] 237 | + (1-has_alpha)*255; 238 | } 239 | for( x = mx; x < 4; ++x ) 240 | { 241 | ublock[idx++] = ublock[0]; 242 | ublock[idx++] = ublock[1]; 243 | ublock[idx++] = ublock[2]; 244 | ublock[idx++] = ublock[3]; 245 | } 246 | } 247 | for( y = my; y < 4; ++y ) 248 | { 249 | for( x = 0; x < 4; ++x ) 250 | { 251 | ublock[idx++] = ublock[0]; 252 | ublock[idx++] = ublock[1]; 253 | ublock[idx++] = ublock[2]; 254 | ublock[idx++] = ublock[3]; 255 | } 256 | } 257 | /* now compress the alpha block */ 258 | compress_DDS_alpha_block( ublock, cblock ); 259 | /* copy the data from the compressed alpha block into the main buffer */ 260 | for( x = 0; x < 8; ++x ) 261 | { 262 | compressed[index++] = cblock[x]; 263 | } 264 | /* then compress the color block */ 265 | ++block_count; 266 | compress_DDS_color_block( 4, ublock, cblock ); 267 | /* copy the data from the compressed color block into the main buffer */ 268 | for( x = 0; x < 8; ++x ) 269 | { 270 | compressed[index++] = cblock[x]; 271 | } 272 | } 273 | } 274 | return compressed; 275 | } 276 | 277 | /********* Helper Functions *********/ 278 | int convert_bit_range( int c, int from_bits, int to_bits ) 279 | { 280 | int b = (1 << (from_bits - 1)) + c * ((1 << to_bits) - 1); 281 | return (b + (b >> from_bits)) >> from_bits; 282 | } 283 | 284 | int rgb_to_565( int r, int g, int b ) 285 | { 286 | return 287 | (convert_bit_range( r, 8, 5 ) << 11) | 288 | (convert_bit_range( g, 8, 6 ) << 05) | 289 | (convert_bit_range( b, 8, 5 ) << 00); 290 | } 291 | 292 | void rgb_888_from_565( unsigned int c, int *r, int *g, int *b ) 293 | { 294 | *r = convert_bit_range( (c >> 11) & 31, 5, 8 ); 295 | *g = convert_bit_range( (c >> 05) & 63, 6, 8 ); 296 | *b = convert_bit_range( (c >> 00) & 31, 5, 8 ); 297 | } 298 | 299 | void compute_color_line_STDEV( 300 | const unsigned char *const uncompressed, 301 | int channels, 302 | float point[3], float direction[3] ) 303 | { 304 | const float inv_16 = 1.0f / 16.0f; 305 | int i; 306 | float sum_r = 0.0f, sum_g = 0.0f, sum_b = 0.0f; 307 | float sum_rr = 0.0f, sum_gg = 0.0f, sum_bb = 0.0f; 308 | float sum_rg = 0.0f, sum_rb = 0.0f, sum_gb = 0.0f; 309 | /* calculate all data needed for the covariance matrix 310 | ( to compare with _rygdxt code) */ 311 | for( i = 0; i < 16*channels; i += channels ) 312 | { 313 | sum_r += uncompressed[i+0]; 314 | sum_rr += uncompressed[i+0] * uncompressed[i+0]; 315 | sum_g += uncompressed[i+1]; 316 | sum_gg += uncompressed[i+1] * uncompressed[i+1]; 317 | sum_b += uncompressed[i+2]; 318 | sum_bb += uncompressed[i+2] * uncompressed[i+2]; 319 | sum_rg += uncompressed[i+0] * uncompressed[i+1]; 320 | sum_rb += uncompressed[i+0] * uncompressed[i+2]; 321 | sum_gb += uncompressed[i+1] * uncompressed[i+2]; 322 | } 323 | /* convert the sums to averages */ 324 | sum_r *= inv_16; 325 | sum_g *= inv_16; 326 | sum_b *= inv_16; 327 | /* and convert the squares to the squares of the value - avg_value */ 328 | sum_rr -= 16.0f * sum_r * sum_r; 329 | sum_gg -= 16.0f * sum_g * sum_g; 330 | sum_bb -= 16.0f * sum_b * sum_b; 331 | sum_rg -= 16.0f * sum_r * sum_g; 332 | sum_rb -= 16.0f * sum_r * sum_b; 333 | sum_gb -= 16.0f * sum_g * sum_b; 334 | /* the point on the color line is the average */ 335 | point[0] = sum_r; 336 | point[1] = sum_g; 337 | point[2] = sum_b; 338 | #if USE_COV_MAT 339 | /* 340 | The following idea was from ryg. 341 | (https://mollyrocket.com/forums/viewtopic.php?t=392) 342 | The method worked great (less RMSE than mine) most of 343 | the time, but had some issues handling some simple 344 | boundary cases, like full green next to full red, 345 | which would generate a covariance matrix like this: 346 | 347 | | 1 -1 0 | 348 | | -1 1 0 | 349 | | 0 0 0 | 350 | 351 | For a given starting vector, the power method can 352 | generate all zeros! So no starting with {1,1,1} 353 | as I was doing! This kind of error is still a 354 | slight posibillity, but will be very rare. 355 | */ 356 | /* use the covariance matrix directly 357 | (1st iteration, don't use all 1.0 values!) */ 358 | sum_r = 1.0f; 359 | sum_g = 2.718281828f; 360 | sum_b = 3.141592654f; 361 | direction[0] = sum_r*sum_rr + sum_g*sum_rg + sum_b*sum_rb; 362 | direction[1] = sum_r*sum_rg + sum_g*sum_gg + sum_b*sum_gb; 363 | direction[2] = sum_r*sum_rb + sum_g*sum_gb + sum_b*sum_bb; 364 | /* 2nd iteration, use results from the 1st guy */ 365 | sum_r = direction[0]; 366 | sum_g = direction[1]; 367 | sum_b = direction[2]; 368 | direction[0] = sum_r*sum_rr + sum_g*sum_rg + sum_b*sum_rb; 369 | direction[1] = sum_r*sum_rg + sum_g*sum_gg + sum_b*sum_gb; 370 | direction[2] = sum_r*sum_rb + sum_g*sum_gb + sum_b*sum_bb; 371 | /* 3rd iteration, use results from the 2nd guy */ 372 | sum_r = direction[0]; 373 | sum_g = direction[1]; 374 | sum_b = direction[2]; 375 | direction[0] = sum_r*sum_rr + sum_g*sum_rg + sum_b*sum_rb; 376 | direction[1] = sum_r*sum_rg + sum_g*sum_gg + sum_b*sum_gb; 377 | direction[2] = sum_r*sum_rb + sum_g*sum_gb + sum_b*sum_bb; 378 | #else 379 | /* use my standard deviation method 380 | (very robust, a tiny bit slower and less accurate) */ 381 | direction[0] = sqrt( sum_rr ); 382 | direction[1] = sqrt( sum_gg ); 383 | direction[2] = sqrt( sum_bb ); 384 | /* which has a greater component */ 385 | if( sum_gg > sum_rr ) 386 | { 387 | /* green has greater component, so base the other signs off of green */ 388 | if( sum_rg < 0.0f ) 389 | { 390 | direction[0] = -direction[0]; 391 | } 392 | if( sum_gb < 0.0f ) 393 | { 394 | direction[2] = -direction[2]; 395 | } 396 | } else 397 | { 398 | /* red has a greater component */ 399 | if( sum_rg < 0.0f ) 400 | { 401 | direction[1] = -direction[1]; 402 | } 403 | if( sum_rb < 0.0f ) 404 | { 405 | direction[2] = -direction[2]; 406 | } 407 | } 408 | #endif 409 | } 410 | 411 | void LSE_master_colors_max_min( 412 | int *cmax, int *cmin, 413 | int channels, 414 | const unsigned char *const uncompressed ) 415 | { 416 | int i, j; 417 | /* the master colors */ 418 | int c0[3], c1[3]; 419 | /* used for fitting the line */ 420 | float sum_x[] = { 0.0f, 0.0f, 0.0f }; 421 | float sum_x2[] = { 0.0f, 0.0f, 0.0f }; 422 | float dot_max = 1.0f, dot_min = -1.0f; 423 | float vec_len2 = 0.0f; 424 | float dot; 425 | /* error check */ 426 | if( (channels < 3) || (channels > 4) ) 427 | { 428 | return; 429 | } 430 | compute_color_line_STDEV( uncompressed, channels, sum_x, sum_x2 ); 431 | vec_len2 = 1.0f / ( 0.00001f + 432 | sum_x2[0]*sum_x2[0] + sum_x2[1]*sum_x2[1] + sum_x2[2]*sum_x2[2] ); 433 | /* finding the max and min vector values */ 434 | dot_max = 435 | ( 436 | sum_x2[0] * uncompressed[0] + 437 | sum_x2[1] * uncompressed[1] + 438 | sum_x2[2] * uncompressed[2] 439 | ); 440 | dot_min = dot_max; 441 | for( i = 1; i < 16; ++i ) 442 | { 443 | dot = 444 | ( 445 | sum_x2[0] * uncompressed[i*channels+0] + 446 | sum_x2[1] * uncompressed[i*channels+1] + 447 | sum_x2[2] * uncompressed[i*channels+2] 448 | ); 449 | if( dot < dot_min ) 450 | { 451 | dot_min = dot; 452 | } else if( dot > dot_max ) 453 | { 454 | dot_max = dot; 455 | } 456 | } 457 | /* and the offset (from the average location) */ 458 | dot = sum_x2[0]*sum_x[0] + sum_x2[1]*sum_x[1] + sum_x2[2]*sum_x[2]; 459 | dot_min -= dot; 460 | dot_max -= dot; 461 | /* post multiply by the scaling factor */ 462 | dot_min *= vec_len2; 463 | dot_max *= vec_len2; 464 | /* OK, build the master colors */ 465 | for( i = 0; i < 3; ++i ) 466 | { 467 | /* color 0 */ 468 | c0[i] = (int)(0.5f + sum_x[i] + dot_max * sum_x2[i]); 469 | if( c0[i] < 0 ) 470 | { 471 | c0[i] = 0; 472 | } else if( c0[i] > 255 ) 473 | { 474 | c0[i] = 255; 475 | } 476 | /* color 1 */ 477 | c1[i] = (int)(0.5f + sum_x[i] + dot_min * sum_x2[i]); 478 | if( c1[i] < 0 ) 479 | { 480 | c1[i] = 0; 481 | } else if( c1[i] > 255 ) 482 | { 483 | c1[i] = 255; 484 | } 485 | } 486 | /* down_sample (with rounding?) */ 487 | i = rgb_to_565( c0[0], c0[1], c0[2] ); 488 | j = rgb_to_565( c1[0], c1[1], c1[2] ); 489 | if( i > j ) 490 | { 491 | *cmax = i; 492 | *cmin = j; 493 | } else 494 | { 495 | *cmax = j; 496 | *cmin = i; 497 | } 498 | } 499 | 500 | void 501 | compress_DDS_color_block 502 | ( 503 | int channels, 504 | const unsigned char *const uncompressed, 505 | unsigned char compressed[8] 506 | ) 507 | { 508 | /* variables */ 509 | int i; 510 | int next_bit; 511 | int enc_c0, enc_c1; 512 | int c0[4], c1[4]; 513 | float color_line[] = { 0.0f, 0.0f, 0.0f, 0.0f }; 514 | float vec_len2 = 0.0f, dot_offset = 0.0f; 515 | /* stupid order */ 516 | int swizzle4[] = { 0, 2, 3, 1 }; 517 | /* get the master colors */ 518 | LSE_master_colors_max_min( &enc_c0, &enc_c1, channels, uncompressed ); 519 | /* store the 565 color 0 and color 1 */ 520 | compressed[0] = (enc_c0 >> 0) & 255; 521 | compressed[1] = (enc_c0 >> 8) & 255; 522 | compressed[2] = (enc_c1 >> 0) & 255; 523 | compressed[3] = (enc_c1 >> 8) & 255; 524 | /* zero out the compressed data */ 525 | compressed[4] = 0; 526 | compressed[5] = 0; 527 | compressed[6] = 0; 528 | compressed[7] = 0; 529 | /* reconstitute the master color vectors */ 530 | rgb_888_from_565( enc_c0, &c0[0], &c0[1], &c0[2] ); 531 | rgb_888_from_565( enc_c1, &c1[0], &c1[1], &c1[2] ); 532 | /* the new vector */ 533 | vec_len2 = 0.0f; 534 | for( i = 0; i < 3; ++i ) 535 | { 536 | color_line[i] = (float)(c1[i] - c0[i]); 537 | vec_len2 += color_line[i] * color_line[i]; 538 | } 539 | if( vec_len2 > 0.0f ) 540 | { 541 | vec_len2 = 1.0f / vec_len2; 542 | } 543 | /* pre-proform the scaling */ 544 | color_line[0] *= vec_len2; 545 | color_line[1] *= vec_len2; 546 | color_line[2] *= vec_len2; 547 | /* compute the offset (constant) portion of the dot product */ 548 | dot_offset = color_line[0]*c0[0] + color_line[1]*c0[1] + color_line[2]*c0[2]; 549 | /* store the rest of the bits */ 550 | next_bit = 8*4; 551 | for( i = 0; i < 16; ++i ) 552 | { 553 | /* find the dot product of this color, to place it on the line 554 | (should be [-1,1]) */ 555 | int next_value = 0; 556 | float dot_product = 557 | color_line[0] * uncompressed[i*channels+0] + 558 | color_line[1] * uncompressed[i*channels+1] + 559 | color_line[2] * uncompressed[i*channels+2] - 560 | dot_offset; 561 | /* map to [0,3] */ 562 | next_value = (int)( dot_product * 3.0f + 0.5f ); 563 | if( next_value > 3 ) 564 | { 565 | next_value = 3; 566 | } else if( next_value < 0 ) 567 | { 568 | next_value = 0; 569 | } 570 | /* OK, store this value */ 571 | compressed[next_bit >> 3] |= swizzle4[ next_value ] << (next_bit & 7); 572 | next_bit += 2; 573 | } 574 | /* done compressing to DXT1 */ 575 | } 576 | 577 | void 578 | compress_DDS_alpha_block 579 | ( 580 | const unsigned char *const uncompressed, 581 | unsigned char compressed[8] 582 | ) 583 | { 584 | /* variables */ 585 | int i; 586 | int next_bit; 587 | int a0, a1; 588 | float scale_me; 589 | /* stupid order */ 590 | int swizzle8[] = { 1, 7, 6, 5, 4, 3, 2, 0 }; 591 | /* get the alpha limits (a0 > a1) */ 592 | a0 = a1 = uncompressed[3]; 593 | for( i = 4+3; i < 16*4; i += 4 ) 594 | { 595 | if( uncompressed[i] > a0 ) 596 | { 597 | a0 = uncompressed[i]; 598 | } else if( uncompressed[i] < a1 ) 599 | { 600 | a1 = uncompressed[i]; 601 | } 602 | } 603 | /* store those limits, and zero the rest of the compressed dataset */ 604 | compressed[0] = a0; 605 | compressed[1] = a1; 606 | /* zero out the compressed data */ 607 | compressed[2] = 0; 608 | compressed[3] = 0; 609 | compressed[4] = 0; 610 | compressed[5] = 0; 611 | compressed[6] = 0; 612 | compressed[7] = 0; 613 | /* store the all of the alpha values */ 614 | next_bit = 8*2; 615 | scale_me = 7.9999f / (a0 - a1); 616 | for( i = 3; i < 16*4; i += 4 ) 617 | { 618 | /* convert this alpha value to a 3 bit number */ 619 | int svalue; 620 | int value = (int)((uncompressed[i] - a1) * scale_me); 621 | svalue = swizzle8[ value&7 ]; 622 | /* OK, store this value, start with the 1st byte */ 623 | compressed[next_bit >> 3] |= svalue << (next_bit & 7); 624 | if( (next_bit & 7) > 5 ) 625 | { 626 | /* spans 2 bytes, fill in the start of the 2nd byte */ 627 | compressed[1 + (next_bit >> 3)] |= svalue >> (8 - (next_bit & 7) ); 628 | } 629 | next_bit += 3; 630 | } 631 | /* done compressing to DXT1 */ 632 | } 633 | -------------------------------------------------------------------------------- /src/SOIL.c: -------------------------------------------------------------------------------- 1 | /* 2 | Jonathan Dummer 3 | 2007-07-26-10.36 4 | 5 | Simple OpenGL Image Library 6 | 7 | Public Domain 8 | using Sean Barret's stb_image as a base 9 | 10 | Thanks to: 11 | * Sean Barret - for the awesome stb_image 12 | * Dan Venkitachalam - for finding some non-compliant DDS files, and patching some explicit casts 13 | * everybody at gamedev.net 14 | */ 15 | 16 | #define SOIL_CHECK_FOR_GL_ERRORS 0 17 | 18 | #ifdef WIN32 19 | #define WIN32_LEAN_AND_MEAN 20 | #include 21 | #include 22 | #include 23 | #elif defined(__APPLE__) || defined(__APPLE_CC__) 24 | /* I can't test this Apple stuff! */ 25 | #include 26 | #include 27 | #define APIENTRY 28 | #else 29 | #include 30 | #include 31 | #endif 32 | 33 | #include "SOIL/SOIL.h" 34 | #include "SOIL/stb_image_aug.h" 35 | #include "SOIL/image_helper.h" 36 | #include "SOIL/image_DXT.h" 37 | 38 | #include 39 | #include 40 | 41 | /* error reporting */ 42 | char *result_string_pointer = "SOIL initialized"; 43 | 44 | /* for loading cube maps */ 45 | enum{ 46 | SOIL_CAPABILITY_UNKNOWN = -1, 47 | SOIL_CAPABILITY_NONE = 0, 48 | SOIL_CAPABILITY_PRESENT = 1 49 | }; 50 | static int has_cubemap_capability = SOIL_CAPABILITY_UNKNOWN; 51 | int query_cubemap_capability( void ); 52 | #define SOIL_TEXTURE_WRAP_R 0x8072 53 | #define SOIL_CLAMP_TO_EDGE 0x812F 54 | #define SOIL_NORMAL_MAP 0x8511 55 | #define SOIL_REFLECTION_MAP 0x8512 56 | #define SOIL_TEXTURE_CUBE_MAP 0x8513 57 | #define SOIL_TEXTURE_BINDING_CUBE_MAP 0x8514 58 | #define SOIL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515 59 | #define SOIL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516 60 | #define SOIL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517 61 | #define SOIL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518 62 | #define SOIL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519 63 | #define SOIL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A 64 | #define SOIL_PROXY_TEXTURE_CUBE_MAP 0x851B 65 | #define SOIL_MAX_CUBE_MAP_TEXTURE_SIZE 0x851C 66 | /* for non-power-of-two texture */ 67 | static int has_NPOT_capability = SOIL_CAPABILITY_UNKNOWN; 68 | int query_NPOT_capability( void ); 69 | /* for texture rectangles */ 70 | static int has_tex_rectangle_capability = SOIL_CAPABILITY_UNKNOWN; 71 | int query_tex_rectangle_capability( void ); 72 | #define SOIL_TEXTURE_RECTANGLE_ARB 0x84F5 73 | #define SOIL_MAX_RECTANGLE_TEXTURE_SIZE_ARB 0x84F8 74 | /* for using DXT compression */ 75 | static int has_DXT_capability = SOIL_CAPABILITY_UNKNOWN; 76 | int query_DXT_capability( void ); 77 | #define SOIL_RGB_S3TC_DXT1 0x83F0 78 | #define SOIL_RGBA_S3TC_DXT1 0x83F1 79 | #define SOIL_RGBA_S3TC_DXT3 0x83F2 80 | #define SOIL_RGBA_S3TC_DXT5 0x83F3 81 | typedef void (APIENTRY * P_SOIL_GLCOMPRESSEDTEXIMAGE2DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid * data); 82 | P_SOIL_GLCOMPRESSEDTEXIMAGE2DPROC soilGlCompressedTexImage2D = NULL; 83 | unsigned int SOIL_direct_load_DDS( 84 | const char *filename, 85 | unsigned int reuse_texture_ID, 86 | int flags, 87 | int loading_as_cubemap ); 88 | unsigned int SOIL_direct_load_DDS_from_memory( 89 | const unsigned char *const buffer, 90 | int buffer_length, 91 | unsigned int reuse_texture_ID, 92 | int flags, 93 | int loading_as_cubemap ); 94 | /* other functions */ 95 | unsigned int 96 | SOIL_internal_create_OGL_texture 97 | ( 98 | const unsigned char *const data, 99 | int width, int height, int channels, 100 | unsigned int reuse_texture_ID, 101 | unsigned int flags, 102 | unsigned int opengl_texture_type, 103 | unsigned int opengl_texture_target, 104 | unsigned int texture_check_size_enum 105 | ); 106 | 107 | /* and the code magic begins here [8^) */ 108 | unsigned int 109 | SOIL_load_OGL_texture 110 | ( 111 | const char *filename, 112 | int force_channels, 113 | unsigned int reuse_texture_ID, 114 | unsigned int flags 115 | ) 116 | { 117 | /* variables */ 118 | unsigned char* img; 119 | int width, height, channels; 120 | unsigned int tex_id; 121 | /* does the user want direct uploading of the image as a DDS file? */ 122 | if( flags & SOIL_FLAG_DDS_LOAD_DIRECT ) 123 | { 124 | /* 1st try direct loading of the image as a DDS file 125 | note: direct uploading will only load what is in the 126 | DDS file, no MIPmaps will be generated, the image will 127 | not be flipped, etc. */ 128 | tex_id = SOIL_direct_load_DDS( filename, reuse_texture_ID, flags, 0 ); 129 | if( tex_id ) 130 | { 131 | /* hey, it worked!! */ 132 | return tex_id; 133 | } 134 | } 135 | /* try to load the image */ 136 | img = SOIL_load_image( filename, &width, &height, &channels, force_channels ); 137 | /* channels holds the original number of channels, which may have been forced */ 138 | if( (force_channels >= 1) && (force_channels <= 4) ) 139 | { 140 | channels = force_channels; 141 | } 142 | if( NULL == img ) 143 | { 144 | /* image loading failed */ 145 | result_string_pointer = stbi_failure_reason(); 146 | return 0; 147 | } 148 | /* OK, make it a texture! */ 149 | tex_id = SOIL_internal_create_OGL_texture( 150 | img, width, height, channels, 151 | reuse_texture_ID, flags, 152 | GL_TEXTURE_2D, GL_TEXTURE_2D, 153 | GL_MAX_TEXTURE_SIZE ); 154 | /* and nuke the image data */ 155 | SOIL_free_image_data( img ); 156 | /* and return the handle, such as it is */ 157 | return tex_id; 158 | } 159 | 160 | unsigned int 161 | SOIL_load_OGL_HDR_texture 162 | ( 163 | const char *filename, 164 | int fake_HDR_format, 165 | int rescale_to_max, 166 | unsigned int reuse_texture_ID, 167 | unsigned int flags 168 | ) 169 | { 170 | /* variables */ 171 | unsigned char* img; 172 | int width, height, channels; 173 | unsigned int tex_id; 174 | /* no direct uploading of the image as a DDS file */ 175 | /* error check */ 176 | if( (fake_HDR_format != SOIL_HDR_RGBE) && 177 | (fake_HDR_format != SOIL_HDR_RGBdivA) && 178 | (fake_HDR_format != SOIL_HDR_RGBdivA2) ) 179 | { 180 | result_string_pointer = "Invalid fake HDR format specified"; 181 | return 0; 182 | } 183 | /* try to load the image (only the HDR type) */ 184 | img = stbi_hdr_load_rgbe( filename, &width, &height, &channels, 4 ); 185 | /* channels holds the original number of channels, which may have been forced */ 186 | if( NULL == img ) 187 | { 188 | /* image loading failed */ 189 | result_string_pointer = stbi_failure_reason(); 190 | return 0; 191 | } 192 | /* the load worked, do I need to convert it? */ 193 | if( fake_HDR_format == SOIL_HDR_RGBdivA ) 194 | { 195 | RGBE_to_RGBdivA( img, width, height, rescale_to_max ); 196 | } else if( fake_HDR_format == SOIL_HDR_RGBdivA2 ) 197 | { 198 | RGBE_to_RGBdivA2( img, width, height, rescale_to_max ); 199 | } 200 | /* OK, make it a texture! */ 201 | tex_id = SOIL_internal_create_OGL_texture( 202 | img, width, height, channels, 203 | reuse_texture_ID, flags, 204 | GL_TEXTURE_2D, GL_TEXTURE_2D, 205 | GL_MAX_TEXTURE_SIZE ); 206 | /* and nuke the image data */ 207 | SOIL_free_image_data( img ); 208 | /* and return the handle, such as it is */ 209 | return tex_id; 210 | } 211 | 212 | unsigned int 213 | SOIL_load_OGL_texture_from_memory 214 | ( 215 | const unsigned char *const buffer, 216 | int buffer_length, 217 | int force_channels, 218 | unsigned int reuse_texture_ID, 219 | unsigned int flags 220 | ) 221 | { 222 | /* variables */ 223 | unsigned char* img; 224 | int width, height, channels; 225 | unsigned int tex_id; 226 | /* does the user want direct uploading of the image as a DDS file? */ 227 | if( flags & SOIL_FLAG_DDS_LOAD_DIRECT ) 228 | { 229 | /* 1st try direct loading of the image as a DDS file 230 | note: direct uploading will only load what is in the 231 | DDS file, no MIPmaps will be generated, the image will 232 | not be flipped, etc. */ 233 | tex_id = SOIL_direct_load_DDS_from_memory( 234 | buffer, buffer_length, 235 | reuse_texture_ID, flags, 0 ); 236 | if( tex_id ) 237 | { 238 | /* hey, it worked!! */ 239 | return tex_id; 240 | } 241 | } 242 | /* try to load the image */ 243 | img = SOIL_load_image_from_memory( 244 | buffer, buffer_length, 245 | &width, &height, &channels, 246 | force_channels ); 247 | /* channels holds the original number of channels, which may have been forced */ 248 | if( (force_channels >= 1) && (force_channels <= 4) ) 249 | { 250 | channels = force_channels; 251 | } 252 | if( NULL == img ) 253 | { 254 | /* image loading failed */ 255 | result_string_pointer = stbi_failure_reason(); 256 | return 0; 257 | } 258 | /* OK, make it a texture! */ 259 | tex_id = SOIL_internal_create_OGL_texture( 260 | img, width, height, channels, 261 | reuse_texture_ID, flags, 262 | GL_TEXTURE_2D, GL_TEXTURE_2D, 263 | GL_MAX_TEXTURE_SIZE ); 264 | /* and nuke the image data */ 265 | SOIL_free_image_data( img ); 266 | /* and return the handle, such as it is */ 267 | return tex_id; 268 | } 269 | 270 | unsigned int 271 | SOIL_load_OGL_cubemap 272 | ( 273 | const char *x_pos_file, 274 | const char *x_neg_file, 275 | const char *y_pos_file, 276 | const char *y_neg_file, 277 | const char *z_pos_file, 278 | const char *z_neg_file, 279 | int force_channels, 280 | unsigned int reuse_texture_ID, 281 | unsigned int flags 282 | ) 283 | { 284 | /* variables */ 285 | unsigned char* img; 286 | int width, height, channels; 287 | unsigned int tex_id; 288 | /* error checking */ 289 | if( (x_pos_file == NULL) || 290 | (x_neg_file == NULL) || 291 | (y_pos_file == NULL) || 292 | (y_neg_file == NULL) || 293 | (z_pos_file == NULL) || 294 | (z_neg_file == NULL) ) 295 | { 296 | result_string_pointer = "Invalid cube map files list"; 297 | return 0; 298 | } 299 | /* capability checking */ 300 | if( query_cubemap_capability() != SOIL_CAPABILITY_PRESENT ) 301 | { 302 | result_string_pointer = "No cube map capability present"; 303 | return 0; 304 | } 305 | /* 1st face: try to load the image */ 306 | img = SOIL_load_image( x_pos_file, &width, &height, &channels, force_channels ); 307 | /* channels holds the original number of channels, which may have been forced */ 308 | if( (force_channels >= 1) && (force_channels <= 4) ) 309 | { 310 | channels = force_channels; 311 | } 312 | if( NULL == img ) 313 | { 314 | /* image loading failed */ 315 | result_string_pointer = stbi_failure_reason(); 316 | return 0; 317 | } 318 | /* upload the texture, and create a texture ID if necessary */ 319 | tex_id = SOIL_internal_create_OGL_texture( 320 | img, width, height, channels, 321 | reuse_texture_ID, flags, 322 | SOIL_TEXTURE_CUBE_MAP, SOIL_TEXTURE_CUBE_MAP_POSITIVE_X, 323 | SOIL_MAX_CUBE_MAP_TEXTURE_SIZE ); 324 | /* and nuke the image data */ 325 | SOIL_free_image_data( img ); 326 | /* continue? */ 327 | if( tex_id != 0 ) 328 | { 329 | /* 1st face: try to load the image */ 330 | img = SOIL_load_image( x_neg_file, &width, &height, &channels, force_channels ); 331 | /* channels holds the original number of channels, which may have been forced */ 332 | if( (force_channels >= 1) && (force_channels <= 4) ) 333 | { 334 | channels = force_channels; 335 | } 336 | if( NULL == img ) 337 | { 338 | /* image loading failed */ 339 | result_string_pointer = stbi_failure_reason(); 340 | return 0; 341 | } 342 | /* upload the texture, but reuse the assigned texture ID */ 343 | tex_id = SOIL_internal_create_OGL_texture( 344 | img, width, height, channels, 345 | tex_id, flags, 346 | SOIL_TEXTURE_CUBE_MAP, SOIL_TEXTURE_CUBE_MAP_NEGATIVE_X, 347 | SOIL_MAX_CUBE_MAP_TEXTURE_SIZE ); 348 | /* and nuke the image data */ 349 | SOIL_free_image_data( img ); 350 | } 351 | /* continue? */ 352 | if( tex_id != 0 ) 353 | { 354 | /* 1st face: try to load the image */ 355 | img = SOIL_load_image( y_pos_file, &width, &height, &channels, force_channels ); 356 | /* channels holds the original number of channels, which may have been forced */ 357 | if( (force_channels >= 1) && (force_channels <= 4) ) 358 | { 359 | channels = force_channels; 360 | } 361 | if( NULL == img ) 362 | { 363 | /* image loading failed */ 364 | result_string_pointer = stbi_failure_reason(); 365 | return 0; 366 | } 367 | /* upload the texture, but reuse the assigned texture ID */ 368 | tex_id = SOIL_internal_create_OGL_texture( 369 | img, width, height, channels, 370 | tex_id, flags, 371 | SOIL_TEXTURE_CUBE_MAP, SOIL_TEXTURE_CUBE_MAP_POSITIVE_Y, 372 | SOIL_MAX_CUBE_MAP_TEXTURE_SIZE ); 373 | /* and nuke the image data */ 374 | SOIL_free_image_data( img ); 375 | } 376 | /* continue? */ 377 | if( tex_id != 0 ) 378 | { 379 | /* 1st face: try to load the image */ 380 | img = SOIL_load_image( y_neg_file, &width, &height, &channels, force_channels ); 381 | /* channels holds the original number of channels, which may have been forced */ 382 | if( (force_channels >= 1) && (force_channels <= 4) ) 383 | { 384 | channels = force_channels; 385 | } 386 | if( NULL == img ) 387 | { 388 | /* image loading failed */ 389 | result_string_pointer = stbi_failure_reason(); 390 | return 0; 391 | } 392 | /* upload the texture, but reuse the assigned texture ID */ 393 | tex_id = SOIL_internal_create_OGL_texture( 394 | img, width, height, channels, 395 | tex_id, flags, 396 | SOIL_TEXTURE_CUBE_MAP, SOIL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 397 | SOIL_MAX_CUBE_MAP_TEXTURE_SIZE ); 398 | /* and nuke the image data */ 399 | SOIL_free_image_data( img ); 400 | } 401 | /* continue? */ 402 | if( tex_id != 0 ) 403 | { 404 | /* 1st face: try to load the image */ 405 | img = SOIL_load_image( z_pos_file, &width, &height, &channels, force_channels ); 406 | /* channels holds the original number of channels, which may have been forced */ 407 | if( (force_channels >= 1) && (force_channels <= 4) ) 408 | { 409 | channels = force_channels; 410 | } 411 | if( NULL == img ) 412 | { 413 | /* image loading failed */ 414 | result_string_pointer = stbi_failure_reason(); 415 | return 0; 416 | } 417 | /* upload the texture, but reuse the assigned texture ID */ 418 | tex_id = SOIL_internal_create_OGL_texture( 419 | img, width, height, channels, 420 | tex_id, flags, 421 | SOIL_TEXTURE_CUBE_MAP, SOIL_TEXTURE_CUBE_MAP_POSITIVE_Z, 422 | SOIL_MAX_CUBE_MAP_TEXTURE_SIZE ); 423 | /* and nuke the image data */ 424 | SOIL_free_image_data( img ); 425 | } 426 | /* continue? */ 427 | if( tex_id != 0 ) 428 | { 429 | /* 1st face: try to load the image */ 430 | img = SOIL_load_image( z_neg_file, &width, &height, &channels, force_channels ); 431 | /* channels holds the original number of channels, which may have been forced */ 432 | if( (force_channels >= 1) && (force_channels <= 4) ) 433 | { 434 | channels = force_channels; 435 | } 436 | if( NULL == img ) 437 | { 438 | /* image loading failed */ 439 | result_string_pointer = stbi_failure_reason(); 440 | return 0; 441 | } 442 | /* upload the texture, but reuse the assigned texture ID */ 443 | tex_id = SOIL_internal_create_OGL_texture( 444 | img, width, height, channels, 445 | tex_id, flags, 446 | SOIL_TEXTURE_CUBE_MAP, SOIL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 447 | SOIL_MAX_CUBE_MAP_TEXTURE_SIZE ); 448 | /* and nuke the image data */ 449 | SOIL_free_image_data( img ); 450 | } 451 | /* and return the handle, such as it is */ 452 | return tex_id; 453 | } 454 | 455 | unsigned int 456 | SOIL_load_OGL_cubemap_from_memory 457 | ( 458 | const unsigned char *const x_pos_buffer, 459 | int x_pos_buffer_length, 460 | const unsigned char *const x_neg_buffer, 461 | int x_neg_buffer_length, 462 | const unsigned char *const y_pos_buffer, 463 | int y_pos_buffer_length, 464 | const unsigned char *const y_neg_buffer, 465 | int y_neg_buffer_length, 466 | const unsigned char *const z_pos_buffer, 467 | int z_pos_buffer_length, 468 | const unsigned char *const z_neg_buffer, 469 | int z_neg_buffer_length, 470 | int force_channels, 471 | unsigned int reuse_texture_ID, 472 | unsigned int flags 473 | ) 474 | { 475 | /* variables */ 476 | unsigned char* img; 477 | int width, height, channels; 478 | unsigned int tex_id; 479 | /* error checking */ 480 | if( (x_pos_buffer == NULL) || 481 | (x_neg_buffer == NULL) || 482 | (y_pos_buffer == NULL) || 483 | (y_neg_buffer == NULL) || 484 | (z_pos_buffer == NULL) || 485 | (z_neg_buffer == NULL) ) 486 | { 487 | result_string_pointer = "Invalid cube map buffers list"; 488 | return 0; 489 | } 490 | /* capability checking */ 491 | if( query_cubemap_capability() != SOIL_CAPABILITY_PRESENT ) 492 | { 493 | result_string_pointer = "No cube map capability present"; 494 | return 0; 495 | } 496 | /* 1st face: try to load the image */ 497 | img = SOIL_load_image_from_memory( 498 | x_pos_buffer, x_pos_buffer_length, 499 | &width, &height, &channels, force_channels ); 500 | /* channels holds the original number of channels, which may have been forced */ 501 | if( (force_channels >= 1) && (force_channels <= 4) ) 502 | { 503 | channels = force_channels; 504 | } 505 | if( NULL == img ) 506 | { 507 | /* image loading failed */ 508 | result_string_pointer = stbi_failure_reason(); 509 | return 0; 510 | } 511 | /* upload the texture, and create a texture ID if necessary */ 512 | tex_id = SOIL_internal_create_OGL_texture( 513 | img, width, height, channels, 514 | reuse_texture_ID, flags, 515 | SOIL_TEXTURE_CUBE_MAP, SOIL_TEXTURE_CUBE_MAP_POSITIVE_X, 516 | SOIL_MAX_CUBE_MAP_TEXTURE_SIZE ); 517 | /* and nuke the image data */ 518 | SOIL_free_image_data( img ); 519 | /* continue? */ 520 | if( tex_id != 0 ) 521 | { 522 | /* 1st face: try to load the image */ 523 | img = SOIL_load_image_from_memory( 524 | x_neg_buffer, x_neg_buffer_length, 525 | &width, &height, &channels, force_channels ); 526 | /* channels holds the original number of channels, which may have been forced */ 527 | if( (force_channels >= 1) && (force_channels <= 4) ) 528 | { 529 | channels = force_channels; 530 | } 531 | if( NULL == img ) 532 | { 533 | /* image loading failed */ 534 | result_string_pointer = stbi_failure_reason(); 535 | return 0; 536 | } 537 | /* upload the texture, but reuse the assigned texture ID */ 538 | tex_id = SOIL_internal_create_OGL_texture( 539 | img, width, height, channels, 540 | tex_id, flags, 541 | SOIL_TEXTURE_CUBE_MAP, SOIL_TEXTURE_CUBE_MAP_NEGATIVE_X, 542 | SOIL_MAX_CUBE_MAP_TEXTURE_SIZE ); 543 | /* and nuke the image data */ 544 | SOIL_free_image_data( img ); 545 | } 546 | /* continue? */ 547 | if( tex_id != 0 ) 548 | { 549 | /* 1st face: try to load the image */ 550 | img = SOIL_load_image_from_memory( 551 | y_pos_buffer, y_pos_buffer_length, 552 | &width, &height, &channels, force_channels ); 553 | /* channels holds the original number of channels, which may have been forced */ 554 | if( (force_channels >= 1) && (force_channels <= 4) ) 555 | { 556 | channels = force_channels; 557 | } 558 | if( NULL == img ) 559 | { 560 | /* image loading failed */ 561 | result_string_pointer = stbi_failure_reason(); 562 | return 0; 563 | } 564 | /* upload the texture, but reuse the assigned texture ID */ 565 | tex_id = SOIL_internal_create_OGL_texture( 566 | img, width, height, channels, 567 | tex_id, flags, 568 | SOIL_TEXTURE_CUBE_MAP, SOIL_TEXTURE_CUBE_MAP_POSITIVE_Y, 569 | SOIL_MAX_CUBE_MAP_TEXTURE_SIZE ); 570 | /* and nuke the image data */ 571 | SOIL_free_image_data( img ); 572 | } 573 | /* continue? */ 574 | if( tex_id != 0 ) 575 | { 576 | /* 1st face: try to load the image */ 577 | img = SOIL_load_image_from_memory( 578 | y_neg_buffer, y_neg_buffer_length, 579 | &width, &height, &channels, force_channels ); 580 | /* channels holds the original number of channels, which may have been forced */ 581 | if( (force_channels >= 1) && (force_channels <= 4) ) 582 | { 583 | channels = force_channels; 584 | } 585 | if( NULL == img ) 586 | { 587 | /* image loading failed */ 588 | result_string_pointer = stbi_failure_reason(); 589 | return 0; 590 | } 591 | /* upload the texture, but reuse the assigned texture ID */ 592 | tex_id = SOIL_internal_create_OGL_texture( 593 | img, width, height, channels, 594 | tex_id, flags, 595 | SOIL_TEXTURE_CUBE_MAP, SOIL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 596 | SOIL_MAX_CUBE_MAP_TEXTURE_SIZE ); 597 | /* and nuke the image data */ 598 | SOIL_free_image_data( img ); 599 | } 600 | /* continue? */ 601 | if( tex_id != 0 ) 602 | { 603 | /* 1st face: try to load the image */ 604 | img = SOIL_load_image_from_memory( 605 | z_pos_buffer, z_pos_buffer_length, 606 | &width, &height, &channels, force_channels ); 607 | /* channels holds the original number of channels, which may have been forced */ 608 | if( (force_channels >= 1) && (force_channels <= 4) ) 609 | { 610 | channels = force_channels; 611 | } 612 | if( NULL == img ) 613 | { 614 | /* image loading failed */ 615 | result_string_pointer = stbi_failure_reason(); 616 | return 0; 617 | } 618 | /* upload the texture, but reuse the assigned texture ID */ 619 | tex_id = SOIL_internal_create_OGL_texture( 620 | img, width, height, channels, 621 | tex_id, flags, 622 | SOIL_TEXTURE_CUBE_MAP, SOIL_TEXTURE_CUBE_MAP_POSITIVE_Z, 623 | SOIL_MAX_CUBE_MAP_TEXTURE_SIZE ); 624 | /* and nuke the image data */ 625 | SOIL_free_image_data( img ); 626 | } 627 | /* continue? */ 628 | if( tex_id != 0 ) 629 | { 630 | /* 1st face: try to load the image */ 631 | img = SOIL_load_image_from_memory( 632 | z_neg_buffer, z_neg_buffer_length, 633 | &width, &height, &channels, force_channels ); 634 | /* channels holds the original number of channels, which may have been forced */ 635 | if( (force_channels >= 1) && (force_channels <= 4) ) 636 | { 637 | channels = force_channels; 638 | } 639 | if( NULL == img ) 640 | { 641 | /* image loading failed */ 642 | result_string_pointer = stbi_failure_reason(); 643 | return 0; 644 | } 645 | /* upload the texture, but reuse the assigned texture ID */ 646 | tex_id = SOIL_internal_create_OGL_texture( 647 | img, width, height, channels, 648 | tex_id, flags, 649 | SOIL_TEXTURE_CUBE_MAP, SOIL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 650 | SOIL_MAX_CUBE_MAP_TEXTURE_SIZE ); 651 | /* and nuke the image data */ 652 | SOIL_free_image_data( img ); 653 | } 654 | /* and return the handle, such as it is */ 655 | return tex_id; 656 | } 657 | 658 | unsigned int 659 | SOIL_load_OGL_single_cubemap 660 | ( 661 | const char *filename, 662 | const char face_order[6], 663 | int force_channels, 664 | unsigned int reuse_texture_ID, 665 | unsigned int flags 666 | ) 667 | { 668 | /* variables */ 669 | unsigned char* img; 670 | int width, height, channels, i; 671 | unsigned int tex_id = 0; 672 | /* error checking */ 673 | if( filename == NULL ) 674 | { 675 | result_string_pointer = "Invalid single cube map file name"; 676 | return 0; 677 | } 678 | /* does the user want direct uploading of the image as a DDS file? */ 679 | if( flags & SOIL_FLAG_DDS_LOAD_DIRECT ) 680 | { 681 | /* 1st try direct loading of the image as a DDS file 682 | note: direct uploading will only load what is in the 683 | DDS file, no MIPmaps will be generated, the image will 684 | not be flipped, etc. */ 685 | tex_id = SOIL_direct_load_DDS( filename, reuse_texture_ID, flags, 1 ); 686 | if( tex_id ) 687 | { 688 | /* hey, it worked!! */ 689 | return tex_id; 690 | } 691 | } 692 | /* face order checking */ 693 | for( i = 0; i < 6; ++i ) 694 | { 695 | if( (face_order[i] != 'N') && 696 | (face_order[i] != 'S') && 697 | (face_order[i] != 'W') && 698 | (face_order[i] != 'E') && 699 | (face_order[i] != 'U') && 700 | (face_order[i] != 'D') ) 701 | { 702 | result_string_pointer = "Invalid single cube map face order"; 703 | return 0; 704 | }; 705 | } 706 | /* capability checking */ 707 | if( query_cubemap_capability() != SOIL_CAPABILITY_PRESENT ) 708 | { 709 | result_string_pointer = "No cube map capability present"; 710 | return 0; 711 | } 712 | /* 1st off, try to load the full image */ 713 | img = SOIL_load_image( filename, &width, &height, &channels, force_channels ); 714 | /* channels holds the original number of channels, which may have been forced */ 715 | if( (force_channels >= 1) && (force_channels <= 4) ) 716 | { 717 | channels = force_channels; 718 | } 719 | if( NULL == img ) 720 | { 721 | /* image loading failed */ 722 | result_string_pointer = stbi_failure_reason(); 723 | return 0; 724 | } 725 | /* now, does this image have the right dimensions? */ 726 | if( (width != 6*height) && 727 | (6*width != height) ) 728 | { 729 | SOIL_free_image_data( img ); 730 | result_string_pointer = "Single cubemap image must have a 6:1 ratio"; 731 | return 0; 732 | } 733 | /* try the image split and create */ 734 | tex_id = SOIL_create_OGL_single_cubemap( 735 | img, width, height, channels, 736 | face_order, reuse_texture_ID, flags 737 | ); 738 | /* nuke the temporary image data and return the texture handle */ 739 | SOIL_free_image_data( img ); 740 | return tex_id; 741 | } 742 | 743 | unsigned int 744 | SOIL_load_OGL_single_cubemap_from_memory 745 | ( 746 | const unsigned char *const buffer, 747 | int buffer_length, 748 | const char face_order[6], 749 | int force_channels, 750 | unsigned int reuse_texture_ID, 751 | unsigned int flags 752 | ) 753 | { 754 | /* variables */ 755 | unsigned char* img; 756 | int width, height, channels, i; 757 | unsigned int tex_id = 0; 758 | /* error checking */ 759 | if( buffer == NULL ) 760 | { 761 | result_string_pointer = "Invalid single cube map buffer"; 762 | return 0; 763 | } 764 | /* does the user want direct uploading of the image as a DDS file? */ 765 | if( flags & SOIL_FLAG_DDS_LOAD_DIRECT ) 766 | { 767 | /* 1st try direct loading of the image as a DDS file 768 | note: direct uploading will only load what is in the 769 | DDS file, no MIPmaps will be generated, the image will 770 | not be flipped, etc. */ 771 | tex_id = SOIL_direct_load_DDS_from_memory( 772 | buffer, buffer_length, 773 | reuse_texture_ID, flags, 1 ); 774 | if( tex_id ) 775 | { 776 | /* hey, it worked!! */ 777 | return tex_id; 778 | } 779 | } 780 | /* face order checking */ 781 | for( i = 0; i < 6; ++i ) 782 | { 783 | if( (face_order[i] != 'N') && 784 | (face_order[i] != 'S') && 785 | (face_order[i] != 'W') && 786 | (face_order[i] != 'E') && 787 | (face_order[i] != 'U') && 788 | (face_order[i] != 'D') ) 789 | { 790 | result_string_pointer = "Invalid single cube map face order"; 791 | return 0; 792 | }; 793 | } 794 | /* capability checking */ 795 | if( query_cubemap_capability() != SOIL_CAPABILITY_PRESENT ) 796 | { 797 | result_string_pointer = "No cube map capability present"; 798 | return 0; 799 | } 800 | /* 1st off, try to load the full image */ 801 | img = SOIL_load_image_from_memory( 802 | buffer, buffer_length, 803 | &width, &height, &channels, 804 | force_channels ); 805 | /* channels holds the original number of channels, which may have been forced */ 806 | if( (force_channels >= 1) && (force_channels <= 4) ) 807 | { 808 | channels = force_channels; 809 | } 810 | if( NULL == img ) 811 | { 812 | /* image loading failed */ 813 | result_string_pointer = stbi_failure_reason(); 814 | return 0; 815 | } 816 | /* now, does this image have the right dimensions? */ 817 | if( (width != 6*height) && 818 | (6*width != height) ) 819 | { 820 | SOIL_free_image_data( img ); 821 | result_string_pointer = "Single cubemap image must have a 6:1 ratio"; 822 | return 0; 823 | } 824 | /* try the image split and create */ 825 | tex_id = SOIL_create_OGL_single_cubemap( 826 | img, width, height, channels, 827 | face_order, reuse_texture_ID, flags 828 | ); 829 | /* nuke the temporary image data and return the texture handle */ 830 | SOIL_free_image_data( img ); 831 | return tex_id; 832 | } 833 | 834 | unsigned int 835 | SOIL_create_OGL_single_cubemap 836 | ( 837 | const unsigned char *const data, 838 | int width, int height, int channels, 839 | const char face_order[6], 840 | unsigned int reuse_texture_ID, 841 | unsigned int flags 842 | ) 843 | { 844 | /* variables */ 845 | unsigned char* sub_img; 846 | int dw, dh, sz, i; 847 | unsigned int tex_id; 848 | /* error checking */ 849 | if( data == NULL ) 850 | { 851 | result_string_pointer = "Invalid single cube map image data"; 852 | return 0; 853 | } 854 | /* face order checking */ 855 | for( i = 0; i < 6; ++i ) 856 | { 857 | if( (face_order[i] != 'N') && 858 | (face_order[i] != 'S') && 859 | (face_order[i] != 'W') && 860 | (face_order[i] != 'E') && 861 | (face_order[i] != 'U') && 862 | (face_order[i] != 'D') ) 863 | { 864 | result_string_pointer = "Invalid single cube map face order"; 865 | return 0; 866 | }; 867 | } 868 | /* capability checking */ 869 | if( query_cubemap_capability() != SOIL_CAPABILITY_PRESENT ) 870 | { 871 | result_string_pointer = "No cube map capability present"; 872 | return 0; 873 | } 874 | /* now, does this image have the right dimensions? */ 875 | if( (width != 6*height) && 876 | (6*width != height) ) 877 | { 878 | result_string_pointer = "Single cubemap image must have a 6:1 ratio"; 879 | return 0; 880 | } 881 | /* which way am I stepping? */ 882 | if( width > height ) 883 | { 884 | dw = height; 885 | dh = 0; 886 | } else 887 | { 888 | dw = 0; 889 | dh = width; 890 | } 891 | sz = dw+dh; 892 | sub_img = (unsigned char *)malloc( sz*sz*channels ); 893 | /* do the splitting and uploading */ 894 | tex_id = reuse_texture_ID; 895 | for( i = 0; i < 6; ++i ) 896 | { 897 | int x, y, idx = 0; 898 | unsigned int cubemap_target = 0; 899 | /* copy in the sub-image */ 900 | for( y = i*dh; y < i*dh+sz; ++y ) 901 | { 902 | for( x = i*dw*channels; x < (i*dw+sz)*channels; ++x ) 903 | { 904 | sub_img[idx++] = data[y*width*channels+x]; 905 | } 906 | } 907 | /* what is my texture target? 908 | remember, this coordinate system is 909 | LHS if viewed from inside the cube! */ 910 | switch( face_order[i] ) 911 | { 912 | case 'N': 913 | cubemap_target = SOIL_TEXTURE_CUBE_MAP_POSITIVE_Z; 914 | break; 915 | case 'S': 916 | cubemap_target = SOIL_TEXTURE_CUBE_MAP_NEGATIVE_Z; 917 | break; 918 | case 'W': 919 | cubemap_target = SOIL_TEXTURE_CUBE_MAP_NEGATIVE_X; 920 | break; 921 | case 'E': 922 | cubemap_target = SOIL_TEXTURE_CUBE_MAP_POSITIVE_X; 923 | break; 924 | case 'U': 925 | cubemap_target = SOIL_TEXTURE_CUBE_MAP_POSITIVE_Y; 926 | break; 927 | case 'D': 928 | cubemap_target = SOIL_TEXTURE_CUBE_MAP_NEGATIVE_Y; 929 | break; 930 | } 931 | /* upload it as a texture */ 932 | tex_id = SOIL_internal_create_OGL_texture( 933 | sub_img, sz, sz, channels, 934 | tex_id, flags, 935 | SOIL_TEXTURE_CUBE_MAP, 936 | cubemap_target, 937 | SOIL_MAX_CUBE_MAP_TEXTURE_SIZE ); 938 | } 939 | /* and nuke the image and sub-image data */ 940 | SOIL_free_image_data( sub_img ); 941 | /* and return the handle, such as it is */ 942 | return tex_id; 943 | } 944 | 945 | unsigned int 946 | SOIL_create_OGL_texture 947 | ( 948 | const unsigned char *const data, 949 | int width, int height, int channels, 950 | unsigned int reuse_texture_ID, 951 | unsigned int flags 952 | ) 953 | { 954 | /* wrapper function for 2D textures */ 955 | return SOIL_internal_create_OGL_texture( 956 | data, width, height, channels, 957 | reuse_texture_ID, flags, 958 | GL_TEXTURE_2D, GL_TEXTURE_2D, 959 | GL_MAX_TEXTURE_SIZE ); 960 | } 961 | 962 | #if SOIL_CHECK_FOR_GL_ERRORS 963 | void check_for_GL_errors( const char *calling_location ) 964 | { 965 | /* check for errors */ 966 | GLenum err_code = glGetError(); 967 | while( GL_NO_ERROR != err_code ) 968 | { 969 | printf( "OpenGL Error @ %s: %i", calling_location, err_code ); 970 | err_code = glGetError(); 971 | } 972 | } 973 | #else 974 | void check_for_GL_errors( const char *calling_location ) 975 | { 976 | /* no check for errors */ 977 | } 978 | #endif 979 | 980 | unsigned int 981 | SOIL_internal_create_OGL_texture 982 | ( 983 | const unsigned char *const data, 984 | int width, int height, int channels, 985 | unsigned int reuse_texture_ID, 986 | unsigned int flags, 987 | unsigned int opengl_texture_type, 988 | unsigned int opengl_texture_target, 989 | unsigned int texture_check_size_enum 990 | ) 991 | { 992 | /* variables */ 993 | unsigned char* img; 994 | unsigned int tex_id; 995 | unsigned int internal_texture_format = 0, original_texture_format = 0; 996 | int DXT_mode = SOIL_CAPABILITY_UNKNOWN; 997 | int max_supported_size; 998 | /* If the user wants to use the texture rectangle I kill a few flags */ 999 | if( flags & SOIL_FLAG_TEXTURE_RECTANGLE ) 1000 | { 1001 | /* well, the user asked for it, can we do that? */ 1002 | if( query_tex_rectangle_capability() == SOIL_CAPABILITY_PRESENT ) 1003 | { 1004 | /* only allow this if the user in _NOT_ trying to do a cubemap! */ 1005 | if( opengl_texture_type == GL_TEXTURE_2D ) 1006 | { 1007 | /* clean out the flags that cannot be used with texture rectangles */ 1008 | flags &= ~( 1009 | SOIL_FLAG_POWER_OF_TWO | SOIL_FLAG_MIPMAPS | 1010 | SOIL_FLAG_TEXTURE_REPEATS 1011 | ); 1012 | /* and change my target */ 1013 | opengl_texture_target = SOIL_TEXTURE_RECTANGLE_ARB; 1014 | opengl_texture_type = SOIL_TEXTURE_RECTANGLE_ARB; 1015 | } else 1016 | { 1017 | /* not allowed for any other uses (yes, I'm looking at you, cubemaps!) */ 1018 | flags &= ~SOIL_FLAG_TEXTURE_RECTANGLE; 1019 | } 1020 | 1021 | } else 1022 | { 1023 | /* can't do it, and that is a breakable offense (uv coords use pixels instead of [0,1]!) */ 1024 | result_string_pointer = "Texture Rectangle extension unsupported"; 1025 | return 0; 1026 | } 1027 | } 1028 | /* create a copy the image data */ 1029 | img = (unsigned char*)malloc( width*height*channels ); 1030 | memcpy( img, data, width*height*channels ); 1031 | /* does the user want me to invert the image? */ 1032 | if( flags & SOIL_FLAG_INVERT_Y ) 1033 | { 1034 | int i, j; 1035 | for( j = 0; j*2 < height; ++j ) 1036 | { 1037 | int index1 = j * width * channels; 1038 | int index2 = (height - 1 - j) * width * channels; 1039 | for( i = width * channels; i > 0; --i ) 1040 | { 1041 | unsigned char temp = img[index1]; 1042 | img[index1] = img[index2]; 1043 | img[index2] = temp; 1044 | ++index1; 1045 | ++index2; 1046 | } 1047 | } 1048 | } 1049 | /* does the user want me to scale the colors into the NTSC safe RGB range? */ 1050 | if( flags & SOIL_FLAG_NTSC_SAFE_RGB ) 1051 | { 1052 | scale_image_RGB_to_NTSC_safe( img, width, height, channels ); 1053 | } 1054 | /* does the user want me to convert from straight to pre-multiplied alpha? 1055 | (and do we even _have_ alpha?) */ 1056 | if( flags & SOIL_FLAG_MULTIPLY_ALPHA ) 1057 | { 1058 | int i; 1059 | switch( channels ) 1060 | { 1061 | case 2: 1062 | for( i = 0; i < 2*width*height; i += 2 ) 1063 | { 1064 | img[i] = (img[i] * img[i+1] + 128) >> 8; 1065 | } 1066 | break; 1067 | case 4: 1068 | for( i = 0; i < 4*width*height; i += 4 ) 1069 | { 1070 | img[i+0] = (img[i+0] * img[i+3] + 128) >> 8; 1071 | img[i+1] = (img[i+1] * img[i+3] + 128) >> 8; 1072 | img[i+2] = (img[i+2] * img[i+3] + 128) >> 8; 1073 | } 1074 | break; 1075 | default: 1076 | /* no other number of channels contains alpha data */ 1077 | break; 1078 | } 1079 | } 1080 | /* if the user can't support NPOT textures, make sure we force the POT option */ 1081 | if( (query_NPOT_capability() == SOIL_CAPABILITY_NONE) && 1082 | !(flags & SOIL_FLAG_TEXTURE_RECTANGLE) ) 1083 | { 1084 | /* add in the POT flag */ 1085 | flags |= SOIL_FLAG_POWER_OF_TWO; 1086 | } 1087 | /* how large of a texture can this OpenGL implementation handle? */ 1088 | /* texture_check_size_enum will be GL_MAX_TEXTURE_SIZE or SOIL_MAX_CUBE_MAP_TEXTURE_SIZE */ 1089 | glGetIntegerv( texture_check_size_enum, &max_supported_size ); 1090 | /* do I need to make it a power of 2? */ 1091 | if( 1092 | (flags & SOIL_FLAG_POWER_OF_TWO) || /* user asked for it */ 1093 | (flags & SOIL_FLAG_MIPMAPS) || /* need it for the MIP-maps */ 1094 | (width > max_supported_size) || /* it's too big, (make sure it's */ 1095 | (height > max_supported_size) ) /* 2^n for later down-sampling) */ 1096 | { 1097 | int new_width = 1; 1098 | int new_height = 1; 1099 | while( new_width < width ) 1100 | { 1101 | new_width *= 2; 1102 | } 1103 | while( new_height < height ) 1104 | { 1105 | new_height *= 2; 1106 | } 1107 | /* still? */ 1108 | if( (new_width != width) || (new_height != height) ) 1109 | { 1110 | /* yep, resize */ 1111 | unsigned char *resampled = (unsigned char*)malloc( channels*new_width*new_height ); 1112 | up_scale_image( 1113 | img, width, height, channels, 1114 | resampled, new_width, new_height ); 1115 | /* OJO this is for debug only! */ 1116 | /* 1117 | SOIL_save_image( "\\showme.bmp", SOIL_SAVE_TYPE_BMP, 1118 | new_width, new_height, channels, 1119 | resampled ); 1120 | */ 1121 | /* nuke the old guy, then point it at the new guy */ 1122 | SOIL_free_image_data( img ); 1123 | img = resampled; 1124 | width = new_width; 1125 | height = new_height; 1126 | } 1127 | } 1128 | /* now, if it is too large... */ 1129 | if( (width > max_supported_size) || (height > max_supported_size) ) 1130 | { 1131 | /* I've already made it a power of two, so simply use the MIPmapping 1132 | code to reduce its size to the allowable maximum. */ 1133 | unsigned char *resampled; 1134 | int reduce_block_x = 1, reduce_block_y = 1; 1135 | int new_width, new_height; 1136 | if( width > max_supported_size ) 1137 | { 1138 | reduce_block_x = width / max_supported_size; 1139 | } 1140 | if( height > max_supported_size ) 1141 | { 1142 | reduce_block_y = height / max_supported_size; 1143 | } 1144 | new_width = width / reduce_block_x; 1145 | new_height = height / reduce_block_y; 1146 | resampled = (unsigned char*)malloc( channels*new_width*new_height ); 1147 | /* perform the actual reduction */ 1148 | mipmap_image( img, width, height, channels, 1149 | resampled, reduce_block_x, reduce_block_y ); 1150 | /* nuke the old guy, then point it at the new guy */ 1151 | SOIL_free_image_data( img ); 1152 | img = resampled; 1153 | width = new_width; 1154 | height = new_height; 1155 | } 1156 | /* does the user want us to use YCoCg color space? */ 1157 | if( flags & SOIL_FLAG_CoCg_Y ) 1158 | { 1159 | /* this will only work with RGB and RGBA images */ 1160 | convert_RGB_to_YCoCg( img, width, height, channels ); 1161 | /* 1162 | save_image_as_DDS( "CoCg_Y.dds", width, height, channels, img ); 1163 | */ 1164 | } 1165 | /* create the OpenGL texture ID handle 1166 | (note: allowing a forced texture ID lets me reload a texture) */ 1167 | tex_id = reuse_texture_ID; 1168 | if( tex_id == 0 ) 1169 | { 1170 | glGenTextures( 1, &tex_id ); 1171 | } 1172 | check_for_GL_errors( "glGenTextures" ); 1173 | /* Note: sometimes glGenTextures fails (usually no OpenGL context) */ 1174 | if( tex_id ) 1175 | { 1176 | /* and what type am I using as the internal texture format? */ 1177 | switch( channels ) 1178 | { 1179 | case 1: 1180 | original_texture_format = GL_LUMINANCE; 1181 | break; 1182 | case 2: 1183 | original_texture_format = GL_LUMINANCE_ALPHA; 1184 | break; 1185 | case 3: 1186 | original_texture_format = GL_RGB; 1187 | break; 1188 | case 4: 1189 | original_texture_format = GL_RGBA; 1190 | break; 1191 | } 1192 | internal_texture_format = original_texture_format; 1193 | /* does the user want me to, and can I, save as DXT? */ 1194 | if( flags & SOIL_FLAG_COMPRESS_TO_DXT ) 1195 | { 1196 | DXT_mode = query_DXT_capability(); 1197 | if( DXT_mode == SOIL_CAPABILITY_PRESENT ) 1198 | { 1199 | /* I can use DXT, whether I compress it or OpenGL does */ 1200 | if( (channels & 1) == 1 ) 1201 | { 1202 | /* 1 or 3 channels = DXT1 */ 1203 | internal_texture_format = SOIL_RGB_S3TC_DXT1; 1204 | } else 1205 | { 1206 | /* 2 or 4 channels = DXT5 */ 1207 | internal_texture_format = SOIL_RGBA_S3TC_DXT5; 1208 | } 1209 | } 1210 | } 1211 | /* bind an OpenGL texture ID */ 1212 | glBindTexture( opengl_texture_type, tex_id ); 1213 | check_for_GL_errors( "glBindTexture" ); 1214 | /* upload the main image */ 1215 | if( DXT_mode == SOIL_CAPABILITY_PRESENT ) 1216 | { 1217 | /* user wants me to do the DXT conversion! */ 1218 | int DDS_size; 1219 | unsigned char *DDS_data = NULL; 1220 | if( (channels & 1) == 1 ) 1221 | { 1222 | /* RGB, use DXT1 */ 1223 | DDS_data = convert_image_to_DXT1( img, width, height, channels, &DDS_size ); 1224 | } else 1225 | { 1226 | /* RGBA, use DXT5 */ 1227 | DDS_data = convert_image_to_DXT5( img, width, height, channels, &DDS_size ); 1228 | } 1229 | if( DDS_data ) 1230 | { 1231 | soilGlCompressedTexImage2D( 1232 | opengl_texture_target, 0, 1233 | internal_texture_format, width, height, 0, 1234 | DDS_size, DDS_data ); 1235 | check_for_GL_errors( "glCompressedTexImage2D" ); 1236 | SOIL_free_image_data( DDS_data ); 1237 | /* printf( "Internal DXT compressor\n" ); */ 1238 | } else 1239 | { 1240 | /* my compression failed, try the OpenGL driver's version */ 1241 | glTexImage2D( 1242 | opengl_texture_target, 0, 1243 | internal_texture_format, width, height, 0, 1244 | original_texture_format, GL_UNSIGNED_BYTE, img ); 1245 | check_for_GL_errors( "glTexImage2D" ); 1246 | /* printf( "OpenGL DXT compressor\n" ); */ 1247 | } 1248 | } else 1249 | { 1250 | /* user want OpenGL to do all the work! */ 1251 | glTexImage2D( 1252 | opengl_texture_target, 0, 1253 | internal_texture_format, width, height, 0, 1254 | original_texture_format, GL_UNSIGNED_BYTE, img ); 1255 | check_for_GL_errors( "glTexImage2D" ); 1256 | /*printf( "OpenGL DXT compressor\n" ); */ 1257 | } 1258 | /* are any MIPmaps desired? */ 1259 | if( flags & SOIL_FLAG_MIPMAPS ) 1260 | { 1261 | int MIPlevel = 1; 1262 | int MIPwidth = (width+1) / 2; 1263 | int MIPheight = (height+1) / 2; 1264 | unsigned char *resampled = (unsigned char*)malloc( channels*MIPwidth*MIPheight ); 1265 | while( ((1< 0; --i ) 1407 | { 1408 | unsigned char temp = pixel_data[index1]; 1409 | pixel_data[index1] = pixel_data[index2]; 1410 | pixel_data[index2] = temp; 1411 | ++index1; 1412 | ++index2; 1413 | } 1414 | } 1415 | 1416 | /* save the image */ 1417 | save_result = SOIL_save_image( filename, image_type, width, height, 3, pixel_data); 1418 | 1419 | /* And free the memory */ 1420 | SOIL_free_image_data( pixel_data ); 1421 | return save_result; 1422 | } 1423 | 1424 | unsigned char* 1425 | SOIL_load_image 1426 | ( 1427 | const char *filename, 1428 | int *width, int *height, int *channels, 1429 | int force_channels 1430 | ) 1431 | { 1432 | unsigned char *result = stbi_load( filename, 1433 | width, height, channels, force_channels ); 1434 | if( result == NULL ) 1435 | { 1436 | result_string_pointer = stbi_failure_reason(); 1437 | } else 1438 | { 1439 | result_string_pointer = "Image loaded"; 1440 | } 1441 | return result; 1442 | } 1443 | 1444 | unsigned char* 1445 | SOIL_load_image_from_memory 1446 | ( 1447 | const unsigned char *const buffer, 1448 | int buffer_length, 1449 | int *width, int *height, int *channels, 1450 | int force_channels 1451 | ) 1452 | { 1453 | unsigned char *result = stbi_load_from_memory( 1454 | buffer, buffer_length, 1455 | width, height, channels, 1456 | force_channels ); 1457 | if( result == NULL ) 1458 | { 1459 | result_string_pointer = stbi_failure_reason(); 1460 | } else 1461 | { 1462 | result_string_pointer = "Image loaded from memory"; 1463 | } 1464 | return result; 1465 | } 1466 | 1467 | int 1468 | SOIL_save_image 1469 | ( 1470 | const char *filename, 1471 | int image_type, 1472 | int width, int height, int channels, 1473 | const unsigned char *const data 1474 | ) 1475 | { 1476 | int save_result; 1477 | 1478 | /* error check */ 1479 | if( (width < 1) || (height < 1) || 1480 | (channels < 1) || (channels > 4) || 1481 | (data == NULL) || 1482 | (filename == NULL) ) 1483 | { 1484 | return 0; 1485 | } 1486 | if( image_type == SOIL_SAVE_TYPE_BMP ) 1487 | { 1488 | save_result = stbi_write_bmp( filename, 1489 | width, height, channels, (void*)data ); 1490 | } else 1491 | if( image_type == SOIL_SAVE_TYPE_TGA ) 1492 | { 1493 | save_result = stbi_write_tga( filename, 1494 | width, height, channels, (void*)data ); 1495 | } else 1496 | if( image_type == SOIL_SAVE_TYPE_DDS ) 1497 | { 1498 | save_result = save_image_as_DDS( filename, 1499 | width, height, channels, (const unsigned char *const)data ); 1500 | } else 1501 | { 1502 | save_result = 0; 1503 | } 1504 | if( save_result == 0 ) 1505 | { 1506 | result_string_pointer = "Saving the image failed"; 1507 | } else 1508 | { 1509 | result_string_pointer = "Image saved"; 1510 | } 1511 | return save_result; 1512 | } 1513 | 1514 | void 1515 | SOIL_free_image_data 1516 | ( 1517 | unsigned char *img_data 1518 | ) 1519 | { 1520 | free( (void*)img_data ); 1521 | } 1522 | 1523 | const char* 1524 | SOIL_last_result 1525 | ( 1526 | void 1527 | ) 1528 | { 1529 | return result_string_pointer; 1530 | } 1531 | 1532 | unsigned int SOIL_direct_load_DDS_from_memory( 1533 | const unsigned char *const buffer, 1534 | int buffer_length, 1535 | unsigned int reuse_texture_ID, 1536 | int flags, 1537 | int loading_as_cubemap ) 1538 | { 1539 | /* variables */ 1540 | DDS_header header; 1541 | unsigned int buffer_index = 0; 1542 | unsigned int tex_ID = 0; 1543 | /* file reading variables */ 1544 | unsigned int S3TC_type = 0; 1545 | unsigned char *DDS_data; 1546 | unsigned int DDS_main_size; 1547 | unsigned int DDS_full_size; 1548 | unsigned int width, height; 1549 | int mipmaps, cubemap, uncompressed, block_size = 16; 1550 | unsigned int flag; 1551 | unsigned int cf_target, ogl_target_start, ogl_target_end; 1552 | unsigned int opengl_texture_type; 1553 | int i; 1554 | /* 1st off, does the filename even exist? */ 1555 | if( NULL == buffer ) 1556 | { 1557 | /* we can't do it! */ 1558 | result_string_pointer = "NULL buffer"; 1559 | return 0; 1560 | } 1561 | if( buffer_length < sizeof( DDS_header ) ) 1562 | { 1563 | /* we can't do it! */ 1564 | result_string_pointer = "DDS file was too small to contain the DDS header"; 1565 | return 0; 1566 | } 1567 | /* try reading in the header */ 1568 | memcpy ( (void*)(&header), (const void *)buffer, sizeof( DDS_header ) ); 1569 | buffer_index = sizeof( DDS_header ); 1570 | /* guilty until proven innocent */ 1571 | result_string_pointer = "Failed to read a known DDS header"; 1572 | /* validate the header (warning, "goto"'s ahead, shield your eyes!!) */ 1573 | flag = ('D'<<0)|('D'<<8)|('S'<<16)|(' '<<24); 1574 | if( header.dwMagic != flag ) {goto quick_exit;} 1575 | if( header.dwSize != 124 ) {goto quick_exit;} 1576 | /* I need all of these */ 1577 | flag = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT; 1578 | if( (header.dwFlags & flag) != flag ) {goto quick_exit;} 1579 | /* According to the MSDN spec, the dwFlags should contain 1580 | DDSD_LINEARSIZE if it's compressed, or DDSD_PITCH if 1581 | uncompressed. Some DDS writers do not conform to the 1582 | spec, so I need to make my reader more tolerant */ 1583 | /* I need one of these */ 1584 | flag = DDPF_FOURCC | DDPF_RGB; 1585 | if( (header.sPixelFormat.dwFlags & flag) == 0 ) {goto quick_exit;} 1586 | if( header.sPixelFormat.dwSize != 32 ) {goto quick_exit;} 1587 | if( (header.sCaps.dwCaps1 & DDSCAPS_TEXTURE) == 0 ) {goto quick_exit;} 1588 | /* make sure it is a type we can upload */ 1589 | if( (header.sPixelFormat.dwFlags & DDPF_FOURCC) && 1590 | !( 1591 | (header.sPixelFormat.dwFourCC == (('D'<<0)|('X'<<8)|('T'<<16)|('1'<<24))) || 1592 | (header.sPixelFormat.dwFourCC == (('D'<<0)|('X'<<8)|('T'<<16)|('3'<<24))) || 1593 | (header.sPixelFormat.dwFourCC == (('D'<<0)|('X'<<8)|('T'<<16)|('5'<<24))) 1594 | ) ) 1595 | { 1596 | goto quick_exit; 1597 | } 1598 | /* OK, validated the header, let's load the image data */ 1599 | result_string_pointer = "DDS header loaded and validated"; 1600 | width = header.dwWidth; 1601 | height = header.dwHeight; 1602 | uncompressed = 1 - (header.sPixelFormat.dwFlags & DDPF_FOURCC) / DDPF_FOURCC; 1603 | cubemap = (header.sCaps.dwCaps2 & DDSCAPS2_CUBEMAP) / DDSCAPS2_CUBEMAP; 1604 | if( uncompressed ) 1605 | { 1606 | S3TC_type = GL_RGB; 1607 | block_size = 3; 1608 | if( header.sPixelFormat.dwFlags & DDPF_ALPHAPIXELS ) 1609 | { 1610 | S3TC_type = GL_RGBA; 1611 | block_size = 4; 1612 | } 1613 | DDS_main_size = width * height * block_size; 1614 | } else 1615 | { 1616 | /* can we even handle direct uploading to OpenGL DXT compressed images? */ 1617 | if( query_DXT_capability() != SOIL_CAPABILITY_PRESENT ) 1618 | { 1619 | /* we can't do it! */ 1620 | result_string_pointer = "Direct upload of S3TC images not supported by the OpenGL driver"; 1621 | return 0; 1622 | } 1623 | /* well, we know it is DXT1/3/5, because we checked above */ 1624 | switch( (header.sPixelFormat.dwFourCC >> 24) - '0' ) 1625 | { 1626 | case 1: 1627 | S3TC_type = SOIL_RGBA_S3TC_DXT1; 1628 | block_size = 8; 1629 | break; 1630 | case 3: 1631 | S3TC_type = SOIL_RGBA_S3TC_DXT3; 1632 | block_size = 16; 1633 | break; 1634 | case 5: 1635 | S3TC_type = SOIL_RGBA_S3TC_DXT5; 1636 | block_size = 16; 1637 | break; 1638 | } 1639 | DDS_main_size = ((width+3)>>2)*((height+3)>>2)*block_size; 1640 | } 1641 | if( cubemap ) 1642 | { 1643 | /* does the user want a cubemap? */ 1644 | if( !loading_as_cubemap ) 1645 | { 1646 | /* we can't do it! */ 1647 | result_string_pointer = "DDS image was a cubemap"; 1648 | return 0; 1649 | } 1650 | /* can we even handle cubemaps with the OpenGL driver? */ 1651 | if( query_cubemap_capability() != SOIL_CAPABILITY_PRESENT ) 1652 | { 1653 | /* we can't do it! */ 1654 | result_string_pointer = "Direct upload of cubemap images not supported by the OpenGL driver"; 1655 | return 0; 1656 | } 1657 | ogl_target_start = SOIL_TEXTURE_CUBE_MAP_POSITIVE_X; 1658 | ogl_target_end = SOIL_TEXTURE_CUBE_MAP_NEGATIVE_Z; 1659 | opengl_texture_type = SOIL_TEXTURE_CUBE_MAP; 1660 | } else 1661 | { 1662 | /* does the user want a non-cubemap? */ 1663 | if( loading_as_cubemap ) 1664 | { 1665 | /* we can't do it! */ 1666 | result_string_pointer = "DDS image was not a cubemap"; 1667 | return 0; 1668 | } 1669 | ogl_target_start = GL_TEXTURE_2D; 1670 | ogl_target_end = GL_TEXTURE_2D; 1671 | opengl_texture_type = GL_TEXTURE_2D; 1672 | } 1673 | if( (header.sCaps.dwCaps1 & DDSCAPS_MIPMAP) && (header.dwMipMapCount > 1) ) 1674 | { 1675 | int shift_offset; 1676 | mipmaps = header.dwMipMapCount - 1; 1677 | DDS_full_size = DDS_main_size; 1678 | if( uncompressed ) 1679 | { 1680 | /* uncompressed DDS, simple MIPmap size calculation */ 1681 | shift_offset = 0; 1682 | } else 1683 | { 1684 | /* compressed DDS, MIPmap size calculation is block based */ 1685 | shift_offset = 2; 1686 | } 1687 | for( i = 1; i <= mipmaps; ++ i ) 1688 | { 1689 | int w, h; 1690 | w = width >> (shift_offset + i); 1691 | h = height >> (shift_offset + i); 1692 | if( w < 1 ) 1693 | { 1694 | w = 1; 1695 | } 1696 | if( h < 1 ) 1697 | { 1698 | h = 1; 1699 | } 1700 | DDS_full_size += w*h*block_size; 1701 | } 1702 | } else 1703 | { 1704 | mipmaps = 0; 1705 | DDS_full_size = DDS_main_size; 1706 | } 1707 | DDS_data = (unsigned char*)malloc( DDS_full_size ); 1708 | /* got the image data RAM, create or use an existing OpenGL texture handle */ 1709 | tex_ID = reuse_texture_ID; 1710 | if( tex_ID == 0 ) 1711 | { 1712 | glGenTextures( 1, &tex_ID ); 1713 | } 1714 | /* bind an OpenGL texture ID */ 1715 | glBindTexture( opengl_texture_type, tex_ID ); 1716 | /* do this for each face of the cubemap! */ 1717 | for( cf_target = ogl_target_start; cf_target <= ogl_target_end; ++cf_target ) 1718 | { 1719 | if( buffer_index + DDS_full_size <= buffer_length ) 1720 | { 1721 | unsigned int byte_offset = DDS_main_size; 1722 | memcpy( (void*)DDS_data, (const void*)(&buffer[buffer_index]), DDS_full_size ); 1723 | buffer_index += DDS_full_size; 1724 | /* upload the main chunk */ 1725 | if( uncompressed ) 1726 | { 1727 | /* and remember, DXT uncompressed uses BGR(A), 1728 | so swap to RGB(A) for ALL MIPmap levels */ 1729 | for( i = 0; i < DDS_full_size; i += block_size ) 1730 | { 1731 | unsigned char temp = DDS_data[i]; 1732 | DDS_data[i] = DDS_data[i+2]; 1733 | DDS_data[i+2] = temp; 1734 | } 1735 | glTexImage2D( 1736 | cf_target, 0, 1737 | S3TC_type, width, height, 0, 1738 | S3TC_type, GL_UNSIGNED_BYTE, DDS_data ); 1739 | } else 1740 | { 1741 | soilGlCompressedTexImage2D( 1742 | cf_target, 0, 1743 | S3TC_type, width, height, 0, 1744 | DDS_main_size, DDS_data ); 1745 | } 1746 | /* upload the mipmaps, if we have them */ 1747 | for( i = 1; i <= mipmaps; ++i ) 1748 | { 1749 | int w, h, mip_size; 1750 | w = width >> i; 1751 | h = height >> i; 1752 | if( w < 1 ) 1753 | { 1754 | w = 1; 1755 | } 1756 | if( h < 1 ) 1757 | { 1758 | h = 1; 1759 | } 1760 | /* upload this mipmap */ 1761 | if( uncompressed ) 1762 | { 1763 | mip_size = w*h*block_size; 1764 | glTexImage2D( 1765 | cf_target, i, 1766 | S3TC_type, w, h, 0, 1767 | S3TC_type, GL_UNSIGNED_BYTE, &DDS_data[byte_offset] ); 1768 | } else 1769 | { 1770 | mip_size = ((w+3)/4)*((h+3)/4)*block_size; 1771 | soilGlCompressedTexImage2D( 1772 | cf_target, i, 1773 | S3TC_type, w, h, 0, 1774 | mip_size, &DDS_data[byte_offset] ); 1775 | } 1776 | /* and move to the next mipmap */ 1777 | byte_offset += mip_size; 1778 | } 1779 | /* it worked! */ 1780 | result_string_pointer = "DDS file loaded"; 1781 | } else 1782 | { 1783 | glDeleteTextures( 1, & tex_ID ); 1784 | tex_ID = 0; 1785 | cf_target = ogl_target_end + 1; 1786 | result_string_pointer = "DDS file was too small for expected image data"; 1787 | } 1788 | }/* end reading each face */ 1789 | SOIL_free_image_data( DDS_data ); 1790 | if( tex_ID ) 1791 | { 1792 | /* did I have MIPmaps? */ 1793 | if( mipmaps > 0 ) 1794 | { 1795 | /* instruct OpenGL to use the MIPmaps */ 1796 | glTexParameteri( opengl_texture_type, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); 1797 | glTexParameteri( opengl_texture_type, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR ); 1798 | } else 1799 | { 1800 | /* instruct OpenGL _NOT_ to use the MIPmaps */ 1801 | glTexParameteri( opengl_texture_type, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); 1802 | glTexParameteri( opengl_texture_type, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); 1803 | } 1804 | /* does the user want clamping, or wrapping? */ 1805 | if( flags & SOIL_FLAG_TEXTURE_REPEATS ) 1806 | { 1807 | glTexParameteri( opengl_texture_type, GL_TEXTURE_WRAP_S, GL_REPEAT ); 1808 | glTexParameteri( opengl_texture_type, GL_TEXTURE_WRAP_T, GL_REPEAT ); 1809 | glTexParameteri( opengl_texture_type, SOIL_TEXTURE_WRAP_R, GL_REPEAT ); 1810 | } else 1811 | { 1812 | /* unsigned int clamp_mode = SOIL_CLAMP_TO_EDGE; */ 1813 | unsigned int clamp_mode = GL_CLAMP; 1814 | glTexParameteri( opengl_texture_type, GL_TEXTURE_WRAP_S, clamp_mode ); 1815 | glTexParameteri( opengl_texture_type, GL_TEXTURE_WRAP_T, clamp_mode ); 1816 | glTexParameteri( opengl_texture_type, SOIL_TEXTURE_WRAP_R, clamp_mode ); 1817 | } 1818 | } 1819 | 1820 | quick_exit: 1821 | /* report success or failure */ 1822 | return tex_ID; 1823 | } 1824 | 1825 | unsigned int SOIL_direct_load_DDS( 1826 | const char *filename, 1827 | unsigned int reuse_texture_ID, 1828 | int flags, 1829 | int loading_as_cubemap ) 1830 | { 1831 | FILE *f; 1832 | unsigned char *buffer; 1833 | size_t buffer_length, bytes_read; 1834 | unsigned int tex_ID = 0; 1835 | /* error checks */ 1836 | if( NULL == filename ) 1837 | { 1838 | result_string_pointer = "NULL filename"; 1839 | return 0; 1840 | } 1841 | f = fopen( filename, "rb" ); 1842 | if( NULL == f ) 1843 | { 1844 | /* the file doesn't seem to exist (or be open-able) */ 1845 | result_string_pointer = "Can not find DDS file"; 1846 | return 0; 1847 | } 1848 | fseek( f, 0, SEEK_END ); 1849 | buffer_length = ftell( f ); 1850 | fseek( f, 0, SEEK_SET ); 1851 | buffer = (unsigned char *) malloc( buffer_length ); 1852 | if( NULL == buffer ) 1853 | { 1854 | result_string_pointer = "malloc failed"; 1855 | fclose( f ); 1856 | return 0; 1857 | } 1858 | bytes_read = fread( (void*)buffer, 1, buffer_length, f ); 1859 | fclose( f ); 1860 | if( bytes_read < buffer_length ) 1861 | { 1862 | /* huh? */ 1863 | buffer_length = bytes_read; 1864 | } 1865 | /* now try to do the loading */ 1866 | tex_ID = SOIL_direct_load_DDS_from_memory( 1867 | (const unsigned char *const)buffer, buffer_length, 1868 | reuse_texture_ID, flags, loading_as_cubemap ); 1869 | SOIL_free_image_data( buffer ); 1870 | return tex_ID; 1871 | } 1872 | 1873 | int query_NPOT_capability( void ) 1874 | { 1875 | /* check for the capability */ 1876 | if( has_NPOT_capability == SOIL_CAPABILITY_UNKNOWN ) 1877 | { 1878 | /* we haven't yet checked for the capability, do so */ 1879 | if( 1880 | (NULL == strstr( (char const*)glGetString( GL_EXTENSIONS ), 1881 | "GL_ARB_texture_non_power_of_two" ) ) 1882 | ) 1883 | { 1884 | /* not there, flag the failure */ 1885 | has_NPOT_capability = SOIL_CAPABILITY_NONE; 1886 | } else 1887 | { 1888 | /* it's there! */ 1889 | has_NPOT_capability = SOIL_CAPABILITY_PRESENT; 1890 | } 1891 | } 1892 | /* let the user know if we can do non-power-of-two textures or not */ 1893 | return has_NPOT_capability; 1894 | } 1895 | 1896 | int query_tex_rectangle_capability( void ) 1897 | { 1898 | /* check for the capability */ 1899 | if( has_tex_rectangle_capability == SOIL_CAPABILITY_UNKNOWN ) 1900 | { 1901 | /* we haven't yet checked for the capability, do so */ 1902 | if( 1903 | (NULL == strstr( (char const*)glGetString( GL_EXTENSIONS ), 1904 | "GL_ARB_texture_rectangle" ) ) 1905 | && 1906 | (NULL == strstr( (char const*)glGetString( GL_EXTENSIONS ), 1907 | "GL_EXT_texture_rectangle" ) ) 1908 | && 1909 | (NULL == strstr( (char const*)glGetString( GL_EXTENSIONS ), 1910 | "GL_NV_texture_rectangle" ) ) 1911 | ) 1912 | { 1913 | /* not there, flag the failure */ 1914 | has_tex_rectangle_capability = SOIL_CAPABILITY_NONE; 1915 | } else 1916 | { 1917 | /* it's there! */ 1918 | has_tex_rectangle_capability = SOIL_CAPABILITY_PRESENT; 1919 | } 1920 | } 1921 | /* let the user know if we can do texture rectangles or not */ 1922 | return has_tex_rectangle_capability; 1923 | } 1924 | 1925 | int query_cubemap_capability( void ) 1926 | { 1927 | /* check for the capability */ 1928 | if( has_cubemap_capability == SOIL_CAPABILITY_UNKNOWN ) 1929 | { 1930 | /* we haven't yet checked for the capability, do so */ 1931 | if( 1932 | (NULL == strstr( (char const*)glGetString( GL_EXTENSIONS ), 1933 | "GL_ARB_texture_cube_map" ) ) 1934 | && 1935 | (NULL == strstr( (char const*)glGetString( GL_EXTENSIONS ), 1936 | "GL_EXT_texture_cube_map" ) ) 1937 | ) 1938 | { 1939 | /* not there, flag the failure */ 1940 | has_cubemap_capability = SOIL_CAPABILITY_NONE; 1941 | } else 1942 | { 1943 | /* it's there! */ 1944 | has_cubemap_capability = SOIL_CAPABILITY_PRESENT; 1945 | } 1946 | } 1947 | /* let the user know if we can do cubemaps or not */ 1948 | return has_cubemap_capability; 1949 | } 1950 | 1951 | int query_DXT_capability( void ) 1952 | { 1953 | /* check for the capability */ 1954 | if( has_DXT_capability == SOIL_CAPABILITY_UNKNOWN ) 1955 | { 1956 | /* we haven't yet checked for the capability, do so */ 1957 | if( NULL == strstr( 1958 | (char const*)glGetString( GL_EXTENSIONS ), 1959 | "GL_EXT_texture_compression_s3tc" ) ) 1960 | { 1961 | /* not there, flag the failure */ 1962 | has_DXT_capability = SOIL_CAPABILITY_NONE; 1963 | } else 1964 | { 1965 | /* and find the address of the extension function */ 1966 | P_SOIL_GLCOMPRESSEDTEXIMAGE2DPROC ext_addr = NULL; 1967 | #ifdef WIN32 1968 | ext_addr = (P_SOIL_GLCOMPRESSEDTEXIMAGE2DPROC) 1969 | wglGetProcAddress 1970 | ( 1971 | "glCompressedTexImage2DARB" 1972 | ); 1973 | #elif defined(__APPLE__) || defined(__APPLE_CC__) 1974 | /* I can't test this Apple stuff! */ 1975 | CFBundleRef bundle; 1976 | CFURLRef bundleURL = 1977 | CFURLCreateWithFileSystemPath( 1978 | kCFAllocatorDefault, 1979 | CFSTR("/System/Library/Frameworks/OpenGL.framework"), 1980 | kCFURLPOSIXPathStyle, 1981 | true ); 1982 | CFStringRef extensionName = 1983 | CFStringCreateWithCString( 1984 | kCFAllocatorDefault, 1985 | "glCompressedTexImage2DARB", 1986 | kCFStringEncodingASCII ); 1987 | bundle = CFBundleCreate( kCFAllocatorDefault, bundleURL ); 1988 | assert( bundle != NULL ); 1989 | ext_addr = (P_SOIL_GLCOMPRESSEDTEXIMAGE2DPROC) 1990 | CFBundleGetFunctionPointerForName 1991 | ( 1992 | bundle, extensionName 1993 | ); 1994 | CFRelease( bundleURL ); 1995 | CFRelease( extensionName ); 1996 | CFRelease( bundle ); 1997 | #else 1998 | ext_addr = (P_SOIL_GLCOMPRESSEDTEXIMAGE2DPROC) 1999 | glXGetProcAddressARB 2000 | ( 2001 | (const GLubyte *)"glCompressedTexImage2DARB" 2002 | ); 2003 | #endif 2004 | /* Flag it so no checks needed later */ 2005 | if( NULL == ext_addr ) 2006 | { 2007 | /* hmm, not good!! This should not happen, but does on my 2008 | laptop's VIA chipset. The GL_EXT_texture_compression_s3tc 2009 | spec requires that ARB_texture_compression be present too. 2010 | this means I can upload and have the OpenGL drive do the 2011 | conversion, but I can't use my own routines or load DDS files 2012 | from disk and upload them directly [8^( */ 2013 | has_DXT_capability = SOIL_CAPABILITY_NONE; 2014 | } else 2015 | { 2016 | /* all's well! */ 2017 | soilGlCompressedTexImage2D = ext_addr; 2018 | has_DXT_capability = SOIL_CAPABILITY_PRESENT; 2019 | } 2020 | } 2021 | } 2022 | /* let the user know if we can do DXT or not */ 2023 | return has_DXT_capability; 2024 | } 2025 | --------------------------------------------------------------------------------