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

Generated by: LCOV version 1.11