Fix keymap.
[vuplus_xbmc] / xbmc / guilib / DDSImage.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://xbmc.org
4  *
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)
8  *  any later version.
9  *
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.
14  *
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/>.
18  *
19  */
20
21 #include "DDSImage.h"
22 #include "XBTF.h"
23 #include "libsquish/squish.h"
24 #include "utils/log.h"
25 #include <string.h>
26
27 #ifndef NO_XBMC_FILESYSTEM
28 #include "filesystem/File.h"
29 using namespace XFILE;
30 #else
31 #include "SimpleFS.h"
32 #endif
33
34 using namespace std;
35
36 CDDSImage::CDDSImage()
37 {
38   m_data = NULL;
39   memset(&m_desc, 0, sizeof(m_desc));
40 }
41
42 CDDSImage::CDDSImage(unsigned int width, unsigned int height, unsigned int format)
43 {
44   m_data = NULL;
45   Allocate(width, height, format);
46 }
47
48 CDDSImage::~CDDSImage()
49 {
50   delete[] m_data;
51 }
52
53 unsigned int CDDSImage::GetWidth() const
54 {
55   return m_desc.width;
56 }
57
58 unsigned int CDDSImage::GetHeight() const
59 {
60   return m_desc.height;
61 }
62
63 unsigned int CDDSImage::GetFormat() const
64 {
65   if (m_desc.pixelFormat.flags & DDPF_RGB)
66     return 0; // Not supported
67   if (m_desc.pixelFormat.flags & DDPF_FOURCC)
68   {
69     if (strncmp((const char *)&m_desc.pixelFormat.fourcc, "DXT1", 4) == 0)
70       return XB_FMT_DXT1;
71     if (strncmp((const char *)&m_desc.pixelFormat.fourcc, "DXT3", 4) == 0)
72       return XB_FMT_DXT3;
73     if (strncmp((const char *)&m_desc.pixelFormat.fourcc, "DXT5", 4) == 0)
74       return XB_FMT_DXT5;
75     if (strncmp((const char *)&m_desc.pixelFormat.fourcc, "ARGB", 4) == 0)
76       return XB_FMT_A8R8G8B8;
77   }
78   return 0;
79 }
80
81 unsigned int CDDSImage::GetSize() const
82 {
83   return m_desc.linearSize;
84 }
85
86 unsigned char *CDDSImage::GetData() const
87 {
88   return m_data;
89 }
90
91 bool CDDSImage::ReadFile(const std::string &inputFile)
92 {
93   // open the file
94   CFile file;
95   if (!file.Open(inputFile))
96     return false;
97
98   // read the header
99   uint32_t magic;
100   if (file.Read(&magic, 4) != 4)
101     return false;
102   if (file.Read(&m_desc, sizeof(m_desc)) != sizeof(m_desc))
103     return false;
104   if (!GetFormat())
105     return false;  // not supported
106
107   // allocate our data
108   m_data = new unsigned char[m_desc.linearSize];
109   if (!m_data)
110     return false;
111
112   // and read it in
113   if (file.Read(m_data, m_desc.linearSize) != m_desc.linearSize)
114     return false;
115
116   file.Close();
117   return true;
118 }
119
120 bool CDDSImage::Create(const std::string &outputFile, unsigned int width, unsigned int height, unsigned int pitch, unsigned char const *brga, double maxMSE)
121 {
122   if (!brga)
123     return false;
124   if (!Compress(width, height, pitch, brga, maxMSE))
125   { // use ARGB
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));
129   }
130   return WriteFile(outputFile);
131 }
132
133 bool CDDSImage::WriteFile(const std::string &outputFile) const
134 {
135   // open the file
136   CFile file;
137   if (!file.OpenForWrite(outputFile, true))
138     return false;
139
140   // write the header
141   file.Write("DDS ", 4);
142   file.Write(&m_desc, sizeof(m_desc));
143   // now the data
144   file.Write(m_data, m_desc.linearSize);
145   file.Close();
146   return true;
147 }
148
149 unsigned int CDDSImage::GetStorageRequirements(unsigned int width, unsigned int height, unsigned int format)
150 {
151   switch (format)
152   {
153   case XB_FMT_DXT1:
154     return ((width + 3) / 4) * ((height + 3) / 4) * 8;
155   case XB_FMT_DXT3:
156   case XB_FMT_DXT5:
157     return ((width + 3) / 4) * ((height + 3) / 4) * 16;
158   case XB_FMT_A8R8G8B8:
159   default:
160     return width * height * 4;
161   }
162 }
163
164 bool CDDSImage::Compress(unsigned int width, unsigned int height, unsigned int pitch, unsigned char const *brga, double maxMSE)
165 {
166   // first try DXT1, which is only 4bits/pixel
167   Allocate(width, height, XB_FMT_DXT1);
168
169   squish::CompressImage(brga, width, height, pitch, m_data, squish::kDxt1 | squish::kSourceBGRA);
170   const char *fourCC = NULL;
171
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))
175     fourCC = "DXT1";
176   else
177   {
178     if (alphaMSE == 0)
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;
186        }
187        */
188     }
189     if (alphaMSE > 0)
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
196         double dxt5MSE;
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)
201           fourCC = "DXT3";
202         else if (dxt5MSE < maxMSE)
203         { // DXT5 passes
204           fourCC = "DXT5";
205           std::swap(m_data, data2);
206           alphaMSE = dxt5MSE;
207         }
208         delete[] data2;
209       }
210     }
211   }
212   if (fourCC)
213   {
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);
216     return true;
217   }
218   CLog::Log(LOGDEBUG, "%s - no format suitable (min error is: %2.2f:%2.2f)", __FUNCTION__, colorMSE, alphaMSE);
219   return false;
220 }
221
222 bool CDDSImage::Decompress(unsigned char *argb, unsigned int width, unsigned int height, unsigned int pitch, unsigned char const *dxt, unsigned int format)
223 {
224   if (!argb || !dxt || !(format & XB_FMT_DXT_MASK))
225     return false;
226
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);
233
234   return true;
235 }
236
237 void CDDSImage::Allocate(unsigned int width, unsigned int height, unsigned int format)
238 {
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;
249   delete[] m_data;
250   m_data = new unsigned char[m_desc.linearSize];
251 }
252
253 const char *CDDSImage::GetFourCC(unsigned int format)
254 {
255   switch (format)
256   {
257   case XB_FMT_DXT1:
258     return "DXT1";
259   case XB_FMT_DXT3:
260     return "DXT3";
261   case XB_FMT_DXT5:
262     return "DXT5";
263   case XB_FMT_A8R8G8B8:
264   default:
265     return "ARGB";
266   }
267 }