/////////////////////////////////////////////////////////////////////////// // // Copyright (c) 2012, Weta Digital Ltd // // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Weta Digital nor the names of // its contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // /////////////////////////////////////////////////////////////////////////// #include "ImfCompositeDeepScanLine.h" #include "ImfDeepScanLineInputPart.h" #include "ImfDeepScanLineInputFile.h" #include "ImfChannelList.h" #include "ImfFrameBuffer.h" #include "ImfDeepFrameBuffer.h" #include "ImfDeepCompositing.h" #include "ImfPixelType.h" #include "IlmThreadPool.h" #include #include OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_ENTER using std::vector; using std::string; using IMATH_NAMESPACE::Box2i; using ILMTHREAD_NAMESPACE::Task; using ILMTHREAD_NAMESPACE::TaskGroup; using ILMTHREAD_NAMESPACE::ThreadPool; struct CompositeDeepScanLine::Data{ public : vector _file; // array of files vector _part; // array of parts FrameBuffer _outputFrameBuffer; // output frame buffer provided bool _zback; // true if we are using zback (otherwise channel 1 = channel 0) vector< vector > _channeldata; // pixel values, read from the input, one array per channel vector< int > _sampleCounts; // total per-pixel sample counts, Box2i _dataWindow; // data window of combined inputs DeepCompositing * _comp; // user-provided compositor vector _channels; // names of channels that will be composited vector _bufferMap; // entry _outputFrameBuffer[n].name() == _channels[ _bufferMap[n] ].name() void check_valid(const Header & header); // check newly added part/file is OK; on first good call, set _zback/_dataWindow // // set up the given deep frame buffer to contain the required channels // resize counts and pointers to the width of _dataWindow // zero-out all counts, since the datawindow may be smaller than/not include this part // void handleDeepFrameBuffer (DeepFrameBuffer & buf, vector & counts, //per-pixel counts vector< vector > & pointers, //per-channel-per-pixel pointers to data const Header & header, int start, int end); Data(); }; CompositeDeepScanLine::Data::Data() : _zback(false) , _comp(NULL) {} CompositeDeepScanLine::CompositeDeepScanLine() : _Data(new Data) {} CompositeDeepScanLine::~CompositeDeepScanLine() { delete _Data; } void CompositeDeepScanLine::addSource(DeepScanLineInputPart* part) { _Data->check_valid(part->header()); _Data->_part.push_back(part); } void CompositeDeepScanLine::addSource(DeepScanLineInputFile* file) { _Data->check_valid(file->header()); _Data->_file.push_back(file); } int CompositeDeepScanLine::sources() const { return int(_Data->_part.size())+int(_Data->_file.size()); } void CompositeDeepScanLine::Data::check_valid(const Header & header) { bool has_z=false; bool has_alpha=false; // check good channel names for( ChannelList::ConstIterator i=header.channels().begin();i!=header.channels().end();++i) { std::string n(i.name()); if(n=="ZBack") { _zback=true; } else if(n=="Z") { has_z=true; } else if(n=="A") { has_alpha=true; } } if(!has_z) { throw IEX_NAMESPACE::ArgExc("Deep data provided to CompositeDeepScanLine is missing a Z channel"); } if(!has_alpha) { throw IEX_NAMESPACE::ArgExc("Deep data provided to CompositeDeepScanLine is missing an alpha channel"); } if(_part.size()==0 && _file.size()==0) { // first in - update and return _dataWindow = header.dataWindow(); return; } const Header * const match_header = _part.size()>0 ? &_part[0]->header() : &_file[0]->header(); // check the sizes match if(match_header->displayWindow() != header.displayWindow()) { throw IEX_NAMESPACE::ArgExc("Deep data provided to CompositeDeepScanLine has a different displayWindow to previously provided data"); } _dataWindow.extendBy(header.dataWindow()); } void CompositeDeepScanLine::Data::handleDeepFrameBuffer (DeepFrameBuffer& buf, std::vector< unsigned int > & counts, vector< std::vector< float* > > & pointers, const Header& header, int start, int end) { int width=_dataWindow.size().x+1; size_t pixelcount = width * (end-start+1); pointers.resize(_channels.size()); counts.resize(pixelcount); buf.insertSampleCountSlice (Slice (OPENEXR_IMF_INTERNAL_NAMESPACE::UINT, (char *) (&counts[0]-_dataWindow.min.x-start*width), sizeof(unsigned int), sizeof(unsigned int)*width)); pointers[0].resize(pixelcount); buf.insert ("Z", DeepSlice (OPENEXR_IMF_INTERNAL_NAMESPACE::FLOAT, (char *)(&pointers[0][0]-_dataWindow.min.x-start*width), sizeof(float *), sizeof(float *)*width, sizeof(float) )); if(_zback) { pointers[1].resize(pixelcount); buf.insert ("ZBack", DeepSlice (OPENEXR_IMF_INTERNAL_NAMESPACE::FLOAT, (char *)(&pointers[1][0]-_dataWindow.min.x-start*width), sizeof(float *), sizeof(float *)*width, sizeof(float) )); } pointers[2].resize(pixelcount); buf.insert ("A", DeepSlice (OPENEXR_IMF_INTERNAL_NAMESPACE::FLOAT, (char *)(&pointers[2][0]-_dataWindow.min.x-start*width), sizeof(float *), sizeof(float *)*width, sizeof(float) )); size_t i =0; for(FrameBuffer::ConstIterator qt = _outputFrameBuffer.begin(); qt != _outputFrameBuffer.end(); qt++) { int channel_in_source = _bufferMap[i]; if(channel_in_source>2) { // not dealt with yet (0,1,2 previously inserted) pointers[channel_in_source].resize(pixelcount); buf.insert (qt.name(), DeepSlice (OPENEXR_IMF_INTERNAL_NAMESPACE::FLOAT, (char *)(&pointers[channel_in_source][0]-_dataWindow.min.x-start*width), sizeof(float *), sizeof(float *)*width, sizeof(float) )); } i++; } } void CompositeDeepScanLine::setCompositing(DeepCompositing* c) { _Data->_comp=c; } const IMATH_NAMESPACE::Box2i& CompositeDeepScanLine::dataWindow() const { return _Data->_dataWindow; } void CompositeDeepScanLine::setFrameBuffer(const FrameBuffer& fr) { // // count channels; build map between channels in frame buffer // and channels in internal buffers // _Data->_channels.resize(3); _Data->_channels[0]="Z"; _Data->_channels[1]=_Data->_zback ? "ZBack" : "Z"; _Data->_channels[2]="A"; _Data->_bufferMap.resize(0); for(FrameBuffer::ConstIterator q=fr.begin();q!=fr.end();q++) { string name(q.name()); if(name=="ZBack") { _Data->_bufferMap.push_back(1); }else if(name=="Z") { _Data->_bufferMap.push_back(0); }else if(name=="A") { _Data->_bufferMap.push_back(2); }else{ _Data->_bufferMap.push_back(_Data->_channels.size()); _Data->_channels.push_back(name); } } _Data->_outputFrameBuffer=fr; } namespace { class LineCompositeTask : public Task { public: LineCompositeTask ( TaskGroup* group , CompositeDeepScanLine::Data * data, int y, int start, vector* names, vector > >* pointers, vector* total_sizes, vector* num_sources ) : Task(group) , _Data(data), _y(y), _start(start), _names(names), _pointers(pointers), _total_sizes(total_sizes), _num_sources(num_sources) {} virtual ~LineCompositeTask () {} virtual void execute (); CompositeDeepScanLine::Data* _Data; int _y; int _start; vector* _names; vector > >* _pointers; vector* _total_sizes; vector* _num_sources; }; void composite_line(int y, int start, CompositeDeepScanLine::Data * _Data, vector & names, const vector > > & pointers, const vector & total_sizes, const vector & num_sources ) { vector output_pixel(names.size()); //the pixel we'll output to vector inputs(names.size()); DeepCompositing d; // fallback compositing engine DeepCompositing * comp= _Data->_comp ? _Data->_comp : &d; int pixel = (y-start)*(_Data->_dataWindow.max.x+1-_Data->_dataWindow.min.x); for(int x=_Data->_dataWindow.min.x;x<=_Data->_dataWindow.max.x;x++) { // set inputs[] to point to the first sample of the first part of each channel // if there's a zback, set all channel independently... if(_Data->_zback) { for(size_t channel=0;channelcomposite_pixel(&output_pixel[0], &inputs[0], &names[0], names.size(), total_sizes[pixel], num_sources[pixel] ); size_t channel_number=0; // // write out composited value into internal frame buffer // for(FrameBuffer::Iterator it = _Data->_outputFrameBuffer.begin();it !=_Data->_outputFrameBuffer.end();it++) { float value = output_pixel[ _Data->_bufferMap[channel_number] ]; // value to write // cast to half float if necessary if(it.slice().type==OPENEXR_IMF_INTERNAL_NAMESPACE::FLOAT) { * (float *)(it.slice().base + y*it.slice().yStride + x*it.slice().xStride) = value; } else if(it.slice().type==HALF) { * (half *)(it.slice().base + y*it.slice().yStride + x*it.slice().xStride) = half(value); } channel_number++; } pixel++; }// next pixel on row } void LineCompositeTask::execute() { composite_line(_y,_start,_Data,*_names,*_pointers,*_total_sizes,*_num_sources); } } void CompositeDeepScanLine::readPixels(int start, int end) { size_t parts = _Data->_file.size() + _Data->_part.size(); // total of files+parts vector framebuffers(parts); vector< vector > counts(parts); // // for each part, a pointer to an array of channels // vector > > pointers(parts); vector headers(parts); { size_t i; for(i=0;i<_Data->_file.size();i++) { headers[i] = &_Data->_file[i]->header(); } for(size_t j=0;j<_Data->_part.size();j++) { headers[i+j] = &_Data->_part[j]->header(); } } for(size_t i=0;ihandleDeepFrameBuffer(framebuffers[i],counts[i],pointers[i],*headers[i],start,end); } // // set frame buffers and read scanlines from all parts // TODO what happens if SCANLINE not in data window? // { size_t i=0; for(i=0;i<_Data->_file.size();i++) { _Data->_file[i]->setFrameBuffer(framebuffers[i]); _Data->_file[i]->readPixelSampleCounts(start,end); } for(size_t j=0;j<_Data->_part.size();j++) { _Data->_part[j]->setFrameBuffer(framebuffers[i+j]); _Data->_part[j]->readPixelSampleCounts(start,end); } } // // total width // size_t total_width = _Data->_dataWindow.size().x+1; size_t total_pixels = total_width*(end-start+1); vector total_sizes(total_pixels); vector num_sources(total_pixels); //number of parts with non-zero sample count size_t overall_sample_count=0; // sum of all samples in all images between start and end // // accumulate pixel counts // for(size_t ptr=0;ptr0) num_sources[ptr]++; } overall_sample_count+=total_sizes[ptr]; } // // allocate arrays for pixel data // samples array accessed as in pixels[channel][sample] // vector > samples( _Data->_channels.size() ); for(size_t channel=0;channel<_Data->_channels.size();channel++) { if( channel!=1 || _Data->_zback) { samples[channel].resize(overall_sample_count); } } for(size_t channel=0;channel_zback) { samples[channel].resize(overall_sample_count); // // allocate pointers for channel data // size_t offset=0; for(size_t pixel=0;pixel_file.size();i++) { _Data->_file[i]->readPixels(start,end); } for(size_t j=0;j<_Data->_part.size();j++) { _Data->_part[j]->readPixels(start,end); } // // composite pixels and write back to framebuffer // // turn vector of strings into array of char * // and make sure 'ZBack' channel is correct vector names(_Data->_channels.size()); for(size_t i=0;i_channels[i].c_str(); } if(!_Data->_zback) names[1]=names[0]; // no zback channel, so make it point to z TaskGroup g; for(int y=start;y<=end;y++) { ThreadPool::addGlobalTask(new LineCompositeTask(&g,_Data,y,start,&names,&pointers,&total_sizes,&num_sources)); }//next row } const FrameBuffer& CompositeDeepScanLine::frameBuffer() const { return _Data->_outputFrameBuffer; } OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_EXIT