Path: blob/master/3rdparty/openexr/IlmImf/ImfInputFile.cpp
16337 views
///////////////////////////////////////////////////////////////////////////1//2// Copyright (c) 2004, Industrial Light & Magic, a division of Lucas3// Digital Ltd. LLC4//5// All rights reserved.6//7// Redistribution and use in source and binary forms, with or without8// modification, are permitted provided that the following conditions are9// met:10// * Redistributions of source code must retain the above copyright11// notice, this list of conditions and the following disclaimer.12// * Redistributions in binary form must reproduce the above13// copyright notice, this list of conditions and the following disclaimer14// in the documentation and/or other materials provided with the15// distribution.16// * Neither the name of Industrial Light & Magic nor the names of17// its contributors may be used to endorse or promote products derived18// from this software without specific prior written permission.19//20// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS21// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT22// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR23// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT24// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,25// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT26// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,27// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY28// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT29// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE30// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.31//32///////////////////////////////////////////////////////////////////////////3334//-----------------------------------------------------------------------------35//36// class InputFile37//38//-----------------------------------------------------------------------------3940#include <ImfInputFile.h>41#include <ImfScanLineInputFile.h>42#include <ImfTiledInputFile.h>43#include <ImfChannelList.h>44#include <ImfMisc.h>45#include <ImfStdIO.h>46#include <ImfVersion.h>47#include "ImathFun.h"48#include "IlmThreadMutex.h"49#include "Iex.h"50#include "half.h"51#include <fstream>52#include <algorithm>535455namespace Imf {565758using Imath::Box2i;59using Imath::divp;60using Imath::modp;61using IlmThread::Mutex;62using IlmThread::Lock;636465//66// Struct InputFile::Data stores things that will be67// needed between calls to readPixels68//6970struct InputFile::Data: public Mutex71{72Header header;73int version;74IStream * is;75bool deleteStream;7677TiledInputFile * tFile;78ScanLineInputFile * sFile;7980LineOrder lineOrder; // the file's lineorder81int minY; // data window's min y coord82int maxY; // data window's max x coord8384FrameBuffer tFileBuffer;85FrameBuffer * cachedBuffer;8687int cachedTileY;88int offset;8990int numThreads;9192Data (bool del, int numThreads);93~Data ();9495void deleteCachedBuffer();96};979899InputFile::Data::Data (bool del, int numThreads):100is (0),101deleteStream (del),102tFile (0),103sFile (0),104cachedBuffer (0),105cachedTileY (-1),106numThreads (numThreads)107{108// empty109}110111112InputFile::Data::~Data ()113{114delete tFile;115delete sFile;116117if (deleteStream)118delete is;119120deleteCachedBuffer();121}122123124void125InputFile::Data::deleteCachedBuffer()126{127//128// Delete the cached frame buffer, and all memory129// allocated for the slices in the cached frameBuffer.130//131132if (cachedBuffer)133{134for (FrameBuffer::Iterator k = cachedBuffer->begin();135k != cachedBuffer->end();136++k)137{138Slice &s = k.slice();139140switch (s.type)141{142case UINT:143144delete [] (((unsigned int *)s.base) + offset);145break;146147case HALF:148149delete [] ((half *)s.base + offset);150break;151152case FLOAT:153154delete [] (((float *)s.base) + offset);155break;156}157}158159//160// delete the cached frame buffer161//162163delete cachedBuffer;164cachedBuffer = 0;165}166}167168169namespace {170171void172bufferedReadPixels (InputFile::Data* ifd, int scanLine1, int scanLine2)173{174//175// bufferedReadPixels reads each row of tiles that intersect the176// scan-line range (scanLine1 to scanLine2). The previous row of177// tiles is cached in order to prevent redundent tile reads when178// accessing scanlines sequentially.179//180181int minY = std::min (scanLine1, scanLine2);182int maxY = std::max (scanLine1, scanLine2);183184if (minY < ifd->minY || maxY > ifd->maxY)185{186throw Iex::ArgExc ("Tried to read scan line outside "187"the image file's data window.");188}189190//191// The minimum and maximum y tile coordinates that intersect this192// scanline range193//194195int minDy = (minY - ifd->minY) / ifd->tFile->tileYSize();196int maxDy = (maxY - ifd->minY) / ifd->tFile->tileYSize();197198//199// Figure out which one is first in the file so we can read without seeking200//201202int yStart, yEnd, yStep;203204if (ifd->lineOrder == DECREASING_Y)205{206yStart = maxDy;207yEnd = minDy - 1;208yStep = -1;209}210else211{212yStart = minDy;213yEnd = maxDy + 1;214yStep = 1;215}216217//218// the number of pixels in a row of tiles219//220221Box2i levelRange = ifd->tFile->dataWindowForLevel(0);222223//224// Read the tiles into our temporary framebuffer and copy them into225// the user's buffer226//227228for (int j = yStart; j != yEnd; j += yStep)229{230Box2i tileRange = ifd->tFile->dataWindowForTile (0, j, 0);231232int minYThisRow = std::max (minY, tileRange.min.y);233int maxYThisRow = std::min (maxY, tileRange.max.y);234235if (j != ifd->cachedTileY)236{237//238// We don't have any valid buffered info, so we need to read in239// from the file.240//241242ifd->tFile->readTiles (0, ifd->tFile->numXTiles (0) - 1, j, j);243ifd->cachedTileY = j;244}245246//247// Copy the data from our cached framebuffer into the user's248// framebuffer.249//250251for (FrameBuffer::ConstIterator k = ifd->cachedBuffer->begin();252k != ifd->cachedBuffer->end();253++k)254{255Slice fromSlice = k.slice(); // slice to write from256Slice toSlice = ifd->tFileBuffer[k.name()]; // slice to write to257258char *fromPtr, *toPtr;259int size = pixelTypeSize (toSlice.type);260261int xStart = levelRange.min.x;262int yStart = minYThisRow;263264while (modp (xStart, toSlice.xSampling) != 0)265++xStart;266267while (modp (yStart, toSlice.ySampling) != 0)268++yStart;269270for (int y = yStart;271y <= maxYThisRow;272y += toSlice.ySampling)273{274//275// Set the pointers to the start of the y scanline in276// this row of tiles277//278279fromPtr = fromSlice.base +280(y - tileRange.min.y) * fromSlice.yStride +281xStart * fromSlice.xStride;282283toPtr = toSlice.base +284divp (y, toSlice.ySampling) * toSlice.yStride +285divp (xStart, toSlice.xSampling) * toSlice.xStride;286287//288// Copy all pixels for the scanline in this row of tiles289//290291for (int x = xStart;292x <= levelRange.max.x;293x += toSlice.xSampling)294{295for (size_t i = 0; i < size; ++i)296toPtr[i] = fromPtr[i];297298fromPtr += fromSlice.xStride * toSlice.xSampling;299toPtr += toSlice.xStride;300}301}302}303}304}305306} // namespace307308309310InputFile::InputFile (const char fileName[], int numThreads):311_data (new Data (true, numThreads))312{313try314{315_data->is = new StdIFStream (fileName);316initialize();317}318catch (Iex::BaseExc &e)319{320delete _data;321322REPLACE_EXC (e, "Cannot read image file "323"\"" << fileName << "\". " << e);324throw;325}326catch (...)327{328delete _data;329throw;330}331}332333334InputFile::InputFile (IStream &is, int numThreads):335_data (new Data (false, numThreads))336{337try338{339_data->is = &is;340initialize();341}342catch (Iex::BaseExc &e)343{344delete _data;345346REPLACE_EXC (e, "Cannot read image file "347"\"" << is.fileName() << "\". " << e);348throw;349}350catch (...)351{352delete _data;353throw;354}355}356357358void359InputFile::initialize ()360{361_data->header.readFrom (*_data->is, _data->version);362_data->header.sanityCheck (isTiled (_data->version));363364if (isTiled (_data->version))365{366_data->lineOrder = _data->header.lineOrder();367368//369// Save the dataWindow information370//371372const Box2i &dataWindow = _data->header.dataWindow();373_data->minY = dataWindow.min.y;374_data->maxY = dataWindow.max.y;375376_data->tFile = new TiledInputFile (_data->header,377_data->is,378_data->version,379_data->numThreads);380}381else382{383_data->sFile = new ScanLineInputFile (_data->header,384_data->is,385_data->numThreads);386}387}388389390InputFile::~InputFile ()391{392delete _data;393}394395396const char *397InputFile::fileName () const398{399return _data->is->fileName();400}401402403const Header &404InputFile::header () const405{406return _data->header;407}408409410int411InputFile::version () const412{413return _data->version;414}415416417void418InputFile::setFrameBuffer (const FrameBuffer &frameBuffer)419{420if (isTiled (_data->version))421{422Lock lock (*_data);423424//425// We must invalidate the cached buffer if the new frame426// buffer has a different set of channels than the old427// frame buffer, or if the type of a channel has changed.428//429430const FrameBuffer &oldFrameBuffer = _data->tFileBuffer;431432FrameBuffer::ConstIterator i = oldFrameBuffer.begin();433FrameBuffer::ConstIterator j = frameBuffer.begin();434435while (i != oldFrameBuffer.end() && j != frameBuffer.end())436{437if (strcmp (i.name(), j.name()) || i.slice().type != j.slice().type)438break;439440++i;441++j;442}443444if (i != oldFrameBuffer.end() || j != frameBuffer.end())445{446//447// Invalidate the cached buffer.448//449450_data->deleteCachedBuffer ();451_data->cachedTileY = -1;452453//454// Create new a cached frame buffer. It can hold a single455// row of tiles. The cached buffer can be reused for each456// row of tiles because we set the yTileCoords parameter of457// each Slice to true.458//459460const Box2i &dataWindow = _data->header.dataWindow();461_data->cachedBuffer = new FrameBuffer();462_data->offset = dataWindow.min.x;463464int tileRowSize = (dataWindow.max.x - dataWindow.min.x + 1) *465_data->tFile->tileYSize();466467for (FrameBuffer::ConstIterator k = frameBuffer.begin();468k != frameBuffer.end();469++k)470{471Slice s = k.slice();472473switch (s.type)474{475case UINT:476477_data->cachedBuffer->insert478(k.name(),479Slice (UINT,480(char *)(new unsigned int[tileRowSize] -481_data->offset),482sizeof (unsigned int),483sizeof (unsigned int) *484_data->tFile->levelWidth(0),4851, 1,486s.fillValue,487false, true));488break;489490case HALF:491492_data->cachedBuffer->insert493(k.name(),494Slice (HALF,495(char *)(new half[tileRowSize] -496_data->offset),497sizeof (half),498sizeof (half) *499_data->tFile->levelWidth(0),5001, 1,501s.fillValue,502false, true));503break;504505case FLOAT:506507_data->cachedBuffer->insert508(k.name(),509Slice (FLOAT,510(char *)(new float[tileRowSize] -511_data->offset),512sizeof(float),513sizeof(float) *514_data->tFile->levelWidth(0),5151, 1,516s.fillValue,517false, true));518break;519520default:521522throw Iex::ArgExc ("Unknown pixel data type.");523}524}525526_data->tFile->setFrameBuffer (*_data->cachedBuffer);527}528529_data->tFileBuffer = frameBuffer;530}531else532{533_data->sFile->setFrameBuffer (frameBuffer);534}535}536537538const FrameBuffer &539InputFile::frameBuffer () const540{541if (isTiled (_data->version))542{543Lock lock (*_data);544return _data->tFileBuffer;545}546else547{548return _data->sFile->frameBuffer();549}550}551552553bool554InputFile::isComplete () const555{556if (isTiled (_data->version))557return _data->tFile->isComplete();558else559return _data->sFile->isComplete();560}561562563void564InputFile::readPixels (int scanLine1, int scanLine2)565{566if (isTiled (_data->version))567{568Lock lock (*_data);569bufferedReadPixels (_data, scanLine1, scanLine2);570}571else572{573_data->sFile->readPixels (scanLine1, scanLine2);574}575}576577578void579InputFile::readPixels (int scanLine)580{581readPixels (scanLine, scanLine);582}583584585void586InputFile::rawPixelData (int firstScanLine,587const char *&pixelData,588int &pixelDataSize)589{590try591{592if (isTiled (_data->version))593{594throw Iex::ArgExc ("Tried to read a raw scanline "595"from a tiled image.");596}597598_data->sFile->rawPixelData (firstScanLine, pixelData, pixelDataSize);599}600catch (Iex::BaseExc &e)601{602REPLACE_EXC (e, "Error reading pixel data from image "603"file \"" << fileName() << "\". " << e);604throw;605}606}607608609void610InputFile::rawTileData (int &dx, int &dy,611int &lx, int &ly,612const char *&pixelData,613int &pixelDataSize)614{615try616{617if (!isTiled (_data->version))618{619throw Iex::ArgExc ("Tried to read a raw tile "620"from a scanline-based image.");621}622623_data->tFile->rawTileData (dx, dy, lx, ly, pixelData, pixelDataSize);624}625catch (Iex::BaseExc &e)626{627REPLACE_EXC (e, "Error reading tile data from image "628"file \"" << fileName() << "\". " << e);629throw;630}631}632633634TiledInputFile*635InputFile::tFile()636{637if (!isTiled (_data->version))638{639throw Iex::ArgExc ("Cannot get a TiledInputFile pointer "640"from an InputFile that is not tiled.");641}642643return _data->tFile;644}645646647} // namespace Imf648649650