LCOV - code coverage report
Current view: top level - eq - image.cpp (source / functions) Hit Total Coverage
Test: Equalizer Lines: 538 925 58.2 %
Date: 2017-12-16 05:07:20 Functions: 67 94 71.3 %

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

Generated by: LCOV version 1.11