LCOV - code coverage report
Current view: top level - eq - image.cpp (source / functions) Hit Total Coverage
Test: Equalizer Lines: 388 854 45.4 %
Date: 2016-07-30 05:04:55 Functions: 49 82 59.8 %

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

Generated by: LCOV version 1.11