From 4baf2ce3111ca3bd9f2a4916f53068bc8f9e7ca8 Mon Sep 17 00:00:00 2001 From: Pavel Krajcevski Date: Fri, 4 Oct 2013 18:35:18 -0400 Subject: [PATCH] Combine image functionality from PVRTCEncoder into Base library. --- Base/include/Image.h | 120 +++++++---- Base/include/ImageFwd.h | 63 ++++++ Base/src/Image.cpp | 196 ++++++++++++------ Base/test/CMakeLists.txt | 4 +- Base/test/TestImage.cpp | 121 +++++++++++ Base/test/Utils.h | 82 ++++++++ CLTool/src/clunix.cpp | 2 +- Core/include/CompressedImage.h | 22 +- Core/include/TexComp.h | 5 +- Core/src/CompressedImage.cpp | 70 ++++--- Core/src/TexComp.cpp | 44 ++-- IO/config/ImageWriter.h.in | 20 +- IO/include/ImageFile.h | 10 +- IO/src/ImageFile.cpp | 6 +- IO/src/ImageWriter.cpp | 9 +- IO/src/ImageWriterPNG.cpp | 22 +- IO/src/ImageWriterPNG.h | 4 +- PVRTCEncoder/CMakeLists.txt | 4 +- PVRTCEncoder/src/Compressor.cpp | 2 +- PVRTCEncoder/src/Decompressor.cpp | 12 +- .../src/{Image.cpp => PVRTCImage.cpp} | 123 ++++------- PVRTCEncoder/src/{Image.h => PVRTCImage.h} | 27 +-- PVRTCEncoder/test/CMakeLists.txt | 4 +- PVRTCEncoder/test/ImageTest.cpp | 85 +------- 24 files changed, 669 insertions(+), 388 deletions(-) create mode 100644 Base/include/ImageFwd.h create mode 100644 Base/test/TestImage.cpp create mode 100644 Base/test/Utils.h rename PVRTCEncoder/src/{Image.cpp => PVRTCImage.cpp} (79%) rename PVRTCEncoder/src/{Image.h => PVRTCImage.h} (87%) diff --git a/Base/include/Image.h b/Base/include/Image.h index bd34b95..0c0eaec 100644 --- a/Base/include/Image.h +++ b/Base/include/Image.h @@ -41,58 +41,86 @@ * */ -#ifndef __TEXCOMP_IMAGE_H__ -#define __TEXCOMP_IMAGE_H__ +#ifndef FASTC_BASE_INCLUDE_IMAGE_H_ +#define FASTC_BASE_INCLUDE_IMAGE_H_ #include "TexCompTypes.h" +#include "ImageFwd.h" -class Image { +namespace FasTC { - public: - Image(uint32 width, uint32 height, - const uint32 *pixels, - bool bBlockStreamOrder = false); - Image(const Image &); - Image &operator=(const Image &); - virtual ~Image(); + template + extern double ComputePSNR(Image *img1, Image *img2); - virtual Image *Clone() const { - return new Image(*this); + // Forward declare + template + class Image { + + public: + Image(uint32 width, uint32 height); + Image(uint32 width, uint32 height, + const PixelType *pixels, + bool bBlockStreamOrder = false); + Image(uint32 width, uint32 height, + const uint32 *pixels, + bool bBlockStreamOrder = false); + Image(const Image &); + Image &operator=(const Image &); + virtual ~Image(); + + virtual Image *Clone() const { + return new Image(*this); + }; + + PixelType &operator()(uint32 i, uint32 j); + const PixelType &operator()(uint32 i, uint32 j) const; + + // Reads a buffer full of pixels and stores them in the + // data associated with this image. + virtual bool ReadPixels(const uint32 *rgba); + const PixelType *GetPixels() const { return m_Pixels; } + + uint32 GetWidth() const { return m_Width; } + uint32 GetHeight() const { return m_Height; } + uint32 GetNumPixels() const { return GetWidth() * GetHeight(); } + + void SetBlockStreamOrder(bool flag) { + if(flag) { + ConvertToBlockStreamOrder(); + } else { + ConvertFromBlockStreamOrder(); + } + } + bool GetBlockStreamOrder() const { return m_bBlockStreamOrder; } + + template + double ComputePSNR(Image *other) { + return FasTC::ComputePSNR(this, other); + } + + // Function to allow derived classes to populate the pixel array. + // This may involve decompressing a compressed image or otherwise + // processing some data in order to populate the m_Pixels pointer. + // This function should use SetImageData in order to set all of the + // appropriate pixels. + virtual void ComputePixels() { } + + private: + uint32 m_Width; + uint32 m_Height; + + bool m_bBlockStreamOrder; + + PixelType *m_Pixels; + + protected: + + void SetImageData(uint32 width, uint32 height, PixelType *data); + + void ConvertToBlockStreamOrder(); + void ConvertFromBlockStreamOrder(); }; - const uint8 *RawData() const { return m_Data; } - - uint32 GetWidth() const { return m_Width; } - uint32 GetHeight() const { return m_Height; } - - void SetBlockStreamOrder(bool flag) { - if(flag) { - ConvertToBlockStreamOrder(); - } else { - ConvertFromBlockStreamOrder(); - } - } - bool GetBlockStreamOrder() const { return m_bBlockStreamOrder; } - - double ComputePSNR(Image *other); - - virtual void ComputeRGBA() { } - virtual const uint32 *GetRGBA() const { - return reinterpret_cast(RawData()); - } - - private: - uint32 m_Width; - uint32 m_Height; - - bool m_bBlockStreamOrder; - - protected: - uint32 m_DataSz; - uint8 *m_Data; - - void ConvertToBlockStreamOrder(); - void ConvertFromBlockStreamOrder(); -}; +} // namespace FasTC #endif // __TEXCOMP_IMAGE_H__ diff --git a/Base/include/ImageFwd.h b/Base/include/ImageFwd.h new file mode 100644 index 0000000..9204781 --- /dev/null +++ b/Base/include/ImageFwd.h @@ -0,0 +1,63 @@ +/* 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 . + * + * 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 . + * + * 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 + * + * + */ + +#ifndef FASTC_BASE_INCLUDE_IMAGEFWD_H_ +#define FASTC_BASE_INCLUDE_IMAGEFWD_H_ + +#include "TexCompTypes.h" + +namespace FasTC { + class Pixel; + template class Image; +} + +#endif // FASTC_BASE_INCLUDE_IMAGEFWD_H_ diff --git a/Base/src/Image.cpp b/Base/src/Image.cpp index ea61a6e..557af1a 100644 --- a/Base/src/Image.cpp +++ b/Base/src/Image.cpp @@ -49,107 +49,156 @@ #include #include +#include "Pixel.h" + template static inline T sad( const T &a, const T &b ) { return (a > b)? a - b : b - a; } -Image::Image(const Image &other) +namespace FasTC { + +template +Image::Image(uint32 width, uint32 height) + : m_Width(width) + , m_Height(height) + , m_bBlockStreamOrder(false) + , m_Pixels(new PixelType[GetNumPixels()]) +{ } + +template +Image::Image(uint32 width, uint32 height, + const PixelType *pixels, + bool bBlockStreamOrder) + : m_Width(width) + , m_Height(height) + , m_bBlockStreamOrder(false) +{ + if(pixels) { + m_Pixels = new PixelType[GetNumPixels()]; + memcpy(m_Pixels, pixels, GetNumPixels() * sizeof(PixelType)); + } else { + m_Pixels = 0; + } +} + +template +Image::Image(const Image &other) : m_Width(other.m_Width) , m_Height(other.m_Height) , m_bBlockStreamOrder(other.GetBlockStreamOrder()) - , m_DataSz(other.m_DataSz) - , m_Data(new uint8[m_DataSz]) + , m_Pixels(new PixelType[GetNumPixels()]) { - if(m_Data) { - memcpy(m_Data, other.m_Data, m_DataSz); - } else { - fprintf(stderr, "Out of memory!\n"); - } + memcpy(m_Pixels, other.m_Pixels, GetNumPixels() * sizeof(PixelType)); } -Image::Image(uint32 width, uint32 height, const uint32 *pixels, bool bBlockStreamOrder) +template +bool Image::ReadPixels(const uint32 *rgba) { + + assert(m_Pixels); + for(uint32 i = 0; i < GetNumPixels(); i++) { + m_Pixels[i].Unpack(rgba[i]); + } + + return true; +} + +template +Image::Image(uint32 width, uint32 height, const uint32 *pixels, bool bBlockStreamOrder) : m_Width(width) , m_Height(height) , m_bBlockStreamOrder(bBlockStreamOrder) - , m_DataSz(m_Width * m_Height * sizeof(uint32)) { if(pixels) { - m_Data = new uint8[m_DataSz]; - memcpy(m_Data, pixels, m_DataSz); + m_Pixels = new PixelType[GetNumPixels()]; + ReadPixels(pixels); } else { - m_Data = NULL; + m_Pixels = NULL; } } -Image::~Image() { - if(m_Data) { - delete [] m_Data; - m_Data = 0; +template +Image::~Image() { + if(m_Pixels) { + delete [] m_Pixels; + m_Pixels = 0; } } -Image &Image::operator=(const Image &other) { +template +Image &Image::operator=(const Image &other) { m_Width = other.m_Width; m_Height = other.m_Height; m_bBlockStreamOrder = other.GetBlockStreamOrder(); - m_DataSz = other.m_DataSz; - if(m_Data) { - delete [] m_Data; + if(m_Pixels) { + delete [] m_Pixels; } - if(other.m_Data) { - m_Data = new uint8[m_DataSz]; - if(m_Data) - memcpy(m_Data, other.m_Data, m_DataSz); + if(other.m_Pixels) { + m_Pixels = new PixelType[GetNumPixels()]; + if(m_Pixels) + memcpy(m_Pixels, other.m_Pixels, GetNumPixels() * sizeof(PixelType)); else fprintf(stderr, "Out of memory!\n"); + } else { + m_Pixels = NULL; } - else { - m_Data = other.m_Data; - } - + return *this; } -double Image::ComputePSNR(Image *other) { - if(!other) +template +PixelType & Image::operator()(uint32 i, uint32 j) { + assert(i < GetWidth()); + assert(j < GetHeight()); + return m_Pixels[j * GetWidth() + i]; +} + +template +const PixelType & Image::operator()(uint32 i, uint32 j) const { + assert(i < GetWidth()); + assert(j < GetHeight()); + return m_Pixels[j * GetWidth() + i]; +} + +template +double ComputePSNR(Image *img1, Image *img2) { + if(!img1 || !img2) return -1.0; - if(other->GetWidth() != GetWidth() || - other->GetHeight() != GetHeight()) { + if(img1->GetWidth() != img2->GetWidth() || + img1->GetHeight() != img2->GetHeight()) { return -1.0; } // Compute raw 8-bit RGBA data... - other->ComputeRGBA(); - ComputeRGBA(); + img1->ComputePixels(); + img2->ComputePixels(); - const uint8 *ourData = - reinterpret_cast(GetRGBA()); - const uint8 *otherData = - reinterpret_cast(other->GetRGBA()); + const PixelTypeOne *ourPixels = img1->GetPixels(); + const PixelTypeTwo *otherPixels = img2->GetPixels(); // const double w[3] = { 0.2126, 0.7152, 0.0722 }; const double w[3] = { 1.0, 1.0, 1.0 }; double mse = 0.0; - const uint32 imageSz = GetWidth() * GetHeight() * 4; - for(uint32 i = 0; i < imageSz; i+=4) { + const uint32 imageSz = img1->GetNumPixels(); + for(uint32 i = 0; i < imageSz; i++) { - const unsigned char *pixelDataRaw = ourData + i; - const unsigned char *pixelDataUncomp = otherData + i; + uint32 ourPixel = ourPixels[i].Pack(); + uint32 otherPixel = otherPixels[i].Pack(); double r[4], u[4]; for(uint32 c = 0; c < 4; c++) { + uint32 shift = c * 8; if(c == 3) { - r[c] = pixelDataRaw[c] / 255.0; - u[c] = pixelDataUncomp[c] / 255.0; + r[c] = static_cast((ourPixel >> shift) & 0xFF) / 255.0; + u[c] = static_cast((otherPixel >> shift) & 0xFF) / 255.0; } else { - r[c] = static_cast(pixelDataRaw[c]) * w[c]; - u[c] = static_cast(pixelDataUncomp[c]) * w[c]; + r[c] = static_cast((ourPixel >> shift) & 0xFF) * w[c]; + u[c] = static_cast((otherPixel >> shift) & 0xFF) * w[c]; } } @@ -159,19 +208,22 @@ double Image::ComputePSNR(Image *other) { } } - mse /= GetWidth() * GetHeight(); + mse /= img1->GetWidth() * img1->GetHeight(); const double C = 255.0 * 255.0; double maxi = (w[0]*w[0] + w[1]*w[1] + w[2]*w[2]) * C; return 10 * log10(maxi/mse); } +template double ComputePSNR(Image *, Image *); + // !FIXME! These won't work for non-RGBA8 data. -void Image::ConvertToBlockStreamOrder() { - if(m_bBlockStreamOrder || !m_Data) +template +void Image::ConvertToBlockStreamOrder() { + if(m_bBlockStreamOrder || !m_Pixels) return; - uint32 *newPixelData = new uint32[GetWidth() * GetHeight() * 4]; + PixelType *newPixelData = new PixelType[GetWidth() * GetHeight()]; for(uint32 j = 0; j < GetHeight(); j+=4) { for(uint32 i = 0; i < GetWidth(); i+=4) { uint32 blockX = i / 4; @@ -182,22 +234,22 @@ void Image::ConvertToBlockStreamOrder() { for(uint32 t = 0; t < 16; t++) { uint32 x = i + t % 4; uint32 y = j + t / 4; - newPixelData[offset + t] = - reinterpret_cast(m_Data)[y*GetWidth() + x]; + newPixelData[offset + t] = m_Pixels[y*GetWidth() + x]; } } } - delete m_Data; - m_Data = reinterpret_cast(newPixelData); + delete m_Pixels; + m_Pixels = newPixelData; m_bBlockStreamOrder = true; } -void Image::ConvertFromBlockStreamOrder() { - if(!m_bBlockStreamOrder || !m_Data) +template +void Image::ConvertFromBlockStreamOrder() { + if(!m_bBlockStreamOrder || !m_Pixels) return; - uint32 *newPixelData = new uint32[GetWidth() * GetHeight() * 4]; + PixelType *newPixelData = new PixelType[GetWidth() * GetHeight()]; for(uint32 j = 0; j < GetHeight(); j+=4) { for(uint32 i = 0; i < GetWidth(); i+=4) { uint32 blockX = i / 4; @@ -208,13 +260,33 @@ void Image::ConvertFromBlockStreamOrder() { for(uint32 t = 0; t < 16; t++) { uint32 x = i + t % 4; uint32 y = j + t / 4; - newPixelData[y*GetWidth() + x] = - reinterpret_cast(m_Data)[offset + t]; + newPixelData[y*GetWidth() + x] = m_Pixels[offset + t]; } } } - delete m_Data; - m_Data = reinterpret_cast(newPixelData); + delete m_Pixels; + m_Pixels = newPixelData; m_bBlockStreamOrder = false; } + +template +void Image::SetImageData(uint32 width, uint32 height, PixelType *data) { + if(m_Pixels) { + delete m_Pixels; + } + + if(!data) { + width = 0; + height = 0; + m_Pixels = NULL; + } else { + m_Width = width; + m_Height = height; + m_Pixels = data; + } +} + +template class Image; + +} // namespace FasTC diff --git a/Base/test/CMakeLists.txt b/Base/test/CMakeLists.txt index 43fb154..84518f7 100644 --- a/Base/test/CMakeLists.txt +++ b/Base/test/CMakeLists.txt @@ -53,11 +53,11 @@ INCLUDE_DIRECTORIES(${FasTC_SOURCE_DIR}/Base/include) INCLUDE_DIRECTORIES(${FasTC_SOURCE_DIR}/GTest/include) SET(TESTS - Pixel + Pixel Image ) FOREACH(TEST ${TESTS}) - SET(TEST_NAME Test${TEST}) + SET(TEST_NAME Test_Base_${TEST}) SET(TEST_MODULE Test${TEST}.cpp) ADD_EXECUTABLE(${TEST_NAME} ${TEST_MODULE}) TARGET_LINK_LIBRARIES(${TEST_NAME} FasTCBase) diff --git a/Base/test/TestImage.cpp b/Base/test/TestImage.cpp new file mode 100644 index 0000000..df1ffc7 --- /dev/null +++ b/Base/test/TestImage.cpp @@ -0,0 +1,121 @@ +/* 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 . + * + * 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 . + * + * 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 + * + * + */ + +#include "gtest/gtest.h" +#include "Image.h" +#include "Pixel.h" +#include "Utils.h" + +#include + +TEST(Image, NonSpecificConstructor) { + FasTC::Pixel p; + FasTC::Image img (4, 4); + for(uint32 i = 0; i < 4; i++) { + for(uint32 j = 0; j < 4; j++) { + EXPECT_TRUE(img(i, j) == p); + } + } +} + +TEST(Image, SpecificConstructor) { + FasTC::Pixel pxs[16]; + for(uint32 i = 0; i < 4; i++) { + for(uint32 j = 0; j < 4; j++) { + pxs[j*4 + i].R() = i; + pxs[j*4 + i].G() = j; + } + } + + FasTC::Image img(4, 4, pxs); + for(uint32 i = 0; i < 4; i++) { + for(uint32 j = 0; j < 4; j++) { + EXPECT_TRUE(img(i, j) == pxs[j*4 + i]); + } + } +} + +TEST(Image, CopyConstructor) { + FasTC::Pixel pxs[16]; + for(uint32 i = 0; i < 4; i++) { + for(uint32 j = 0; j < 4; j++) { + pxs[j*4 + i].R() = i; + pxs[j*4 + i].G() = j; + } + } + + FasTC::Image img(4, 4, pxs); + FasTC::Image img2(img); + for(uint32 i = 0; i < 4; i++) { + for(uint32 j = 0; j < 4; j++) { + EXPECT_TRUE(img2(i, j) == pxs[j*4 + i]); + } + } +} + +TEST(Image, AssignmentOperator) { + FasTC::Pixel pxs[16]; + for(uint32 i = 0; i < 4; i++) { + for(uint32 j = 0; j < 4; j++) { + pxs[j*4 + i].R() = i; + pxs[j*4 + i].G() = j; + } + } + + FasTC::Image img(4, 4, pxs); + FasTC::Image img2 = img; + for(uint32 i = 0; i < 4; i++) { + for(uint32 j = 0; j < 4; j++) { + EXPECT_TRUE(img2(i, j) == pxs[j*4 + i]); + } + } +} diff --git a/Base/test/Utils.h b/Base/test/Utils.h new file mode 100644 index 0000000..9c371d3 --- /dev/null +++ b/Base/test/Utils.h @@ -0,0 +1,82 @@ +/* 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 . + * + * 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 . + * + * 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 + * + * + */ + +#ifndef PVRTCENCODER_TEST_TESTUTILS_H_ +#define PVRTCENCODER_TEST_TESTUTILS_H_ + +#include "TexCompTypes.h" + +class PixelPrinter { + private: + uint32 m_PixelValue; + public: + explicit PixelPrinter(uint32 p) : m_PixelValue(p) { } + bool operator==(const PixelPrinter &other) const { + return other.m_PixelValue == this->m_PixelValue; + } + uint32 Value() const { return m_PixelValue; } +}; + +inline ::std::ostream& operator<<(::std::ostream& os, const PixelPrinter& pp) { + uint32 p = pp.Value(); + uint32 r = p & 0xFF; + uint32 g = (p >> 8) & 0xFF; + uint32 b = (p >> 16) & 0xFF; + uint32 a = (p >> 24) & 0xFF; + return os << + "R: 0x" << ::std::hex << r << " " << + "G: 0x" << ::std::hex << g << " " << + "B: 0x" << ::std::hex << b << " " << + "A: 0x" << ::std::hex << a; +} + +#endif // PVRTCENCODER_TEST_TESTUTILS_H_ diff --git a/CLTool/src/clunix.cpp b/CLTool/src/clunix.cpp index e6c4035..24f0e73 100644 --- a/CLTool/src/clunix.cpp +++ b/CLTool/src/clunix.cpp @@ -208,7 +208,7 @@ int main(int argc, char **argv) { return 1; } - Image img = Image(*file.GetImage()); + FasTC::Image<> img(*file.GetImage()); if(format == eCompressionFormat_PVRTC) { img.SetBlockStreamOrder(false); } diff --git a/Core/include/CompressedImage.h b/Core/include/CompressedImage.h index f07818e..930d160 100644 --- a/Core/include/CompressedImage.h +++ b/Core/include/CompressedImage.h @@ -57,10 +57,10 @@ enum ECompressionFormat { #include "Image.h" -class CompressedImage : public Image { +class CompressedImage : public FasTC::Image { private: ECompressionFormat m_Format; - uint32 *m_RGBAData; + uint8 *m_CompressedData; public: CompressedImage(const CompressedImage &); @@ -70,20 +70,19 @@ class CompressedImage : public Image { // the passed format. The size of the data is expected to conform // to the width, height, and format specified. CompressedImage( - const uint32 width, - const uint32 height, - const ECompressionFormat format, + const uint32 width, + const uint32 height, + const ECompressionFormat format, const uint8 *data ); virtual ~CompressedImage(); - virtual Image *Clone() const { + virtual FasTC::Image *Clone() const { return new CompressedImage(*this); } - virtual void ComputeRGBA(); - virtual const uint32 *GetRGBA() const { return m_RGBAData; } + virtual void ComputePixels(); static uint32 GetCompressedSize(uint32 uncompressedSize, ECompressionFormat format); static uint32 GetUncompressedSize(uint32 compressedSize, ECompressionFormat format) { @@ -91,6 +90,13 @@ class CompressedImage : public Image { return compressedSize * (compressedSize / cmp); } + uint32 GetCompressedSize() const { + return GetCompressedSize(GetUncompressedSize(), m_Format); + } + uint32 GetUncompressedSize() const { + return GetWidth() * GetHeight() * sizeof(uint32); + } + // Decompress the compressed image data into outBuf. outBufSz is expected // to be the proper size determined by the width, height, and format. // !FIXME! We should have a function to explicitly return the in/out buf diff --git a/Core/include/TexComp.h b/Core/include/TexComp.h index f62bbd6..bb431c9 100644 --- a/Core/include/TexComp.h +++ b/Core/include/TexComp.h @@ -48,9 +48,9 @@ #include "CompressionJob.h" #include +#include "ImageFwd.h" // Forward declarations -class Image; class ImageFile; struct SCompressionSettings { @@ -91,7 +91,8 @@ struct SCompressionSettings { std::ostream *logStream; }; -extern CompressedImage *CompressImage(Image *img, const SCompressionSettings &settings); +template +extern CompressedImage *CompressImage(FasTC::Image *img, const SCompressionSettings &settings); extern bool CompressImageData( const unsigned char *data, diff --git a/Core/src/CompressedImage.cpp b/Core/src/CompressedImage.cpp index cc68fe4..f6f4f1f 100644 --- a/Core/src/CompressedImage.cpp +++ b/Core/src/CompressedImage.cpp @@ -48,6 +48,8 @@ #include #include +#include "Pixel.h" + #include "TexCompTypes.h" #include "BC7Compressor.h" #include "PVRTCCompressor.h" @@ -55,11 +57,12 @@ CompressedImage::CompressedImage( const CompressedImage &other ) : Image(other) , m_Format(other.m_Format) - , m_RGBAData(0) + , m_CompressedData(0) { - if(other.m_RGBAData) { - m_RGBAData = new uint32[GetWidth() * GetHeight()]; - memcpy(m_RGBAData, other.m_RGBAData, sizeof(uint32) * GetWidth() * GetHeight()); + if(other.m_CompressedData) { + uint32 compressedSz = GetCompressedSize(); + m_CompressedData = new uint8[compressedSz]; + memcpy(m_CompressedData, other.m_CompressedData, compressedSz); } } @@ -69,47 +72,44 @@ CompressedImage::CompressedImage( const ECompressionFormat format, const unsigned char *data ) - : Image(width, height, NULL, format != eCompressionFormat_PVRTC) + : FasTC::Image<>(width, height, + reinterpret_cast(NULL), + format != eCompressionFormat_PVRTC) , m_Format(format) - , m_RGBAData(0) + , m_CompressedData(0) { - m_DataSz = GetCompressedSize(GetWidth() * GetHeight() * 4, m_Format); - if(m_DataSz > 0) { - assert(!m_Data); - m_Data = new unsigned char[m_DataSz]; - memcpy(m_Data, data, m_DataSz); + uint32 cmpSz = GetCompressedSize(); + if(cmpSz > 0) { + assert(!m_CompressedData); + m_CompressedData = new uint8[cmpSz]; + memcpy(m_CompressedData, data, cmpSz); } } CompressedImage &CompressedImage::operator=(const CompressedImage &other) { Image::operator=(other); m_Format = other.m_Format; - if(other.m_RGBAData) { - m_RGBAData = new uint32[GetWidth() * GetHeight()]; - memcpy(m_RGBAData, other.m_RGBAData, sizeof(uint32) * GetWidth() * GetHeight()); + if(other.m_CompressedData) { + uint32 cmpSz = GetCompressedSize(); + m_CompressedData = new uint8[cmpSz]; + memcpy(m_CompressedData, other.m_CompressedData, cmpSz); } return *this; } CompressedImage::~CompressedImage() { - if(m_RGBAData) { - delete m_RGBAData; - m_RGBAData = NULL; + if(m_CompressedData) { + delete m_CompressedData; + m_CompressedData = NULL; } } bool CompressedImage::DecompressImage(unsigned char *outBuf, unsigned int outBufSz) const { - // First make sure that we have enough data - uint32 dataSz = GetUncompressedSize(m_DataSz, m_Format); - if(dataSz > outBufSz) { - fprintf(stderr, "Not enough space to store entire decompressed image! " - "Got %d bytes, but need %d!\n", outBufSz, dataSz); - assert(false); - return false; - } + assert(outBufSz == GetUncompressedSize()); - DecompressionJob dj (m_Data, outBuf, GetWidth(), GetHeight()); + uint8 *byteData = reinterpret_cast(m_CompressedData); + DecompressionJob dj (byteData, outBuf, GetWidth(), GetHeight()); switch(m_Format) { case eCompressionFormat_PVRTC: { @@ -135,15 +135,20 @@ bool CompressedImage::DecompressImage(unsigned char *outBuf, unsigned int outBuf return true; } -void CompressedImage::ComputeRGBA() { +void CompressedImage::ComputePixels() { - if(m_RGBAData) { - delete m_RGBAData; + uint32 unCompSz = GetWidth() * GetHeight() * 4; + uint8 *unCompBuf = new uint8[unCompSz]; + DecompressImage(unCompBuf, unCompSz); + + uint32 * newPixelBuf = reinterpret_cast(unCompBuf); + + FasTC::Pixel *newPixels = new FasTC::Pixel[GetWidth() * GetHeight()]; + for(uint32 i = 0; i < GetWidth() * GetHeight(); i++) { + newPixels[i].Unpack(newPixelBuf[i]); } - m_RGBAData = new uint32[GetWidth() * GetHeight()]; - uint8 *pixelData = reinterpret_cast(m_RGBAData); - DecompressImage(pixelData, GetWidth() * GetHeight() * 4); + SetImageData(GetWidth(), GetHeight(), newPixels); } uint32 CompressedImage::GetCompressedSize(uint32 uncompressedSize, ECompressionFormat format) { @@ -167,4 +172,3 @@ uint32 CompressedImage::GetCompressedSize(uint32 uncompressedSize, ECompressionF return cmpDataSzNeeded; } - diff --git a/Core/src/TexComp.cpp b/Core/src/TexComp.cpp index ee91714..2d4d03a 100644 --- a/Core/src/TexComp.cpp +++ b/Core/src/TexComp.cpp @@ -54,6 +54,7 @@ #include "CompressionFuncs.h" #include "Image.h" #include "ImageFile.h" +#include "Pixel.h" #include "PVRTCCompressor.h" #include "Thread.h" #include "ThreadGroup.h" @@ -349,8 +350,9 @@ static double CompressImageWithWorkerQueue( return cmpTimeTotal / double(settings.iNumCompressions); } +template CompressedImage *CompressImage( - Image *img, const SCompressionSettings &settings + FasTC::Image *img, const SCompressionSettings &settings ) { if(!img) return NULL; @@ -359,6 +361,7 @@ CompressedImage *CompressImage( CompressedImage *outImg = NULL; const unsigned int dataSz = w * h * 4; + uint32 *data = new uint32[dataSz / 4]; assert(dataSz > 0); @@ -366,24 +369,33 @@ CompressedImage *CompressImage( uint32 cmpDataSz = CompressedImage::GetCompressedSize(dataSz, settings.format); // Make sure that we have RGBA data... - img->ComputeRGBA(); + img->ComputePixels(); + const PixelType *pixels = img->GetPixels(); + for(uint32 i = 0; i < img->GetNumPixels(); i++) { + data[i] = pixels[i].Pack(); + } unsigned char *cmpData = new unsigned char[cmpDataSz]; - const uint8 *pixelData = reinterpret_cast(img->GetRGBA()); - CompressImageData(pixelData, w, h, cmpData, cmpDataSz, settings); + CompressImageData(reinterpret_cast(data), w, h, cmpData, cmpDataSz, settings); outImg = new CompressedImage(w, h, settings.format, cmpData); + delete [] data; delete [] cmpData; return outImg; } +// !FIXME! Ideally, we wouldn't have to do this because there would be a way to instantiate this +// function in the header or using some fancy template metaprogramming. I can't think of the way +// at the moment. +template CompressedImage *CompressImage(FasTC::Image *, const SCompressionSettings &settings); + bool CompressImageData( - const unsigned char *data, - const unsigned int width, - const unsigned int height, - unsigned char *cmpData, - const unsigned int cmpDataSz, + const uint8 *data, + const uint32 width, + const uint32 height, + uint8 *compressedData, + const uint32 cmpDataSz, const SCompressionSettings &settings ) { @@ -417,14 +429,14 @@ bool CompressImageData( } // Allocate data based on the compression method - uint32 cmpDataSzNeeded = + uint32 compressedDataSzNeeded = CompressedImage::GetCompressedSize(dataSz, settings.format); - if(cmpDataSzNeeded == 0) { + if(compressedDataSzNeeded == 0) { ReportError("Unknown compression format"); return false; } - else if(cmpDataSzNeeded > cmpDataSz) { + else if(compressedDataSzNeeded > cmpDataSz) { ReportError("Not enough space for compressed data!"); return false; } @@ -436,15 +448,15 @@ bool CompressImageData( if(numThreads > 1) { if(settings.bUseAtomics) { - cmpMSTime = CompressImageWithAtomics(data, width, height, settings, cmpData); + cmpMSTime = CompressImageWithAtomics(data, width, height, settings, compressedData); } else if(settings.iJobSize > 0) { - cmpMSTime = CompressImageWithWorkerQueue(data, dataSz, settings, cmpData); + cmpMSTime = CompressImageWithWorkerQueue(data, dataSz, settings, compressedData); } else { - cmpMSTime = CompressImageWithThreads(data, dataSz, settings, cmpData); + cmpMSTime = CompressImageWithThreads(data, dataSz, settings, compressedData); } } else { - cmpMSTime = CompressImageInSerial(data, width, height, settings, cmpData); + cmpMSTime = CompressImageInSerial(data, width, height, settings, compressedData); } // Report compression time diff --git a/IO/config/ImageWriter.h.in b/IO/config/ImageWriter.h.in index c876a08..0d609d5 100644 --- a/IO/config/ImageWriter.h.in +++ b/IO/config/ImageWriter.h.in @@ -47,19 +47,23 @@ #include "ImageFileFormat.h" #include "TexCompTypes.h" +namespace FasTC { + class Pixel; +} + class ImageWriter { protected: - const uint8 *m_PixelData; + const FasTC::Pixel *m_Pixels; uint32 m_RawFileDataSz; uint8 *m_RawFileData; uint32 m_Width; uint32 m_Height; - ImageWriter(const int width, const int height, const uint8 *rawData) - : m_PixelData(rawData) + ImageWriter(const int width, const int height, const FasTC::Pixel *rawData) + : m_Pixels(rawData) , m_RawFileDataSz(256) , m_RawFileData(new uint8[m_RawFileDataSz]) , m_Width(width), m_Height(height) @@ -69,15 +73,15 @@ class ImageWriter { public: virtual ~ImageWriter() { - if(m_RawFileData) { - delete m_RawFileData; - m_RawFileData = 0; - } + if(m_RawFileData) { + delete m_RawFileData; + m_RawFileData = 0; + } } uint32 GetWidth() const { return m_Width; } uint32 GetHeight() const { return m_Height; } - uint32 GetImageDataSz() const { return m_Width * m_Height * 4; } + uint32 GetImageDataSz() const { return m_Width * m_Height * sizeof(uint32); } uint32 GetRawFileDataSz() const { return m_RawFileDataSz; } uint8 *GetRawFileData() const { return m_RawFileData; } virtual bool WriteImage() = 0; diff --git a/IO/include/ImageFile.h b/IO/include/ImageFile.h index eb365c6..6c2bbfe 100644 --- a/IO/include/ImageFile.h +++ b/IO/include/ImageFile.h @@ -46,9 +46,9 @@ #include "TexCompTypes.h" #include "ImageFileFormat.h" +#include "ImageFwd.h" // Forward declare -class Image; class CompressedImage; struct SCompressionSettings; @@ -66,13 +66,13 @@ public: // Creates an imagefile with the corresponding image data. This is ready // to be written to disk with the passed filename. - ImageFile(const char *filename, EImageFileFormat format, const Image &); + ImageFile(const char *filename, EImageFileFormat format, const FasTC::Image<> &); ~ImageFile(); unsigned int GetWidth() const { return m_Width; } unsigned int GetHeight() const { return m_Height; } - Image *GetImage() const { return m_Image; } + FasTC::Image<> *GetImage() const { return m_Image; } // Loads the image into memory. If this function returns true, then a valid // m_Image will be created and available. @@ -91,12 +91,12 @@ public: const EImageFileFormat m_FileFormat; - Image *m_Image; + FasTC::Image<> *m_Image; static unsigned char *ReadFileData(const CHAR *filename); static bool WriteImageDataToFile(const uint8 *data, const uint32 dataSz, const CHAR *filename); static EImageFileFormat DetectFileFormat(const CHAR *filename); - Image *LoadImage(const unsigned char *rawImageData) const; + FasTC::Image<> *LoadImage(const unsigned char *rawImageData) const; }; #endif // _IMAGE_FILE_H_ diff --git a/IO/src/ImageFile.cpp b/IO/src/ImageFile.cpp index 293bc9b..8b29a34 100644 --- a/IO/src/ImageFile.cpp +++ b/IO/src/ImageFile.cpp @@ -105,7 +105,7 @@ ImageFile::ImageFile(const CHAR *filename, EImageFileFormat format) strncpy(m_Filename, filename, kMaxFilenameSz); } -ImageFile::ImageFile(const char *filename, EImageFileFormat format, const Image &image) +ImageFile::ImageFile(const char *filename, EImageFileFormat format, const FasTC::Image<> &image) : m_FileFormat(format) , m_Image(image.Clone()) { @@ -165,7 +165,7 @@ bool ImageFile::Write() { return true; } -Image *ImageFile::LoadImage(const unsigned char *rawImageData) const { +FasTC::Image<> *ImageFile::LoadImage(const unsigned char *rawImageData) const { ImageLoader *loader = NULL; switch(m_FileFormat) { @@ -207,7 +207,7 @@ Image *ImageFile::LoadImage(const unsigned char *rawImageData) const { } uint32 *pixels = reinterpret_cast(pixelData); - Image *i = new Image(loader->GetWidth(), loader->GetHeight(), pixels, true); + FasTC::Image<> *i = new FasTC::Image<>(loader->GetWidth(), loader->GetHeight(), pixels, true); // Cleanup delete loader; diff --git a/IO/src/ImageWriter.cpp b/IO/src/ImageWriter.cpp index a01ec50..17e3ac8 100644 --- a/IO/src/ImageWriter.cpp +++ b/IO/src/ImageWriter.cpp @@ -42,6 +42,7 @@ */ #include "ImageWriter.h" +#include "Pixel.h" uint32 ImageWriter::GetChannelForPixel(uint32 x, uint32 y, uint32 ch) { @@ -58,10 +59,8 @@ uint32 ImageWriter::GetChannelForPixel(uint32 x, uint32 y, uint32 ch) { const uint32 blockOffsetY = y % 4; const uint32 pixelOffset = blockOffsetY * 4 + blockOffsetX; - // There are 16 pixels per block and bytes per pixel... - uint32 dataOffset = blockIdx * 4 * 16; - dataOffset += 4 * pixelOffset; - dataOffset += ch; + // There are 16 pixels per block... + uint32 dataOffset = blockIdx * 16 + pixelOffset; - return m_PixelData[dataOffset]; + return m_Pixels[dataOffset].Component(ch); } diff --git a/IO/src/ImageWriterPNG.cpp b/IO/src/ImageWriterPNG.cpp index d9d1344..1013253 100644 --- a/IO/src/ImageWriterPNG.cpp +++ b/IO/src/ImageWriterPNG.cpp @@ -43,14 +43,15 @@ #include "ImageWriterPNG.h" -#include -#include -#include - -#include "Image.h" +#include +#include +#include #include +#include "Image.h" +#include "Pixel.h" + class PNGStreamWriter { public: static void WriteDataToStream( @@ -84,13 +85,13 @@ public: }; -ImageWriterPNG::ImageWriterPNG(Image &im) - : ImageWriter(im.GetWidth(), im.GetHeight(), im.RawData()) +ImageWriterPNG::ImageWriterPNG(FasTC::Image<> &im) + : ImageWriter(im.GetWidth(), im.GetHeight(), im.GetPixels()) , m_bBlockStreamOrder(im.GetBlockStreamOrder()) , m_StreamPosition(0) { - im.ComputeRGBA(); - m_PixelData = reinterpret_cast(im.GetRGBA()); + im.ComputePixels(); + m_Pixels = im.GetPixels(); } bool ImageWriterPNG::WriteImage() { @@ -136,8 +137,7 @@ bool ImageWriterPNG::WriteImage() { *row++ = GetChannelForPixel(x, y, ch); } } else { - reinterpret_cast(row)[x] = - reinterpret_cast(m_PixelData)[y * m_Width + x]; + reinterpret_cast(row)[x] = m_Pixels[y * m_Width + x].Pack(); } } } diff --git a/IO/src/ImageWriterPNG.h b/IO/src/ImageWriterPNG.h index 3b4c515..5bd03ca 100644 --- a/IO/src/ImageWriterPNG.h +++ b/IO/src/ImageWriterPNG.h @@ -45,12 +45,12 @@ #define _IMAGE_WRITER_PNG_H_ #include "ImageWriter.h" +#include "ImageFwd.h" // Forward Declare -class Image; class ImageWriterPNG : public ImageWriter { public: - ImageWriterPNG(Image &); + ImageWriterPNG(FasTC::Image<> &); virtual ~ImageWriterPNG() { } virtual bool WriteImage(); diff --git a/PVRTCEncoder/CMakeLists.txt b/PVRTCEncoder/CMakeLists.txt index 9c3ff6c..1f0df2d 100644 --- a/PVRTCEncoder/CMakeLists.txt +++ b/PVRTCEncoder/CMakeLists.txt @@ -57,14 +57,14 @@ INCLUDE_DIRECTORIES(${FasTC_SOURCE_DIR}/Base/include) SET( HEADERS include/PVRTCCompressor.h src/Block.h - src/Image.h + src/PVRTCImage.h ) SET( SOURCES src/Compressor.cpp src/Decompressor.cpp src/Block.cpp - src/Image.cpp + src/PVRTCImage.cpp ) ADD_LIBRARY( PVRTCEncoder diff --git a/PVRTCEncoder/src/Compressor.cpp b/PVRTCEncoder/src/Compressor.cpp index 7126d6f..1620a2c 100644 --- a/PVRTCEncoder/src/Compressor.cpp +++ b/PVRTCEncoder/src/Compressor.cpp @@ -58,7 +58,7 @@ #include #include "Pixel.h" -#include "Image.h" +#include "PVRTCImage.h" #include "Block.h" namespace PVRTCC { diff --git a/PVRTCEncoder/src/Decompressor.cpp b/PVRTCEncoder/src/Decompressor.cpp index 9b7bb5b..7fb66fc 100644 --- a/PVRTCEncoder/src/Decompressor.cpp +++ b/PVRTCEncoder/src/Decompressor.cpp @@ -57,7 +57,7 @@ #include "Pixel.h" #include "Block.h" -#include "Image.h" +#include "PVRTCImage.h" namespace PVRTCC { @@ -94,7 +94,7 @@ namespace PVRTCC { assert(imgA.GetWidth() == imgB.GetWidth()); assert(imgA.GetHeight() == imgB.GetHeight()); - Image debugModulation(h, w); + Image debugModulation(w, h); const uint8 debugModulationBitDepth[4] = { 8, 4, 4, 4 }; debugModulation.ChangeBitDepth(debugModulationBitDepth); @@ -113,7 +113,6 @@ namespace PVRTCC { const Pixel &pa = imgA(i, j); const Pixel &pb = imgB(i, j); - Pixel result; bool punchThrough = false; uint8 lerpVal = 0; if(b.GetModeBit()) { @@ -147,6 +146,7 @@ namespace PVRTCC { } } + Pixel result; for(uint32 c = 0; c < 4; c++) { uint16 va = static_cast(pa.Component(c)); uint16 vb = static_cast(pb.Component(c)); @@ -274,7 +274,7 @@ namespace PVRTCC { } if(bDebugImages) { - Image dbgMod(h, w); + Image dbgMod(w, h); for(uint32 i = 0; i < h*w; i++) { float fb = static_cast(modValues[i]); uint8 val = static_cast((fb / 8.0f) * 15.0f); @@ -324,8 +324,8 @@ namespace PVRTCC { assert(blocks.size() > 0); // Extract the endpoints into A and B images - Image imgA(blocksH, blocksW); - Image imgB(blocksH, blocksW); + Image imgA(blocksW, blocksH); + Image imgB(blocksW, blocksH); for(uint32 j = 0; j < blocksH; j++) { for(uint32 i = 0; i < blocksW; i++) { diff --git a/PVRTCEncoder/src/Image.cpp b/PVRTCEncoder/src/PVRTCImage.cpp similarity index 79% rename from PVRTCEncoder/src/Image.cpp rename to PVRTCEncoder/src/PVRTCImage.cpp index 9ac282f..d6beb2f 100644 --- a/PVRTCEncoder/src/Image.cpp +++ b/PVRTCEncoder/src/PVRTCImage.cpp @@ -55,7 +55,7 @@ # define snprintf _snprintf #endif -#include "Image.h" +#include "PVRTCImage.h" #include #include @@ -80,55 +80,39 @@ static float ConvertChannelToFloat(uint8 channel, uint8 bitDepth) { namespace PVRTCC { -Image::Image(uint32 height, uint32 width) - : m_Width(width) - , m_Height(height) - , m_Pixels(new Pixel[width * height]) - , m_FractionalPixels(new Pixel[width * height]) { +Image::Image(uint32 width, uint32 height) + : FasTC::Image(width, height) + , m_FractionalPixels(new FasTC::Pixel[width * height]) { assert(width > 0); assert(height > 0); } -Image::Image(uint32 height, uint32 width, const Pixel *pixels) - : m_Width(width) - , m_Height(height) - , m_Pixels(new Pixel[width * height]) - , m_FractionalPixels(new Pixel[width * height]) { +Image::Image(uint32 width, uint32 height, const FasTC::Pixel *pixels) + : FasTC::Image(width, height, pixels) + , m_FractionalPixels(new FasTC::Pixel[width * height]) { assert(width > 0); assert(height > 0); - memcpy(m_Pixels, pixels, width * height * sizeof(Pixel)); } Image::Image(const Image &other) - : m_Width(other.GetWidth()) - , m_Height(other.GetHeight()) - , m_Pixels(new Pixel[other.GetWidth() * other.GetHeight()]) - , m_FractionalPixels(new Pixel[other.GetWidth() * other.GetHeight()]) { - memcpy(m_Pixels, other.m_Pixels, GetWidth() * GetHeight() * sizeof(Pixel)); + : FasTC::Image(other) + , m_FractionalPixels(new FasTC::Pixel[other.GetWidth() * other.GetHeight()]) { + memcpy(m_FractionalPixels, other.m_FractionalPixels, GetWidth() * GetHeight() * sizeof(FasTC::Pixel)); } Image &Image::operator=(const Image &other) { - m_Width = other.GetWidth(); - m_Height = other.GetHeight(); - - assert(m_Pixels); - delete m_Pixels; - m_Pixels = new Pixel[other.GetWidth() * other.GetHeight()]; - memcpy(m_Pixels, other.m_Pixels, GetWidth() * GetHeight() * sizeof(Pixel)); + FasTC::Image::operator=(other); assert(m_FractionalPixels); delete m_FractionalPixels; - m_FractionalPixels = new Pixel[other.GetWidth() * other.GetHeight()]; + m_FractionalPixels = new FasTC::Pixel[other.GetWidth() * other.GetHeight()]; memcpy(m_FractionalPixels, other.m_FractionalPixels, - GetWidth() * GetHeight() * sizeof(Pixel)); + GetWidth() * GetHeight() * sizeof(FasTC::Pixel)); return *this; } Image::~Image() { - assert(m_Pixels); - delete [] m_Pixels; - assert(m_FractionalPixels); delete [] m_FractionalPixels; } @@ -155,18 +139,18 @@ void Image::BilinearUpscale(uint32 xtimes, uint32 ytimes, const uint32 yscale = 1 << ytimes; const uint32 yoffset = yscale >> 1; - Pixel *upscaledPixels = new Pixel[newWidth * newHeight]; + FasTC::Pixel *upscaledPixels = new FasTC::Pixel[newWidth * newHeight]; assert(m_FractionalPixels); delete m_FractionalPixels; - m_FractionalPixels = new Pixel[newWidth * newHeight]; + m_FractionalPixels = new FasTC::Pixel[newWidth * newHeight]; for(uint32 j = 0; j < newHeight; j++) { for(uint32 i = 0; i < newWidth; i++) { const uint32 pidx = j * newWidth + i; - Pixel &p = upscaledPixels[pidx]; - Pixel &fp = m_FractionalPixels[pidx]; + FasTC::Pixel &p = upscaledPixels[pidx]; + FasTC::Pixel &fp = m_FractionalPixels[pidx]; const int32 highXIdx = (i + xoffset) / xscale; const int32 lowXIdx = highXIdx - 1; @@ -183,10 +167,10 @@ void Image::BilinearUpscale(uint32 xtimes, uint32 ytimes, const uint32 bottomLeftWeight = lowXWeight * highYWeight; const uint32 bottomRightWeight = highXWeight * highYWeight; - const Pixel &topLeft = GetPixel(lowXIdx, lowYIdx, wrapMode); - const Pixel &topRight = GetPixel(highXIdx, lowYIdx, wrapMode); - const Pixel &bottomLeft = GetPixel(lowXIdx, highYIdx, wrapMode); - const Pixel &bottomRight = GetPixel(highXIdx, highYIdx, wrapMode); + const FasTC::Pixel &topLeft = GetPixel(lowXIdx, lowYIdx, wrapMode); + const FasTC::Pixel &topRight = GetPixel(highXIdx, lowYIdx, wrapMode); + const FasTC::Pixel &bottomLeft = GetPixel(lowXIdx, highYIdx, wrapMode); + const FasTC::Pixel &bottomRight = GetPixel(highXIdx, highYIdx, wrapMode); // Make sure the bit depth matches the original... uint8 bitDepth[4]; @@ -224,10 +208,7 @@ void Image::BilinearUpscale(uint32 xtimes, uint32 ytimes, } } - delete m_Pixels; - m_Pixels = upscaledPixels; - m_Width = newWidth; - m_Height = newHeight; + SetImageData(newWidth, newHeight, upscaledPixels); } void Image::ContentAwareDownscale(uint32 xtimes, uint32 ytimes, @@ -238,11 +219,11 @@ void Image::ContentAwareDownscale(uint32 xtimes, uint32 ytimes, const uint32 newWidth = w >> xtimes; const uint32 newHeight = h >> ytimes; - Pixel *downscaledPixels = new Pixel[newWidth * newHeight]; + FasTC::Pixel *downscaledPixels = new FasTC::Pixel[newWidth * newHeight]; const uint32 numDownscaledPixels = newWidth * newHeight; uint8 bitDepth[4]; - m_Pixels[0].GetBitDepth(bitDepth); + GetPixels()[0].GetBitDepth(bitDepth); for(uint32 i = 0; i < numDownscaledPixels; i++) { downscaledPixels[i].ChangeBitDepth(bitDepth); @@ -282,10 +263,10 @@ void Image::ContentAwareDownscale(uint32 xtimes, uint32 ytimes, for(uint32 i = 0; i < w * h; i++) { // First convert the pixel values to floats using // premultiplied alpha... - float a = ConvertChannelToFloat(m_Pixels[i].A(), bitDepth[0]); - float r = a * ConvertChannelToFloat(m_Pixels[i].R(), bitDepth[1]); - float g = a * ConvertChannelToFloat(m_Pixels[i].G(), bitDepth[2]); - float b = a * ConvertChannelToFloat(m_Pixels[i].B(), bitDepth[3]); + float a = ConvertChannelToFloat(GetPixels()[i].A(), bitDepth[0]); + float r = a * ConvertChannelToFloat(GetPixels()[i].R(), bitDepth[1]); + float g = a * ConvertChannelToFloat(GetPixels()[i].G(), bitDepth[2]); + float b = a * ConvertChannelToFloat(GetPixels()[i].B(), bitDepth[3]); I[i] = r * 0.21f + g * 0.71f + b * 0.07f; } @@ -308,7 +289,7 @@ void Image::ContentAwareDownscale(uint32 xtimes, uint32 ytimes, Iy[idx] = (I[yphidx] - I[ymhidx]) / 2.0f; for(uint32 c = 0; c <= 3; c++) { - #define CPNT(dx) ConvertChannelToFloat(m_Pixels[dx].Component(c), bitDepth[c]) + #define CPNT(dx) ConvertChannelToFloat(GetPixels()[dx].Component(c), bitDepth[c]) Ix[c][idx] = (CPNT(xphidx) - CPNT(xmhidx)) / 2.0f; Ixx[c][idx] = (CPNT(xphidx) - 2.0f*CPNT(idx) + CPNT(xmhidx)) / 2.0f; Iyy[c][idx] = (CPNT(yphidx) - 2.0f*CPNT(idx) + CPNT(ymhidx)) / 2.0f; @@ -339,9 +320,9 @@ void Image::ContentAwareDownscale(uint32 xtimes, uint32 ytimes, } uint32 idx = GetPixelIndex(x, y); - Pixel current = m_Pixels[idx]; + FasTC::Pixel current = GetPixels()[idx]; - Pixel result; + FasTC::Pixel result; result.ChangeBitDepth(bitDepth); float Ixsq = Ix[4][idx] * Ix[4][idx]; @@ -364,26 +345,21 @@ void Image::ContentAwareDownscale(uint32 xtimes, uint32 ytimes, } } - delete m_Pixels; - m_Pixels = downscaledPixels; - m_Width = newWidth; - m_Height = newHeight; - + SetImageData(newWidth, newHeight, downscaledPixels); delete [] imgData; } void Image::ChangeBitDepth(const uint8 (&depths)[4]) { for(uint32 j = 0; j < GetHeight(); j++) { for(uint32 i = 0; i < GetWidth(); i++) { - uint32 pidx = j * GetWidth() + i; - m_Pixels[pidx].ChangeBitDepth(depths); + (*this)(i, j).ChangeBitDepth(depths); } } } void Image::ExpandTo8888() { uint8 currentDepth[4]; - m_Pixels[0].GetBitDepth(currentDepth); + GetPixels()[0].GetBitDepth(currentDepth); uint8 fractionDepth[4]; const uint8 fullDepth[4] = { 8, 8, 8, 8 }; @@ -391,8 +367,10 @@ void Image::ExpandTo8888() { for(uint32 j = 0; j < GetHeight(); j++) { for(uint32 i = 0; i < GetWidth(); i++) { + FasTC::Pixel &p = (*this)(i, j); + p.ChangeBitDepth(fullDepth); + uint32 pidx = j * GetWidth() + i; - m_Pixels[pidx].ChangeBitDepth(fullDepth); m_FractionalPixels[pidx].GetBitDepth(fractionDepth); for(uint32 c = 0; c < 4; c++) { @@ -402,17 +380,17 @@ void Image::ExpandTo8888() { uint32 shift = fractionDepth[c] - (fullDepth[c] - currentDepth[c]); uint32 fractionBits = m_FractionalPixels[pidx].Component(c) >> shift; - uint32 component = m_Pixels[pidx].Component(c); + uint32 component = p.Component(c); component += ((fractionBits * numerator) / denominator); - m_Pixels[pidx].Component(c) = component; + p.Component(c) = component; } } } } -const Pixel &Image::GetPixel(int32 i, int32 j, EWrapMode wrapMode) const { - return m_Pixels[GetPixelIndex(i, j, wrapMode)]; +const FasTC::Pixel &Image::GetPixel(int32 i, int32 j, EWrapMode wrapMode) const { + return GetPixels()[GetPixelIndex(i, j, wrapMode)]; } const uint32 Image::GetPixelIndex(int32 i, int32 j, EWrapMode wrapMode) const { @@ -454,33 +432,20 @@ const uint32 Image::GetPixelIndex(int32 i, int32 j, EWrapMode wrapMode) const { return idx; } -Pixel & Image::operator()(uint32 i, uint32 j) { - assert(i < GetWidth()); - assert(j < GetHeight()); - return m_Pixels[j * GetWidth() + i]; -} - -const Pixel & Image::operator()(uint32 i, uint32 j) const { - assert(i < GetWidth()); - assert(j < GetHeight()); - return m_Pixels[j * GetWidth() + i]; -} - void Image::DebugOutput(const char *filename) const { uint32 *outPixels = new uint32[GetWidth() * GetHeight()]; const uint8 fullDepth[4] = { 8, 8, 8, 8 }; for(uint32 j = 0; j < GetHeight(); j++) { for(uint32 i = 0; i < GetWidth(); i++) { - uint32 idx = j * GetWidth() + i; - Pixel p = m_Pixels[idx]; + FasTC::Pixel p = (*this)(i, j); p.ChangeBitDepth(fullDepth); p.A() = 255; - outPixels[idx] = p.Pack(); + outPixels[j*GetWidth() + i] = p.Pack(); } } - ::Image img(GetWidth(), GetHeight(), outPixels); + FasTC::Image<> img(GetWidth(), GetHeight(), outPixels); char debugFilename[256]; snprintf(debugFilename, sizeof(debugFilename), "%s.png", filename); diff --git a/PVRTCEncoder/src/Image.h b/PVRTCEncoder/src/PVRTCImage.h similarity index 87% rename from PVRTCEncoder/src/Image.h rename to PVRTCEncoder/src/PVRTCImage.h index 33616d3..69bfe73 100644 --- a/PVRTCEncoder/src/Image.h +++ b/PVRTCEncoder/src/PVRTCImage.h @@ -55,6 +55,7 @@ #include "TexCompTypes.h" #include "PVRTCCompressor.h" +#include "Image.h" // Forward include namespace FasTC { @@ -63,15 +64,13 @@ namespace FasTC { namespace PVRTCC { -using FasTC::Pixel; -class Image { +class Image : public FasTC::Image { public: - Image(uint32 height, uint32 width); - Image(uint32 height, uint32 width, const Pixel *pixels); + Image(uint32 width, uint32 height); + Image(uint32 width, uint32 height, const FasTC::Pixel *pixels); Image(const Image &); Image &operator=(const Image &); - ~Image(); - + virtual ~Image(); void BilinearUpscale(uint32 xtimes, uint32 ytimes, EWrapMode wrapMode = eWrapMode_Wrap); @@ -84,25 +83,15 @@ class Image { EWrapMode wrapMode = eWrapMode_Wrap, bool bOffsetNewPixels = false); - void ChangeBitDepth(const uint8 (&depths)[4]); void ExpandTo8888(); - - Pixel &operator()(uint32 i, uint32 j); - const Pixel &operator()(uint32 i, uint32 j) const; - - uint32 GetWidth() const { return m_Width; } - uint32 GetHeight() const { return m_Height; } - + void ChangeBitDepth(const uint8 (&depths)[4]); void DebugOutput(const char *filename) const; private: - uint32 m_Width; - uint32 m_Height; - Pixel *m_Pixels; - Pixel *m_FractionalPixels; + FasTC::Pixel *m_FractionalPixels; const uint32 GetPixelIndex(int32 i, int32 j, EWrapMode wrapMode = eWrapMode_Clamp) const; - const Pixel &GetPixel(int32 i, int32 j, EWrapMode wrapMode = eWrapMode_Clamp) const; + const FasTC::Pixel &GetPixel(int32 i, int32 j, EWrapMode wrapMode = eWrapMode_Clamp) const; }; } // namespace PVRTCC diff --git a/PVRTCEncoder/test/CMakeLists.txt b/PVRTCEncoder/test/CMakeLists.txt index 3f2e1ec..75889ed 100644 --- a/PVRTCEncoder/test/CMakeLists.txt +++ b/PVRTCEncoder/test/CMakeLists.txt @@ -60,7 +60,7 @@ SET(TESTS ) FOREACH(TEST ${TESTS}) - SET(TEST_NAME Test${TEST}) + SET(TEST_NAME Test_PVRTCEncoder_${TEST}) SET(TEST_MODULE ${TEST}Test.cpp) ADD_EXECUTABLE(${TEST_NAME} ${TEST_MODULE}) TARGET_LINK_LIBRARIES(${TEST_NAME} PVRTCEncoder) @@ -71,7 +71,7 @@ ENDFOREACH() # Test the decompressor against the included PVR Texture library.... IF(PVRTEXLIB_FOUND) - SET(TEST_NAME TestDecompVersusPVRLib) + SET(TEST_NAME Test_PVRTCEncoder_DecompVersusPVRLib) # Copy the .pvr files that we will use for testing... SET(TEST_IMAGES diff --git a/PVRTCEncoder/test/ImageTest.cpp b/PVRTCEncoder/test/ImageTest.cpp index ead8ba4..0022418 100644 --- a/PVRTCEncoder/test/ImageTest.cpp +++ b/PVRTCEncoder/test/ImageTest.cpp @@ -51,78 +51,14 @@ */ #include "gtest/gtest.h" -#include "Image.h" +#include "PVRTCImage.h" #include "Pixel.h" #include "TestUtils.h" #include -TEST(Image, NonSpecificConstructor) { - PVRTCC::Pixel p; - - PVRTCC::Image img (4, 4); - for(uint32 i = 0; i < 4; i++) { - for(uint32 j = 0; j < 4; j++) { - EXPECT_TRUE(img(i, j) == p); - } - } -} - -TEST(Image, SpecificConstructor) { - PVRTCC::Pixel pxs[16]; - for(uint32 i = 0; i < 4; i++) { - for(uint32 j = 0; j < 4; j++) { - pxs[j*4 + i].R() = i; - pxs[j*4 + i].G() = j; - } - } - - PVRTCC::Image img(4, 4, pxs); - for(uint32 i = 0; i < 4; i++) { - for(uint32 j = 0; j < 4; j++) { - EXPECT_TRUE(img(i, j) == pxs[j*4 + i]); - } - } -} - -TEST(Image, CopyConstructor) { - PVRTCC::Pixel pxs[16]; - for(uint32 i = 0; i < 4; i++) { - for(uint32 j = 0; j < 4; j++) { - pxs[j*4 + i].R() = i; - pxs[j*4 + i].G() = j; - } - } - - PVRTCC::Image img(4, 4, pxs); - PVRTCC::Image img2(img); - for(uint32 i = 0; i < 4; i++) { - for(uint32 j = 0; j < 4; j++) { - EXPECT_TRUE(img2(i, j) == pxs[j*4 + i]); - } - } -} - -TEST(Image, AssignmentOperator) { - PVRTCC::Pixel pxs[16]; - for(uint32 i = 0; i < 4; i++) { - for(uint32 j = 0; j < 4; j++) { - pxs[j*4 + i].R() = i; - pxs[j*4 + i].G() = j; - } - } - - PVRTCC::Image img(4, 4, pxs); - PVRTCC::Image img2 = img; - for(uint32 i = 0; i < 4; i++) { - for(uint32 j = 0; j < 4; j++) { - EXPECT_TRUE(img2(i, j) == pxs[j*4 + i]); - } - } -} - TEST(Image, BilinearUpscale) { - PVRTCC::Pixel pxs[16]; + FasTC::Pixel pxs[16]; for(uint32 i = 0; i < 4; i++) { for(uint32 j = 0; j < 4; j++) { pxs[j*4 + i].R() = i*2; @@ -152,7 +88,6 @@ TEST(Image, BilinearUpscale) { } } - TEST(Image, BilinearUpscaleMaintainsPixels) { srand(0xabd1ca7e); @@ -160,7 +95,7 @@ TEST(Image, BilinearUpscaleMaintainsPixels) { const uint32 w = 4; const uint32 h = 4; - PVRTCC::Pixel pxs[16]; + FasTC::Pixel pxs[16]; for(uint32 i = 0; i < w; i++) { for(uint32 j = 0; j < h; j++) { pxs[j*w + i].R() = rand() % 256; @@ -177,7 +112,7 @@ TEST(Image, BilinearUpscaleMaintainsPixels) { for(uint32 i = 2; i < img.GetWidth(); i+=4) { for(uint32 j = 2; j < img.GetHeight(); j+=4) { - PVRTCC::Pixel p = img(i, j); + FasTC::Pixel p = img(i, j); uint32 idx = ((j - 2) / 4) * w + ((i-2)/4); EXPECT_EQ(PixelPrinter(p.Pack()), PixelPrinter(pxs[idx].Pack())); } @@ -190,7 +125,7 @@ TEST(Image, NonuniformBilinearUpscale) { const uint32 kWidth = 4; const uint32 kHeight = 8; - PVRTCC::Pixel pxs[kWidth * kHeight]; + FasTC::Pixel pxs[kWidth * kHeight]; for(uint32 i = 0; i < kWidth; i++) { for(uint32 j = 0; j < kHeight; j++) { pxs[j*kWidth + i].R() = i*4; @@ -198,7 +133,7 @@ TEST(Image, NonuniformBilinearUpscale) { } } - PVRTCC::Image img(kHeight, kWidth, pxs); + PVRTCC::Image img(kWidth, kHeight, pxs); img.BilinearUpscale(2, 1, PVRTCC::eWrapMode_Clamp); EXPECT_EQ(img.GetWidth(), static_cast(kWidth << 2)); EXPECT_EQ(img.GetHeight(), static_cast(kHeight << 1)); @@ -223,7 +158,7 @@ TEST(Image, NonuniformBilinearUpscale) { } TEST(Image, BilinearUpscaleWrapped) { - PVRTCC::Pixel pxs[16]; + FasTC::Pixel pxs[16]; // Make sure that our bit depth is less than full... for(uint32 i = 0; i < 16; i++) { @@ -245,7 +180,7 @@ TEST(Image, BilinearUpscaleWrapped) { for(uint32 i = 0; i < img.GetWidth(); i++) { for(uint32 j = 0; j < img.GetHeight(); j++) { - const PVRTCC::Pixel &p = img(i, j); + const FasTC::Pixel &p = img(i, j); // First make sure that the bit depth didn't change uint8 depth[4]; @@ -284,9 +219,9 @@ TEST(Image, ContentAwareDownscale) { for(uint32 j = 0; j < img.GetHeight(); j++) { for(uint32 i = 0; i < img.GetWidth(); i++) { if(j < 4) { - img(i, j) = PVRTCC::Pixel( 0xFF000000 ); + img(i, j) = FasTC::Pixel( 0xFF000000 ); } else { - img(i, j) = PVRTCC::Pixel( 0xFF0000FF ); + img(i, j) = FasTC::Pixel( 0xFF0000FF ); } } }