📄 imftiledoutputfile.cpp
字号:
///////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2004, Industrial Light & Magic, a division of Lucas
// Digital Ltd. LLC
//
// 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 Industrial Light & Magic 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.
//
///////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//
// class TiledOutputFile
//
//-----------------------------------------------------------------------------
#include <ImfTiledOutputFile.h>
#include <ImfTiledInputFile.h>
#include <ImfInputFile.h>
#include <ImfTileDescriptionAttribute.h>
#include <ImfPreviewImageAttribute.h>
#include <ImfChannelList.h>
#include <ImfMisc.h>
#include <ImfTiledMisc.h>
#include <ImfStdIO.h>
#include <ImfCompressor.h>
#include "ImathBox.h"
#include <ImfArray.h>
#include <ImfXdr.h>
#include <ImfVersion.h>
#include <ImfTileOffsets.h>
#include <ImfThreading.h>
#include "IlmThreadPool.h"
#include "IlmThreadSemaphore.h"
#include "IlmThreadMutex.h"
#include "Iex.h"
#include <string>
#include <vector>
#include <fstream>
#include <assert.h>
#include <map>
namespace Imf {
using Imath::Box2i;
using Imath::V2i;
using std::string;
using std::vector;
using std::ofstream;
using std::map;
using std::min;
using std::max;
using std::swap;
using IlmThread::Mutex;
using IlmThread::Lock;
using IlmThread::Semaphore;
using IlmThread::Task;
using IlmThread::TaskGroup;
using IlmThread::ThreadPool;
namespace {
struct TOutSliceInfo
{
PixelType type;
const char * base;
size_t xStride;
size_t yStride;
bool zero;
int xTileCoords;
int yTileCoords;
TOutSliceInfo (PixelType type = HALF,
const char *base = 0,
size_t xStride = 0,
size_t yStride = 0,
bool zero = false,
int xTileCoords = 0,
int yTileCoords = 0);
};
TOutSliceInfo::TOutSliceInfo (PixelType t,
const char *b,
size_t xs, size_t ys,
bool z,
int xtc,
int ytc)
:
type (t),
base (b),
xStride (xs),
yStride (ys),
zero (z),
xTileCoords (xtc),
yTileCoords (ytc)
{
// empty
}
struct TileCoord
{
int dx;
int dy;
int lx;
int ly;
TileCoord (int xTile = 0, int yTile = 0,
int xLevel = 0, int yLevel = 0)
:
dx (xTile), dy (yTile),
lx (xLevel), ly (yLevel)
{
// empty
}
bool
operator < (const TileCoord &other) const
{
return (ly < other.ly) ||
(ly == other.ly && lx < other.lx) ||
((ly == other.ly && lx == other.lx) &&
((dy < other.dy) || (dy == other.dy && dx < other.dx)));
}
bool
operator == (const TileCoord &other) const
{
return lx == other.lx &&
ly == other.ly &&
dx == other.dx &&
dy == other.dy;
}
};
struct BufferedTile
{
char * pixelData;
int pixelDataSize;
BufferedTile (const char *data, int size):
pixelData (0),
pixelDataSize(size)
{
pixelData = new char[pixelDataSize];
memcpy (pixelData, data, pixelDataSize);
}
~BufferedTile()
{
delete [] pixelData;
}
};
typedef map <TileCoord, BufferedTile *> TileMap;
struct TileBuffer
{
Array<char> buffer;
const char * dataPtr;
int dataSize;
Compressor * compressor;
TileCoord tileCoord;
bool hasException;
string exception;
TileBuffer (Compressor *comp);
~TileBuffer ();
inline void wait () {_sem.wait();}
inline void post () {_sem.post();}
protected:
Semaphore _sem;
};
TileBuffer::TileBuffer (Compressor *comp):
dataPtr (0),
dataSize (0),
compressor (comp),
hasException (false),
exception (),
_sem (1)
{
// empty
}
TileBuffer::~TileBuffer ()
{
delete compressor;
}
} // namespace
struct TiledOutputFile::Data: public Mutex
{
Header header; // the image header
int version; // file format version
TileDescription tileDesc; // describes the tile layout
FrameBuffer frameBuffer; // framebuffer to write into
Int64 previewPosition;
LineOrder lineOrder; // the file's lineorder
int minX; // data window's min x coord
int maxX; // data window's max x coord
int minY; // data window's min y coord
int maxY; // data window's max x coord
int numXLevels; // number of x levels
int numYLevels; // number of y levels
int * numXTiles; // number of x tiles at a level
int * numYTiles; // number of y tiles at a level
TileOffsets tileOffsets; // stores offsets in file for
// each tile
Compressor::Format format; // compressor's data format
vector<TOutSliceInfo> slices; // info about channels in file
OStream * os; // file stream to write to
bool deleteStream;
size_t maxBytesPerTileLine; // combined size of a tile line
// over all channels
vector<TileBuffer*> tileBuffers;
size_t tileBufferSize; // size of a tile buffer
Int64 tileOffsetsPosition; // position of the tile index
Int64 currentPosition; // current position in the file
TileMap tileMap;
TileCoord nextTileToWrite;
Data (bool del, int numThreads);
~Data ();
inline TileBuffer * getTileBuffer (int number);
// hash function from tile
// buffer coords into our
// vector of tile buffers
TileCoord nextTileCoord (const TileCoord &a);
};
TiledOutputFile::Data::Data (bool del, int numThreads):
numXTiles(0),
numYTiles(0),
os (0),
deleteStream (del),
tileOffsetsPosition (0)
{
//
// We need at least one tileBuffer, but if threading is used,
// to keep n threads busy we need 2*n tileBuffers
//
tileBuffers.resize (max (1, 2 * numThreads));
}
TiledOutputFile::Data::~Data ()
{
delete [] numXTiles;
delete [] numYTiles;
if (deleteStream)
delete os;
//
// Delete all the tile buffers, if any still happen to exist
//
for (TileMap::iterator i = tileMap.begin(); i != tileMap.end(); ++i)
delete i->second;
for (size_t i = 0; i < tileBuffers.size(); i++)
delete tileBuffers[i];
}
TileBuffer*
TiledOutputFile::Data::getTileBuffer (int number)
{
return tileBuffers[number % tileBuffers.size()];
}
TileCoord
TiledOutputFile::Data::nextTileCoord (const TileCoord &a)
{
TileCoord b = a;
if (lineOrder == INCREASING_Y)
{
b.dx++;
if (b.dx >= numXTiles[b.lx])
{
b.dx = 0;
b.dy++;
if (b.dy >= numYTiles[b.ly])
{
//
// the next tile is in the next level
//
b.dy = 0;
switch (tileDesc.mode)
{
case ONE_LEVEL:
case MIPMAP_LEVELS:
b.lx++;
b.ly++;
break;
case RIPMAP_LEVELS:
b.lx++;
if (b.lx >= numXLevels)
{
b.lx = 0;
b.ly++;
#ifdef DEBUG
assert (b.ly <= numYLevels);
#endif
}
break;
}
}
}
}
else if (lineOrder == DECREASING_Y)
{
b.dx++;
if (b.dx >= numXTiles[b.lx])
{
b.dx = 0;
b.dy--;
if (b.dy < 0)
{
//
// the next tile is in the next level
//
switch (tileDesc.mode)
{
case ONE_LEVEL:
case MIPMAP_LEVELS:
b.lx++;
b.ly++;
break;
case RIPMAP_LEVELS:
b.lx++;
if (b.lx >= numXLevels)
{
b.lx = 0;
b.ly++;
#ifdef DEBUG
assert (b.ly <= numYLevels);
#endif
}
break;
}
if (b.ly < numYLevels)
b.dy = numYTiles[b.ly] - 1;
}
}
}
return b;
}
namespace {
void
writeTileData (TiledOutputFile::Data *ofd,
int dx, int dy,
int lx, int ly,
const char pixelData[],
int pixelDataSize)
{
//
// Store a block of pixel data in the output file, and try
// to keep track of the current writing position the file,
// without calling tellp() (tellp() can be fairly expensive).
//
Int64 currentPosition = ofd->currentPosition;
ofd->currentPosition = 0;
if (currentPosition == 0)
currentPosition = ofd->os->tellp();
ofd->tileOffsets (dx, dy, lx, ly) = currentPosition;
#ifdef DEBUG
assert (ofd->os->tellp() == currentPosition);
#endif
//
// Write the tile header.
//
Xdr::write <StreamIO> (*ofd->os, dx);
Xdr::write <StreamIO> (*ofd->os, dy);
Xdr::write <StreamIO> (*ofd->os, lx);
Xdr::write <StreamIO> (*ofd->os, ly);
Xdr::write <StreamIO> (*ofd->os, pixelDataSize);
ofd->os->write (pixelData, pixelDataSize);
//
// Keep current position in the file so that we can avoid
// redundant seekg() operations (seekg() can be fairly expensive).
//
ofd->currentPosition = currentPosition +
5 * Xdr::size<int>() +
pixelDataSize;
}
void
bufferedTileWrite (TiledOutputFile::Data *ofd,
int dx, int dy,
int lx, int ly,
const char pixelData[],
int pixelDataSize)
{
//
// Check if a tile with coordinates (dx,dy,lx,ly) has already been written.
//
if (ofd->tileOffsets (dx, dy, lx, ly))
{
THROW (Iex::ArgExc,
"Attempt to write tile "
"(" << dx << ", " << dy << ", " << lx << "," << ly << ") "
"more than once.");
}
//
// If tiles can be written in random order, then don't buffer anything.
//
if (ofd->lineOrder == RANDOM_Y)
{
writeTileData (ofd, dx, dy, lx, ly, pixelData, pixelDataSize);
return;
}
//
// If the tiles cannot be written in random order, then check if a
// tile with coordinates (dx,dy,lx,ly) has already been buffered.
//
TileCoord currentTile = TileCoord(dx, dy, lx, ly);
if (ofd->tileMap.find (currentTile) != ofd->tileMap.end())
{
THROW (Iex::ArgExc,
"Attempt to write tile "
"(" << dx << ", " << dy << ", " << lx << "," << ly << ") "
"more than once.");
}
//
// If all the tiles before this one have already been written to the file,
// then write this tile immediately and check if we have buffered tiles
// that can be written after this tile.
//
// Otherwise, buffer the tile so it can be written to file later.
//
if (ofd->nextTileToWrite == currentTile)
{
writeTileData (ofd, dx, dy, lx, ly, pixelData, pixelDataSize);
ofd->nextTileToWrite = ofd->nextTileCoord (ofd->nextTileToWrite);
TileMap::iterator i = ofd->tileMap.find (ofd->nextTileToWrite);
//
// Step through the tiles and write all successive buffered tiles after
// the current one.
//
while(i != ofd->tileMap.end())
{
//
// Write the tile, and then delete the tile's buffered data
//
writeTileData (ofd,
i->first.dx, i->first.dy,
i->first.lx, i->first.ly,
i->second->pixelData,
i->second->pixelDataSize);
delete i->second;
ofd->tileMap.erase (i);
//
// Proceed to the next tile
//
ofd->nextTileToWrite = ofd->nextTileCoord (ofd->nextTileToWrite);
i = ofd->tileMap.find (ofd->nextTileToWrite);
}
}
else
{
//
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -