2012-08-25 04:31:49 +08:00
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Copyright (c) 2004, Industrial Light & Magic, a division of Lucas
|
|
|
|
// Digital Ltd. LLC
|
2019-06-11 01:04:23 +08:00
|
|
|
//
|
2012-08-25 04:31:49 +08:00
|
|
|
// All rights reserved.
|
2019-06-11 01:04:23 +08:00
|
|
|
//
|
2012-08-25 04:31:49 +08:00
|
|
|
// 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
|
2019-06-11 01:04:23 +08:00
|
|
|
// from this software without specific prior written permission.
|
|
|
|
//
|
2012-08-25 04:31:49 +08:00
|
|
|
// 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.
|
|
|
|
//
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// Miscellaneous stuff related to tiled files
|
|
|
|
//
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
#include <ImfTiledMisc.h>
|
|
|
|
#include "Iex.h"
|
|
|
|
#include <ImfMisc.h>
|
|
|
|
#include <ImfChannelList.h>
|
2019-06-11 01:04:23 +08:00
|
|
|
#include <ImfTileDescription.h>
|
|
|
|
#include <algorithm>
|
2012-08-25 04:31:49 +08:00
|
|
|
|
2019-06-11 01:04:23 +08:00
|
|
|
#include "ImfNamespace.h"
|
2012-08-25 04:31:49 +08:00
|
|
|
|
2019-06-11 01:04:23 +08:00
|
|
|
OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_ENTER
|
2012-08-25 04:31:49 +08:00
|
|
|
|
2019-06-11 01:04:23 +08:00
|
|
|
using IMATH_NAMESPACE::Box2i;
|
|
|
|
using IMATH_NAMESPACE::V2i;
|
2012-08-25 04:31:49 +08:00
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
levelSize (int min, int max, int l, LevelRoundingMode rmode)
|
|
|
|
{
|
|
|
|
if (l < 0)
|
2019-06-11 01:04:23 +08:00
|
|
|
throw IEX_NAMESPACE::ArgExc ("Argument not in valid range.");
|
2012-08-25 04:31:49 +08:00
|
|
|
|
|
|
|
int a = max - min + 1;
|
|
|
|
int b = (1 << l);
|
|
|
|
int size = a / b;
|
|
|
|
|
|
|
|
if (rmode == ROUND_UP && size * b < a)
|
2019-06-11 01:04:23 +08:00
|
|
|
size += 1;
|
2012-08-25 04:31:49 +08:00
|
|
|
|
|
|
|
return std::max (size, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Box2i
|
|
|
|
dataWindowForLevel (const TileDescription &tileDesc,
|
2019-06-11 01:04:23 +08:00
|
|
|
int minX, int maxX,
|
|
|
|
int minY, int maxY,
|
|
|
|
int lx, int ly)
|
2012-08-25 04:31:49 +08:00
|
|
|
{
|
|
|
|
V2i levelMin = V2i (minX, minY);
|
|
|
|
|
|
|
|
V2i levelMax = levelMin +
|
2019-06-11 01:04:23 +08:00
|
|
|
V2i (levelSize (minX, maxX, lx, tileDesc.roundingMode) - 1,
|
|
|
|
levelSize (minY, maxY, ly, tileDesc.roundingMode) - 1);
|
2012-08-25 04:31:49 +08:00
|
|
|
|
|
|
|
return Box2i(levelMin, levelMax);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Box2i
|
|
|
|
dataWindowForTile (const TileDescription &tileDesc,
|
2019-06-11 01:04:23 +08:00
|
|
|
int minX, int maxX,
|
|
|
|
int minY, int maxY,
|
|
|
|
int dx, int dy,
|
|
|
|
int lx, int ly)
|
2012-08-25 04:31:49 +08:00
|
|
|
{
|
|
|
|
V2i tileMin = V2i (minX + dx * tileDesc.xSize,
|
2019-06-11 01:04:23 +08:00
|
|
|
minY + dy * tileDesc.ySize);
|
2012-08-25 04:31:49 +08:00
|
|
|
|
|
|
|
V2i tileMax = tileMin + V2i (tileDesc.xSize - 1, tileDesc.ySize - 1);
|
|
|
|
|
|
|
|
V2i levelMax = dataWindowForLevel
|
2019-06-11 01:04:23 +08:00
|
|
|
(tileDesc, minX, maxX, minY, maxY, lx, ly).max;
|
2012-08-25 04:31:49 +08:00
|
|
|
|
|
|
|
tileMax = V2i (std::min (tileMax[0], levelMax[0]),
|
2019-06-11 01:04:23 +08:00
|
|
|
std::min (tileMax[1], levelMax[1]));
|
2012-08-25 04:31:49 +08:00
|
|
|
|
|
|
|
return Box2i (tileMin, tileMax);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
size_t
|
|
|
|
calculateBytesPerPixel (const Header &header)
|
|
|
|
{
|
|
|
|
const ChannelList &channels = header.channels();
|
|
|
|
|
|
|
|
size_t bytesPerPixel = 0;
|
|
|
|
|
|
|
|
for (ChannelList::ConstIterator c = channels.begin();
|
2019-06-11 01:04:23 +08:00
|
|
|
c != channels.end();
|
|
|
|
++c)
|
2012-08-25 04:31:49 +08:00
|
|
|
{
|
2019-06-11 01:04:23 +08:00
|
|
|
bytesPerPixel += pixelTypeSize (c.channel().type);
|
2012-08-25 04:31:49 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return bytesPerPixel;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-06-11 01:04:23 +08:00
|
|
|
void
|
|
|
|
calculateBytesPerLine (const Header &header,
|
|
|
|
char* sampleCountBase,
|
|
|
|
int sampleCountXStride,
|
|
|
|
int sampleCountYStride,
|
|
|
|
int minX, int maxX,
|
|
|
|
int minY, int maxY,
|
|
|
|
std::vector<int>& xOffsets,
|
|
|
|
std::vector<int>& yOffsets,
|
|
|
|
std::vector<Int64>& bytesPerLine)
|
|
|
|
{
|
|
|
|
const ChannelList &channels = header.channels();
|
|
|
|
|
|
|
|
int pos = 0;
|
|
|
|
for (ChannelList::ConstIterator c = channels.begin();
|
|
|
|
c != channels.end();
|
|
|
|
++c, ++pos)
|
|
|
|
{
|
|
|
|
int xOffset = xOffsets[pos];
|
|
|
|
int yOffset = yOffsets[pos];
|
|
|
|
int i = 0;
|
|
|
|
for (int y = minY - yOffset; y <= maxY - yOffset; y++, i++)
|
|
|
|
for (int x = minX - xOffset; x <= maxX - xOffset; x++)
|
|
|
|
{
|
|
|
|
bytesPerLine[i] += sampleCount(sampleCountBase,
|
|
|
|
sampleCountXStride,
|
|
|
|
sampleCountYStride,
|
|
|
|
x, y)
|
|
|
|
* pixelTypeSize (c.channel().type);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-08-25 04:31:49 +08:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
int
|
|
|
|
floorLog2 (int x)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// For x > 0, floorLog2(y) returns floor(log(x)/log(2)).
|
|
|
|
//
|
|
|
|
|
|
|
|
int y = 0;
|
|
|
|
|
|
|
|
while (x > 1)
|
|
|
|
{
|
2019-06-11 01:04:23 +08:00
|
|
|
y += 1;
|
|
|
|
x >>= 1;
|
2012-08-25 04:31:49 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return y;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
ceilLog2 (int x)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// For x > 0, ceilLog2(y) returns ceil(log(x)/log(2)).
|
|
|
|
//
|
|
|
|
|
|
|
|
int y = 0;
|
|
|
|
int r = 0;
|
|
|
|
|
|
|
|
while (x > 1)
|
|
|
|
{
|
2019-06-11 01:04:23 +08:00
|
|
|
if (x & 1)
|
|
|
|
r = 1;
|
2012-08-25 04:31:49 +08:00
|
|
|
|
2019-06-11 01:04:23 +08:00
|
|
|
y += 1;
|
|
|
|
x >>= 1;
|
2012-08-25 04:31:49 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return y + r;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
roundLog2 (int x, LevelRoundingMode rmode)
|
|
|
|
{
|
|
|
|
return (rmode == ROUND_DOWN)? floorLog2 (x): ceilLog2 (x);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
calculateNumXLevels (const TileDescription& tileDesc,
|
2019-06-11 01:04:23 +08:00
|
|
|
int minX, int maxX,
|
|
|
|
int minY, int maxY)
|
2012-08-25 04:31:49 +08:00
|
|
|
{
|
|
|
|
int num = 0;
|
|
|
|
|
|
|
|
switch (tileDesc.mode)
|
|
|
|
{
|
|
|
|
case ONE_LEVEL:
|
|
|
|
|
2019-06-11 01:04:23 +08:00
|
|
|
num = 1;
|
|
|
|
break;
|
2012-08-25 04:31:49 +08:00
|
|
|
|
|
|
|
case MIPMAP_LEVELS:
|
|
|
|
|
2019-06-11 01:04:23 +08:00
|
|
|
{
|
|
|
|
int w = maxX - minX + 1;
|
|
|
|
int h = maxY - minY + 1;
|
|
|
|
num = roundLog2 (std::max (w, h), tileDesc.roundingMode) + 1;
|
|
|
|
}
|
2012-08-25 04:31:49 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case RIPMAP_LEVELS:
|
|
|
|
|
2019-06-11 01:04:23 +08:00
|
|
|
{
|
|
|
|
int w = maxX - minX + 1;
|
|
|
|
num = roundLog2 (w, tileDesc.roundingMode) + 1;
|
|
|
|
}
|
|
|
|
break;
|
2012-08-25 04:31:49 +08:00
|
|
|
|
|
|
|
default:
|
|
|
|
|
2019-06-11 01:04:23 +08:00
|
|
|
throw IEX_NAMESPACE::ArgExc ("Unknown LevelMode format.");
|
2012-08-25 04:31:49 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return num;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
calculateNumYLevels (const TileDescription& tileDesc,
|
2019-06-11 01:04:23 +08:00
|
|
|
int minX, int maxX,
|
|
|
|
int minY, int maxY)
|
2012-08-25 04:31:49 +08:00
|
|
|
{
|
|
|
|
int num = 0;
|
|
|
|
|
|
|
|
switch (tileDesc.mode)
|
|
|
|
{
|
|
|
|
case ONE_LEVEL:
|
|
|
|
|
2019-06-11 01:04:23 +08:00
|
|
|
num = 1;
|
|
|
|
break;
|
2012-08-25 04:31:49 +08:00
|
|
|
|
|
|
|
case MIPMAP_LEVELS:
|
|
|
|
|
2019-06-11 01:04:23 +08:00
|
|
|
{
|
|
|
|
int w = maxX - minX + 1;
|
|
|
|
int h = maxY - minY + 1;
|
|
|
|
num = roundLog2 (std::max (w, h), tileDesc.roundingMode) + 1;
|
|
|
|
}
|
2012-08-25 04:31:49 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case RIPMAP_LEVELS:
|
|
|
|
|
2019-06-11 01:04:23 +08:00
|
|
|
{
|
|
|
|
int h = maxY - minY + 1;
|
|
|
|
num = roundLog2 (h, tileDesc.roundingMode) + 1;
|
|
|
|
}
|
|
|
|
break;
|
2012-08-25 04:31:49 +08:00
|
|
|
|
|
|
|
default:
|
|
|
|
|
2019-06-11 01:04:23 +08:00
|
|
|
throw IEX_NAMESPACE::ArgExc ("Unknown LevelMode format.");
|
2012-08-25 04:31:49 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return num;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
calculateNumTiles (int *numTiles,
|
2019-06-11 01:04:23 +08:00
|
|
|
int numLevels,
|
|
|
|
int min, int max,
|
|
|
|
int size,
|
|
|
|
LevelRoundingMode rmode)
|
2012-08-25 04:31:49 +08:00
|
|
|
{
|
|
|
|
for (int i = 0; i < numLevels; i++)
|
|
|
|
{
|
2019-06-11 01:04:23 +08:00
|
|
|
numTiles[i] = (levelSize (min, max, i, rmode) + size - 1) / size;
|
2012-08-25 04:31:49 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
precalculateTileInfo (const TileDescription& tileDesc,
|
2019-06-11 01:04:23 +08:00
|
|
|
int minX, int maxX,
|
|
|
|
int minY, int maxY,
|
|
|
|
int *&numXTiles, int *&numYTiles,
|
|
|
|
int &numXLevels, int &numYLevels)
|
2012-08-25 04:31:49 +08:00
|
|
|
{
|
|
|
|
numXLevels = calculateNumXLevels(tileDesc, minX, maxX, minY, maxY);
|
|
|
|
numYLevels = calculateNumYLevels(tileDesc, minX, maxX, minY, maxY);
|
2019-06-11 01:04:23 +08:00
|
|
|
|
2012-08-25 04:31:49 +08:00
|
|
|
numXTiles = new int[numXLevels];
|
|
|
|
numYTiles = new int[numYLevels];
|
|
|
|
|
|
|
|
calculateNumTiles (numXTiles,
|
2019-06-11 01:04:23 +08:00
|
|
|
numXLevels,
|
|
|
|
minX, maxX,
|
|
|
|
tileDesc.xSize,
|
|
|
|
tileDesc.roundingMode);
|
2012-08-25 04:31:49 +08:00
|
|
|
|
|
|
|
calculateNumTiles (numYTiles,
|
2019-06-11 01:04:23 +08:00
|
|
|
numYLevels,
|
|
|
|
minY, maxY,
|
|
|
|
tileDesc.ySize,
|
|
|
|
tileDesc.roundingMode);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
getTiledChunkOffsetTableSize(const Header& header)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Save the dataWindow information
|
|
|
|
//
|
|
|
|
|
|
|
|
const Box2i &dataWindow = header.dataWindow();
|
|
|
|
|
|
|
|
//
|
|
|
|
// Precompute level and tile information.
|
|
|
|
//
|
|
|
|
|
|
|
|
int* numXTiles;
|
|
|
|
int* numYTiles;
|
|
|
|
int numXLevels;
|
|
|
|
int numYLevels;
|
|
|
|
precalculateTileInfo (header.tileDescription(),
|
|
|
|
dataWindow.min.x, dataWindow.max.x,
|
|
|
|
dataWindow.min.y, dataWindow.max.y,
|
|
|
|
numXTiles, numYTiles,
|
|
|
|
numXLevels, numYLevels);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Calculate lineOffsetSize.
|
|
|
|
//
|
|
|
|
int lineOffsetSize = 0;
|
|
|
|
const TileDescription &desc = header.tileDescription();
|
|
|
|
switch (desc.mode)
|
|
|
|
{
|
|
|
|
case ONE_LEVEL:
|
|
|
|
case MIPMAP_LEVELS:
|
|
|
|
for (int i = 0; i < numXLevels; i++)
|
|
|
|
lineOffsetSize += numXTiles[i] * numYTiles[i];
|
|
|
|
break;
|
|
|
|
case RIPMAP_LEVELS:
|
|
|
|
for (int i = 0; i < numXLevels; i++)
|
|
|
|
for (int j = 0; j < numYLevels; j++)
|
|
|
|
lineOffsetSize += numXTiles[i] * numYTiles[j];
|
|
|
|
break;
|
|
|
|
case NUM_LEVELMODES :
|
|
|
|
throw IEX_NAMESPACE::LogicExc("Bad level mode getting chunk offset table size");
|
|
|
|
}
|
|
|
|
|
|
|
|
delete[] numXTiles;
|
|
|
|
delete[] numYTiles;
|
|
|
|
|
|
|
|
return lineOffsetSize;
|
2012-08-25 04:31:49 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-06-11 01:04:23 +08:00
|
|
|
OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_EXIT
|