From 160fbb5e39791de6384f58f118a80940463f698d Mon Sep 17 00:00:00 2001 From: Pavel Krajcevski Date: Thu, 3 Oct 2013 14:12:41 -0400 Subject: [PATCH] Add method for downscaling by averaging nearby pixel values. --- PVRTCEncoder/src/Image.cpp | 60 +++++++++++++++++++++++++++++++++ PVRTCEncoder/src/Image.h | 4 +++ PVRTCEncoder/test/ImageTest.cpp | 23 +++++++++++++ 3 files changed, 87 insertions(+) diff --git a/PVRTCEncoder/src/Image.cpp b/PVRTCEncoder/src/Image.cpp index 09b256f..07a24d8 100644 --- a/PVRTCEncoder/src/Image.cpp +++ b/PVRTCEncoder/src/Image.cpp @@ -225,6 +225,66 @@ void Image::BilinearUpscale(uint32 xtimes, uint32 ytimes, m_Height = newHeight; } +static Pixel AveragePixels(const ::std::vector &pixels) { + if(pixels.size() == 0) { + return Pixel(); + } + + uint32 sum[4] = {0}; + ::std::vector::const_iterator it; + for(it = pixels.begin(); it != pixels.end(); it++) { + for(uint32 c = 0; c < 4; c++) { + sum[c] += (*it).Component(c); + } + } + + Pixel result; + for(uint32 c = 0; c < 4; c++) { + result.Component(c) = sum[c] / pixels.size(); + } + + return result; +} + +void Image::AverageDownscale(uint32 xtimes, uint32 ytimes, EWrapMode wrapMode) { + const uint32 w = GetWidth(); + const uint32 h = GetHeight(); + + const uint32 newWidth = w >> xtimes; + const uint32 newHeight = h >> ytimes; + + Pixel *downscaledPixels = new Pixel[newWidth * newHeight]; + + uint8 bitDepth[4]; + m_Pixels[0].GetBitDepth(bitDepth); + + uint32 pixelsX = 1 << xtimes; + uint32 pixelsY = 1 << ytimes; + + ::std::vector toAvg; + toAvg.reserve(pixelsX * pixelsY); + + for(uint32 j = 0; j < newHeight; j++) { + for(uint32 i = 0; i < newWidth; i++) { + uint32 newIdx = j * newWidth + i; + + toAvg.clear(); + for(uint32 y = j * pixelsY; y < (j+1) * pixelsY; y++) { + for(uint32 x = i * pixelsX; x < (i+1) * pixelsX; x++) { + toAvg.push_back(GetPixel(x, y, wrapMode)); + } + } + + downscaledPixels[newIdx] = AveragePixels(toAvg); + } + } + + delete m_Pixels; + m_Pixels = downscaledPixels; + m_Width = newWidth; + m_Height = newHeight; +} + void Image::ContentAwareDownscale(uint32 xtimes, uint32 ytimes, EWrapMode wrapMode, bool bOffsetNewPixels) { const uint32 w = GetWidth(); diff --git a/PVRTCEncoder/src/Image.h b/PVRTCEncoder/src/Image.h index e908495..7f63f57 100644 --- a/PVRTCEncoder/src/Image.h +++ b/PVRTCEncoder/src/Image.h @@ -82,6 +82,10 @@ class Image { EWrapMode wrapMode = eWrapMode_Wrap, bool bOffsetNewPixels = false); + // Downscales the image by using a simple averaging of the neighboring pixel values + void AverageDownscale(uint32 xtimes, uint32 ytimes, + EWrapMode wrapMode = eWrapMode_Wrap); + void ComputeHessianEigenvalues(::std::vector &eigOne, ::std::vector &eigTwo, EWrapMode wrapMode = eWrapMode_Wrap); diff --git a/PVRTCEncoder/test/ImageTest.cpp b/PVRTCEncoder/test/ImageTest.cpp index 527e530..785d77e 100644 --- a/PVRTCEncoder/test/ImageTest.cpp +++ b/PVRTCEncoder/test/ImageTest.cpp @@ -279,6 +279,29 @@ TEST(Image, BilinearUpscaleWrapped) { } } +TEST(Image, AverageDownscale) { + PVRTCC::Image img(8, 8); + for(uint32 j = 0; j < img.GetHeight(); j++) { + for(uint32 i = 0; i < img.GetWidth(); i++) { + if((i ^ j) & 1) { + img(i, j) = PVRTCC::Pixel(0xFF000000); + } else { + img(i, j) = PVRTCC::Pixel(0xFFFFFFFF); + } + } + } + + img.AverageDownscale(1, 2); + EXPECT_EQ(img.GetWidth(), static_cast(4)); + EXPECT_EQ(img.GetHeight(), static_cast(2)); + + for(uint32 j = 0; j < img.GetHeight(); j++) { + for(uint32 i = 0; i < img.GetWidth(); i++) { + EXPECT_EQ(PixelPrinter(0xFF7F7F7F), PixelPrinter(img(i, j).PackRGBA())); + } + } +} + TEST(Image, ContentAwareDownscale) { PVRTCC::Image img(8, 8); for(uint32 j = 0; j < img.GetHeight(); j++) {