Revert "Ensure only a temp file is deleted, not the actual source file."
[xbmc:xbmc-antiquated.git] / guilib / Texture.cpp
1 /*
2 *      Copyright (C) 2005-2008 Team XBMC
3 *      http://www.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, write to
17 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18 *  http://www.gnu.org/copyleft/gpl.html
19 *
20 */
21
22 #include "Texture.h"
23 #include "Picture.h"
24 #include "WindowingFactory.h"
25 #include "utils/log.h"
26 #include "utils/FileUtils.h"
27 #include "DllImageLib.h"
28 #include "DDSImage.h"
29 #include "Util.h"
30
31 /************************************************************************/
32 /*                                                                      */
33 /************************************************************************/
34 CBaseTexture::CBaseTexture(unsigned int width, unsigned int height, unsigned int format)
35  : m_hasAlpha( true )
36 {
37 #ifndef HAS_DX
38   m_texture = NULL;
39 #endif
40   m_pixels = NULL;
41   m_loadedToGPU = false;
42   Allocate(width, height, format);
43 }
44
45 CBaseTexture::~CBaseTexture()
46 {
47   delete[] m_pixels;
48 }
49
50 void CBaseTexture::Allocate(unsigned int width, unsigned int height, unsigned int format)
51 {
52   m_imageWidth = width;
53   m_imageHeight = height;
54   m_format = format;
55   m_orientation = 0;
56
57   if (!g_Windowing.SupportsNPOT((m_format & XB_FMT_DXT_MASK) != 0))
58   {
59     m_textureWidth = PadPow2(m_imageWidth);
60     m_textureHeight = PadPow2(m_imageHeight);
61   }
62   else
63   {
64     m_textureWidth = m_imageWidth;
65     m_textureHeight = m_imageHeight;
66   }
67   if (m_format & XB_FMT_DXT_MASK)
68   { // DXT textures must be a multiple of 4 in width and height
69     m_textureWidth = ((m_textureWidth + 3) / 4) * 4;
70     m_textureHeight = ((m_textureHeight + 3) / 4) * 4;
71   }
72
73   // check for max texture size
74   #define CLAMP(x, y) { if (x > y) x = y; }
75   CLAMP(m_textureWidth, g_Windowing.GetMaxTextureSize());
76   CLAMP(m_textureHeight, g_Windowing.GetMaxTextureSize());
77   CLAMP(m_imageWidth, m_textureWidth);
78   CLAMP(m_imageHeight, m_textureHeight);
79
80   delete[] m_pixels;
81   m_pixels = new unsigned char[GetPitch() * GetRows()];
82 }
83
84 void CBaseTexture::Update(unsigned int width, unsigned int height, unsigned int pitch, unsigned int format, const unsigned char *pixels, bool loadToGPU)
85 {
86   if (pixels == NULL)
87     return;
88
89   if (format & XB_FMT_DXT_MASK && !g_Windowing.SupportsDXT())
90   { // compressed format that we don't support
91     Allocate(width, height, XB_FMT_A8R8G8B8);
92     CDDSImage::Decompress(m_pixels, std::min(width, m_textureWidth), std::min(height, m_textureHeight), GetPitch(m_textureWidth), pixels, format);
93   }
94   else
95   {
96     Allocate(width, height, format);
97
98     unsigned int srcPitch = pitch ? pitch : GetPitch(width);
99     unsigned int srcRows = GetRows(height);
100     unsigned int dstPitch = GetPitch(m_textureWidth);
101     unsigned int dstRows = GetRows(m_textureHeight);
102
103     if (srcPitch == dstPitch)
104       memcpy(m_pixels, pixels, srcPitch * std::min(srcRows, dstRows));
105     else
106     {
107       const unsigned char *src = pixels;
108       unsigned char* dst = m_pixels;
109       for (unsigned int y = 0; y < srcRows && y < dstRows; y++)
110       {
111         memcpy(dst, src, std::min(srcPitch, dstPitch));
112         src += srcPitch;
113         dst += dstPitch;
114       }
115     }
116   }
117   ClampToEdge();
118
119   if (loadToGPU)
120     LoadToGPU();
121 }
122
123 void CBaseTexture::ClampToEdge()
124 {
125   unsigned int imagePitch = GetPitch(m_imageWidth);
126   unsigned int imageRows = GetRows(m_imageHeight);
127   unsigned int texturePitch = GetPitch(m_textureWidth);
128   unsigned int textureRows = GetRows(m_textureHeight);
129   if (imagePitch < texturePitch)
130   {
131     unsigned int blockSize = GetBlockSize();
132     unsigned char *src = m_pixels + imagePitch - blockSize;
133     unsigned char *dst = m_pixels;
134     for (unsigned int y = 0; y < imageRows; y++)
135     {
136       for (unsigned int x = imagePitch; x < texturePitch; x += blockSize)
137         memcpy(dst + x, src, blockSize);
138       dst += texturePitch;
139     }
140   }
141
142   if (imageRows < textureRows)
143   {
144     unsigned char *dst = m_pixels + imageRows * texturePitch;
145     for (unsigned int y = imageRows; y < textureRows; y++)
146     {
147       memcpy(dst, dst - texturePitch, texturePitch);
148       dst += texturePitch;
149     }
150   }
151 }
152
153 bool CBaseTexture::LoadFromFile(const CStdString& texturePath, unsigned int maxWidth, unsigned int maxHeight,
154                                 bool autoRotate, unsigned int *originalWidth, unsigned int *originalHeight)
155 {
156   if (CUtil::GetExtension(texturePath).Equals(".dds"))
157   { // special case for DDS images
158     CDDSImage image;
159     if (image.ReadFile(texturePath))
160     {
161       Update(image.GetWidth(), image.GetHeight(), 0, image.GetFormat(), image.GetData(), false);
162       return true;
163     }
164     return false;
165   }
166
167   DllImageLib dll;
168   if (!dll.Load())
169     return false;
170
171   ImageInfo image;
172   memset(&image, 0, sizeof(image));
173
174   unsigned int width = maxWidth ? std::min(maxWidth, g_Windowing.GetMaxTextureSize()) : g_Windowing.GetMaxTextureSize();
175   unsigned int height = maxHeight ? std::min(maxHeight, g_Windowing.GetMaxTextureSize()) : g_Windowing.GetMaxTextureSize();
176
177   bool isTemp = false;
178   CStdString tsrc;
179   CFileUtils::GetLocalPath(texturePath, tsrc);
180   if (tsrc.IsEmpty())
181   {
182     CFileUtils::TmpCache(texturePath, tsrc);
183     if (tsrc.IsEmpty())
184     {
185       CLog::Log(LOGERROR, "Texture manager unable to cache file: %s", texturePath.c_str());
186       return false;
187     }
188     isTemp = true;
189   }
190
191   if(!dll.LoadImage(tsrc.c_str(), width, height, &image))
192   {
193     CLog::Log(LOGERROR, "Texture manager unable to load file: %s", texturePath.c_str());
194     if (isTemp)
195       CFileUtils::Delete(texturePath);
196     return false;
197   }
198
199   m_hasAlpha = NULL != image.alpha;
200
201   Allocate(image.width, image.height, XB_FMT_A8R8G8B8);
202   if (autoRotate && image.exifInfo.Orientation)
203     m_orientation = image.exifInfo.Orientation - 1;
204   if (originalWidth)
205     *originalWidth = image.originalwidth;
206   if (originalHeight)
207     *originalHeight = image.originalheight;
208
209   unsigned int destPitch = GetPitch();
210   unsigned int srcPitch = ((image.width + 1)* 3 / 4) * 4; // bitmap row length is aligned to 4 bytes
211
212   for (unsigned int y = 0; y < m_imageHeight; y++)
213   {
214     unsigned char *dst = m_pixels + y * destPitch;
215     unsigned char *src = image.texture + (m_imageHeight - 1 - y) * srcPitch;
216     unsigned char *alpha = image.alpha + (m_imageHeight - 1 - y) * m_imageWidth;
217     for (unsigned int x = 0; x < m_imageWidth; x++)
218     {
219       *dst++ = *src++;
220       *dst++ = *src++;
221       *dst++ = *src++;
222       *dst++ = (image.alpha) ? *alpha++ : 0xff;
223     }
224   }
225
226   dll.ReleaseImage(&image);
227
228   ClampToEdge();
229
230   if (isTemp)
231     CFileUtils::Delete(tsrc);
232
233   return true;
234 }
235
236 bool CBaseTexture::LoadFromMemory(unsigned int width, unsigned int height, unsigned int pitch, unsigned int format, unsigned char* pixels)
237 {
238   m_imageWidth = width;
239   m_imageHeight = height;
240   m_format = format;
241   Update(width, height, pitch, format, pixels, false);
242   return true;
243 }
244
245 bool CBaseTexture::LoadPaletted(unsigned int width, unsigned int height, unsigned int pitch, unsigned int format, const unsigned char *pixels, const COLOR *palette)
246 {
247   if (pixels == NULL || palette == NULL)
248     return false;
249
250   Allocate(width, height, format);
251
252   for (unsigned int y = 0; y < m_imageHeight; y++)
253   {
254     unsigned char *dest = m_pixels + y * GetPitch();
255     const unsigned char *src = pixels + y * pitch;
256     for (unsigned int x = 0; x < m_imageWidth; x++)
257     {
258       COLOR col = palette[*src++];
259       *dest++ = col.b;
260       *dest++ = col.g;
261       *dest++ = col.r;
262       *dest++ = col.x;
263     }
264   }
265   ClampToEdge();
266   return true;
267 }
268
269 unsigned int CBaseTexture::PadPow2(unsigned int x)
270 {
271   --x;
272   x |= x >> 1;
273   x |= x >> 2;
274   x |= x >> 4;
275   x |= x >> 8;
276   x |= x >> 16;
277   return ++x;
278 }
279
280 unsigned int CBaseTexture::GetPitch(unsigned int width) const
281 {
282   switch (m_format)
283   {
284   case XB_FMT_DXT1:
285     return ((width + 3) / 4) * 8;
286   case XB_FMT_DXT3:
287   case XB_FMT_DXT5:
288   case XB_FMT_DXT5_YCoCg:
289     return ((width + 3) / 4) * 16;
290   case XB_FMT_A8:
291     return width;
292   case XB_FMT_A8R8G8B8:
293   default:
294     return width*4;
295   }
296 }
297
298 unsigned int CBaseTexture::GetRows(unsigned int height) const
299 {
300   switch (m_format)
301   {
302   case XB_FMT_DXT1:
303     return (height + 3) / 4;
304   case XB_FMT_DXT3:
305   case XB_FMT_DXT5:
306   case XB_FMT_DXT5_YCoCg:
307     return (height + 3) / 4;
308   default:
309     return height;
310   }
311 }
312
313 unsigned int CBaseTexture::GetBlockSize() const
314 {
315   switch (m_format)
316   {
317   case XB_FMT_DXT1:
318     return 8;
319   case XB_FMT_DXT3:
320   case XB_FMT_DXT5:
321   case XB_FMT_DXT5_YCoCg:
322     return 16;
323   case XB_FMT_A8:
324     return 1;
325   default:
326     return 4;
327   }
328 }
329
330 bool CBaseTexture::HasAlpha() const
331 {
332   return m_hasAlpha;
333 }