LCOV - code coverage report
Current view: top level - eq/client - image.cpp (source / functions) Hit Total Coverage
Test: lcov2.info Lines: 569 818 69.6 %
Date: 2014-06-18 Functions: 70 82 85.4 %

          Line data    Source code
       1             : 
       2             : /* Copyright (c) 2006-2014, Stefan Eilemann <eile@equalizergraphics.com>
       3             :  *                    2011, Daniel Nachbaur <danielnachbaur@gmail.com>
       4             :  *                    2010, Cedric Stalder <cedric.stalder@gmail.com>
       5             :  *
       6             :  * This library is free software; you can redistribute it and/or modify it under
       7             :  * the terms of the GNU Lesser General Public License version 2.1 as published
       8             :  * by the Free Software Foundation.
       9             :  *
      10             :  * This library is distributed in the hope that it will be useful, but WITHOUT
      11             :  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
      12             :  * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
      13             :  * details.
      14             :  *
      15             :  * You should have received a copy of the GNU Lesser General Public License
      16             :  * along with this library; if not, write to the Free Software Foundation, Inc.,
      17             :  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
      18             :  */
      19             : 
      20             : #include "image.h"
      21             : 
      22             : #include "gl.h"
      23             : #include "half.h"
      24             : #include "log.h"
      25             : #include "pixelData.h"
      26             : #include "windowSystem.h"
      27             : 
      28             : #include <eq/util/frameBufferObject.h>
      29             : #include <eq/util/objectManager.h>
      30             : #include <eq/fabric/colorMask.h>
      31             : 
      32             : #include <co/global.h>
      33             : 
      34             : #include <lunchbox/buffer.h>
      35             : #include <lunchbox/compressor.h>
      36             : #include <lunchbox/decompressor.h>
      37             : #include <lunchbox/downloader.h>
      38             : #include <lunchbox/memoryMap.h>
      39             : #include <lunchbox/omp.h>
      40             : #include <lunchbox/pluginRegistry.h>
      41             : #include <lunchbox/uploader.h>
      42             : 
      43             : #include <boost/filesystem.hpp>
      44             : #include <fstream>
      45             : 
      46             : #ifdef _WIN32
      47             : #  include <malloc.h>
      48             : #else
      49             : #  include <alloca.h>
      50             : #endif
      51             : 
      52             : #include "transferFinder.h"
      53             : 
      54             : namespace eq
      55             : {
      56             : namespace
      57             : {
      58             : /** @internal Raw image data. */
      59          54 : struct Memory : public PixelData
      60             : {
      61             : public:
      62        1674 :     Memory()
      63             :         : state( INVALID )
      64        1674 :         , hasAlpha( true )
      65        1674 :     {}
      66             : 
      67          34 :     void flush()
      68             :     {
      69          34 :         PixelData::reset();
      70          34 :         state = INVALID;
      71          34 :         localBuffer.clear();
      72          34 :         hasAlpha = true;
      73          34 :     }
      74             : 
      75         874 :     void useLocalBuffer()
      76             :     {
      77         874 :         LBASSERT( internalFormat != 0 );
      78         874 :         LBASSERT( externalFormat != 0 );
      79         874 :         LBASSERT( pixelSize > 0 );
      80         874 :         LBASSERT( pvp.hasArea( ));
      81             : 
      82         874 :         localBuffer.resize( pvp.getArea() * pixelSize );
      83         874 :         pixels = localBuffer.getData();
      84         874 :     }
      85             : 
      86             :     enum State
      87             :     {
      88             :         INVALID,
      89             :         VALID,
      90             :         DOWNLOAD // async RB is in progress
      91             :     };
      92             : 
      93             :     State state;   //!< The current state of the memory
      94             : 
      95             :     /** During the call of setPixelData or writeImage, we have to
      96             :      * manage an internal buffer to copy the data. Otherwise the downloader
      97             :      * allocates the memory. */
      98             :     lunchbox::Bufferb localBuffer;
      99             : 
     100             :     bool hasAlpha; //!< The uncompressed pixels contain alpha
     101             : };
     102             : 
     103             : enum ActivePlugin
     104             : {
     105             :     PLUGIN_FULL,
     106             :     PLUGIN_LOSSY,
     107             :     PLUGIN_ALL
     108             : };
     109             : 
     110             : /** @internal The individual parameters for a buffer. */
     111             : struct Attachment
     112             : {
     113             :     ActivePlugin active;
     114             :     lunchbox::Compressor compressor[ PLUGIN_ALL ];
     115             :     lunchbox::Decompressor decompressor[ PLUGIN_ALL ];
     116             :     lunchbox::Downloader downloader[ PLUGIN_ALL ];
     117             : 
     118             :     float quality; //!< the minimum quality
     119             : 
     120             :     /** The texture name for this image component (texture images). */
     121             :     util::Texture texture;
     122             : 
     123             :     /** Current pixel data (memory images). */
     124             :     Memory memory;
     125             : 
     126             :     Zoom zoom; //!< zoom factor of pending readback
     127             : 
     128        1674 :     Attachment()
     129             :         : active( PLUGIN_FULL )
     130             :         , quality( 1.f )
     131        1674 :         , texture( GL_TEXTURE_RECTANGLE_ARB )
     132        1674 :         {}
     133             : 
     134          54 :     ~Attachment()
     135         162 :     {
     136          54 :         LBASSERT( !compressor[ PLUGIN_FULL ].isGood( ));
     137          54 :         LBASSERT( !compressor[ PLUGIN_LOSSY ].isGood( ));
     138          54 :         LBASSERT( !decompressor[ PLUGIN_FULL ].isGood( ));
     139          54 :         LBASSERT( !decompressor[ PLUGIN_LOSSY ].isGood( ));
     140          54 :         LBASSERT( !downloader[ PLUGIN_FULL ].isGood( ));
     141          54 :         LBASSERT( !downloader[ PLUGIN_LOSSY ].isGood( ));
     142         162 :     }
     143             : 
     144          34 :     void flush()
     145             :     {
     146          34 :         memory.flush();
     147          34 :         texture.flush();
     148          34 :         resetPlugins();
     149          34 :     }
     150             : 
     151          38 :     void resetPlugins()
     152             :     {
     153          38 :         compressor[ PLUGIN_FULL ].clear();
     154          38 :         compressor[ PLUGIN_LOSSY ].clear();
     155          38 :         decompressor[ PLUGIN_FULL ].clear();
     156          38 :         decompressor[ PLUGIN_LOSSY ].clear();
     157          38 :         downloader[ PLUGIN_FULL ].clear();
     158          38 :         downloader[ PLUGIN_LOSSY ].clear();
     159          38 :     }
     160             : };
     161             : }
     162             : 
     163             : namespace detail
     164             : {
     165          27 : class Image
     166             : {
     167             : public:
     168         837 :     Image() : type( eq::Frame::TYPE_MEMORY ), ignoreAlpha( false ) {}
     169             : 
     170             :     /** The rectangle of the current pixel data. */
     171             :     PixelViewport pvp;
     172             : 
     173             :     /** Zoom factor used for compositing. */
     174             :     Zoom zoom;
     175             : 
     176             :     /** The storage type for the pixel data. */
     177             :     eq::Frame::Type type;
     178             : 
     179             :     Attachment color;
     180             :     Attachment depth;
     181             : 
     182             :     /** Alpha channel significance. */
     183             :     bool ignoreAlpha;
     184             : 
     185       10087 :     Attachment& getAttachment( const eq::Frame::Buffer buffer )
     186             :     {
     187       10087 :         switch( buffer )
     188             :         {
     189             :           case eq::Frame::BUFFER_COLOR:
     190        9341 :               return color;
     191             :           case eq::Frame::BUFFER_DEPTH:
     192         746 :               return depth;
     193             :           default:
     194           0 :               LBUNIMPLEMENTED;
     195             :         }
     196           0 :         return color;
     197             :     }
     198             : 
     199         146 :     const Attachment& getAttachment( const eq::Frame::Buffer buffer ) const
     200             :     {
     201         146 :         switch( buffer )
     202             :         {
     203             :           case eq::Frame::BUFFER_COLOR:
     204         141 :               return color;
     205             :           case eq::Frame::BUFFER_DEPTH:
     206           5 :               return depth;
     207             :           default:
     208           0 :               LBUNIMPLEMENTED;
     209             :         }
     210           0 :         return color;
     211             :     }
     212             : 
     213        6855 :     Memory& getMemory( const eq::Frame::Buffer buffer )
     214        6855 :         { return getAttachment( buffer ).memory; }
     215             :     const Memory& getMemory( const eq::Frame::Buffer buffer ) const
     216             :         { return getAttachment( buffer ).memory; }
     217             : 
     218         146 :     EqCompressorInfos findTransferers( const eq::Frame::Buffer buffer,
     219             :                                        const GLEWContext* gl ) const
     220             :     {
     221         146 :         const Attachment& attachment = getAttachment( buffer );
     222         146 :         const Memory& memory = attachment.memory;
     223             :         TransferFinder finder( memory.internalFormat, memory.externalFormat, 0,
     224         146 :                                attachment.quality, ignoreAlpha, gl );
     225         146 :         co::Global::getPluginRegistry().accept( finder );
     226         146 :         return finder.result;
     227             :     }
     228             : };
     229             : }
     230             : 
     231         837 : Image::Image()
     232         837 :         : _impl( new detail::Image )
     233             : {
     234         837 :     reset();
     235         837 : }
     236             : 
     237          33 : Image::~Image()
     238             : {
     239          27 :     delete _impl;
     240          33 : }
     241             : 
     242         840 : void Image::reset()
     243             : {
     244         840 :     _impl->ignoreAlpha = false;
     245         840 :     setPixelViewport( PixelViewport( ));
     246         840 : }
     247             : 
     248          17 : void Image::flush()
     249             : {
     250          17 :     _impl->color.flush();
     251          17 :     _impl->depth.flush();
     252          17 : }
     253             : 
     254           2 : void Image::resetPlugins()
     255             : {
     256           2 :     _impl->color.resetPlugins();
     257           2 :     _impl->depth.resetPlugins();
     258           2 : }
     259             : 
     260           4 : void Image::deleteGLObjects( util::ObjectManager& om )
     261             : {
     262           4 :     const char* key = reinterpret_cast< const char* >( this );
     263          20 :     for( size_t i=0; i < 4; ++i )
     264             :     {
     265          16 :         om.deleteEqUploader( key + i );
     266          16 :         om.deleteEqTexture( key + i );
     267             :     }
     268           4 : }
     269             : 
     270           0 : const void* Image::_getBufferKey( const Frame::Buffer buffer ) const
     271             : {
     272           0 :     switch( buffer )
     273             :     {
     274             :         // Check also deleteGLObjects!
     275             :         case Frame::BUFFER_COLOR:
     276           0 :             return ( reinterpret_cast< const char* >( this ) + 0 );
     277             :         case Frame::BUFFER_DEPTH:
     278           0 :             return ( reinterpret_cast< const char* >( this ) + 1 );
     279             :         default:
     280           0 :             LBUNIMPLEMENTED;
     281           0 :             return ( reinterpret_cast< const char* >( this ) + 2 );
     282             :     }
     283             : }
     284             : 
     285           2 : const void* Image::_getCompressorKey( const Frame::Buffer buffer ) const
     286             : {
     287           2 :     const Attachment& attachment = _impl->getAttachment( buffer );
     288             : 
     289           2 :     switch( buffer )
     290             :     {
     291             :         // Check also deleteGLObjects!
     292             :         case Frame::BUFFER_COLOR:
     293           2 :             if( attachment.quality == 1.0f )
     294           2 :                 return ( reinterpret_cast< const char* >( this ) + 0 );
     295           0 :             return ( reinterpret_cast< const char* >( this ) + 1 );
     296             :         case Frame::BUFFER_DEPTH:
     297           0 :             if( attachment.quality == 1.0f )
     298           0 :                 return ( reinterpret_cast< const char* >( this ) + 2 );
     299           0 :             return ( reinterpret_cast< const char* >( this ) + 3 );
     300             :         default:
     301           0 :             LBUNIMPLEMENTED;
     302           0 :             return ( reinterpret_cast< const char* >( this ) + 0 );
     303             :     }
     304             : }
     305             : 
     306        1083 : uint32_t Image::getPixelDataSize( const Frame::Buffer buffer ) const
     307             : {
     308        1083 :     const Memory& memory = _impl->getMemory( buffer );
     309        1083 :     return memory.pvp.getArea() * memory.pixelSize;
     310             : }
     311             : 
     312             : 
     313         719 : void Image::_setExternalFormat( const Frame::Buffer buffer,
     314             :                                 const uint32_t externalFormat,
     315             :                                 const uint32_t pixelSize, const bool hasAlpha_ )
     316             : {
     317         719 :     Memory& memory = _impl->getMemory( buffer );
     318         719 :     if( memory.externalFormat == externalFormat )
     319        1110 :         return;
     320             : 
     321         328 :     memory.externalFormat = externalFormat;
     322         328 :     memory.pixelSize = pixelSize;
     323         328 :     memory.hasAlpha = buffer == Frame::BUFFER_DEPTH ? false : hasAlpha_;
     324         328 :     memory.state = Memory::INVALID;
     325             : }
     326             : 
     327        1557 : void Image::setInternalFormat( const Frame::Buffer buffer,
     328             :                                const uint32_t internalFormat )
     329             : {
     330        1557 :     Memory& memory = _impl->getMemory( buffer );
     331        1557 :     if( memory.internalFormat == internalFormat )
     332         403 :         return;
     333             : 
     334        1154 :     memory.internalFormat = internalFormat;
     335        1154 :     allocCompressor( buffer, EQ_COMPRESSOR_INVALID );
     336        1154 :     if( internalFormat == 0 )
     337           0 :         return;
     338             : }
     339             : 
     340           0 : uint32_t Image::getInternalFormat( const Frame::Buffer buffer ) const
     341             : {
     342           0 :     const Memory& memory = _impl->getMemory( buffer );
     343           0 :     LBASSERT( memory.internalFormat );
     344           0 :     return memory.internalFormat;
     345             : }
     346             : 
     347             : namespace
     348             : {
     349         663 : class CompressorFinder : public lunchbox::ConstPluginVisitor
     350             : {
     351             : public:
     352         663 :     CompressorFinder( const uint32_t token ) : token_( token ) {}
     353             : 
     354       45747 :     virtual fabric::VisitorResult visit( const lunchbox::Plugin&,
     355             :                                          const EqCompressorInfo& info )
     356             :     {
     357       45747 :         if( info.capabilities & EQ_COMPRESSOR_TRANSFER )
     358       19890 :             return fabric::TRAVERSE_CONTINUE;
     359             : 
     360       25857 :         if( info.tokenType == token_ )
     361        2613 :             result.push_back( info.name );
     362       25857 :         return fabric::TRAVERSE_CONTINUE;
     363             :     }
     364             : 
     365             :     std::vector< uint32_t > result;
     366             : 
     367             : private:
     368             :     const uint32_t token_;
     369             : };
     370             : }
     371             : 
     372         663 : std::vector< uint32_t > Image::findCompressors( const Frame::Buffer buffer )
     373             :     const
     374             : {
     375         663 :     const lunchbox::PluginRegistry& registry = co::Global::getPluginRegistry();
     376         663 :     CompressorFinder finder( getExternalFormat( buffer ));
     377         663 :     registry.accept( finder );
     378             : 
     379         663 :     LBLOG( lunchbox::LOG_PLUGIN )
     380           0 :         << "Found " << finder.result.size() << " compressors for token type 0x"
     381         663 :         << std::hex << getExternalFormat( buffer ) << std::dec << std::endl;
     382         663 :     return finder.result;
     383             : }
     384             : 
     385           0 : std::vector< uint32_t > Image::findTransferers( const Frame::Buffer buffer,
     386             :                                                 const GLEWContext* gl ) const
     387             : {
     388           0 :     std::vector< uint32_t > result;
     389           0 :     const EqCompressorInfos& infos = _impl->findTransferers( buffer, gl );
     390           0 :     for( EqCompressorInfosCIter i = infos.begin(); i != infos.end(); ++i )
     391           0 :         result.push_back( i->name );
     392           0 :     return result;
     393             : }
     394             : 
     395         354 : bool Image::hasAlpha() const
     396             : {
     397         708 :     return hasPixelData( Frame::BUFFER_COLOR ) &&
     398         708 :            _impl->getMemory( Frame::BUFFER_COLOR ).hasAlpha;
     399             : }
     400             : 
     401        1201 : void Image::setAlphaUsage( const bool enabled )
     402             : {
     403        1201 :     if( _impl->ignoreAlpha != enabled )
     404        2112 :         return;
     405             : 
     406         290 :     _impl->ignoreAlpha = !enabled;
     407         290 :     _impl->color.memory.compressedData = lunchbox::CompressorResult();
     408         290 :     _impl->depth.memory.compressedData = lunchbox::CompressorResult();
     409             : }
     410             : 
     411         841 : void Image::setQuality( const Frame::Buffer buffer, const float quality )
     412             : {
     413         841 :     Attachment& attachment = _impl->getAttachment( buffer );
     414         841 :     if( attachment.quality == quality )
     415        1682 :         return;
     416             : 
     417           0 :     attachment.quality = quality;
     418           0 :     if( quality >= 1.f )
     419           0 :         attachment.active = PLUGIN_FULL;
     420             :     else
     421             :     {
     422           0 :         attachment.active = PLUGIN_LOSSY;
     423           0 :         attachment.compressor[ PLUGIN_LOSSY ].clear();
     424           0 :         attachment.decompressor[ PLUGIN_LOSSY ].clear();
     425           0 :         attachment.downloader[ PLUGIN_LOSSY ].clear();
     426             :     }
     427             : }
     428             : 
     429           0 : float Image::getQuality( const Frame::Buffer buffer ) const
     430             : {
     431           0 :     return _impl->getAttachment( buffer ).quality;
     432             : }
     433             : 
     434           0 : bool Image::hasTextureData( const Frame::Buffer buffer ) const
     435             : {
     436           0 :     return getTexture( buffer ).isValid();
     437             : }
     438             : 
     439           0 : const util::Texture& Image::getTexture( const Frame::Buffer buffer ) const
     440             : {
     441           0 :     return _impl->getAttachment( buffer ).texture;
     442             : }
     443             : 
     444          94 : const uint8_t* Image::getPixelPointer( const Frame::Buffer buffer ) const
     445             : {
     446          94 :     LBASSERT( hasPixelData( buffer ));
     447          94 :     return reinterpret_cast< const uint8_t* >( _impl->getMemory( buffer ).pixels );
     448             : }
     449             : 
     450         146 : uint8_t* Image::getPixelPointer( const Frame::Buffer buffer )
     451             : {
     452         146 :     LBASSERT( hasPixelData( buffer ));
     453         146 :     return  reinterpret_cast< uint8_t* >( _impl->getMemory( buffer ).pixels );
     454             : }
     455             : 
     456          86 : const PixelData& Image::getPixelData( const Frame::Buffer buffer ) const
     457             : {
     458          86 :     LBASSERT( hasPixelData( buffer ));
     459          86 :     return _impl->getMemory( buffer );
     460             : }
     461             : 
     462           2 : bool Image::upload( const Frame::Buffer buffer, util::Texture* texture,
     463             :                     const Vector2i& position, util::ObjectManager& om ) const
     464             : {
     465             :     // freed by deleteGLObjects, e.g., called from Pipe::flushFrames()
     466             :     lunchbox::Uploader* uploader = om.obtainEqUploader(
     467           2 :                                         _getCompressorKey( buffer ));
     468           2 :     const PixelData& pixelData = getPixelData( buffer );
     469           2 :     const uint32_t externalFormat = pixelData.externalFormat;
     470           2 :     const uint32_t internalFormat = pixelData.internalFormat;
     471             :     const uint64_t flags = EQ_COMPRESSOR_TRANSFER | EQ_COMPRESSOR_DATA_2D |
     472           0 :                            ( texture ? texture->getCompressorTarget() :
     473           2 :                                        EQ_COMPRESSOR_USE_FRAMEBUFFER );
     474           2 :     const GLEWContext* const gl = om.glewGetContext();
     475             : 
     476           2 :     if( !uploader->supports( externalFormat, internalFormat, flags, gl ))
     477           2 :         uploader->setup( co::Global::getPluginRegistry(), externalFormat,
     478           2 :                          internalFormat, flags, gl );
     479             : 
     480           2 :     if( !uploader->isGood( gl ))
     481             :     {
     482           0 :         LBWARN << "No upload plugin for " << std::hex << externalFormat
     483           0 :                << " -> " << internalFormat << std::dec << " upload" <<std::endl;
     484           0 :         return false;
     485             :     }
     486             : 
     487           2 :     PixelViewport pvp = getPixelViewport();
     488           2 :     pvp.x = position.x() + pvp.x;
     489           2 :     pvp.y = position.y() + pvp.y;
     490           2 :     if( texture )
     491           0 :         texture->init( internalFormat, _impl->pvp.w, _impl->pvp.h );
     492             : 
     493             :     uint64_t inDims[4], outDims[4];
     494           2 :     pixelData.pvp.convertToPlugin( inDims );
     495           2 :     pvp.convertToPlugin( outDims );
     496             :     uploader->upload( pixelData.pixels, inDims, flags, outDims,
     497           2 :                       texture ? texture->getName() : 0, gl );
     498           2 :     return true;
     499             : }
     500             : 
     501             : //---------------------------------------------------------------------------
     502             : // asynchronous readback
     503             : //---------------------------------------------------------------------------
     504             : #ifndef EQ_2_0_API
     505           0 : bool Image::readback( const uint32_t buffers, const PixelViewport& pvp,
     506             :                       const Zoom& zoom, util::ObjectManager& glObjects )
     507             : {
     508           0 :     if( startReadback( buffers, pvp, zoom, glObjects ))
     509           0 :         finishReadback( zoom, glObjects.glewGetContext( ));
     510           0 :     return true;
     511             : }
     512             : 
     513           0 : void Image::finishReadback( const Zoom&, const GLEWContext* context )
     514             : {
     515           0 :     return finishReadback( context );
     516             : }
     517             : #endif
     518             : 
     519             : // TODO: 2.0 API: rename to readback and return Future
     520           3 : bool Image::startReadback( const uint32_t buffers, const PixelViewport& pvp,
     521             :                            const Zoom& zoom, util::ObjectManager& glObjects )
     522             : {
     523           3 :     LBLOG( LOG_ASSEMBLY ) << "startReadback " << pvp << ", buffers " << buffers
     524           3 :                           << std::endl;
     525             : 
     526           3 :     _impl->pvp = pvp;
     527           3 :     _impl->color.memory.state = Memory::INVALID;
     528           3 :     _impl->depth.memory.state = Memory::INVALID;
     529             : 
     530           6 :     bool needFinish = (buffers & Frame::BUFFER_COLOR) &&
     531           6 :                          _startReadback( Frame::BUFFER_COLOR, zoom, glObjects );
     532             : 
     533           3 :     if( (buffers & Frame::BUFFER_DEPTH) &&
     534           0 :         _startReadback( Frame::BUFFER_DEPTH, zoom, glObjects ))
     535             :     {
     536           0 :         needFinish = true;
     537             :     }
     538             : 
     539           3 :     _impl->pvp.x = 0;
     540           3 :     _impl->pvp.y = 0;
     541           3 :     return needFinish;
     542             : }
     543             : 
     544           3 : bool Image::_startReadback( const Frame::Buffer buffer, const Zoom& zoom,
     545             :                             util::ObjectManager& glObjects )
     546             : {
     547           3 :     Attachment& attachment = _impl->getAttachment( buffer );
     548           3 :     attachment.memory.compressedData = lunchbox::CompressorResult();
     549             : 
     550           3 :     if( _impl->type == Frame::TYPE_TEXTURE )
     551             :     {
     552           0 :         LBASSERTINFO( zoom == Zoom::NONE, "Texture readback zoom not " <<
     553             :                       "implemented, zoom happens during compositing" );
     554           0 :         util::Texture& texture = attachment.texture;
     555           0 :         texture.setGLEWContext( glObjects.glewGetContext( ));
     556           0 :         texture.copyFromFrameBuffer( getInternalFormat( buffer ), _impl->pvp );
     557           0 :         texture.setGLEWContext( 0 );
     558           0 :         return false;
     559             :     }
     560             : 
     561           3 :     attachment.zoom = zoom;
     562           3 :     if( zoom == Zoom::NONE ) // normal framebuffer readback
     563           3 :         return startReadback( buffer, 0, glObjects.glewGetContext( ));
     564             : 
     565             :     // else copy to texture, draw zoomed quad into FBO, (read FBO texture)
     566           0 :     return _readbackZoom( buffer, glObjects );
     567             : }
     568             : 
     569           3 : bool Image::startReadback( const Frame::Buffer buffer,
     570             :                            const util::Texture* texture, const GLEWContext* gl )
     571             : {
     572           3 :     Attachment& attachment = _impl->getAttachment( buffer );
     573           3 :     lunchbox::Downloader& downloader = attachment.downloader[attachment.active];
     574           3 :     Memory& memory = attachment.memory;
     575           3 :     const uint32_t inputToken = memory.internalFormat;
     576             : 
     577           3 :     uint32_t flags = EQ_COMPRESSOR_TRANSFER | EQ_COMPRESSOR_DATA_2D |
     578           0 :                      ( texture ? texture->getCompressorTarget() :
     579           3 :                                  EQ_COMPRESSOR_USE_FRAMEBUFFER );
     580           3 :     const bool noAlpha = _impl->ignoreAlpha && buffer == Frame::BUFFER_COLOR;
     581             : 
     582           3 :     if( !downloader.supports( inputToken, noAlpha, flags ))
     583           3 :         downloader.setup( co::Global::getPluginRegistry(), inputToken,
     584           6 :                            attachment.quality, noAlpha, flags, gl );
     585             : 
     586           3 :     if( !downloader.isGood( ))
     587             :     {
     588           0 :         LBWARN << "Download plugin initialization failed using input 0x"
     589           0 :                << std::hex << inputToken << std::dec << std::endl;
     590           0 :         return false;
     591             :     }
     592             : 
     593             :     // get the pixel type produced by the downloader
     594           3 :     const EqCompressorInfo& info = downloader.getInfo();
     595           3 :     const bool alpha = (info.capabilities & EQ_COMPRESSOR_IGNORE_ALPHA) == 0;
     596             :     _setExternalFormat( buffer, info.outputTokenType, info.outputTokenSize,
     597           3 :                         alpha );
     598           3 :     attachment.memory.state = Memory::DOWNLOAD;
     599             : 
     600           3 :     if( !memory.hasAlpha )
     601           0 :         flags |= EQ_COMPRESSOR_IGNORE_ALPHA;
     602             : 
     603           3 :     uint64_t outDims[4] = {0};
     604           3 :     if( texture )
     605             :     {
     606           0 :         LBASSERT( texture->isValid( ));
     607           0 :         const uint64_t inDims[4] = { 0ull, uint64_t( texture->getWidth( )),
     608           0 :                                      0ull, uint64_t( texture->getHeight( )) };
     609           0 :         if( downloader.start( &memory.pixels, inDims, flags, outDims,
     610           0 :                               texture->getName(), gl ))
     611             :         {
     612           0 :             return true;
     613             :         }
     614             :     }
     615             :     else
     616             :     {
     617             :         uint64_t inDims[4];
     618           3 :         _impl->pvp.convertToPlugin( inDims );
     619           3 :         if( downloader.start( &memory.pixels, inDims, flags, outDims, 0, gl ))
     620           3 :             return true;
     621             :     }
     622             : 
     623           0 :     memory.pvp.convertFromPlugin( outDims );
     624           0 :     attachment.memory.state = Memory::VALID;
     625           0 :     return false;
     626             : }
     627             : 
     628           3 : void Image::finishReadback( const GLEWContext* context )
     629             : {
     630           3 :     LBASSERT( context );
     631           3 :     LBLOG( LOG_ASSEMBLY ) << "finishReadback" << std::endl;
     632             : 
     633           3 :     _finishReadback( Frame::BUFFER_COLOR, context );
     634           3 :     _finishReadback( Frame::BUFFER_DEPTH, context );
     635             : 
     636             : #ifndef NDEBUG
     637           3 :     if( getenv( "EQ_DUMP_IMAGES" ))
     638             :     {
     639           0 :         static a_int32_t counter;
     640           0 :         std::ostringstream stringstream;
     641             : 
     642           0 :         stringstream << "Image_" << std::setfill( '0' ) << std::setw(5)
     643           0 :                      << ++counter;
     644           0 :         writeImages( stringstream.str( ));
     645             :     }
     646             : #endif
     647           3 : }
     648             : 
     649           6 : void Image::_finishReadback( const Frame::Buffer buffer,
     650             :                              const GLEWContext* context )
     651             : {
     652           6 :     if( _impl->type == Frame::TYPE_TEXTURE )
     653           3 :         return;
     654             : 
     655           6 :     Attachment& attachment = _impl->getAttachment( buffer );
     656           6 :     Memory& memory = attachment.memory;
     657           6 :     if( memory.state != Memory::DOWNLOAD )
     658           3 :         return;
     659             : 
     660           3 :     lunchbox::Downloader& downloader = attachment.downloader[attachment.active];
     661           3 :     const uint32_t inputToken = memory.internalFormat;
     662           3 :     const bool alpha = _impl->ignoreAlpha && buffer == Frame::BUFFER_COLOR;
     663             :     uint32_t flags = EQ_COMPRESSOR_TRANSFER | EQ_COMPRESSOR_DATA_2D |
     664           3 :         ( attachment.zoom == Zoom::NONE ? EQ_COMPRESSOR_USE_FRAMEBUFFER :
     665           3 :                                           EQ_COMPRESSOR_USE_TEXTURE_RECT );
     666             : 
     667           3 :     if( !downloader.supports( inputToken, alpha, flags ))
     668             :     {
     669           0 :         LBWARN << "Download plugin initialization failed" << std::endl;
     670           0 :         attachment.memory.state = Memory::INVALID;
     671           0 :         return;
     672             :     }
     673             : 
     674           3 :      flags |= ( memory.hasAlpha ? 0 : EQ_COMPRESSOR_IGNORE_ALPHA );
     675             : 
     676           3 :     uint64_t outDims[4] = {0};
     677             :     uint64_t inDims[4];
     678           3 :     PixelViewport pvp = _impl->pvp;
     679           3 :     if( attachment.zoom != Zoom::NONE )
     680             :     {
     681           0 :         pvp.apply( attachment.zoom );
     682           0 :         pvp.x = 0;
     683           0 :         pvp.y = 0;
     684             :     }
     685           3 :     _impl->pvp.convertToPlugin( inDims );
     686             : 
     687           3 :     downloader.finish( &memory.pixels, inDims, flags, outDims, context );
     688           3 :     memory.pvp.convertFromPlugin( outDims );
     689           3 :     memory.state = Memory::VALID;
     690             : }
     691             : 
     692           0 : bool Image::_readbackZoom( const Frame::Buffer buffer, util::ObjectManager& om )
     693             : {
     694           0 :     LBASSERT( om.supportsEqTexture( ));
     695           0 :     LBASSERT( om.supportsEqFrameBufferObject( ));
     696             : 
     697           0 :     const Attachment& attachment = _impl->getAttachment( buffer );
     698           0 :     PixelViewport pvp = _impl->pvp;
     699           0 :     pvp.apply( attachment.zoom );
     700           0 :     if( !pvp.hasArea( ))
     701           0 :         return false;
     702             : 
     703             :     // copy frame buffer to texture
     704           0 :     const uint32_t inputToken = attachment.memory.internalFormat;
     705           0 :     const void* bufferKey = _getBufferKey( buffer );
     706             :     util::Texture* texture = om.obtainEqTexture( bufferKey,
     707           0 :                                                  GL_TEXTURE_RECTANGLE_ARB );
     708           0 :     texture->copyFromFrameBuffer( inputToken, _impl->pvp );
     709             : 
     710             :     // draw zoomed quad into FBO
     711             :     //  uses the same FBO for color and depth, with masking.
     712           0 :     const void* fboKey = _getBufferKey( Frame::BUFFER_COLOR );
     713           0 :     util::FrameBufferObject* fbo = om.getEqFrameBufferObject( fboKey );
     714             : 
     715           0 :     if( fbo )
     716             :     {
     717           0 :         LBCHECK( fbo->resize( pvp.w, pvp.h ));
     718             :     }
     719             :     else
     720             :     {
     721           0 :         fbo = om.newEqFrameBufferObject( fboKey );
     722           0 :         LBCHECK( fbo->init( pvp.w, pvp.h, inputToken, 24, 0 ));
     723             :     }
     724           0 :     fbo->bind();
     725           0 :     texture->bind();
     726             : 
     727           0 :     if( buffer == Frame::BUFFER_COLOR )
     728           0 :         glDepthMask( false );
     729             :     else
     730             :     {
     731           0 :         LBASSERT( buffer == Frame::BUFFER_DEPTH )
     732           0 :         glColorMask( false, false, false, false );
     733             :     }
     734             : 
     735           0 :     glDisable( GL_LIGHTING );
     736           0 :     glEnable( GL_TEXTURE_RECTANGLE_ARB );
     737           0 :     texture->applyZoomFilter( FILTER_LINEAR );
     738           0 :     glColor3f( 1.0f, 1.0f, 1.0f );
     739             : 
     740           0 :     glBegin( GL_QUADS );
     741           0 :         glTexCoord2f( 0.0f, 0.0f );
     742           0 :         glVertex3f( 0, 0, 0.0f );
     743             : 
     744           0 :         glTexCoord2f( static_cast< float >( _impl->pvp.w ), 0.0f );
     745           0 :         glVertex3f( static_cast< float >( pvp.w ), 0, 0.0f );
     746             : 
     747             :         glTexCoord2f( static_cast< float >( _impl->pvp.w ),
     748           0 :                       static_cast< float >( _impl->pvp.h ));
     749             :         glVertex3f( static_cast< float >( pvp.w ),
     750           0 :                     static_cast< float >( pvp.h ), 0.0f );
     751             : 
     752           0 :         glTexCoord2f( 0.0f, static_cast< float >( _impl->pvp.h ));
     753           0 :         glVertex3f( 0, static_cast< float >( pvp.h ), 0.0f );
     754           0 :     glEnd();
     755             : 
     756             :     // restore state
     757           0 :     glDisable( GL_TEXTURE_RECTANGLE_ARB );
     758             :     // TODO channel->bindFramebuffer()
     759           0 :     glBindTexture( GL_TEXTURE_RECTANGLE_ARB, 0 );
     760           0 :     fbo->unbind();
     761             : 
     762           0 :     const util::Texture* zoomedTexture = 0;
     763           0 :     if( buffer == Frame::BUFFER_COLOR )
     764             :     {
     765           0 :         glDepthMask( true );
     766           0 :         zoomedTexture = fbo->getColorTextures().front();
     767             :     }
     768             :     else
     769             :     {
     770           0 :         const ColorMask colorMask; // TODO = channel->getDrawBufferMask();
     771           0 :         glColorMask( colorMask.red, colorMask.green, colorMask.blue, true );
     772           0 :         zoomedTexture = &fbo->getDepthTexture();
     773             :     }
     774           0 :     LBASSERT( zoomedTexture->isValid( ));
     775           0 :     LBLOG( LOG_ASSEMBLY ) << "Scale " << _impl->pvp << " -> " << pvp << std::endl;
     776             : 
     777             :     // BUG TODO: this is a bug in case of color and depth buffers read-back, as
     778             :     // _impl->pvp will be incorrect for the depth buffer!
     779             :     //
     780             :     // This should be done separately for color an depth buffers!
     781           0 :     _impl->pvp = pvp;
     782             : 
     783           0 :     LBLOG( LOG_ASSEMBLY ) << "Read texture " << getPixelDataSize( buffer )
     784           0 :                           << std::endl;
     785           0 :     return startReadback( buffer, zoomedTexture, om.glewGetContext( ));
     786             : }
     787             : 
     788        1472 : void Image::setPixelViewport( const PixelViewport& pvp )
     789             : {
     790        1472 :     _impl->pvp = pvp;
     791        1472 :     _impl->color.memory.state = Memory::INVALID;
     792        1472 :     _impl->depth.memory.state = Memory::INVALID;
     793        1472 :     _impl->color.memory.compressedData = lunchbox::CompressorResult();
     794        1472 :     _impl->depth.memory.compressedData = lunchbox::CompressorResult();
     795        1472 : }
     796             : 
     797          12 : void Image::clearPixelData( const Frame::Buffer buffer )
     798             : {
     799          12 :     Memory& memory = _impl->getAttachment( buffer ).memory;
     800          12 :     memory.pvp = _impl->pvp;
     801          12 :     const ssize_t size = getPixelDataSize( buffer );
     802          12 :     if( size == 0 )
     803          12 :         return;
     804             : 
     805          12 :     validatePixelData( buffer );
     806             : 
     807          12 :     switch( memory.externalFormat )
     808             :     {
     809             :       case EQ_COMPRESSOR_DATATYPE_DEPTH_UNSIGNED_INT:
     810           3 :         memset( memory.pixels, 0xFF, size );
     811           3 :         break;
     812             : 
     813             :       case EQ_COMPRESSOR_DATATYPE_RGBA:
     814             :       case EQ_COMPRESSOR_DATATYPE_BGRA:
     815             :       {
     816           9 :         uint8_t* data = reinterpret_cast< uint8_t* >( memory.pixels );
     817             : #ifdef Darwin
     818             :         const unsigned char pixel[4] = { 0, 0, 0, 255 };
     819             :         memset_pattern4( data, &pixel, size );
     820             : #else
     821           9 :         lunchbox::setZero( data, size );
     822          68 : #pragma omp parallel for
     823          59 :         for( ssize_t i = 3; i < size; i+=4 )
     824     2393978 :             data[i] = 255;
     825             : #endif
     826           9 :         break;
     827             :       }
     828             :       default:
     829           0 :         LBWARN << "Unknown external format " << memory.externalFormat
     830           0 :                << ", initializing to 0" << std::endl;
     831           0 :         lunchbox::setZero( memory.pixels, size );
     832           0 :         break;
     833             :     }
     834             : }
     835             : 
     836         874 : void Image::validatePixelData( const Frame::Buffer buffer )
     837             : {
     838         874 :     Memory& memory = _impl->getAttachment( buffer ).memory;
     839         874 :     memory.useLocalBuffer();
     840         874 :     memory.state = Memory::VALID;
     841         874 :     memory.compressedData = lunchbox::CompressorResult();
     842         874 : }
     843             : 
     844         146 : void Image::setPixelData( const Frame::Buffer buffer, const PixelData& pixels )
     845             : {
     846         146 :     Memory& memory = _impl->getMemory( buffer );
     847         146 :     memory.externalFormat = pixels.externalFormat;
     848         146 :     memory.internalFormat = pixels.internalFormat;
     849         146 :     memory.pixelSize = pixels.pixelSize;
     850         146 :     memory.pvp       = pixels.pvp;
     851         146 :     memory.state     = Memory::INVALID;
     852         146 :     memory.compressedData = lunchbox::CompressorResult();
     853         146 :     memory.hasAlpha = false;
     854             : 
     855             :     const EqCompressorInfos& transferrers = _impl->findTransferers( buffer,
     856         146 :                                                            0 /*GLEW context*/ );
     857         146 :     if( transferrers.empty( ))
     858           0 :         LBWARN << "No upload engines found for given pixel data" << std::endl;
     859             :     else
     860             :     {
     861             :         memory.hasAlpha =
     862         146 :             transferrers.front().capabilities & EQ_COMPRESSOR_IGNORE_ALPHA;
     863             : #ifndef NDEBUG
     864         876 :         for( EqCompressorInfosCIter i = transferrers.begin();
     865         584 :              i != transferrers.end(); ++i )
     866             :         {
     867         146 :             LBASSERTINFO( memory.hasAlpha ==
     868             :                           bool( i->capabilities & EQ_COMPRESSOR_IGNORE_ALPHA ),
     869             :                           "Uploaders don't agree on alpha state of external " <<
     870             :                           "format: " << transferrers.front() << " != " << *i );
     871             :         }
     872             : #endif
     873             :     }
     874             : 
     875         146 :     const uint32_t size = getPixelDataSize( buffer );
     876         146 :     LBASSERT( size > 0 );
     877         146 :     if( size == 0 )
     878           0 :         return;
     879             : 
     880         146 :     if( pixels.compressedData.compressor <= EQ_COMPRESSOR_NONE )
     881             :     {
     882          12 :         validatePixelData( buffer ); // alloc memory for pixels
     883             : 
     884          12 :         if( pixels.pixels )
     885             :         {
     886           0 :             memcpy( memory.pixels, pixels.pixels, size );
     887           0 :             memory.state = Memory::VALID;
     888             :         }
     889             :         else
     890             :             // no data in pixels, clear image buffer
     891          12 :             clearPixelData( buffer );
     892             : 
     893          12 :         return;
     894             :     }
     895             : 
     896         134 :     LBASSERT( !pixels.compressedData.chunks.empty( ));
     897         134 :     LBASSERT( pixels.compressedData.compressor != EQ_COMPRESSOR_AUTO );
     898             : 
     899         134 :     Attachment& attachment = _impl->getAttachment( buffer );
     900         268 :     if( !attachment.decompressor->setup( co::Global::getPluginRegistry(),
     901         268 :                                          pixels.compressedData.compressor ))
     902             :     {
     903           0 :         LBASSERTINFO( false,
     904             :                       "Can't allocate decompressor " <<
     905             :                       pixels.compressedData.compressor <<
     906             :                       ", mismatched compression plugin installation?" );
     907           0 :         return;
     908             :     }
     909             : 
     910         134 :     const EqCompressorInfo& info = attachment.decompressor->getInfo();
     911         134 :     LBASSERTINFO( info.name == pixels.compressedData.compressor, info );
     912             : 
     913         134 :     if( memory.externalFormat != info.outputTokenType )
     914             :     {
     915             :         // decompressor output differs from compressor input
     916           0 :         memory.externalFormat = info.outputTokenType;
     917           0 :         memory.pixelSize = info.outputTokenSize;
     918             :     }
     919         134 :     validatePixelData( buffer ); // alloc memory for pixels
     920             : 
     921             :     uint64_t outDims[4];
     922         134 :     memory.pvp.convertToPlugin( outDims );
     923             : 
     924             :     attachment.decompressor->decompress( pixels.compressedData, memory.pixels,
     925         134 :                                          outDims, pixels.compressorFlags );
     926             : }
     927             : 
     928             : /** Find and activate a compression engine */
     929        1221 : bool Image::allocCompressor( const Frame::Buffer buffer, const uint32_t name )
     930             : {
     931        1221 :     Attachment& attachment = _impl->getAttachment( buffer );
     932        1221 :     lunchbox::Compressor& compressor = attachment.compressor[attachment.active];
     933        1221 :     if( name <= EQ_COMPRESSOR_NONE )
     934             :     {
     935        1154 :         attachment.memory.compressedData = lunchbox::CompressorResult();
     936        1154 :         compressor.clear();
     937        1154 :         return true;
     938             :     }
     939             : 
     940          67 :     if( compressor.uses( name ))
     941          42 :         return true;
     942             : 
     943          25 :     attachment.memory.compressedData = lunchbox::CompressorResult();
     944          25 :     compressor.setup( co::Global::getPluginRegistry(), name );
     945          25 :     LBLOG( LOG_PLUGIN ) << "Instantiated compressor of type 0x" << std::hex
     946          25 :                         << name << std::dec << std::endl;
     947          25 :     return compressor.isGood();
     948             : }
     949             : 
     950             : /** Find and activate a compression engine */
     951           0 : bool Image::allocDownloader( const Frame::Buffer buffer, const uint32_t name,
     952             :                              const GLEWContext* gl )
     953             : {
     954           0 :     LBASSERT( name > EQ_COMPRESSOR_NONE )
     955           0 :     LBASSERT( gl );
     956             : 
     957           0 :     Attachment& attachment = _impl->getAttachment( buffer );
     958           0 :     lunchbox::Downloader& downloader = attachment.downloader[attachment.active];
     959             : 
     960           0 :     if( name <= EQ_COMPRESSOR_NONE )
     961             :     {
     962           0 :         downloader.clear();
     963           0 :         _setExternalFormat( buffer, EQ_COMPRESSOR_DATATYPE_NONE, 0, true );
     964           0 :         return false;
     965             :     }
     966             : 
     967             : 
     968           0 :     if( downloader.uses( name ))
     969           0 :         return true;
     970             : 
     971           0 :     if( !downloader.setup( co::Global::getPluginRegistry(), name, gl ))
     972           0 :         return false;
     973             : 
     974           0 :     const EqCompressorInfo& info = downloader.getInfo();
     975           0 :     attachment.memory.internalFormat = info.tokenType;
     976             :     _setExternalFormat( buffer, info.outputTokenType, info.outputTokenSize,
     977           0 :                         !(info.capabilities & EQ_COMPRESSOR_IGNORE_ALPHA) );
     978           0 :     return true;
     979             : }
     980             : 
     981           2 : uint32_t Image::getDownloaderName( const Frame::Buffer buffer ) const
     982             : {
     983           2 :     const Attachment& attachment = _impl->getAttachment( buffer );
     984             :     const lunchbox::Downloader& downloader =
     985           2 :         attachment.downloader[attachment.active];
     986           2 :     if( downloader.isGood( ))
     987           2 :         return downloader.getInfo().name;
     988           0 :     return EQ_COMPRESSOR_INVALID;
     989             : }
     990             : 
     991          16 : void Image::useCompressor( const Frame::Buffer buffer, const uint32_t name )
     992             : {
     993          16 :     _impl->getMemory( buffer ).compressorName = name;
     994          16 : }
     995             : 
     996         134 : const PixelData& Image::compressPixelData( const Frame::Buffer buffer )
     997             : {
     998         134 :     LBASSERT( getPixelDataSize( buffer ) > 0 );
     999             : 
    1000         134 :     Attachment& attachment = _impl->getAttachment( buffer );
    1001         134 :     Memory& memory = attachment.memory;
    1002         268 :     if( memory.compressedData.isCompressed() ||
    1003         134 :         memory.compressorName == EQ_COMPRESSOR_NONE )
    1004             :     {
    1005           0 :         LBASSERT( memory.compressorName != EQ_COMPRESSOR_AUTO );
    1006           0 :         return memory;
    1007             :     }
    1008             : 
    1009         134 :     lunchbox::Compressor& compressor = attachment.compressor[attachment.active];
    1010             : 
    1011         402 :     if( !compressor.isGood() ||
    1012         268 :         compressor.getInfo().tokenType != getExternalFormat( buffer ) ||
    1013         134 :         memory.compressorName == EQ_COMPRESSOR_AUTO )
    1014             :     {
    1015           0 :         if( memory.compressorName == EQ_COMPRESSOR_AUTO )
    1016             :         {
    1017           0 :             const uint32_t tokenType = getExternalFormat( buffer );
    1018             :             const float downloadQuality =
    1019           0 :                 attachment.downloader[ attachment.active ].getInfo().quality;
    1020           0 :             const float quality = attachment.quality / downloadQuality;
    1021             : 
    1022           0 :             compressor.setup( co::Global::getPluginRegistry(), tokenType,
    1023           0 :                                quality, _impl->ignoreAlpha );
    1024             :         }
    1025             :         else
    1026           0 :             compressor.setup( co::Global::getPluginRegistry(),
    1027           0 :                               memory.compressorName );
    1028             : 
    1029           0 :         if( !compressor.isGood( ))
    1030             :         {
    1031           0 :             LBWARN << "No compressor found for token type 0x" << std::hex
    1032           0 :                    << getExternalFormat( buffer ) << std::dec << std::endl;
    1033           0 :             compressor.clear();
    1034             :         }
    1035             :     }
    1036             : 
    1037         134 :     memory.compressedData.compressor = compressor.getInfo().name;
    1038         134 :     LBASSERT( memory.compressedData.compressor != EQ_COMPRESSOR_AUTO );
    1039         134 :     LBASSERT( memory.compressedData.compressor != EQ_COMPRESSOR_INVALID );
    1040         134 :     if( memory.compressedData.compressor == EQ_COMPRESSOR_NONE )
    1041           0 :         return memory;
    1042             : 
    1043         134 :     memory.compressorFlags = EQ_COMPRESSOR_DATA_2D;
    1044         134 :     if( _impl->ignoreAlpha && memory.hasAlpha )
    1045             :     {
    1046          66 :         LBASSERT( buffer == Frame::BUFFER_COLOR );
    1047          66 :         memory.compressorFlags |= EQ_COMPRESSOR_IGNORE_ALPHA;
    1048             :     }
    1049             : 
    1050             :     uint64_t inDims[4];
    1051         134 :     memory.pvp.convertToPlugin( inDims );
    1052         134 :     compressor.compress( memory.pixels, inDims, memory.compressorFlags );
    1053         134 :     memory.compressedData = compressor.getResult();
    1054         134 :     return memory;
    1055             : }
    1056             : 
    1057             : 
    1058             : //---------------------------------------------------------------------------
    1059             : // File IO
    1060             : //---------------------------------------------------------------------------
    1061           3 : bool Image::writeImages( const std::string& filenameTemplate ) const
    1062             : {
    1063          15 :     return( writeImage( filenameTemplate + "_color.rgb", Frame::BUFFER_COLOR) &&
    1064          12 :             writeImage( filenameTemplate + "_depth.rgb", Frame::BUFFER_DEPTH ));
    1065             : }
    1066             : 
    1067             : namespace
    1068             : {
    1069             : #define SWAP_SHORT(v) ( v = (v&0xff) << 8 | (v&0xff00) >> 8 )
    1070             : #define SWAP_INT(v)   ( v = (v&0xff) << 24 | (v&0xff00) << 8 |      \
    1071             :                         (v&0xff0000) >> 8 | (v&0xff000000) >> 24)
    1072             : 
    1073             : #ifdef _WIN32
    1074             : #  pragma pack(1)
    1075             : #endif
    1076             : /** @cond IGNORE */
    1077             : struct RGBHeader
    1078             : {
    1079         726 :     RGBHeader()
    1080             :     {
    1081         726 :         memset( this, 0, sizeof( RGBHeader ));
    1082         726 :         magic           = 474;
    1083         726 :         bytesPerChannel = 1;
    1084         726 :         nDimensions     = 3;
    1085         726 :         maxValue        = 255;
    1086         726 :     }
    1087             : 
    1088             :     /**
    1089             :      * Convert to and from big endian by swapping bytes on little endian
    1090             :      * machines.
    1091             :      */
    1092         740 :     void convert()
    1093             :     {
    1094             : #if defined(__i386__) || defined(__amd64__) || defined (__ia64) || \
    1095             :     defined(__x86_64) || defined(_WIN32)
    1096         740 :             SWAP_SHORT(magic);
    1097         740 :             SWAP_SHORT(nDimensions);
    1098         740 :             SWAP_SHORT(width);
    1099         740 :             SWAP_SHORT(height);
    1100         740 :             SWAP_SHORT(depth);
    1101         740 :             SWAP_INT(minValue);
    1102         740 :             SWAP_INT(maxValue);
    1103         740 :             SWAP_INT(colorMode);
    1104             : #endif
    1105         740 :     }
    1106             : 
    1107             :     unsigned short magic;
    1108             :     char compression;
    1109             :     char bytesPerChannel;
    1110             :     unsigned short nDimensions;
    1111             :     unsigned short width;
    1112             :     unsigned short height;
    1113             :     unsigned short depth;
    1114             :     unsigned minValue;
    1115             :     unsigned maxValue;
    1116             :     char unused[4];
    1117             :     char filename[80];
    1118             :     unsigned colorMode;
    1119             :     char fill[404];
    1120             : }
    1121             : /** @endcond */
    1122             : #ifndef _WIN32
    1123             :   __attribute__((packed))
    1124             : #endif
    1125             : ;
    1126             : 
    1127     4608000 : void put32f( std::ostream& os, const char* ptr )
    1128             : {
    1129             :     // cppcheck-suppress invalidPointerCast
    1130     4608000 :     const float& value = *reinterpret_cast< const float* >( ptr );
    1131     4608000 :     const uint8_t byte = uint8_t( value * 255.f );
    1132     4608000 :     os.write( (const char*)&byte, 1 );
    1133     4608000 : }
    1134     2304000 : void put16f( std::ostream& os, const char* ptr )
    1135             : {
    1136     2304000 :     const uint16_t& value = *reinterpret_cast< const uint16_t* >(ptr);
    1137     2304000 :     const float f = half_to_float( value );
    1138     2304000 :     put32f( os, (const char*)&f );
    1139     2304000 : }
    1140             : }
    1141             : 
    1142          12 : bool Image::writeImage( const std::string& filename,
    1143             :                         const Frame::Buffer buffer ) const
    1144             : {
    1145          12 :     const Memory& memory = _impl->getMemory( buffer );
    1146             : 
    1147          12 :     const PixelViewport& pvp = memory.pvp;
    1148          12 :     const size_t nPixels = pvp.w * pvp.h;
    1149             : 
    1150          12 :     if( nPixels == 0 || memory.state != Memory::VALID )
    1151           2 :         return false;
    1152             : 
    1153          10 :     std::ofstream image( filename.c_str(), std::ios::out | std::ios::binary );
    1154          10 :     if( !image.is_open( ))
    1155             :     {
    1156           0 :         LBERROR << "Can't open " << filename << " for writing" << std::endl;
    1157           0 :         return false;
    1158             :     }
    1159             : 
    1160          10 :     RGBHeader header;
    1161          10 :     header.width  = pvp.w;
    1162          10 :     header.height = pvp.h;
    1163             : 
    1164          10 :     switch( getExternalFormat( buffer ))
    1165             :     {
    1166             :         case EQ_COMPRESSOR_DATATYPE_RGB10_A2:
    1167           1 :             header.maxValue = 1023;
    1168             :         case EQ_COMPRESSOR_DATATYPE_BGRA:
    1169             :         case EQ_COMPRESSOR_DATATYPE_BGRA_UINT_8_8_8_8_REV:
    1170             :         case EQ_COMPRESSOR_DATATYPE_RGBA:
    1171             :         case EQ_COMPRESSOR_DATATYPE_RGBA_UINT_8_8_8_8_REV:
    1172           7 :             header.bytesPerChannel = 1;
    1173           7 :             header.depth = 4;
    1174           7 :             break;
    1175             :         case EQ_COMPRESSOR_DATATYPE_BGR:
    1176             :         case EQ_COMPRESSOR_DATATYPE_RGB:
    1177           0 :             header.bytesPerChannel = 1;
    1178           0 :             header.depth = 3;
    1179           0 :             break;
    1180             :         case EQ_COMPRESSOR_DATATYPE_BGRA32F:
    1181             :         case EQ_COMPRESSOR_DATATYPE_RGBA32F:
    1182           1 :             header.bytesPerChannel = 4;
    1183           1 :             header.depth = 4;
    1184           1 :             break;
    1185             :         case EQ_COMPRESSOR_DATATYPE_BGR32F:
    1186             :         case EQ_COMPRESSOR_DATATYPE_RGB32F:
    1187           0 :             header.bytesPerChannel = 4;
    1188           0 :             header.depth = 3;
    1189           0 :             break;
    1190             :         case EQ_COMPRESSOR_DATATYPE_BGRA16F:
    1191             :         case EQ_COMPRESSOR_DATATYPE_RGBA16F:
    1192           1 :             header.bytesPerChannel = 2;
    1193           1 :             header.depth = 4;
    1194           1 :             break;
    1195             :         case EQ_COMPRESSOR_DATATYPE_BGR16F:
    1196             :         case EQ_COMPRESSOR_DATATYPE_RGB16F:
    1197           0 :             header.bytesPerChannel = 2;
    1198           0 :             header.depth = 3;
    1199           0 :             break;
    1200             :         case EQ_COMPRESSOR_DATATYPE_DEPTH_UNSIGNED_INT:
    1201           1 :             header.bytesPerChannel = 4;
    1202           1 :             header.depth = 1;
    1203           1 :             break;
    1204             : 
    1205             :         default:
    1206           0 :             LBERROR << "Unknown image pixel data type" << std::endl;
    1207           0 :             return false;
    1208             :     }
    1209             : 
    1210             :     // Swap red & blue where needed
    1211          10 :     bool swapRB = false;
    1212          10 :     switch( getExternalFormat( buffer ))
    1213             :     {
    1214             :         case EQ_COMPRESSOR_DATATYPE_RGB10_A2:
    1215             :         case EQ_COMPRESSOR_DATATYPE_RGBA:
    1216             :         case EQ_COMPRESSOR_DATATYPE_RGBA_UINT_8_8_8_8_REV:
    1217             :         case EQ_COMPRESSOR_DATATYPE_RGB:
    1218             :         case EQ_COMPRESSOR_DATATYPE_RGBA32F:
    1219             :         case EQ_COMPRESSOR_DATATYPE_RGB32F:
    1220             :         case EQ_COMPRESSOR_DATATYPE_RGBA16F:
    1221             :         case EQ_COMPRESSOR_DATATYPE_RGB16F:
    1222           8 :             swapRB = true;
    1223             :     }
    1224             : 
    1225          10 :     if( header.depth == 1 ) // depth
    1226             :     {
    1227           1 :         LBASSERT( (header.bytesPerChannel % 4) == 0 );
    1228           1 :         header.depth = 4;
    1229           1 :         header.bytesPerChannel /= 4;
    1230             :     }
    1231          10 :     LBASSERT( header.bytesPerChannel > 0 );
    1232             : 
    1233          10 :     strncpy( header.filename, filename.c_str(), 80 );
    1234             : 
    1235          10 :     if( header.bytesPerChannel > 2 )
    1236           3 :         LBWARN << static_cast< int >( header.bytesPerChannel )
    1237           3 :                << " bytes per channel not supported by RGB spec" << std::endl;
    1238             : 
    1239          10 :     header.convert();
    1240          10 :     image.write( reinterpret_cast<const char *>( &header ), sizeof( header ));
    1241          10 :     header.convert();
    1242             : 
    1243             :     // Each channel is saved separately
    1244          10 :     const uint8_t bpc = header.bytesPerChannel;
    1245          10 :     const uint16_t nChannels = header.depth;
    1246          10 :     const size_t depth  = nChannels * bpc;
    1247          10 :     const size_t nBytes = nPixels * depth;
    1248          10 :     const char* data = reinterpret_cast<const char*>( getPixelPointer( buffer));
    1249             : 
    1250          10 :     if( nChannels == 3 || nChannels == 4 )
    1251             :     {
    1252             :         // channel one is R or B
    1253          10 :         if ( swapRB )
    1254    10278472 :             for( size_t j = 0 * bpc; j < nBytes; j += depth )
    1255    10278464 :                 image.write( &data[j], bpc );
    1256             :         else
    1257     1315722 :             for( size_t j = 2 * bpc; j < nBytes; j += depth )
    1258     1315720 :                 image.write( &data[j], bpc );
    1259             : 
    1260             :         // channel two is G
    1261    11594194 :         for( size_t j = 1 * bpc; j < nBytes; j += depth )
    1262    11594184 :             image.write( &data[j], bpc );
    1263             : 
    1264             :         // channel three is B or G
    1265          10 :         if ( swapRB )
    1266    10278472 :             for( size_t j = 2 * bpc; j < nBytes; j += depth )
    1267    10278464 :                 image.write( &data[j], bpc );
    1268             :         else
    1269     1315722 :             for( size_t j = 0; j < nBytes; j += depth )
    1270     1315720 :                 image.write( &data[j], bpc );
    1271             : 
    1272             :         // channel four is Alpha
    1273          10 :         if( nChannels == 4 )
    1274    11594194 :             for( size_t j = 3 * bpc; j < nBytes; j += depth )
    1275    11594194 :                 image.write( &data[j], bpc );
    1276             :     }
    1277             :     else
    1278             :     {
    1279           0 :         for( size_t i = 0; i < nChannels; i += bpc )
    1280           0 :            for( size_t j = i * bpc; j < nBytes; j += depth )
    1281           0 :               image.write(&data[j], bpc );
    1282             :     }
    1283          10 :     image.close();
    1284             : 
    1285          10 :     if( header.bytesPerChannel == 1 )
    1286           8 :         return true;
    1287             :     // else also write 8bpp version
    1288             : 
    1289           2 :     const boost::filesystem::path path( filename );
    1290           4 :     const std::string smallFilename = path.parent_path().string() + "/s_" +
    1291             : #if BOOST_FILESYSTEM_VERSION == 3
    1292           8 :                                       path.filename().string();
    1293             : #else
    1294             :                                       path.filename();
    1295             : #endif
    1296           2 :     image.open( smallFilename.c_str(), std::ios::out | std::ios::binary );
    1297           2 :     if( !image.is_open( ))
    1298             :     {
    1299           0 :         LBERROR << "Can't open " << smallFilename << " for writing" <<std::endl;
    1300           0 :         return false;
    1301             :     }
    1302             : 
    1303           2 :     header.bytesPerChannel = 1;
    1304           2 :     header.maxValue = 255;
    1305           2 :     header.convert();
    1306           2 :     image.write( reinterpret_cast<const char *>( &header ), sizeof( header ));
    1307           2 :     header.convert();
    1308             : 
    1309           2 :     LBASSERTINFO( bpc == 2 || bpc == 4, bpc );
    1310           2 :     const bool twoBPC = bpc == 2;
    1311             : 
    1312           2 :     if( nChannels == 3 || nChannels == 4 )
    1313             :     {
    1314             :         // channel one is R or B
    1315           2 :         if ( swapRB )
    1316     1152002 :             for( size_t j = 0 * bpc; j < nBytes; j += depth )
    1317     1152000 :                 twoBPC ? put16f( image, &data[j] ) : put32f( image, &data[j] );
    1318             :         else
    1319           0 :             for( size_t j = 2 * bpc; j < nBytes; j += depth )
    1320           0 :                 twoBPC ? put16f( image, &data[j] ) : put32f( image, &data[j] );
    1321             : 
    1322             :         // channel two is G
    1323     1152002 :         for( size_t j = 1 * bpc; j < nBytes; j += depth )
    1324     1152000 :                 twoBPC ? put16f( image, &data[j] ) : put32f( image, &data[j] );
    1325             : 
    1326             :         // channel three is B or G
    1327           2 :         if ( swapRB )
    1328     1152002 :             for( size_t j = 2 * bpc; j < nBytes; j += depth )
    1329     1152000 :                 twoBPC ? put16f( image, &data[j] ) : put32f( image, &data[j] );
    1330             :         else
    1331           0 :             for( size_t j = 0; j < nBytes; j += depth )
    1332           0 :                 twoBPC ? put16f( image, &data[j] ) : put32f( image, &data[j] );
    1333             : 
    1334             :          // channel four is Alpha
    1335           2 :         if( nChannels == 4 )
    1336     1152002 :             for( size_t j = 3 * bpc; j < nBytes; j += depth )
    1337     1152002 :                 twoBPC ? put16f( image, &data[j] ) : put32f( image, &data[j] );
    1338             :     }
    1339             :     else
    1340             :     {
    1341           0 :         for( size_t i = 0; i < nChannels; i += bpc )
    1342           0 :            for( size_t j = i * bpc; j < nBytes; j += depth )
    1343           0 :                twoBPC ? put16f( image, &data[j] ) : put32f( image, &data[j] );
    1344             :     }
    1345           2 :     image.close();
    1346             : 
    1347           4 :     return true;
    1348             : }
    1349             : 
    1350         716 : bool Image::readImage( const std::string& filename, const Frame::Buffer buffer )
    1351             : {
    1352         716 :     lunchbox::MemoryMap image;
    1353         716 :     const uint8_t* addr = static_cast< const uint8_t* >( image.map( filename ));
    1354             : 
    1355         716 :     if( !addr )
    1356             :     {
    1357           0 :         LBINFO << "Can't open " << filename << " for reading" << std::endl;
    1358           0 :         return false;
    1359             :     }
    1360             : 
    1361         716 :     const size_t size = image.getSize();
    1362         716 :     if( size < sizeof( RGBHeader ))
    1363             :     {
    1364           0 :         LBWARN << "Image " << filename << " too small" << std::endl;
    1365           0 :         return false;
    1366             :     }
    1367             : 
    1368         716 :     RGBHeader header;
    1369         716 :     memcpy( &header, addr, sizeof( header ));
    1370         716 :     addr += sizeof( header );
    1371             : 
    1372         716 :     header.convert();
    1373             : 
    1374         716 :     if( header.magic != 474)
    1375             :     {
    1376           0 :         LBERROR << "Bad magic number " << filename << std::endl;
    1377           0 :         return false;
    1378             :     }
    1379         716 :     if( header.width == 0 || header.height == 0 )
    1380             :     {
    1381           0 :         LBERROR << "Zero-sized image " << filename << std::endl;
    1382           0 :         return false;
    1383             :     }
    1384         716 :     if( header.compression != 0)
    1385             :     {
    1386           0 :         LBERROR << "Unsupported compression " << filename << std::endl;
    1387           0 :         return false;
    1388             :     }
    1389             : 
    1390         716 :     const unsigned nChannels = header.depth;
    1391             : 
    1392        1432 :     if( header.nDimensions != 3 ||
    1393        1432 :         header.minValue != 0 ||
    1394        1511 :         ( header.maxValue != 255 && header.maxValue != 1023 ) ||
    1395        1432 :         header.colorMode != 0 ||
    1396         635 :         ( buffer == Frame::BUFFER_COLOR && nChannels != 3 && nChannels != 4 ) ||
    1397          81 :         ( buffer == Frame::BUFFER_DEPTH && nChannels != 4 ))
    1398             :     {
    1399           0 :         LBERROR << "Unsupported image type " << filename << std::endl;
    1400           0 :         return false;
    1401             :     }
    1402             : 
    1403         874 :     if(( header.bytesPerChannel != 1 || nChannels == 1 ) &&
    1404         158 :          header.maxValue != 255 )
    1405             :     {
    1406           0 :         LBERROR << "Unsupported value range " << header.maxValue << std::endl;
    1407           0 :         return false;
    1408             :     }
    1409             : 
    1410         716 :     const uint8_t bpc = header.bytesPerChannel;
    1411         716 :     const size_t depth = nChannels * bpc;
    1412         716 :     const size_t nPixels = header.width * header.height;
    1413         716 :     const size_t nComponents = nPixels * nChannels;
    1414         716 :     const size_t nBytes = nComponents * bpc;
    1415             : 
    1416         716 :     if( size < sizeof( RGBHeader ) + nBytes )
    1417             :     {
    1418           0 :         LBERROR << "Image " << filename << " too small" << std::endl;
    1419           0 :         return false;
    1420             :     }
    1421         716 :     LBASSERTINFO( size == sizeof( RGBHeader ) + nBytes,
    1422             :                   "delta " << size - sizeof( RGBHeader ) - nBytes );
    1423             : 
    1424         716 :     switch( buffer )
    1425             :     {
    1426             :         case Frame::BUFFER_DEPTH:
    1427          81 :             if( header.bytesPerChannel != 1 )
    1428             :             {
    1429           0 :                 LBERROR << "Unsupported channel depth "
    1430           0 :                         << static_cast< int >( header.bytesPerChannel )
    1431           0 :                         << std::endl;
    1432           0 :                 return false;
    1433             :             }
    1434             :             _setExternalFormat( Frame::BUFFER_DEPTH,
    1435             :                                 EQ_COMPRESSOR_DATATYPE_DEPTH_UNSIGNED_INT, 4,
    1436          81 :                                 false );
    1437             :             setInternalFormat( Frame::BUFFER_DEPTH,
    1438          81 :                                EQ_COMPRESSOR_DATATYPE_DEPTH );
    1439          81 :             break;
    1440             : 
    1441             :         case Frame::BUFFER_COLOR:
    1442         635 :             switch( header.bytesPerChannel )
    1443             :             {
    1444             :                 case 1:
    1445         477 :                     if( header.maxValue == 1023 )
    1446             :                     {
    1447          79 :                         LBASSERT( nChannels==4 );
    1448             :                         _setExternalFormat( Frame::BUFFER_COLOR,
    1449             :                                             EQ_COMPRESSOR_DATATYPE_RGB10_A2, 4,
    1450          79 :                                             true );
    1451             :                         setInternalFormat( Frame::BUFFER_COLOR,
    1452          79 :                                            EQ_COMPRESSOR_DATATYPE_RGB10_A2 );
    1453             :                     }
    1454             :                     else
    1455             :                     {
    1456         398 :                         if( nChannels == 4 )
    1457             :                             _setExternalFormat( Frame::BUFFER_COLOR,
    1458             :                                                 EQ_COMPRESSOR_DATATYPE_RGBA,
    1459         398 :                                                 4, true );
    1460             :                         else
    1461             :                         {
    1462           0 :                             LBASSERT( nChannels == 3 );
    1463             :                             _setExternalFormat( Frame::BUFFER_COLOR,
    1464             :                                                 EQ_COMPRESSOR_DATATYPE_RGB,
    1465           0 :                                                 nChannels, false );
    1466             :                         }
    1467             :                         setInternalFormat( Frame::BUFFER_COLOR,
    1468         398 :                                            EQ_COMPRESSOR_DATATYPE_RGBA );
    1469             :                     }
    1470         477 :                     break;
    1471             : 
    1472             :                 case 2:
    1473          79 :                     if( nChannels == 4 )
    1474             :                         _setExternalFormat( Frame::BUFFER_COLOR,
    1475             :                                             EQ_COMPRESSOR_DATATYPE_RGBA16F,
    1476          79 :                                             8, true );
    1477             :                     else
    1478             :                     {
    1479           0 :                         LBASSERT( nChannels == 3 );
    1480             :                         _setExternalFormat( Frame::BUFFER_COLOR,
    1481             :                                             EQ_COMPRESSOR_DATATYPE_RGB16F,
    1482           0 :                                             nChannels * 2, false );
    1483             :                     }
    1484             : 
    1485             :                     setInternalFormat( Frame::BUFFER_COLOR,
    1486          79 :                                        EQ_COMPRESSOR_DATATYPE_RGBA16F );
    1487          79 :                     break;
    1488             : 
    1489             :                 case 4:
    1490          79 :                     if( nChannels == 4 )
    1491             :                         _setExternalFormat( Frame::BUFFER_COLOR,
    1492             :                                             EQ_COMPRESSOR_DATATYPE_RGBA32F,
    1493          79 :                                             16, true );
    1494             :                     else
    1495             :                     {
    1496           0 :                         LBASSERT( nChannels == 3 );
    1497             :                         _setExternalFormat( Frame::BUFFER_COLOR,
    1498             :                                             EQ_COMPRESSOR_DATATYPE_RGBA32F,
    1499           0 :                                             nChannels *4, false );
    1500             :                     }
    1501             :                     setInternalFormat( Frame::BUFFER_COLOR,
    1502          79 :                                        EQ_COMPRESSOR_DATATYPE_RGBA32F );
    1503          79 :                     break;
    1504             : 
    1505             :                 default:
    1506           0 :                     LBERROR << "Unsupported channel depth "
    1507           0 :                             << static_cast< int >( header.bytesPerChannel )
    1508           0 :                             << std::endl;
    1509           0 :                     return false;
    1510             :             }
    1511         635 :             break;
    1512             : 
    1513             :         default:
    1514           0 :             LBUNREACHABLE;
    1515             :     }
    1516         716 :     Memory& memory = _impl->getMemory( buffer );
    1517         716 :     const PixelViewport pvp( 0, 0, header.width, header.height );
    1518         716 :     if( pvp != _impl->pvp )
    1519             :     {
    1520         556 :         setPixelViewport( pvp );
    1521             :     }
    1522             : 
    1523         716 :     if ( memory.pvp != pvp )
    1524             :     {
    1525         560 :         memory.pvp = pvp;
    1526         560 :         memory.state = Memory::INVALID;
    1527             :     }
    1528         716 :     validatePixelData( buffer );
    1529             : 
    1530         716 :     uint8_t* data = reinterpret_cast< uint8_t* >( memory.pixels );
    1531         716 :     LBASSERTINFO( nBytes <= getPixelDataSize( buffer ),
    1532             :                   nBytes << " > " << getPixelDataSize( buffer ));
    1533             :     // Each channel is saved separately
    1534         716 :     switch( bpc )
    1535             :     {
    1536             :     case 1:
    1537        2790 :         for( size_t i = 0; i < nChannels; ++i )
    1538  3332313016 :             for( size_t j = i; j < nComponents; j += nChannels )
    1539             :             {
    1540  3332310784 :                 data[j] = *addr;
    1541  3332310784 :                 ++addr;
    1542             :             }
    1543         558 :         break;
    1544             : 
    1545             :     case 2:
    1546         395 :         for( size_t i = 0; i < nChannels; ++i )
    1547   182016316 :             for( size_t j = i; j < nComponents; j += nChannels )
    1548             :             {
    1549   182016000 :                 reinterpret_cast< uint16_t* >( data )[ j ] =
    1550   182016000 :                     *reinterpret_cast< const uint16_t* >( addr );
    1551   182016000 :                 addr += bpc;
    1552             :             }
    1553          79 :         break;
    1554             : 
    1555             :     case 4:
    1556         395 :         for( size_t i = 0; i < nChannels; ++i )
    1557   182016316 :             for( size_t j = i; j < nComponents; j += nChannels )
    1558             :             {
    1559   182016000 :                 reinterpret_cast< uint32_t* >( data )[ j ] =
    1560   182016000 :                     *reinterpret_cast< const uint32_t* >( addr );
    1561   182016000 :                 addr += bpc;
    1562             :             }
    1563          79 :         break;
    1564             : 
    1565             :     default:
    1566           0 :         for( size_t i = 0; i < depth; i += bpc )
    1567           0 :             for( size_t j = i * bpc; j < nBytes; j += depth )
    1568             :             {
    1569           0 :                 memcpy( &data[j], addr, bpc );
    1570           0 :                 addr += bpc;
    1571             :             }
    1572           0 :         break;
    1573             :     }
    1574         716 :     return true;
    1575             : }
    1576             : 
    1577         884 : uint32_t Image::getExternalFormat( const Frame::Buffer buffer ) const
    1578             : {
    1579         884 :     return _impl->getMemory( buffer ).externalFormat;
    1580             : }
    1581             : 
    1582          42 : uint32_t Image::getPixelSize( const Frame::Buffer buffer ) const
    1583             : {
    1584          42 :     return _impl->getMemory( buffer ).pixelSize;
    1585             : }
    1586             : 
    1587         833 : void Image::setStorageType( const Frame::Type type )
    1588             : {
    1589         833 :     _impl->type = type;
    1590         833 : }
    1591             : 
    1592         128 : Frame::Type Image::getStorageType() const
    1593             : {
    1594         128 :     return _impl->type;
    1595             : }
    1596             : 
    1597         201 : const PixelViewport& Image::getPixelViewport() const
    1598             : {
    1599         201 :     return _impl->pvp;
    1600             : }
    1601             : 
    1602           0 : void Image::setZoom( const Zoom& zoom )
    1603             : {
    1604           0 :     _impl->zoom = zoom;
    1605           0 : }
    1606             : 
    1607           2 : const Zoom& Image::getZoom() const
    1608             : {
    1609           2 :     return _impl->zoom;
    1610             : }
    1611             : 
    1612         992 : bool Image::hasPixelData( const Frame::Buffer buffer ) const
    1613             : {
    1614         992 :     return _impl->getMemory( buffer ).state == Memory::VALID;
    1615             : }
    1616             : 
    1617           8 : bool Image::hasAsyncReadback( const Frame::Buffer buffer ) const
    1618             : {
    1619           8 :     return _impl->getMemory( buffer ).state == Memory::DOWNLOAD;
    1620             : }
    1621             : 
    1622           6 : bool Image::hasAsyncReadback() const
    1623             : {
    1624           8 :     return hasAsyncReadback( Frame::BUFFER_COLOR ) ||
    1625           8 :            hasAsyncReadback( Frame::BUFFER_DEPTH );
    1626             : }
    1627             : 
    1628        1223 : bool Image::getAlphaUsage() const
    1629             : {
    1630        1223 :     return !_impl->ignoreAlpha;
    1631             : }
    1632             : 
    1633           2 : void Image::setOffset( int32_t x, int32_t y )
    1634             : {
    1635           2 :     _impl->pvp.x = x;
    1636           2 :     _impl->pvp.y = y;
    1637           2 : }
    1638             : 
    1639          36 : }

Generated by: LCOV version 1.10