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