Actually adhere to the wrap mode passed to the compressor

This commit is contained in:
Pavel Krajcevski 2015-04-11 13:28:47 -07:00
parent b7b9357964
commit 049ea129eb
2 changed files with 183 additions and 97 deletions

View file

@ -62,7 +62,11 @@
#include "FasTC/Pixel.h"
#include "FasTC/Color.h"
#ifndef NDEBUG
# include "PVRTCImage.h"
#endif
#include "Block.h"
#include "Indexer.h"
#define USE_CONSTANT_LUTS
@ -231,18 +235,26 @@ namespace PVRTCC {
#define AssertPOT(x) (void)(0)
#endif
static uint8 LookupIntensityByte(CompressionLabel *labels,
const uint32 *pixels,
uint32 index) {
float i = 255.0f * LookupIntensity(labels, pixels, index);
return static_cast<uint8>(i + 0.5f);
}
static EExtremaResult ComputeLocalExtrema(
CompressionLabel *labels, const uint8 *inBuf,
const uint32 x, const uint32 y, const uint32 width, const uint32 height) {
const uint32 x, const uint32 y, const Indexer &idxr) {
uint32 width = idxr.GetWidth();
uint32 height = idxr.GetHeight();
AssertPOT(width);
AssertPOT(height);
assert(x < width);
assert(y < height);
const uint32 *pixels = reinterpret_cast<const uint32 *>(inBuf);
uint8 i0 = static_cast<uint8>(255.0f * LookupIntensity(labels, pixels, y*width + x) + 0.5f);
uint32 idx0 = idxr(x, y);
uint8 i0 = LookupIntensityByte(labels, pixels, idx0);
int32 ng = 0;
int32 nl = 0;
@ -254,14 +266,10 @@ namespace PVRTCC {
if(i == 0 && j == 0) continue;
int32 xx = (i + static_cast<int32>(x + width)) & (width - 1);
int32 yy = (j + static_cast<int32>(y + height)) & (height - 1);
int32 xx = i + static_cast<int32>(x);
int32 yy = j + static_cast<int32>(y);
uint8 ix = LookupIntensityByte(labels, pixels, idxr(xx, yy));
assert(xx >= 0 && xx < static_cast<int32>(width));
assert(yy >= 0 && yy < static_cast<int32>(height));
uint32 idx = static_cast<uint32>(xx) + width * static_cast<uint32>(yy);
uint8 ix = static_cast<uint8>(255.0f * LookupIntensity(labels, pixels, idx) + 0.5f);
if(ix >= i0) {
ng++;
}
@ -276,15 +284,15 @@ namespace PVRTCC {
return result;
}
CompressionLabel &l = labels[y*width + x];
CompressionLabel &l = labels[idx0];
const int32 kThreshold = kKernelSz * kKernelSz - 1;
if(ng >= kThreshold) {
l.lowLabel.distance = 1;
l.lowLabel.AddIdx(y*width+x);
l.lowLabel.AddIdx(idx0);
result = eExtremaResult_LocalMin;
} else if(nl >= kThreshold) {
l.highLabel.distance = 1;
l.highLabel.AddIdx(y*width+x);
l.highLabel.AddIdx(idx0);
result = eExtremaResult_LocalMax;
}
@ -351,23 +359,25 @@ namespace PVRTCC {
static void LabelImageForward(CompressionLabel *labels,
const uint8 *inBuf,
const uint32 w, const uint32 h) {
const Indexer &idxr) {
uint32 w = idxr.GetWidth();
uint32 h = idxr.GetHeight();
AssertPOT(w);
AssertPOT(h);
for(uint32 j = 0; j < h+3; j++) {
for(uint32 i = 0; i < w; i++) {
EExtremaResult result = ComputeLocalExtrema(labels, inBuf, i, j & (h - 1), w, h);
EExtremaResult result = ComputeLocalExtrema(labels, inBuf, i, j, idxr);
bool dilateMax = result != eExtremaResult_LocalMax;
bool dilateMin = result != eExtremaResult_LocalMin;
if(dilateMax || dilateMin) {
// Look up and to the left to determine the distance...
uint32 upIdx = ((j+h-1) & (h - 1)) * w + i;
uint32 leftIdx = (j & (h - 1)) * w + ((i+w-1) & (w - 1));
uint32 upIdx = idxr(i, j - 1);
uint32 leftIdx = idxr(i - 1, j);
CompressionLabel &l = labels[(j & (h - 1))*w + i];
CompressionLabel &l = labels[idxr(i, j)];
CompressionLabel &up = labels[upIdx];
CompressionLabel &left = labels[leftIdx];
@ -431,8 +441,10 @@ namespace PVRTCC {
#endif
}
static void LabelImageBackward(CompressionLabel *labels,
const uint32 w, const uint32 h) {
static void LabelImageBackward(CompressionLabel *labels, const Indexer &idxr) {
uint32 w = idxr.GetWidth();
uint32 h = idxr.GetHeight();
AssertPOT(w);
AssertPOT(h);
@ -441,22 +453,22 @@ namespace PVRTCC {
for(int32 j = static_cast<int32>(h)+2; j >= 0; j--) {
for(int32 i = static_cast<int32>(w)-1; i >= 0; i--) {
CompressionLabel &l = labels[(j & (h - 1)) * w + i];
CompressionLabel &l = labels[idxr(i, j)];
// Add top right corner
neighbors[0] = &(labels[((j + h - 1) & (h - 1)) * w + ((i + 1) & (w - 1))]);
neighbors[0] = &(labels[idxr(i+1, j-1)]);
// Add right label
neighbors[1] = &(labels[(j & (h - 1)) * w + ((i + 1) & (w - 1))]);
neighbors[1] = &(labels[idxr(i+1, j)]);
// Add bottom right label
neighbors[2] = &(labels[((j + 1) & (h - 1)) * w + ((i + 1) & (w - 1))]);
neighbors[2] = &(labels[idxr(i+1, j+1)]);
// Add bottom label
neighbors[3] = &(labels[((j + 1) & (h - 1)) * w + i]);
neighbors[3] = &(labels[idxr(i, j+1)]);
// Add bottom left label
neighbors[4] = &(labels[((j + 1) & (h - 1)) * w + ((i + w - 1) & (w - 1))]);
neighbors[4] = &(labels[idxr(i-1, j+1)]);
DilateLabelBackward(l.highLabel, neighbors, true);
DilateLabelBackward(l.lowLabel, neighbors, false);
@ -464,60 +476,6 @@ namespace PVRTCC {
}
}
#if 0
static void DilateImage(CompressionLabel *labels, const uint8 *inBuf, uint32 w, uint32 h) {
for(uint32 j = 0; j < h; j++)
for(uint32 i = 0; i < w; i++) {
ComputeLocalExtrema(labels, inBuf, i, j, w, h);
uint32 idx = j*w + i;
uint32 minLowDist = labels[idx].lowLabel.distance == 0? 5 : labels[idx].lowLabel.distance - 1;
uint32 minHighDist = labels[idx].highLabel.distance == 0? 5 : labels[idx].highLabel.distance - 1;
for(int32 y = 0; y < 3; y++)
for(int32 x = 0; x < 3; x++) {
uint32 cidx = ((j + y + h-1) & (h-1))*w + ((i+x+w-1) & (w-1));
if(labels[cidx].lowLabel.distance > 0)
minLowDist = ::std::min<uint32>(minLowDist, labels[cidx].lowLabel.distance);
if(labels[cidx].highLabel.distance > 0)
minHighDist = ::std::min<uint32>(minHighDist, labels[cidx].highLabel.distance);
}
if(static_cast<int32>(minLowDist) != labels[idx].lowLabel.distance - 1) {
labels[idx].lowLabel.nLabels = 0;
}
if(static_cast<int32>(minHighDist) != labels[idx].highLabel.distance - 1) {
labels[idx].highLabel.nLabels = 0;
}
for(int32 y = 0; y < 3; y++)
for(int32 x = 0; x < 3; x++) {
uint32 cidx = ((j + y + h-1) & (h-1))*w + ((i+x+w-1) & (w-1));
if(minLowDist > 0 && labels[cidx].lowLabel.distance == minLowDist) {
labels[idx].lowLabel.Combine(labels[cidx].lowLabel);
}
if(minHighDist > 0 && labels[cidx].highLabel.distance == minHighDist) {
labels[idx].highLabel.Combine(labels[cidx].highLabel);
}
}
if(minLowDist > 0 && minLowDist < 5) {
labels[idx].lowLabel.distance = minLowDist + 1;
}
if(minHighDist > 0 && minHighDist < 5) {
labels[idx].highLabel.distance = minHighDist + 1;
}
}
}
#endif
static FasTC::Color CollectLabel(const uint32 *pixels, const Label &label) {
FasTC::Color ret;
uint32 nPs = 0;
@ -536,7 +494,10 @@ namespace PVRTCC {
static void GenerateLowHighImages(CompressionLabel *labels,
const uint8 *inBuf, uint8 *outBuf,
const uint32 w, const uint32 h) {
const Indexer &idxr) {
uint32 w = idxr.GetWidth();
uint32 h = idxr.GetHeight();
assert((w % 4) == 0);
assert((h % 4) == 0);
AssertPOT(w);
@ -556,7 +517,7 @@ namespace PVRTCC {
for(uint32 y = j*4; y <= (j+1)*4; y++)
for(uint32 x = i*4; x <= (i+1)*4; x++) {
uint32 idx = (y & (h-1))*w + (x & (w-1));
uint32 idx = idxr(x, y);
float intensity = labels[idx].intensity;
if(intensity < minIntensity) {
minIntensity = intensity;
@ -616,9 +577,10 @@ namespace PVRTCC {
#endif
// Average all of the values together now...
FasTC::Color high, low;
for(uint32 y = 0; y < 4; y++)
for(uint32 x = 0; x < 4; x++) {
uint32 idx = y * 4 + x;
Indexer localIdxr(4, 4);
for(uint32 y = 0; y < localIdxr.GetHeight(); y++)
for(uint32 x = 0; x < localIdxr.GetWidth(); x++) {
uint32 idx = localIdxr(x, y);
FasTC::Color c = blockColors[0][idx];
if(c.A() < 0.0f) {
c.Unpack(pixels[maxIntensityIdx]);
@ -699,7 +661,10 @@ namespace PVRTCC {
}
}
static void GenerateModulationValues(uint8 *outBuf, const uint8 *inBuf, uint32 w, uint32 h) {
static void GenerateModulationValues(uint8 *outBuf, const uint8 *inBuf,
const Indexer &idxr) {
uint32 w = idxr.GetWidth();
uint32 h = idxr.GetHeight();
AssertPOT(w);
AssertPOT(h);
@ -715,13 +680,14 @@ namespace PVRTCC {
// every iteration of the loop. Once we finish with a block, topLeft becomes topRight and
// bottomLeft becomes bottomRight. Also, when we go to the next row, bottomRight becomes
// topLeft.
Indexer blkIdxr(blocksW, blocksH, idxr.GetWrapMode());
for(uint32 j = 0; j < blocksH; j++) {
for(uint32 i = 0; i < blocksW; i++) {
const int32 lowXIdx = i;
const int32 highXIdx = (i + 1) & (blocksW - 1);
const int32 highXIdx = blkIdxr.ResolveX(i + 1);
const int32 lowYIdx = j;
const int32 highYIdx = (j + 1) & (blocksH - 1);
const int32 highYIdx = blkIdxr.ResolveY(j + 1);
const uint32 topLeftBlockIdx = GetBlockIndex(lowXIdx, lowYIdx);
const uint32 topRightBlockIdx = GetBlockIndex(highXIdx, lowYIdx);
@ -759,8 +725,8 @@ namespace PVRTCC {
for(uint32 y = 0; y < 4; y++) {
for(uint32 x = 0; x < 4; x++) {
uint32 pixelX = (i*4 + 2 + x) & (w - 1);
uint32 pixelY = (j*4 + 2 + y) & (h - 1);
uint32 pixelX = idxr.ResolveX(i*4 + 2 + x);
uint32 pixelY = idxr.ResolveY(j*4 + 2 + y);
FasTC::Pixel colorA = BilerpPixels(x, y, topLeftA, topRightA, bottomLeftA, bottomRightA);
FasTC::Pixel colorB = BilerpPixels(x, y, topLeftB, topRightB, bottomLeftB, bottomRightB);
FasTC::Pixel original(pixels[pixelY * w + pixelX]);
@ -938,8 +904,10 @@ namespace PVRTCC {
CompressionLabel *labels =
(CompressionLabel *)calloc(width * height, sizeof(CompressionLabel));
Indexer idxr(width, height, wrapMode);
// First traverse forward...
LabelImageForward(labels, cj.InBuf(), width, height);
LabelImageForward(labels, cj.InBuf(), idxr);
#ifndef NDEBUG
gDbgPixels = reinterpret_cast<const uint32 *>(cj.InBuf());
@ -961,7 +929,7 @@ namespace PVRTCC {
#endif
// Then traverse backward...
LabelImageBackward(labels, width, height);
LabelImageBackward(labels, idxr);
#ifndef NDEBUG
DebugOutputLabels("Backward-", labels, width, height);
@ -992,10 +960,10 @@ namespace PVRTCC {
#endif
// Then combine everything...
GenerateLowHighImages(labels, cj.InBuf(), cj.OutBuf(), width, height);
GenerateLowHighImages(labels, cj.InBuf(), cj.OutBuf(), idxr);
// Then compute modulation values
GenerateModulationValues(cj.OutBuf(), cj.InBuf(), width, height);
GenerateModulationValues(cj.OutBuf(), cj.InBuf(), idxr);
// Cleanup
free(labels);

118
PVRTCEncoder/src/Indexer.h Normal file
View file

@ -0,0 +1,118 @@
/* FasTC
* Copyright (c) 2013 University of North Carolina at Chapel Hill.
* All rights reserved.
*
* Permission to use, copy, modify, and distribute this software and its
* documentation for educational, research, and non-profit purposes, without
* fee, and without a written agreement is hereby granted, provided that the
* above copyright notice, this paragraph, and the following four paragraphs
* appear in all copies.
*
* Permission to incorporate this software into commercial products may be
* obtained by contacting the authors or the Office of Technology Development
* at the University of North Carolina at Chapel Hill <otd@unc.edu>.
*
* This software program and documentation are copyrighted by the University of
* North Carolina at Chapel Hill. The software program and documentation are
* supplied "as is," without any accompanying services from the University of
* North Carolina at Chapel Hill or the authors. The University of North
* Carolina at Chapel Hill and the authors do not warrant that the operation of
* the program will be uninterrupted or error-free. The end-user understands
* that the program was developed for research purposes and is advised not to
* rely exclusively on the program for any reason.
*
* IN NO EVENT SHALL THE UNIVERSITY OF NORTH CAROLINA AT CHAPEL HILL OR THE
* AUTHORS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL,
* OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF
* THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF NORTH CAROLINA
* AT CHAPEL HILL OR THE AUTHORS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE UNIVERSITY OF NORTH CAROLINA AT CHAPEL HILL AND THE AUTHORS SPECIFICALLY
* DISCLAIM ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE AND ANY
* STATUTORY WARRANTY OF NON-INFRINGEMENT. THE SOFTWARE PROVIDED HEREUNDER IS ON
* AN "AS IS" BASIS, AND THE UNIVERSITY OF NORTH CAROLINA AT CHAPEL HILL AND
* THE AUTHORS HAVE NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
* ENHANCEMENTS, OR MODIFICATIONS.
*
* Please send all BUG REPORTS to <pavel@cs.unc.edu>.
*
* The authors may be contacted via:
*
* Pavel Krajcevski
* Dept of Computer Science
* 201 S Columbia St
* Frederick P. Brooks, Jr. Computer Science Bldg
* Chapel Hill, NC 27599-3175
* USA
*
* <http://gamma.cs.unc.edu/FasTC/>
*/
#ifndef PVRTCENCODER_SRC_INDEXER_H_
#define PVRTCENCODER_SRC_INDEXER_H_
#include "FasTC/PVRTCCompressor.h"
#include <algorithm>
#include <cassert>
namespace PVRTCC {
class Indexer {
private:
const EWrapMode m_WrapMode;
const uint32 m_Width;
const uint32 m_Height;
uint32 Resolve(int32 i, uint32 limit) const {
int32 r;
switch(m_WrapMode) {
case eWrapMode_Clamp:
r = static_cast<uint32>(std::max(0, std::min<int32>(i, limit)));
break;
case eWrapMode_Wrap:
{
r = i;
int32 l = static_cast<int32>(limit);
while (r >= l) { r -= l; }
while (r < 0) { r += l; }
}
break;
}
assert (r >= 0);
assert (r < limit);
return r;
}
public:
Indexer(uint32 width, uint32 height, EWrapMode wrapMode = eWrapMode_Wrap)
: m_WrapMode(wrapMode)
, m_Width(width)
, m_Height(height)
{ }
uint32 GetWidth() const { return this->m_Width; }
uint32 GetHeight() const { return this->m_Height; }
EWrapMode GetWrapMode() const { return this->m_WrapMode; }
uint32 ResolveX(int32 i) const { return Resolve(i, this->m_Width); }
uint32 ResolveY(int32 i) const { return Resolve(i, this->m_Height); }
uint32 operator()(int32 i, int32 j) const {
uint32 _i = this->ResolveX(i);
uint32 _j = this->ResolveY(j);
uint32 index = _j * this->m_Width + _i;
assert (index < this->m_Width * this->m_Height);
assert (index >= 0);
return index;
}
};
} // namespace PVRTCC
#endif // PVRTCENCODER_SRC_INDEXER_H_