diff options
author | jmarshallnz <jmarshallnz@svn> | 2010-03-21 06:17:33 +0000 |
---|---|---|
committer | jmarshallnz <jmarshallnz@svn> | 2010-03-21 06:17:33 +0000 |
commit | 5f9deb751809c3888295370306eb10dc10736f19 (patch) | |
tree | 51a3d14c5c74e52b0c7f09fe318ff45e861e359e /guilib | |
parent | 4e41f473b58247758438bac91af8e08f3d3007ed (diff) |
added: support for DDS compression of textures
git-svn-id: https://xbmc.svn.sourceforge.net/svnroot/xbmc/trunk@28708 568bbfeb-2a22-0410-94d2-cc84cf5bfa90
Diffstat (limited to 'guilib')
-rw-r--r-- | guilib/DDSImage.cpp | 350 | ||||
-rw-r--r-- | guilib/DDSImage.h | 13 |
2 files changed, 226 insertions, 137 deletions
diff --git a/guilib/DDSImage.cpp b/guilib/DDSImage.cpp index f260606b26..bbd440a9a4 100644 --- a/guilib/DDSImage.cpp +++ b/guilib/DDSImage.cpp @@ -1,137 +1,213 @@ -/*
- * Copyright (C) 2005-2009 Team XBMC
- * http://www.xbmc.org
- *
- * This Program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This Program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with XBMC; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- */
-
-#include "DDSImage.h"
-#include "XBTF.h"
-#include <string.h>
-
-#ifndef NO_XBMC_FILESYSTEM
-#include "FileSystem/File.h"
-using namespace XFILE;
-#else
-#include "SimpleFS.h"
-#endif
-
-CDDSImage::CDDSImage()
-{
- m_data = NULL;
- memset(&m_desc, 0, sizeof(m_desc));
-}
-
-CDDSImage::CDDSImage(unsigned int width, unsigned int height, unsigned int format)
-{
- memset(&m_desc, 0, sizeof(m_desc));
- m_desc.size = sizeof(m_desc);
- m_desc.flags = ddsd_caps | ddsd_pixelformat | ddsd_width | ddsd_height | ddsd_linearsize;
- m_desc.height = height;
- m_desc.width = width;
- m_desc.linearSize = GetStorageRequirements(width, height, format);
- m_desc.pixelFormat.size = sizeof(m_desc.pixelFormat);
- m_desc.pixelFormat.flags = ddpf_fourcc;
- memcpy(&m_desc.pixelFormat.fourcc, (format == XB_FMT_DXT1) ? "DXT1" : "DXT5", 4);
- m_desc.caps.flags1 = ddscaps_texture;
- m_data = new unsigned char[m_desc.linearSize];
-}
-
-CDDSImage::~CDDSImage()
-{
- delete[] m_data;
-}
-
-unsigned int CDDSImage::GetWidth() const
-{
- return m_desc.width;
-}
-
-unsigned int CDDSImage::GetHeight() const
-{
- return m_desc.height;
-}
-
-unsigned int CDDSImage::GetFormat() const
-{
- if (strncmp((const char *)&m_desc.pixelFormat.fourcc, "DXT1", 4) == 0)
- return XB_FMT_DXT1;
- if (strncmp((const char *)&m_desc.pixelFormat.fourcc, "DXT5", 4) == 0)
- return XB_FMT_DXT5;
- return 0;
-}
-
-unsigned int CDDSImage::GetSize() const
-{
- return m_desc.linearSize;
-}
-
-unsigned char *CDDSImage::GetData() const
-{
- return m_data;
-}
-
-bool CDDSImage::ReadFile(const std::string &inputFile)
-{
- // open the file
- CFile file;
- if (!file.Open(inputFile))
- return false;
-
- // read the header
- uint32_t magic;
- if (file.Read(&magic, 4) != 4)
- return false;
- if (file.Read(&m_desc, sizeof(m_desc)) != sizeof(m_desc))
- return false;
- if (!GetFormat())
- return false; // not supported
-
- // allocate our data
- m_data = new unsigned char[m_desc.linearSize];
- if (!m_data)
- return false;
-
- // and read it in
- if (file.Read(m_data, m_desc.linearSize) != m_desc.linearSize)
- return false;
-
- file.Close();
- return true;
-}
-
-bool CDDSImage::WriteFile(const std::string &outputFile) const
-{
- // open the file
- CFile file;
- if (!file.OpenForWrite(outputFile, true))
- return false;
-
- // write the header
- file.Write("DDS ", 4);
- file.Write(&m_desc, sizeof(m_desc));
- // now the data
- file.Write(m_data, m_desc.linearSize);
- file.Close();
- return true;
-}
-
-unsigned int CDDSImage::GetStorageRequirements(unsigned int width, unsigned int height, unsigned int format) const
-{
- unsigned int blockSize = (format == XB_FMT_DXT1) ? 8 : 16;
- return ((width + 3) / 4) * ((height + 3) / 4) * blockSize;
-}
+/* + * Copyright (C) 2005-2009 Team XBMC + * http://www.xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "DDSImage.h" +#include "XBTF.h" +#include "lib/libsquish/squish.h" +#include <string.h> + +#ifndef NO_XBMC_FILESYSTEM +#include "FileSystem/File.h" +using namespace XFILE; +#else +#include "SimpleFS.h" +#endif + +CDDSImage::CDDSImage() +{ + m_data = NULL; + memset(&m_desc, 0, sizeof(m_desc)); +} + +CDDSImage::CDDSImage(unsigned int width, unsigned int height, unsigned int format) +{ + m_data = NULL; + Allocate(width, height, format); +} + +CDDSImage::~CDDSImage() +{ + delete[] m_data; +} + +unsigned int CDDSImage::GetWidth() const +{ + return m_desc.width; +} + +unsigned int CDDSImage::GetHeight() const +{ + return m_desc.height; +} + +unsigned int CDDSImage::GetFormat() const +{ + if (strncmp((const char *)&m_desc.pixelFormat.fourcc, "DXT1", 4) == 0) + return XB_FMT_DXT1; + if (strncmp((const char *)&m_desc.pixelFormat.fourcc, "DXT5", 4) == 0) + return XB_FMT_DXT5; + return 0; +} + +unsigned int CDDSImage::GetSize() const +{ + return m_desc.linearSize; +} + +unsigned char *CDDSImage::GetData() const +{ + return m_data; +} + +bool CDDSImage::ReadFile(const std::string &inputFile) +{ + // open the file + CFile file; + if (!file.Open(inputFile)) + return false; + + // read the header + uint32_t magic; + if (file.Read(&magic, 4) != 4) + return false; + if (file.Read(&m_desc, sizeof(m_desc)) != sizeof(m_desc)) + return false; + if (!GetFormat()) + return false; // not supported + + // allocate our data + m_data = new unsigned char[m_desc.linearSize]; + if (!m_data) + return false; + + // and read it in + if (file.Read(m_data, m_desc.linearSize) != m_desc.linearSize) + return false; + + file.Close(); + return true; +} + +bool CDDSImage::WriteFile(const std::string &outputFile) const +{ + // open the file + CFile file; + if (!file.OpenForWrite(outputFile, true)) + return false; + + // write the header + file.Write("DDS ", 4); + file.Write(&m_desc, sizeof(m_desc)); + // now the data + file.Write(m_data, m_desc.linearSize); + file.Close(); + return true; +} + +unsigned int CDDSImage::GetStorageRequirements(unsigned int width, unsigned int height, unsigned int format) const +{ + unsigned int blockSize = (format == XB_FMT_DXT1) ? 8 : 16; + return ((width + 3) / 4) * ((height + 3) / 4) * blockSize; +} + +bool CDDSImage::Compress(unsigned int width, unsigned int height, unsigned int pitch, unsigned char const *brga, double maxMSE) +{ + delete[] m_data; + m_data = new unsigned char[GetStorageRequirements(width, height, XB_FMT_DXT1)]; + + // first try DXT1, which is only 4bits/pixel + Allocate(width, height, XB_FMT_DXT1); + + squish::CompressImage(brga, width, height, pitch, m_data, squish::kDxt1 | squish::kSourceBGRA); + if (maxMSE) + { // want to bother with other formats + double colorMSE, alphaMSE; + squish::ComputeMSE(brga, width, height, pitch, m_data, squish::kDxt1 | squish::kSourceBGRA, colorMSE, alphaMSE); + if (colorMSE < maxMSE && alphaMSE < maxMSE) + return true; + + Allocate(width, height, XB_FMT_DXT3); + + if (alphaMSE == 0) + { // no alpha channel, so DXT5YCoCg is going to be the best DXT5 format + /* squish::CompressImage(brga, width, height, pitch, data2, squish::kDxt5 | squish::kSourceBGRA); + squish::ComputeMSE(brga, width, height, pitch, m_data, squish::kDxt5 | squish::kSourceBGRA, colorMSE, alphaMSE); + if (colorMSE < maxMSE && alphaMSE < maxMSE) + { // success - use it + compressedSize = squish::GetStorageRequirements(width, height, squish::kDxt5); + format = XB_FMT_DXT5_YCoCg; + } + */ + } + if (alphaMSE > 0) + { // try DXT3 and DXT5 - use whichever is better (color is the same, but alpha will be different) + squish::CompressImage(brga, width, height, pitch, m_data, squish::kDxt3 | squish::kSourceBGRA); + squish::ComputeMSE(brga, width, height, pitch, m_data, squish::kDxt3 | squish::kSourceBGRA, colorMSE, alphaMSE); + if (colorMSE < maxMSE) + { // color is fine, test DXT5 as well + double dxt5MSE; + unsigned char *data2 = new unsigned char[GetStorageRequirements(width, height, XB_FMT_DXT5)]; + squish::CompressImage(brga, width, height, pitch, data2, squish::kDxt5 | squish::kSourceBGRA); + squish::ComputeMSE(brga, width, height, pitch, data2, squish::kDxt5 | squish::kSourceBGRA, colorMSE, alphaMSE); + if (alphaMSE < maxMSE && alphaMSE < dxt5MSE) + { // DXT3 passes and is best + return true; + } + else if (dxt5MSE < maxMSE) + { // DXT5 passes + memcpy(&m_desc.pixelFormat.fourcc, "DXT5", 4); + delete[] m_data; + m_data = data2; + return true; + } + delete[] data2; + } + } + return false; + } + return true; +} + +void CDDSImage::Allocate(unsigned int width, unsigned int height, unsigned int format) +{ + memset(&m_desc, 0, sizeof(m_desc)); + m_desc.size = sizeof(m_desc); + m_desc.flags = ddsd_caps | ddsd_pixelformat | ddsd_width | ddsd_height | ddsd_linearsize; + m_desc.height = height; + m_desc.width = width; + m_desc.linearSize = GetStorageRequirements(width, height, format); + m_desc.pixelFormat.size = sizeof(m_desc.pixelFormat); + m_desc.pixelFormat.flags = ddpf_fourcc; + memcpy(&m_desc.pixelFormat.fourcc, GetFourCC(format), 4); + m_desc.caps.flags1 = ddscaps_texture; + delete[] m_data; + m_data = new unsigned char[m_desc.linearSize]; +} + +const char *CDDSImage::GetFourCC(unsigned int format) const +{ + if (format == XB_FMT_DXT1) + return "DXT1"; + else if (format == XB_FMT_DXT3) + return "DXT3"; + else + return "DXT5"; +} diff --git a/guilib/DDSImage.h b/guilib/DDSImage.h index ebaaa732d2..54bdc346a7 100644 --- a/guilib/DDSImage.h +++ b/guilib/DDSImage.h @@ -40,7 +40,20 @@ public: bool ReadFile(const std::string &file);
bool WriteFile(const std::string &file) const;
+ /*! \brief Compress an ARGB buffer into a DXT1/3/5 image
+ \param width width of the pixel buffer
+ \param height height of the pixel buffer
+ \param pitch pitch of the pixel buffer
+ \param argb pixel buffer
+ \param maxMSE maximum mean square error to allow, ignored if 0 (the default)
+ \return true on successful compression within the given maxMSE, false otherwise
+ */
+ bool Compress(unsigned int width, unsigned int height, unsigned int pitch, unsigned char const *argb, double maxMSE = 0);
+
private:
+ void Allocate(unsigned int width, unsigned int height, unsigned int format);
+ const char *GetFourCC(unsigned int format) const;
+
unsigned int GetStorageRequirements(unsigned int width, unsigned int height, unsigned int format) const;
enum {
ddsd_caps = 0x00000001,
|