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 |
109 | SOIL_load_OGL_texture |
110 | ( |
111 | const char *filename, |
112 | int force_channels, |
113 | unsigned int reuse_texture_ID, |
114 | unsigned int flags |
115 | ) |
116 | { |
117 | /* variables */ |
118 | unsigned char* img; |
119 | int width, height, channels; |
120 | unsigned int tex_id; |
121 | /* does the user want direct uploading of the image as a DDS file? */ |
122 | if( flags & SOIL_FLAG_DDS_LOAD_DIRECT ) |
123 | { |
124 | /* 1st try direct loading of the image as a DDS file |
125 | note: direct uploading will only load what is in the |
126 | DDS file, no MIPmaps will be generated, the image will |
127 | not be flipped, etc. */ |
128 | tex_id = SOIL_direct_load_DDS( filename, reuse_texture_ID, flags, 0 ); |
129 | if( tex_id ) |
130 | { |
131 | /* hey, it worked!! */ |
132 | return tex_id; |
133 | } |
134 | } |
135 | /* try to load the image */ |
136 | img = SOIL_load_image( filename, &width, &height, &channels, force_channels ); |
137 | /* channels holds the original number of channels, which may have been forced */ |
138 | if( (force_channels >= 1) && (force_channels <= 4) ) |
139 | { |
140 | channels = force_channels; |
141 | } |
142 | if( NULL == img ) |
143 | { |
144 | /* image loading failed */ |
145 | result_string_pointer = stbi_failure_reason(); |
146 | return 0; |
147 | } |
148 | /* OK, make it a texture! */ |
149 | tex_id = SOIL_internal_create_OGL_texture( |
150 | img, width, height, channels, |
151 | reuse_texture_ID, flags, |
152 | GL_TEXTURE_2D, GL_TEXTURE_2D, |
153 | GL_MAX_TEXTURE_SIZE ); |
154 | /* and nuke the image data */ |
155 | SOIL_free_image_data( img ); |
156 | /* and return the handle, such as it is */ |
157 | return tex_id; |
158 | } |
159 | |
160 | unsigned int |
161 | SOIL_load_OGL_HDR_texture |
162 | ( |
163 | const char *filename, |
164 | int fake_HDR_format, |
165 | int rescale_to_max, |
166 | unsigned int reuse_texture_ID, |
167 | unsigned int flags |
168 | ) |
169 | { |
170 | /* variables */ |
171 | unsigned char* img; |
172 | int width, height, channels; |
173 | unsigned int tex_id; |
174 | /* no direct uploading of the image as a DDS file */ |
175 | /* error check */ |
176 | if( (fake_HDR_format != SOIL_HDR_RGBE) && |
177 | (fake_HDR_format != SOIL_HDR_RGBdivA) && |
178 | (fake_HDR_format != SOIL_HDR_RGBdivA2) ) |
179 | { |
180 | result_string_pointer = "Invalid fake HDR format specified" ; |
181 | return 0; |
182 | } |
183 | /* try to load the image (only the HDR type) */ |
184 | img = stbi_hdr_load_rgbe( filename, &width, &height, &channels, 4 ); |
185 | /* channels holds the original number of channels, which may have been forced */ |
186 | if( NULL == img ) |
187 | { |
188 | /* image loading failed */ |
189 | result_string_pointer = stbi_failure_reason(); |
190 | return 0; |
191 | } |
192 | /* the load worked, do I need to convert it? */ |
193 | if( fake_HDR_format == SOIL_HDR_RGBdivA ) |
194 | { |
195 | RGBE_to_RGBdivA( img, width, height, rescale_to_max ); |
196 | } else if( fake_HDR_format == SOIL_HDR_RGBdivA2 ) |
197 | { |
198 | RGBE_to_RGBdivA2( img, width, height, rescale_to_max ); |
199 | } |
200 | /* OK, make it a texture! */ |
201 | tex_id = SOIL_internal_create_OGL_texture( |
202 | img, width, height, channels, |
203 | reuse_texture_ID, flags, |
204 | GL_TEXTURE_2D, GL_TEXTURE_2D, |
205 | GL_MAX_TEXTURE_SIZE ); |
206 | /* and nuke the image data */ |
207 | SOIL_free_image_data( img ); |
208 | /* and return the handle, such as it is */ |
209 | return tex_id; |
210 | } |
211 | |
212 | unsigned int |
213 | SOIL_load_OGL_texture_from_memory |
214 | ( |
215 | const unsigned char *const buffer, |
216 | int buffer_length, |
217 | int force_channels, |
218 | unsigned int reuse_texture_ID, |
219 | unsigned int flags |
220 | ) |
221 | { |
222 | /* variables */ |
223 | unsigned char* img; |
224 | int width, height, channels; |
225 | unsigned int tex_id; |
226 | /* does the user want direct uploading of the image as a DDS file? */ |
227 | if( flags & SOIL_FLAG_DDS_LOAD_DIRECT ) |
228 | { |
229 | /* 1st try direct loading of the image as a DDS file |
230 | note: direct uploading will only load what is in the |
231 | DDS file, no MIPmaps will be generated, the image will |
232 | not be flipped, etc. */ |
233 | tex_id = SOIL_direct_load_DDS_from_memory( |
234 | buffer, buffer_length, |
235 | reuse_texture_ID, flags, 0 ); |
236 | if( tex_id ) |
237 | { |
238 | /* hey, it worked!! */ |
239 | return tex_id; |
240 | } |
241 | } |
242 | /* try to load the image */ |
243 | img = SOIL_load_image_from_memory( |
244 | buffer, buffer_length, |
245 | &width, &height, &channels, |
246 | force_channels ); |
247 | /* channels holds the original number of channels, which may have been forced */ |
248 | if( (force_channels >= 1) && (force_channels <= 4) ) |
249 | { |
250 | channels = force_channels; |
251 | } |
252 | if( NULL == img ) |
253 | { |
254 | /* image loading failed */ |
255 | result_string_pointer = stbi_failure_reason(); |
256 | return 0; |
257 | } |
258 | /* OK, make it a texture! */ |
259 | tex_id = SOIL_internal_create_OGL_texture( |
260 | img, width, height, channels, |
261 | reuse_texture_ID, flags, |
262 | GL_TEXTURE_2D, GL_TEXTURE_2D, |
263 | GL_MAX_TEXTURE_SIZE ); |
264 | /* and nuke the image data */ |
265 | SOIL_free_image_data( img ); |
266 | /* and return the handle, such as it is */ |
267 | return tex_id; |
268 | } |
269 | |
270 | unsigned int |
271 | SOIL_load_OGL_cubemap |
272 | ( |
273 | const char *x_pos_file, |
274 | const char *x_neg_file, |
275 | const char *y_pos_file, |
276 | const char *y_neg_file, |
277 | const char *z_pos_file, |
278 | const char *z_neg_file, |
279 | int force_channels, |
280 | unsigned int reuse_texture_ID, |
281 | unsigned int flags |
282 | ) |
283 | { |
284 | /* variables */ |
285 | unsigned char* img; |
286 | int width, height, channels; |
287 | unsigned int tex_id; |
288 | /* error checking */ |
289 | if( (x_pos_file == NULL) || |
290 | (x_neg_file == NULL) || |
291 | (y_pos_file == NULL) || |
292 | (y_neg_file == NULL) || |
293 | (z_pos_file == NULL) || |
294 | (z_neg_file == NULL) ) |
295 | { |
296 | result_string_pointer = "Invalid cube map files list" ; |
297 | return 0; |
298 | } |
299 | /* capability checking */ |
300 | if( query_cubemap_capability() != SOIL_CAPABILITY_PRESENT ) |
301 | { |
302 | result_string_pointer = "No cube map capability present" ; |
303 | return 0; |
304 | } |
305 | /* 1st face: try to load the image */ |
306 | img = SOIL_load_image( x_pos_file, &width, &height, &channels, force_channels ); |
307 | /* channels holds the original number of channels, which may have been forced */ |
308 | if( (force_channels >= 1) && (force_channels <= 4) ) |
309 | { |
310 | channels = force_channels; |
311 | } |
312 | if( NULL == img ) |
313 | { |
314 | /* image loading failed */ |
315 | result_string_pointer = stbi_failure_reason(); |
316 | return 0; |
317 | } |
318 | /* upload the texture, and create a texture ID if necessary */ |
319 | tex_id = SOIL_internal_create_OGL_texture( |
320 | img, width, height, channels, |
321 | reuse_texture_ID, flags, |
322 | SOIL_TEXTURE_CUBE_MAP, SOIL_TEXTURE_CUBE_MAP_POSITIVE_X, |
323 | SOIL_MAX_CUBE_MAP_TEXTURE_SIZE ); |
324 | /* and nuke the image data */ |
325 | SOIL_free_image_data( img ); |
326 | /* continue? */ |
327 | if( tex_id != 0 ) |
328 | { |
329 | /* 1st face: try to load the image */ |
330 | img = SOIL_load_image( x_neg_file, &width, &height, &channels, force_channels ); |
331 | /* channels holds the original number of channels, which may have been forced */ |
332 | if( (force_channels >= 1) && (force_channels <= 4) ) |
333 | { |
334 | channels = force_channels; |
335 | } |
336 | if( NULL == img ) |
337 | { |
338 | /* image loading failed */ |
339 | result_string_pointer = stbi_failure_reason(); |
340 | return 0; |
341 | } |
342 | /* upload the texture, but reuse the assigned texture ID */ |
343 | tex_id = SOIL_internal_create_OGL_texture( |
344 | img, width, height, channels, |
345 | tex_id, flags, |
346 | SOIL_TEXTURE_CUBE_MAP, SOIL_TEXTURE_CUBE_MAP_NEGATIVE_X, |
347 | SOIL_MAX_CUBE_MAP_TEXTURE_SIZE ); |
348 | /* and nuke the image data */ |
349 | SOIL_free_image_data( img ); |
350 | } |
351 | /* continue? */ |
352 | if( tex_id != 0 ) |
353 | { |
354 | /* 1st face: try to load the image */ |
355 | img = SOIL_load_image( y_pos_file, &width, &height, &channels, force_channels ); |
356 | /* channels holds the original number of channels, which may have been forced */ |
357 | if( (force_channels >= 1) && (force_channels <= 4) ) |
358 | { |
359 | channels = force_channels; |
360 | } |
361 | if( NULL == img ) |
362 | { |
363 | /* image loading failed */ |
364 | result_string_pointer = stbi_failure_reason(); |
365 | return 0; |
366 | } |
367 | /* upload the texture, but reuse the assigned texture ID */ |
368 | tex_id = SOIL_internal_create_OGL_texture( |
369 | img, width, height, channels, |
370 | tex_id, flags, |
371 | SOIL_TEXTURE_CUBE_MAP, SOIL_TEXTURE_CUBE_MAP_POSITIVE_Y, |
372 | SOIL_MAX_CUBE_MAP_TEXTURE_SIZE ); |
373 | /* and nuke the image data */ |
374 | SOIL_free_image_data( img ); |
375 | } |
376 | /* continue? */ |
377 | if( tex_id != 0 ) |
378 | { |
379 | /* 1st face: try to load the image */ |
380 | img = SOIL_load_image( y_neg_file, &width, &height, &channels, force_channels ); |
381 | /* channels holds the original number of channels, which may have been forced */ |
382 | if( (force_channels >= 1) && (force_channels <= 4) ) |
383 | { |
384 | channels = force_channels; |
385 | } |
386 | if( NULL == img ) |
387 | { |
388 | /* image loading failed */ |
389 | result_string_pointer = stbi_failure_reason(); |
390 | return 0; |
391 | } |
392 | /* upload the texture, but reuse the assigned texture ID */ |
393 | tex_id = SOIL_internal_create_OGL_texture( |
394 | img, width, height, channels, |
395 | tex_id, flags, |
396 | SOIL_TEXTURE_CUBE_MAP, SOIL_TEXTURE_CUBE_MAP_NEGATIVE_Y, |
397 | SOIL_MAX_CUBE_MAP_TEXTURE_SIZE ); |
398 | /* and nuke the image data */ |
399 | SOIL_free_image_data( img ); |
400 | } |
401 | /* continue? */ |
402 | if( tex_id != 0 ) |
403 | { |
404 | /* 1st face: try to load the image */ |
405 | img = SOIL_load_image( z_pos_file, &width, &height, &channels, force_channels ); |
406 | /* channels holds the original number of channels, which may have been forced */ |
407 | if( (force_channels >= 1) && (force_channels <= 4) ) |
408 | { |
409 | channels = force_channels; |
410 | } |
411 | if( NULL == img ) |
412 | { |
413 | /* image loading failed */ |
414 | result_string_pointer = stbi_failure_reason(); |
415 | return 0; |
416 | } |
417 | /* upload the texture, but reuse the assigned texture ID */ |
418 | tex_id = SOIL_internal_create_OGL_texture( |
419 | img, width, height, channels, |
420 | tex_id, flags, |
421 | SOIL_TEXTURE_CUBE_MAP, SOIL_TEXTURE_CUBE_MAP_POSITIVE_Z, |
422 | SOIL_MAX_CUBE_MAP_TEXTURE_SIZE ); |
423 | /* and nuke the image data */ |
424 | SOIL_free_image_data( img ); |
425 | } |
426 | /* continue? */ |
427 | if( tex_id != 0 ) |
428 | { |
429 | /* 1st face: try to load the image */ |
430 | img = SOIL_load_image( z_neg_file, &width, &height, &channels, force_channels ); |
431 | /* channels holds the original number of channels, which may have been forced */ |
432 | if( (force_channels >= 1) && (force_channels <= 4) ) |
433 | { |
434 | channels = force_channels; |
435 | } |
436 | if( NULL == img ) |
437 | { |
438 | /* image loading failed */ |
439 | result_string_pointer = stbi_failure_reason(); |
440 | return 0; |
441 | } |
442 | /* upload the texture, but reuse the assigned texture ID */ |
443 | tex_id = SOIL_internal_create_OGL_texture( |
444 | img, width, height, channels, |
445 | tex_id, flags, |
446 | SOIL_TEXTURE_CUBE_MAP, SOIL_TEXTURE_CUBE_MAP_NEGATIVE_Z, |
447 | SOIL_MAX_CUBE_MAP_TEXTURE_SIZE ); |
448 | /* and nuke the image data */ |
449 | SOIL_free_image_data( img ); |
450 | } |
451 | /* and return the handle, such as it is */ |
452 | return tex_id; |
453 | } |
454 | |
455 | unsigned int |
456 | SOIL_load_OGL_cubemap_from_memory |
457 | ( |
458 | const unsigned char *const x_pos_buffer, |
459 | int x_pos_buffer_length, |
460 | const unsigned char *const x_neg_buffer, |
461 | int x_neg_buffer_length, |
462 | const unsigned char *const y_pos_buffer, |
463 | int y_pos_buffer_length, |
464 | const unsigned char *const y_neg_buffer, |
465 | int y_neg_buffer_length, |
466 | const unsigned char *const z_pos_buffer, |
467 | int z_pos_buffer_length, |
468 | const unsigned char *const z_neg_buffer, |
469 | int z_neg_buffer_length, |
470 | int force_channels, |
471 | unsigned int reuse_texture_ID, |
472 | unsigned int flags |
473 | ) |
474 | { |
475 | /* variables */ |
476 | unsigned char* img; |
477 | int width, height, channels; |
478 | unsigned int tex_id; |
479 | /* error checking */ |
480 | if( (x_pos_buffer == NULL) || |
481 | (x_neg_buffer == NULL) || |
482 | (y_pos_buffer == NULL) || |
483 | (y_neg_buffer == NULL) || |
484 | (z_pos_buffer == NULL) || |
485 | (z_neg_buffer == NULL) ) |
486 | { |
487 | result_string_pointer = "Invalid cube map buffers list" ; |
488 | return 0; |
489 | } |
490 | /* capability checking */ |
491 | if( query_cubemap_capability() != SOIL_CAPABILITY_PRESENT ) |
492 | { |
493 | result_string_pointer = "No cube map capability present" ; |
494 | return 0; |
495 | } |
496 | /* 1st face: try to load the image */ |
497 | img = SOIL_load_image_from_memory( |
498 | x_pos_buffer, x_pos_buffer_length, |
499 | &width, &height, &channels, force_channels ); |
500 | /* channels holds the original number of channels, which may have been forced */ |
501 | if( (force_channels >= 1) && (force_channels <= 4) ) |
502 | { |
503 | channels = force_channels; |
504 | } |
505 | if( NULL == img ) |
506 | { |
507 | /* image loading failed */ |
508 | result_string_pointer = stbi_failure_reason(); |
509 | return 0; |
510 | } |
511 | /* upload the texture, and create a texture ID if necessary */ |
512 | tex_id = SOIL_internal_create_OGL_texture( |
513 | img, width, height, channels, |
514 | reuse_texture_ID, flags, |
515 | SOIL_TEXTURE_CUBE_MAP, SOIL_TEXTURE_CUBE_MAP_POSITIVE_X, |
516 | SOIL_MAX_CUBE_MAP_TEXTURE_SIZE ); |
517 | /* and nuke the image data */ |
518 | SOIL_free_image_data( img ); |
519 | /* continue? */ |
520 | if( tex_id != 0 ) |
521 | { |
522 | /* 1st face: try to load the image */ |
523 | img = SOIL_load_image_from_memory( |
524 | x_neg_buffer, x_neg_buffer_length, |
525 | &width, &height, &channels, force_channels ); |
526 | /* channels holds the original number of channels, which may have been forced */ |
527 | if( (force_channels >= 1) && (force_channels <= 4) ) |
528 | { |
529 | channels = force_channels; |
530 | } |
531 | if( NULL == img ) |
532 | { |
533 | /* image loading failed */ |
534 | result_string_pointer = stbi_failure_reason(); |
535 | return 0; |
536 | } |
537 | /* upload the texture, but reuse the assigned texture ID */ |
538 | tex_id = SOIL_internal_create_OGL_texture( |
539 | img, width, height, channels, |
540 | tex_id, flags, |
541 | SOIL_TEXTURE_CUBE_MAP, SOIL_TEXTURE_CUBE_MAP_NEGATIVE_X, |
542 | SOIL_MAX_CUBE_MAP_TEXTURE_SIZE ); |
543 | /* and nuke the image data */ |
544 | SOIL_free_image_data( img ); |
545 | } |
546 | /* continue? */ |
547 | if( tex_id != 0 ) |
548 | { |
549 | /* 1st face: try to load the image */ |
550 | img = SOIL_load_image_from_memory( |
551 | y_pos_buffer, y_pos_buffer_length, |
552 | &width, &height, &channels, force_channels ); |
553 | /* channels holds the original number of channels, which may have been forced */ |
554 | if( (force_channels >= 1) && (force_channels <= 4) ) |
555 | { |
556 | channels = force_channels; |
557 | } |
558 | if( NULL == img ) |
559 | { |
560 | /* image loading failed */ |
561 | result_string_pointer = stbi_failure_reason(); |
562 | return 0; |
563 | } |
564 | /* upload the texture, but reuse the assigned texture ID */ |
565 | tex_id = SOIL_internal_create_OGL_texture( |
566 | img, width, height, channels, |
567 | tex_id, flags, |
568 | SOIL_TEXTURE_CUBE_MAP, SOIL_TEXTURE_CUBE_MAP_POSITIVE_Y, |
569 | SOIL_MAX_CUBE_MAP_TEXTURE_SIZE ); |
570 | /* and nuke the image data */ |
571 | SOIL_free_image_data( img ); |
572 | } |
573 | /* continue? */ |
574 | if( tex_id != 0 ) |
575 | { |
576 | /* 1st face: try to load the image */ |
577 | img = SOIL_load_image_from_memory( |
578 | y_neg_buffer, y_neg_buffer_length, |
579 | &width, &height, &channels, force_channels ); |
580 | /* channels holds the original number of channels, which may have been forced */ |
581 | if( (force_channels >= 1) && (force_channels <= 4) ) |
582 | { |
583 | channels = force_channels; |
584 | } |
585 | if( NULL == img ) |
586 | { |
587 | /* image loading failed */ |
588 | result_string_pointer = stbi_failure_reason(); |
589 | return 0; |
590 | } |
591 | /* upload the texture, but reuse the assigned texture ID */ |
592 | tex_id = SOIL_internal_create_OGL_texture( |
593 | img, width, height, channels, |
594 | tex_id, flags, |
595 | SOIL_TEXTURE_CUBE_MAP, SOIL_TEXTURE_CUBE_MAP_NEGATIVE_Y, |
596 | SOIL_MAX_CUBE_MAP_TEXTURE_SIZE ); |
597 | /* and nuke the image data */ |
598 | SOIL_free_image_data( img ); |
599 | } |
600 | /* continue? */ |
601 | if( tex_id != 0 ) |
602 | { |
603 | /* 1st face: try to load the image */ |
604 | img = SOIL_load_image_from_memory( |
605 | z_pos_buffer, z_pos_buffer_length, |
606 | &width, &height, &channels, force_channels ); |
607 | /* channels holds the original number of channels, which may have been forced */ |
608 | if( (force_channels >= 1) && (force_channels <= 4) ) |
609 | { |
610 | channels = force_channels; |
611 | } |
612 | if( NULL == img ) |
613 | { |
614 | /* image loading failed */ |
615 | result_string_pointer = stbi_failure_reason(); |
616 | return 0; |
617 | } |
618 | /* upload the texture, but reuse the assigned texture ID */ |
619 | tex_id = SOIL_internal_create_OGL_texture( |
620 | img, width, height, channels, |
621 | tex_id, flags, |
622 | SOIL_TEXTURE_CUBE_MAP, SOIL_TEXTURE_CUBE_MAP_POSITIVE_Z, |
623 | SOIL_MAX_CUBE_MAP_TEXTURE_SIZE ); |
624 | /* and nuke the image data */ |
625 | SOIL_free_image_data( img ); |
626 | } |
627 | /* continue? */ |
628 | if( tex_id != 0 ) |
629 | { |
630 | /* 1st face: try to load the image */ |
631 | img = SOIL_load_image_from_memory( |
632 | z_neg_buffer, z_neg_buffer_length, |
633 | &width, &height, &channels, force_channels ); |
634 | /* channels holds the original number of channels, which may have been forced */ |
635 | if( (force_channels >= 1) && (force_channels <= 4) ) |
636 | { |
637 | channels = force_channels; |
638 | } |
639 | if( NULL == img ) |
640 | { |
641 | /* image loading failed */ |
642 | result_string_pointer = stbi_failure_reason(); |
643 | return 0; |
644 | } |
645 | /* upload the texture, but reuse the assigned texture ID */ |
646 | tex_id = SOIL_internal_create_OGL_texture( |
647 | img, width, height, channels, |
648 | tex_id, flags, |
649 | SOIL_TEXTURE_CUBE_MAP, SOIL_TEXTURE_CUBE_MAP_NEGATIVE_Z, |
650 | SOIL_MAX_CUBE_MAP_TEXTURE_SIZE ); |
651 | /* and nuke the image data */ |
652 | SOIL_free_image_data( img ); |
653 | } |
654 | /* and return the handle, such as it is */ |
655 | return tex_id; |
656 | } |
657 | |
658 | unsigned int |
659 | SOIL_load_OGL_single_cubemap |
660 | ( |
661 | const char *filename, |
662 | const char face_order[6], |
663 | int force_channels, |
664 | unsigned int reuse_texture_ID, |
665 | unsigned int flags |
666 | ) |
667 | { |
668 | /* variables */ |
669 | unsigned char* img; |
670 | int width, height, channels, i; |
671 | unsigned int tex_id = 0; |
672 | /* error checking */ |
673 | if( filename == NULL ) |
674 | { |
675 | result_string_pointer = "Invalid single cube map file name" ; |
676 | return 0; |
677 | } |
678 | /* does the user want direct uploading of the image as a DDS file? */ |
679 | if( flags & SOIL_FLAG_DDS_LOAD_DIRECT ) |
680 | { |
681 | /* 1st try direct loading of the image as a DDS file |
682 | note: direct uploading will only load what is in the |
683 | DDS file, no MIPmaps will be generated, the image will |
684 | not be flipped, etc. */ |
685 | tex_id = SOIL_direct_load_DDS( filename, reuse_texture_ID, flags, 1 ); |
686 | if( tex_id ) |
687 | { |
688 | /* hey, it worked!! */ |
689 | return tex_id; |
690 | } |
691 | } |
692 | /* face order checking */ |
693 | for( i = 0; i < 6; ++i ) |
694 | { |
695 | if( (face_order[i] != 'N') && |
696 | (face_order[i] != 'S') && |
697 | (face_order[i] != 'W') && |
698 | (face_order[i] != 'E') && |
699 | (face_order[i] != 'U') && |
700 | (face_order[i] != 'D') ) |
701 | { |
702 | result_string_pointer = "Invalid single cube map face order" ; |
703 | return 0; |
704 | }; |
705 | } |
706 | /* capability checking */ |
707 | if( query_cubemap_capability() != SOIL_CAPABILITY_PRESENT ) |
708 | { |
709 | result_string_pointer = "No cube map capability present" ; |
710 | return 0; |
711 | } |
712 | /* 1st off, try to load the full image */ |
713 | img = SOIL_load_image( filename, &width, &height, &channels, force_channels ); |
714 | /* channels holds the original number of channels, which may have been forced */ |
715 | if( (force_channels >= 1) && (force_channels <= 4) ) |
716 | { |
717 | channels = force_channels; |
718 | } |
719 | if( NULL == img ) |
720 | { |
721 | /* image loading failed */ |
722 | result_string_pointer = stbi_failure_reason(); |
723 | return 0; |
724 | } |
725 | /* now, does this image have the right dimensions? */ |
726 | if( (width != 6*height) && |
727 | (6*width != height) ) |
728 | { |
729 | SOIL_free_image_data( img ); |
730 | result_string_pointer = "Single cubemap image must have a 6:1 ratio" ; |
731 | return 0; |
732 | } |
733 | /* try the image split and create */ |
734 | tex_id = SOIL_create_OGL_single_cubemap( |
735 | img, width, height, channels, |
736 | face_order, reuse_texture_ID, flags |
737 | ); |
738 | /* nuke the temporary image data and return the texture handle */ |
739 | SOIL_free_image_data( img ); |
740 | return tex_id; |
741 | } |
742 | |
743 | unsigned int |
744 | SOIL_load_OGL_single_cubemap_from_memory |
745 | ( |
746 | const unsigned char *const buffer, |
747 | int buffer_length, |
748 | const char face_order[6], |
749 | int force_channels, |
750 | unsigned int reuse_texture_ID, |
751 | unsigned int flags |
752 | ) |
753 | { |
754 | /* variables */ |
755 | unsigned char* img; |
756 | int width, height, channels, i; |
757 | unsigned int tex_id = 0; |
758 | /* error checking */ |
759 | if( buffer == NULL ) |
760 | { |
761 | result_string_pointer = "Invalid single cube map buffer" ; |
762 | return 0; |
763 | } |
764 | /* does the user want direct uploading of the image as a DDS file? */ |
765 | if( flags & SOIL_FLAG_DDS_LOAD_DIRECT ) |
766 | { |
767 | /* 1st try direct loading of the image as a DDS file |
768 | note: direct uploading will only load what is in the |
769 | DDS file, no MIPmaps will be generated, the image will |
770 | not be flipped, etc. */ |
771 | tex_id = SOIL_direct_load_DDS_from_memory( |
772 | buffer, buffer_length, |
773 | reuse_texture_ID, flags, 1 ); |
774 | if( tex_id ) |
775 | { |
776 | /* hey, it worked!! */ |
777 | return tex_id; |
778 | } |
779 | } |
780 | /* face order checking */ |
781 | for( i = 0; i < 6; ++i ) |
782 | { |
783 | if( (face_order[i] != 'N') && |
784 | (face_order[i] != 'S') && |
785 | (face_order[i] != 'W') && |
786 | (face_order[i] != 'E') && |
787 | (face_order[i] != 'U') && |
788 | (face_order[i] != 'D') ) |
789 | { |
790 | result_string_pointer = "Invalid single cube map face order" ; |
791 | return 0; |
792 | }; |
793 | } |
794 | /* capability checking */ |
795 | if( query_cubemap_capability() != SOIL_CAPABILITY_PRESENT ) |
796 | { |
797 | result_string_pointer = "No cube map capability present" ; |
798 | return 0; |
799 | } |
800 | /* 1st off, try to load the full image */ |
801 | img = SOIL_load_image_from_memory( |
802 | buffer, buffer_length, |
803 | &width, &height, &channels, |
804 | force_channels ); |
805 | /* channels holds the original number of channels, which may have been forced */ |
806 | if( (force_channels >= 1) && (force_channels <= 4) ) |
807 | { |
808 | channels = force_channels; |
809 | } |
810 | if( NULL == img ) |
811 | { |
812 | /* image loading failed */ |
813 | result_string_pointer = stbi_failure_reason(); |
814 | return 0; |
815 | } |
816 | /* now, does this image have the right dimensions? */ |
817 | if( (width != 6*height) && |
818 | (6*width != height) ) |
819 | { |
820 | SOIL_free_image_data( img ); |
821 | result_string_pointer = "Single cubemap image must have a 6:1 ratio" ; |
822 | return 0; |
823 | } |
824 | /* try the image split and create */ |
825 | tex_id = SOIL_create_OGL_single_cubemap( |
826 | img, width, height, channels, |
827 | face_order, reuse_texture_ID, flags |
828 | ); |
829 | /* nuke the temporary image data and return the texture handle */ |
830 | SOIL_free_image_data( img ); |
831 | return tex_id; |
832 | } |
833 | |
834 | unsigned int |
835 | SOIL_create_OGL_single_cubemap |
836 | ( |
837 | const unsigned char *const data, |
838 | int width, int height, int channels, |
839 | const char face_order[6], |
840 | unsigned int reuse_texture_ID, |
841 | unsigned int flags |
842 | ) |
843 | { |
844 | /* variables */ |
845 | unsigned char* sub_img; |
846 | int dw, dh, sz, i; |
847 | unsigned int tex_id; |
848 | /* error checking */ |
849 | if( data == NULL ) |
850 | { |
851 | result_string_pointer = "Invalid single cube map image data" ; |
852 | return 0; |
853 | } |
854 | /* face order checking */ |
855 | for( i = 0; i < 6; ++i ) |
856 | { |
857 | if( (face_order[i] != 'N') && |
858 | (face_order[i] != 'S') && |
859 | (face_order[i] != 'W') && |
860 | (face_order[i] != 'E') && |
861 | (face_order[i] != 'U') && |
862 | (face_order[i] != 'D') ) |
863 | { |
864 | result_string_pointer = "Invalid single cube map face order" ; |
865 | return 0; |
866 | }; |
867 | } |
868 | /* capability checking */ |
869 | if( query_cubemap_capability() != SOIL_CAPABILITY_PRESENT ) |
870 | { |
871 | result_string_pointer = "No cube map capability present" ; |
872 | return 0; |
873 | } |
874 | /* now, does this image have the right dimensions? */ |
875 | if( (width != 6*height) && |
876 | (6*width != height) ) |
877 | { |
878 | result_string_pointer = "Single cubemap image must have a 6:1 ratio" ; |
879 | return 0; |
880 | } |
881 | /* which way am I stepping? */ |
882 | if( width > height ) |
883 | { |
884 | dw = height; |
885 | dh = 0; |
886 | } else |
887 | { |
888 | dw = 0; |
889 | dh = width; |
890 | } |
891 | sz = dw+dh; |
892 | sub_img = (unsigned char *)malloc( sz*sz*channels ); |
893 | /* do the splitting and uploading */ |
894 | tex_id = reuse_texture_ID; |
895 | for( i = 0; i < 6; ++i ) |
896 | { |
897 | int x, y, idx = 0; |
898 | unsigned int cubemap_target = 0; |
899 | /* copy in the sub-image */ |
900 | for( y = i*dh; y < i*dh+sz; ++y ) |
901 | { |
902 | for( x = i*dw*channels; x < (i*dw+sz)*channels; ++x ) |
903 | { |
904 | sub_img[idx++] = data[y*width*channels+x]; |
905 | } |
906 | } |
907 | /* what is my texture target? |
908 | remember, this coordinate system is |
909 | LHS if viewed from inside the cube! */ |
910 | switch( face_order[i] ) |
911 | { |
912 | case 'N': |
913 | cubemap_target = SOIL_TEXTURE_CUBE_MAP_POSITIVE_Z; |
914 | break; |
915 | case 'S': |
916 | cubemap_target = SOIL_TEXTURE_CUBE_MAP_NEGATIVE_Z; |
917 | break; |
918 | case 'W': |
919 | cubemap_target = SOIL_TEXTURE_CUBE_MAP_NEGATIVE_X; |
920 | break; |
921 | case 'E': |
922 | cubemap_target = SOIL_TEXTURE_CUBE_MAP_POSITIVE_X; |
923 | break; |
924 | case 'U': |
925 | cubemap_target = SOIL_TEXTURE_CUBE_MAP_POSITIVE_Y; |
926 | break; |
927 | case 'D': |
928 | cubemap_target = SOIL_TEXTURE_CUBE_MAP_NEGATIVE_Y; |
929 | break; |
930 | } |
931 | /* upload it as a texture */ |
932 | tex_id = SOIL_internal_create_OGL_texture( |
933 | sub_img, sz, sz, channels, |
934 | tex_id, flags, |
935 | SOIL_TEXTURE_CUBE_MAP, |
936 | cubemap_target, |
937 | SOIL_MAX_CUBE_MAP_TEXTURE_SIZE ); |
938 | } |
939 | /* and nuke the image and sub-image data */ |
940 | SOIL_free_image_data( sub_img ); |
941 | /* and return the handle, such as it is */ |
942 | return tex_id; |
943 | } |
944 | |
945 | unsigned int |
946 | SOIL_create_OGL_texture |
947 | ( |
948 | const unsigned char *const data, |
949 | int width, int height, int channels, |
950 | unsigned int reuse_texture_ID, |
951 | unsigned int flags |
952 | ) |
953 | { |
954 | /* wrapper function for 2D textures */ |
955 | return SOIL_internal_create_OGL_texture( |
956 | data, width, height, channels, |
957 | reuse_texture_ID, flags, |
958 | GL_TEXTURE_2D, GL_TEXTURE_2D, |
959 | GL_MAX_TEXTURE_SIZE ); |
960 | } |
961 | |
962 | #if SOIL_CHECK_FOR_GL_ERRORS |
963 | void check_for_GL_errors( const char *calling_location ) |
964 | { |
965 | /* check for errors */ |
966 | GLenum err_code = glGetError(); |
967 | while( GL_NO_ERROR != err_code ) |
968 | { |
969 | printf( "OpenGL Error @ %s: %i" , calling_location, err_code ); |
970 | err_code = glGetError(); |
971 | } |
972 | } |
973 | #else |
974 | void check_for_GL_errors( const char *calling_location ) |
975 | { |
976 | /* no check for errors */ |
977 | } |
978 | #endif |
979 | |
980 | unsigned int |
981 | SOIL_internal_create_OGL_texture |
982 | ( |
983 | const unsigned char *const data, |
984 | int width, int height, int channels, |
985 | unsigned int reuse_texture_ID, |
986 | unsigned int flags, |
987 | unsigned int opengl_texture_type, |
988 | unsigned int opengl_texture_target, |
989 | unsigned int texture_check_size_enum |
990 | ) |
991 | { |
992 | /* variables */ |
993 | unsigned char* img; |
994 | unsigned int tex_id; |
995 | unsigned int internal_texture_format = 0, original_texture_format = 0; |
996 | int DXT_mode = SOIL_CAPABILITY_UNKNOWN; |
997 | int max_supported_size; |
998 | /* If the user wants to use the texture rectangle I kill a few flags */ |
999 | if( flags & SOIL_FLAG_TEXTURE_RECTANGLE ) |
1000 | { |
1001 | /* well, the user asked for it, can we do that? */ |
1002 | if( query_tex_rectangle_capability() == SOIL_CAPABILITY_PRESENT ) |
1003 | { |
1004 | /* only allow this if the user in _NOT_ trying to do a cubemap! */ |
1005 | if( opengl_texture_type == GL_TEXTURE_2D ) |
1006 | { |
1007 | /* clean out the flags that cannot be used with texture rectangles */ |
1008 | flags &= ~( |
1009 | SOIL_FLAG_POWER_OF_TWO | SOIL_FLAG_MIPMAPS | |
1010 | SOIL_FLAG_TEXTURE_REPEATS |
1011 | ); |
1012 | /* and change my target */ |
1013 | opengl_texture_target = SOIL_TEXTURE_RECTANGLE_ARB; |
1014 | opengl_texture_type = SOIL_TEXTURE_RECTANGLE_ARB; |
1015 | } else |
1016 | { |
1017 | /* not allowed for any other uses (yes, I'm looking at you, cubemaps!) */ |
1018 | flags &= ~SOIL_FLAG_TEXTURE_RECTANGLE; |
1019 | } |
1020 | |
1021 | } else |
1022 | { |
1023 | /* can't do it, and that is a breakable offense (uv coords use pixels instead of [0,1]!) */ |
1024 | result_string_pointer = "Texture Rectangle extension unsupported" ; |
1025 | return 0; |
1026 | } |
1027 | } |
1028 | /* create a copy the image data */ |
1029 | img = (unsigned char*)malloc( width*height*channels ); |
1030 | memcpy( img, data, width*height*channels ); |
1031 | /* does the user want me to invert the image? */ |
1032 | if( flags & SOIL_FLAG_INVERT_Y ) |
1033 | { |
1034 | int i, j; |
1035 | for( j = 0; j*2 < height; ++j ) |
1036 | { |
1037 | int index1 = j * width * channels; |
1038 | int index2 = (height - 1 - j) * width * channels; |
1039 | for( i = width * channels; i > 0; --i ) |
1040 | { |
1041 | unsigned char temp = img[index1]; |
1042 | img[index1] = img[index2]; |
1043 | img[index2] = temp; |
1044 | ++index1; |
1045 | ++index2; |
1046 | } |
1047 | } |
1048 | } |
1049 | /* does the user want me to scale the colors into the NTSC safe RGB range? */ |
1050 | if( flags & SOIL_FLAG_NTSC_SAFE_RGB ) |
1051 | { |
1052 | scale_image_RGB_to_NTSC_safe( img, width, height, channels ); |
1053 | } |
1054 | /* does the user want me to convert from straight to pre-multiplied alpha? |
1055 | (and do we even _have_ alpha?) */ |
1056 | if( flags & SOIL_FLAG_MULTIPLY_ALPHA ) |
1057 | { |
1058 | int i; |
1059 | switch( channels ) |
1060 | { |
1061 | case 2: |
1062 | for( i = 0; i < 2*width*height; i += 2 ) |
1063 | { |
1064 | img[i] = (img[i] * img[i+1] + 128) >> 8; |
1065 | } |
1066 | break; |
1067 | case 4: |
1068 | for( i = 0; i < 4*width*height; i += 4 ) |
1069 | { |
1070 | img[i+0] = (img[i+0] * img[i+3] + 128) >> 8; |
1071 | img[i+1] = (img[i+1] * img[i+3] + 128) >> 8; |
1072 | img[i+2] = (img[i+2] * img[i+3] + 128) >> 8; |
1073 | } |
1074 | break; |
1075 | default: |
1076 | /* no other number of channels contains alpha data */ |
1077 | break; |
1078 | } |
1079 | } |
1080 | /* if the user can't support NPOT textures, make sure we force the POT option */ |
1081 | if( (query_NPOT_capability() == SOIL_CAPABILITY_NONE) && |
1082 | !(flags & SOIL_FLAG_TEXTURE_RECTANGLE) ) |
1083 | { |
1084 | /* add in the POT flag */ |
1085 | flags |= SOIL_FLAG_POWER_OF_TWO; |
1086 | } |
1087 | /* how large of a texture can this OpenGL implementation handle? */ |
1088 | /* texture_check_size_enum will be GL_MAX_TEXTURE_SIZE or SOIL_MAX_CUBE_MAP_TEXTURE_SIZE */ |
1089 | glGetIntegerv( texture_check_size_enum, &max_supported_size ); |
1090 | /* do I need to make it a power of 2? */ |
1091 | if( |
1092 | (flags & SOIL_FLAG_POWER_OF_TWO) || /* user asked for it */ |
1093 | (flags & SOIL_FLAG_MIPMAPS) || /* need it for the MIP-maps */ |
1094 | (width > max_supported_size) || /* it's too big, (make sure it's */ |
1095 | (height > max_supported_size) ) /* 2^n for later down-sampling) */ |
1096 | { |
1097 | int new_width = 1; |
1098 | int new_height = 1; |
1099 | while( new_width < width ) |
1100 | { |
1101 | new_width *= 2; |
1102 | } |
1103 | while( new_height < height ) |
1104 | { |
1105 | new_height *= 2; |
1106 | } |
1107 | /* still? */ |
1108 | if( (new_width != width) || (new_height != height) ) |
1109 | { |
1110 | /* yep, resize */ |
1111 | unsigned char *resampled = (unsigned char*)malloc( channels*new_width*new_height ); |
1112 | up_scale_image( |
1113 | img, width, height, channels, |
1114 | resampled, new_width, new_height ); |
1115 | /* OJO this is for debug only! */ |
1116 | /* |
1117 | SOIL_save_image( "\\showme.bmp", SOIL_SAVE_TYPE_BMP, |
1118 | new_width, new_height, channels, |
1119 | resampled ); |
1120 | */ |
1121 | /* nuke the old guy, then point it at the new guy */ |
1122 | SOIL_free_image_data( img ); |
1123 | img = resampled; |
1124 | width = new_width; |
1125 | height = new_height; |
1126 | } |
1127 | } |
1128 | /* now, if it is too large... */ |
1129 | if( (width > max_supported_size) || (height > max_supported_size) ) |
1130 | { |
1131 | /* I've already made it a power of two, so simply use the MIPmapping |
1132 | code to reduce its size to the allowable maximum. */ |
1133 | unsigned char *resampled; |
1134 | int reduce_block_x = 1, reduce_block_y = 1; |
1135 | int new_width, new_height; |
1136 | if( width > max_supported_size ) |
1137 | { |
1138 | reduce_block_x = width / max_supported_size; |
1139 | } |
1140 | if( height > max_supported_size ) |
1141 | { |
1142 | reduce_block_y = height / max_supported_size; |
1143 | } |
1144 | new_width = width / reduce_block_x; |
1145 | new_height = height / reduce_block_y; |
1146 | resampled = (unsigned char*)malloc( channels*new_width*new_height ); |
1147 | /* perform the actual reduction */ |
1148 | mipmap_image( img, width, height, channels, |
1149 | resampled, reduce_block_x, reduce_block_y ); |
1150 | /* nuke the old guy, then point it at the new guy */ |
1151 | SOIL_free_image_data( img ); |
1152 | img = resampled; |
1153 | width = new_width; |
1154 | height = new_height; |
1155 | } |
1156 | /* does the user want us to use YCoCg color space? */ |
1157 | if( flags & SOIL_FLAG_CoCg_Y ) |
1158 | { |
1159 | /* this will only work with RGB and RGBA images */ |
1160 | convert_RGB_to_YCoCg( img, width, height, channels ); |
1161 | /* |
1162 | save_image_as_DDS( "CoCg_Y.dds", width, height, channels, img ); |
1163 | */ |
1164 | } |
1165 | /* create the OpenGL texture ID handle |
1166 | (note: allowing a forced texture ID lets me reload a texture) */ |
1167 | tex_id = reuse_texture_ID; |
1168 | if( tex_id == 0 ) |
1169 | { |
1170 | glGenTextures( 1, &tex_id ); |
1171 | } |
1172 | check_for_GL_errors( "glGenTextures" ); |
1173 | /* Note: sometimes glGenTextures fails (usually no OpenGL context) */ |
1174 | if( tex_id ) |
1175 | { |
1176 | /* and what type am I using as the internal texture format? */ |
1177 | switch( channels ) |
1178 | { |
1179 | case 1: |
1180 | original_texture_format = GL_LUMINANCE; |
1181 | break; |
1182 | case 2: |
1183 | original_texture_format = GL_LUMINANCE_ALPHA; |
1184 | break; |
1185 | case 3: |
1186 | original_texture_format = GL_RGB; |
1187 | break; |
1188 | case 4: |
1189 | original_texture_format = GL_RGBA; |
1190 | break; |
1191 | } |
1192 | internal_texture_format = original_texture_format; |
1193 | /* does the user want me to, and can I, save as DXT? */ |
1194 | if( flags & SOIL_FLAG_COMPRESS_TO_DXT ) |
1195 | { |
1196 | DXT_mode = query_DXT_capability(); |
1197 | if( DXT_mode == SOIL_CAPABILITY_PRESENT ) |
1198 | { |
1199 | /* I can use DXT, whether I compress it or OpenGL does */ |
1200 | if( (channels & 1) == 1 ) |
1201 | { |
1202 | /* 1 or 3 channels = DXT1 */ |
1203 | internal_texture_format = SOIL_RGB_S3TC_DXT1; |
1204 | } else |
1205 | { |
1206 | /* 2 or 4 channels = DXT5 */ |
1207 | internal_texture_format = SOIL_RGBA_S3TC_DXT5; |
1208 | } |
1209 | } |
1210 | } |
1211 | /* bind an OpenGL texture ID */ |
1212 | glBindTexture( opengl_texture_type, tex_id ); |
1213 | check_for_GL_errors( "glBindTexture" ); |
1214 | /* upload the main image */ |
1215 | if( DXT_mode == SOIL_CAPABILITY_PRESENT ) |
1216 | { |
1217 | /* user wants me to do the DXT conversion! */ |
1218 | int DDS_size; |
1219 | unsigned char *DDS_data = NULL; |
1220 | if( (channels & 1) == 1 ) |
1221 | { |
1222 | /* RGB, use DXT1 */ |
1223 | DDS_data = convert_image_to_DXT1( img, width, height, channels, &DDS_size ); |
1224 | } else |
1225 | { |
1226 | /* RGBA, use DXT5 */ |
1227 | DDS_data = convert_image_to_DXT5( img, width, height, channels, &DDS_size ); |
1228 | } |
1229 | if( DDS_data ) |
1230 | { |
1231 | soilGlCompressedTexImage2D( |
1232 | opengl_texture_target, 0, |
1233 | internal_texture_format, width, height, 0, |
1234 | DDS_size, DDS_data ); |
1235 | check_for_GL_errors( "glCompressedTexImage2D" ); |
1236 | SOIL_free_image_data( DDS_data ); |
1237 | /* printf( "Internal DXT compressor\n" ); */ |
1238 | } else |
1239 | { |
1240 | /* my compression failed, try the OpenGL driver's version */ |
1241 | glTexImage2D( |
1242 | opengl_texture_target, 0, |
1243 | internal_texture_format, width, height, 0, |
1244 | original_texture_format, GL_UNSIGNED_BYTE, img ); |
1245 | check_for_GL_errors( "glTexImage2D" ); |
1246 | /* printf( "OpenGL DXT compressor\n" ); */ |
1247 | } |
1248 | } else |
1249 | { |
1250 | /* user want OpenGL to do all the work! */ |
1251 | glTexImage2D( |
1252 | opengl_texture_target, 0, |
1253 | internal_texture_format, width, height, 0, |
1254 | original_texture_format, GL_UNSIGNED_BYTE, img ); |
1255 | check_for_GL_errors( "glTexImage2D" ); |
1256 | /*printf( "OpenGL DXT compressor\n" ); */ |
1257 | } |
1258 | /* are any MIPmaps desired? */ |
1259 | if( flags & SOIL_FLAG_MIPMAPS ) |
1260 | { |
1261 | int MIPlevel = 1; |
1262 | int MIPwidth = (width+1) / 2; |
1263 | int MIPheight = (height+1) / 2; |
1264 | unsigned char *resampled = (unsigned char*)malloc( channels*MIPwidth*MIPheight ); |
1265 | while( ((1<<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 | |
1367 | int |
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 | |
1424 | unsigned char* |
1425 | SOIL_load_image |
1426 | ( |
1427 | const char *filename, |
1428 | int *width, int *height, int *channels, |
1429 | int force_channels |
1430 | ) |
1431 | { |
1432 | unsigned char *result = stbi_load( filename, |
1433 | width, height, channels, force_channels ); |
1434 | if( result == NULL ) |
1435 | { |
1436 | result_string_pointer = stbi_failure_reason(); |
1437 | } else |
1438 | { |
1439 | result_string_pointer = "Image loaded" ; |
1440 | } |
1441 | return result; |
1442 | } |
1443 | |
1444 | unsigned char* |
1445 | SOIL_load_image_from_memory |
1446 | ( |
1447 | const unsigned char *const buffer, |
1448 | int buffer_length, |
1449 | int *width, int *height, int *channels, |
1450 | int force_channels |
1451 | ) |
1452 | { |
1453 | unsigned char *result = stbi_load_from_memory( |
1454 | buffer, buffer_length, |
1455 | width, height, channels, |
1456 | force_channels ); |
1457 | if( result == NULL ) |
1458 | { |
1459 | result_string_pointer = stbi_failure_reason(); |
1460 | } else |
1461 | { |
1462 | result_string_pointer = "Image loaded from memory" ; |
1463 | } |
1464 | return result; |
1465 | } |
1466 | |
1467 | int |
1468 | SOIL_save_image |
1469 | ( |
1470 | const char *filename, |
1471 | int image_type, |
1472 | int width, int height, int channels, |
1473 | const unsigned char *const data |
1474 | ) |
1475 | { |
1476 | int save_result; |
1477 | |
1478 | /* error check */ |
1479 | if( (width < 1) || (height < 1) || |
1480 | (channels < 1) || (channels > 4) || |
1481 | (data == NULL) || |
1482 | (filename == NULL) ) |
1483 | { |
1484 | return 0; |
1485 | } |
1486 | if( image_type == SOIL_SAVE_TYPE_BMP ) |
1487 | { |
1488 | save_result = stbi_write_bmp( filename, |
1489 | width, height, channels, (void*)data ); |
1490 | } else |
1491 | if( image_type == SOIL_SAVE_TYPE_TGA ) |
1492 | { |
1493 | save_result = stbi_write_tga( filename, |
1494 | width, height, channels, (void*)data ); |
1495 | } else |
1496 | if( image_type == SOIL_SAVE_TYPE_DDS ) |
1497 | { |
1498 | save_result = save_image_as_DDS( filename, |
1499 | width, height, channels, (const unsigned char *const)data ); |
1500 | } else |
1501 | { |
1502 | save_result = 0; |
1503 | } |
1504 | if( save_result == 0 ) |
1505 | { |
1506 | result_string_pointer = "Saving the image failed" ; |
1507 | } else |
1508 | { |
1509 | result_string_pointer = "Image saved" ; |
1510 | } |
1511 | return save_result; |
1512 | } |
1513 | |
1514 | void |
1515 | SOIL_free_image_data |
1516 | ( |
1517 | unsigned char *img_data |
1518 | ) |
1519 | { |
1520 | free( (void*)img_data ); |
1521 | } |
1522 | |
1523 | const char* |
1524 | SOIL_last_result |
1525 | ( |
1526 | void |
1527 | ) |
1528 | { |
1529 | return result_string_pointer; |
1530 | } |
1531 | |
1532 | unsigned int SOIL_direct_load_DDS_from_memory( |
1533 | const unsigned char *const buffer, |
1534 | int buffer_length, |
1535 | unsigned int reuse_texture_ID, |
1536 | int flags, |
1537 | int loading_as_cubemap ) |
1538 | { |
1539 | /* variables */ |
1540 | DDS_header ; |
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 | |
1820 | quick_exit: |
1821 | /* report success or failure */ |
1822 | return tex_ID; |
1823 | } |
1824 | |
1825 | unsigned int SOIL_direct_load_DDS( |
1826 | const char *filename, |
1827 | unsigned int reuse_texture_ID, |
1828 | int flags, |
1829 | int loading_as_cubemap ) |
1830 | { |
1831 | FILE *f; |
1832 | unsigned char *buffer; |
1833 | size_t buffer_length, bytes_read; |
1834 | unsigned int tex_ID = 0; |
1835 | /* error checks */ |
1836 | if( NULL == filename ) |
1837 | { |
1838 | result_string_pointer = "NULL filename" ; |
1839 | return 0; |
1840 | } |
1841 | f = fopen( filename, "rb" ); |
1842 | if( NULL == f ) |
1843 | { |
1844 | /* the file doesn't seem to exist (or be open-able) */ |
1845 | result_string_pointer = "Can not find DDS file" ; |
1846 | return 0; |
1847 | } |
1848 | fseek( f, 0, SEEK_END ); |
1849 | buffer_length = ftell( f ); |
1850 | fseek( f, 0, SEEK_SET ); |
1851 | buffer = (unsigned char *) malloc( buffer_length ); |
1852 | if( NULL == buffer ) |
1853 | { |
1854 | result_string_pointer = "malloc failed" ; |
1855 | fclose( f ); |
1856 | return 0; |
1857 | } |
1858 | bytes_read = fread( (void*)buffer, 1, buffer_length, f ); |
1859 | fclose( f ); |
1860 | if( bytes_read < buffer_length ) |
1861 | { |
1862 | /* huh? */ |
1863 | buffer_length = bytes_read; |
1864 | } |
1865 | /* now try to do the loading */ |
1866 | tex_ID = SOIL_direct_load_DDS_from_memory( |
1867 | (const unsigned char *const)buffer, buffer_length, |
1868 | reuse_texture_ID, flags, loading_as_cubemap ); |
1869 | SOIL_free_image_data( buffer ); |
1870 | return tex_ID; |
1871 | } |
1872 | |
1873 | int query_NPOT_capability( void ) |
1874 | { |
1875 | /* check for the capability */ |
1876 | if( has_NPOT_capability == SOIL_CAPABILITY_UNKNOWN ) |
1877 | { |
1878 | /* we haven't yet checked for the capability, do so */ |
1879 | if( |
1880 | (NULL == strstr( (char const*)glGetString( GL_EXTENSIONS ), |
1881 | "GL_ARB_texture_non_power_of_two" ) ) |
1882 | ) |
1883 | { |
1884 | /* not there, flag the failure */ |
1885 | has_NPOT_capability = SOIL_CAPABILITY_NONE; |
1886 | } else |
1887 | { |
1888 | /* it's there! */ |
1889 | has_NPOT_capability = SOIL_CAPABILITY_PRESENT; |
1890 | } |
1891 | } |
1892 | /* let the user know if we can do non-power-of-two textures or not */ |
1893 | return has_NPOT_capability; |
1894 | } |
1895 | |
1896 | int query_tex_rectangle_capability( void ) |
1897 | { |
1898 | /* check for the capability */ |
1899 | if( has_tex_rectangle_capability == SOIL_CAPABILITY_UNKNOWN ) |
1900 | { |
1901 | /* we haven't yet checked for the capability, do so */ |
1902 | if( |
1903 | (NULL == strstr( (char const*)glGetString( GL_EXTENSIONS ), |
1904 | "GL_ARB_texture_rectangle" ) ) |
1905 | && |
1906 | (NULL == strstr( (char const*)glGetString( GL_EXTENSIONS ), |
1907 | "GL_EXT_texture_rectangle" ) ) |
1908 | && |
1909 | (NULL == strstr( (char const*)glGetString( GL_EXTENSIONS ), |
1910 | "GL_NV_texture_rectangle" ) ) |
1911 | ) |
1912 | { |
1913 | /* not there, flag the failure */ |
1914 | has_tex_rectangle_capability = SOIL_CAPABILITY_NONE; |
1915 | } else |
1916 | { |
1917 | /* it's there! */ |
1918 | has_tex_rectangle_capability = SOIL_CAPABILITY_PRESENT; |
1919 | } |
1920 | } |
1921 | /* let the user know if we can do texture rectangles or not */ |
1922 | return has_tex_rectangle_capability; |
1923 | } |
1924 | |
1925 | int query_cubemap_capability( void ) |
1926 | { |
1927 | /* check for the capability */ |
1928 | if( has_cubemap_capability == SOIL_CAPABILITY_UNKNOWN ) |
1929 | { |
1930 | /* we haven't yet checked for the capability, do so */ |
1931 | if( |
1932 | (NULL == strstr( (char const*)glGetString( GL_EXTENSIONS ), |
1933 | "GL_ARB_texture_cube_map" ) ) |
1934 | && |
1935 | (NULL == strstr( (char const*)glGetString( GL_EXTENSIONS ), |
1936 | "GL_EXT_texture_cube_map" ) ) |
1937 | ) |
1938 | { |
1939 | /* not there, flag the failure */ |
1940 | has_cubemap_capability = SOIL_CAPABILITY_NONE; |
1941 | } else |
1942 | { |
1943 | /* it's there! */ |
1944 | has_cubemap_capability = SOIL_CAPABILITY_PRESENT; |
1945 | } |
1946 | } |
1947 | /* let the user know if we can do cubemaps or not */ |
1948 | return has_cubemap_capability; |
1949 | } |
1950 | |
1951 | int query_DXT_capability( void ) |
1952 | { |
1953 | /* check for the capability */ |
1954 | if( has_DXT_capability == SOIL_CAPABILITY_UNKNOWN ) |
1955 | { |
1956 | /* we haven't yet checked for the capability, do so */ |
1957 | if( NULL == strstr( |
1958 | (char const*)glGetString( GL_EXTENSIONS ), |
1959 | "GL_EXT_texture_compression_s3tc" ) ) |
1960 | { |
1961 | /* not there, flag the failure */ |
1962 | has_DXT_capability = SOIL_CAPABILITY_NONE; |
1963 | } else |
1964 | { |
1965 | /* and find the address of the extension function */ |
1966 | P_SOIL_GLCOMPRESSEDTEXIMAGE2DPROC ext_addr = NULL; |
1967 | #ifdef WIN32 |
1968 | ext_addr = (P_SOIL_GLCOMPRESSEDTEXIMAGE2DPROC) |
1969 | wglGetProcAddress |
1970 | ( |
1971 | "glCompressedTexImage2DARB" |
1972 | ); |
1973 | #elif defined(__APPLE__) || defined(__APPLE_CC__) |
1974 | /* I can't test this Apple stuff! */ |
1975 | CFBundleRef bundle; |
1976 | CFURLRef bundleURL = |
1977 | CFURLCreateWithFileSystemPath( |
1978 | kCFAllocatorDefault, |
1979 | CFSTR("/System/Library/Frameworks/OpenGL.framework" ), |
1980 | kCFURLPOSIXPathStyle, |
1981 | true ); |
1982 | CFStringRef extensionName = |
1983 | CFStringCreateWithCString( |
1984 | kCFAllocatorDefault, |
1985 | "glCompressedTexImage2DARB" , |
1986 | kCFStringEncodingASCII ); |
1987 | bundle = CFBundleCreate( kCFAllocatorDefault, bundleURL ); |
1988 | assert( bundle != NULL ); |
1989 | ext_addr = (P_SOIL_GLCOMPRESSEDTEXIMAGE2DPROC) |
1990 | CFBundleGetFunctionPointerForName |
1991 | ( |
1992 | bundle, extensionName |
1993 | ); |
1994 | CFRelease( bundleURL ); |
1995 | CFRelease( extensionName ); |
1996 | CFRelease( bundle ); |
1997 | #else |
1998 | ext_addr = (P_SOIL_GLCOMPRESSEDTEXIMAGE2DPROC) |
1999 | glXGetProcAddressARB |
2000 | ( |
2001 | (const GLubyte *)"glCompressedTexImage2DARB" |
2002 | ); |
2003 | #endif |
2004 | /* Flag it so no checks needed later */ |
2005 | if( NULL == ext_addr ) |
2006 | { |
2007 | /* hmm, not good!! This should not happen, but does on my |
2008 | laptop's VIA chipset. The GL_EXT_texture_compression_s3tc |
2009 | spec requires that ARB_texture_compression be present too. |
2010 | this means I can upload and have the OpenGL drive do the |
2011 | conversion, but I can't use my own routines or load DDS files |
2012 | from disk and upload them directly [8^( */ |
2013 | has_DXT_capability = SOIL_CAPABILITY_NONE; |
2014 | } else |
2015 | { |
2016 | /* all's well! */ |
2017 | soilGlCompressedTexImage2D = ext_addr; |
2018 | has_DXT_capability = SOIL_CAPABILITY_PRESENT; |
2019 | } |
2020 | } |
2021 | } |
2022 | /* let the user know if we can do DXT or not */ |
2023 | return has_DXT_capability; |
2024 | } |
2025 | |