Merge pull request #473 from Montellese/onplaybackspeedchanged
[vuplus_xbmc] / xbmc / 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 "windowing/WindowingFactory.h"
24 #include "utils/log.h"
25 #include "utils/URIUtils.h"
26 #include "pictures/DllImageLib.h"
27 #include "DDSImage.h"
28 #include "filesystem/SpecialProtocol.h"
29 #include "JpegIO.h"
30 #if defined(__APPLE__) && defined(__arm__)
31 #include <ImageIO/ImageIO.h>
32 #include "filesystem/File.h"
33 #include "osx/DarwinUtils.h"
34 #endif
35
36 /************************************************************************/
37 /*                                                                      */
38 /************************************************************************/
39 CBaseTexture::CBaseTexture(unsigned int width, unsigned int height, unsigned int format)
40  : m_hasAlpha( true )
41 {
42 #ifndef HAS_DX 
43   m_texture = 0; 
44 #endif
45   m_pixels = NULL;
46   m_loadedToGPU = false;
47   Allocate(width, height, format);
48 }
49
50 CBaseTexture::~CBaseTexture()
51 {
52   delete[] m_pixels;
53 }
54
55 void CBaseTexture::Allocate(unsigned int width, unsigned int height, unsigned int format)
56 {
57   m_imageWidth = width;
58   m_imageHeight = height;
59   m_format = format;
60   m_orientation = 0;
61
62   m_textureWidth = m_imageWidth;
63   m_textureHeight = m_imageHeight;
64
65   if (m_format & XB_FMT_DXT_MASK)
66     while (GetPitch() < g_Windowing.GetMinDXTPitch())
67       m_textureWidth += GetBlockSize();
68
69   if (!g_Windowing.SupportsNPOT((m_format & XB_FMT_DXT_MASK) != 0))
70   {
71     m_textureWidth = PadPow2(m_textureWidth);
72     m_textureHeight = PadPow2(m_textureHeight);
73   }
74   if (m_format & XB_FMT_DXT_MASK)
75   { // DXT textures must be a multiple of 4 in width and height
76     m_textureWidth = ((m_textureWidth + 3) / 4) * 4;
77     m_textureHeight = ((m_textureHeight + 3) / 4) * 4;
78   }
79
80   // check for max texture size
81   #define CLAMP(x, y) { if (x > y) x = y; }
82   CLAMP(m_textureWidth, g_Windowing.GetMaxTextureSize());
83   CLAMP(m_textureHeight, g_Windowing.GetMaxTextureSize());
84   CLAMP(m_imageWidth, m_textureWidth);
85   CLAMP(m_imageHeight, m_textureHeight);
86   delete[] m_pixels;
87   m_pixels = new unsigned char[GetPitch() * GetRows()];
88 }
89
90 void CBaseTexture::Update(unsigned int width, unsigned int height, unsigned int pitch, unsigned int format, const unsigned char *pixels, bool loadToGPU)
91 {
92   if (pixels == NULL)
93     return;
94
95   if (format & XB_FMT_DXT_MASK && !g_Windowing.SupportsDXT())
96   { // compressed format that we don't support
97     Allocate(width, height, XB_FMT_A8R8G8B8);
98     CDDSImage::Decompress(m_pixels, std::min(width, m_textureWidth), std::min(height, m_textureHeight), GetPitch(m_textureWidth), pixels, format);
99   }
100   else
101   {
102     Allocate(width, height, format);
103
104     unsigned int srcPitch = pitch ? pitch : GetPitch(width);
105     unsigned int srcRows = GetRows(height);
106     unsigned int dstPitch = GetPitch(m_textureWidth);
107     unsigned int dstRows = GetRows(m_textureHeight);
108
109     if (srcPitch == dstPitch)
110       memcpy(m_pixels, pixels, srcPitch * std::min(srcRows, dstRows));
111     else
112     {
113       const unsigned char *src = pixels;
114       unsigned char* dst = m_pixels;
115       for (unsigned int y = 0; y < srcRows && y < dstRows; y++)
116       {
117         memcpy(dst, src, std::min(srcPitch, dstPitch));
118         src += srcPitch;
119         dst += dstPitch;
120       }
121     }
122   }
123   ClampToEdge();
124
125   if (loadToGPU)
126     LoadToGPU();
127 }
128
129 void CBaseTexture::ClampToEdge()
130 {
131   unsigned int imagePitch = GetPitch(m_imageWidth);
132   unsigned int imageRows = GetRows(m_imageHeight);
133   unsigned int texturePitch = GetPitch(m_textureWidth);
134   unsigned int textureRows = GetRows(m_textureHeight);
135   if (imagePitch < texturePitch)
136   {
137     unsigned int blockSize = GetBlockSize();
138     unsigned char *src = m_pixels + imagePitch - blockSize;
139     unsigned char *dst = m_pixels;
140     for (unsigned int y = 0; y < imageRows; y++)
141     {
142       for (unsigned int x = imagePitch; x < texturePitch; x += blockSize)
143         memcpy(dst + x, src, blockSize);
144       dst += texturePitch;
145     }
146   }
147
148   if (imageRows < textureRows)
149   {
150     unsigned char *dst = m_pixels + imageRows * texturePitch;
151     for (unsigned int y = imageRows; y < textureRows; y++)
152     {
153       memcpy(dst, dst - texturePitch, texturePitch);
154       dst += texturePitch;
155     }
156   }
157 }
158
159 bool CBaseTexture::LoadFromFile(const CStdString& texturePath, unsigned int maxWidth, unsigned int maxHeight,
160                                 bool autoRotate, unsigned int *originalWidth, unsigned int *originalHeight)
161 {
162   if (URIUtils::GetExtension(texturePath).Equals(".dds"))
163   { // special case for DDS images
164     CDDSImage image;
165     if (image.ReadFile(texturePath))
166     {
167       Update(image.GetWidth(), image.GetHeight(), 0, image.GetFormat(), image.GetData(), false);
168       return true;
169     }
170     return false;
171   }
172
173   //ImageLib is sooo sloow for jpegs. Try our own decoder first. If it fails, fall back to ImageLib.
174   if (URIUtils::GetExtension(texturePath).Equals(".jpg") || URIUtils::GetExtension(texturePath).Equals(".tbn"))
175   {
176     CJpegIO jpegfile;
177     if (jpegfile.Open(texturePath))
178     {
179       if (jpegfile.Width() > 0 && jpegfile.Height() > 0)
180       {
181         Allocate(jpegfile.Width(), jpegfile.Height(), XB_FMT_A8R8G8B8);
182         if (jpegfile.Decode(m_pixels, GetPitch(), XB_FMT_A8R8G8B8))
183         {
184           if (autoRotate && jpegfile.Orientation())
185             m_orientation = jpegfile.Orientation() - 1;
186           m_hasAlpha=false;
187           return true;
188         }
189       }
190     }
191   }
192
193   DllImageLib dll;
194   if (!dll.Load())
195     return false;
196
197   ImageInfo image;
198   memset(&image, 0, sizeof(image));
199
200   unsigned int width = maxWidth ? std::min(maxWidth, g_Windowing.GetMaxTextureSize()) : g_Windowing.GetMaxTextureSize();
201   unsigned int height = maxHeight ? std::min(maxHeight, g_Windowing.GetMaxTextureSize()) : g_Windowing.GetMaxTextureSize();
202
203   if(!dll.LoadImage(texturePath.c_str(), width, height, &image))
204   {
205     CLog::Log(LOGERROR, "Texture manager unable to load file: %s", texturePath.c_str());
206     return false;
207   }
208
209   m_hasAlpha = NULL != image.alpha;
210
211   Allocate(image.width, image.height, XB_FMT_A8R8G8B8);
212   if (autoRotate && image.exifInfo.Orientation)
213     m_orientation = image.exifInfo.Orientation - 1;
214   if (originalWidth)
215     *originalWidth = image.originalwidth;
216   if (originalHeight)
217     *originalHeight = image.originalheight;
218
219   unsigned int dstPitch = GetPitch();
220   unsigned int srcPitch = ((image.width + 1)* 3 / 4) * 4; // bitmap row length is aligned to 4 bytes
221
222   unsigned char *dst = m_pixels;
223   unsigned char *src = image.texture + (m_imageHeight - 1) * srcPitch;
224
225   for (unsigned int y = 0; y < m_imageHeight; y++)
226   {
227     unsigned char *dst2 = dst;
228     unsigned char *src2 = src;
229     for (unsigned int x = 0; x < m_imageWidth; x++, dst2 += 4, src2 += 3)
230     {
231       dst2[0] = src2[0];
232       dst2[1] = src2[1];
233       dst2[2] = src2[2];
234       dst2[3] = 0xff;
235     }
236     src -= srcPitch;
237     dst += dstPitch;
238   }
239
240   if(image.alpha)
241   {
242     dst = m_pixels + 3;
243     src = image.alpha + (m_imageHeight - 1) * m_imageWidth;
244
245     for (unsigned int y = 0; y < m_imageHeight; y++)
246     {
247       unsigned char *dst2 = dst;
248       unsigned char *src2 = src;
249
250       for (unsigned int x = 0; x < m_imageWidth; x++,  dst2+=4, src2++)
251         *dst2 = *src2;
252       src -= m_imageWidth;
253       dst += dstPitch;
254     }
255   }
256   dll.ReleaseImage(&image);
257
258   ClampToEdge();
259
260   return true;
261 }
262
263 bool CBaseTexture::LoadFromMemory(unsigned int width, unsigned int height, unsigned int pitch, unsigned int format, bool hasAlpha, unsigned char* pixels)
264 {
265   m_imageWidth = width;
266   m_imageHeight = height;
267   m_format = format;
268   m_hasAlpha = hasAlpha;
269   Update(width, height, pitch, format, pixels, false);
270   return true;
271 }
272
273 bool CBaseTexture::LoadPaletted(unsigned int width, unsigned int height, unsigned int pitch, unsigned int format, const unsigned char *pixels, const COLOR *palette)
274 {
275   if (pixels == NULL || palette == NULL)
276     return false;
277
278   Allocate(width, height, format);
279
280   for (unsigned int y = 0; y < m_imageHeight; y++)
281   {
282     unsigned char *dest = m_pixels + y * GetPitch();
283     const unsigned char *src = pixels + y * pitch;
284     for (unsigned int x = 0; x < m_imageWidth; x++)
285     {
286       COLOR col = palette[*src++];
287       *dest++ = col.b;
288       *dest++ = col.g;
289       *dest++ = col.r;
290       *dest++ = col.x;
291     }
292   }
293   ClampToEdge();
294   return true;
295 }
296
297 unsigned int CBaseTexture::PadPow2(unsigned int x)
298 {
299   --x;
300   x |= x >> 1;
301   x |= x >> 2;
302   x |= x >> 4;
303   x |= x >> 8;
304   x |= x >> 16;
305   return ++x;
306 }
307
308 bool CBaseTexture::SwapBlueRed(unsigned char *pixels, unsigned int height, unsigned int pitch, unsigned int elements, unsigned int offset)
309 {
310   if (!pixels) return false;
311   unsigned char *dst = pixels;
312   for (unsigned int y = 0; y < height; y++)
313   {
314     dst = pixels + (y * pitch);
315     for (unsigned int x = 0; x < pitch; x+=elements)
316       std::swap(dst[x+offset], dst[x+2+offset]);
317   }
318   return true;
319 }
320
321 unsigned int CBaseTexture::GetPitch(unsigned int width) const
322 {
323   switch (m_format)
324   {
325   case XB_FMT_DXT1:
326     return ((width + 3) / 4) * 8;
327   case XB_FMT_DXT3:
328   case XB_FMT_DXT5:
329   case XB_FMT_DXT5_YCoCg:
330     return ((width + 3) / 4) * 16;
331   case XB_FMT_A8:
332     return width;
333   case XB_FMT_RGB8:
334     return (((width + 1)* 3 / 4) * 4);
335   case XB_FMT_RGBA8:
336   case XB_FMT_A8R8G8B8:
337   default:
338     return width*4;
339   }
340 }
341
342 unsigned int CBaseTexture::GetRows(unsigned int height) const
343 {
344   switch (m_format)
345   {
346   case XB_FMT_DXT1:
347     return (height + 3) / 4;
348   case XB_FMT_DXT3:
349   case XB_FMT_DXT5:
350   case XB_FMT_DXT5_YCoCg:
351     return (height + 3) / 4;
352   default:
353     return height;
354   }
355 }
356
357 unsigned int CBaseTexture::GetBlockSize() const
358 {
359   switch (m_format)
360   {
361   case XB_FMT_DXT1:
362     return 8;
363   case XB_FMT_DXT3:
364   case XB_FMT_DXT5:
365   case XB_FMT_DXT5_YCoCg:
366     return 16;
367   case XB_FMT_A8:
368     return 1;
369   default:
370     return 4;
371   }
372 }
373
374 bool CBaseTexture::HasAlpha() const
375 {
376   return m_hasAlpha;
377 }