FreeWRL / FreeX3D 4.3.0
LoadTextures.c
1/*
2
3 FreeWRL support library.
4 New implementation of texture loading.
5
6*/
7
8/****************************************************************************
9 This file is part of the FreeWRL/FreeX3D Distribution.
10
11 Copyright 2009 CRC Canada. (http://www.crc.gc.ca)
12
13 FreeWRL/FreeX3D is free software: you can redistribute it and/or modify
14 it under the terms of the GNU Lesser Public License as published by
15 the Free Software Foundation, either version 3 of the License, or
16 (at your option) any later version.
17
18 FreeWRL/FreeX3D is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU General Public License for more details.
22
23 You should have received a copy of the GNU General Public License
24 along with FreeWRL/FreeX3D. If not, see <http://www.gnu.org/licenses/>.
25****************************************************************************/
26
27/*
28Sept 6, 2016 note:
29- looks like for years we have been loading image files into BGRA order in tti (texturetableindexstruct).
30- then over in Textures.c for desktop we tell opengl our data is in GL_BGRA order
31- and for mobile we tell it its in GL_RGBA order
32- and we ask it to store internally in the same GL_RGBA format (so what's the performance gain)
33
34from 2003:
35"It was true that BGR most of the times resulted in faster performance.
36Even if this would be true right now, there's nothing to worry.
37The video card will put the texture in its own internal format so besides downloading speed to it
38(which will be unnoticeably faster/slower thosedays),
39there should be no problem in using one format or the other."
40
41Options:
421. leave as is -with different platforms loading in different order- and document better
43 I'll use // BGRA below to identify code that's swapping to BGRA
442. fix by fixing all loading to do in one order (but GL_BGRA source format isn't available on mobile,
45 so that means changing all the desktop loading code to RGBA)
463. fix with a BGRA to RGBA byte swapper, and apply to output of desktop loader code
474. add member to tti struct to say what order the loader used, so textures.c can apply right order if
48 available for the platform
49
50Decision:
51#3 - see texture_swap_B_R(tti) calls below, and changes to Textures.c to assume tti is RGBA on all platforms
52Dec 6, 2016 tti->data now always in RGBA
53
54*/
55
56#include <config.h>
57#include <system.h>
58#include <system_threads.h>
59#include <display.h>
60#include <internal.h>
61
62#include "vrml_parser/Structs.h"
63#include "main/ProdCon.h"
64#include "OpenGL_Utils.h"
65#include "Textures.h"
66#include "LoadTextures.h"
67#include "../scenegraph/Component_CubeMapTexturing.h"
68#include "../scenegraph/Polyrep.h"
69#include <list.h>
70#include <io_files.h>
71#include <io_http.h>
72
73#include <threads.h>
74
75#include <libFreeWRL.h>
76
77/* We do not want to include Struct.h: enormous file :) */
78typedef struct _Multi_String Multi_String;
79void Multi_String_print(struct Multi_String *url);
80
81#ifdef _MSC_VER
82#include "ImageLoader.h"
83#else //_MSC_VER
84#if !(defined(_ANDROID) || defined(ANDROIDNDK))
85#ifdef HAVE_IMLIB2
86 #include <Imlib2.h>
87#endif //HAVE_IMLIB2
88#endif //NOT ANDROID
89#endif //_MSC_VER
90
91
92
94//int TextureThreadInitialized = FALSE;
95
96
97
98//GLuint defaultBlankTexture;
99
100typedef struct pLoadTextures{
101 s_list_t* texture_request_list;// = NULL;
102 bool loader_waiting;// = false;
103 /* list of texture table entries to load */
104 s_list_t *texture_list;// = NULL;
105 /* are we currently active? */
106 int TextureParsing; // = FALSE;
107}* ppLoadTextures;
108void *LoadTextures_constructor(){
109 void *v = MALLOCV(sizeof(struct pLoadTextures));
110 memset(v,0,sizeof(struct pLoadTextures));
111 return v;
112}
113void LoadTextures_init(struct tLoadTextures *t)
114{
115 //public
116 /* is the texture thread up and running yet? */
117 //t->TextureThreadInitialized = FALSE;
118
119 //private
120 t->prv = LoadTextures_constructor();
121 {
122 ppLoadTextures p = (ppLoadTextures)t->prv;
123 p->texture_request_list = NULL;
124 p->loader_waiting = false;
125 /* list of texture table entries to load */
126 p->texture_list = NULL;
127 /* are we currently active? */
128 p->TextureParsing = FALSE;
129 }
130}
131//s_list_t* texture_request_list = NULL;
132//bool loader_waiting = false;
133
134enum {
135 IMAGETYPE_UNKNOWN = 0,
136 IMAGETYPE_PNG = 1,
137 IMAGETYPE_JPEG,
138 IMAGETYPE_GIF,
139 IMAGETYPE_DDS,
140 IMAGETYPE_WEB3DIT,
141 IMAGETYPE_NRRD,
142 IMAGETYPE_VOL,
143};
144static int sniffImageFileHeader(char *filename) {
145// return value:
146// 0 unknown
147// 1 png
148// 2 jpeg
149// 3 gif
150//filenames coming in can be temp file names - scrambled
151//there are 3 ways to tell in the backend what type of image file:
152//a) .xxx original filename suffix
153//b) MIME type
154//c) file signature https://en.wikipedia.org/wiki/List_of_file_signatures
155// right now we aren't passing in the .xxx or mime or signature bytes
156// except through the file conents we can get the signature
157 char header[20];
158 int iret;
159 size_t rvt;
160 UNUSED(rvt);
161
162 FILE* fp = fopen(filename,"rb");
163 rvt=fread(header,20,1,fp);
164 fclose(fp);
165
166 iret = IMAGETYPE_UNKNOWN;
167 if(!strncmp(&header[1],"PNG",3))
168 iret = IMAGETYPE_PNG;
169
170 if(!strncmp(header,"ÿØÿ",3)) //JPEG
171 iret = IMAGETYPE_JPEG;
172
173 if(!strncmp(header,"GIF",3))
174 iret = IMAGETYPE_GIF;
175
176 if(!strncmp(header,"DDS ",4)) // MS .dds cubemap and 3d textures
177 iret = IMAGETYPE_DDS;
178
179 if(!strncmp(header,"web3dit",7)) //.web3dit dug9/freewrl invention
180 iret = IMAGETYPE_WEB3DIT;
181
182 if(!strncmp(header,"NRRD",4)) //.nrrd 3D volume texture
183 iret = IMAGETYPE_NRRD;
184
185 if(!strncmp(header,"vol",3)) //.vol 3D volume
186 iret = IMAGETYPE_VOL;
187
188 return iret;
189}
190static int sniffImageHeader(char* header) {
191 // return value:
192 // 0 unknown
193 // 1 png
194 // 2 jpeg
195 // 3 gif
196 //filenames coming in can be temp file names - scrambled
197 //there are 3 ways to tell in the backend what type of image file:
198 //a) .xxx original filename suffix
199 //b) MIME type
200 //c) file signature https://en.wikipedia.org/wiki/List_of_file_signatures
201 // right now we aren't passing in the .xxx or mime or signature bytes
202 // except through the file conents we can get the signature
203
204 int iret;
205 iret = IMAGETYPE_UNKNOWN;
206 if (!strncmp(&header[1], "PNG", 3))
207 iret = IMAGETYPE_PNG;
208
209 if (!strncmp(header, "ÿØÿ", 3)) //JPEG
210 iret = IMAGETYPE_JPEG;
211
212 if (!strncmp(header, "GIF", 3))
213 iret = IMAGETYPE_GIF;
214
215 if (!strncmp(header, "DDS ", 4)) // MS .dds cubemap and 3d textures
216 iret = IMAGETYPE_DDS;
217
218 if (!strncmp(header, "web3dit", 7)) //.web3dit dug9/freewrl invention
219 iret = IMAGETYPE_WEB3DIT;
220
221 if (!strncmp(header, "NRRD", 4)) //.nrrd 3D volume texture
222 iret = IMAGETYPE_NRRD;
223
224 if (!strncmp(header, "vol", 3)) //.vol 3D volume
225 iret = IMAGETYPE_VOL;
226
227 return iret;
228}
229static int sniffImageChannels_bruteForce(unsigned char *imageblob, int width, int height){
230 //iterates over entire 4byte-per-pixel RGBA image blob, or until it knows the answer,
231 // and returns number of channels 1=Luminance, 2=Lum-alpha 3=rgb 4=rgba
232 //detects by comparing alpha != 1 to detect alpha, and r != g != b to detect color
233 int i,ii4,j,jj4, hasAlpha, hasColor, channels;
234 hasAlpha = 0;
235 hasColor = 0;
236 channels = 4;
237 for(i=0;i<height;i++){
238 ii4 = i*width*4;
239 if(!hasColor){
240 //for gray-scale images, will need to scan the whole image looking for r != g != b
241 //not tested with lossy compression ie jpeg, but jpeg is usually RGB -not gray, and no alpha-
242 // - so jpeg should exit color detection early anyway
243 for(j=0;j<width;j++){
244 jj4 = ii4 + j*4;
245 hasAlpha = hasAlpha || imageblob[jj4+3] != 255;
246 hasColor = hasColor || imageblob[jj4] != imageblob[jj4+1] || imageblob[jj4+1] != imageblob[jj4+2];
247 }
248 }else{
249 //color found, can stop looking for color. now just look for alpha
250 //- this is likely the most work, because if Alpha all 1s, it won't know until it scans whole image
251 for(j=3;j<width*4;j+=4){
252 hasAlpha = hasAlpha || imageblob[ii4 + j] != 255;
253 }
254 }
255 if(hasAlpha && hasColor)break; //got the maximum possible answer, can exit early
256 }
257 channels = hasColor ? 3 : 1;
258 channels = hasAlpha ? channels + 1 : channels;
259 return channels;
260}
261
262
263/* All functions here works with the array of 'textureTableIndexStruct'.
264 * In the future we may want to refactor this struct.
265 * In the meantime lets make it work :).
266 */
267
268#ifdef TEXVERBOSE
269static void texture_dump_entry(textureTableIndexStruct_s *entry)
270{
271 DEBUG_MSG("%s\t%p\t%s\n", texst(entry->status), entry, entry->filename);
272}
273#endif
274
275void texture_dump_list()
276{
277#ifdef TEXVERBOSE
278 DEBUG_MSG("TEXTURE: wait queue\n");
279 ppLoadTextures p = (ppLoadTextures)gglobal()->LoadTextures.prv;
280 ml_foreach(p->texture_list, texture_dump_entry(ml_elem(__l)));
281 DEBUG_MSG("TEXTURE: end wait queue\n");
282#endif
283}
284
285static size_t st(int k){
286 return (size_t)k;
287}
288static void texture_swap_B_R(textureTableIndexStruct_s* this_tex)
289{
290 //swap red and blue // BGRA - converts back and forth from BGRA to RGBA
291 //search for GL_RGBA in textures.c
292 int x,y,z,i,j,k;
293 size_t ipix, ibyte;
294 unsigned char R,B,*data;
295 x = this_tex->x;
296 y = this_tex->y;
297 z = this_tex->z;
298 data = this_tex->texdata;
299 if(data) //can be null from generatedcubemap during startup
300 for(i=0;i<z;i++){
301 for(j=0;j<y;j++){
302 for(k=0;k<x;k++)
303 {
304 //ipix = (i*y + j)*x + k;
305 //ibyte = ipix * 4L; //assumes tti->texdata is 4 bytes per pixel, in BGRA or RGBA order
306 ipix = (st(i)*st(y) + st(j))*st(x) + st(k);
307 ibyte = ipix * st(4); //assumes tti->texdata is 4 bytes per pixel, in BGRA or RGBA order
308 R = data[ibyte];
309 B = data[ibyte+st(2)];
310 data[ibyte] = B;
311 data[ibyte+st(2)] = R;
312 }
313 }
314 }
315}
316
321static void texture_load_from_pixelTexture (textureTableIndexStruct_s* this_tex, struct X3D_PixelTexture *node)
322{
323
324/* load a PixelTexture that is stored in the PixelTexture as an MFInt32 */
325 int hei,wid,depth;
326 unsigned char *texture;
327 int count;
328 int ok;
329 int *iptr;
330 int tctr;
331
332 iptr = node->image.arr.p;
333
334 ok = TRUE;
335
336 DEBUG_TEX ("start of texture_load_from_pixelTexture...\n");
337
338 /* are there enough numbers for the texture? */
339 //if (node->image.arr.n < 3) {
340 // printf ("PixelTexture, need at least 3 elements, have %d\n",node->image.n);
341 // ok = FALSE;
342 //} else
343 {
344 //http://www.web3d.org/documents/specifications/19775-1/V3.3/Part01/fieldsDef.html#SFImageAndMFImage
345 //SFImage fields contain three integers representing the width, height and number of components in the image
346 //Pixels are specified from left to right, bottom to top (ie like a texture, not an image)
347 //wid = *iptr; iptr++;
348 //hei = *iptr; iptr++;
349 //depth = *iptr; iptr++;
350 wid = node->image.whc[0];
351 hei = node->image.whc[1];
352 depth = node->image.whc[2];
353
354 DEBUG_TEX ("wid %d hei %d depth %d\n",wid,hei,depth);
355
356 if ((depth < 0) || (depth >4)) {
357 printf ("PixelTexture, depth %d out of range, assuming 1\n",(int) depth);
358 depth = 1;
359 }
360
361 if ((wid*hei) > node->image.arr.n) {
362 printf ("PixelTexture, not enough data for wid %d hei %d, have %d\n",
363 wid, hei, node->image.arr.n);
364 ok = FALSE;
365 }
366 }
367
368 /* did we have any errors? if so, create a grey pixeltexture and get out of here */
369 if (!ok) {
370 return;
371 }
372
373 /* ok, we are good to go here */
374 this_tex->x = wid;
375 this_tex->y = hei;
376 this_tex->hasAlpha = ((depth == 2) || (depth == 4));
377 this_tex->channels = depth;
378
379 texture = MALLOC (unsigned char *, wid*hei*4);
380 this_tex->texdata = texture; /* this will be freed when texture opengl-ized */
381 this_tex->status = TEX_NEEDSBINDING;
382
383 tctr = 0;
384 if(texture != NULL){
385 for (count = 0; count < (wid*hei); count++) {
386 switch (depth) {
387 case 1: {
388 texture[tctr++] = *iptr & 0xff;
389 texture[tctr++] = *iptr & 0xff;
390 texture[tctr++] = *iptr & 0xff;
391 texture[tctr++] = 0xff; /*alpha, but force it to be ff */
392 break;
393 }
394 case 2: {
395 texture[tctr++] = (*iptr>>8) & 0xff; /*G*/
396 texture[tctr++] = (*iptr>>8) & 0xff; /*G*/
397 texture[tctr++] = (*iptr>>8) & 0xff; /*G*/
398 texture[tctr++] = (*iptr>>0) & 0xff; /*A*/
399 break;
400 }
401 case 3: {
402 texture[tctr++] = (*iptr>>16) & 0xff; /*R*/
403 texture[tctr++] = (*iptr>>8) & 0xff; /*G*/
404 texture[tctr++] = (*iptr>>0) & 0xff; /*B*/
405 texture[tctr++] = 0xff; /*alpha, but force it to be ff */
406 break;
407 }
408 case 4: {
409 texture[tctr++] = (*iptr>>24) & 0xff; /*R*/
410 texture[tctr++] = (*iptr>>16) & 0xff; /*G*/
411 texture[tctr++] = (*iptr>>8) & 0xff; /*B*/
412 texture[tctr++] = (*iptr>>0) & 0xff; /*A*/
413 break;
414 }
415 }
416 iptr++;
417 }
418 }
419}
420
421static void texture_load_blank_Texture(textureTableIndexStruct_s* this_tex, struct X3D_GeneratedTexture* node)
422{
423 int hei, wid, depth;
424 unsigned char* texture;
425 int count;
426 int ok;
427 int* iptr;
428 int tctr;
429 char pix;
430
431 wid = node->size.p[0];
432 hei = node->size.p[1];
433 depth = 4;
434
435 /* did we have any errors? if so, create a grey pixeltexture and get out of here */
436 if (!wid*hei) {
437 return;
438 }
439
440 /* ok, we are good to go here */
441 this_tex->x = wid;
442 this_tex->y = hei;
443 this_tex->hasAlpha = (depth == 4);
444 this_tex->channels = depth;
445
446 texture = MALLOC(unsigned char*, wid * hei * 4);
447 memset(texture, 0, wid * hei * 4);
448 this_tex->texdata = texture; /* this will be freed when texture opengl-ized */
449 this_tex->status = TEX_NEEDSBINDING;
450
451 tctr = 0;
452 if (texture != NULL) {
453
454 for (count = 0; count < (wid * hei); count++) {
455 texture[tctr+3] = 0xff; /*alpha, but force it to be ff */
456 tctr += 4;
457 }
458 }
459}
460
461
462static void texture_load_from_pixelTexture3D (textureTableIndexStruct_s* this_tex, struct X3D_PixelTexture3D *node)
463{
464
465// load a PixelTexture that is stored in the PixelTexture as an MFInt32
466 int hei,wid,bpp,dep,nvox,nints;
467 unsigned char *texture;
468 int count;
469 int ok;
470 int *iptr;
471 int tctr;
472
473 iptr = node->image.p;
474
475 ok = TRUE;
476
477 DEBUG_TEX ("start of texture_load_from_pixelTexture...\n");
478
479 // are there enough numbers for the texture?
480 if (node->image.n < 4) {
481 printf ("PixelTexture, need at least 3 elements, have %d\n",node->image.n);
482 ok = FALSE;
483 } else {
484 //http://www.web3d.org/documents/specifications/19775-1/V3.3/Part01/components/texture3D.html#PixelTexture3D
485 //MFInt32 image field contain 4 integers representing channels(aka components), width, height,depth the image
486 //it doesn't say the row-order (y-up like texture or y-down like image)
487 //closest analogy: SFImage Field uses y-up (texture) convention
488 //We will use y-up texture convention here. That means no row flipping as you read,
489 //first row is bottom of texture
490 bpp = *iptr; iptr++;
491 wid = *iptr; iptr++;
492 hei = *iptr; iptr++;
493 dep = *iptr; iptr++;
494
495 DEBUG_TEX ("bpp %d wid %d hei %d dep %d \n",bpp,wid,hei,dep);
496
497 if ((bpp < 0) || (bpp >4)) {
498 printf ("PixelTexture, bytes per pixel %d out of range, assuming 1\n",(int) bpp);
499 bpp = 1;
500 }
501 nvox = wid*hei*dep;
502 nints = (nvox * bpp) / 4; //4 bytes per int, how many ints
503 if ((nints + 4) > node->image.n) {
504 printf ("PixelTexture3D, not enough data for bpp %d wid %d hei %d, dep %d, need %d have %d\n",
505 bpp, wid, hei, dep, nints + 4, node->image.n);
506 ok = FALSE;
507 }
508 }
509
510 // did we have any errors? if so, create a grey pixeltexture and get out of here
511 if (!ok) {
512 return;
513 }
514
515 // ok, we are good to go here
516 this_tex->x = wid;
517 this_tex->y = hei;
518 this_tex->z = dep;
519 this_tex->hasAlpha = ((bpp == 2) || (bpp == 4));
520 this_tex->channels = bpp;
521
522 texture = MALLOC (unsigned char *, wid*hei*4*dep);
523 this_tex->texdata = texture; // this will be freed when texture opengl-ized
524 this_tex->status = TEX_NEEDSBINDING;
525
526 tctr = 0;
527 if(texture != NULL){
528 for (count = 0; count < (wid*hei*dep); count++) {
529 switch (bpp) {
530 case 1: {
531 texture[tctr++] = *iptr & 0xff;
532 texture[tctr++] = *iptr & 0xff;
533 texture[tctr++] = *iptr & 0xff;
534 texture[tctr++] = 0xff; //alpha, but force it to be ff
535 break;
536 }
537 case 2: {
538 texture[tctr++] = (*iptr>>8) & 0xff; //G
539 texture[tctr++] = (*iptr>>8) & 0xff; //G
540 texture[tctr++] = (*iptr>>8) & 0xff; //G
541 texture[tctr++] = (*iptr>>0) & 0xff; //A
542 break;
543 }
544 case 3: {
545 texture[tctr++] = (*iptr>>16) & 0xff; //R
546 texture[tctr++] = (*iptr>>8) & 0xff; //G
547 texture[tctr++] = (*iptr>>0) & 0xff; //B
548 texture[tctr++] = 0xff; //alpha, but force it to be ff
549 break;
550 }
551 case 4: {
552 texture[tctr++] = (*iptr>>24) & 0xff; //R
553 texture[tctr++] = (*iptr>>16) & 0xff; //G
554 texture[tctr++] = (*iptr>>8) & 0xff; //B
555 texture[tctr++] = (*iptr>>0) & 0xff; //A
556 break;
557 }
558 }
559 iptr++;
560 }
561 }
562}
563
564int loadImage3D_x3di3d(struct textureTableIndexStruct *tti, char *fname){
565/* SUPERCEEDED by web3dit
566 reads 3D image in ascii format like you would put inline for PixelTexture3D
567 except with sniffable header x3dimage3d ie:
568 """
569 x3di3d
570 3 4 6 2 0xFF00FF ....
571 """
572 3 channgels, nx=4, ny=6, nz=2 and one int string per pixel
573 format 'invented' by dug9 for testing
574*/
575 int i,j,k,nx,ny,nz,ishex, iret, totalbytes, ipix, nchan;
576 unsigned int pixint;
577 FILE *fp;
578
579 iret = FALSE;
580
581 fp = fopen(fname,"r");
582 if (fp != NULL) {
583 char *rv;
584 int rvi;
585 char line [1000];
586
587 UNUSED(rv);
588 UNUSED(rvi);
589
590 rv=fgets(line,1000,fp);
591 if(strncmp(line,"x3di3d",6)){
592 //not our type
593 fclose(fp);
594 return iret;
595 }
596 ishex = 0;
597 if(!strncmp(line,"x3di3d x",8)) ishex = 1;
598 rvi=fscanf(fp,"%d %d %d %d",&nchan, &nx,&ny,&nz);
599 totalbytes = 4 * nx * ny * nz;
600 if(totalbytes <= 128 * 128 * 128 * 4){
601 unsigned char *rgbablob;
602 rgbablob = malloc(nx * ny * nz * 4);
603 memset(rgbablob,0,nx*ny*nz*4);
604
605 //now convert to RGBA 4 bytes per pixel
606 for(i=0;i<nz;i++){
607 for(j=0;j<ny;j++){
608 for(k=0;k<nx;k++){
609 unsigned char pixel[4],*rgba;
610 if(ishex)
611 rvi=fscanf(fp,"%x",&pixint);
612 else
613 rvi=fscanf(fp,"%d",&pixint);
614 //assume incoming red is high order, alpha is low order byte
615 pixel[0] = (pixint >> 0) & 0xff; //low byte/little endian ie alpha, or B for RGB
616 pixel[1] = (pixint >> 8) & 0xff;
617 pixel[2] = (pixint >> 16) & 0xff;
618 pixel[3] = (pixint >> 24) & 0xff;
619 //printf("[%x %x %x %x]",(int)pixel[0],(int)pixel[1],(int)pixel[2],(int)pixel[3]);
620 ipix = (i*nz +j)*ny +k;
621 rgba = &rgbablob[ipix*4];
622 //http://www.color-hex.com/ #aabbcc
623 switch(nchan){
624 case 1: rgba[0] = rgba[1] = rgba[2] = pixel[0]; rgba[3] = 255;break;
625 case 2: rgba[0] = rgba[1] = rgba[2] = pixel[1]; rgba[3] = pixel[0];break;
626 case 3: rgba[0] = pixel[2]; rgba[1] = pixel[1]; rgba[2] = pixel[2]; rgba[3] = 255; // BGRA
627 break;
628 case 4: rgba[0] = pixel[3]; rgba[1] = pixel[2]; rgba[2] = pixel[1]; rgba[3] = pixel[0]; break; // BGRA
629 default:
630 break;
631 }
632 //memcpy(rgba,&pixint,4);
633 }
634 }
635 }
636 tti->channels = nchan;
637 tti->x = nx;
638 tti->y = ny;
639 tti->z = nz;
640 tti->texdata = rgbablob;
641 iret = TRUE;
642 }
643 fclose(fp);
644 }
645 return iret;
646
647}
648void saveImage3D_x3di3d(struct textureTableIndexStruct *tti, char *fname){
649/* SUPERCEEDED by web3dit
650 reads 3D image in ascii format like you would put inline for PixelTexture3D
651 except with sniffable header x3dimage3d ie:
652 """
653 x3di3d
654 3 4 6 2 0xFF00FF ....
655 """
656 3 channgels, nx=4, ny=6, nz=2 and one int string per pixel
657 format 'invented' by dug9 for testing
658*/
659 int i,j,k,nx,ny,nz, ipix, nchan;
660 unsigned int pixint;
661 unsigned char *rgbablob;
662 FILE *fp;
663
664 fp = fopen(fname,"w+");
665 nchan = tti->channels;
666 nx = tti->x;
667 ny = tti->y;
668 nz = tti->z;
669 rgbablob = tti->texdata;
670
671 fprintf(fp,"x3di3d x\n"); //x for hex, i for int rgba, la order ie red is high
672 fprintf(fp,"%d %d %d %d",nchan, nx,ny,nz);
673
674 for(i=0;i<nz;i++){
675 for(j=0;j<ny;j++){
676 for(k=0;k<nx;k++){
677 unsigned char *rgba;
678 ipix = (i*nz +j)*ny +k;
679 rgba = &rgbablob[ipix*4];
680 pixint = 0;
681 switch(nchan){
682 case 1: pixint = rgba[0];break;
683 case 2: pixint = (rgba[0] << 8) + rgba[3];break;
684 case 3: pixint = (rgba[0] << 16) + (rgba[1] << 8) + (rgba[2] << 0);break;
685 case 4: pixint = (rgba[0] << 24) + (rgba[1] << 16) + (rgba[2] << 8) + rgba[3];break; // BGRA
686 default:
687 pixint = 0;
688 }
689 switch(nchan){
690 case 1: fprintf(fp," %#.2x",pixint);break;
691 case 2: fprintf(fp," %#.4x",pixint);break;
692 case 3: fprintf(fp," %#.6x",pixint);break;
693 case 4: fprintf(fp," %#.8x",pixint);break;
694 default:
695 fprintf(fp," 0x00");break;
696 }
697 }
698 }
699 }
700 fclose(fp);
701
702}
703
704int loadImage_web3dit(struct textureTableIndexStruct *tti, char *fname){
705/* TESTED ONLY RGB Geometry 3 and C AS OF SEPT 9, 2016
706 reads image in ascii format almost like you would put inline for PixelTexture
707 Goal: easy to create image file format just sufficient for web3d types:
708 2 2D texture
709 3 3D texture
710 C cubemap
711 volume (float luminance)
712 panorama
713 with sniffable header web3dit:
714 """
715web3ditG #H 7 byte magic header, means web3d compatible image in text form, 1byte for Geometry sniffing
7161 #F file version
717C #G {C,P,3,2}: image geometry: C: cubemap RHS y-up z/depth/layer/order [+-x,+-y,+-z], top of top +z, bottom of bottom -z P: 360 panorama [L->R, 360/z ], 3: texture3D or Volume [z=depth], 2: texture2D
718 #O optional description
719x #T {x,i,f} how to read space-delimited value: x as hex, i as int, f as float
7200 255 #R range of channel, most useful for normalizing floats
7214 #N channels/components per value ie RGBA as int: 4, RGBA as 4 ints: 1
7221 #M values per pixel ie RGBA as int: 1, RGBA as 4 ints: 4
723RGBA #C[N*M] component names and order, choose from: {R,G,B,A,L} ie RGBA, LA, L, RGB
7243 #D number of dimensions, 2 for normal 2D image, 3 for 3D image
7253 3 3 #P[D] size in pixels in each dimension: x,y,z (use 1 for z if 2D)
726D #Y {U,D} image y-Down or texture y-Up row order
727#I image values follow with x in inner loop, Y image direction, z in outer:
7280xFF00FF ....
729 """
730 format 'invented' by dug9 for testing freewrl, License: MIT
731*/
732 int i,j,k,m,nx,ny,nz,nv,nc, iret, totalbytes, ipix, jpix, kpix, nchan;
733 int version, Rmin, Rmax, Nchannelspervalue, Mvaluesperpixel, Dimensions;
734 unsigned int pixint, Pixels[10], iydown;
735 float pixfloat;
736 char Geometry, ODescription[200], Type, Componentnames[10], YDirection;
737 FILE *fp;
738
739 iret = FALSE;
740
741 fp = fopen(fname,"r");
742 if (fp != NULL) {
743 char *rv;
744 UNUSED(rv);
745
746 char line [1000];
747 rv = fgets(line,1000,fp);
748 if(strncmp(line,"web3dit",7)){
749 //not our type
750 fclose(fp);
751 return iret;
752 }
753 //could sniff Geometry here, if caller says what geometry type is OK for them, return if not OK
754 rv=fgets(line,1000,fp);
755 sscanf(line,"%c",&Geometry);
756 rv=fgets(line,1000,fp);
757 sscanf(line,"%d",&version);
758 rv=fgets(line,1000,fp);
759 sscanf(line,"%s",ODescription);
760 rv=fgets(line,1000,fp);
761 sscanf(line,"%c",&Type);
762 rv=fgets(line,1000,fp);
763 sscanf(line,"%d %d",&Rmin,&Rmax);
764
765 rv=fgets(line,1000,fp);
766 sscanf(line,"%d",&Nchannelspervalue);
767 rv=fgets(line,1000,fp);
768 sscanf(line,"%d",&Mvaluesperpixel);
769 rv=fgets(line,1000,fp);
770 sscanf(line,"%s",Componentnames);
771 rv=fgets(line,1000,fp);
772 sscanf(line,"%d",&Dimensions);
773 rv=fgets(line,1000,fp);
774 sscanf(line,"%d %d %d",&Pixels[0], &Pixels[1], &Pixels[2]);
775 rv=fgets(line,1000,fp);
776 sscanf(line,"%c",&YDirection);
777 rv=fgets(line,1000,fp); //waste #I Image warning line
778
779
780 nx = ny = nz = 1;
781 nx = Pixels[0];
782 ny = Pixels[1];
783 if(Dimensions > 2) nz = Pixels[2];
784 nv = Mvaluesperpixel;
785 nc = Nchannelspervalue;
786 nchan = nv * nc;
787 iydown = 1;
788 if(YDirection == 'U') iydown = 0;
789
790 totalbytes = 4 * nx * ny * nz; //output 4 channel RGBA image size
791 if(totalbytes <= 256 * 256 * 256 * 4){
792 unsigned char *rgbablob;
793 rgbablob = malloc(totalbytes);
794 memset(rgbablob,0,totalbytes);
795
796 //now convert to RGBA 4 bytes per pixel
797 for(i=0;i<nz;i++){
798 for(j=0;j<ny;j++){
799 for(k=0;k<nx;k++){
800 unsigned char pixel[4],*rgba, n;
801 pixel[0] = pixel[1] = pixel[2] = pixel[3] = 0;
802 for(m=0;m<nv;m++){
803 int rvi;
804 UNUSED(rvi);
805
806 switch(Type){
807 case 'f':
808 rvi=fscanf(fp,"%f",&pixfloat);
809 break;
810 case 'x':
811 rvi=fscanf(fp,"%x",&pixint);
812 break;
813 case 'i':
814 default:
815 rvi=fscanf(fp,"%d",&pixint);
816 break;
817 }
818 for(n=0;n<nc;n++){
819 switch(Type){
820 case 'f':
821 pixel[n] = (unsigned char)(unsigned int)((pixfloat - Rmin) / (Rmax - Rmin) * 255.0);
822 break;
823 case 'x':
824 case 'i':
825 default:
826 pixel[n] = (pixint >> n*8) & 0xff;
827 break;
828 }
829 }
830
831 }
832 //for RGBA, pixel[0] is A, pixel[3] is B
833 //printf("[%x %x %x %x]\n",(int)pixel[0],(int)pixel[1],(int)pixel[2],(int)pixel[3]);
834
835 ipix = (i*ny +j)*nx +k; //if file is like outgoing y-up texture order: first row is bottom of texture
836 jpix = (i*ny +(ny-1-j))*nx + k; //if file is in y-down image order: first row is top of image
837 kpix = iydown ? jpix : ipix;
838 rgba = &rgbablob[kpix*4];
839 //http://www.color-hex.com/ #aabbcc
840 switch(nchan){
841 case 1: rgba[0] = rgba[1] = rgba[2] = pixel[0]; rgba[3] = 255;break;
842 case 2: rgba[0] = rgba[1] = rgba[2] = pixel[1]; rgba[3] = pixel[0];break;
843 case 3: rgba[0] = pixel[2]; rgba[1] = pixel[1]; rgba[2] = pixel[0]; rgba[3] = 255; // BGRA
844 break;
845 case 4: rgba[0] = pixel[3]; rgba[1] = pixel[2]; rgba[2] = pixel[1]; rgba[3] = pixel[0]; break; // BGRA
846 default:
847 break;
848 }
849 //memcpy(rgba,&pixint,4);
850
851 }
852 }
853 }
854 tti->channels = nchan;
855 tti->x = nx;
856 tti->y = ny;
857 tti->z = nz;
858 tti->texdata = rgbablob;
859 if(0){
860 printf("\n");
861 for(i=0;i<tti->z;i++){
862 for(j=0;j<tti->y;j++){
863 for(k=0;k<tti->x;k++){
864 int ipix,kpix;
865 // int jpix;
866 unsigned int pixint;
867 ipix = (i*tti->y + j)*tti->x + k;
868 //jpix = (i*tti->y + (tti->y -1 - j))*tti->x + k;
869 kpix = ipix; //print it like we see it
870 memcpy(&pixint,&tti->texdata[kpix*4],4);
871 printf("%x ",pixint);
872 }
873 printf("\n");
874 }
875
876 }
877 }
878 iret = TRUE;
879 }
880 fclose(fp);
881 }
882 return iret;
883
884}
885void saveImage_web3dit(struct textureTableIndexStruct *tti, char *fname){
886/* TESTED ONLY RGB Geometry 3 and C AS OF SEPT 9, 2016
887 writes image in ascii format almost like you would put inline for PixelTexture
888 please put .web3dit suffix on fname (i stands for image, t stands for text format (future x xml, j json formats?)
889 Goal: easy to create image file format just sufficient for web3d types:
890 2D texture
891 3D texture
892 cubemap
893 volume (float luminance)
894 and write out the explanation of the header with the header so don't need this document
895 with sniffable header web3dit:
896 """
897web3ditG #H 7 byte magic header, means web3d compatible image in text form, 1byte for Geometry sniffing
898C #G {C,P,3,2} image geometry: C: cubemap RHS y-up z/depth/layer/order [+-x,+-y,+-z], top of top +z, bottom of bottom -z P: 360 panorama [L->R, 360/z ], 3: texture3D or Volume [z=depth], 2: texture2D
8991 #F file version
900 #O optional description
901x #T {x,i,f} how to read space-delimited value: x as hex, i as int, f as float
9020 255 #R range of channel, most useful for normalizing floats
9034 #N channels/components per value ie RGBA as int: 4, RGBA as 4 ints: 1
9041 #M values per pixel ie RGBA as int: 1, RGBA as 4 ints: 4
905RGBA #C[N*M] component names and order, choose from: {R,G,B,A,L} ie RGBA, LA, L, RGB
9063 #D number of dimensions, 2 for normal 2D image, 3 for 3D image
9073 3 3 #P[D] size in pixels in each dimension: x,y,z(depth/layer) (use 1 for z if 2D)
908D #Y {U,D} image y-Down or texture y-Up row order
909#I image values follow with x in inner loop, Y image direction, z in outer:
9100xFF00FF ....
911 """
912 format 'invented' by dug9 for testing freewrl, License: MIT
913*/
914 int i,j,k,nx,ny,nz, ipix, jpix, kpix, iydown, nchan;
915 int version, Rmin, Rmax, Nchannelspervalue, Mvaluesperpixel, Dimensions;
916 unsigned int pixint;
917 char Geometry, *ODescription, Type, *Componentnames, YDirection;
918 //char Pixels[10];
919 static char *LRGBA [] = {"L","LA","RGB","RGBA"};
920 FILE *fp;
921
922 fp = fopen(fname,"w+");
923 if (fp != NULL) {
924 unsigned char *rgbablob;
925 nchan = tti->channels;
926 nx = tti->x;
927 ny = tti->y;
928 nz = tti->z;
929 rgbablob = tti->texdata;
930 Dimensions = nz == 1 ? 2 : 3;
931 //Pixels[0] = nx;
932 //Pixels[1] = ny;
933 //Pixels[2] = nz;
934 Nchannelspervalue = nchan;
935 Mvaluesperpixel = 1;
936 Rmin = 0;
937 Rmax = 255;
938 Type = 'x';
939 ODescription = "";
940 Geometry = '2';
941 if(nz > 1) Geometry = '3';
942 Componentnames = LRGBA[nchan -1]; //"RGBA";
943 version = 1;
944 YDirection = 'D';
945 iydown = (YDirection == 'D') ? 1 : 0;
946
947 fprintf(fp,"web3dit%c #H 7 byte magic header, means web3d compatible image in text form, 1byte for Geometry sniffing\n",Geometry);
948 fprintf(fp,"%c #G {C,P,3,2}: image geometry: C: cubemap RHS y-up z/depth/layer/order [+-x,+-y,+-z], top of top +z, bottom of bottom -z P: 360 panorama [L->R, 360/z ], 3: texture3D or Volume [z=depth], 2: texture2D\n",Geometry);
949 fprintf(fp,"%d #F {1} file version\n",version);
950 fprintf(fp,"%s #O optional description\n",ODescription);
951 fprintf(fp,"%c #T {x,i,f} how to read space-delimited value: x as hex, i as int, f as float\n",Type);
952 fprintf(fp,"%d %d #R range of channel, most useful for normalizing floats\n",Rmin,Rmax);
953 fprintf(fp,"%d #N channels/components per value ie RGBA as int: 4, RGBA as 4 ints: 1\n",Nchannelspervalue);
954 fprintf(fp,"%d #M values per pixel ie RGBA as int: 1, RGBA as 4 ints: 4\n",Mvaluesperpixel);
955 fprintf(fp,"%s #C[N*M] component names and order, choose from: {R,G,B,A,L} ie RGBA, LA, L, RGB\n",Componentnames);
956 fprintf(fp,"%d #D number of dimensions, 2 for normal 2D image, 3 for 3D image\n",Dimensions);
957 fprintf(fp,"%d %d %d #P[D] size in pixels in each dimension: x,y,z (use 1 for z if 2D)\n",nx,ny,nz);
958 fprintf(fp,"%c #Y {U,D} image y-Down or texture y-Up row order\n",YDirection);
959 fprintf(fp,"#I image values follow with x in inner loop, y-down image direction, z in outer:\n");
960
961 //now convert to RGBA 4 bytes per pixel
962 for(i=0;i<nz;i++){
963 for(j=0;j<ny;j++){
964 for(k=0;k<nx;k++){
965 unsigned char *rgba;
966 ipix = (i*ny +j)*nx +k; //incoming assumed in y-up texture order
967 jpix = (i*ny +(ny-1-j))*nx + k; //outgoing in y-down image order
968 kpix = iydown ? jpix : ipix;
969 rgba = &rgbablob[kpix*4];
970 pixint = 0;
971 switch(nchan){
972 case 1: pixint = rgba[0];break;
973 case 2: pixint = (rgba[0] << 8) + rgba[3];break;
974 case 3: pixint = (rgba[0] << 16) + (rgba[1] << 8) + (rgba[2] << 0);break;
975 case 4: pixint = (rgba[0] << 24) + (rgba[1] << 16) + (rgba[2] << 8) + rgba[3];break; // RGBA
976 default:
977 pixint = 0;
978 }
979 switch(nchan){
980 case 1: fprintf(fp," %#.2x",pixint);break;
981 case 2: fprintf(fp," %#.4x",pixint);break;
982 case 3: fprintf(fp," %#.6x",pixint);break;
983 case 4: fprintf(fp," %#.8x",pixint);break;
984 default:
985 fprintf(fp," 0x00");break;
986 }
987 }
988 }
989 }
990 fclose(fp);
991 }
992}
993
994int loadImage3DVol(struct textureTableIndexStruct *tti, char *fname){
995/* UNTESTED, UNUSED AS OF SEPT 6, 2016
996 a simple 3D volume texture format
997 - primarily for int gray/luminance, useful for VolumeRendering
998 - does have a 4 channel
999 x but only 2 bytes for 4 channels - 4 bits per channel
1000 x doesn't have an official sniff header
1001 - more appropriate for communicating between your own 2 programs,
1002 where you know the meaning of the numbers
1003 x not generally appropriate for international standards support like web3d
1004
1005http://paulbourke.net/dataformats/volumetric/
1006The data type is indicated as follows
10071 - single bit per cell, two categories
10082 - two byes per cell, 4 discrete levels or categories
10094 - nibble per cell, 16 discrete levels
10108 - one byte per cell (unsigned), 256 levels
101116 - two bytes representing a signed "short" integer
101232 - four bytes representing a signed integer
1013The endian is one of
10140 for big endian (most significant byte first). For example Motorola processors, Sun, SGI, some HP.
10151 for little endian (least significant byte first). For example Intel processors, Dec Alphas.
1016*/
1017 int i,j,k,nx,ny,nz, bitsperpixel, bpp, iendian, iret, totalbytes, ipix, nchan;
1018 // unused int jpix;
1019 float sx,sy,sz,tx,ty,tz;
1020 FILE *fp;
1021
1022 iret = FALSE;
1023
1024 fp = fopen(fname,"r+b");
1025 if (fp != NULL) {
1026 char *rv;
1027 UNUSED(rv);
1028
1029 char line [1000];
1030 rv=fgets(line,1000,fp);
1031 if(strncmp(line,"vol",3)){
1032 //for now we'll enforce 'vol' as first the chars of file for sniffing, but not enforcable
1033 fclose(fp);
1034 return iret;
1035 }
1036 rv=fgets(line,1000,fp);
1037 sscanf(line,"%d %d %d",&nx,&ny,&nz);
1038 rv=fgets(line,1000,fp);
1039 sscanf(line,"%f %f %f",&sx,&sy,&sz);
1040 rv=fgets(line,1000,fp);
1041 sscanf(line,"%f %f %f",&tx,&ty,&tz);
1042 rv=fgets(line,1000,fp);
1043 sscanf(line,"%d %d",&bitsperpixel,&iendian);
1044 bpp = bitsperpixel / 8;
1045 nchan = 1;
1046 switch(bitsperpixel){
1047 case 1: nchan = 1; break;
1048 //1 - single bit per cell, two categories
1049 case 2: nchan = 4; break;
1050 //2 - two byes per cell, 4 discrete levels or categories
1051 case 4: nchan = 1; break;
1052 //4 - nibble per cell, 16 discrete levels
1053 case 8: nchan = 1; break;
1054 //8 - one byte per cell (unsigned), 256 levels
1055 case 16: nchan = 1; break;
1056 //16 - two bytes representing a signed "short" integer
1057 case 32: nchan = 1; break;
1058 //32 - four bytes representing a signed integer
1059 default:
1060 break;
1061 }
1062
1063 totalbytes = bpp * nx * ny * nz;
1064 if(totalbytes < 128 * 128 * 128 *4){
1065 unsigned char* blob, *rgbablob;
1066 size_t rvt;
1067 UNUSED(rvt);
1068
1069 blob = malloc(totalbytes + 4);
1070 rgbablob = malloc(nx * ny * nz * 4);
1071 memset(rgbablob,0,nx*ny*nz*4);
1072
1073 rvt=fread(blob,totalbytes,1,fp);
1074 //now convert to RGBA 4 bytes per pixel
1075 for(i=0;i<nz;i++){
1076 for(j=0;j<ny;j++){
1077 for(k=0;k<nx;k++){
1078 unsigned char *pixel,*rgba;
1079 ipix = (i*ny +j)*nx +k; //incoming image
1080 //jpix = (i*ny +(ny-1-j))*nx + k;
1081 pixel = &blob[ipix*bpp];
1082 rgba = &rgbablob[ipix*4];
1083 rgba[3] = 255;
1084 switch(bitsperpixel){
1085 case 1: break;
1086 //1 - single bit per cell, two categories
1087 //rgba[0] = rgba[1] = rgba[2] =
1088 case 2:
1089 //2 - two byes per cell, 4 discrete levels or categories
1090 rgba[0] = pixel[0] >> 4;
1091 rgba[1] = pixel[0] & 0xF;
1092 rgba[2] = pixel[1] >> 4;
1093 rgba[3] = pixel[1] & 0xF;
1094 break;
1095 case 4:
1096 //4 - nibble per cell, 16 discrete levels
1097 break;
1098 case 8:
1099 //8 - one byte per cell (unsigned), 256 levels
1100 rgba[0] = rgba[1] = rgba[2] = (unsigned char)pixel[0];
1101 break;
1102 case 16:
1103 //16 - two bytes representing a signed "short" integer
1104 rgba[0] = rgba[1] = rgba[2] = (unsigned char) *(unsigned short*)pixel;
1105 break;
1106 case 32:
1107 //32 - four bytes representing a signed integer
1108 rgba[0] = pixel[0]; //too much range, we will split high part into rgb
1109 rgba[1] = pixel[1];
1110 rgba[2] = pixel[2];
1111 rgba[3] = pixel[3]; //and we'll even take a byte as alpha, for fun
1112 break;
1113 default:
1114 break;
1115 }
1116 }
1117 }
1118 }
1119 if(blob) free(blob);
1120 tti->channels = nchan;
1121 tti->x = nx;
1122 tti->y = ny;
1123 tti->z = nz;
1124 tti->texdata = rgbablob;
1125 iret = TRUE;
1126 }
1127 fclose(fp);
1128 }
1129 return iret;
1130
1131}
1132
1133// NRRD MIT ===========================>>>>>>>>>>>>>>
1134// a mini-nrrd reader, with MIT licence
1135// Oct 2, 2016: only luminance / scalar-value-per-voxel implemented, only unsigned char type tested
1136//#include <endian.h> //windows doesn't have
1137//#define IS_LITTLE_ENDIAN (1 == *(unsigned char *)&(const int)1) //found on internet
1138int isMachineLittleEndian(){
1139 //dug9: this will/should detect at runtime between big and little endian host machines
1140 // which reverse byte order- but not mixed endian ie pdp endian
1141 unsigned short int one = 1;
1142 unsigned char *c;
1143 int iret; //, itest;
1144 c = (unsigned char *)&one;
1145 iret = (c[0] == 1) ? TRUE : FALSE;
1146 //itest = IS_LITTLE_ENDIAN ? TRUE : FALSE;
1147 //if(iret != itest) printf("endian confusion\n");
1148 return iret;
1149}
1150enum {
1151NRRDFIELD_type = 1,
1152NRRDFIELD_dimension,
1153NRRDFIELD_sizes,
1154NRRDFIELD_spacing,
1155NRRDFIELD_encoding,
1156NRRDFIELD_endian,
1157NRRDFIELD_nchannel,
1158NRRDFIELD_interpretation,
1159};
1160struct {
1161const char *fieldname;
1162int len;
1163const int fieldtype;
1164} nrrdfields [] = {
1165{"type:",5,NRRDFIELD_type},
1166{"dimension:",10,NRRDFIELD_dimension},
1167{"sizes:",6,NRRDFIELD_sizes},
1168{"spacings:",9,NRRDFIELD_spacing},
1169{"encoding:",9,NRRDFIELD_encoding},
1170{"endian:",7,NRRDFIELD_endian},
1171{"nchannel:=",10,NRRDFIELD_nchannel},
1172{"interpretation:=",16,NRRDFIELD_interpretation},
1173{NULL,0,0},
1174};
1175enum {
1176CDATATYPE_char = 1,
1177CDATATYPE_uchar,
1178CDATATYPE_short,
1179CDATATYPE_ushort,
1180CDATATYPE_int,
1181CDATATYPE_uint,
1182CDATATYPE_longlong,
1183CDATATYPE_ulonglong,
1184CDATATYPE_float,
1185CDATATYPE_double,
1186};
1187struct {
1188const char * stypes[7];
1189const int itype;
1190const int bsize;
1191const char * fmt;
1192} nrrddatatypes [] = {
1193{{"signed char", "int8_t", "int8", NULL,NULL,NULL,NULL}, CDATATYPE_char, 1, "%hh"},
1194{{"uchar", "unsigned char", "uint8", "uint8_t", NULL,NULL,NULL}, CDATATYPE_uchar, 1, "%hhu" },
1195{{"short", "short int", "signed short", "signed short int", "int16", "int16_t", NULL}, CDATATYPE_short, 2, "%hd" },
1196{{"ushort", "unsigned short", "unsigned short int", "uint16", "uint16_t", NULL, NULL}, CDATATYPE_ushort, 2, "%hu" },
1197{{"int", "signed int", "int32", "int32_t", NULL, NULL, NULL}, CDATATYPE_int, 4, "%d"},
1198{{"uint", "unsigned int", "uint32", "uint32_t", NULL, NULL, NULL}, CDATATYPE_uint, 4, "%u" },
1199{{"longlong", "long long", "long long int", "signed long long", "signed long long int", "int64", "int64_t"}, CDATATYPE_longlong, 8, "%lld"},
1200{{"ulonglong", "unsigned long long", "unsigned long long int", "uint64", "uint64_t", NULL, NULL}, CDATATYPE_ulonglong, 8, "%llu" },
1201{{"float", NULL,NULL,NULL,NULL, NULL,NULL},CDATATYPE_float,4, "%f" },
1202{{"double", NULL,NULL,NULL,NULL, NULL,NULL},CDATATYPE_double,8, "%lf"},
1203{{NULL,NULL,NULL,NULL, NULL,NULL,NULL},0},
1204};
1205enum {
1206NRRDENDIAN_LITTLE = 1,
1207NRRDENDIAN_BIG,
1208};
1209enum {
1210NRRDENCODING_RAW = 1,
1211NRRDENCODING_ASCII,
1212};
1213int loadImage_nrrd(struct textureTableIndexStruct *tti, char *fname){
1214/*
1215 license on this function: MIT or equivalent
1216 volume / 3D images, for VolumeRendering and Texturing3D components
1217 http://teem.sourceforge.net/nrrd/format.html
1218 subset implemented here: assumes luminance-only (scalar per voxel)
1219 (see the kinds[] field for more general interpretation of the rows)
1220 we will skip non-basic / optional fields and key/values since we aren't also writing back out in a full cycle
1221 The headers are ascii, and you can type the file to the console to see the header
1222C:>type brain.nrrd
1223NRRD0001
1224content: "MRI Brain for 3DVisualizer"
1225type: unsigned char
1226dimension: 3
1227sizes: 512 512 230
1228spacings: 1 1 0.4
1229encoding: raw
1230
1231C:>type supine.nrrd
1232NRRD0001
1233content: "Torso Supine"
1234type: unsigned short
1235dimension: 3
1236sizes: 512 512 426
1237spacings: 1 1 1
1238endian: little
1239encoding: raw
1240
1241a few sample images have degenerate first dimension
1242NRRD0001
1243type: unsigned char
1244dimension: 4
1245sizes: 1 256 256 124
1246spacings: NaN 0.01 0.01 0.01
1247encoding: raw
1248
1249
1250*/
1251 int iret;
1252 FILE *fp;
1253 iret = FALSE;
1254
1255 fp = fopen(fname,"r+b"); //need +b for binary mode, to read over nulls
1256 if (fp != NULL) {
1257 unsigned long long i,j,k;
1258 int ifieldtype, idatatype;
1259 // int kdatatype;
1260 int idim, ilen, isize[4], iendian, iencoding, ifound,slen,klen, bsize;
1261 char line [2048];
1262 char cendian[256], cencoding[256];
1263 char *remainder;
1264 const char *fmt;
1265 unsigned long long nvoxel;
1266 unsigned long long totalbytes;
1267 unsigned char *data;
1268 unsigned char *voxel;
1269 double dhi, dlo;
1270 double d255range;
1271 int counts[256]; //histogram
1272 char *rv;
1273 UNUSED(rv);
1274 int nchannel = 1; //1 voxel is a scalar 3 voxel is a vector // 2 scalar + alpha 4 vector + alpha
1275
1276 dhi=0.0; dlo=0.0;
1277
1278 rv=fgets(line,2047,fp);
1279 if(strncmp(line,"NRRD",4)){
1280 //not our type
1281 fclose(fp);
1282 return iret;
1283 }
1284
1285 fmt = "";
1286 iendian = 0;// NRRDENDIAN_LITTLE;
1287 idim = 0; //3;
1288 idatatype = 0; // CDATATYPE_int;
1289 isize[0] = isize[1] = isize[2] = isize[3] = 0;
1290 iencoding = 0; //NRRDENCODING_RAW;
1291 bsize = 1; //binary size of voxel, in bytes, for mallocing
1292 // kdatatype = 0; //index into nrrddatatypes array
1293 //read header field, one per loop:
1294 for(;;){
1295 rv=fgets(line,2047,fp);
1296 i = 0;
1297 ifieldtype = 0; //unknown
1298 ilen = 0; //length of field string
1299 if(strlen(line) < 3){
1300 // '...the data (following the blank line after the header) ..'
1301 break; //nrrd signals end of header with blank line ie \n' or \r\n
1302 }
1303
1304 //see if we have a matching field from the sub-list that we care about
1305 for(;;){
1306 if(!nrrdfields[i].fieldname)break;
1307 if(!strncmp(line,nrrdfields[i].fieldname,nrrdfields[i].len)){
1308 ifieldtype = nrrdfields[i].fieldtype;
1309 ilen = nrrdfields[i].len;
1310 break;
1311 }
1312 i++;
1313 }
1314 remainder = &line[ilen];
1315 switch(ifieldtype){
1316 case NRRDFIELD_type:
1317 //find first non-blank byte where it starts ie 'unsigned short int'
1318 for(i=0;i<10;i++){
1319 if(remainder[0] == ' ') remainder = &remainder[1];
1320 else break;
1321 }
1322 slen = strlen(remainder);
1323 //find last non-blank, non CRLF
1324 klen = slen;
1325 for(i=0;i<slen;i++){
1326 char c = remainder[slen-1 -i];
1327 if(c == '\n' || c == '\r' || c == ' ') klen--;
1328 else break;
1329 }
1330 //compare known types to the full remainder string ie "unsigned short int"
1331 k = 0;
1332 ifound = FALSE;
1333 for(;;){
1334 if(nrrddatatypes[k].itype == 0) break;
1335 for(j=0;j<7;j++){
1336 if(nrrddatatypes[k].stypes[j]){ //some are null
1337 if(!strncmp(remainder,nrrddatatypes[k].stypes[j],klen)){
1338 ifound = TRUE;
1339 idatatype = nrrddatatypes[k].itype;
1340 //kdatatype = (int)k;
1341 bsize = nrrddatatypes[k].bsize;
1342 fmt = nrrddatatypes[k].fmt;
1343 break; //break out of 0,7 loop
1344 }
1345 }
1346 }
1347 if(ifound) break;
1348 k++;
1349 }
1350 break;
1351 case NRRDFIELD_dimension:
1352 sscanf(remainder,"%d",&idim);
1353 idim = min(4,idim); //we can't use more yet ie a time-varying 3D image or separate R,G,B or X,Y,Z per voxel - just scalar per voxel
1354 break;
1355 case NRRDFIELD_sizes:
1356 switch(idim){
1357 case 1:
1358 sscanf(remainder,"%d",&isize[0]);break;
1359 case 2:
1360 sscanf(remainder,"%d%d",&isize[0],&isize[1]);break;
1361 case 3:
1362 sscanf(remainder,"%d%d%d",&isize[0],&isize[1],&isize[2]);break;
1363 case 4:
1364 sscanf(remainder,"%d%d%d%d",&isize[0],&isize[1],&isize[2],&isize[3]);break;
1365 default:
1366 break;
1367 }
1368 break;
1369 case NRRDFIELD_encoding:
1370 sscanf(remainder,"%s",cencoding);
1371 if(!strcmp(cencoding,"raw"))
1372 iencoding = NRRDENCODING_RAW;
1373 else if(!strcmp(cencoding,"ascii"))
1374 iencoding = NRRDENCODING_ASCII;
1375 break;
1376
1377 break;
1378 case NRRDFIELD_endian:
1379 sscanf(remainder,"%s",cendian);
1380 if(!strcmp(cendian,"little"))
1381 iendian = NRRDENDIAN_LITTLE;
1382 else if(!strcmp(cendian,"big"))
1383 iendian = NRRDENDIAN_BIG;
1384 break;
1385 //we may need kinds[] which say how to interpret the scalars, otherwise limited to scalar-per-voxel
1386 case NRRDFIELD_nchannel:
1387 sscanf(remainder, "%d", &nchannel);
1388 break;
1389 case NRRDFIELD_interpretation:
1390 printf("nrrd interpretation:=%s",remainder);
1391 break;
1392
1393 //range field? would be helpful when compressing voxel significant bits into displayable unsigned char range
1394 default:
1395 //skip fields and key/value stuff we dont need or care about for our display app
1396 break;
1397 }
1398 }
1399 if(1){
1400 printf("iendian %d idatatype %d iencoding %d idim %d isizes %d %d %d %d bsize %d\n",
1401 iendian,idatatype,iencoding,idim,isize[0],isize[1],isize[2],isize[3], bsize);
1402 printf("machine endian isLittle=%d\n",isMachineLittleEndian());
1403 printf("hows that?\n");
1404
1405 }
1406 //clean up dimensions
1407 if(isize[0] == 1){
1408 //remove degenerate dimension, found in some images
1409 for(i=0;i<idim-1;i++){
1410 isize[i] = isize[i+1];
1411 //spacing[i] = spacing[i+1];
1412 }
1413 idim--;
1414 }
1415 if(idim <3) isize[2] = 1;
1416 if(idim <2) isize[1] = 1;
1417 if(idim >3) {
1418 idim = 3; //as of oct 3, 2016 we just do scalar / iso-value 3D images, not color, not time-series, not xyz
1419 }
1420
1421 //malloc data buffer
1422 nvoxel = isize[0] * isize[1] * isize[2];
1423 totalbytes = nvoxel * bsize;
1424 data = MALLOC(unsigned char *,(size_t)totalbytes);
1425 memset(data,0,(size_t)totalbytes);
1426 voxel = MALLOC(unsigned char *, bsize);
1427 //read data
1428 if(iencoding == NRRDENCODING_RAW){
1429 int dataLittleEndian;
1430 size_t nelem_read, element_size = 0L;
1431 element_size = bsize;
1432 nelem_read = fread(data,element_size, (size_t)nvoxel,fp);
1433 printf("num elems read = %llu elemsize %ld bytes requeted = %llu %llu\n",(unsigned long long)nelem_read,(long)bsize,bsize*nvoxel,totalbytes);
1434 //endian conversion
1435 dataLittleEndian = iendian == NRRDENDIAN_LITTLE ? TRUE : FALSE;
1436 if(isMachineLittleEndian() != dataLittleEndian && bsize > 1){
1437 //data endian doesnt match machine endian - swap unconditionally
1438 printf("swapping endian\n");
1439 for(i=0;i<nvoxel;i++){
1440 unsigned char * voxel = &data[i*bsize];
1441 for(j=0;j<bsize/2;j++){
1442 char c;
1443 k = bsize -1 - j;
1444 c = voxel[j];
1445 voxel[j] = voxel[k];
1446 voxel[k] = c;
1447 }
1448 }
1449 }
1450 }else if(iencoding == NRRDENCODING_ASCII){
1451 int kvox = 0;
1452 //read all slices
1453 for(i=0;i<isize[2];i++){
1454 //read a slice
1455 for(j=0;j<isize[1];j++){
1456 //read a row
1457 for(k=0;k<isize[0];k++){
1458 int rvi;
1459 UNUSED(rvi);
1460
1461 //read a voxel - unfamiliar theory/method, dont trust
1462 rvi=fscanf(fp,fmt,voxel);
1463 //put voxel in data
1464 memcpy(&data[kvox*bsize],voxel,bsize);
1465 }
1466 }
1467 }
1468 }
1469 //we have binary data in voxel datatype described in file
1470 if (nchannel == 1) {
1471 //currently (Oct 2, 2016) this function assumes scalar-per-voxel aka luminance or alpha
1472
1473 //find range of data so we can compress range into unsigned char range 0-255 from much bigger ints and floats
1474 //initialize range - use maxint, minint or just init to first pixel which we do here
1475 voxel = &data[0];
1476 switch (idatatype) {
1477 case CDATATYPE_char:
1478 dlo = -127.0;
1479 dhi = 127.0; //or is it 128?
1480 break;
1481 case CDATATYPE_uchar:
1482 dlo = 0.0;
1483 dhi = 255.0;
1484 break;
1485 case CDATATYPE_short:
1486 dlo = dhi = (double)*(short*)(voxel);
1487 break;
1488 case CDATATYPE_ushort:
1489 dlo = dhi = (double)*(unsigned short*)(voxel);
1490 printf("initial range for ushort hi %lf lo %lf\n", dhi, dlo);
1491 break;
1492 case CDATATYPE_int:
1493 dlo = dhi = (double)*(long*)(voxel);
1494 break;
1495 case CDATATYPE_uint:
1496 dlo = dhi = (double)*(unsigned long*)(voxel);
1497 break;
1498 case CDATATYPE_longlong:
1499 dlo = dhi = (double)*(long long*)(voxel);
1500 break;
1501 case CDATATYPE_ulonglong:
1502 dlo = dhi = (double)*(unsigned long long*)(voxel);
1503 break;
1504 case CDATATYPE_float:
1505 dlo = dhi = (double)*(float*)(voxel);
1506 break;
1507 case CDATATYPE_double:
1508 dlo = dhi = *(double*)(voxel);
1509 break;
1510 default:
1511 break;
1512 }
1513 //find lower and upper of range by looking at every value
1514 for (i = 0; i < nvoxel; i++) {
1515 unsigned char* voxel;
1516 //unsigned char A;
1517 // unused unsigned char *rgba = &tti->texdata[i*4];
1518 //LUM-ALPHA with RGB=1, A= voxel scalar
1519 voxel = &data[i * bsize];
1520 switch (idatatype) {
1521 case CDATATYPE_char:
1522 dlo = min(dlo, (double)*(char*)(voxel));
1523 dhi = max(dhi, (double)*(char*)(voxel));
1524 break;
1525 case CDATATYPE_uchar:
1526 dlo = min(dlo, (double)*(unsigned char*)(voxel));
1527 dhi = max(dhi, (double)*(unsigned char*)(voxel));
1528 break;
1529 case CDATATYPE_short:
1530 dlo = min(dlo, (double)*(short*)(voxel));
1531 dhi = max(dhi, (double)*(short*)(voxel));
1532 break;
1533 case CDATATYPE_ushort:
1534 dlo = min(dlo, (double)*(unsigned short*)(voxel));
1535 dhi = max(dhi, (double)*(unsigned short*)(voxel));
1536 break;
1537 case CDATATYPE_int:
1538 dlo = min(dlo, (double)*(long*)(voxel));
1539 dhi = max(dhi, (double)*(long*)(voxel));
1540 break;
1541 case CDATATYPE_uint:
1542 dlo = min(dlo, (double)*(unsigned long*)(voxel));
1543 dhi = max(dhi, (double)*(unsigned long*)(voxel));
1544 break;
1545 case CDATATYPE_longlong:
1546 dlo = min(dlo, (double)*(unsigned long long*)(voxel));
1547 dhi = max(dhi, (double)*(unsigned long long*)(voxel));
1548 break;
1549 case CDATATYPE_ulonglong:
1550 dlo = min(dlo, (double)*(unsigned long*)(voxel));
1551 dhi = max(dhi, (double)*(unsigned long*)(voxel));
1552 break;
1553 case CDATATYPE_float:
1554 dlo = min(dlo, (double)*(float*)(voxel));
1555 dhi = max(dhi, (double)*(float*)(voxel));
1556 break;
1557 case CDATATYPE_double:
1558 dlo = min(dlo, (double)*(double*)(voxel));
1559 dhi = max(dhi, (double)*(double*)(voxel));
1560 break;
1561 default:
1562 break;
1563 }
1564 }
1565 d255range = 255.0 / (dhi - dlo);
1566 if (1) printf("nrrd image voxel range hi %lf lo %lf 255range scale factor %lf\n", dhi, dlo, d255range);
1567 }
1568 //now convert to display usable data type which currently is RGBA
1569 tti->texdata = MALLOC(unsigned char *,(size_t)nvoxel * 4); //4 for RGBA
1570 tti->channels = nchannel; //1=lum 2=lum-alpha 3=rgb 4=rgba //doing 2-channel allows modulation of material color
1571 //Oct 16, 2016: in textures.c we now compute gradient automatically and put in RGB, if channels == 1 and z > 1
1572 tti->hasAlpha = TRUE;
1573 tti->x = isize[0];
1574 tti->y = isize[1];
1575 tti->z = isize[2];
1576 memset(counts,0,256*sizeof(int));
1577 for(i=0;i<nvoxel;i++){
1578 unsigned char *voxel;
1579 unsigned char A;
1580 unsigned char *rgba = &tti->texdata[i*4];
1581 //LUM-ALPHA with RGB=1, A= voxel scalar
1582
1583 A = '\0';
1584 voxel = &data[i*bsize];
1585 if (nchannel == 1) {
1586 if (1) {
1587 //no range-scale method - might be needed for experiments
1588 switch (idatatype) {
1589 case CDATATYPE_char:
1590 A = (char)(voxel[0]) + 127; //convert from signed char to unsigned
1591 break;
1592 case CDATATYPE_uchar:
1593 A = voxel[0];
1594 break;
1595 case CDATATYPE_short:
1596 A = (unsigned char)((*(short*)voxel) / 255) + 127; //scale into uchar range, assumes short range is fully used
1597 break;
1598 case CDATATYPE_ushort:
1599 {
1600 //static unsigned short lastushort = 1;
1601 unsigned short thisushort;
1602 memcpy(&thisushort, voxel, bsize);
1603 //thisushort = *(unsigned short*)voxel;
1604 //A = (unsigned char) ((*(unsigned short *)voxel) / 255); //scale into uchar range, "
1605 //A = (*(unsigned short *)voxel) >> 8;
1606 //A = ((*(unsigned short *)voxel) << 8) >> 8;
1607 //A = (unsigned char) abs(*(unsigned short *)voxel)/256;
1608 thisushort /= 256;
1609 //if(thisushort != lastushort)
1610 // printf("%d ", (int)thisushort);
1611 counts[thisushort]++;
1612 //lastushort = thisushort;
1613 A = (unsigned char)thisushort;
1614 }
1615 break;
1616 case CDATATYPE_int:
1617 A = (unsigned char)((*((long*)voxel)) / 65536 / 255 + 127);
1618 break;
1619 case CDATATYPE_uint:
1620 A = (unsigned char)((*((unsigned long*)voxel)) / 65536 / 255);
1621 break;
1622 case CDATATYPE_longlong:
1623 A = (unsigned char)((*((long long*)voxel)) / 65536 / 65536 / 255 + 127);
1624 break;
1625 case CDATATYPE_ulonglong:
1626 A = (unsigned char)((*((unsigned long long*)voxel)) / 65536 / 65536 / 255);
1627 break;
1628 //case CDATATYPE_float:
1629 // A = (unsigned char) ((int)((*((float *)voxel))/range + range/2.0f) + 127) ;
1630 //break;
1631 //case CDATATYPE_double:
1632 // A = (unsigned char) ((int)((*((double *)voxel))/range + range/2.0f) + 127) ;
1633 //break;
1634 default:
1635 break;
1636 }
1637 }
1638 else {
1639 //range scaling method
1640 double dtemp; //, dtemp2;
1641 //unsigned int lutemp;
1642 //unsigned short utemp;
1643 //unsigned char uctemp;
1644
1645 switch (idatatype) {
1646 case CDATATYPE_char:
1647 A = (unsigned char)((int)(voxel[0])) + 127; //convert from signed char to unsigned
1648 break;
1649 case CDATATYPE_uchar:
1650 A = voxel[0];
1651 break;
1652 case CDATATYPE_short:
1653 dtemp = (double)(*(short*)voxel);
1654 A = (unsigned char)(unsigned short)(unsigned int)((dtemp - dlo) * d255range);
1655 break;
1656 case CDATATYPE_ushort:
1657 dtemp = (double)(*(unsigned short*)voxel);
1658 //dtemp2 = (dtemp - dlo)*d255range;
1659 //lutemp = (unsigned int)dtemp2;
1660 //utemp = (unsigned short)lutemp;
1661 //uctemp = (unsigned char)utemp;
1662 //A = uctemp;
1663 //tip: get it into 0-255 range while still double, then cast to uchar
1664 A = (unsigned char)(unsigned short)(unsigned int)((dtemp - dlo) * d255range);
1665 //A = (unsigned char)(unsigned short)(unsigned int)dtemp2;
1666 //printf("[%lf %lu %u %d] ",dtemp2,lutemp,utemp,(int)uctemp);
1667 break;
1668 case CDATATYPE_int:
1669 dtemp = (double)(*(long*)voxel);
1670 A = (unsigned char)(unsigned short)(unsigned int)((dtemp - dlo) * d255range);
1671 break;
1672 case CDATATYPE_uint:
1673 dtemp = (double)(*(unsigned long*)voxel);
1674 A = (unsigned char)(unsigned short)(unsigned int)((dtemp - dlo) * d255range);
1675 break;
1676 case CDATATYPE_longlong:
1677 dtemp = (double)(*(long long*)voxel);
1678 A = (unsigned char)(unsigned short)(unsigned int)((dtemp - dlo) * d255range);
1679 break;
1680 case CDATATYPE_ulonglong:
1681 dtemp = (double)(*(unsigned long long*)voxel);
1682 A = (unsigned char)(unsigned short)(unsigned int)((dtemp - dlo) * d255range);
1683 break;
1684 case CDATATYPE_float:
1685 dtemp = (double)(*(float*)voxel);
1686 A = (unsigned char)(unsigned short)(unsigned int)((dtemp - dlo) * d255range);
1687 break;
1688 case CDATATYPE_double:
1689 dtemp = (double)(*(double*)voxel);
1690 A = (unsigned char)(unsigned short)(unsigned int)((dtemp - dlo) * d255range);
1691 break;
1692 default:
1693 break;
1694 }
1695 counts[(int)A]++; //histogram accumulation
1696
1697
1698 }
1699 //this displays nice in texturing3D as 'white bones x-ray'
1700 rgba[0] = 255;
1701 rgba[1] = 255;
1702 rgba[2] = 255;
1703 rgba[3] = A;
1704 //print histogram to console
1705 if (0) for (i = 0; i < 256; i++)
1706 if (counts[i] != 0)
1707 printf("counts[%ld]=%ld\n", (long)i, (long)counts[i]);
1708 } else {
1709 //more than one channel. July 2022 dug9 formula for guessing how to allocate voxel bytes
1710 rgba[0] = voxel[0];
1711 if (bsize < 3) {
1712 rgba[1] = rgba[2] = rgba[0];
1713 }
1714 if (bsize == 2) {
1715 rgba[3] = voxel[1];
1716 }
1717 if (bsize > 2) {
1718 rgba[1] = voxel[1];
1719 rgba[2] = voxel[2];
1720 }
1721 if(bsize == 3)
1722 rgba[3] = 255;
1723 if (bsize == 4)
1724 rgba[3] = voxel[3];
1725 }
1726 }
1727 FREE_IF_NZ(data); //free the raw data we malloced, now that we have rgba, unless we plan to do more processing on scalar values later.
1728 }
1729 return TRUE;
1730
1731}
1732//<<< NRRD MIT ================================
1733
1734
1735#if defined(_ANDROID) || defined(ANDROIDNDK)
1736// sometimes (usually?) we have to flip an image vertically.
1737static unsigned char *flipImageVerticallyB(unsigned char *input, int height, int width, int bpp) {
1738 int i,ii,rowcount;
1739 unsigned char *sourcerow, *destrow;
1740 unsigned char * blob;
1741
1742 rowcount = width * bpp; //4; bytes per pixel
1743 blob = MALLOC(unsigned char*, height * rowcount);
1744 for(i=0;i<height;i++) {
1745 ii = height - 1 - i;
1746 sourcerow = &input[i*rowcount];
1747 destrow = &blob[ii*rowcount];
1748 memcpy(destrow,sourcerow,rowcount);
1749 }
1750 //FREE_IF_NZ(input);
1751 return blob;
1752}
1753static unsigned char *flipImageVertically(unsigned char *input, int height, int width) {
1754 return flipImageVerticallyB(input,height,width,4);
1755}
1756static unsigned char *expandto4bppfromGray(unsigned char *input, int height, int width, int bpp) {
1757 int i, j, rowcountin, rowcountout;
1758 unsigned char *sourcerow, *destrow;
1759 unsigned char * blob;
1760
1761 rowcountin = width * bpp; //bytes per pixel
1762 rowcountout = width * 4;
1763 blob = MALLOCV(height * rowcountout);
1764 for (i = 0; i<height; i++) {
1765 sourcerow = &input[i*rowcountin];
1766 destrow = &blob[i*rowcountout];
1767 for (j = 0; j<width; j++) {
1768 unsigned char *op = &destrow[j * 4];
1769 op[0] = op[1] = op[2] = sourcerow[j*bpp];
1770 op[3] = bpp == 1 ? 255 : sourcerow[j*bpp + 1];
1771 }
1772 }
1773 //FREE_IF_NZ(input);
1774 return blob;
1775}
1776static unsigned char *expandto4bppfromRGB(unsigned char *input, int height, int width, int bpp) {
1777 int i, j, rowcountin, rowcountout;
1778 unsigned char *sourcerow, *destrow;
1779 unsigned char * blob;
1780
1781 rowcountin = width * bpp; //bytes per pixel
1782 rowcountout = width * 4;
1783 blob = MALLOCV(height * rowcountout);
1784 for (i = 0; i<height; i++) {
1785 sourcerow = &input[i*rowcountin];
1786 destrow = &blob[i*rowcountout];
1787 for(j=0;j<width;j++){
1788 memcpy(&destrow[j*4], &sourcerow[j*bpp], bpp);
1789 destrow[j*4 + 3] = 255;
1790 }
1791 }
1792 //FREE_IF_NZ(input);
1793 return blob;
1794}
1795static unsigned char *expandto4bpp(unsigned char *input, int height, int width, int bpp) {
1796 unsigned char * retval = NULL;
1797 if(bpp == 1 || bpp == 2)
1798 retval = expandto4bppfromGray(input, height, width, bpp);
1799 else //if(bpp == 3)
1800 retval = expandto4bppfromRGB(input, height, width, bpp);
1801 return retval;
1802}
1803#endif //ANDROID - for flipImageVertically
1804
1805
1806
1807
1808#ifdef QNX
1809#include <img/img.h>
1810static img_lib_t ilib = NULL;
1811int loadImage(textureTableIndexStruct_s* tti, char* fname)
1812{
1813 int ierr, iret;
1814 img_t img;
1815 if(!ilib) ierr = img_lib_attach( &ilib );
1816 img.format = IMG_FMT_PKLE_ARGB8888; //GLES2 little endian 32bit - saw in sample code, no idea
1817 img.flags |= IMG_FORMAT;
1818 ierr= img_load_file(ilib, fname, NULL, &img);
1819 iret = 0;
1820 if(ierr == NULL)
1821 {
1822
1823 //deep copy data so browser owns it (and does its FREE_IF_NZ) and we can delete our copy here and forget about it
1824 tti->x = img.w;
1825 tti->y = img.h;
1826 tti->frames = 1;
1827 tti->texdata = img.access.direct.data;
1828 if(!tti->texdata)
1829 printf("ouch in gdiplus image loader L140 - no image data\n");
1830 else
1831 {
1832 int flipvertically = 1;
1833 if(flipvertically){
1834 int i,j,ii,rowcount;
1835 unsigned char *sourcerow, *destrow;
1836 unsigned char * blob;
1837 rowcount = tti->x * 4;
1838 blob = MALLOCV(img.h * rowcount);
1839 for(i=0;i<img.h;i++) {
1840 ii = tti->y - 1 - i;
1841 sourcerow = &tti->texdata[i*rowcount];
1842 destrow = &blob[ii*rowcount];
1843 memcpy(destrow,sourcerow,rowcount);
1844 }
1845 tti->texdata = blob;
1846 //try johns next time: tti->texdata = flipImageVertically(myFile->fileData, myFile->imageHeight, myFile->imageWidth);
1847
1848 }
1849 }
1850 tti->hasAlpha = 1; //img.transparency; //Gdiplus::IsAlphaPixelFormat(bitmap->GetPixelFormat())?1:0;
1851 tti->channels = 4; //don't know, don't have img_load_file() function
1852 //printf("fname=%s alpha=%ld\n",fname,tti->hasAlpha);
1853 iret = 1;
1854 }
1855 return iret;
1856}
1857
1858#endif
1859
1860char* download_file(char* filename);
1861void close_openned_file(openned_file_t *file);
1862int load_file_blob(const char *filename, char **blob, int *len);
1863
1864#if defined(ANDROIDNDK)
1865#define HAVE_LIBJPEG_H 1
1866#ifdef HAVE_LIBJPEG_H
1867#include <jpeglib.h>
1868#include <setjmp.h>
1869struct my_error_mgr {
1870 struct jpeg_error_mgr pub; /* "public" fields */
1871 jmp_buf setjmp_buffer; /* for return to caller */
1872};
1873
1874typedef struct my_error_mgr * my_error_ptr;
1875
1876/*
1877* Here's the routine that will replace the standard error_exit method:
1878*/
1879
1880METHODDEF(void)
1881my_error_exit(j_common_ptr cinfo)
1882{
1883 /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */
1884 my_error_ptr myerr = (my_error_ptr)cinfo->err;
1885
1886 /* Always display the message. */
1887 /* We could postpone this until after returning, if we chose. */
1888 /* JAS (*cinfo->err->output_message) (cinfo); */
1889
1890 /* Return control to the setjmp point */
1891 longjmp(myerr->setjmp_buffer, 1);
1892}
1893#define ERROR -1
1894#define NOT_JPEGT -2
1895#define JPEG_SUCCESS 0
1896
1897static int loadImageTexture_jpeg(textureTableIndexStruct_s* this_tex, char *filename) {
1898 FILE *infile;
1899 //char *filename;
1900 GLuint texture_num;
1901 unsigned char *image_data = 0;
1902
1903
1904 /* jpeg variables */
1905 struct jpeg_decompress_struct cinfo;
1906 struct my_error_mgr jerr;
1907 JDIMENSION nrows;
1908 JSAMPROW row = 0;
1909 JSAMPROW rowptr[1];
1910 unsigned rowcount, columncount;
1911 int dp;
1912
1913 int tempInt;
1914
1915
1916 if ((infile = fopen(filename, "rb")) == NULL) {
1917 fprintf(stderr, "can't open %s\n", filename);
1918 return ERROR;
1919 }
1920
1921 /* is it a jpeg file */
1922
1923 /* Select recommended processing options for quick-and-dirty output. */
1924 //cinfo.two_pass_quantize = FALSE;
1925 //cinfo.dither_mode = JDITHER_ORDERED;
1926 //cinfo.desired_number_of_colors = 216;
1927 //cinfo.dct_method = JDCT_FASTEST;
1928 //cinfo.do_fancy_upsampling = FALSE;
1929
1930 /* call my error handler if there is an error */
1931 cinfo.err = jpeg_std_error(&jerr.pub);
1932 jerr.pub.error_exit = my_error_exit;
1933 if (setjmp(jerr.setjmp_buffer)) {
1934 /* if we are here, we have a JPEG error */
1935 ConsoleMessage("FreeWRL Image problem - could not read %s\n", filename);
1936 jpeg_destroy_compress((j_compress_ptr)&cinfo);
1937 fclose(infile);
1938 return ERROR;
1939 }
1940
1941
1942 jpeg_create_decompress(&cinfo);
1943
1944 /* Specify data source for decompression */
1945 jpeg_stdio_src(&cinfo, infile);
1946
1947 /* Read file header, set default decompression parameters */
1948 /* (void) jpeg_read_header(&cinfo, TRUE); */
1949 // https://www4.cs.fau.de/Services/Doc/graphics/doc/jpeg/libjpeg.html
1950 tempInt = jpeg_read_header(&cinfo, TRUE);
1951
1952
1953 /* Start decompressor */
1954 (void)jpeg_start_decompress(&cinfo);
1955
1956
1957
1958 row = (JSAMPLE*)MALLOCV(cinfo.output_width * sizeof(JSAMPLE)*cinfo.output_components);
1959 rowptr[0] = row;
1960 image_data = (unsigned char *)MALLOCV(cinfo.output_width * sizeof(JSAMPLE) * cinfo.output_height * cinfo.output_components);
1961 /* Process data */
1962 for (rowcount = 0; rowcount < cinfo.output_height; rowcount++) {
1963 nrows = jpeg_read_scanlines(&cinfo, rowptr, 1);
1964 /* yield for a bit */
1965 sched_yield();
1966
1967
1968 for (columncount = 0; columncount < cinfo.output_width; columncount++) {
1969 for (dp = 0; dp<cinfo.output_components; dp++) {
1970 image_data[(cinfo.output_height - rowcount - 1)
1971 *cinfo.output_width*cinfo.output_components
1972 + columncount* cinfo.output_components + dp]
1973 = row[columncount*cinfo.output_components + dp];
1974 }
1975 }
1976 }
1977
1978 int iret = JPEG_SUCCESS;
1979 if (jpeg_finish_decompress(&cinfo) != TRUE) {
1980 printf("warning: jpeg_finish_decompress error\n");
1981 //releaseTexture(loadThisTexture->scenegraphNode);
1982 iret = ERROR;
1983 }
1984
1985
1986 //store_tex_info(loadThisTexture,
1987 // cinfo.output_components,
1988 // (int)cinfo.output_width,
1989 // (int)cinfo.output_height, image_data, cinfo.output_components == 4);
1990 fclose(infile);
1991
1992 this_tex->x = (int)cinfo.output_width;
1993 this_tex->y = (int)cinfo.output_height;
1994 this_tex->hasAlpha = 0; //jpeg doesn't have alpha?
1995 //int bpp = this_tex->hasAlpha ? 4 : 3; //bytes per pixel
1996 int bpp = cinfo.output_components; //4
1997 this_tex->channels = bpp; //3; //always RGB?
1998
1999 //char *dataflipped = flipImageVerticallyB(image_data, this_tex->y, this_tex->x, bpp);
2000 char *data4bpp = expandto4bpp(image_data,this_tex->y,this_tex->x,bpp);
2001 //free(image_data);
2002 this_tex->frames = 1;
2003 this_tex->texdata = data4bpp;
2004 FREE_IF_NZ(image_data);
2005 this_tex->filename = filename;
2006
2007 jpeg_destroy_decompress(&cinfo);
2008 FREE_IF_NZ(row);
2009
2010 return JPEG_SUCCESS;
2011}
2012
2013
2014
2015#endif //HAVE_LIBJPEG_H
2016
2017#define HAVE_LIBPNG_H 1
2018#ifdef HAVE_LIBPNG_H
2019#include <png.h>
2020
2021#define ERROR -1
2022#define NOT_PNG -2
2023#define PNG_SUCCESS 0
2024// http://www.learnopengles.com/loading-a-png-into-memory-and-displaying-it-as-a-texture-with-opengl-es-2-using-almost-the-same-code-on-ios-android-and-emscripten/
2025typedef struct {
2026 const png_byte* data;
2027 const png_size_t size;
2028} DataHandle;
2029
2030typedef struct {
2031 const DataHandle data;
2032 png_size_t offset;
2033} ReadDataHandle;
2034
2035typedef struct {
2036 const png_uint_32 width;
2037 const png_uint_32 height;
2038 const int color_type;
2039} PngInfo;
2040static GLenum get_gl_color_format(const int png_color_format) {
2041 //assert(png_color_format == PNG_COLOR_TYPE_GRAY
2042 // || png_color_format == PNG_COLOR_TYPE_RGB_ALPHA
2043 // || png_color_format == PNG_COLOR_TYPE_GRAY_ALPHA);
2044
2045 switch (png_color_format) {
2046 case PNG_COLOR_TYPE_GRAY:
2047 return GL_LUMINANCE;
2048 case PNG_COLOR_TYPE_RGB_ALPHA:
2049 return GL_RGBA;
2050 case PNG_COLOR_TYPE_GRAY_ALPHA:
2051 return GL_LUMINANCE_ALPHA;
2052 case PNG_COLOR_TYPE_RGB:
2053 return GL_RGB;
2054 }
2055
2056 return 0;
2057}
2058
2059static PngInfo read_and_update_info(const png_structp png_ptr, const png_infop info_ptr)
2060{
2061 png_uint_32 width, height;
2062 int bit_depth, color_type;
2063
2064 png_read_info(png_ptr, info_ptr);
2065 png_get_IHDR(
2066 png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL);
2067
2068 // Convert transparency to full alpha
2069 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
2070 png_set_tRNS_to_alpha(png_ptr);
2071
2072 // Convert grayscale, if needed.
2073 if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
2074 png_set_expand_gray_1_2_4_to_8(png_ptr);
2075
2076 // Convert paletted images, if needed.
2077 if (color_type == PNG_COLOR_TYPE_PALETTE)
2078 png_set_palette_to_rgb(png_ptr);
2079
2080 // Add alpha channel, if there is none.
2081 // Rationale: GL_RGBA is faster than GL_RGB on many GPUs)
2082 if (color_type == PNG_COLOR_TYPE_PALETTE || color_type == PNG_COLOR_TYPE_RGB)
2083 png_set_add_alpha(png_ptr, 0xFF, PNG_FILLER_AFTER);
2084
2085 // Ensure 8-bit packing
2086 if (bit_depth < 8)
2087 png_set_packing(png_ptr);
2088 else if (bit_depth == 16)
2089 png_set_scale_16(png_ptr);
2090
2091 png_read_update_info(png_ptr, info_ptr);
2092
2093 // Read the new color type after updates have been made.
2094 color_type = png_get_color_type(png_ptr, info_ptr);
2095
2096 return (PngInfo) { width, height, color_type };
2097}
2098static DataHandle read_entire_png_image(
2099 const png_structp png_ptr,
2100 const png_infop info_ptr,
2101 const png_uint_32 height)
2102{
2103 const png_size_t row_size = png_get_rowbytes(png_ptr, info_ptr);
2104 const int data_length = row_size * height;
2105 assert(row_size > 0);
2106
2107 png_byte* raw_image = malloc(data_length);
2108 assert(raw_image != NULL);
2109
2110 png_byte* row_ptrs[height];
2111
2112 png_uint_32 i;
2113 for (i = 0; i < height; i++) {
2114 row_ptrs[i] = raw_image + i * row_size;
2115 }
2116
2117 png_read_image(png_ptr, &row_ptrs[0]);
2118
2119 return (DataHandle) { raw_image, data_length };
2120}
2121static void read_png_data_callback(
2122 png_structp png_ptr, png_byte* raw_data, png_size_t read_length) {
2123 ReadDataHandle* handle = png_get_io_ptr(png_ptr);
2124 const png_byte* png_src = handle->data.data + handle->offset;
2125
2126 memcpy(raw_data, png_src, read_length);
2127 handle->offset += read_length;
2128}
2129enum {
2130TACTIC_FROM_FILE = 1,
2131TACTIC_FROM_BLOB = 2,
2132};
2133static int loadImageTexture_png(textureTableIndexStruct_s* this_tex, char *filename) {
2134 FILE *fp;
2135 //char *filename;
2136 GLuint texture_num;
2137 unsigned char *image_data = 0;
2138 int image_data_isMalloced;
2139
2140 /* png reading variables */
2141 int rc;
2142 unsigned long image_width = 0;
2143 unsigned long image_height = 0;
2144 unsigned long image_rowbytes = 0;
2145 int image_channels = 0;
2146 int glcolortype = 0;
2147 double display_exponent = 0.0;
2148 char * png_data;
2149 int png_data_size;
2150 int is_png;
2151 int tactic;
2152 int tempInt;
2153 tactic= TACTIC_FROM_BLOB;
2154
2155
2156 png_structp png_ptr = png_create_read_struct(
2157 PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
2158 png_infop info_ptr = png_create_info_struct(png_ptr);
2159
2160 //from memory (if from file there s png_set_io
2161 if(tactic == TACTIC_FROM_FILE){
2162 size_t rvt;
2163 char header[8];
2164 fp = fopen(filename,"rb");
2165 rvt=fread(header, 1, 8, fp);
2166 is_png = !png_sig_cmp(png_data, 0, 8);
2167 fclose(fp);
2168 if (!is_png)
2169 {
2170 return (NOT_PNG);
2171 }
2172 fp = fopen(filename,"rb");
2173 png_init_io(png_ptr, fp);
2174 } else if(tactic == TACTIC_FROM_BLOB){
2175 if (!load_file_blob(filename, &png_data, &png_data_size)) {
2176 return ERROR;
2177 }
2178 is_png = !png_sig_cmp(png_data, 0, 8);
2179 if (!is_png)
2180 {
2181 return (NOT_PNG);
2182 }
2183 ReadDataHandle png_data_handle = (ReadDataHandle) { {png_data, png_data_size}, 0 };
2184 png_set_read_fn(png_ptr, &png_data_handle, read_png_data_callback);
2185 }
2186
2187 if (setjmp(png_jmpbuf(png_ptr)))
2188 {
2189 png_destroy_read_struct(&png_ptr, &info_ptr,
2190 (png_infopp)NULL);
2191 if (tactic == TACTIC_FROM_FILE) fclose(fp);
2192 return (ERROR);
2193 }
2194
2195 //png_read_png(png_ptr, info_ptr, 0, NULL);
2196
2197 //image_data = readpng_get_image(display_exponent, &image_channels,
2198 // &image_rowbytes);
2199 const PngInfo png_info = read_and_update_info(png_ptr, info_ptr);
2200 const DataHandle raw_image = read_entire_png_image(
2201 png_ptr, info_ptr, png_info.height);
2202
2203 png_read_end(png_ptr, info_ptr);
2204 this_tex->x = png_info.width;
2205 this_tex->y = png_info.height;
2206 //glcolortype = get_gl_color_format(png_info.color_type);
2207 //this_tex->hasAlpha = png_info.color_type == GL_RGBA || png_info.color_type == GL_LUMINANCE_ALPHA;
2208 //switch(glcolortype) { //png_info.color_type){
2209 // case GL_LUMINANCE: this_tex->channels = 1; break;
2210 // case GL_LUMINANCE_ALPHA: this_tex->channels = 2; break;
2211 // case GL_RGB: this_tex->channels = 3; break;
2212 // case GL_RGBA: this_tex->channels = 4; break;
2213 // default:
2214 // this_tex->channels = 4; break;
2215 //}
2216 image_channels = 4;
2217 switch (png_info.color_type) {
2218 case PNG_COLOR_TYPE_GRAY: image_channels = 1; break;
2219 case PNG_COLOR_TYPE_GRAY_ALPHA: image_channels = 2; break;
2220 case PNG_COLOR_TYPE_RGB: image_channels = 3; break;
2221 case PNG_COLOR_TYPE_RGB_ALPHA: image_channels = 4; break;
2222 default:
2223 image_channels = 4;
2224 }
2225 this_tex->channels = image_channels;
2226 this_tex->hasAlpha = this_tex->channels == 2 || this_tex->channels == 4;
2227 //int bpp = this_tex->hasAlpha ? 4 : 3; //bytes per pixel
2228 image_data = raw_image.data;
2229
2230 image_data_isMalloced = 0;
2231 if(image_channels < 4){
2232 image_data = expandto4bpp(image_data, this_tex->y, this_tex->x, image_channels);
2233 image_data_isMalloced = 1;
2234 }
2235 int bpp = 4;
2236 unsigned char *dataflipped = flipImageVerticallyB(image_data, this_tex->y, this_tex->x, bpp);
2237 free(raw_image.data);
2238 if(image_data_isMalloced) free(image_data);
2239 this_tex->frames = 1;
2240 this_tex->texdata = dataflipped;
2241 this_tex->filename = filename;
2242 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
2243
2244
2245 //readpng_cleanup(FALSE);
2246
2247 if (tactic == TACTIC_FROM_FILE) fclose(fp);
2248 return PNG_SUCCESS;
2249}
2250
2251#endif //HAVE_LIBPNG_H
2252
2253#define HAVE_LIBGIF_H 1
2254#ifdef HAVE_LIBGIF_H
2255#include <gif_lib.h> //loads stdbool.h
2256// http://cd.textfiles.com/amigaplus/lesercd16/Tools/Development/ming0_2/util/gif2dbl.c
2257
2258int getTransparentColor(GifFileType * file)
2259{
2260 //get the color index of transparent
2261 int i;
2262 ExtensionBlock * ext = file->SavedImages[0].ExtensionBlocks;
2263
2264 for (i = 0; i < file->SavedImages[0].ExtensionBlockCount; i++, ext++) {
2265
2266 if (ext->Function == GRAPHICS_EXT_FUNC_CODE) {
2267 if (ext->Bytes[0] & 1) // there is a transparent color
2268 return ext->Bytes[3]; // here it is
2269 }
2270 }
2271
2272 return -1;
2273}
2274
2275
2276//#define GIF_ERROR -1
2277#define NOT_GIF -2
2278#define GIF_SUCCESS 0
2279
2280static int loadImageTexture_gif(textureTableIndexStruct_s* this_tex, char *filename) {
2281// http://giflib.sourceforge.net/gif_lib.html#idm46571690371280
2282
2283 int ErrorCode, alpha, iret;
2284 int
2285 InterlacedOffset[] = { 0, 4, 2, 1 }, /* The way Interlaced image should. */
2286 InterlacedJumps[] = { 8, 8, 4, 2 }; /* be read - offsets and jumps... */
2287 ColorMapObject *ColorMap;
2288 GifRowType *ScreenBuffer;
2289 int Error;
2290
2291 GifFileType *GifFile = DGifOpenFileName(filename, &ErrorCode);
2292 if(!GifFile){
2293 return GIF_ERROR;
2294 }
2295 if (GifFile->SHeight == 0 || GifFile->SWidth == 0) {
2296 return GIF_ERROR;
2297 }
2298
2299 ErrorCode = DGifSlurp(GifFile);
2300 if(ErrorCode != GIF_OK)
2301 return GIF_ERROR;
2302 alpha = getTransparentColor(GifFile);
2303 iret = GIF_ERROR;
2304 ColorMap = (GifFile->Image.ColorMap
2305 ? GifFile->Image.ColorMap
2306 : GifFile->SColorMap);
2307 if (ColorMap == NULL) {
2308 return GIF_ERROR;
2309 }
2310
2311 if(GifFile->ImageCount){
2312 unsigned char *pixel;
2313 int i,j,ipix,icolor;
2314
2315 unsigned char * raw = GifFile->SavedImages[0].RasterBits;
2316 int width = GifFile->SavedImages[0].ImageDesc.Width;
2317 int height = GifFile->SavedImages[0].ImageDesc.Height;
2318 GifColorType *Colors = ColorMap->Colors;
2319 unsigned char *rgba = MALLOCV(width * height * 4);
2320 GifColorType *color;
2321
2322 for(i=0;i<height;i++){
2323 for(j=0;j<width;j++){
2324 ipix = i*width + j;
2325 pixel = &rgba[ipix*4];
2326 icolor = raw[ipix];
2327 color = &Colors[icolor];
2328 pixel[0] = color->Red;
2329 pixel[1] = color->Green;
2330 pixel[2] = color->Blue;
2331 pixel[3] = icolor == alpha ? 0 : 255;
2332 }
2333 }
2334 this_tex->x = width;
2335 this_tex->y = height;
2336 this_tex->hasAlpha = alpha > -1 ? 1 : 0; //jpeg doesn't have alpha?
2337 this_tex->channels = 3 + this_tex->hasAlpha;
2338 this_tex->frames = 1;
2339 int bpp = 4;
2340 char *dataflipped = flipImageVerticallyB(rgba, this_tex->y, this_tex->x, bpp);
2341 FREE_IF_NZ(rgba);
2342 this_tex->texdata = dataflipped;
2343 this_tex->filename = filename;
2344 iret = GIF_SUCCESS;
2345 }
2346 return iret;
2347
2348}
2349#define bool int //set back to freewrl convention
2350#endif //HAVE_LIBGIF_H
2351
2352
2353static void __reallyloadImageTexture(textureTableIndexStruct_s* this_tex, char *filename) {
2354//filenames coming in can be temp file names - scrambled
2355//there are 3 ways to tell in the backend what type of image file:
2356//a) .xxx original filename suffix
2357//b) MIME type
2358//c) file signature https://en.wikipedia.org/wiki/List_of_file_signatures
2359// right now we aren't passing in the .xxx or mime or signature bytes
2360// except through the file conents we can get the signature
2361 char header[20];
2362 size_t rvt;
2363 UNUSED(rvt);
2364
2365 FILE* fp = fopen(filename,"rb");
2366 rvt=fread(header,20,1,fp);
2367 fclose(fp);
2368
2369#ifdef HAVE_LIBPNG_H
2370 if(!strncmp(&header[1],"PNG",3))
2371 loadImageTexture_png(this_tex, filename);
2372#endif
2373#ifdef HAVE_LIBJPEG_H
2374 if(!strncmp(header,"ÿØÿ",3))
2375 loadImageTexture_jpeg(this_tex, filename);
2376#endif
2377#ifdef HAVE_LIBGIF_H
2378 if(!strncmp(header,"GIF",3))
2379 loadImageTexture_gif(this_tex, filename);
2380#endif
2381 return;
2382}
2383
2384
2385#endif // ANDROIDNDK
2386
2387#define STB_IMAGE_IMPLEMENTATION
2388#include "stb_image.h"
2389int textureIsDDS(textureTableIndexStruct_s* this_tex, char *filename);
2390int texture_load_from_buffer(textureTableIndexStruct_s* this_tex, char* buffer, int len) {
2391 //the image data is already loaded in memory as a blob, for example gltf .bin / .glb buffer
2392 // still packed in image file format / mime_type .jpg, .png etc
2393 // so needs to be unpacked into regular texture array here
2394 int ret, imtype;
2395 char* data = NULL;
2396 int x, y, nchannels;
2397 imtype = sniffImageHeader(buffer);
2398
2399 ret = FALSE;
2400 switch (imtype) {
2401 case IMAGETYPE_PNG:
2402 case IMAGETYPE_JPEG:
2403 case IMAGETYPE_GIF:
2404 stbi_set_flip_vertically_on_load(TRUE);
2405 data = stbi_load_from_memory(buffer, len, &x, &y, &nchannels, 4);
2406 int bpp = 4;
2407 if (data) {
2408 this_tex->channels = nchannels;
2409 this_tex->x = x;
2410 this_tex->y = y;
2411 this_tex->frames = 1;
2412 this_tex->texdata = data;
2413 this_tex->hasAlpha = nchannels == 2 || nchannels == 4 ? 1 : 0;
2414 this_tex->status = TEX_NEEDSBINDING;
2415 //unsigned char* dataflipped = flipImageVerticallyB(image_data, this_tex->y, this_tex->x, bpp);
2416
2417 ret = TRUE;
2418 }
2419 //{
2420 // int nchan;
2421 // if (imtype == IMAGETYPE_JPEG) {
2422 // nchan = 3; //jpeg always rgb, no alpha
2423 // }
2424 // else {
2425 // nchan = sniffImageChannels_bruteForce(this_tex->texdata, this_tex->x, this_tex->y);
2426 // }
2427 // if (nchan > -1) this_tex->channels = nchan;
2428 //}
2429 break;
2430 //case IMAGETYPE_DDS:
2431 // ret = textureIsDDS(this_tex, fname); break;
2432 //case IMAGETYPE_WEB3DIT:
2433 // ret = loadImage_web3dit(this_tex, fname); break;
2434 //case IMAGETYPE_NRRD:
2435 // ret = loadImage_nrrd(this_tex, fname); break;
2436 //case IMAGETYPE_VOL:
2437 // ret = loadImage3DVol(this_tex, fname); break;
2438 case IMAGETYPE_UNKNOWN:
2439 default:
2440 ret = FALSE;
2441 }
2442
2443 return (ret != 0);
2444
2445}
2450
2451int texture_load_from_file(textureTableIndexStruct_s* this_tex, char *filename)
2452{
2453
2454/* Android, put it here... */
2455
2456#if defined(ANDROIDNDK)
2457 int imtype, ret;
2458 char * fname = STRDUP(filename);
2459 imtype = sniffImageFileHeader(fname);
2460
2461 ret = FALSE;
2462 switch(imtype){
2463 case IMAGETYPE_PNG:
2464 #ifdef HAVE_LIBPNG_H
2465 ret = loadImageTexture_png(this_tex, filename);
2466 #endif
2467 break;
2468 case IMAGETYPE_JPEG:
2469 #ifdef HAVE_LIBJPEG_H
2470 ret = loadImageTexture_jpeg(this_tex, filename);
2471 #endif
2472 break;
2473 case IMAGETYPE_GIF:
2474 #ifdef HAVE_LIBGIF_H
2475 ret = loadImageTexture_gif(this_tex, filename);
2476 #endif
2477 break;
2478 case IMAGETYPE_DDS:
2479 ret = textureIsDDS(this_tex, fname); break;
2480 case IMAGETYPE_WEB3DIT:
2481 ret = loadImage_web3dit(this_tex,fname); break;
2482 case IMAGETYPE_NRRD:
2483 ret = loadImage_nrrd(this_tex,fname);
2484 break;
2485 case IMAGETYPE_VOL:
2486 ret = loadImage3DVol(this_tex, fname); break;
2487 case IMAGETYPE_UNKNOWN:
2488 default:
2489 ret = FALSE;
2490 }
2491
2492 //if(loadImage_web3dit(this_tex,fname)){
2493 // return TRUE;
2494 //}
2495 //if (loadImage3DVol(this_tex, fname))
2496 // return TRUE;
2497 //if (textureIsDDS(this_tex, fname)) {
2498 // //saveImage3D_x3di3d(this_tex,"temp2.x3di3d"); //good for testing round trip
2499 // return TRUE;
2500 //}
2501
2502 //__reallyloadImageTexture(this_tex, filename);
2503
2504 /*
2505 // if we got null for data, lets assume that there was not a file there
2506 if (myFile->fileData == NULL) {
2507 result = FALSE;
2508 }
2509 else {
2510 this_tex->texdata = flipImageVertically(myFile->fileData, myFile->imageHeight, myFile->imageWidth);
2511
2512 this_tex->filename = filename;
2513 this_tex->hasAlpha = myFile->imageAlpha;
2514 this_tex->frames = 1;
2515 this_tex->x = myFile->imageWidth;
2516 this_tex->y = myFile->imageHeight;
2517 result = TRUE;
2518 }
2519 //close_openned_file(myFile);
2520 FREE_IF_NZ(myFile);
2521 */
2522 //return (ret != 0); //
2523 return this_tex->frames;
2524
2525#endif //ANDROIDNDK
2526
2527
2528
2529#if defined(_ANDROID)
2530 unsigned char *image = NULL;
2531 unsigned char *imagePtr;
2532 int i;
2533
2534 openned_file_t *myFile = load_file (filename);
2535 bool result = FALSE;
2536 /* if we got null for data, lets assume that there was not a file there */
2537 if (myFile->fileData == NULL) {
2538 result = FALSE;
2539 } else {
2540 //this_tex->texdata = MALLOC(unsigned char*,myFile->fileDataSize);
2541 //memcpy(this_tex->texdata,myFile->fileData,myFile->fileDataSize);
2542/*
2543{char me[200]; sprintf(me,"texture_load, %d * %d * 4 = %d, is it %d??",myFile->imageHeight, myFile->imageWidth,
2544 myFile->imageHeight*myFile->imageWidth*4, myFile->fileDataSize);
2545ConsoleMessage(me);}
2546*/
2547
2548 this_tex->texdata = flipImageVertically(myFile->fileData, myFile->imageHeight, myFile->imageWidth);
2549
2550 this_tex->filename = filename;
2551 this_tex->hasAlpha = myFile->imageAlpha;
2552 this_tex->channels = 4; //don't know but but someone might. I added opened_files_t.imageChannels in case
2553 this_tex->frames = 1;
2554 this_tex->x = myFile->imageWidth;
2555 this_tex->y = myFile->imageHeight;
2556 result = TRUE;
2557 }
2558#ifdef FRONTEND_GETS_FILES
2559 close_openned_file(myFile);
2560 FREE_IF_NZ(myFile);
2561#endif
2562 return result;
2563
2564#endif //ANDROID
2565
2566
2567
2568/* WINDOWS */
2569#if defined (_MSC_VER)
2570 char *fname;
2571 int ret, imtype;
2572
2573 fname = STRDUP(filename);
2574 imtype = sniffImageFileHeader(fname);
2575
2576 ret = FALSE;
2577 switch(imtype){
2578 case IMAGETYPE_PNG:
2579 case IMAGETYPE_JPEG:
2580 case IMAGETYPE_GIF:
2581 ret = loadImage(this_tex, fname);
2582 #ifndef GL_ES_VERSION_2_0
2583 texture_swap_B_R(this_tex); //just for windows desktop gdiplusimage loading
2584 #endif
2585 {
2586 int nchan;
2587 if(imtype == IMAGETYPE_JPEG){
2588 nchan = 3; //jpeg always rgb, no alpha
2589 }else{
2590 nchan = sniffImageChannels_bruteForce(this_tex->texdata, this_tex->x, this_tex->y);
2591 }
2592 if(nchan > -1) this_tex->channels = nchan;
2593 }
2594 break;
2595 case IMAGETYPE_DDS:
2596 ret = textureIsDDS(this_tex, fname); break;
2597 case IMAGETYPE_WEB3DIT:
2598 ret = loadImage_web3dit(this_tex,fname); break;
2599 case IMAGETYPE_NRRD:
2600 ret = loadImage_nrrd(this_tex,fname); break;
2601 case IMAGETYPE_VOL:
2602 ret = loadImage3DVol(this_tex, fname); break;
2603 case IMAGETYPE_UNKNOWN:
2604 default:
2605 ret = FALSE;
2606 }
2607
2608 FREE(fname);
2609 return (ret != 0);
2610
2611#endif
2612
2613
2614/* LINUX */
2615
2616#if !defined (_MSC_VER) && !defined(_ANDROID) && !defined(ANDROIDNDK)
2617#ifdef HAVE_IMLIB2
2618 Imlib_Image image;
2619 Imlib_Load_Error error_return;
2620 char *fname;
2621 int ret, imtype;
2622
2623 fname = STRDUP(filename);
2624 imtype = sniffImageFileHeader(fname);
2625 ret = FALSE;
2626
2627 switch(imtype){
2628 case IMAGETYPE_DDS:
2629 ret = textureIsDDS(this_tex, fname); break;
2630 case IMAGETYPE_WEB3DIT:
2631 ret = loadImage_web3dit(this_tex,fname); break;
2632 case IMAGETYPE_NRRD:
2633 ret = loadImage_nrrd(this_tex,fname); break;
2634 case IMAGETYPE_VOL:
2635 ret = loadImage3DVol(this_tex, fname); break;
2636 case IMAGETYPE_PNG:
2637 case IMAGETYPE_JPEG:
2638 case IMAGETYPE_GIF:
2639 case IMAGETYPE_UNKNOWN:
2640 default:
2641 //JAS ret = FALSE;
2642 //image = imlib_load_image_immediately(filename);
2643 //image = imlib_load_image(filename);
2644 image = imlib_load_image_with_error_return(filename,&error_return);
2645 ret = (error_return == 0);
2646
2647 if (!image) {
2648 char *es = NULL;
2649 switch(error_return){
2650 case IMLIB_LOAD_ERROR_NONE: es = "IMLIB_LOAD_ERROR_NONE";break;
2651 case IMLIB_LOAD_ERROR_FILE_DOES_NOT_EXIST: es = "IMLIB_LOAD_ERROR_FILE_DOES_NOT_EXIST";break;
2652 case IMLIB_LOAD_ERROR_FILE_IS_DIRECTORY: es = "IMLIB_LOAD_ERROR_FILE_IS_DIRECTORY";break;
2653 case IMLIB_LOAD_ERROR_PERMISSION_DENIED_TO_READ: es = "IMLIB_LOAD_ERROR_PERMISSION_DENIED_TO_READ";break;
2654 case IMLIB_LOAD_ERROR_NO_LOADER_FOR_FILE_FORMAT: es = "IMLIB_LOAD_ERROR_NO_LOADER_FOR_FILE_FORMAT";break;
2655 case IMLIB_LOAD_ERROR_PATH_TOO_LONG: es = "IMLIB_LOAD_ERROR_PATH_TOO_LONG";break;
2656 case IMLIB_LOAD_ERROR_PATH_COMPONENT_NON_EXISTANT: es = "IMLIB_LOAD_ERROR_PATH_COMPONENT_NON_EXISTANT";break;
2657 case IMLIB_LOAD_ERROR_PATH_COMPONENT_NOT_DIRECTORY: es = "IMLIB_LOAD_ERROR_PATH_COMPONENT_NOT_DIRECTORY";break;
2658 case IMLIB_LOAD_ERROR_PATH_POINTS_OUTSIDE_ADDRESS_SPACE: es = "IMLIB_LOAD_ERROR_PATH_POINTS_OUTSIDE_ADDRESS_SPACE";break;
2659 case IMLIB_LOAD_ERROR_TOO_MANY_SYMBOLIC_LINKS: es = "IMLIB_LOAD_ERROR_TOO_MANY_SYMBOLIC_LINKS";break;
2660 case IMLIB_LOAD_ERROR_OUT_OF_MEMORY: es = "IMLIB_LOAD_ERROR_OUT_OF_MEMORY";break;
2661 case IMLIB_LOAD_ERROR_OUT_OF_FILE_DESCRIPTORS: es = "IMLIB_LOAD_ERROR_OUT_OF_FILE_DESCRIPTORS";break;
2662 case IMLIB_LOAD_ERROR_PERMISSION_DENIED_TO_WRITE: es = "IMLIB_LOAD_ERROR_PERMISSION_DENIED_TO_WRITE";break;
2663 case IMLIB_LOAD_ERROR_OUT_OF_DISK_SPACE: es = "IMLIB_LOAD_ERROR_OUT_OF_DISK_SPACE";break;
2664 case IMLIB_LOAD_ERROR_UNKNOWN:
2665 default:
2666 es = "IMLIB_LOAD_ERROR_UNKNOWN";break;
2667 }
2668 ERROR_MSG("imlib load error = %d %s\n",error_return,es);
2669 ERROR_MSG("load_texture_from_file: failed to load image: %s\n", filename);
2670 return FALSE;
2671 }
2672 DEBUG_TEX("load_texture_from_file: Imlib2 succeeded to load image: %s\n", filename);
2673
2674 imlib_context_set_image(image);
2675 imlib_image_flip_vertical(); /* FIXME: do we really need this ? */
2676
2677 /* store actual filename, status, ... */
2678 this_tex->filename = filename;
2679 this_tex->hasAlpha = (imlib_image_has_alpha() == 1);
2680 this_tex->channels = this_tex->hasAlpha ? 4 : 3;
2681 this_tex->frames = 1;
2682 this_tex->x = imlib_image_get_width();
2683 this_tex->y = imlib_image_get_height();
2684
2685 this_tex->texdata = (unsigned char *) imlib_image_get_data_for_reading_only();
2686 {
2687 int nchan;
2688 //int nchan, imtype;
2689 if(imtype == IMAGETYPE_JPEG)
2690 nchan = 3; //jpeg always rgb, no alpha
2691 else
2692 nchan = sniffImageChannels_bruteForce(this_tex->texdata, this_tex->x, this_tex->y);
2693 //nchan = sniffImageChannels(fname);
2694 if(nchan > -1) this_tex->channels = nchan;
2695 }
2696 //(Sept 5, 2016 change) assuming imlib gives BGRA:
2697 texture_swap_B_R(this_tex);
2698 //this_tex->data should now be RGBA. (if not comment above line)
2699 break;
2700 }
2701
2702 FREE(fname);
2703 return (ret);
2704
2705#endif //HAVE_IMLIB2
2706#endif //NOT MSC, ANDROID
2707
2708
2709 return FALSE;
2710}
2711
2719static bool texture_process_entry(textureTableIndexStruct_s *entry)
2720{
2721 resource_item_t *res;
2722 resource_type_t restype;
2723 struct Multi_String *url;
2724 resource_item_t *parentPath = NULL;
2725
2726 DEBUG_TEX("textureThread - working on %p (%s)\n"
2727 "which is node %p, nodeType %d status %s, opengltex %u, and frames %d\n",
2728 entry, entry->filename, entry->scenegraphNode, entry->nodeType,
2729 texst(entry->status), entry->OpenGLTexture,
2730 entry->frames);
2731
2732 entry->status = TEX_LOADING;
2733 url = NULL;
2734 res = NULL;
2735
2736 /* did this node just disappear? */
2737 if (!checkNode(entry->scenegraphNode,__FILE__,__LINE__)) {
2738 ConsoleMessage ("node for texture just deleted...\n");
2739 return FALSE;
2740 }
2741
2742
2743 switch (entry->nodeType) {
2744
2745 case NODE_PixelTexture:
2746 texture_load_from_pixelTexture(entry,(struct X3D_PixelTexture *)entry->scenegraphNode);
2747 //sets TEX_NEEDSBINDING internally
2748 return TRUE;
2749 break;
2750 case NODE_GeneratedTexture:
2751 texture_load_blank_Texture(entry, (struct X3D_GeneratedTexture*)entry->scenegraphNode);
2752 //sets TEX_NEEDSBINDING internally
2753 return TRUE;
2754 break;
2755
2756
2757 case NODE_PixelTexture3D:
2758 texture_load_from_pixelTexture3D(entry,(struct X3D_PixelTexture3D *)entry->scenegraphNode);
2759 //sets TEX_NEEDSBINDING internally
2760 return TRUE;
2761 break;
2762
2763 case NODE_ImageTexture:
2764 url = & (((struct X3D_ImageTexture *)entry->scenegraphNode)->url);
2765 parentPath = (resource_item_t *)(((struct X3D_ImageTexture *)entry->scenegraphNode)->_parentResource);
2766 restype = resm_image;
2767 break;
2768
2769 case NODE_BufferTexture:
2770 restype = resm_image_buffer;
2771 struct X3D_TextureRep* tr = (struct X3D_TextureRep*)((struct X3D_BufferTexture*)entry->scenegraphNode)->_intern;
2772 if (tr && tr->buffer->loaded) {
2773 char *address = tr->buffer->address + tr->byteOffset; //buffer
2774 int len = tr->byteSize; //buffer len
2775 texture_load_from_buffer(entry, address, len);
2776 return TRUE;
2777 } else {
2778 return FALSE;
2779 }
2780 break;
2781
2782 case NODE_ImageTexture3D:
2783 url = & (((struct X3D_ImageTexture3D *)entry->scenegraphNode)->url);
2784 parentPath = (resource_item_t *)(((struct X3D_ImageTexture3D *)entry->scenegraphNode)->_parentResource);
2785 restype = resm_image;
2786 break;
2787
2788 case NODE_ComposedTexture3D:
2789 return TRUE;
2790 break;
2791
2792
2793 case NODE_MovieTexture:
2794 url = & (((struct X3D_MovieTexture *)entry->scenegraphNode)->url);
2795 parentPath = (resource_item_t *)(((struct X3D_MovieTexture *)entry->scenegraphNode)->_parentResource);
2796 entry->status = TEX_NEEDSBINDING; //as with pixeltexture, just do the move_to_opengl part, we load from file elsewhere
2797 restype = resm_movie;
2798 return TRUE; //like pixeltexture - assume the pixels are delivered magically, not from file, so just return
2799 break;
2800 case NODE_ImageCubeMapTexture:
2801 url = & (((struct X3D_ImageCubeMapTexture *)entry->scenegraphNode)->url);
2802 parentPath = (resource_item_t *)(((struct X3D_ImageCubeMapTexture *)entry->scenegraphNode)->_parentResource);
2803 restype = resm_image;
2804 break;
2805
2806 default:
2807 printf ("invalid nodetype given to loadTexture, %s is not valid\n",stringNodeType(entry->nodeType));
2808 }
2809 if(!url){
2810 entry->status = TEX_NOTFOUND;
2811 return FALSE;
2812 }
2813
2814 //TEX_LOADING
2815 res = resource_create_multi(url);
2816 res->type = rest_multi;
2817 res->media_type = restype; //resm_image; /* quick hack */
2818 resource_identify(parentPath, res);
2819 res->whereToPlaceData = entry;
2820 res->textureNumber = entry->textureNumber;
2821 resitem_enqueue(ml_new(res));
2822 return TRUE;
2823
2824}
2825/*
2826parsing thread --> texture_loading_thread hand-off
2827GOAL: texture thread blocks when no textures requested. (rather than sleep(500) and for(;;) )
2828IT IS AN ERROR TO CALL (condition signal) before calling (condition wait).
2829So you might have a global variable bool waiting = false.
28301. The threads start, list=null, waiting=false
28312. The texture thread loops to lock_mutex line, checks if list=null,
2832 if so it sets waiting = true, and sets condition wait, and blocks,
2833 waiting for the main thread to give it some texure names
28343. The parsing/main thread goes to schedule a texture. It mutex locks,
2835 list= add new item. it checks if textureloader is waiting,
2836 if so signals condition (which locks momentarily blocks while
2837 other thread does something to the list) then unlock mutex.
28384. The texture thread gets a signal its waiting on. it copies the list and sets it null,
2839 sets waiting =false, and unlocks and does its loading work
2840 (on its copy of the list), and goes back around to 2.
2841
2842*/
2843
2847static void texture_process_list_item(s_list_t *item)
2848{
2849 bool remove_it = FALSE;
2850 textureTableIndexStruct_s *entry;
2851 // OLDCODE UNUSED ppLoadTextures p = (ppLoadTextures)gglobal()->LoadTextures.prv;
2852
2853 if (!item || !item->elem)
2854 return;
2855
2856 entry = ml_elem(item);
2857
2858 DEBUG_TEX("texture_process_list: %s\n", entry->filename);
2859
2860 /* FIXME: it seems there is no case in which we not want to remote it ... */
2861
2862 switch (entry->status) {
2863
2864 /* JAS - put in the TEX_LOADING flag here - it helps on OSX */
2865 case TEX_LOADING:
2866 if (texture_process_entry(entry)) {
2867 remove_it = TRUE;
2868 }else{
2869 remove_it = TRUE; //still remove it
2870 // url doesn't exist (or none of multi-url exist)
2871 // no point in trying again,
2872 // you'll just get the same result in a vicious cycle
2873 }
2874 //printf("texture_process LOADING\n");
2875 break;
2876 case TEX_READ:
2877 entry->status = TEX_NEEDSBINDING;
2878 remove_it = TRUE;
2879 //printf("texture_process READ\n");
2880 break;
2881 default:
2882 //DEBUG_MSG("Could not process texture entry: %s\n", entry->filename);
2883 //printf("texture_process default\n");
2884 remove_it = TRUE;
2885 break;
2886 }
2887
2888 if (remove_it) {
2889 /* free the parsed resource and list item */
2890 //OLDCODE UNUSED p->texture_list = ml_delete_self(p->texture_list, item);
2891 ml_free(item);
2892 }
2893}
2894
2895void threadsafe_enqueue_item_signal(s_list_t *item, s_list_t** queue, pthread_mutex_t* queue_lock, pthread_cond_t *queue_nonzero);
2896s_list_t* threadsafe_dequeue_item_wait(s_list_t** queue, pthread_mutex_t *queue_lock, pthread_cond_t *queue_nonzero, BOOL* wait);
2897
2898void texitem_enqueue(s_list_t *item){
2899 ppLoadTextures p;
2900 ttglobal tg = gglobal();
2901 p = (ppLoadTextures)gglobal()->LoadTextures.prv;
2902
2903 threadsafe_enqueue_item_signal(item, &p->texture_request_list, &tg->threads.mutex_texture_list, &tg->threads.texture_list_condition);
2904}
2905s_list_t *texitem_dequeue(){
2906 ppLoadTextures p;
2907 ttglobal tg = gglobal();
2908 p = (ppLoadTextures)gglobal()->LoadTextures.prv;
2909
2910 return threadsafe_dequeue_item_wait(&p->texture_request_list, &tg->threads.mutex_texture_list, &tg->threads.texture_list_condition, &tg->threads.TextureThreadWaiting);
2911}
2912//we want the void* addresses of the following, so the int value doesn't matter
2913static const int tex_command_exit;
2914
2915void texitem_queue_exit(){
2916 texitem_enqueue(ml_new(&tex_command_exit));
2917}
2918
2919void send_texture_to_loader(textureTableIndexStruct_s *entry)
2920{
2921 texitem_enqueue(ml_new(entry));
2922}
2923textureTableIndexStruct_s *getTableIndex(int i);
2924void process_res_texitem(resource_item_t *res){
2925 //resitem after download+load -> texture thread
2926 textureTableIndexStruct_s *entry;
2927 int textureNumber;
2928 textureNumber = res->textureNumber;
2929 //check in case texture has been deleted due to inline unloading during image download
2930 //entry = res->whereToPlaceData;
2931 entry = getTableIndex(textureNumber);
2932 if(entry)
2933 texitem_enqueue(ml_new(entry));
2934}
2935
2939
2940
2941#if !defined(HAVE_PTHREAD_CANCEL)
2942void Texture_thread_exit_handler(int sig)
2943{
2944 ConsoleMessage("Texture_thread_exit_handler: No pTheadCancel - textureThread exiting - maybe should cleanup? Should be done but need to check some rainy day");
2945 pthread_exit(0);
2946}
2947#endif //HAVE_PTHREAD_CANCEL
2948
2949
2950
2951void _textureThread(void *globalcontext)
2952{
2953 ttglobal tg = (ttglobal)globalcontext;
2954 tg->threads.loadThread = pthread_self();
2955 fwl_setCurrentHandle(tg, __FILE__, __LINE__);
2956 //ENTER_THREAD("texture loading");
2957 {
2958 ppLoadTextures p;
2959 //ttglobal tg = gglobal();
2960 p = (ppLoadTextures)tg->LoadTextures.prv;
2961
2962 //tg->LoadTextures.TextureThreadInitialized = TRUE;
2963 tg->threads.TextureThreadRunning = TRUE;
2964
2965 /* we wait forever for the data signal to be sent */
2966 for (;;) {
2967 void* elem;
2968 s_list_t *item = texitem_dequeue();
2969 elem = ml_elem(item);
2970 // printf ("textureThread - got a hit - tg %p\n",tg);
2971 if (elem == &tex_command_exit){
2972 FREE_IF_NZ(item);
2973 break;
2974 }
2975 if (tg->threads.flushing){
2976 FREE_IF_NZ(item);
2977 continue;
2978 }
2979 p->TextureParsing = TRUE;
2980 texture_process_list_item(item);
2981 p->TextureParsing = FALSE;
2982 }
2983 }
2984 printf("Ending texture load thread gracefully\n");
2985 tg->threads.TextureThreadRunning = FALSE;
2986
2987}