arbeit
Main Page | Namespace List | Class Hierarchy | Alphabetical List | Compound List | File List | Namespace Members | Compound Members | File Members

gliftUtil.cpp

Go to the documentation of this file.
00001 /////////////////////////////////////////////////////////////////////
00002 // 8/11/02      Aaron Lefohn    Scientific Computing and Imaging Institute
00003 // School of Computing          University of Utah
00004 //
00005 //  This library is free software; you can redistribute it and/or
00006 //  modify it under the terms of the GNU Lesser General Public
00007 //  License as published by the Free Software Foundation; either
00008 //  version 2.1 of the License, or (at your option) any later version.
00009 //
00010 //  This library is distributed in the hope that it will be useful,
00011 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013 //  Lesser General Public License for more details.
00014 //
00015 //  You should have received a copy of the GNU Lesser General Public
00016 //  License along with this library; if not, write to the Free Software
00017 //  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00018 ////////////////////////////////////////////////////////////////////////
00019 
00020 #include <util/gliftUtil.h>
00021 #include <texture/subTex.h>
00022 #include <texture/texNd.h>
00023 #include <drawable/planarQuad.h>
00024 #include <drawable/shadedPrim.h>
00025 #include <texCoordGen/winTexGen2D.h>
00026 
00027 #ifdef GLIFT_NRRD
00028 #include <nrrd.h>
00029 #endif
00030 #include <string>
00031 #include <vector>
00032 
00033 using namespace std;
00034 using namespace gutz;
00035 
00036 using namespace glift;
00037 
00038 /// The OpenGL texture unit names, indexable by texUnitNumber
00039 /// - glim.max_tex_units should be checked for number of texture
00040 ///   units available on GPU.
00041 const GLenum g_texUnitName[NUM_TEX_UNIT_NAMES] = 
00042 {       
00043    GL_TEXTURE0, 
00044       GL_TEXTURE1,  
00045       GL_TEXTURE2, 
00046       GL_TEXTURE3, 
00047       GL_TEXTURE4, 
00048       GL_TEXTURE5,
00049       GL_TEXTURE6,
00050       GL_TEXTURE7,
00051       GL_TEXTURE8,
00052       GL_TEXTURE9,
00053       GL_TEXTURE10,
00054       GL_TEXTURE11,
00055       GL_TEXTURE12,
00056       GL_TEXTURE13,
00057       GL_TEXTURE14,
00058       GL_TEXTURE15,
00059       GL_TEXTURE16, 
00060       GL_TEXTURE17, 
00061       GL_TEXTURE18, 
00062       GL_TEXTURE19, 
00063       GL_TEXTURE20, 
00064       GL_TEXTURE21,
00065       GL_TEXTURE22,
00066       GL_TEXTURE23,
00067       GL_TEXTURE24,
00068       GL_TEXTURE25,
00069       GL_TEXTURE26,
00070       GL_TEXTURE27,
00071       GL_TEXTURE28,
00072       GL_TEXTURE29,
00073       GL_TEXTURE30,
00074       GL_TEXTURE31,
00075 };
00076 
00077 // Draw pixels in 'data' into the texture, 'tex'.
00078 // Data is sent to texture via render-to-texture or by glCopySubTexImage2D(...). 
00079 // Dimensions of pixel rectangle are 'data.dim(1)' x 'data.dim(0)'
00080 // Number of color channels is determined by 'data.dim(2)'
00081 void drawPixels( const arrayw3ub& data, SingleTex* tex, GLenum drawBuffer )
00082 {
00083    drawSubPixels( vec2i(0), vec2i(0), vec2i(data.dim(1), data.dim(0)), data, tex, drawBuffer );
00084 }
00085 
00086 // Draw a 2D tile of pixels from 'data' into 'tex'
00087 // - 'dataOrig' and 'dimen' denote the data tile
00088 // - 'texOrig' and 'dimen' denote where in the texture the data is written
00089 void drawSubPixels( const vec2i& dataOrig, const vec2i& texOrig, const vec2i& dimen,
00090                    const arrayw3ub& data, SingleTex* tex, GLenum drawBuffer )
00091 {
00092    // Set the data format
00093    int numChannels = data.dim(2);
00094    GLenum glType;
00095    if(   numChannels == 1 ) { glType = GL_LUMINANCE; }
00096    else if( numChannels == 3 ) { glType = GL_RGB; }
00097    else if( numChannels == 4 ) { glType = GL_RGBA; }
00098    else {
00099       cerr << "glift::drawSubPixels(...) Error:\n\tCan only draw data with 1, 3, or 4 color channels.\n";
00100       exit(1);
00101    }
00102 
00103    // Crop data if necessary
00104    ubyte* dataPtr  = (uchar*)data.data();
00105    bool   nukeData = false;
00106 
00107 #ifdef GLIFT_NRRD
00108    if( dataOrig != 0 ) {        
00109       vec3i minV( 0, dataOrig.x, dataOrig.y);
00110       vec3i maxV( numChannels - 1, dimen.x + dataOrig.x - 1, dimen.y + dataOrig.y - 1 );
00111 
00112       int e = 0; 
00113       Nrrd* nIn = nrrdNew();
00114       Nrrd* nOut = nrrdNew();
00115       nerr( nrrdWrap(nIn, dataPtr, nrrdTypeUChar, 3, data.dim(2), data.dim(1), data.dim(0)), e );
00116       nerr( nrrdCrop(nOut, nIn, minV.v(), maxV.v()), e );
00117 
00118       dataPtr = (ubyte*)nOut->data;
00119       nrrdNix(nIn);
00120       nrrdNix(nOut);
00121       nukeData = true;
00122 
00123       if( e ) {
00124          cerr << "drawSubPixels(...) Error\n"
00125             << biffGetDone(NRRD) << endl;
00126          exit(1);
00127       }
00128    }
00129 #else
00130    if( dataOrig != 0 ) {
00131       cerr << "drawSubPixels(...) Error:\n\t"
00132          << "'dataOrig' != 0.\n"
00133          << "\tGlift must be compiled with GLIFT_NRRD defined for this to work.\n";
00134       exit(1);
00135    }
00136 #endif
00137 
00138    // Enable pbuffer if possible
00139    PBuffGlift* pbuff = tex->pbuff();
00140    if( pbuff ) {
00141       pbuff->enable();//true);
00142    }
00143 
00144    // DEBUG:
00145    /*vec4i curView;
00146    glGetIntegerv(GL_VIEWPORT, curView.v());
00147    cerr << "drawSingleTexQuad(...) curViewport = " << curView.x << ", " << curView.y << ", " << curView.z << ", " << curView.w << endl;
00148    */
00149 
00150    // Save/set drawBuffer
00151    int saveDrawBuff = 0;
00152    glGetIntegerv( GL_DRAW_BUFFER, &saveDrawBuff ); 
00153    glDrawBuffer( drawBuffer ); 
00154 
00155    // Draw pixels
00156    if( SubTex* vtex = dynamic_cast<SubTex*>(tex) ) {
00157       vec2i origin = vtex->origin() + texOrig;
00158       setColorMask( vtex->channel() ); 
00159       glRasterPos2i( origin.x, origin.y );
00160       glDrawPixels( dimen.x, dimen.y, glType, GL_UNSIGNED_BYTE, dataPtr );
00161 
00162       glRasterPos2i(0,0);
00163       setColorMask( GL_RGBA );
00164    }
00165    else {
00166       glRasterPos2i(texOrig.x, texOrig.y);
00167       glDrawPixels( dimen.x, dimen.y, glType, GL_UNSIGNED_BYTE, dataPtr );
00168       glRasterPos2i(0,0);
00169    }
00170 
00171    // Clean up...
00172    glDrawBuffer( saveDrawBuff );
00173    if( pbuff) {
00174       //pbuff->disable();
00175    }
00176    else {
00177       if( Tex2D* tex2D = dynamic_cast<Tex2D*>(tex) ) {
00178          tex2D->copyToTex2D();
00179       }
00180    }
00181 
00182    if( nukeData ) {free(dataPtr); }
00183 }
00184 
00185 // - Draw pixels in 'data' into the texture, 'tex'
00186 // - Dimensions of pixel rectangle are 'data.dim(1)' x 'data.dim(0)'
00187 // - Number of color channels is determined by 'data.dim(2)'
00188 // - Draws to the currenly set surface UNLESS 'tex->pbuff()' is a SubPBuff.
00189 //   If that is the case, the surface defined in the SubPBuff is used instead
00190 //
00191 // WARNINGS:
00192 // 1) Assumes an orthographic projection is already set up in the pbuffer context
00193 // 2) Leaves pbuffer context enabled after drawing
00194 void drawPixels( const arrayw3f& data, SingleTex* tex )
00195 {
00196    vec2i size( data.dim(1), data.dim(0) );
00197 
00198    // Set the data format
00199    int numChannels = data.dim(2);
00200    GLenum glType;
00201    if(   numChannels == 1 ) { glType = GL_LUMINANCE; }
00202    else if( numChannels == 3 ) { glType = GL_RGB; }
00203    else if( numChannels == 4 ) { glType = GL_RGBA; }
00204    else {
00205       cerr << "glift::drawSubPixels(...) Error:\n\tCan only draw data with 1, 3, or 4 color channels.\n";
00206       exit(1);
00207    }
00208 
00209    // Enable pbuffer if possible
00210    PBuffGlift* pbuff = tex->pbuff();
00211    if( pbuff ) {
00212       pbuff->enable();//true);
00213    }
00214 
00215    // Save/set drawBuffer
00216    int saveDrawBuff = 0;
00217    glGetIntegerv( GL_DRAW_BUFFER, &saveDrawBuff ); 
00218 
00219    // Set surface to subTex surface if possible
00220    if( SubPBuff* subPB = dynamic_cast<SubPBuff*>(tex->pbuff()) ) {
00221       glDrawBuffer( subPB->glSurface() ); 
00222    }
00223 
00224    // Draw pixels
00225    glRasterPos2i(0,0);
00226    glDrawPixels( size.x, size.y, glType, GL_FLOAT, (float*)data.data() );
00227    glRasterPos2i(0,0);
00228 
00229    // Reset draw buffer
00230    glDrawBuffer( saveDrawBuff );
00231 }
00232 
00233 void drawSingleTexQuad( SingleTex* tex, bool drawToPbuff )
00234 {
00235    drawSingleTexQuad(tex, tex->dimen(), drawToPbuff );
00236 }
00237 
00238 // Draw a quad to the screen that is of size 'tex->dimen()'
00239 // Will draw to 'tex->pbuff()' if 'drawToPbuff' is true
00240 // Assumes that render mode is in appropriate state ( glOrtho2D(...)? )
00241 void drawSingleTexQuad( SingleTex* tex, const vec2i& dimen, bool drawToPbuff )
00242 {
00243    // Don't try to draw if the texture is NULL!
00244    if( tex == NULL ) {
00245       return;
00246    }
00247 
00248    // Create a texture-mapped quad with the correct texture coordinates
00249    PlanarQuad* quadP = NULL;
00250 
00251    // Generate geom with either [0, winSize] texCoords or [0,1] coords
00252    if( dynamic_cast<TexRect*>(tex) ) {
00253       // Generate geometry with texCoords[0, dimen.x] x [0, dimen.y]
00254       WinTexGen2D* texGen = new WinTexGen2D();
00255       quadP = new PlanarQuad( vec2f(), vec2f((float)dimen.x, (float)dimen.y), 0.0f, true,
00256          false, texGen );
00257       delete texGen;
00258    }
00259    else {
00260       quadP = new PlanarQuad( vec2f(0.0f), vec2f( (float)dimen.x, (float)dimen.y), 0.0f, true );
00261    }
00262    Shader          shader = Shader( tex );
00263    ShadedPrim shadedQuad( quadP, &shader );
00264 
00265    PBuffGlift* pbuff = tex->pbuff();
00266    if( drawToPbuff ) {
00267       if( pbuff ) {
00268          pbuff->enable(true);
00269       }
00270 
00271       tex->tryToBindPbuff(false);
00272    }
00273 
00274    /*   glViewport(0, 0, dimen.x, dimen.y);
00275    glMatrixMode(GL_PROJECTION);
00276    glPushMatrix();
00277    glLoadIdentity();
00278    gluOrtho2D(0.0f, (float)dimen.x, 0.0f, (float)dimen.y);
00279    glMatrixMode(GL_MODELVIEW);
00280    */
00281    // Set ColorMask for this subtexture
00282    if( SubTex* subtex = dynamic_cast<SubTex*>(tex) ) {
00283       if(       subtex->channel() != GL_RGBA ) {        
00284          glClear(GL_COLOR_BUFFER_BIT);
00285          setColorMask( subtex->channel() );
00286       }
00287    }
00288 
00289    shadedQuad.draw(); //Draw textured-quad
00290    setColorMask( GL_RGBA );
00291 
00292    /*   glMatrixMode(GL_PROJECTION);
00293    glPopMatrix();
00294    glMatrixMode(GL_MODELVIEW);
00295    */
00296    if( drawToPbuff ) {
00297       tex->tryToBindPbuff(true);
00298 
00299       if( pbuff ) {
00300          pbuff->disable();
00301       }
00302    }
00303 }
00304 
00305 // Draw a quad of 'color' to lowerLeft=origin, upperRight=origin + dimen to 
00306 // 'pbuff' or current context (if pbuff==NULL)
00307 void drawColoredTile( const vec2i& origin, const vec2i& dimen, const vec2i& fullQuadDim, 
00308                      const vec3f& color, SingleTex* tex )
00309 {
00310    assert(tex);
00311    PBuffGlift* pbuff = tex->pbuff();
00312 
00313    vec2i lowerLeft  = origin;
00314    vec2i upperRight = origin + dimen;
00315 
00316    vec2f lowerLeftF( 0.0f, 0.0f );
00317    vec2f upperRightF( (float)fullQuadDim.x, (float)fullQuadDim.y);
00318 
00319    static PlanarQuad quad( lowerLeftF, upperRightF, 0.0f );
00320 
00321    if( pbuff ) {
00322       pbuff->enable();//true);
00323    }
00324    vec4i oldView;                                                       // Save old viewport
00325    glGetIntegerv(GL_VIEWPORT, oldView.v());
00326 
00327    glColor3f(color.x, color.y, color.z);        // Draw quad
00328    glViewport(lowerLeft.x, lowerLeft.y, dimen.x, dimen.y);
00329    quad.draw();
00330 
00331    // Restore state
00332    glViewport(oldView[0], oldView[1], oldView[2], oldView[3]);
00333    if( pbuff ) {
00334       //pbuff->disable();
00335    }
00336    else {
00337       if( Tex2D* tex2D = dynamic_cast<Tex2D*>(tex) ) {
00338          tex2D->copyToTex2D();
00339       }
00340    }
00341 }
00342 
00343 /////////////////////////////////////////////////////////////////////////////////////
00344 // Nrrd Utilities, only valid of complile define "GLIFT_NRRD" is set
00345 /////////////////////////////////////////////////////////////////////////////////////
00346 #ifdef GLIFT_NRRD
00347 
00348 /* /// Get header info from PNM
00349 int readDimenPNM( const char* fileName, vec3i& dimen )
00350 {
00351 // Allocate the destination nrrd
00352 Nrrd* nIn = nrrdNew();
00353 
00354 int e = nrrdLoadHeader_glift( nIn, fileName );
00355 
00356 if( !e ) {
00357 /// Is file PGM or PPM?
00358 GliftFormat format = nrrdTypePNM_glift(nIn);
00359 if( format == GliftFormatPGM ) { // PGM
00360 dimen.x = nIn->axis[0].size;
00361 dimen.y = nIn->axis[1].size;
00362 }
00363 else if( format == GliftFormatPPM ) { // PPM
00364 dimen.x = nIn->axis[1].size;
00365 dimen.y = nIn->axis[2].size;
00366 }
00367 else {
00368 cerr << "readDimenPNM(...) Error:\n"
00369 << "\tUnknown data format. Only PGM and PPM files are recognized.\n";
00370 exit(1);
00371 }
00372 }
00373 else {
00374 cerr << "readDimenPNM(...) Error:\n"
00375 << "\tCannot read " << fileName << endl;
00376 }
00377 
00378 nrrdNuke(  nIn );
00379 return e;
00380 }*/
00381 
00382 /// Get header info from 'fileName's header
00383 /// - 'fileName' can be a PGM, PPM, or any Nrrd-compatible file
00384 /// - 'dimen' will hold the size of each dimension of the dataset
00385 /// - 'dimen.size()' is the dimensionality of the dataset
00386 /// - Note 1: Ordering of the dimensions is Glift-like. 
00387 ///   - i.e. slowest index first, fastest index last (opposite of Nrrd convention)
00388 ///   - Example: y, x, color
00389 /// - Note 2: Scalar data is a dimension of size 1 (counter to Nrrd convention!) 
00390 /// - Note 3: 'isPNM' is true if file is PGM or PPM
00391 int readDimenNrrd( const char* fileName, arrayo1i& dimen, bool& isPNM)
00392 {       
00393    // Allocate the destination nrrd
00394    Nrrd* nIn = nrrdNew();
00395 
00396    int e = gliftNrrdLoadHeader( nIn, fileName );
00397 
00398    if( !e ) {
00399       /// Is file PGM or PPM?
00400       GliftFormat format = gliftNrrdTypePNM(nIn);
00401 
00402       if(       format == GliftFormatPGM ) {     // PGM
00403          dimen = arrayo1i( 3, (int)0);
00404          dimen[0] = nIn->axis[1].size;   // y
00405          dimen[1] = nIn->axis[0].size;   // x
00406          dimen[2] = 1;                                   // greyscale value
00407          isPNM = true;
00408       }
00409       else if( format == GliftFormatPPM ) {// PPM
00410          dimen = arrayo1i( 3, (int)0 );
00411          dimen[0] = nIn->axis[2].size;
00412          dimen[1] = nIn->axis[1].size;
00413          dimen[2] = 3;
00414          isPNM = true;
00415       }
00416       else {    // Any dimension
00417          dimen = arrayo1i( nIn->dim, (int)0 );
00418 
00419          // Set each dimension...in slowest to fastest order
00420          for(int i=0; i < dimen.size(); i++) {
00421             int nrrdI = i;//dimen.size() - i - 1;// Nrrd is fastest to slowest order
00422             dimen[i] = nIn->axis[ nrrdI ].size;
00423          }
00424          isPNM = false;
00425       }
00426    }
00427    else { 
00428       cerr << "readDimenNrrd(...) Error:\n"
00429          << "\tCannot read " << fileName
00430          << biffGetDone(NRRD) << endl;
00431    }
00432 
00433    nrrdNuke(  nIn );
00434    return e;
00435 }
00436 
00437 /// Read the PNM file, 'inFileName', sets 'width' and 'height' based on input file,
00438 /// and returns image in 'rgbaImage'
00439 /// - If file is PGM, all 'channels' have the replicated, scalar value
00440 /// - If file is PPM, up to 'channels' are output.
00441 /// - If format is RGBA and file is PPM, alpha channel is set to 255
00442 /// - If 'makeScalar' is true, PPMs are averaged down to scalar value (replicated as necessary)
00443 ///   If false, RGB data is retained and alpha channel is padded with 255 if necessary
00444 /// - Returns nrrd error code (0 is alles klar)
00445 int loadPNM( GLenum channels, bool makeScalar, const char* inFileName, gutz::arrayo3ub& data )
00446 {       
00447    int e = 0;//Nrrd error accumulator
00448    int numChannels = getNumChannels(channels);
00449    vec3i newDim;
00450    vec3i minV(0,0,0);
00451    vec3i maxV;
00452 
00453    ///Load the file
00454    Nrrd* nIn = nrrdNew();
00455    nerr( nrrdLoad(nIn, inFileName), e );
00456 
00457    if( e ) { //Die gracefully if there was an error
00458       cerr << "loadPNM(...) Error:\n"
00459          << biffGetDone(NRRD) << endl;
00460       return e;
00461    }
00462 
00463    // Figure out what kind of file it was
00464    GliftFormat format = gliftNrrdTypePNM(nIn);
00465 
00466    // Correctly format data and put result in 'nOut'
00467    Nrrd* nOut = nrrdNew();      //The correctly-formatted data
00468    if( format == GliftFormatPGM ) {
00469       newDim.x = nIn->axis[0].size;
00470       newDim.y = nIn->axis[1].size;
00471       newDim.z = numChannels;
00472       maxV = vec3i( numChannels-1, newDim.x-1, newDim.y-1 );
00473 
00474       // Replicate scalar value out to 'numChannels' of color
00475       Nrrd* nT1 = nrrdNew();
00476       nerr( nrrdFlip(nT1, nIn, 1), e );/// Flip the y-axis for OpenGL
00477       nerr( nrrdPad( nOut, nT1, minV.v(), maxV.v(), nrrdBoundaryBleed ), e );
00478       nrrdNuke( nT1 );
00479    }
00480    else if( format == GliftFormatPPM ) {
00481       newDim.x = nIn->axis[1].size;
00482       newDim.y = nIn->axis[2].size;
00483       newDim.z = numChannels;
00484       maxV = vec3i( numChannels-1, newDim.x-1, newDim.y-1 );
00485 
00486       Nrrd* nT1 = nrrdNew();
00487       nerr( nrrdFlip(nT1, nIn, 2), e );/// Flip the y-axis for OpenGL
00488 
00489       if( makeScalar ) {
00490          ///TODO: This should be luminance calc instead of average!!!
00491          int axis = 0;//Color axis
00492          nerr( nrrdProject( nOut, nT1, axis, nrrdMeasureMean), e );
00493          nerr( nrrdReshape( nOut, nOut, 3, 1, newDim.x, newDim.y), e );//Reshape as 3D
00494 
00495          if( numChannels > 1 ) { /// Replicate scalar in all channels
00496             nerr( nrrdPad( nT1, nOut, minV.v(), maxV.v(), nrrdBoundaryBleed ), e );
00497             nerr( nrrdCopy( nOut, nT1 ), e );
00498          }
00499       }
00500       else {
00501          if( numChannels == 1 || numChannels == 2 ) {//Data reductions
00502             nrrdCrop(nOut, nT1, minV.v(), maxV.v() );
00503          }
00504          else if( numChannels == 3 )  {
00505             nerr( nrrdCopy( nOut, nT1 ), e);
00506          }
00507          else if( numChannels == 4 ) { /// Pad alpha channel with 255
00508             nerr( nrrdPad( nOut, nT1, minV.v(), maxV.v(), nrrdBoundaryPad, 255 ), e );
00509          }
00510       }
00511 
00512       nrrdNuke( nT1 );
00513    }
00514 
00515    if( !e ) {//If alles klar...
00516       //dimen = newDim;
00517 
00518       /// The image.
00519       // TODO: Use a "transfer" here!!
00520       data.transfer( newDim.y, newDim.x, newDim.z, (uchar*)nOut->data, false );
00521    }
00522    else {
00523       cerr << "loadPNM(...) Error\n"
00524          << biffGetDone(NRRD) << endl
00525          << "\tError processing input. No data returned.\n";
00526    }
00527 
00528    /// Clean up
00529    nrrdNix( nOut );
00530    nrrdNix( nIn );
00531 
00532    return e;
00533 }
00534 
00535 /// Read the Nrrd file, 'inFileName'.
00536 /// - WARNING: 'inFileName' MUST be a scalar volume or this fcn will return an error
00537 ///             and 'dimen' and 'data' will be unaltered.
00538 /// - 'channels' is GL_RED, GL_RGB, GL_RGBA, etc and sets how many times the scalar
00539 ///    voxel value is copied.
00540 /// - 'dimen' contains the size of each dimension of the volume.
00541 /// - 'data'  contains the scalar volume data. Accessors are: z, y, x, channel
00542 int loadNrrd3D( GLenum channels, const char* inFileName, arrayo4ub& data )
00543 {
00544    int e = 0;//Nrrd error accumulator
00545    int numChannels = getNumChannels(channels);
00546 
00547    // Load the file
00548    Nrrd* nIn = nrrdNew();
00549    nerr( nrrdLoad(nIn, inFileName), e );
00550 
00551    if( e ) {                      // Die gracefully if there was an error
00552       cerr << "loadNrrd3D(...) Error:\n"
00553          << biffGetDone(NRRD) << endl;
00554       return e;
00555    }
00556 
00557    if( nIn->dim != 3 &&
00558       !(nIn->dim == 4 && nIn->axis[3].size == 1) ) { // Die gracefully if dataset is not scalar volume
00559          cerr << "loadNrrd3D(...) Error:\n"
00560             << "\t" << inFileName << " is not a scalar volume file.\n";
00561          return e;
00562       }
00563 
00564       if( nIn->type != nrrdTypeUChar ) { // Die gracefully if dataset is not of uchar type
00565          cerr << "loadNrrd3D(...) Error:\n"
00566             << "\t" << inFileName << " is not in unsigned byte format.\n";
00567          return e;
00568       }
00569 
00570 
00571       // Set the dimensions and min/max vectors
00572       vec4i newDim;
00573       newDim.x = nIn->axis[0].size;
00574       newDim.y = nIn->axis[1].size;
00575       newDim.z = nIn->axis[2].size;
00576       newDim.w = numChannels;
00577 
00578       vec4i minV( 0, 0, 0, 0);
00579       vec4i maxV( numChannels-1, newDim.x-1, newDim.y-1, newDim.z-1 );
00580 
00581       /// Correctly format data and put result in 'nOut'
00582       Nrrd* nOut = nrrdNew();   // The correctly-formatted data
00583       Nrrd* nT1  = nrrdNew();   // A temporary Nrrd
00584 
00585       nerr( nrrdFlip(nT1, nIn, 1), e );// Flip the y-axis for OpenGL (TODO: Is this necessary?)
00586 
00587       // Replicate scalar value out to 'numChannels' of color
00588       int newDimen = 4;
00589       nerr( nrrdReshape( nIn, nT1, newDimen, 1, newDim.x, newDim.y, newDim.z ), e ); // Reshape to 4D   
00590       nerr( nrrdPad( nOut, nIn, minV.v(), maxV.v(), nrrdBoundaryBleed ), e );
00591 
00592       nrrdNuke( nT1 );
00593 
00594       if( !e ) {//If alles klar...
00595          //dimen = newDim;
00596 
00597          // The volume. Transfer the data to the data array 
00598          // - Nuke whatever is in 'data' and shallow copy in the new data.
00599          data.transfer( newDim.z, newDim.y, newDim.x, newDim.w, (uchar*)nOut->data, false );
00600       }
00601       else {
00602          cerr << "loadNrrd3D(...) Error\n"
00603             << biffGetDone(NRRD) << endl
00604             << "\tError processing input. No data returned.\n";
00605       }
00606 
00607       /// Clean up
00608       nrrdNix( nOut );
00609       nrrdNuke( nIn );
00610 
00611       return e;
00612 }
00613 
00614 /// Read framebuffer to CPU memory and write to file.
00615 void writeFBtoPPM( GLenum channels, const vec2i& origin, const vec2i& dimen, 
00616                   char* baseFileName, bool sepChannels )
00617 {
00618    int numChannels = getNumChannels(channels);
00619    long numElts = dimen.x * dimen.y * numChannels;
00620    GLubyte* tempImage = new GLubyte[numElts];
00621    glReadPixels(0, 0, dimen.x, dimen.y, channels, GL_UNSIGNED_BYTE, tempImage );
00622 
00623    arrayw3ub data( dimen.y, dimen.x, numChannels, tempImage );
00624    writePNM( baseFileName, data, sepChannels );
00625    delete tempImage;
00626 }
00627 
00628 /// Write to a pgm or ppm file called 'baseFileName.pgm' or 'baseFileName.ppm'
00629 /// - 'data' has dimens: height, width, numChannels
00630 /// - PGM is written if 'numChannels' == 1, otherwise a PPM is written
00631 /// - If 'numChannels' == 4, the last channel (alpha) is written to a separate pgm.
00632 void writePNM( char* baseFileName, const arrayw3ub& data, bool sepChannels )
00633 {
00634    ubyte* imageData = (ubyte*)data.data();
00635    vec2i  dimen( data.dim(1), data.dim(0) );
00636    int    numChannels = data.dim(2);
00637    int     numFiles = 1;
00638 
00639    string baseName( baseFileName );
00640    vector<string> fileName(numChannels);
00641 
00642    vector<Nrrd*> nOut(numChannels,(Nrrd*)NULL);
00643    Nrrd* nIn = nrrdNew();
00644    int e = 0;                     //Nrrd error code
00645 
00646    /// Initialize the output nrrds
00647    int i;
00648    for(i=0; i < numChannels; i++) {
00649       nOut[i] = nrrdNew();
00650    }
00651 
00652    /// Partion the data appropriately
00653    if( numChannels == 1 ) {
00654       fileName[0] = baseName  + ".pgm";
00655 
00656       /// Wrap the data in a nrrd
00657       nerr( nrrdWrap( nIn, imageData, nrrdTypeUChar, 2, dimen.x, dimen.y), e );// "2" is for "2D"
00658       nerr( nrrdFlip( nOut[0], nIn, 1 ), e );   //Flip y-axis
00659       nrrdNix( nIn );
00660    }
00661    else {
00662       Nrrd* nT1 = nrrdNew();
00663       nerr( nrrdWrap( nT1, imageData, nrrdTypeUChar, 3, numChannels, dimen.x, dimen.y ), e);// "3" is for "3D"
00664       nerr( nrrdFlip( nIn, nT1, 2 ), e );               //Flip y-axis
00665       nrrdNix( nT1 ); // WARNING: Do NOT nuke here to protect "imageData" memory!
00666 
00667       if( numChannels == 2 ) {
00668          /// Wrap the data in a nrrd
00669 
00670          if( sepChannels ) {
00671             numFiles = 2;
00672             fileName[0] = baseName + "R.pgm";
00673             fileName[1] = baseName + "G.pgm";
00674 
00675             int axis = 0;//Color axis
00676             nerr( nrrdSlice( nOut[0], nIn, axis, 0 ), e );//Red channel
00677             nerr( nrrdSlice( nOut[1], nIn, axis, 1 ), e );//Green channel
00678          }
00679          else {
00680             numFiles = 1;
00681             fileName[0] = baseName + ".ppm";
00682 
00683             /// Pad the BLUE channel with 0's and write a PPM
00684             vec3i minV(0,0,0);
00685             vec3i maxV( 2, dimen.x-1, dimen.y-1 ); 
00686             int padVal = 0;
00687 
00688             nerr( nrrdPad( nOut[0], nIn, minV.v(), maxV.v(), nrrdBoundaryPad, padVal), e );
00689          }
00690       }
00691       else if( numChannels == 3 ) {
00692          if( sepChannels ) {
00693             numFiles = 3;
00694             fileName[0] = baseName + "R.pgm";
00695             fileName[1] = baseName + "G.pgm";
00696             fileName[2] = baseName + "B.pgm";
00697 
00698             int axis = 0;//Color axis
00699             nerr( nrrdSlice( nOut[0], nIn, axis, 0 ), e );//Red channel
00700             nerr( nrrdSlice( nOut[1], nIn, axis, 1 ), e );//Green channel
00701             nerr( nrrdSlice( nOut[2], nIn, axis, 2 ), e );//Blue channel
00702          }
00703          else {
00704             numFiles = 1;
00705             fileName[0] = baseName + ".ppm";
00706             nOut[0] = nIn;
00707          }
00708       }
00709       else if( numChannels == 4 ) {
00710          if( sepChannels ) {
00711             numFiles = 4;
00712             fileName[0] = baseName + "R.pgm";
00713             fileName[1] = baseName + "G.pgm";
00714             fileName[2] = baseName + "B.pgm";
00715             fileName[3] = baseName + "A.pgm";
00716 
00717             int axis = 0;//Color axis
00718             nerr( nrrdSlice( nOut[0], nIn, axis, 0 ), e );//Red channel
00719             nerr( nrrdSlice( nOut[1], nIn, axis, 1 ), e );//Green channel
00720             nerr( nrrdSlice( nOut[2], nIn, axis, 2 ), e );//Blue channel
00721             nerr( nrrdSlice( nOut[3], nIn, axis, 3 ), e );//Alpha channel
00722          }
00723          else {
00724             numFiles = 2;
00725             fileName[0] = baseName + "RGB.ppm";
00726             fileName[1] = baseName + "A.pgm";
00727 
00728             /// Crop out the alpha channel to make an RGB ppm file
00729             vec3i minV(0,0,0);
00730             vec3i maxV( 2, dimen.x-1, dimen.y-1 ); 
00731 
00732             nerr( nrrdCrop( nOut[0], nIn, minV.v(), maxV.v() ), e );
00733 
00734             /// Slice out alpha channel for pgm
00735             int axis = 0;//Color axis
00736             nerr( nrrdSlice( nOut[1], nIn, axis, 3 ), e );
00737          }
00738       }
00739       else {
00740          cerr << "writePNM(...) Error\n"
00741             << "\tChannels cannot equal " << numChannels << endl
00742             << "\tNo file written.\n";
00743       }
00744 
00745       nrrdNuke( nIn );//Okay to nuke here because it is new data
00746    }
00747 
00748    /// Process Accumulated errors
00749    if(e) { 
00750       cerr << "writePNM(...) Error\n" 
00751          << biffGetDone(NRRD) << endl; 
00752    }
00753 
00754    /// Write the output
00755    for(i=0; i < numFiles; i++) {
00756       nrrdSave( fileName[i].c_str(), nOut[i], NULL );
00757    }
00758 
00759    /// Release nrrd memory 
00760    for(i=0; i < numChannels; i++) {
00761       if( nOut[i] ) {
00762          nrrdNuke( nOut[i] ); //Duke nuke 'em (everything)
00763       }
00764    }
00765 }
00766 
00767 /// Write to a nrrd file called 'baseFileName.nrrd'
00768 /// - 'data' has dimens: numSlabs, height, width, numChannels
00769 void writeNrrd3D( const char* baseFileName, const arrayw4ub& data )
00770 {
00771    vec3i dimen( data.dim(2), data.dim(1), data.dim(0) );
00772    int   numChannels = data.dim(3);
00773 
00774    Nrrd* nWrap = nrrdNew();
00775    int e = nrrdWrap( nWrap, (ubyte*)data.data(), nrrdTypeUChar, 
00776       4, dimen.x, dimen.y, dimen.z, numChannels );
00777 
00778    Nrrd* nOut = nrrdNew();
00779    nerr( nrrdFlip( nOut, nWrap, 1 ), e );
00780 
00781    /// Process Accumulated errors
00782    if(e) { 
00783       cerr << "writeNrrd3D(...) Error\n" 
00784          << biffGetDone(NRRD) << endl; 
00785    }
00786 
00787    /// Write the output
00788    string fileName = baseFileName + string(".nrrd");
00789    nerr( nrrdSave( fileName.c_str(), nOut, NULL ), e );
00790 
00791    if( e ) {
00792       cerr << "gliftUtil::writeNrrd3D(...) Error : \n\t"
00793          << "Cannot write file \"" << fileName << "\" because\n\t"
00794          << biffGetDone(NRRD) << endl;
00795    }
00796 
00797    // Clean up
00798    nrrdNix( nWrap );
00799    nrrdNuke(nOut  );
00800 }
00801 
00802 // Take a scalar volume and pack into an RGB volume
00803 // - sizeZ is the number of slabs in 'data' to process 
00804 // - sizeZ MUST be a multiple of 3!!!
00805 // - Returned data MUST be deleted to avoid memory leaks :(
00806 arrayw4ub packScalarToRGB( const arrayw4ub& data, int sizeZ )
00807 {
00808    assert( sizeZ <= data.dim(0) );
00809    assert( sizeZ % 3 == 0 );
00810    assert( data.dim(3) == 1 );
00811 
00812    int   e = 0;                 // Nrrd error code
00813    int   numChannels = 1;
00814    vec3i dim( data.dim(2), data.dim(1), sizeZ );
00815    int   zPacked = sizeZ/3;
00816 
00817    // Pack scalar slabs into RGB
00818    // 1) Wrap phiData in nIn
00819    Nrrd* nIn = nrrdNew();
00820    nerr( nrrdWrap( nIn, (ubyte*)data.data(), nrrdTypeUChar,
00821       4, numChannels, dim.x, dim.y, dim.z ), e );
00822 
00823    // 2) Crop out every 3 slices into a single, a 3-slice volume with the color and z axes swapped
00824    Nrrd** nSlab = new Nrrd*[zPacked];
00825    Nrrd*  nOut  = nrrdNew();
00826 
00827    vec4i minV( 0, 0, 0, 0 );
00828    vec4i maxV( 0, dim.x-1, dim.y-1, 2 );
00829    int z;
00830    for(z=0; z < zPacked; z++) {
00831       nSlab[z] = nrrdNew();
00832       minV[3] = z*3;
00833       maxV[3] = (z+1)*3 - 1;
00834       nerr( nrrdCrop( nOut, nIn, minV.v(), maxV.v() ), e );
00835       nerr( nrrdAxesSwap( nSlab[z], nOut, 0, 3 ), e );
00836    }
00837 
00838    // 3) Rebuild volume
00839    int joinAxis = 3;
00840    nerr( nrrdJoin( nOut, nSlab, zPacked, joinAxis, false ), e );
00841 
00842    // 4) Create arrayWrap
00843    arrayw4ub outData( zPacked, dim.y, dim.x, 3, (ubyte*)nOut->data );
00844 
00845    // Clean up
00846    for(z=0; z < zPacked; z++) {
00847       nrrdNuke(nSlab[z]);
00848    }
00849    delete nSlab;
00850    nrrdNix(nIn);        // The input data
00851    nrrdNix(nOut);       // The output data
00852 
00853    if(e) {
00854       cerr << "SolverRLS::initPhiDenseMem(...) Nrrd Error:\n\t"
00855          << biffGetDone(NRRD) << endl;
00856       exit(1);
00857    }
00858 
00859    return outData;
00860 }
00861 
00862 // Take an RGB-packed, scalar volume and unpack it into scalar slabs
00863 // - Returned data MUST be deleted to avoid memory leaks :(
00864 //       TODO: This could be fixed with a transfer operation?
00865 // - 'unpackZ' is the maxZ value for the unpacked data
00866 gutz::arrayw4ub unpackRGBToScalar( const gutz::arrayw4ub& data, int unpackZ )
00867 {
00868    assert( unpackZ <= data.dim(0)*3 );
00869    assert( data.dim(3) == 3 );
00870 
00871    int   e = 0;                 // Nrrd error code
00872    int   numPackChannels = 3;
00873    vec3i packDim( data.dim(2), data.dim(1), data.dim(0) );
00874    vec3i upackDim(data.dim(2), data.dim(2), unpackZ );
00875 
00876    // Pack scalar slabs into RGB
00877    // 1) Wrap phiData in nIn
00878    Nrrd* nIn = nrrdNew();
00879    nerr( nrrdWrap( nIn, (ubyte*)data.data(), nrrdTypeUChar,
00880       4, numPackChannels, packDim.x, packDim.y, packDim.z ), e );
00881 
00882    // 2) Crop out every RGB slice into a single, a 3-slice volume with the color and z axes swapped
00883    Nrrd** nSlab = new Nrrd*[packDim.z];
00884    Nrrd*  nOut  = nrrdNew();
00885 
00886    vec4i minV( 0, 0, 0, 0 );
00887    vec4i maxV( 2, packDim.x-1, packDim.y-1, 1 );
00888    int z;
00889    for(z=0; z < packDim.z; z++) {
00890       nSlab[z] = nrrdNew();
00891       minV[3] = z;
00892       maxV[3] = z;
00893       nerr( nrrdCrop( nOut, nIn, minV.v(), maxV.v() ), e );
00894       nerr( nrrdAxesSwap( nSlab[z], nOut, 0, 3 ), e );
00895    }
00896 
00897    // 3) Rebuild volume
00898    int joinAxis = 3;
00899    nerr( nrrdJoin( nOut, nSlab, packDim.z, joinAxis, false ), e );
00900    int newZ = nOut->axis[3].size;
00901 
00902    // 4) Crop top slabs if necessary
00903    if( upackDim.z < newZ ) {
00904       minV = vec4i(0,0,0,0);
00905       maxV = vec4i(0, upackDim.x-1, upackDim.y-1, upackDim.z-1);
00906       nerr( nrrdCrop( nSlab[0], nOut, minV.v(), maxV.v() ), e );
00907       nerr( nrrdCopy( nOut, nSlab[0]), e);
00908    }
00909 
00910    // 4) Create arrayWrap
00911    arrayw4ub outData( upackDim.z, upackDim.y, upackDim.x, 1, (ubyte*)nOut->data );
00912 
00913    // Clean up
00914    for(z=0; z < packDim.z; z++) {
00915       nrrdNuke(nSlab[z]);
00916    }
00917    delete nSlab;
00918    nrrdNix(nIn);        // The input data
00919    nrrdNix(nOut);       // The output data
00920 
00921    if(e) {
00922       cerr << "SolverRLS::initPhiDenseMem(...) Nrrd Error:\n\t"
00923          << biffGetDone(NRRD) << endl;
00924       exit(1);
00925    }
00926 
00927    return outData;
00928 }
00929 
00930 
00931 #endif //GLIFT_NRRD
00932 
00933 

Send questions, comments, and bug reports to:
jmk