aboutsummaryrefslogtreecommitdiff
path: root/guilib
diff options
context:
space:
mode:
authorjmarshallnz <jmarshallnz@svn>2010-03-21 06:17:33 +0000
committerjmarshallnz <jmarshallnz@svn>2010-03-21 06:17:33 +0000
commit5f9deb751809c3888295370306eb10dc10736f19 (patch)
tree51a3d14c5c74e52b0c7f09fe318ff45e861e359e /guilib
parent4e41f473b58247758438bac91af8e08f3d3007ed (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.cpp350
-rw-r--r--guilib/DDSImage.h13
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,