//////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2006, Industrial Light & Magic, a division of Lucasfilm
// Entertainment Company Ltd. Portions contributed and copyright held by
// others as indicated. 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
// any other contributors to this software 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 "compareB44.h"
#include <algorithm>
#include <cassert>
using namespace OPENEXR_IMF_NAMESPACE;
using namespace std;
int
shiftAndRound (int x, int shift)
{
x <<= 1;
int a = (1 << shift) - 1;
shift += 1;
int b = (x >> shift) & 1;
return (x + a + b) >> shift;
}
bool
withinB44ErrorBounds (const half A[4][4], const half B[4][4])
{
//
// Assuming that a 4x4 pixel block, B, was generated by
// compressing and uncompressing another pixel block, A,
// using OpenEXR's B44 compression method, check whether
// the differences between A and B are what we would
// expect from the compressor.
//
//
// The block may not have been compressed at all if it
// was part of a very small tile.
//
bool equal = true;
for (int i = 0; i < 4; ++i)
for (int j = 0; j < 4; ++j)
if (A[i][j] != B[i][j])
equal = false;
if (equal)
return true;
//
// The block was compressed.
//
// Perform a "light" version of the B44 compression on A
// (see the pack() function in ImfB44Compressor.cpp).
//
unsigned short t[16];
for (int i = 0; i < 16; ++i)
{
unsigned short Abits = A[i / 4][i % 4].bits();
if ((Abits & 0x7c00) == 0x7c00)
t[i] = 0x8000;
else if (Abits & 0x8000)
t[i] = ~Abits;
else
t[i] = Abits | 0x8000;
}
unsigned short tMax = 0;
for (int i = 0; i < 16; ++i)
if (tMax < t[i])
tMax = t[i];
int shift = -1;
int d[16];
int r[15];
int rMin;
int rMax;
do
{
shift += 1;
for (int i = 0; i < 16; ++i)
d[i] = shiftAndRound (tMax - t[i], shift);
const int bias = 0x20;
r[ 0] = d[ 0] - d[ 4] + bias;
r[ 1] = d[ 4] - d[ 8] + bias;
r[ 2] = d[ 8] - d[12] + bias;
r[ 3] = d[ 0] - d[ 1] + bias;
r[ 4] = d[ 4] - d[ 5] + bias;
r[ 5] = d[ 8] - d[ 9] + bias;
r[ 6] = d[12] - d[13] + bias;
r[ 7] = d[ 1] - d[ 2] + bias;
r[ 8] = d[ 5] - d[ 6] + bias;
r[ 9] = d[ 9] - d[10] + bias;
r[10] = d[13] - d[14] + bias;
r[11] = d[ 2] - d[ 3] + bias;
r[12] = d[ 6] - d[ 7] + bias;
r[13] = d[10] - d[11] + bias;
r[14] = d[14] - d[15] + bias;
rMin = r[0];
rMax = r[0];
for (int i = 1; i < 15; ++i)
{
if (rMin > r[i])
rMin = r[i];
if (rMax < r[i])
rMax = r[i];
}
}
while (rMin < 0 || rMax > 0x3f);
t[0] = tMax - (d[0] << shift);
//
// Now perform a "light" version of the decompression method.
// (see the unpack() function in ImfB44Compressor.cpp).
//
unsigned short A1[16];
const int bias = 0x20 << shift;
A1[ 0] = t[ 0];
A1[ 4] = A1[ 0] + (r[ 0] << shift) - bias;
A1[ 8] = A1[ 4] + (r[ 1] << shift) - bias;
A1[12] = A1[ 8] + (r[ 2] << shift) - bias;
A1[ 1] = A1[ 0] + (r[ 3] << shift) - bias;
A1[ 5] = A1[ 4] + (r[ 4] << shift) - bias;
A1[ 9] = A1[ 8] + (r[ 5] << shift) - bias;
A1[13] = A1[12] + (r[ 6] << shift) - bias;
A1[ 2] = A1[ 1] + (r[ 7] << shift) - bias;
A1[ 6] = A1[ 5] + (r[ 8] << shift) - bias;
A1[10] = A1[ 9] + (r[ 9] << shift) - bias;
A1[14] = A1[13] + (r[10] << shift) - bias;
A1[ 3] = A1[ 2] + (r[11] << shift) - bias;
A1[ 7] = A1[ 6] + (r[12] << shift) - bias;
A1[11] = A1[10] + (r[13] << shift) - bias;
A1[15] = A1[14] + (r[14] << shift) - bias;
//
// Compare the result with B, allowing for an difference
// of a couple of units in the last place.
//
for (int i = 0; i < 16; ++i)
{
unsigned short A1bits = A1[i];
unsigned short Bbits = B[i / 4][i % 4].bits();
if (Bbits & 0x8000)
Bbits = ~Bbits;
else
Bbits = Bbits | 0x8000;
if (Bbits > A1bits + 5 || Bbits < A1bits - 5)
return false;
}
return true;
}
void
compareB44 (int width,
int height,
const Array2D<half> &p1,
const Array2D<half> &p2)
{
for (int y = 0; y < height; y += 4)
{
for (int x = 0; x < width; x += 4)
{
half A[4][4];
half B[4][4];
for (int y1 = 0; y1 < 4; ++y1)
{
for (int x1 = 0; x1 < 4; ++x1)
{
int y2 = min (y + y1, height - 1);
int x2 = min (x + x1, width - 1);
A[y1][x1] = p1[y2][x2];
B[y1][x1] = p2[y2][x2];
}
}
assert (withinB44ErrorBounds (A, B));
}
}
}
void
compareB44 (int width,
int height,
const Array2D<Rgba> &p1,
const Array2D<Rgba> &p2,
RgbaChannels channels)
{
if (channels & WRITE_R)
{
for (int y = 0; y < height; y += 4)
{
for (int x = 0; x < width; x += 4)
{
half A[4][4];
half B[4][4];
for (int y1 = 0; y1 < 4; ++y1)
{
for (int x1 = 0; x1 < 4; ++x1)
{
int y2 = min (y + y1, height - 1);
int x2 = min (x + x1, width - 1);
A[y1][x1] = p1[y2][x2].r;
B[y1][x1] = p2[y2][x2].r;
}
}
assert (withinB44ErrorBounds (A, B));
}
}
}
else
{
for (int y = 0; y < height; y += 1)
for (int x = 0; x < width; x += 1)
assert (p2[y][x].r == 0);
}
if (channels & WRITE_G)
{
for (int y = 0; y < height; y += 4)
{
for (int x = 0; x < width; x += 4)
{
half A[4][4];
half B[4][4];
for (int y1 = 0; y1 < 4; ++y1)
{
for (int x1 = 0; x1 < 4; ++x1)
{
int y2 = min (y + y1, height - 1);
int x2 = min (x + x1, width - 1);
A[y1][x1] = p1[y2][x2].g;
B[y1][x1] = p2[y2][x2].g;
}
}
assert (withinB44ErrorBounds (A, B));
}
}
}
else
{
for (int y = 0; y < height; y += 1)
for (int x = 0; x < width; x += 1)
assert (p2[y][x].g == 0);
}
if (channels & WRITE_B)
{
for (int y = 0; y < height; y += 4)
{
for (int x = 0; x < width; x += 4)
{
half A[4][4];
half B[4][4];
for (int y1 = 0; y1 < 4; ++y1)
{
for (int x1 = 0; x1 < 4; ++x1)
{
int y2 = min (y + y1, height - 1);
int x2 = min (x + x1, width - 1);
A[y1][x1] = p1[y2][x2].b;
B[y1][x1] = p2[y2][x2].b;
}
}
assert (withinB44ErrorBounds (A, B));
}
}
}
else
{
for (int y = 0; y < height; y += 1)
for (int x = 0; x < width; x += 1)
assert (p2[y][x].b == 0);
}
if (channels & WRITE_A)
{
for (int y = 0; y < height; y += 4)
{
for (int x = 0; x < width; x += 4)
{
half A[4][4];
half B[4][4];
for (int y1 = 0; y1 < 4; ++y1)
{
for (int x1 = 0; x1 < 4; ++x1)
{
int y2 = min (y + y1, height - 1);
int x2 = min (x + x1, width - 1);
A[y1][x1] = p1[y2][x2].a;
B[y1][x1] = p2[y2][x2].a;
}
}
assert (withinB44ErrorBounds (A, B));
}
}
}
else
{
for (int y = 0; y < height; y += 1)
for (int x = 0; x < width; x += 1)
assert (p2[y][x].a == 1);
}
}