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