////////////////////////////////////////////////////////////////////////////// // // 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 #include 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 &p1, const Array2D &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 &p1, const Array2D &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); } }