image.cpp

00001 
00002 /* Copyright (c) 2006-2009, Stefan Eilemann <eile@equalizergraphics.com>
00003  *
00004  * This library is free software; you can redistribute it and/or modify it under
00005  * the terms of the GNU Lesser General Public License version 2.1 as published
00006  * by the Free Software Foundation.
00007  *  
00008  * This library is distributed in the hope that it will be useful, but WITHOUT
00009  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
00010  * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
00011  * details.
00012  * 
00013  * You should have received a copy of the GNU Lesser General Public License
00014  * along with this library; if not, write to the Free Software Foundation, Inc.,
00015  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
00016  */
00017 
00018 #include "image.h"
00019 
00020 #include "compressor.h"
00021 #include "frame.h"
00022 #include "frameBufferObject.h"
00023 #include "frameData.h"
00024 #include "global.h"
00025 #include "pixel.h"
00026 #include "pluginRegistry.h"
00027 #include "log.h"
00028 #include "windowSystem.h"
00029 
00030 #include <eq/base/memoryMap.h>
00031 #include <eq/base/omp.h>
00032 #include <eq/net/node.h>
00033 
00034 #include <fstream>
00035 
00036 #ifdef WIN32
00037 #  include <malloc.h>
00038 #  define bzero( ptr, size ) memset( ptr, 0, size );
00039 #else
00040 #  include <alloca.h>
00041 #endif
00042 
00043 
00044 using namespace std;
00045 
00046 namespace eq
00047 {
00048 
00049 
00050 Image::Image()
00051         : _glObjects( 0 )
00052         , _type( Frame::TYPE_MEMORY )
00053 {
00054     reset();
00055 }
00056 
00057 Image::~Image()
00058 {
00059 }
00060 
00061 void Image::reset()
00062 {
00063     _usePBO = false;
00064     _ignoreAlpha = false;
00065     setPixelViewport( PixelViewport( ));
00066 }
00067 
00068 void Image::flush()
00069 {
00070     _color.memory.flush();
00071     _depth.memory.flush();
00072     _color.texture.flush();
00073     _depth.texture.flush();
00074 }
00075 
00076 uint32_t Image::getDepth( const Frame::Buffer buffer ) const
00077 {
00078     return getNumChannels( buffer ) * getChannelSize( buffer );
00079 }
00080 
00081 uint8_t Image::getNumChannels( const Frame::Buffer buffer ) const
00082 {
00083     switch( getFormat( buffer ) )
00084     {
00085         case GL_RGBA:
00086         case GL_RGBA8:
00087         case GL_BGRA:
00088             return 4;
00089 
00090         case GL_RGB:
00091         case GL_BGR:
00092             return 3;
00093 
00094         case GL_DEPTH_COMPONENT:
00095         case GL_DEPTH_STENCIL_NV:
00096             return 1;
00097 
00098         default :
00099             EQWARN << "Unknown number of components for format "
00100                    << getFormat( buffer ) << " of buffer " << buffer << endl;
00101             EQUNIMPLEMENTED;
00102     } 
00103     return 0;
00104 }
00105 
00106 uint8_t Image::getChannelSize( const Frame::Buffer buffer ) const
00107 {
00108     switch( getType( buffer ))
00109     {
00110         case GL_UNSIGNED_BYTE:
00111         case GL_UNSIGNED_INT_8_8_8_8_REV:
00112             return 1;
00113 
00114         case GL_HALF_FLOAT: 
00115             return 2;
00116 
00117         case GL_FLOAT:
00118         case GL_UNSIGNED_INT:
00119         case GL_UNSIGNED_INT_24_8_NV:
00120             return 4;
00121 
00122         default :
00123             EQUNIMPLEMENTED;
00124     }
00125     return 0;
00126 }
00127 
00128 uint32_t Image::getInternalTextureFormat( const Frame::Buffer which ) const
00129 {
00130     switch( getFormat( which ))
00131     {
00132         case GL_RGBA:
00133         case GL_RGBA8:
00134         case GL_BGRA:
00135             return GL_RGBA;
00136 
00137         case GL_RGB:
00138         case GL_BGR:
00139             return GL_RGB;
00140 
00141         case GL_DEPTH_STENCIL_NV:
00142         case GL_DEPTH_COMPONENT:
00143             return GL_DEPTH_COMPONENT;
00144 
00145         default :
00146             EQWARN << "Unknown format " << getFormat( which ) << " of buffer "
00147                    << which << endl;
00148             EQUNIMPLEMENTED;
00149             return GL_RGBA;
00150     }
00151 }
00152 
00153 void Image::setFormat( const Frame::Buffer buffer, const uint32_t format )
00154 {
00155     Memory& memory = _getAttachment( buffer ).memory;
00156     if( memory.format == format )
00157         return;
00158 
00159     memory.format = format;
00160     memory.state = Memory::INVALID;
00161     allocCompressor( buffer, 0 );
00162 
00163     _getAttachment( buffer ).texture.setFormat( format );
00164 }
00165 
00166 void Image::setType( const Frame::Buffer buffer, const uint32_t type )
00167 {
00168     Memory& memory = _getAttachment( buffer ).memory;
00169     if( memory.type == type )
00170         return;
00171 
00172     memory.type = type;
00173     memory.state = Memory::INVALID;
00174     allocCompressor( buffer, 0 );
00175 }
00176 
00177 uint32_t Image::getFormat( const Frame::Buffer buffer ) const
00178 {
00179     const Memory& memory = _getAttachment( buffer ).memory;
00180     EQASSERT( memory.format );
00181     return memory.format;
00182 }
00183 
00184 uint32_t Image::getType( const Frame::Buffer buffer ) const
00185 {
00186     const Memory& memory = _getAttachment( buffer ).memory;
00187     EQASSERT( memory.type );
00188     return memory.type;
00189 }
00190 
00191 uint32_t Image::_getCompressorTokenType( const Frame::Buffer buffer ) const
00192 {
00193     switch( getNumChannels( buffer ))
00194     {
00195         case 4:
00196             switch( getType( buffer ))
00197             {
00198                 case GL_UNSIGNED_BYTE:
00199                     return EQ_COMPRESSOR_DATATYPE_4_BYTE;
00200                 case GL_HALF_FLOAT:
00201                     return EQ_COMPRESSOR_DATATYPE_4_HALF_FLOAT;
00202                 case GL_FLOAT:
00203                     return EQ_COMPRESSOR_DATATYPE_4_FLOAT;
00204                 default:
00205                     EQUNIMPLEMENTED;
00206             }
00207 
00208         case 3:
00209             switch( getType( buffer ))
00210             {
00211                 case GL_UNSIGNED_BYTE:
00212                     return EQ_COMPRESSOR_DATATYPE_3_BYTE;
00213                 case GL_HALF_FLOAT:
00214                     return EQ_COMPRESSOR_DATATYPE_3_HALF_FLOAT;
00215                 case GL_FLOAT:
00216                     return EQ_COMPRESSOR_DATATYPE_3_FLOAT;
00217                 default:
00218                     EQUNIMPLEMENTED;
00219             }
00220 
00221         case 1:
00222             switch( getType( buffer ))
00223             {
00224                 case GL_FLOAT:
00225                     return EQ_COMPRESSOR_DATATYPE_FLOAT;
00226                 case GL_UNSIGNED_INT:
00227                     EQASSERT( buffer == Frame::BUFFER_DEPTH );
00228                     return EQ_COMPRESSOR_DATATYPE_UNSIGNED;
00229                 default:
00230                     EQUNIMPLEMENTED;
00231             }
00232 
00233         default:
00234             EQUNIMPLEMENTED;
00235     };
00236 
00237     return 0;
00238 }
00239 
00240 bool Image::_canIgnoreAlpha( const Frame::Buffer buffer ) const
00241 { 
00242     return ( buffer == Frame::BUFFER_COLOR && _ignoreAlpha &&
00243              getNumChannels( Frame::BUFFER_COLOR ) == 4 );
00244 }
00245 
00246 std::vector< uint32_t > Image::findCompressors( const Frame::Buffer buffer )
00247     const
00248 {
00249     const uint32_t tokenType = _getCompressorTokenType( buffer );
00250     EQINFO << "Searching compressors for token type " << tokenType << std::endl;
00251 
00252     const PluginRegistry& registry = Global::getPluginRegistry();
00253     const CompressorVector& compressors = registry.getCompressors();
00254     std::vector< uint32_t > names;
00255 
00256     for( CompressorVector::const_iterator i = compressors.begin();
00257          i != compressors.end(); ++i )
00258     {
00259         const Compressor* compressor = *i;
00260         EQINFO << "Searching in DSO " << (void*)compressor << std::endl;
00261 
00262         const CompressorInfoVector& infos = compressor->getInfos();
00263         
00264         for( CompressorInfoVector::const_iterator j = infos.begin();
00265              j != infos.end(); ++j )
00266         {
00267             const EqCompressorInfo& info = *j;
00268             if( info.tokenType == tokenType )
00269                 names.push_back( info.name );
00270         }
00271     }
00272 
00273     return names;
00274 }
00275 
00276 uint32_t Image::_getCompressorName( const Frame::Buffer buffer ) const
00277 {
00278     const uint32_t tokenType = _getCompressorTokenType( buffer );
00279     uint32_t name = EQ_COMPRESSOR_NONE;
00280     float ratio = 1.0f;
00281 
00282     EQINFO << "Searching compressor for token type " << tokenType << std::endl;
00283 
00284     const PluginRegistry& registry = Global::getPluginRegistry();
00285     const CompressorVector& compressors = registry.getCompressors();
00286     for( CompressorVector::const_iterator i = compressors.begin();
00287          i != compressors.end(); ++i )
00288     {
00289         const Compressor* compressor = *i;
00290         const CompressorInfoVector& infos = compressor->getInfos();
00291 
00292         EQINFO << "Searching in DSO " << (void*)compressor << std::endl;
00293         
00294         for( CompressorInfoVector::const_iterator j = infos.begin();
00295              j != infos.end(); ++j )
00296         {
00297             const EqCompressorInfo& info = *j;
00298             if( info.tokenType != tokenType )
00299                 continue;
00300 
00301             float infoRatio = info.ratio;
00302             if( _canIgnoreAlpha( buffer ) && 
00303                 ( info.capabilities & EQ_COMPRESSOR_IGNORE_MSE ))
00304             {
00305                 infoRatio *= .75f;
00306             }
00307 
00308             if( ratio > infoRatio ) // TODO: be smarter
00309             {
00310                 name = info.name;
00311                 ratio = infoRatio;
00312             }
00313         }
00314     }
00315 
00316     EQINFO << "Selected compressor " << name << std::endl;
00317     return name;
00318 }
00319 
00320 bool Image::hasAlpha() const
00321 {
00322     switch( getFormat( Frame::BUFFER_COLOR ))
00323     {
00324         case GL_RGBA16F:
00325         case GL_RGBA32F:
00326         case GL_RGBA:
00327         case GL_RGBA8:
00328         case GL_BGRA:
00329             return true;
00330 
00331         default:
00332             return false;
00333     }
00334 }
00335 
00336 bool Image::hasData( const Frame::Buffer buffer ) const
00337 {
00338     if( _type == Frame::TYPE_MEMORY )
00339         return hasPixelData( buffer );
00340 
00341     EQASSERT( _type == Frame::TYPE_TEXTURE );
00342     return hasTextureData( buffer );
00343 }
00344 
00345 void Image::enableAlphaUsage()
00346 {
00347     if( !_ignoreAlpha )
00348         return;
00349 
00350     _ignoreAlpha = false;
00351     _color.memory.isCompressed = false;
00352     _depth.memory.isCompressed = false;
00353 }    
00354 
00355 void Image::disableAlphaUsage()
00356 {
00357     if( _ignoreAlpha )
00358         return;
00359 
00360     _ignoreAlpha = true;
00361     _color.memory.isCompressed = false;
00362     _depth.memory.isCompressed = false;
00363 }    
00364 
00365 bool Image::hasTextureData( const Frame::Buffer buffer ) const
00366 {
00367     return getTexture( buffer ).isValid(); 
00368 }
00369 
00370 const Texture& Image::getTexture( const Frame::Buffer buffer ) const
00371 {
00372     return _getAttachment( buffer ).texture;
00373 }
00374 
00375 const uint8_t* Image::getPixelPointer( const Frame::Buffer buffer ) const
00376 {
00377     EQASSERT( hasPixelData( buffer ));
00378     return _getAttachment( buffer ).memory.pixels.getData();
00379 }
00380 
00381 uint8_t* Image::getPixelPointer( const Frame::Buffer buffer )
00382 {
00383     EQASSERT( hasPixelData( buffer ));
00384     return _getAttachment( buffer ).memory.pixels.getData();
00385 }
00386 
00387 const Image::PixelData& Image::getPixelData( const Frame::Buffer buffer ) const
00388 {
00389     EQASSERT(hasPixelData(buffer));
00390     return _getAttachment( buffer ).memory;
00391 }
00392 
00393 void Image::startReadback( const uint32_t buffers, const PixelViewport& pvp,
00394                            const Zoom& zoom, Window::ObjectManager* glObjects )
00395 {
00396     EQASSERT( glObjects );
00397     EQASSERTINFO( !_glObjects, "Another readback in progress?" );
00398     EQLOG( LOG_ASSEMBLY ) << "startReadback " << pvp << ", buffers " << buffers
00399                           << endl;
00400 
00401     _glObjects = glObjects;
00402     _pvp       = pvp;
00403 
00404     _color.memory.state = Memory::INVALID;
00405     _depth.memory.state = Memory::INVALID;
00406 
00407     if( buffers & Frame::BUFFER_COLOR )
00408         _startReadback( Frame::BUFFER_COLOR, zoom );
00409 
00410     if( buffers & Frame::BUFFER_DEPTH )
00411         _startReadback( Frame::BUFFER_DEPTH, zoom );
00412 
00413 
00414     _pvp.apply( zoom );
00415     _pvp.x = 0;
00416     _pvp.y = 0;
00417 }
00418 
00419 void Image::syncReadback()
00420 {
00421     _syncReadback( Frame::BUFFER_COLOR );
00422     _syncReadback( Frame::BUFFER_DEPTH );
00423     _glObjects = 0;
00424 }
00425 
00426 void Image::Memory::resize( const uint32_t size )
00427 {
00428     // Allocate some more bytes so that compressors can use larger tokens
00429     pixels.reserve( size+32 );
00430     pixels.resize( size );
00431 }
00432 
00433 const void* Image::_getBufferKey( const Frame::Buffer buffer ) const
00434 {
00435     switch( buffer )
00436     {
00437         case Frame::BUFFER_COLOR:
00438             return ( reinterpret_cast< const char* >( this ) + 0 );
00439         case Frame::BUFFER_DEPTH:
00440             return ( reinterpret_cast< const char* >( this ) + 1 );
00441         default:
00442             EQUNIMPLEMENTED;
00443             return ( reinterpret_cast< const char* >( this ) + 2 );
00444     }
00445 }
00446 
00447 void Image::_startReadback( const Frame::Buffer buffer, const Zoom& zoom )
00448 {
00449     Attachment& attachment = _getAttachment( buffer );
00450     attachment.memory.isCompressed = false;
00451 
00452     if ( _type == Frame::TYPE_TEXTURE )
00453     {
00454         EQASSERTINFO( zoom == Zoom::NONE, "Texture readback zoom not "
00455                       << "implemented, zoom happens during compositing" );
00456         Texture& texture = _getAttachment( buffer ).texture;
00457         texture.copyFromFrameBuffer( _pvp );
00458         return;
00459     }
00460 
00461     if( _usePBO && _glObjects->supportsBuffers( )) // use async PBO readback
00462     {
00463         EQASSERTINFO( zoom == Zoom::NONE, "Not Implemented" );
00464         _startReadbackPBO( buffer );
00465         return;
00466     }
00467 
00468     if( zoom == Zoom::NONE ) // normal glReadPixels
00469     {
00470         Memory&    memory = attachment.memory;
00471         const size_t size = getPixelDataSize( buffer );
00472 
00473         memory.resize( size );
00474         glReadPixels( _pvp.x, _pvp.y, _pvp.w, _pvp.h, getFormat( buffer ),
00475                       getType( buffer ), memory.pixels.getData() );
00476         memory.state = Memory::VALID;
00477         return;
00478     }
00479 
00480     // else copy to texture, draw zoomed quad into FBO, (read FBO texture)
00481     _startReadbackZoom( buffer, zoom );
00482 }
00483 
00484 void Image::_startReadbackPBO( const Frame::Buffer buffer )
00485 {
00486     Memory& memory = _getAttachment( buffer ).memory;
00487     memory.state = Memory::PBO_READBACK;
00488 
00489     const void* bufferKey = _getBufferKey( buffer );
00490     GLuint pbo = _glObjects->obtainBuffer( bufferKey );
00491     
00492     EQ_GL_CALL( glBindBuffer( GL_PIXEL_PACK_BUFFER, pbo ));
00493 
00494     const size_t size = getPixelDataSize( buffer );
00495     if( memory.pboSize < size )
00496     {
00497         EQ_GL_CALL( glBufferData( GL_PIXEL_PACK_BUFFER, size, 0,
00498                                   GL_DYNAMIC_READ ));
00499         memory.pboSize = size;
00500     }
00501     
00502     EQ_GL_CALL( glReadPixels( _pvp.x, _pvp.y, _pvp.w, _pvp.h,
00503                              getFormat( buffer ), getType( buffer ), 0 ));
00504     EQ_GL_CALL( glBindBuffer( GL_PIXEL_PACK_BUFFER, 0 ));
00505 } 
00506 
00507 void Image::_startReadbackZoom( const Frame::Buffer buffer, const Zoom& zoom )
00508 {
00509     EQASSERT( _glObjects );
00510     EQASSERT( _glObjects->supportsEqTexture( ));
00511     EQASSERT( _glObjects->supportsEqFrameBufferObject( ));
00512 
00513     PixelViewport pvp = _pvp;
00514     pvp.apply( zoom );
00515     if( !pvp.hasArea( ))
00516         return;
00517 
00518     Memory& memory = _getAttachment( buffer ).memory;
00519     memory.state = Memory::ZOOM_READBACK;
00520     
00521     // copy frame buffer to texture
00522     const void* bufferKey = _getBufferKey( buffer );
00523     Texture*    texture   = _glObjects->obtainEqTexture( bufferKey );
00524 
00525     texture->setFormat( getInternalTextureFormat( buffer ));
00526     texture->copyFromFrameBuffer( _pvp );
00527 
00528     // draw zoomed quad into FBO
00529     //  uses the same FBO for color and depth, with masking.
00530     const void*     fboKey = _getBufferKey( Frame::BUFFER_COLOR );
00531     FrameBufferObject* fbo = _glObjects->getEqFrameBufferObject( fboKey );
00532 
00533     if( fbo )
00534     {
00535         EQCHECK( fbo->resize( pvp.w, pvp.h ));
00536     }
00537     else
00538     {
00539         fbo = _glObjects->newEqFrameBufferObject( fboKey );
00540         fbo->setColorFormat( getInternalTextureFormat( buffer ) );
00541         fbo->init( pvp.w, pvp.h, 24, 0 );
00542     }
00543     fbo->bind();
00544     texture->bind();
00545 
00546     if ( buffer == Frame::BUFFER_COLOR )
00547         glDepthMask( false );
00548     else
00549     {
00550         EQASSERT( buffer == Frame::BUFFER_DEPTH )
00551         glColorMask( false, false, false, false );
00552     }
00553 
00554     glDisable( GL_LIGHTING );
00555     glEnable( GL_TEXTURE_RECTANGLE_ARB );
00556     glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
00557     glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
00558     glColor3f( 1.0f, 1.0f, 1.0f );
00559 
00560     glBegin( GL_QUADS );
00561         glTexCoord2f( 0.0f, 0.0f );
00562         glVertex3f( 0, 0, 0.0f );
00563 
00564         glTexCoord2f( static_cast< float >( _pvp.w ), 0.0f );
00565         glVertex3f( static_cast< float >( pvp.w ), 0, 0.0f );
00566 
00567         glTexCoord2f( static_cast< float >( _pvp.w ),
00568                       static_cast< float >( _pvp.h ));
00569         glVertex3f( static_cast< float >( pvp.w ),
00570                     static_cast< float >( pvp.h ), 0.0f );
00571 
00572         glTexCoord2f( 0.0f, static_cast< float >( _pvp.h ));
00573         glVertex3f( 0, static_cast< float >( pvp.h ), 0.0f );
00574     glEnd();
00575 
00576     // restore state
00577     glDisable( GL_TEXTURE_RECTANGLE_ARB );
00578 
00579     if ( buffer == Frame::BUFFER_COLOR )
00580         glDepthMask( true );
00581     else
00582     {
00583         const ColorMask colorMask; // TODO = channel->getDrawBufferMask();
00584         glColorMask( colorMask.red, colorMask.green, colorMask.blue, true );
00585     }
00586     // TODO channel->bindFramebuffer()
00587     fbo->unbind();
00588 
00589     EQLOG( LOG_ASSEMBLY ) << "Scale " << _pvp << " -> " << pvp << std::endl;
00590 }
00591 
00592 void Image::_syncReadback( const Frame::Buffer buffer )
00593 {
00594     Memory& memory = _getAttachment( buffer ).memory;
00595     switch( memory.state )
00596     {
00597         case Memory::PBO_READBACK:
00598             _syncReadbackPBO( buffer );
00599             break;
00600 
00601         case Memory::ZOOM_READBACK:
00602             _syncReadbackZoom( buffer );
00603             break;
00604 
00605         default:
00606             break;
00607     }
00608 }
00609 
00610 void Image::_syncReadbackPBO( const Frame::Buffer buffer )
00611 {
00612     // async readback only possible when PBOs are used and supported
00613     EQASSERT( _usePBO );
00614     EQASSERT( _glObjects->supportsBuffers( ));
00615     EQ_GL_ERROR( "before Image::_syncReadback" );
00616 
00617     const size_t size      = getPixelDataSize( buffer );
00618     const void*  bufferKey = _getBufferKey( buffer );
00619     GLuint       pbo       = _glObjects->getBuffer( bufferKey );
00620     EQASSERT( pbo != Window::ObjectManager::INVALID );
00621 
00622     Memory& memory = _getAttachment( buffer ).memory;
00623     memory.resize( size );
00624 
00625     EQ_GL_CALL( glBindBuffer( GL_PIXEL_PACK_BUFFER, pbo ));
00626     const void* data = glMapBuffer( GL_PIXEL_PACK_BUFFER, GL_READ_ONLY );
00627     EQ_GL_ERROR( "glMapBuffer" );
00628     EQASSERT( data );
00629 
00630     memcpy( memory.pixels.getData(), data, size );
00631 
00632     glUnmapBuffer( GL_PIXEL_PACK_BUFFER );
00633     glBindBuffer( GL_PIXEL_PACK_BUFFER, 0 );
00634 
00635     memory.state = Memory::VALID;
00636 }
00637 
00638 void Image::_syncReadbackZoom( const Frame::Buffer buffer )
00639 {
00640     Memory& memory = _getAttachment( buffer ).memory;
00641     const size_t size = getPixelDataSize( buffer );
00642     memory.resize( size );
00643 
00644     const void*  bufferKey = _getBufferKey( buffer );
00645     FrameBufferObject* fbo = _glObjects->getEqFrameBufferObject( bufferKey );
00646     EQASSERT( fbo != 0 );
00647     
00648     switch( buffer )
00649     {
00650         case Frame::BUFFER_COLOR:
00651             fbo->getColorTextures()[0]->download( memory.pixels.getData(),
00652                                                   getFormat( buffer ), 
00653                                                   getType( buffer ));
00654             break;
00655 
00656         default:
00657             EQUNIMPLEMENTED;
00658         case Frame::BUFFER_DEPTH:
00659             fbo->getDepthTexture().download( memory.pixels.getData(),
00660                                              getFormat( buffer ), 
00661                                              getType( buffer ));
00662             break;
00663     }
00664 
00665     memory.state = Memory::VALID;
00666     EQLOG( LOG_ASSEMBLY ) << "Read texture " << _pvp << std::endl;
00667 }
00668 
00669 void Image::setPixelViewport( const PixelViewport& pvp )
00670 {
00671     _pvp = pvp;
00672     _color.memory.state = Memory::INVALID;
00673     _depth.memory.state = Memory::INVALID;
00674     _color.memory.isCompressed = false;
00675     _depth.memory.isCompressed = false;
00676 }
00677 
00678 void Image::clearPixelData( const Frame::Buffer buffer )
00679 {
00680     const ssize_t size  = getPixelDataSize( buffer );
00681     if( size == 0 )
00682         return;
00683 
00684     validatePixelData( buffer );
00685     Memory& memory = _getAttachment( buffer ).memory;
00686 
00687     if( buffer == Frame::BUFFER_DEPTH )
00688     {
00689         memset( memory.pixels.getData(), 0xFF, size );
00690     }
00691     else
00692     {
00693         if( getDepth( Frame::BUFFER_COLOR ) == 4 )
00694         {
00695             uint8_t* data = memory.pixels.getData();
00696 #ifdef LEOPARD
00697             const unsigned char pixel[4] = { 0, 0, 0, 255 };
00698             memset_pattern4( data, &pixel, size );
00699 #else
00700             bzero( data, size );
00701 
00702             if( getDepth( Frame::BUFFER_COLOR ) == 4 )
00703 #ifdef EQ_USE_OPENMP
00704 #pragma omp parallel for
00705 #endif
00706                 for( ssize_t i = 3; i < size; i+=4 )
00707                     data[i] = 255;
00708 #endif
00709         }
00710         else
00711             bzero( memory.pixels.getData(), size );
00712     }
00713 }
00714 
00715 void Image::validatePixelData( const Frame::Buffer buffer )
00716 {
00717     Memory& memory = _getAttachment( buffer ).memory;
00718     const size_t size = getPixelDataSize( buffer );
00719 
00720     memory.resize( size );
00721     memory.state = Memory::VALID;
00722     memory.isCompressed = false;
00723 }
00724 
00725 void Image::setPixelData( const Frame::Buffer buffer, const uint8_t* data )
00726 {
00727     const uint32_t size  = getPixelDataSize( buffer );
00728     if( size == 0 )
00729         return;
00730 
00731     Memory& memory = _getAttachment( buffer ).memory;
00732 
00733     memory.resize( size );
00734     memcpy( memory.pixels.getData(), data, size );
00735     memory.state = Memory::VALID;
00736     memory.isCompressed = false;
00737 }
00738 
00739 void Image::setPixelData( const Frame::Buffer buffer, const PixelData& pixels )
00740 {
00741     setFormat( buffer, pixels.format );
00742     setType( buffer, pixels.type );
00743 
00744     const uint32_t size = getPixelDataSize( buffer );
00745     if( size == 0 )
00746         return;
00747 
00748     if( pixels.compressorName <= EQ_COMPRESSOR_NONE )
00749     {
00750         EQASSERT( size == pixels.pixels.getSize() );
00751         setPixelData( buffer, pixels.pixels.getData() );
00752         return;
00753     }
00754 
00755     Attachment& attachment = _getAttachment( buffer );
00756     if( !_allocDecompressor( attachment, pixels.compressorName ))
00757     {
00758         EQASSERTINFO( 0,
00759                       "Can't allocate decompressor " << pixels.compressorName <<
00760                       ", mismatched compressor installation?" );
00761         return;
00762     }
00763 
00764     Memory& memory = attachment.memory;
00765 
00766     EQASSERT( size > 0 );
00767     memory.resize( size );
00768     memory.isCompressed = false;
00769 
00770     // Get number of blocks in compressed data
00771     const uint64_t nBlocks  = pixels.compressedSize.size();
00772     EQASSERT( nBlocks == pixels.compressedData.size( ));
00773 
00774     void* outData = reinterpret_cast< uint8_t* >( memory.pixels.getData() );
00775     uint64_t outDim[4] = { _pvp.x, _pvp.w, _pvp.y, _pvp.h }; 
00776     uint64_t flags = EQ_COMPRESSOR_DATA_2D;
00777     if( _canIgnoreAlpha( buffer ))
00778         flags |= EQ_COMPRESSOR_IGNORE_MSE;
00779 
00780     EQASSERT( attachment.compressor.plugin != 0 );
00781     EQASSERT( !attachment.compressor.isCompressor );
00782 
00783     attachment.compressor.plugin->decompress( attachment.compressor.instance,
00784                                               attachment.compressor.name,
00785                                               &pixels.compressedData.front(),
00786                                               &pixels.compressedSize.front(),
00787                                               nBlocks, outData, 
00788                                               outDim, flags );
00789     memory.state = Memory::VALID;
00790 }
00791 
00792 Image::Attachment& Image::_getAttachment( const Frame::Buffer buffer )
00793 {
00794    switch( buffer )
00795    {
00796        case Frame::BUFFER_COLOR:
00797            return _color;
00798        case Frame::BUFFER_DEPTH:
00799            return _depth;
00800        default:
00801            EQUNIMPLEMENTED;
00802    }
00803    return _color;
00804 }
00805 
00806 const Image::Attachment& Image::_getAttachment( const Frame::Buffer buffer ) 
00807     const
00808 {
00809    switch( buffer )
00810    {
00811        case Frame::BUFFER_COLOR:
00812            return _color;
00813        case Frame::BUFFER_DEPTH:
00814            return _depth;
00815        default:
00816            EQUNIMPLEMENTED;
00817    }
00818    return _color;
00819 }
00820 
00821 Image::Attachment::CompressorData::CompressorData()
00822         : name( 0 )
00823         , instance( 0 )
00824         , plugin( 0 )
00825         , isCompressor( true )
00826 {}
00827 
00828 void Image::Attachment::CompressorData::flush()
00829 {
00830     if( !instance )
00831         return;
00832     EQASSERT( plugin );
00833 
00834     if( isCompressor )
00835         plugin->deleteCompressor( instance );
00836     else
00837         plugin->deleteDecompressor( instance );
00838 
00839     plugin = 0;
00840     instance = 0;
00841 }
00842 
00844 bool Image::allocCompressor( const Frame::Buffer buffer, const uint32_t name )
00845 {
00846     Attachment& attachment = _getAttachment( buffer );
00847     if( name <= EQ_COMPRESSOR_NONE )
00848     {
00849         attachment.compressor.flush();
00850         attachment.compressor.name = name;
00851         attachment.compressor.isCompressor = true;
00852         attachment.memory.isCompressed = false;
00853         return true;
00854     }
00855         
00856     if( !attachment.compressor.plugin || attachment.compressor.name != name )
00857     {
00858         attachment.compressor.flush();
00859         attachment.compressor.name = name;
00860         attachment.compressor.isCompressor = true;
00861         attachment.memory.isCompressed = false;
00862 
00863 #ifndef NDEBUG
00864         const std::vector< uint32_t > names( findCompressors( buffer ));
00865         EQASSERT( std::find( names.begin(), names.end(), name) != names.end( ));
00866 #endif
00867 
00868         PluginRegistry& registry = Global::getPluginRegistry();
00869         attachment.compressor.plugin = registry.findCompressor( name );
00870         if( !attachment.compressor.plugin )
00871             return false;
00872 
00873         attachment.compressor.instance =
00874             attachment.compressor.plugin->newCompressor( name );
00875         EQASSERT( attachment.compressor.instance );
00876         EQINFO << "Instantiated compressor of type " << name << std::endl;
00877     }
00878     return ( attachment.compressor.instance != 0 );
00879 }
00880 
00882 bool Image::_allocDecompressor( Attachment& attachment, uint32_t name )
00883 {
00884     if( !attachment.compressor.plugin || attachment.compressor.name != name )
00885     {
00886         attachment.compressor.flush();
00887         attachment.compressor.name = name;
00888         attachment.compressor.isCompressor = false;
00889 
00890         PluginRegistry& registry = Global::getPluginRegistry();
00891         attachment.compressor.plugin = registry.findCompressor( name );
00892         if( !attachment.compressor.plugin )
00893             return false;
00894 
00895         attachment.compressor.instance = 
00896             attachment.compressor.plugin->newDecompressor( name );
00897     }
00898     return true;
00899 }
00900 
00901 void Image::Memory::flush()
00902 {
00903     pboSize = 0;
00904     state = INVALID;
00905     isCompressed = false;
00906     PixelData::flush();
00907 }
00908 
00909 Image::PixelData::PixelData()
00910         : format( GL_FALSE )
00911         , type( GL_FALSE )
00912         , compressorName( 0 )
00913         , isCompressed( false )
00914 {}
00915 
00916 void Image::PixelData::flush()
00917 {
00918     pixels.clear();
00919     format = GL_FALSE;
00920     type   = GL_FALSE;
00921     compressorName = 0;
00922     isCompressed = false;
00923     compressedSize.clear();
00924     compressedData.clear();
00925 }
00926 
00927 Image::PixelData::~PixelData()
00928 {
00929     flush();
00930 }
00931 
00932 const Image::PixelData& Image::compressPixelData( const Frame::Buffer buffer )
00933 {
00934     EQASSERT( getPixelDataSize( buffer ) > 0 );
00935 
00936     Attachment& attachment = _getAttachment( buffer );
00937     Memory& memory = attachment.memory;
00938     if( memory.isCompressed )
00939         return memory;
00940 
00941     if( attachment.compressor.name == 0 )
00942         memory.compressorName = _getCompressorName( buffer );
00943     else
00944         memory.compressorName = attachment.compressor.name;
00945 
00946     EQASSERT( memory.compressorName != 0 );
00947 
00948     if( !allocCompressor( buffer, memory.compressorName ) || 
00949         memory.compressorName == EQ_COMPRESSOR_NONE )
00950     {
00951         EQWARN << "No compressor found for token type " 
00952                << _getCompressorTokenType( buffer ) << std::endl;
00953         return memory;
00954     }
00955 
00956     const uint64_t inDims[4]  = { _pvp.x, _pvp.w, _pvp.y, _pvp.h}; 
00957     
00958     EQASSERT( attachment.compressor.plugin != 0 );
00959     uint64_t flags = EQ_COMPRESSOR_DATA_2D;
00960     if( _canIgnoreAlpha( buffer ))
00961         flags |= EQ_COMPRESSOR_IGNORE_MSE;
00962 
00963     attachment.compressor.plugin->compress( attachment.compressor.instance,
00964                                             attachment.compressor.name,
00965                                             memory.pixels.getData(),
00966                                             inDims, flags );
00967 
00968     const size_t numResults = attachment.compressor.plugin->getNumResults( 
00969         attachment.compressor.instance, attachment.compressor.name );
00970     
00971     memory.compressedSize.resize( numResults );
00972     memory.compressedData.resize( numResults );
00973 
00974     for( size_t i = 0; i < numResults ; i++ )
00975     {
00976         attachment.compressor.plugin->getResult( attachment.compressor.instance,
00977                                                  attachment.compressor.name,
00978                                                  i, 
00979                                                  &memory.compressedData[i], 
00980                                                  &memory.compressedSize[i] );
00981     }
00982 
00983     memory.isCompressed = true;
00984     return memory;
00985 }
00986 
00987 
00988 //---------------------------------------------------------------------------
00989 // IO
00990 //---------------------------------------------------------------------------
00991 
00992 void Image::writeImages( const std::string& filenameTemplate ) const
00993 {
00994     writeImage( filenameTemplate + "_color.rgb", Frame::BUFFER_COLOR );
00995     writeImage( filenameTemplate + "_depth.rgb", Frame::BUFFER_DEPTH );
00996 }
00997 
00998 #define SWAP_SHORT(v) ( v = (v&0xff) << 8 | (v&0xff00) >> 8 )
00999 #define SWAP_INT(v)   ( v = (v&0xff) << 24 | (v&0xff00) << 8 |      \
01000                         (v&0xff0000) >> 8 | (v&0xff000000) >> 24)
01001 
01002 #ifdef WIN32
01003 #  pragma pack(1)
01004 #endif
01005 
01006 struct RGBHeader
01007 {
01008     RGBHeader()
01009         {
01010             memset( this, 0, sizeof( RGBHeader ));
01011             magic           = 474;
01012             bytesPerChannel = 1;
01013             nDimensions     = 3;
01014             maxValue        = 255;
01015         }
01016 
01021         void convert()
01022         {
01023 #if defined(__i386__) || defined(__amd64__) || defined (__ia64) || \
01024     defined(__x86_64) || defined(WIN32)
01025             SWAP_SHORT(magic);
01026             SWAP_SHORT(nDimensions);
01027             SWAP_SHORT(width);
01028             SWAP_SHORT(height);
01029             SWAP_SHORT(depth);
01030             SWAP_INT(minValue);
01031             SWAP_INT(maxValue);
01032             SWAP_INT(colorMode);
01033 #endif
01034         }
01035 
01036     unsigned short magic;
01037     char compression;
01038     char bytesPerChannel;
01039     unsigned short nDimensions;
01040     unsigned short width;
01041     unsigned short height;
01042     unsigned short depth;
01043     unsigned minValue;
01044     unsigned maxValue;
01045     char unused[4];
01046     char filename[80];
01047     unsigned colorMode;
01048     char fill[404];
01049 }
01051 #ifndef WIN32
01052   __attribute__((packed))
01053 #endif
01054 ;
01055 
01056 void Image::writeImage( const std::string& filename,
01057                         const Frame::Buffer buffer ) const
01058 {
01059     const size_t  nPixels = _pvp.w * _pvp.h;
01060     const Memory& memory = _getAttachment( buffer ).memory;
01061 
01062     if( nPixels == 0 || memory.state != Memory::VALID )
01063         return;
01064 
01065     ofstream image( filename.c_str(), ios::out | ios::binary );
01066     if( !image.is_open( ))
01067     {
01068         EQERROR << "Can't open " << filename << " for writing" << endl;
01069         return;
01070     }
01071 
01072     RGBHeader    header;
01073 
01074     header.width  = _pvp.w;
01075     header.height = _pvp.h;
01076 
01077     header.bytesPerChannel = getChannelSize( buffer );
01078     header.depth = getNumChannels( buffer );
01079 
01080     if( header.depth == 1 ) // depth
01081     {
01082         EQASSERT( (header.bytesPerChannel % 4) == 0 );
01083         header.depth = 4;
01084         header.bytesPerChannel /= 4;
01085     }
01086     EQASSERT( header.bytesPerChannel > 0 );
01087     if( header.bytesPerChannel > 2 )
01088         EQWARN << static_cast< int >( header.bytesPerChannel ) 
01089                << " bytes per channel not supported by RGB spec" << std::endl;
01090 
01091     const uint8_t bpc = header.bytesPerChannel;
01092     const uint16_t nChannels = header.depth;
01093 
01094     strncpy( header.filename, filename.c_str(), 80 );
01095 
01096     header.convert();
01097     image.write( reinterpret_cast<const char *>( &header ), sizeof( header ));
01098 
01099     // Each channel is saved separately
01100     const size_t depth  = nChannels * bpc;
01101     const size_t nBytes = nPixels * depth;
01102     const char* data = reinterpret_cast<const char*>( getPixelPointer( buffer));
01103 
01104     switch( getFormat( buffer ))
01105     {
01106         case GL_BGR:
01107             for( size_t j = 2 * bpc; j < nBytes; j += depth )
01108                 image.write( &data[j], bpc );
01109             for( size_t j = 1 * bpc; j < nBytes; j += depth )
01110                 image.write( &data[j], bpc );
01111             for( size_t j = 0; j < nBytes; j += depth )
01112                 image.write( &data[j], bpc );
01113             break;
01114 
01115         case GL_BGRA:
01116             for( size_t j = 2 * bpc; j < nBytes; j += depth )
01117                 image.write( &data[j], bpc );
01118             for( size_t j = 1 * bpc; j < nBytes; j += depth )
01119                 image.write( &data[j], bpc );
01120             for( size_t j = 0; j < nBytes; j += depth )
01121                 image.write( &data[j], bpc );
01122             // invert alpha
01123             for( size_t j = 3 * bpc; j < nBytes; j += depth )
01124             {
01125                 if( bpc == 1 )
01126                 {
01127                     const uint8_t val = 255 - 
01128                         *reinterpret_cast< const uint8_t* >( &data[j] );
01129                     image.write( reinterpret_cast<const char*>( &val ), 1 );
01130                 }
01131                 else
01132                     image.write(&data[j], bpc );
01133             }
01134             break;
01135 
01136         default:
01137             for( size_t i = 0; i < nChannels; i += bpc )
01138                 for( size_t j = i * bpc; j < nBytes; j += depth )
01139                     image.write(&data[j], bpc );
01140     }
01141 
01142     image.close();
01143 }
01144 
01145 bool Image::readImage( const std::string& filename, const Frame::Buffer buffer )
01146 {
01147     base::MemoryMap image;
01148     const uint8_t* addr = static_cast< const uint8_t* >( image.map( filename ));
01149 
01150     if( !addr )
01151     {
01152         EQERROR << "Can't open " << filename << " for reading" << endl;
01153         return false;
01154     }
01155 
01156     const size_t size = image.getSize();
01157     if( size < sizeof( RGBHeader ))
01158     {
01159         EQERROR << "Image " << filename << " too small" << endl;
01160         return false;
01161     }
01162 
01163     RGBHeader header;
01164     memcpy( &header, addr, sizeof( header ));
01165     addr += sizeof( header );
01166 
01167     header.convert();
01168 
01169     if( header.magic != 474)
01170     {
01171         EQERROR << "Bad magic number " << filename << endl;
01172         return false;
01173     }
01174     if( header.width == 0 || header.height == 0 )
01175     {
01176         EQERROR << "Zero-sized image " << filename << endl;
01177         return false;
01178     }
01179     if( header.compression != 0)
01180     {
01181         EQERROR << "Unsupported compression " << filename << endl;
01182         return false;
01183     }
01184 
01185     const size_t nChannels = header.depth;
01186 
01187     if( header.nDimensions != 3 ||
01188         header.minValue != 0 ||
01189         header.maxValue != 255 ||
01190         header.colorMode != 0 ||
01191         ( buffer == Frame::BUFFER_COLOR && nChannels != 3 && nChannels != 4 ) ||
01192         ( buffer == Frame::BUFFER_DEPTH && nChannels != 4 ))
01193     {
01194         EQERROR << "Unsupported image type " << filename << endl;
01195         return false;
01196     }
01197 
01198     const uint8_t bpc     = header.bytesPerChannel;
01199     const size_t  depth   = nChannels * bpc;
01200     const size_t  nPixels = header.width * header.height;
01201     const size_t  nBytes  = nPixels * depth;
01202 
01203     if( size < sizeof( RGBHeader ) + nBytes )
01204     {
01205         EQERROR << "Image " << filename << " too small" << endl;
01206         return false;
01207     }
01208     EQASSERT( size == sizeof( RGBHeader ) + nBytes );
01209 
01210     switch( buffer )
01211     {
01212         case Frame::BUFFER_DEPTH:
01213             if( header.bytesPerChannel != 1 )
01214             {
01215                 EQERROR << "Unsupported channel depth " 
01216                         << static_cast< int >( header.bytesPerChannel ) << endl;
01217                         return false;
01218             }
01219 
01220             setFormat( Frame::BUFFER_DEPTH, GL_DEPTH_COMPONENT );
01221             setType(   Frame::BUFFER_DEPTH, GL_UNSIGNED_INT );
01222             break;
01223 
01224         default:
01225             EQUNREACHABLE;
01226         case Frame::BUFFER_COLOR:
01227             setFormat( Frame::BUFFER_COLOR, (nChannels==4) ? GL_RGBA : GL_RGB );
01228             switch( header.bytesPerChannel )
01229             {
01230                 case 1:
01231                     setType( Frame::BUFFER_COLOR, GL_UNSIGNED_BYTE );
01232                     break;
01233                 case 2:
01234                     setType( Frame::BUFFER_COLOR, GL_HALF_FLOAT );
01235                     break;
01236                 case 4:
01237                     setType( Frame::BUFFER_COLOR, GL_FLOAT );
01238                     break;
01239                 default:
01240                     EQERROR << "Unsupported channel depth " 
01241                             << static_cast< int >( header.bytesPerChannel )
01242                             << std::endl;
01243                     return false;
01244             }
01245             break;
01246     }
01247 
01248     const PixelViewport pvp( 0, 0, header.width, header.height );
01249     if( pvp != getPixelViewport( ))
01250         setPixelViewport( pvp );
01251 
01252     validatePixelData( buffer );
01253 
01254     Memory& memory = _getAttachment( buffer ).memory;
01255     uint8_t* data = reinterpret_cast< uint8_t* >( memory.pixels.getData() );
01256     EQASSERTINFO( nBytes <= memory.pixels.getSize(), 
01257                   nBytes << " > " << memory.pixels.getSize() );
01258 
01259     // Each channel is saved separately
01260     for( size_t i = 0; i < depth; i += bpc )
01261         for( size_t j = i * bpc; j < nBytes; j += depth )
01262         {
01263             EQASSERT( addr + bpc - ( const uint8_t* )( image.getAddress( )) <= 
01264                       static_cast< ssize_t >( size ));
01265 
01266             memcpy( &data[j], addr, bpc );
01267             addr += bpc;
01268         }
01269 
01270     return true;
01271 }
01272 
01273 std::ostream& operator << ( std::ostream& os, const Image* image )
01274 {
01275     os << "image " << image->_pvp;
01276     return os;
01277 }
01278 }
Generated on Mon Aug 10 18:58:39 2009 for Equalizer 0.9 by  doxygen 1.5.8