2 * Copyright (C) 2005-2013 Team XBMC
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, see
17 * <http://www.gnu.org/licenses/>.
23 #include "libsquish/squish.h"
24 #include "utils/log.h"
27 #ifndef NO_XBMC_FILESYSTEM
28 #include "filesystem/File.h"
29 using namespace XFILE;
36 CDDSImage::CDDSImage()
39 memset(&m_desc, 0, sizeof(m_desc));
42 CDDSImage::CDDSImage(unsigned int width, unsigned int height, unsigned int format)
45 Allocate(width, height, format);
48 CDDSImage::~CDDSImage()
53 unsigned int CDDSImage::GetWidth() const
58 unsigned int CDDSImage::GetHeight() const
63 unsigned int CDDSImage::GetFormat() const
65 if (m_desc.pixelFormat.flags & DDPF_RGB)
66 return 0; // Not supported
67 if (m_desc.pixelFormat.flags & DDPF_FOURCC)
69 if (strncmp((const char *)&m_desc.pixelFormat.fourcc, "DXT1", 4) == 0)
71 if (strncmp((const char *)&m_desc.pixelFormat.fourcc, "DXT3", 4) == 0)
73 if (strncmp((const char *)&m_desc.pixelFormat.fourcc, "DXT5", 4) == 0)
75 if (strncmp((const char *)&m_desc.pixelFormat.fourcc, "ARGB", 4) == 0)
76 return XB_FMT_A8R8G8B8;
81 unsigned int CDDSImage::GetSize() const
83 return m_desc.linearSize;
86 unsigned char *CDDSImage::GetData() const
91 bool CDDSImage::ReadFile(const std::string &inputFile)
95 if (!file.Open(inputFile))
100 if (file.Read(&magic, 4) != 4)
102 if (file.Read(&m_desc, sizeof(m_desc)) != sizeof(m_desc))
105 return false; // not supported
108 m_data = new unsigned char[m_desc.linearSize];
113 if (file.Read(m_data, m_desc.linearSize) != m_desc.linearSize)
120 bool CDDSImage::Create(const std::string &outputFile, unsigned int width, unsigned int height, unsigned int pitch, unsigned char const *brga, double maxMSE)
124 if (!Compress(width, height, pitch, brga, maxMSE))
126 Allocate(width, height, XB_FMT_A8R8G8B8);
127 for (unsigned int i = 0; i < height; i++)
128 memcpy(m_data + i * width * 4, brga + i * pitch, min(width * 4, pitch));
130 return WriteFile(outputFile);
133 bool CDDSImage::WriteFile(const std::string &outputFile) const
137 if (!file.OpenForWrite(outputFile, true))
141 file.Write("DDS ", 4);
142 file.Write(&m_desc, sizeof(m_desc));
144 file.Write(m_data, m_desc.linearSize);
149 unsigned int CDDSImage::GetStorageRequirements(unsigned int width, unsigned int height, unsigned int format)
154 return ((width + 3) / 4) * ((height + 3) / 4) * 8;
157 return ((width + 3) / 4) * ((height + 3) / 4) * 16;
158 case XB_FMT_A8R8G8B8:
160 return width * height * 4;
164 bool CDDSImage::Compress(unsigned int width, unsigned int height, unsigned int pitch, unsigned char const *brga, double maxMSE)
166 // first try DXT1, which is only 4bits/pixel
167 Allocate(width, height, XB_FMT_DXT1);
169 squish::CompressImage(brga, width, height, pitch, m_data, squish::kDxt1 | squish::kSourceBGRA);
170 const char *fourCC = NULL;
172 double colorMSE, alphaMSE;
173 squish::ComputeMSE(brga, width, height, pitch, m_data, squish::kDxt1 | squish::kSourceBGRA, colorMSE, alphaMSE);
174 if (!maxMSE || (colorMSE < maxMSE && alphaMSE < maxMSE))
179 { // no alpha channel, so DXT5YCoCg is going to be the best DXT5 format
180 /* squish::CompressImage(brga, width, height, pitch, data2, squish::kDxt5 | squish::kSourceBGRA);
181 squish::ComputeMSE(brga, width, height, pitch, m_data, squish::kDxt5 | squish::kSourceBGRA, colorMSE, alphaMSE);
182 if (colorMSE < maxMSE && alphaMSE < maxMSE)
183 { // success - use it
184 compressedSize = squish::GetStorageRequirements(width, height, squish::kDxt5);
185 format = XB_FMT_DXT5_YCoCg;
190 { // try DXT3 and DXT5 - use whichever is better (color is the same as DXT1, but alpha will be different)
191 Allocate(width, height, XB_FMT_DXT3);
192 squish::CompressImage(brga, width, height, pitch, m_data, squish::kDxt3 | squish::kSourceBGRA);
193 squish::ComputeMSE(brga, width, height, pitch, m_data, squish::kDxt3 | squish::kSourceBGRA, colorMSE, alphaMSE);
194 if (colorMSE < maxMSE)
195 { // color is fine, test DXT5 as well
197 unsigned char *data2 = new unsigned char[GetStorageRequirements(width, height, XB_FMT_DXT5)];
198 squish::CompressImage(brga, width, height, pitch, data2, squish::kDxt5 | squish::kSourceBGRA);
199 squish::ComputeMSE(brga, width, height, pitch, data2, squish::kDxt5 | squish::kSourceBGRA, colorMSE, dxt5MSE);
200 if (alphaMSE < maxMSE && alphaMSE < dxt5MSE)
202 else if (dxt5MSE < maxMSE)
205 std::swap(m_data, data2);
214 memcpy(&m_desc.pixelFormat.fourcc, fourCC, 4);
215 CLog::Log(LOGDEBUG, "%s - using %s (min error is: %2.2f:%2.2f)", __FUNCTION__, fourCC, colorMSE, alphaMSE);
218 CLog::Log(LOGDEBUG, "%s - no format suitable (min error is: %2.2f:%2.2f)", __FUNCTION__, colorMSE, alphaMSE);
222 bool CDDSImage::Decompress(unsigned char *argb, unsigned int width, unsigned int height, unsigned int pitch, unsigned char const *dxt, unsigned int format)
224 if (!argb || !dxt || !(format & XB_FMT_DXT_MASK))
227 if (format == XB_FMT_DXT1)
228 squish::DecompressImage(argb, width, height, pitch, dxt, squish::kDxt1 | squish::kSourceBGRA);
229 else if (format == XB_FMT_DXT3)
230 squish::DecompressImage(argb, width, height, pitch, dxt, squish::kDxt3 | squish::kSourceBGRA);
231 else if (format == XB_FMT_DXT5)
232 squish::DecompressImage(argb, width, height, pitch, dxt, squish::kDxt5 | squish::kSourceBGRA);
237 void CDDSImage::Allocate(unsigned int width, unsigned int height, unsigned int format)
239 memset(&m_desc, 0, sizeof(m_desc));
240 m_desc.size = sizeof(m_desc);
241 m_desc.flags = ddsd_caps | ddsd_pixelformat | ddsd_width | ddsd_height | ddsd_linearsize;
242 m_desc.height = height;
243 m_desc.width = width;
244 m_desc.linearSize = GetStorageRequirements(width, height, format);
245 m_desc.pixelFormat.size = sizeof(m_desc.pixelFormat);
246 m_desc.pixelFormat.flags = ddpf_fourcc;
247 memcpy(&m_desc.pixelFormat.fourcc, GetFourCC(format), 4);
248 m_desc.caps.flags1 = ddscaps_texture;
250 m_data = new unsigned char[m_desc.linearSize];
253 const char *CDDSImage::GetFourCC(unsigned int format)
263 case XB_FMT_A8R8G8B8: