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 <windows.h>
21 	#include <wingdi.h>
22 	#include <GL/gl.h>
23 #elif defined(__APPLE__) || defined(__APPLE_CC__)
24 	/*	I can't test this Apple stuff!	*/
25 	#include <OpenGL/gl.h>
26 	#include <Carbon/Carbon.h>
27 	#define APIENTRY
28 #else
29 	#include <GL/gl.h>
30 	#include <GL/glx.h>
31 #endif
32 
33 #include "SOIL.h"
34 #include "stb_image_aug.h"
35 #include "image_helper.h"
36 #include "image_DXT.h"
37 
38 #include <stdlib.h>
39 #include <string.h>
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
SOIL_load_OGL_texture(const char * filename,int force_channels,unsigned int reuse_texture_ID,unsigned int flags)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 {
118 	int width, height;
119 	return SOIL_load_OGL_texture_size(filename,force_channels,reuse_texture_ID,flags,&width, &height);
120 }
121 
122 unsigned int
SOIL_load_OGL_texture_size(const char * filename,int force_channels,unsigned int reuse_texture_ID,unsigned int flags,int * width,int * height)123 	SOIL_load_OGL_texture_size
124 	(
125 		const char *filename,
126 		int force_channels,
127 		unsigned int reuse_texture_ID,
128 		unsigned int flags,
129 		int *width,
130 		int *height
131 	)
132 {
133 	/*	variables	*/
134 	unsigned char* img;
135 	int channels;
136 	unsigned int tex_id;
137 	/*	does the user want direct uploading of the image as a DDS file?	*/
138 	if( flags & SOIL_FLAG_DDS_LOAD_DIRECT )
139 	{
140 		/*	1st try direct loading of the image as a DDS file
141 			note: direct uploading will only load what is in the
142 			DDS file, no MIPmaps will be generated, the image will
143 			not be flipped, etc.	*/
144 		tex_id = SOIL_direct_load_DDS( filename, reuse_texture_ID, flags, 0 );
145 		if( tex_id )
146 		{
147 			/*	hey, it worked!!	*/
148 			return tex_id;
149 		}
150 	}
151 	/*	try to load the image	*/
152 	img = SOIL_load_image( filename, width, height, &channels, force_channels );
153 	/*	channels holds the original number of channels, which may have been forced	*/
154 	if( (force_channels >= 1) && (force_channels <= 4) )
155 	{
156 		channels = force_channels;
157 	}
158 	if( NULL == img )
159 	{
160 		/*	image loading failed	*/
161 		result_string_pointer = stbi_failure_reason();
162 		return 0;
163 	}
164 	/*	OK, make it a texture!	*/
165 	tex_id = SOIL_internal_create_OGL_texture(
166 			img, *width, *height, channels,
167 			reuse_texture_ID, flags,
168 			GL_TEXTURE_2D, GL_TEXTURE_2D,
169 			GL_MAX_TEXTURE_SIZE );
170 	/*	and nuke the image data	*/
171 	SOIL_free_image_data( img );
172 	/*	and return the handle, such as it is	*/
173 	return tex_id;
174 }
175 
176 unsigned int
SOIL_load_OGL_HDR_texture(const char * filename,int fake_HDR_format,int rescale_to_max,unsigned int reuse_texture_ID,unsigned int flags)177 	SOIL_load_OGL_HDR_texture
178 	(
179 		const char *filename,
180 		int fake_HDR_format,
181 		int rescale_to_max,
182 		unsigned int reuse_texture_ID,
183 		unsigned int flags
184 	)
185 {
186 	/*	variables	*/
187 	unsigned char* img;
188 	int width, height, channels;
189 	unsigned int tex_id;
190 	/*	no direct uploading of the image as a DDS file	*/
191 	/* error check */
192 	if( (fake_HDR_format != SOIL_HDR_RGBE) &&
193 		(fake_HDR_format != SOIL_HDR_RGBdivA) &&
194 		(fake_HDR_format != SOIL_HDR_RGBdivA2) )
195 	{
196 		result_string_pointer = "Invalid fake HDR format specified";
197 		return 0;
198 	}
199 	/*	try to load the image (only the HDR type) */
200 	img = stbi_hdr_load_rgbe( filename, &width, &height, &channels, 4 );
201 	/*	channels holds the original number of channels, which may have been forced	*/
202 	if( NULL == img )
203 	{
204 		/*	image loading failed	*/
205 		result_string_pointer = stbi_failure_reason();
206 		return 0;
207 	}
208 	/* the load worked, do I need to convert it? */
209 	if( fake_HDR_format == SOIL_HDR_RGBdivA )
210 	{
211 		RGBE_to_RGBdivA( img, width, height, rescale_to_max );
212 	} else if( fake_HDR_format == SOIL_HDR_RGBdivA2 )
213 	{
214 		RGBE_to_RGBdivA2( img, width, height, rescale_to_max );
215 	}
216 	/*	OK, make it a texture!	*/
217 	tex_id = SOIL_internal_create_OGL_texture(
218 			img, width, height, channels,
219 			reuse_texture_ID, flags,
220 			GL_TEXTURE_2D, GL_TEXTURE_2D,
221 			GL_MAX_TEXTURE_SIZE );
222 	/*	and nuke the image data	*/
223 	SOIL_free_image_data( img );
224 	/*	and return the handle, such as it is	*/
225 	return tex_id;
226 }
227 
228 unsigned int
SOIL_load_OGL_texture_from_memory(const unsigned char * const buffer,int buffer_length,int force_channels,unsigned int reuse_texture_ID,unsigned int flags)229 	SOIL_load_OGL_texture_from_memory
230 	(
231 		const unsigned char *const buffer,
232 		int buffer_length,
233 		int force_channels,
234 		unsigned int reuse_texture_ID,
235 		unsigned int flags
236 	)
237 {
238 	/*	variables	*/
239 	unsigned char* img;
240 	int width, height, channels;
241 	unsigned int tex_id;
242 	/*	does the user want direct uploading of the image as a DDS file?	*/
243 	if( flags & SOIL_FLAG_DDS_LOAD_DIRECT )
244 	{
245 		/*	1st try direct loading of the image as a DDS file
246 			note: direct uploading will only load what is in the
247 			DDS file, no MIPmaps will be generated, the image will
248 			not be flipped, etc.	*/
249 		tex_id = SOIL_direct_load_DDS_from_memory(
250 				buffer, buffer_length,
251 				reuse_texture_ID, flags, 0 );
252 		if( tex_id )
253 		{
254 			/*	hey, it worked!!	*/
255 			return tex_id;
256 		}
257 	}
258 	/*	try to load the image	*/
259 	img = SOIL_load_image_from_memory(
260 					buffer, buffer_length,
261 					&width, &height, &channels,
262 					force_channels );
263 	/*	channels holds the original number of channels, which may have been forced	*/
264 	if( (force_channels >= 1) && (force_channels <= 4) )
265 	{
266 		channels = force_channels;
267 	}
268 	if( NULL == img )
269 	{
270 		/*	image loading failed	*/
271 		result_string_pointer = stbi_failure_reason();
272 		return 0;
273 	}
274 	/*	OK, make it a texture!	*/
275 	tex_id = SOIL_internal_create_OGL_texture(
276 			img, width, height, channels,
277 			reuse_texture_ID, flags,
278 			GL_TEXTURE_2D, GL_TEXTURE_2D,
279 			GL_MAX_TEXTURE_SIZE );
280 	/*	and nuke the image data	*/
281 	SOIL_free_image_data( img );
282 	/*	and return the handle, such as it is	*/
283 	return tex_id;
284 }
285 
286 unsigned int
SOIL_load_OGL_cubemap(const char * x_pos_file,const char * x_neg_file,const char * y_pos_file,const char * y_neg_file,const char * z_pos_file,const char * z_neg_file,int force_channels,unsigned int reuse_texture_ID,unsigned int flags)287 	SOIL_load_OGL_cubemap
288 	(
289 		const char *x_pos_file,
290 		const char *x_neg_file,
291 		const char *y_pos_file,
292 		const char *y_neg_file,
293 		const char *z_pos_file,
294 		const char *z_neg_file,
295 		int force_channels,
296 		unsigned int reuse_texture_ID,
297 		unsigned int flags
298 	)
299 {
300 	/*	variables	*/
301 	unsigned char* img;
302 	int width, height, channels;
303 	unsigned int tex_id;
304 	/*	error checking	*/
305 	if( (x_pos_file == NULL) ||
306 		(x_neg_file == NULL) ||
307 		(y_pos_file == NULL) ||
308 		(y_neg_file == NULL) ||
309 		(z_pos_file == NULL) ||
310 		(z_neg_file == NULL) )
311 	{
312 		result_string_pointer = "Invalid cube map files list";
313 		return 0;
314 	}
315 	/*	capability checking	*/
316 	if( query_cubemap_capability() != SOIL_CAPABILITY_PRESENT )
317 	{
318 		result_string_pointer = "No cube map capability present";
319 		return 0;
320 	}
321 	/*	1st face: try to load the image	*/
322 	img = SOIL_load_image( x_pos_file, &width, &height, &channels, force_channels );
323 	/*	channels holds the original number of channels, which may have been forced	*/
324 	if( (force_channels >= 1) && (force_channels <= 4) )
325 	{
326 		channels = force_channels;
327 	}
328 	if( NULL == img )
329 	{
330 		/*	image loading failed	*/
331 		result_string_pointer = stbi_failure_reason();
332 		return 0;
333 	}
334 	/*	upload the texture, and create a texture ID if necessary	*/
335 	tex_id = SOIL_internal_create_OGL_texture(
336 			img, width, height, channels,
337 			reuse_texture_ID, flags,
338 			SOIL_TEXTURE_CUBE_MAP, SOIL_TEXTURE_CUBE_MAP_POSITIVE_X,
339 			SOIL_MAX_CUBE_MAP_TEXTURE_SIZE );
340 	/*	and nuke the image data	*/
341 	SOIL_free_image_data( img );
342 	/*	continue?	*/
343 	if( tex_id != 0 )
344 	{
345 		/*	1st face: try to load the image	*/
346 		img = SOIL_load_image( x_neg_file, &width, &height, &channels, force_channels );
347 		/*	channels holds the original number of channels, which may have been forced	*/
348 		if( (force_channels >= 1) && (force_channels <= 4) )
349 		{
350 			channels = force_channels;
351 		}
352 		if( NULL == img )
353 		{
354 			/*	image loading failed	*/
355 			result_string_pointer = stbi_failure_reason();
356 			return 0;
357 		}
358 		/*	upload the texture, but reuse the assigned texture ID	*/
359 		tex_id = SOIL_internal_create_OGL_texture(
360 				img, width, height, channels,
361 				tex_id, flags,
362 				SOIL_TEXTURE_CUBE_MAP, SOIL_TEXTURE_CUBE_MAP_NEGATIVE_X,
363 				SOIL_MAX_CUBE_MAP_TEXTURE_SIZE );
364 		/*	and nuke the image data	*/
365 		SOIL_free_image_data( img );
366 	}
367 	/*	continue?	*/
368 	if( tex_id != 0 )
369 	{
370 		/*	1st face: try to load the image	*/
371 		img = SOIL_load_image( y_pos_file, &width, &height, &channels, force_channels );
372 		/*	channels holds the original number of channels, which may have been forced	*/
373 		if( (force_channels >= 1) && (force_channels <= 4) )
374 		{
375 			channels = force_channels;
376 		}
377 		if( NULL == img )
378 		{
379 			/*	image loading failed	*/
380 			result_string_pointer = stbi_failure_reason();
381 			return 0;
382 		}
383 		/*	upload the texture, but reuse the assigned texture ID	*/
384 		tex_id = SOIL_internal_create_OGL_texture(
385 				img, width, height, channels,
386 				tex_id, flags,
387 				SOIL_TEXTURE_CUBE_MAP, SOIL_TEXTURE_CUBE_MAP_POSITIVE_Y,
388 				SOIL_MAX_CUBE_MAP_TEXTURE_SIZE );
389 		/*	and nuke the image data	*/
390 		SOIL_free_image_data( img );
391 	}
392 	/*	continue?	*/
393 	if( tex_id != 0 )
394 	{
395 		/*	1st face: try to load the image	*/
396 		img = SOIL_load_image( y_neg_file, &width, &height, &channels, force_channels );
397 		/*	channels holds the original number of channels, which may have been forced	*/
398 		if( (force_channels >= 1) && (force_channels <= 4) )
399 		{
400 			channels = force_channels;
401 		}
402 		if( NULL == img )
403 		{
404 			/*	image loading failed	*/
405 			result_string_pointer = stbi_failure_reason();
406 			return 0;
407 		}
408 		/*	upload the texture, but reuse the assigned texture ID	*/
409 		tex_id = SOIL_internal_create_OGL_texture(
410 				img, width, height, channels,
411 				tex_id, flags,
412 				SOIL_TEXTURE_CUBE_MAP, SOIL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
413 				SOIL_MAX_CUBE_MAP_TEXTURE_SIZE );
414 		/*	and nuke the image data	*/
415 		SOIL_free_image_data( img );
416 	}
417 	/*	continue?	*/
418 	if( tex_id != 0 )
419 	{
420 		/*	1st face: try to load the image	*/
421 		img = SOIL_load_image( z_pos_file, &width, &height, &channels, force_channels );
422 		/*	channels holds the original number of channels, which may have been forced	*/
423 		if( (force_channels >= 1) && (force_channels <= 4) )
424 		{
425 			channels = force_channels;
426 		}
427 		if( NULL == img )
428 		{
429 			/*	image loading failed	*/
430 			result_string_pointer = stbi_failure_reason();
431 			return 0;
432 		}
433 		/*	upload the texture, but reuse the assigned texture ID	*/
434 		tex_id = SOIL_internal_create_OGL_texture(
435 				img, width, height, channels,
436 				tex_id, flags,
437 				SOIL_TEXTURE_CUBE_MAP, SOIL_TEXTURE_CUBE_MAP_POSITIVE_Z,
438 				SOIL_MAX_CUBE_MAP_TEXTURE_SIZE );
439 		/*	and nuke the image data	*/
440 		SOIL_free_image_data( img );
441 	}
442 	/*	continue?	*/
443 	if( tex_id != 0 )
444 	{
445 		/*	1st face: try to load the image	*/
446 		img = SOIL_load_image( z_neg_file, &width, &height, &channels, force_channels );
447 		/*	channels holds the original number of channels, which may have been forced	*/
448 		if( (force_channels >= 1) && (force_channels <= 4) )
449 		{
450 			channels = force_channels;
451 		}
452 		if( NULL == img )
453 		{
454 			/*	image loading failed	*/
455 			result_string_pointer = stbi_failure_reason();
456 			return 0;
457 		}
458 		/*	upload the texture, but reuse the assigned texture ID	*/
459 		tex_id = SOIL_internal_create_OGL_texture(
460 				img, width, height, channels,
461 				tex_id, flags,
462 				SOIL_TEXTURE_CUBE_MAP, SOIL_TEXTURE_CUBE_MAP_NEGATIVE_Z,
463 				SOIL_MAX_CUBE_MAP_TEXTURE_SIZE );
464 		/*	and nuke the image data	*/
465 		SOIL_free_image_data( img );
466 	}
467 	/*	and return the handle, such as it is	*/
468 	return tex_id;
469 }
470 
471 unsigned int
SOIL_load_OGL_cubemap_from_memory(const unsigned char * const x_pos_buffer,int x_pos_buffer_length,const unsigned char * const x_neg_buffer,int x_neg_buffer_length,const unsigned char * const y_pos_buffer,int y_pos_buffer_length,const unsigned char * const y_neg_buffer,int y_neg_buffer_length,const unsigned char * const z_pos_buffer,int z_pos_buffer_length,const unsigned char * const z_neg_buffer,int z_neg_buffer_length,int force_channels,unsigned int reuse_texture_ID,unsigned int flags)472 	SOIL_load_OGL_cubemap_from_memory
473 	(
474 		const unsigned char *const x_pos_buffer,
475 		int x_pos_buffer_length,
476 		const unsigned char *const x_neg_buffer,
477 		int x_neg_buffer_length,
478 		const unsigned char *const y_pos_buffer,
479 		int y_pos_buffer_length,
480 		const unsigned char *const y_neg_buffer,
481 		int y_neg_buffer_length,
482 		const unsigned char *const z_pos_buffer,
483 		int z_pos_buffer_length,
484 		const unsigned char *const z_neg_buffer,
485 		int z_neg_buffer_length,
486 		int force_channels,
487 		unsigned int reuse_texture_ID,
488 		unsigned int flags
489 	)
490 {
491 	/*	variables	*/
492 	unsigned char* img;
493 	int width, height, channels;
494 	unsigned int tex_id;
495 	/*	error checking	*/
496 	if( (x_pos_buffer == NULL) ||
497 		(x_neg_buffer == NULL) ||
498 		(y_pos_buffer == NULL) ||
499 		(y_neg_buffer == NULL) ||
500 		(z_pos_buffer == NULL) ||
501 		(z_neg_buffer == NULL) )
502 	{
503 		result_string_pointer = "Invalid cube map buffers list";
504 		return 0;
505 	}
506 	/*	capability checking	*/
507 	if( query_cubemap_capability() != SOIL_CAPABILITY_PRESENT )
508 	{
509 		result_string_pointer = "No cube map capability present";
510 		return 0;
511 	}
512 	/*	1st face: try to load the image	*/
513 	img = SOIL_load_image_from_memory(
514 			x_pos_buffer, x_pos_buffer_length,
515 			&width, &height, &channels, force_channels );
516 	/*	channels holds the original number of channels, which may have been forced	*/
517 	if( (force_channels >= 1) && (force_channels <= 4) )
518 	{
519 		channels = force_channels;
520 	}
521 	if( NULL == img )
522 	{
523 		/*	image loading failed	*/
524 		result_string_pointer = stbi_failure_reason();
525 		return 0;
526 	}
527 	/*	upload the texture, and create a texture ID if necessary	*/
528 	tex_id = SOIL_internal_create_OGL_texture(
529 			img, width, height, channels,
530 			reuse_texture_ID, flags,
531 			SOIL_TEXTURE_CUBE_MAP, SOIL_TEXTURE_CUBE_MAP_POSITIVE_X,
532 			SOIL_MAX_CUBE_MAP_TEXTURE_SIZE );
533 	/*	and nuke the image data	*/
534 	SOIL_free_image_data( img );
535 	/*	continue?	*/
536 	if( tex_id != 0 )
537 	{
538 		/*	1st face: try to load the image	*/
539 		img = SOIL_load_image_from_memory(
540 				x_neg_buffer, x_neg_buffer_length,
541 				&width, &height, &channels, force_channels );
542 		/*	channels holds the original number of channels, which may have been forced	*/
543 		if( (force_channels >= 1) && (force_channels <= 4) )
544 		{
545 			channels = force_channels;
546 		}
547 		if( NULL == img )
548 		{
549 			/*	image loading failed	*/
550 			result_string_pointer = stbi_failure_reason();
551 			return 0;
552 		}
553 		/*	upload the texture, but reuse the assigned texture ID	*/
554 		tex_id = SOIL_internal_create_OGL_texture(
555 				img, width, height, channels,
556 				tex_id, flags,
557 				SOIL_TEXTURE_CUBE_MAP, SOIL_TEXTURE_CUBE_MAP_NEGATIVE_X,
558 				SOIL_MAX_CUBE_MAP_TEXTURE_SIZE );
559 		/*	and nuke the image data	*/
560 		SOIL_free_image_data( img );
561 	}
562 	/*	continue?	*/
563 	if( tex_id != 0 )
564 	{
565 		/*	1st face: try to load the image	*/
566 		img = SOIL_load_image_from_memory(
567 				y_pos_buffer, y_pos_buffer_length,
568 				&width, &height, &channels, force_channels );
569 		/*	channels holds the original number of channels, which may have been forced	*/
570 		if( (force_channels >= 1) && (force_channels <= 4) )
571 		{
572 			channels = force_channels;
573 		}
574 		if( NULL == img )
575 		{
576 			/*	image loading failed	*/
577 			result_string_pointer = stbi_failure_reason();
578 			return 0;
579 		}
580 		/*	upload the texture, but reuse the assigned texture ID	*/
581 		tex_id = SOIL_internal_create_OGL_texture(
582 				img, width, height, channels,
583 				tex_id, flags,
584 				SOIL_TEXTURE_CUBE_MAP, SOIL_TEXTURE_CUBE_MAP_POSITIVE_Y,
585 				SOIL_MAX_CUBE_MAP_TEXTURE_SIZE );
586 		/*	and nuke the image data	*/
587 		SOIL_free_image_data( img );
588 	}
589 	/*	continue?	*/
590 	if( tex_id != 0 )
591 	{
592 		/*	1st face: try to load the image	*/
593 		img = SOIL_load_image_from_memory(
594 				y_neg_buffer, y_neg_buffer_length,
595 				&width, &height, &channels, force_channels );
596 		/*	channels holds the original number of channels, which may have been forced	*/
597 		if( (force_channels >= 1) && (force_channels <= 4) )
598 		{
599 			channels = force_channels;
600 		}
601 		if( NULL == img )
602 		{
603 			/*	image loading failed	*/
604 			result_string_pointer = stbi_failure_reason();
605 			return 0;
606 		}
607 		/*	upload the texture, but reuse the assigned texture ID	*/
608 		tex_id = SOIL_internal_create_OGL_texture(
609 				img, width, height, channels,
610 				tex_id, flags,
611 				SOIL_TEXTURE_CUBE_MAP, SOIL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
612 				SOIL_MAX_CUBE_MAP_TEXTURE_SIZE );
613 		/*	and nuke the image data	*/
614 		SOIL_free_image_data( img );
615 	}
616 	/*	continue?	*/
617 	if( tex_id != 0 )
618 	{
619 		/*	1st face: try to load the image	*/
620 		img = SOIL_load_image_from_memory(
621 				z_pos_buffer, z_pos_buffer_length,
622 				&width, &height, &channels, force_channels );
623 		/*	channels holds the original number of channels, which may have been forced	*/
624 		if( (force_channels >= 1) && (force_channels <= 4) )
625 		{
626 			channels = force_channels;
627 		}
628 		if( NULL == img )
629 		{
630 			/*	image loading failed	*/
631 			result_string_pointer = stbi_failure_reason();
632 			return 0;
633 		}
634 		/*	upload the texture, but reuse the assigned texture ID	*/
635 		tex_id = SOIL_internal_create_OGL_texture(
636 				img, width, height, channels,
637 				tex_id, flags,
638 				SOIL_TEXTURE_CUBE_MAP, SOIL_TEXTURE_CUBE_MAP_POSITIVE_Z,
639 				SOIL_MAX_CUBE_MAP_TEXTURE_SIZE );
640 		/*	and nuke the image data	*/
641 		SOIL_free_image_data( img );
642 	}
643 	/*	continue?	*/
644 	if( tex_id != 0 )
645 	{
646 		/*	1st face: try to load the image	*/
647 		img = SOIL_load_image_from_memory(
648 				z_neg_buffer, z_neg_buffer_length,
649 				&width, &height, &channels, force_channels );
650 		/*	channels holds the original number of channels, which may have been forced	*/
651 		if( (force_channels >= 1) && (force_channels <= 4) )
652 		{
653 			channels = force_channels;
654 		}
655 		if( NULL == img )
656 		{
657 			/*	image loading failed	*/
658 			result_string_pointer = stbi_failure_reason();
659 			return 0;
660 		}
661 		/*	upload the texture, but reuse the assigned texture ID	*/
662 		tex_id = SOIL_internal_create_OGL_texture(
663 				img, width, height, channels,
664 				tex_id, flags,
665 				SOIL_TEXTURE_CUBE_MAP, SOIL_TEXTURE_CUBE_MAP_NEGATIVE_Z,
666 				SOIL_MAX_CUBE_MAP_TEXTURE_SIZE );
667 		/*	and nuke the image data	*/
668 		SOIL_free_image_data( img );
669 	}
670 	/*	and return the handle, such as it is	*/
671 	return tex_id;
672 }
673 
674 unsigned int
SOIL_load_OGL_single_cubemap(const char * filename,const char face_order[6],int force_channels,unsigned int reuse_texture_ID,unsigned int flags)675 	SOIL_load_OGL_single_cubemap
676 	(
677 		const char *filename,
678 		const char face_order[6],
679 		int force_channels,
680 		unsigned int reuse_texture_ID,
681 		unsigned int flags
682 	)
683 {
684 	/*	variables	*/
685 	unsigned char* img;
686 	int width, height, channels, i;
687 	unsigned int tex_id = 0;
688 	/*	error checking	*/
689 	if( filename == NULL )
690 	{
691 		result_string_pointer = "Invalid single cube map file name";
692 		return 0;
693 	}
694 	/*	does the user want direct uploading of the image as a DDS file?	*/
695 	if( flags & SOIL_FLAG_DDS_LOAD_DIRECT )
696 	{
697 		/*	1st try direct loading of the image as a DDS file
698 			note: direct uploading will only load what is in the
699 			DDS file, no MIPmaps will be generated, the image will
700 			not be flipped, etc.	*/
701 		tex_id = SOIL_direct_load_DDS( filename, reuse_texture_ID, flags, 1 );
702 		if( tex_id )
703 		{
704 			/*	hey, it worked!!	*/
705 			return tex_id;
706 		}
707 	}
708 	/*	face order checking	*/
709 	for( i = 0; i < 6; ++i )
710 	{
711 		if( (face_order[i] != 'N') &&
712 			(face_order[i] != 'S') &&
713 			(face_order[i] != 'W') &&
714 			(face_order[i] != 'E') &&
715 			(face_order[i] != 'U') &&
716 			(face_order[i] != 'D') )
717 		{
718 			result_string_pointer = "Invalid single cube map face order";
719 			return 0;
720 		};
721 	}
722 	/*	capability checking	*/
723 	if( query_cubemap_capability() != SOIL_CAPABILITY_PRESENT )
724 	{
725 		result_string_pointer = "No cube map capability present";
726 		return 0;
727 	}
728 	/*	1st off, try to load the full image	*/
729 	img = SOIL_load_image( filename, &width, &height, &channels, force_channels );
730 	/*	channels holds the original number of channels, which may have been forced	*/
731 	if( (force_channels >= 1) && (force_channels <= 4) )
732 	{
733 		channels = force_channels;
734 	}
735 	if( NULL == img )
736 	{
737 		/*	image loading failed	*/
738 		result_string_pointer = stbi_failure_reason();
739 		return 0;
740 	}
741 	/*	now, does this image have the right dimensions?	*/
742 	if( (width != 6*height) &&
743 		(6*width != height) )
744 	{
745 		SOIL_free_image_data( img );
746 		result_string_pointer = "Single cubemap image must have a 6:1 ratio";
747 		return 0;
748 	}
749 	/*	try the image split and create	*/
750 	tex_id = SOIL_create_OGL_single_cubemap(
751 			img, width, height, channels,
752 			face_order, reuse_texture_ID, flags
753 			);
754 	/*	nuke the temporary image data and return the texture handle	*/
755 	SOIL_free_image_data( img );
756 	return tex_id;
757 }
758 
759 unsigned int
SOIL_load_OGL_single_cubemap_from_memory(const unsigned char * const buffer,int buffer_length,const char face_order[6],int force_channels,unsigned int reuse_texture_ID,unsigned int flags)760 	SOIL_load_OGL_single_cubemap_from_memory
761 	(
762 		const unsigned char *const buffer,
763 		int buffer_length,
764 		const char face_order[6],
765 		int force_channels,
766 		unsigned int reuse_texture_ID,
767 		unsigned int flags
768 	)
769 {
770 	/*	variables	*/
771 	unsigned char* img;
772 	int width, height, channels, i;
773 	unsigned int tex_id = 0;
774 	/*	error checking	*/
775 	if( buffer == NULL )
776 	{
777 		result_string_pointer = "Invalid single cube map buffer";
778 		return 0;
779 	}
780 	/*	does the user want direct uploading of the image as a DDS file?	*/
781 	if( flags & SOIL_FLAG_DDS_LOAD_DIRECT )
782 	{
783 		/*	1st try direct loading of the image as a DDS file
784 			note: direct uploading will only load what is in the
785 			DDS file, no MIPmaps will be generated, the image will
786 			not be flipped, etc.	*/
787 		tex_id = SOIL_direct_load_DDS_from_memory(
788 				buffer, buffer_length,
789 				reuse_texture_ID, flags, 1 );
790 		if( tex_id )
791 		{
792 			/*	hey, it worked!!	*/
793 			return tex_id;
794 		}
795 	}
796 	/*	face order checking	*/
797 	for( i = 0; i < 6; ++i )
798 	{
799 		if( (face_order[i] != 'N') &&
800 			(face_order[i] != 'S') &&
801 			(face_order[i] != 'W') &&
802 			(face_order[i] != 'E') &&
803 			(face_order[i] != 'U') &&
804 			(face_order[i] != 'D') )
805 		{
806 			result_string_pointer = "Invalid single cube map face order";
807 			return 0;
808 		};
809 	}
810 	/*	capability checking	*/
811 	if( query_cubemap_capability() != SOIL_CAPABILITY_PRESENT )
812 	{
813 		result_string_pointer = "No cube map capability present";
814 		return 0;
815 	}
816 	/*	1st off, try to load the full image	*/
817 	img = SOIL_load_image_from_memory(
818 			buffer, buffer_length,
819 			&width, &height, &channels,
820 			force_channels );
821 	/*	channels holds the original number of channels, which may have been forced	*/
822 	if( (force_channels >= 1) && (force_channels <= 4) )
823 	{
824 		channels = force_channels;
825 	}
826 	if( NULL == img )
827 	{
828 		/*	image loading failed	*/
829 		result_string_pointer = stbi_failure_reason();
830 		return 0;
831 	}
832 	/*	now, does this image have the right dimensions?	*/
833 	if( (width != 6*height) &&
834 		(6*width != height) )
835 	{
836 		SOIL_free_image_data( img );
837 		result_string_pointer = "Single cubemap image must have a 6:1 ratio";
838 		return 0;
839 	}
840 	/*	try the image split and create	*/
841 	tex_id = SOIL_create_OGL_single_cubemap(
842 			img, width, height, channels,
843 			face_order, reuse_texture_ID, flags
844 			);
845 	/*	nuke the temporary image data and return the texture handle	*/
846 	SOIL_free_image_data( img );
847 	return tex_id;
848 }
849 
850 unsigned int
SOIL_create_OGL_single_cubemap(const unsigned char * const data,int width,int height,int channels,const char face_order[6],unsigned int reuse_texture_ID,unsigned int flags)851 	SOIL_create_OGL_single_cubemap
852 	(
853 		const unsigned char *const data,
854 		int width, int height, int channels,
855 		const char face_order[6],
856 		unsigned int reuse_texture_ID,
857 		unsigned int flags
858 	)
859 {
860 	/*	variables	*/
861 	unsigned char* sub_img;
862 	int dw, dh, sz, i;
863 	unsigned int tex_id;
864 	/*	error checking	*/
865 	if( data == NULL )
866 	{
867 		result_string_pointer = "Invalid single cube map image data";
868 		return 0;
869 	}
870 	/*	face order checking	*/
871 	for( i = 0; i < 6; ++i )
872 	{
873 		if( (face_order[i] != 'N') &&
874 			(face_order[i] != 'S') &&
875 			(face_order[i] != 'W') &&
876 			(face_order[i] != 'E') &&
877 			(face_order[i] != 'U') &&
878 			(face_order[i] != 'D') )
879 		{
880 			result_string_pointer = "Invalid single cube map face order";
881 			return 0;
882 		};
883 	}
884 	/*	capability checking	*/
885 	if( query_cubemap_capability() != SOIL_CAPABILITY_PRESENT )
886 	{
887 		result_string_pointer = "No cube map capability present";
888 		return 0;
889 	}
890 	/*	now, does this image have the right dimensions?	*/
891 	if( (width != 6*height) &&
892 		(6*width != height) )
893 	{
894 		result_string_pointer = "Single cubemap image must have a 6:1 ratio";
895 		return 0;
896 	}
897 	/*	which way am I stepping?	*/
898 	if( width > height )
899 	{
900 		dw = height;
901 		dh = 0;
902 	} else
903 	{
904 		dw = 0;
905 		dh = width;
906 	}
907 	sz = dw+dh;
908 	sub_img = (unsigned char *)malloc( sz*sz*channels );
909 	/*	do the splitting and uploading	*/
910 	tex_id = reuse_texture_ID;
911 	for( i = 0; i < 6; ++i )
912 	{
913 		int x, y, idx = 0;
914 		unsigned int cubemap_target = 0;
915 		/*	copy in the sub-image	*/
916 		for( y = i*dh; y < i*dh+sz; ++y )
917 		{
918 			for( x = i*dw*channels; x < (i*dw+sz)*channels; ++x )
919 			{
920 				sub_img[idx++] = data[y*width*channels+x];
921 			}
922 		}
923 		/*	what is my texture target?
924 			remember, this coordinate system is
925 			LHS if viewed from inside the cube!	*/
926 		switch( face_order[i] )
927 		{
928 		case 'N':
929 			cubemap_target = SOIL_TEXTURE_CUBE_MAP_POSITIVE_Z;
930 			break;
931 		case 'S':
932 			cubemap_target = SOIL_TEXTURE_CUBE_MAP_NEGATIVE_Z;
933 			break;
934 		case 'W':
935 			cubemap_target = SOIL_TEXTURE_CUBE_MAP_NEGATIVE_X;
936 			break;
937 		case 'E':
938 			cubemap_target = SOIL_TEXTURE_CUBE_MAP_POSITIVE_X;
939 			break;
940 		case 'U':
941 			cubemap_target = SOIL_TEXTURE_CUBE_MAP_POSITIVE_Y;
942 			break;
943 		case 'D':
944 			cubemap_target = SOIL_TEXTURE_CUBE_MAP_NEGATIVE_Y;
945 			break;
946 		}
947 		/*	upload it as a texture	*/
948 		tex_id = SOIL_internal_create_OGL_texture(
949 				sub_img, sz, sz, channels,
950 				tex_id, flags,
951 				SOIL_TEXTURE_CUBE_MAP,
952 				cubemap_target,
953 				SOIL_MAX_CUBE_MAP_TEXTURE_SIZE );
954 	}
955 	/*	and nuke the image and sub-image data	*/
956 	SOIL_free_image_data( sub_img );
957 	/*	and return the handle, such as it is	*/
958 	return tex_id;
959 }
960 
961 unsigned int
SOIL_create_OGL_texture(const unsigned char * const data,int width,int height,int channels,unsigned int reuse_texture_ID,unsigned int flags)962 	SOIL_create_OGL_texture
963 	(
964 		const unsigned char *const data,
965 		int width, int height, int channels,
966 		unsigned int reuse_texture_ID,
967 		unsigned int flags
968 	)
969 {
970 	/*	wrapper function for 2D textures	*/
971 	return SOIL_internal_create_OGL_texture(
972 				data, width, height, channels,
973 				reuse_texture_ID, flags,
974 				GL_TEXTURE_2D, GL_TEXTURE_2D,
975 				GL_MAX_TEXTURE_SIZE );
976 }
977 
978 #if SOIL_CHECK_FOR_GL_ERRORS
check_for_GL_errors(const char * calling_location)979 void check_for_GL_errors( const char *calling_location )
980 {
981 	/*	check for errors	*/
982 	GLenum err_code = glGetError();
983 	while( GL_NO_ERROR != err_code )
984 	{
985 		printf( "OpenGL Error @ %s: %i", calling_location, err_code );
986 		err_code = glGetError();
987 	}
988 }
989 #else
check_for_GL_errors(const char * calling_location)990 void check_for_GL_errors( const char *calling_location )
991 {
992 	/*	no check for errors	*/
993 }
994 #endif
995 
996 unsigned int
SOIL_internal_create_OGL_texture(const unsigned char * const data,int width,int height,int channels,unsigned int reuse_texture_ID,unsigned int flags,unsigned int opengl_texture_type,unsigned int opengl_texture_target,unsigned int texture_check_size_enum)997 	SOIL_internal_create_OGL_texture
998 	(
999 		const unsigned char *const data,
1000 		int width, int height, int channels,
1001 		unsigned int reuse_texture_ID,
1002 		unsigned int flags,
1003 		unsigned int opengl_texture_type,
1004 		unsigned int opengl_texture_target,
1005 		unsigned int texture_check_size_enum
1006 	)
1007 {
1008 	/*	variables	*/
1009 	unsigned char* img;
1010 	unsigned int tex_id;
1011 	unsigned int internal_texture_format = 0, original_texture_format = 0;
1012 	int DXT_mode = SOIL_CAPABILITY_UNKNOWN;
1013 	int max_supported_size;
1014 	/*	If the user wants to use the texture rectangle I kill a few flags	*/
1015 	if( flags & SOIL_FLAG_TEXTURE_RECTANGLE )
1016 	{
1017 		/*	well, the user asked for it, can we do that?	*/
1018 		if( query_tex_rectangle_capability() == SOIL_CAPABILITY_PRESENT )
1019 		{
1020 			/*	only allow this if the user in _NOT_ trying to do a cubemap!	*/
1021 			if( opengl_texture_type == GL_TEXTURE_2D )
1022 			{
1023 				/*	clean out the flags that cannot be used with texture rectangles	*/
1024 				flags &= ~(
1025 						SOIL_FLAG_POWER_OF_TWO | SOIL_FLAG_MIPMAPS |
1026 						SOIL_FLAG_TEXTURE_REPEATS
1027 					);
1028 				/*	and change my target	*/
1029 				opengl_texture_target = SOIL_TEXTURE_RECTANGLE_ARB;
1030 				opengl_texture_type = SOIL_TEXTURE_RECTANGLE_ARB;
1031 			} else
1032 			{
1033 				/*	not allowed for any other uses (yes, I'm looking at you, cubemaps!)	*/
1034 				flags &= ~SOIL_FLAG_TEXTURE_RECTANGLE;
1035 			}
1036 
1037 		} else
1038 		{
1039 			/*	can't do it, and that is a breakable offense (uv coords use pixels instead of [0,1]!)	*/
1040 			result_string_pointer = "Texture Rectangle extension unsupported";
1041 			return 0;
1042 		}
1043 	}
1044 	/*	create a copy the image data	*/
1045 	img = (unsigned char*)malloc( width*height*channels );
1046 	memcpy( img, data, width*height*channels );
1047 	/*	does the user want me to invert the image?	*/
1048 	if( flags & SOIL_FLAG_INVERT_Y )
1049 	{
1050 		int i, j;
1051 		for( j = 0; j*2 < height; ++j )
1052 		{
1053 			int index1 = j * width * channels;
1054 			int index2 = (height - 1 - j) * width * channels;
1055 			for( i = width * channels; i > 0; --i )
1056 			{
1057 				unsigned char temp = img[index1];
1058 				img[index1] = img[index2];
1059 				img[index2] = temp;
1060 				++index1;
1061 				++index2;
1062 			}
1063 		}
1064 	}
1065 	/*	does the user want me to scale the colors into the NTSC safe RGB range?	*/
1066 	if( flags & SOIL_FLAG_NTSC_SAFE_RGB )
1067 	{
1068 		scale_image_RGB_to_NTSC_safe( img, width, height, channels );
1069 	}
1070 	/*	does the user want me to convert from straight to pre-multiplied alpha?
1071 		(and do we even _have_ alpha?)	*/
1072 	if( flags & SOIL_FLAG_MULTIPLY_ALPHA )
1073 	{
1074 		int i;
1075 		switch( channels )
1076 		{
1077 		case 2:
1078 			for( i = 0; i < 2*width*height; i += 2 )
1079 			{
1080 				img[i] = (img[i] * img[i+1] + 128) >> 8;
1081 			}
1082 			break;
1083 		case 4:
1084 			for( i = 0; i < 4*width*height; i += 4 )
1085 			{
1086 				img[i+0] = (img[i+0] * img[i+3] + 128) >> 8;
1087 				img[i+1] = (img[i+1] * img[i+3] + 128) >> 8;
1088 				img[i+2] = (img[i+2] * img[i+3] + 128) >> 8;
1089 			}
1090 			break;
1091 		default:
1092 			/*	no other number of channels contains alpha data	*/
1093 			break;
1094 		}
1095 	}
1096 	/*	if the user can't support NPOT textures, make sure we force the POT option	*/
1097 	if( (query_NPOT_capability() == SOIL_CAPABILITY_NONE) &&
1098 		!(flags & SOIL_FLAG_TEXTURE_RECTANGLE) )
1099 	{
1100 		/*	add in the POT flag */
1101 		flags |= SOIL_FLAG_POWER_OF_TWO;
1102 	}
1103 	/*	how large of a texture can this OpenGL implementation handle?	*/
1104 	/*	texture_check_size_enum will be GL_MAX_TEXTURE_SIZE or SOIL_MAX_CUBE_MAP_TEXTURE_SIZE	*/
1105 	glGetIntegerv( texture_check_size_enum, &max_supported_size );
1106 	/*	do I need to make it a power of 2?	*/
1107 	if(
1108 		(flags & SOIL_FLAG_POWER_OF_TWO) ||	/*	user asked for it	*/
1109 		(flags & SOIL_FLAG_MIPMAPS) ||		/*	need it for the MIP-maps	*/
1110 		(width > max_supported_size) ||		/*	it's too big, (make sure it's	*/
1111 		(height > max_supported_size) )		/*	2^n for later down-sampling)	*/
1112 	{
1113 		int new_width = 1;
1114 		int new_height = 1;
1115 		while( new_width < width )
1116 		{
1117 			new_width *= 2;
1118 		}
1119 		while( new_height < height )
1120 		{
1121 			new_height *= 2;
1122 		}
1123 		/*	still?	*/
1124 		if( (new_width != width) || (new_height != height) )
1125 		{
1126 			/*	yep, resize	*/
1127 			unsigned char *resampled = (unsigned char*)malloc( channels*new_width*new_height );
1128 			up_scale_image(
1129 					img, width, height, channels,
1130 					resampled, new_width, new_height );
1131 			/*	OJO	this is for debug only!	*/
1132 			/*
1133 			SOIL_save_image( "\\showme.bmp", SOIL_SAVE_TYPE_BMP,
1134 							new_width, new_height, channels,
1135 							resampled );
1136 			*/
1137 			/*	nuke the old guy, then point it at the new guy	*/
1138 			SOIL_free_image_data( img );
1139 			img = resampled;
1140 			width = new_width;
1141 			height = new_height;
1142 		}
1143 	}
1144 	/*	now, if it is too large...	*/
1145 	if( (width > max_supported_size) || (height > max_supported_size) )
1146 	{
1147 		/*	I've already made it a power of two, so simply use the MIPmapping
1148 			code to reduce its size to the allowable maximum.	*/
1149 		unsigned char *resampled;
1150 		int reduce_block_x = 1, reduce_block_y = 1;
1151 		int new_width, new_height;
1152 		if( width > max_supported_size )
1153 		{
1154 			reduce_block_x = width / max_supported_size;
1155 		}
1156 		if( height > max_supported_size )
1157 		{
1158 			reduce_block_y = height / max_supported_size;
1159 		}
1160 		new_width = width / reduce_block_x;
1161 		new_height = height / reduce_block_y;
1162 		resampled = (unsigned char*)malloc( channels*new_width*new_height );
1163 		/*	perform the actual reduction	*/
1164 		mipmap_image(	img, width, height, channels,
1165 						resampled, reduce_block_x, reduce_block_y );
1166 		/*	nuke the old guy, then point it at the new guy	*/
1167 		SOIL_free_image_data( img );
1168 		img = resampled;
1169 		width = new_width;
1170 		height = new_height;
1171 	}
1172 	/*	does the user want us to use YCoCg color space?	*/
1173 	if( flags & SOIL_FLAG_CoCg_Y )
1174 	{
1175 		/*	this will only work with RGB and RGBA images */
1176 		convert_RGB_to_YCoCg( img, width, height, channels );
1177 		/*
1178 		save_image_as_DDS( "CoCg_Y.dds", width, height, channels, img );
1179 		*/
1180 	}
1181 	/*	create the OpenGL texture ID handle
1182     	(note: allowing a forced texture ID lets me reload a texture)	*/
1183     tex_id = reuse_texture_ID;
1184     if( tex_id == 0 )
1185     {
1186 		glGenTextures( 1, &tex_id );
1187     }
1188 	check_for_GL_errors( "glGenTextures" );
1189 	/* Note: sometimes glGenTextures fails (usually no OpenGL context)	*/
1190 	if( tex_id )
1191 	{
1192 		/*	and what type am I using as the internal texture format?	*/
1193 		switch( channels )
1194 		{
1195 		case 1:
1196 			original_texture_format = GL_LUMINANCE;
1197 			break;
1198 		case 2:
1199 			original_texture_format = GL_LUMINANCE_ALPHA;
1200 			break;
1201 		case 3:
1202 			original_texture_format = GL_RGB;
1203 			break;
1204 		case 4:
1205 			original_texture_format = GL_RGBA;
1206 			break;
1207 		}
1208 		internal_texture_format = original_texture_format;
1209 		/*	does the user want me to, and can I, save as DXT?	*/
1210 		if( flags & SOIL_FLAG_COMPRESS_TO_DXT )
1211 		{
1212 			DXT_mode = query_DXT_capability();
1213 			if( DXT_mode == SOIL_CAPABILITY_PRESENT )
1214 			{
1215 				/*	I can use DXT, whether I compress it or OpenGL does	*/
1216 				if( (channels & 1) == 1 )
1217 				{
1218 					/*	1 or 3 channels = DXT1	*/
1219 					internal_texture_format = SOIL_RGB_S3TC_DXT1;
1220 				} else
1221 				{
1222 					/*	2 or 4 channels = DXT5	*/
1223 					internal_texture_format = SOIL_RGBA_S3TC_DXT5;
1224 				}
1225 			}
1226 		}
1227 		/*  bind an OpenGL texture ID	*/
1228 		glBindTexture( opengl_texture_type, tex_id );
1229 		check_for_GL_errors( "glBindTexture" );
1230 		/*  upload the main image	*/
1231 		if( DXT_mode == SOIL_CAPABILITY_PRESENT )
1232 		{
1233 			/*	user wants me to do the DXT conversion!	*/
1234 			int DDS_size;
1235 			unsigned char *DDS_data = NULL;
1236 			if( (channels & 1) == 1 )
1237 			{
1238 				/*	RGB, use DXT1	*/
1239 				DDS_data = convert_image_to_DXT1( img, width, height, channels, &DDS_size );
1240 			} else
1241 			{
1242 				/*	RGBA, use DXT5	*/
1243 				DDS_data = convert_image_to_DXT5( img, width, height, channels, &DDS_size );
1244 			}
1245 			if( DDS_data )
1246 			{
1247 				soilGlCompressedTexImage2D(
1248 					opengl_texture_target, 0,
1249 					internal_texture_format, width, height, 0,
1250 					DDS_size, DDS_data );
1251 				check_for_GL_errors( "glCompressedTexImage2D" );
1252 				SOIL_free_image_data( DDS_data );
1253 				/*	printf( "Internal DXT compressor\n" );	*/
1254 			} else
1255 			{
1256 				/*	my compression failed, try the OpenGL driver's version	*/
1257 				glTexImage2D(
1258 					opengl_texture_target, 0,
1259 					internal_texture_format, width, height, 0,
1260 					original_texture_format, GL_UNSIGNED_BYTE, img );
1261 				check_for_GL_errors( "glTexImage2D" );
1262 				/*	printf( "OpenGL DXT compressor\n" );	*/
1263 			}
1264 		} else
1265 		{
1266 			/*	user want OpenGL to do all the work!	*/
1267 			glTexImage2D(
1268 				opengl_texture_target, 0,
1269 				internal_texture_format, width, height, 0,
1270 				original_texture_format, GL_UNSIGNED_BYTE, img );
1271 			check_for_GL_errors( "glTexImage2D" );
1272 			/*printf( "OpenGL DXT compressor\n" );	*/
1273 		}
1274 		/*	are any MIPmaps desired?	*/
1275 		if( flags & SOIL_FLAG_MIPMAPS )
1276 		{
1277 			int MIPlevel = 1;
1278 			int MIPwidth = (width+1) / 2;
1279 			int MIPheight = (height+1) / 2;
1280 			unsigned char *resampled = (unsigned char*)malloc( channels*MIPwidth*MIPheight );
1281 			while( ((1<<MIPlevel) <= width) || ((1<<MIPlevel) <= height) )
1282 			{
1283 				/*	do this MIPmap level	*/
1284 				mipmap_image(
1285 						img, width, height, channels,
1286 						resampled,
1287 						(1 << MIPlevel), (1 << MIPlevel) );
1288 				/*  upload the MIPmaps	*/
1289 				if( DXT_mode == SOIL_CAPABILITY_PRESENT )
1290 				{
1291 					/*	user wants me to do the DXT conversion!	*/
1292 					int DDS_size;
1293 					unsigned char *DDS_data = NULL;
1294 					if( (channels & 1) == 1 )
1295 					{
1296 						/*	RGB, use DXT1	*/
1297 						DDS_data = convert_image_to_DXT1(
1298 								resampled, MIPwidth, MIPheight, channels, &DDS_size );
1299 					} else
1300 					{
1301 						/*	RGBA, use DXT5	*/
1302 						DDS_data = convert_image_to_DXT5(
1303 								resampled, MIPwidth, MIPheight, channels, &DDS_size );
1304 					}
1305 					if( DDS_data )
1306 					{
1307 						soilGlCompressedTexImage2D(
1308 							opengl_texture_target, MIPlevel,
1309 							internal_texture_format, MIPwidth, MIPheight, 0,
1310 							DDS_size, DDS_data );
1311 						check_for_GL_errors( "glCompressedTexImage2D" );
1312 						SOIL_free_image_data( DDS_data );
1313 					} else
1314 					{
1315 						/*	my compression failed, try the OpenGL driver's version	*/
1316 						glTexImage2D(
1317 							opengl_texture_target, MIPlevel,
1318 							internal_texture_format, MIPwidth, MIPheight, 0,
1319 							original_texture_format, GL_UNSIGNED_BYTE, resampled );
1320 						check_for_GL_errors( "glTexImage2D" );
1321 					}
1322 				} else
1323 				{
1324 					/*	user want OpenGL to do all the work!	*/
1325 					glTexImage2D(
1326 						opengl_texture_target, MIPlevel,
1327 						internal_texture_format, MIPwidth, MIPheight, 0,
1328 						original_texture_format, GL_UNSIGNED_BYTE, resampled );
1329 					check_for_GL_errors( "glTexImage2D" );
1330 				}
1331 				/*	prep for the next level	*/
1332 				++MIPlevel;
1333 				MIPwidth = (MIPwidth + 1) / 2;
1334 				MIPheight = (MIPheight + 1) / 2;
1335 			}
1336 			SOIL_free_image_data( resampled );
1337 			/*	instruct OpenGL to use the MIPmaps	*/
1338 			glTexParameteri( opengl_texture_type, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
1339 			glTexParameteri( opengl_texture_type, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
1340 			check_for_GL_errors( "GL_TEXTURE_MIN/MAG_FILTER" );
1341 		} else
1342 		{
1343 			/*	instruct OpenGL _NOT_ to use the MIPmaps	*/
1344 			glTexParameteri( opengl_texture_type, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
1345 			glTexParameteri( opengl_texture_type, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
1346 			check_for_GL_errors( "GL_TEXTURE_MIN/MAG_FILTER" );
1347 		}
1348 		/*	does the user want clamping, or wrapping?	*/
1349 		if( flags & SOIL_FLAG_TEXTURE_REPEATS )
1350 		{
1351 			glTexParameteri( opengl_texture_type, GL_TEXTURE_WRAP_S, GL_REPEAT );
1352 			glTexParameteri( opengl_texture_type, GL_TEXTURE_WRAP_T, GL_REPEAT );
1353 			if( opengl_texture_type == SOIL_TEXTURE_CUBE_MAP )
1354 			{
1355 				/*	SOIL_TEXTURE_WRAP_R is invalid if cubemaps aren't supported	*/
1356 				glTexParameteri( opengl_texture_type, SOIL_TEXTURE_WRAP_R, GL_REPEAT );
1357 			}
1358 			check_for_GL_errors( "GL_TEXTURE_WRAP_*" );
1359 		} else
1360 		{
1361 			/*	unsigned int clamp_mode = SOIL_CLAMP_TO_EDGE;	*/
1362 			unsigned int clamp_mode = GL_CLAMP;
1363 			glTexParameteri( opengl_texture_type, GL_TEXTURE_WRAP_S, clamp_mode );
1364 			glTexParameteri( opengl_texture_type, GL_TEXTURE_WRAP_T, clamp_mode );
1365 			if( opengl_texture_type == SOIL_TEXTURE_CUBE_MAP )
1366 			{
1367 				/*	SOIL_TEXTURE_WRAP_R is invalid if cubemaps aren't supported	*/
1368 				glTexParameteri( opengl_texture_type, SOIL_TEXTURE_WRAP_R, clamp_mode );
1369 			}
1370 			check_for_GL_errors( "GL_TEXTURE_WRAP_*" );
1371 		}
1372 		/*	done	*/
1373 		result_string_pointer = "Image loaded as an OpenGL texture";
1374 	} else
1375 	{
1376 		/*	failed	*/
1377 		result_string_pointer = "Failed to generate an OpenGL texture name; missing OpenGL context?";
1378 	}
1379 	SOIL_free_image_data( img );
1380 	return tex_id;
1381 }
1382 
1383 int
SOIL_save_screenshot(const char * filename,int image_type,int x,int y,int width,int height)1384 	SOIL_save_screenshot
1385 	(
1386 		const char *filename,
1387 		int image_type,
1388 		int x, int y,
1389 		int width, int height
1390 	)
1391 {
1392 	unsigned char *pixel_data;
1393 	int i, j;
1394 	int save_result;
1395 
1396 	/*	error checks	*/
1397 	if( (width < 1) || (height < 1) )
1398 	{
1399 		result_string_pointer = "Invalid screenshot dimensions";
1400 		return 0;
1401 	}
1402 	if( (x < 0) || (y < 0) )
1403 	{
1404 		result_string_pointer = "Invalid screenshot location";
1405 		return 0;
1406 	}
1407 	if( filename == NULL )
1408 	{
1409 		result_string_pointer = "Invalid screenshot filename";
1410 		return 0;
1411 	}
1412 
1413     /*  Get the data from OpenGL	*/
1414     pixel_data = (unsigned char*)malloc( 3*width*height );
1415     glReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, pixel_data);
1416 
1417     /*	invert the image	*/
1418     for( j = 0; j*2 < height; ++j )
1419 	{
1420 		int index1 = j * width * 3;
1421 		int index2 = (height - 1 - j) * width * 3;
1422 		for( i = width * 3; i > 0; --i )
1423 		{
1424 			unsigned char temp = pixel_data[index1];
1425 			pixel_data[index1] = pixel_data[index2];
1426 			pixel_data[index2] = temp;
1427 			++index1;
1428 			++index2;
1429 		}
1430 	}
1431 
1432     /*	save the image	*/
1433     save_result = SOIL_save_image( filename, image_type, width, height, 3, pixel_data);
1434 
1435     /*  And free the memory	*/
1436     SOIL_free_image_data( pixel_data );
1437 	return save_result;
1438 }
1439 
1440 unsigned char*
SOIL_load_image(const char * filename,int * width,int * height,int * channels,int force_channels)1441 	SOIL_load_image
1442 	(
1443 		const char *filename,
1444 		int *width, int *height, int *channels,
1445 		int force_channels
1446 	)
1447 {
1448 	unsigned char *result = stbi_load( filename,
1449 			width, height, channels, force_channels );
1450 	if( result == NULL )
1451 	{
1452 		result_string_pointer = stbi_failure_reason();
1453 	} else
1454 	{
1455 		result_string_pointer = "Image loaded";
1456 	}
1457 	return result;
1458 }
1459 
1460 unsigned char*
SOIL_load_image_from_memory(const unsigned char * const buffer,int buffer_length,int * width,int * height,int * channels,int force_channels)1461 	SOIL_load_image_from_memory
1462 	(
1463 		const unsigned char *const buffer,
1464 		int buffer_length,
1465 		int *width, int *height, int *channels,
1466 		int force_channels
1467 	)
1468 {
1469 	unsigned char *result = stbi_load_from_memory(
1470 				buffer, buffer_length,
1471 				width, height, channels,
1472 				force_channels );
1473 	if( result == NULL )
1474 	{
1475 		result_string_pointer = stbi_failure_reason();
1476 	} else
1477 	{
1478 		result_string_pointer = "Image loaded from memory";
1479 	}
1480 	return result;
1481 }
1482 
1483 int
SOIL_save_image(const char * filename,int image_type,int width,int height,int channels,const unsigned char * const data)1484 	SOIL_save_image
1485 	(
1486 		const char *filename,
1487 		int image_type,
1488 		int width, int height, int channels,
1489 		const unsigned char *const data
1490 	)
1491 {
1492 	int save_result;
1493 
1494 	/*	error check	*/
1495 	if( (width < 1) || (height < 1) ||
1496 		(channels < 1) || (channels > 4) ||
1497 		(data == NULL) ||
1498 		(filename == NULL) )
1499 	{
1500 		return 0;
1501 	}
1502 	if( image_type == SOIL_SAVE_TYPE_BMP )
1503 	{
1504 		save_result = stbi_write_bmp( filename,
1505 				width, height, channels, (void*)data );
1506 	} else
1507 	if( image_type == SOIL_SAVE_TYPE_TGA )
1508 	{
1509 		save_result = stbi_write_tga( filename,
1510 				width, height, channels, (void*)data );
1511 	} else
1512 	if( image_type == SOIL_SAVE_TYPE_DDS )
1513 	{
1514 		save_result = save_image_as_DDS( filename,
1515 				width, height, channels, (const unsigned char *const)data );
1516 	} else
1517 	{
1518 		save_result = 0;
1519 	}
1520 	if( save_result == 0 )
1521 	{
1522 		result_string_pointer = "Saving the image failed";
1523 	} else
1524 	{
1525 		result_string_pointer = "Image saved";
1526 	}
1527 	return save_result;
1528 }
1529 
1530 void
SOIL_free_image_data(unsigned char * img_data)1531 	SOIL_free_image_data
1532 	(
1533 		unsigned char *img_data
1534 	)
1535 {
1536 	free( (void*)img_data );
1537 }
1538 
1539 const char*
SOIL_last_result(void)1540 	SOIL_last_result
1541 	(
1542 		void
1543 	)
1544 {
1545 	return result_string_pointer;
1546 }
1547 
SOIL_direct_load_DDS_from_memory(const unsigned char * const buffer,int buffer_length,unsigned int reuse_texture_ID,int flags,int loading_as_cubemap)1548 unsigned int SOIL_direct_load_DDS_from_memory(
1549 		const unsigned char *const buffer,
1550 		int buffer_length,
1551 		unsigned int reuse_texture_ID,
1552 		int flags,
1553 		int loading_as_cubemap )
1554 {
1555 	/*	variables	*/
1556 	DDS_header header;
1557 	unsigned int buffer_index = 0;
1558 	unsigned int tex_ID = 0;
1559 	/*	file reading variables	*/
1560 	unsigned int S3TC_type = 0;
1561 	unsigned char *DDS_data;
1562 	unsigned int DDS_main_size;
1563 	unsigned int DDS_full_size;
1564 	unsigned int width, height;
1565 	int mipmaps, cubemap, uncompressed, block_size = 16;
1566 	unsigned int flag;
1567 	unsigned int cf_target, ogl_target_start, ogl_target_end;
1568 	unsigned int opengl_texture_type;
1569 	int i;
1570 	/*	1st off, does the filename even exist?	*/
1571 	if( NULL == buffer )
1572 	{
1573 		/*	we can't do it!	*/
1574 		result_string_pointer = "NULL buffer";
1575 		return 0;
1576 	}
1577 	if( buffer_length < sizeof( DDS_header ) )
1578 	{
1579 		/*	we can't do it!	*/
1580 		result_string_pointer = "DDS file was too small to contain the DDS header";
1581 		return 0;
1582 	}
1583 	/*	try reading in the header	*/
1584 	memcpy ( (void*)(&header), (const void *)buffer, sizeof( DDS_header ) );
1585 	buffer_index = sizeof( DDS_header );
1586 	/*	guilty until proven innocent	*/
1587 	result_string_pointer = "Failed to read a known DDS header";
1588 	/*	validate the header (warning, "goto"'s ahead, shield your eyes!!)	*/
1589 	flag = ('D'<<0)|('D'<<8)|('S'<<16)|(' '<<24);
1590 	if( header.dwMagic != flag ) {goto quick_exit;}
1591 	if( header.dwSize != 124 ) {goto quick_exit;}
1592 	/*	I need all of these	*/
1593 	flag = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT;
1594 	if( (header.dwFlags & flag) != flag ) {goto quick_exit;}
1595 	/*	According to the MSDN spec, the dwFlags should contain
1596 		DDSD_LINEARSIZE if it's compressed, or DDSD_PITCH if
1597 		uncompressed.  Some DDS writers do not conform to the
1598 		spec, so I need to make my reader more tolerant	*/
1599 	/*	I need one of these	*/
1600 	flag = DDPF_FOURCC | DDPF_RGB;
1601 	if( (header.sPixelFormat.dwFlags & flag) == 0 ) {goto quick_exit;}
1602 	if( header.sPixelFormat.dwSize != 32 ) {goto quick_exit;}
1603 	if( (header.sCaps.dwCaps1 & DDSCAPS_TEXTURE) == 0 ) {goto quick_exit;}
1604 	/*	make sure it is a type we can upload	*/
1605 	if( (header.sPixelFormat.dwFlags & DDPF_FOURCC) &&
1606 		!(
1607 		(header.sPixelFormat.dwFourCC == (('D'<<0)|('X'<<8)|('T'<<16)|('1'<<24))) ||
1608 		(header.sPixelFormat.dwFourCC == (('D'<<0)|('X'<<8)|('T'<<16)|('3'<<24))) ||
1609 		(header.sPixelFormat.dwFourCC == (('D'<<0)|('X'<<8)|('T'<<16)|('5'<<24)))
1610 		) )
1611 	{
1612 		goto quick_exit;
1613 	}
1614 	/*	OK, validated the header, let's load the image data	*/
1615 	result_string_pointer = "DDS header loaded and validated";
1616 	width = header.dwWidth;
1617 	height = header.dwHeight;
1618 	uncompressed = 1 - (header.sPixelFormat.dwFlags & DDPF_FOURCC) / DDPF_FOURCC;
1619 	cubemap = (header.sCaps.dwCaps2 & DDSCAPS2_CUBEMAP) / DDSCAPS2_CUBEMAP;
1620 	if( uncompressed )
1621 	{
1622 		S3TC_type = GL_RGB;
1623 		block_size = 3;
1624 		if( header.sPixelFormat.dwFlags & DDPF_ALPHAPIXELS )
1625 		{
1626 			S3TC_type = GL_RGBA;
1627 			block_size = 4;
1628 		}
1629 		DDS_main_size = width * height * block_size;
1630 	} else
1631 	{
1632 		/*	can we even handle direct uploading to OpenGL DXT compressed images?	*/
1633 		if( query_DXT_capability() != SOIL_CAPABILITY_PRESENT )
1634 		{
1635 			/*	we can't do it!	*/
1636 			result_string_pointer = "Direct upload of S3TC images not supported by the OpenGL driver";
1637 			return 0;
1638 		}
1639 		/*	well, we know it is DXT1/3/5, because we checked above	*/
1640 		switch( (header.sPixelFormat.dwFourCC >> 24) - '0' )
1641 		{
1642 		case 1:
1643 			S3TC_type = SOIL_RGBA_S3TC_DXT1;
1644 			block_size = 8;
1645 			break;
1646 		case 3:
1647 			S3TC_type = SOIL_RGBA_S3TC_DXT3;
1648 			block_size = 16;
1649 			break;
1650 		case 5:
1651 			S3TC_type = SOIL_RGBA_S3TC_DXT5;
1652 			block_size = 16;
1653 			break;
1654 		}
1655 		DDS_main_size = ((width+3)>>2)*((height+3)>>2)*block_size;
1656 	}
1657 	if( cubemap )
1658 	{
1659 		/* does the user want a cubemap?	*/
1660 		if( !loading_as_cubemap )
1661 		{
1662 			/*	we can't do it!	*/
1663 			result_string_pointer = "DDS image was a cubemap";
1664 			return 0;
1665 		}
1666 		/*	can we even handle cubemaps with the OpenGL driver?	*/
1667 		if( query_cubemap_capability() != SOIL_CAPABILITY_PRESENT )
1668 		{
1669 			/*	we can't do it!	*/
1670 			result_string_pointer = "Direct upload of cubemap images not supported by the OpenGL driver";
1671 			return 0;
1672 		}
1673 		ogl_target_start = SOIL_TEXTURE_CUBE_MAP_POSITIVE_X;
1674 		ogl_target_end =   SOIL_TEXTURE_CUBE_MAP_NEGATIVE_Z;
1675 		opengl_texture_type = SOIL_TEXTURE_CUBE_MAP;
1676 	} else
1677 	{
1678 		/* does the user want a non-cubemap?	*/
1679 		if( loading_as_cubemap )
1680 		{
1681 			/*	we can't do it!	*/
1682 			result_string_pointer = "DDS image was not a cubemap";
1683 			return 0;
1684 		}
1685 		ogl_target_start = GL_TEXTURE_2D;
1686 		ogl_target_end =   GL_TEXTURE_2D;
1687 		opengl_texture_type = GL_TEXTURE_2D;
1688 	}
1689 	if( (header.sCaps.dwCaps1 & DDSCAPS_MIPMAP) && (header.dwMipMapCount > 1) )
1690 	{
1691 		int shift_offset;
1692 		mipmaps = header.dwMipMapCount - 1;
1693 		DDS_full_size = DDS_main_size;
1694 		if( uncompressed )
1695 		{
1696 			/*	uncompressed DDS, simple MIPmap size calculation	*/
1697 			shift_offset = 0;
1698 		} else
1699 		{
1700 			/*	compressed DDS, MIPmap size calculation is block based	*/
1701 			shift_offset = 2;
1702 		}
1703 		for( i = 1; i <= mipmaps; ++ i )
1704 		{
1705 			int w, h;
1706 			w = width >> (shift_offset + i);
1707 			h = height >> (shift_offset + i);
1708 			if( w < 1 )
1709 			{
1710 				w = 1;
1711 			}
1712 			if( h < 1 )
1713 			{
1714 				h = 1;
1715 			}
1716 			DDS_full_size += w*h*block_size;
1717 		}
1718 	} else
1719 	{
1720 		mipmaps = 0;
1721 		DDS_full_size = DDS_main_size;
1722 	}
1723 	DDS_data = (unsigned char*)malloc( DDS_full_size );
1724 	/*	got the image data RAM, create or use an existing OpenGL texture handle	*/
1725 	tex_ID = reuse_texture_ID;
1726 	if( tex_ID == 0 )
1727 	{
1728 		glGenTextures( 1, &tex_ID );
1729 	}
1730 	/*  bind an OpenGL texture ID	*/
1731 	glBindTexture( opengl_texture_type, tex_ID );
1732 	/*	do this for each face of the cubemap!	*/
1733 	for( cf_target = ogl_target_start; cf_target <= ogl_target_end; ++cf_target )
1734 	{
1735 		if( buffer_index + DDS_full_size <= buffer_length )
1736 		{
1737 			unsigned int byte_offset = DDS_main_size;
1738 			memcpy( (void*)DDS_data, (const void*)(&buffer[buffer_index]), DDS_full_size );
1739 			buffer_index += DDS_full_size;
1740 			/*	upload the main chunk	*/
1741 			if( uncompressed )
1742 			{
1743 				/*	and remember, DXT uncompressed uses BGR(A),
1744 					so swap to RGB(A) for ALL MIPmap levels	*/
1745 				for( i = 0; i < DDS_full_size; i += block_size )
1746 				{
1747 					unsigned char temp = DDS_data[i];
1748 					DDS_data[i] = DDS_data[i+2];
1749 					DDS_data[i+2] = temp;
1750 				}
1751 				glTexImage2D(
1752 					cf_target, 0,
1753 					S3TC_type, width, height, 0,
1754 					S3TC_type, GL_UNSIGNED_BYTE, DDS_data );
1755 			} else
1756 			{
1757 				soilGlCompressedTexImage2D(
1758 					cf_target, 0,
1759 					S3TC_type, width, height, 0,
1760 					DDS_main_size, DDS_data );
1761 			}
1762 			/*	upload the mipmaps, if we have them	*/
1763 			for( i = 1; i <= mipmaps; ++i )
1764 			{
1765 				int w, h, mip_size;
1766 				w = width >> i;
1767 				h = height >> i;
1768 				if( w < 1 )
1769 				{
1770 					w = 1;
1771 				}
1772 				if( h < 1 )
1773 				{
1774 					h = 1;
1775 				}
1776 				/*	upload this mipmap	*/
1777 				if( uncompressed )
1778 				{
1779 					mip_size = w*h*block_size;
1780 					glTexImage2D(
1781 						cf_target, i,
1782 						S3TC_type, w, h, 0,
1783 						S3TC_type, GL_UNSIGNED_BYTE, &DDS_data[byte_offset] );
1784 				} else
1785 				{
1786 					mip_size = ((w+3)/4)*((h+3)/4)*block_size;
1787 					soilGlCompressedTexImage2D(
1788 						cf_target, i,
1789 						S3TC_type, w, h, 0,
1790 						mip_size, &DDS_data[byte_offset] );
1791 				}
1792 				/*	and move to the next mipmap	*/
1793 				byte_offset += mip_size;
1794 			}
1795 			/*	it worked!	*/
1796 			result_string_pointer = "DDS file loaded";
1797 		} else
1798 		{
1799 			glDeleteTextures( 1, & tex_ID );
1800 			tex_ID = 0;
1801 			cf_target = ogl_target_end + 1;
1802 			result_string_pointer = "DDS file was too small for expected image data";
1803 		}
1804 	}/* end reading each face */
1805 	SOIL_free_image_data( DDS_data );
1806 	if( tex_ID )
1807 	{
1808 		/*	did I have MIPmaps?	*/
1809 		if( mipmaps > 0 )
1810 		{
1811 			/*	instruct OpenGL to use the MIPmaps	*/
1812 			glTexParameteri( opengl_texture_type, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
1813 			glTexParameteri( opengl_texture_type, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
1814 		} else
1815 		{
1816 			/*	instruct OpenGL _NOT_ to use the MIPmaps	*/
1817 			glTexParameteri( opengl_texture_type, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
1818 			glTexParameteri( opengl_texture_type, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
1819 		}
1820 		/*	does the user want clamping, or wrapping?	*/
1821 		if( flags & SOIL_FLAG_TEXTURE_REPEATS )
1822 		{
1823 			glTexParameteri( opengl_texture_type, GL_TEXTURE_WRAP_S, GL_REPEAT );
1824 			glTexParameteri( opengl_texture_type, GL_TEXTURE_WRAP_T, GL_REPEAT );
1825 			glTexParameteri( opengl_texture_type, SOIL_TEXTURE_WRAP_R, GL_REPEAT );
1826 		} else
1827 		{
1828 			/*	unsigned int clamp_mode = SOIL_CLAMP_TO_EDGE;	*/
1829 			unsigned int clamp_mode = GL_CLAMP;
1830 			glTexParameteri( opengl_texture_type, GL_TEXTURE_WRAP_S, clamp_mode );
1831 			glTexParameteri( opengl_texture_type, GL_TEXTURE_WRAP_T, clamp_mode );
1832 			glTexParameteri( opengl_texture_type, SOIL_TEXTURE_WRAP_R, clamp_mode );
1833 		}
1834 	}
1835 
1836 quick_exit:
1837 	/*	report success or failure	*/
1838 	return tex_ID;
1839 }
1840 
SOIL_direct_load_DDS(const char * filename,unsigned int reuse_texture_ID,int flags,int loading_as_cubemap)1841 unsigned int SOIL_direct_load_DDS(
1842 		const char *filename,
1843 		unsigned int reuse_texture_ID,
1844 		int flags,
1845 		int loading_as_cubemap )
1846 {
1847 	FILE *f;
1848 	unsigned char *buffer;
1849 	size_t buffer_length, bytes_read;
1850 	unsigned int tex_ID = 0;
1851 	/*	error checks	*/
1852 	if( NULL == filename )
1853 	{
1854 		result_string_pointer = "NULL filename";
1855 		return 0;
1856 	}
1857 	f = fopen( filename, "rb" );
1858 	if( NULL == f )
1859 	{
1860 		/*	the file doesn't seem to exist (or be open-able)	*/
1861 		result_string_pointer = "Can not find DDS file";
1862 		return 0;
1863 	}
1864 	fseek( f, 0, SEEK_END );
1865 	buffer_length = ftell( f );
1866 	fseek( f, 0, SEEK_SET );
1867 	buffer = (unsigned char *) malloc( buffer_length );
1868 	if( NULL == buffer )
1869 	{
1870 		result_string_pointer = "malloc failed";
1871 		fclose( f );
1872 		return 0;
1873 	}
1874 	bytes_read = fread( (void*)buffer, 1, buffer_length, f );
1875 	fclose( f );
1876 	if( bytes_read < buffer_length )
1877 	{
1878 		/*	huh?	*/
1879 		buffer_length = bytes_read;
1880 	}
1881 	/*	now try to do the loading	*/
1882 	tex_ID = SOIL_direct_load_DDS_from_memory(
1883 		(const unsigned char *const)buffer, buffer_length,
1884 		reuse_texture_ID, flags, loading_as_cubemap );
1885 	SOIL_free_image_data( buffer );
1886 	return tex_ID;
1887 }
1888 
query_NPOT_capability(void)1889 int query_NPOT_capability( void )
1890 {
1891 	/*	check for the capability	*/
1892 	if( has_NPOT_capability == SOIL_CAPABILITY_UNKNOWN )
1893 	{
1894 		/*	we haven't yet checked for the capability, do so	*/
1895 		if(
1896 			(NULL == strstr( (char const*)glGetString( GL_EXTENSIONS ),
1897 				"GL_ARB_texture_non_power_of_two" ) )
1898 			)
1899 		{
1900 			/*	not there, flag the failure	*/
1901 			has_NPOT_capability = SOIL_CAPABILITY_NONE;
1902 		} else
1903 		{
1904 			/*	it's there!	*/
1905 			has_NPOT_capability = SOIL_CAPABILITY_PRESENT;
1906 		}
1907 	}
1908 	/*	let the user know if we can do non-power-of-two textures or not	*/
1909 	return has_NPOT_capability;
1910 }
1911 
query_tex_rectangle_capability(void)1912 int query_tex_rectangle_capability( void )
1913 {
1914 	/*	check for the capability	*/
1915 	if( has_tex_rectangle_capability == SOIL_CAPABILITY_UNKNOWN )
1916 	{
1917 		/*	we haven't yet checked for the capability, do so	*/
1918 		if(
1919 			(NULL == strstr( (char const*)glGetString( GL_EXTENSIONS ),
1920 				"GL_ARB_texture_rectangle" ) )
1921 		&&
1922 			(NULL == strstr( (char const*)glGetString( GL_EXTENSIONS ),
1923 				"GL_EXT_texture_rectangle" ) )
1924 		&&
1925 			(NULL == strstr( (char const*)glGetString( GL_EXTENSIONS ),
1926 				"GL_NV_texture_rectangle" ) )
1927 			)
1928 		{
1929 			/*	not there, flag the failure	*/
1930 			has_tex_rectangle_capability = SOIL_CAPABILITY_NONE;
1931 		} else
1932 		{
1933 			/*	it's there!	*/
1934 			has_tex_rectangle_capability = SOIL_CAPABILITY_PRESENT;
1935 		}
1936 	}
1937 	/*	let the user know if we can do texture rectangles or not	*/
1938 	return has_tex_rectangle_capability;
1939 }
1940 
query_cubemap_capability(void)1941 int query_cubemap_capability( void )
1942 {
1943 	/*	check for the capability	*/
1944 	if( has_cubemap_capability == SOIL_CAPABILITY_UNKNOWN )
1945 	{
1946 		/*	we haven't yet checked for the capability, do so	*/
1947 		if(
1948 			(NULL == strstr( (char const*)glGetString( GL_EXTENSIONS ),
1949 				"GL_ARB_texture_cube_map" ) )
1950 		&&
1951 			(NULL == strstr( (char const*)glGetString( GL_EXTENSIONS ),
1952 				"GL_EXT_texture_cube_map" ) )
1953 			)
1954 		{
1955 			/*	not there, flag the failure	*/
1956 			has_cubemap_capability = SOIL_CAPABILITY_NONE;
1957 		} else
1958 		{
1959 			/*	it's there!	*/
1960 			has_cubemap_capability = SOIL_CAPABILITY_PRESENT;
1961 		}
1962 	}
1963 	/*	let the user know if we can do cubemaps or not	*/
1964 	return has_cubemap_capability;
1965 }
1966 
query_DXT_capability(void)1967 int query_DXT_capability( void )
1968 {
1969 	/*	check for the capability	*/
1970 	if( has_DXT_capability == SOIL_CAPABILITY_UNKNOWN )
1971 	{
1972 		/*	we haven't yet checked for the capability, do so	*/
1973 		if( NULL == strstr(
1974 				(char const*)glGetString( GL_EXTENSIONS ),
1975 				"GL_EXT_texture_compression_s3tc" ) )
1976 		{
1977 			/*	not there, flag the failure	*/
1978 			has_DXT_capability = SOIL_CAPABILITY_NONE;
1979 		} else
1980 		{
1981 			/*	and find the address of the extension function	*/
1982 			P_SOIL_GLCOMPRESSEDTEXIMAGE2DPROC ext_addr = NULL;
1983 			#ifdef WIN32
1984 				ext_addr = (P_SOIL_GLCOMPRESSEDTEXIMAGE2DPROC)
1985 						wglGetProcAddress
1986 						(
1987 							"glCompressedTexImage2DARB"
1988 						);
1989 			#elif defined(__APPLE__) || defined(__APPLE_CC__)
1990 				/*	I can't test this Apple stuff!	*/
1991 				CFBundleRef bundle;
1992 				CFURLRef bundleURL =
1993 					CFURLCreateWithFileSystemPath(
1994 						kCFAllocatorDefault,
1995 						CFSTR("/System/Library/Frameworks/OpenGL.framework"),
1996 						kCFURLPOSIXPathStyle,
1997 						true );
1998 				CFStringRef extensionName =
1999 					CFStringCreateWithCString(
2000 						kCFAllocatorDefault,
2001 						"glCompressedTexImage2DARB",
2002 						kCFStringEncodingASCII );
2003 				bundle = CFBundleCreate( kCFAllocatorDefault, bundleURL );
2004 				assert( bundle != NULL );
2005 				ext_addr = (P_SOIL_GLCOMPRESSEDTEXIMAGE2DPROC)
2006 						CFBundleGetFunctionPointerForName
2007 						(
2008 							bundle, extensionName
2009 						);
2010 				CFRelease( bundleURL );
2011 				CFRelease( extensionName );
2012 				CFRelease( bundle );
2013 			#else
2014 				ext_addr = (P_SOIL_GLCOMPRESSEDTEXIMAGE2DPROC)
2015 						glXGetProcAddressARB
2016 						(
2017 							(const GLubyte *)"glCompressedTexImage2DARB"
2018 						);
2019 			#endif
2020 			/*	Flag it so no checks needed later	*/
2021 			if( NULL == ext_addr )
2022 			{
2023 				/*	hmm, not good!!  This should not happen, but does on my
2024 					laptop's VIA chipset.  The GL_EXT_texture_compression_s3tc
2025 					spec requires that ARB_texture_compression be present too.
2026 					this means I can upload and have the OpenGL drive do the
2027 					conversion, but I can't use my own routines or load DDS files
2028 					from disk and upload them directly [8^(	*/
2029 				has_DXT_capability = SOIL_CAPABILITY_NONE;
2030 			} else
2031 			{
2032 				/*	all's well!	*/
2033 				soilGlCompressedTexImage2D = ext_addr;
2034 				has_DXT_capability = SOIL_CAPABILITY_PRESENT;
2035 			}
2036 		}
2037 	}
2038 	/*	let the user know if we can do DXT or not	*/
2039 	return has_DXT_capability;
2040 }
2041