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 */
42char *result_string_pointer = "SOIL initialized";
43
44/* for loading cube maps */
45enum{
46 SOIL_CAPABILITY_UNKNOWN = -1,
47 SOIL_CAPABILITY_NONE = 0,
48 SOIL_CAPABILITY_PRESENT = 1
49};
50static int has_cubemap_capability = SOIL_CAPABILITY_UNKNOWN;
51int 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 */
67static int has_NPOT_capability = SOIL_CAPABILITY_UNKNOWN;
68int query_NPOT_capability( void );
69/* for texture rectangles */
70static int has_tex_rectangle_capability = SOIL_CAPABILITY_UNKNOWN;
71int 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 */
75static int has_DXT_capability = SOIL_CAPABILITY_UNKNOWN;
76int 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
81typedef void (APIENTRY * P_SOIL_GLCOMPRESSEDTEXIMAGE2DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid * data);
82P_SOIL_GLCOMPRESSEDTEXIMAGE2DPROC soilGlCompressedTexImage2D = NULL;
83unsigned int SOIL_direct_load_DDS(
84 const char *filename,
85 unsigned int reuse_texture_ID,
86 int flags,
87 int loading_as_cubemap );
88unsigned 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 */
95unsigned 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^) */
108unsigned 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
160unsigned 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
212unsigned 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
270unsigned 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
455unsigned 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
658unsigned 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
743unsigned 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
834unsigned 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
945unsigned 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
963void 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
974void check_for_GL_errors( const char *calling_location )
975{
976 /* no check for errors */
977}
978#endif
979
980unsigned 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<<MIPlevel) <= width) || ((1<<MIPlevel) <= height) )
1266 {
1267 /* do this MIPmap level */
1268 mipmap_image(
1269 img, width, height, channels,
1270 resampled,
1271 (1 << MIPlevel), (1 << MIPlevel) );
1272 /* upload the MIPmaps */
1273 if( DXT_mode == SOIL_CAPABILITY_PRESENT )
1274 {
1275 /* user wants me to do the DXT conversion! */
1276 int DDS_size;
1277 unsigned char *DDS_data = NULL;
1278 if( (channels & 1) == 1 )
1279 {
1280 /* RGB, use DXT1 */
1281 DDS_data = convert_image_to_DXT1(
1282 resampled, MIPwidth, MIPheight, channels, &DDS_size );
1283 } else
1284 {
1285 /* RGBA, use DXT5 */
1286 DDS_data = convert_image_to_DXT5(
1287 resampled, MIPwidth, MIPheight, channels, &DDS_size );
1288 }
1289 if( DDS_data )
1290 {
1291 soilGlCompressedTexImage2D(
1292 opengl_texture_target, MIPlevel,
1293 internal_texture_format, MIPwidth, MIPheight, 0,
1294 DDS_size, DDS_data );
1295 check_for_GL_errors( "glCompressedTexImage2D" );
1296 SOIL_free_image_data( DDS_data );
1297 } else
1298 {
1299 /* my compression failed, try the OpenGL driver's version */
1300 glTexImage2D(
1301 opengl_texture_target, MIPlevel,
1302 internal_texture_format, MIPwidth, MIPheight, 0,
1303 original_texture_format, GL_UNSIGNED_BYTE, resampled );
1304 check_for_GL_errors( "glTexImage2D" );
1305 }
1306 } else
1307 {
1308 /* user want OpenGL to do all the work! */
1309 glTexImage2D(
1310 opengl_texture_target, MIPlevel,
1311 internal_texture_format, MIPwidth, MIPheight, 0,
1312 original_texture_format, GL_UNSIGNED_BYTE, resampled );
1313 check_for_GL_errors( "glTexImage2D" );
1314 }
1315 /* prep for the next level */
1316 ++MIPlevel;
1317 MIPwidth = (MIPwidth + 1) / 2;
1318 MIPheight = (MIPheight + 1) / 2;
1319 }
1320 SOIL_free_image_data( resampled );
1321 /* instruct OpenGL to use the MIPmaps */
1322 glTexParameteri( opengl_texture_type, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
1323 glTexParameteri( opengl_texture_type, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
1324 check_for_GL_errors( "GL_TEXTURE_MIN/MAG_FILTER" );
1325 } else
1326 {
1327 /* instruct OpenGL _NOT_ to use the MIPmaps */
1328 glTexParameteri( opengl_texture_type, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
1329 glTexParameteri( opengl_texture_type, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
1330 check_for_GL_errors( "GL_TEXTURE_MIN/MAG_FILTER" );
1331 }
1332 /* does the user want clamping, or wrapping? */
1333 if( flags & SOIL_FLAG_TEXTURE_REPEATS )
1334 {
1335 glTexParameteri( opengl_texture_type, GL_TEXTURE_WRAP_S, GL_REPEAT );
1336 glTexParameteri( opengl_texture_type, GL_TEXTURE_WRAP_T, GL_REPEAT );
1337 if( opengl_texture_type == SOIL_TEXTURE_CUBE_MAP )
1338 {
1339 /* SOIL_TEXTURE_WRAP_R is invalid if cubemaps aren't supported */
1340 glTexParameteri( opengl_texture_type, SOIL_TEXTURE_WRAP_R, GL_REPEAT );
1341 }
1342 check_for_GL_errors( "GL_TEXTURE_WRAP_*" );
1343 } else
1344 {
1345 /* unsigned int clamp_mode = SOIL_CLAMP_TO_EDGE; */
1346 unsigned int clamp_mode = GL_CLAMP;
1347 glTexParameteri( opengl_texture_type, GL_TEXTURE_WRAP_S, clamp_mode );
1348 glTexParameteri( opengl_texture_type, GL_TEXTURE_WRAP_T, clamp_mode );
1349 if( opengl_texture_type == SOIL_TEXTURE_CUBE_MAP )
1350 {
1351 /* SOIL_TEXTURE_WRAP_R is invalid if cubemaps aren't supported */
1352 glTexParameteri( opengl_texture_type, SOIL_TEXTURE_WRAP_R, clamp_mode );
1353 }
1354 check_for_GL_errors( "GL_TEXTURE_WRAP_*" );
1355 }
1356 /* done */
1357 result_string_pointer = "Image loaded as an OpenGL texture";
1358 } else
1359 {
1360 /* failed */
1361 result_string_pointer = "Failed to generate an OpenGL texture name; missing OpenGL context?";
1362 }
1363 SOIL_free_image_data( img );
1364 return tex_id;
1365}
1366
1367int
1368 SOIL_save_screenshot
1369 (
1370 const char *filename,
1371 int image_type,
1372 int x, int y,
1373 int width, int height
1374 )
1375{
1376 unsigned char *pixel_data;
1377 int i, j;
1378 int save_result;
1379
1380 /* error checks */
1381 if( (width < 1) || (height < 1) )
1382 {
1383 result_string_pointer = "Invalid screenshot dimensions";
1384 return 0;
1385 }
1386 if( (x < 0) || (y < 0) )
1387 {
1388 result_string_pointer = "Invalid screenshot location";
1389 return 0;
1390 }
1391 if( filename == NULL )
1392 {
1393 result_string_pointer = "Invalid screenshot filename";
1394 return 0;
1395 }
1396
1397 /* Get the data from OpenGL */
1398 pixel_data = (unsigned char*)malloc( 3*width*height );
1399 glReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, pixel_data);
1400
1401 /* invert the image */
1402 for( j = 0; j*2 < height; ++j )
1403 {
1404 int index1 = j * width * 3;
1405 int index2 = (height - 1 - j) * width * 3;
1406 for( i = width * 3; i > 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
1424unsigned 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
1444unsigned 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
1467int
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
1514void
1515 SOIL_free_image_data
1516 (
1517 unsigned char *img_data
1518 )
1519{
1520 free( (void*)img_data );
1521}
1522
1523const char*
1524 SOIL_last_result
1525 (
1526 void
1527 )
1528{
1529 return result_string_pointer;
1530}
1531
1532unsigned 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( (int)(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 < (int)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
1820quick_exit:
1821 /* report success or failure */
1822 return tex_ID;
1823}
1824
1825unsigned 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
1873int 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
1896int 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
1925int 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
1951int 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