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