Merge remote branch 'mine/ext-python'
[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 #if defined(__APPLE__) && defined(__arm__)
30 #include <ImageIO/ImageIO.h>
31 #include "filesystem/File.h"
32 #include "osx/DarwinUtils.h"
33 #endif
34
35 /************************************************************************/
36 /*                                                                      */
37 /************************************************************************/
38 CBaseTexture::CBaseTexture(unsigned int width, unsigned int height, unsigned int format)
39  : m_hasAlpha( true )
40 {
41 #ifndef HAS_DX 
42   m_texture = NULL; 
43 #endif
44   m_pixels = NULL;
45   m_loadedToGPU = false;
46   Allocate(width, height, format);
47 }
48
49 CBaseTexture::~CBaseTexture()
50 {
51   delete[] m_pixels;
52 }
53
54 void CBaseTexture::Allocate(unsigned int width, unsigned int height, unsigned int format)
55 {
56   m_imageWidth = width;
57   m_imageHeight = height;
58   m_format = format;
59   m_orientation = 0;
60
61   m_textureWidth = m_imageWidth;
62   m_textureHeight = m_imageHeight;
63
64   if (m_format & XB_FMT_DXT_MASK)
65     while (GetPitch() < g_Windowing.GetMinDXTPitch())
66       m_textureWidth += GetBlockSize();
67
68   if (!g_Windowing.SupportsNPOT((m_format & XB_FMT_DXT_MASK) != 0))
69   {
70     m_textureWidth = PadPow2(m_textureWidth);
71     m_textureHeight = PadPow2(m_textureHeight);
72   }
73   if (m_format & XB_FMT_DXT_MASK)
74   { // DXT textures must be a multiple of 4 in width and height
75     m_textureWidth = ((m_textureWidth + 3) / 4) * 4;
76     m_textureHeight = ((m_textureHeight + 3) / 4) * 4;
77   }
78
79   // check for max texture size
80   #define CLAMP(x, y) { if (x > y) x = y; }
81   CLAMP(m_textureWidth, g_Windowing.GetMaxTextureSize());
82   CLAMP(m_textureHeight, g_Windowing.GetMaxTextureSize());
83   CLAMP(m_imageWidth, m_textureWidth);
84   CLAMP(m_imageHeight, m_textureHeight);
85
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 #if defined(__APPLE__) && defined(__arm__)
174   XFILE::CFile file;
175   UInt8 *imageBuff      = NULL;
176   int64_t imageBuffSize = 0;
177
178   //open path and read data to buffer
179   //this handles advancedsettings.xml pathsubstitution
180   //and resulting networking
181   if (file.Open(texturePath, 0))
182   {
183     imageBuffSize =file.GetLength();
184     imageBuff = new UInt8[imageBuffSize];
185     imageBuffSize = file.Read(imageBuff, imageBuffSize);
186     file.Close();
187   }
188   else
189   {
190     CLog::Log(LOGERROR, "Texture manager unable to open file %s", texturePath.c_str());
191     return false;
192   }
193
194   if (imageBuffSize <= 0)
195   {
196     CLog::Log(LOGERROR, "Texture manager read texture file failed.");
197     delete [] imageBuff;
198     return false;
199   }
200
201   // create the image from buffer;
202   CGImageSourceRef imageSource;
203   // create a CFDataRef using CFDataCreateWithBytesNoCopy and kCFAllocatorNull for deallocator.
204   // this allows us to do a nocopy reference and we handle the free of imageBuff
205   CFDataRef cfdata = CFDataCreateWithBytesNoCopy(NULL, imageBuff, imageBuffSize, kCFAllocatorNull);
206   imageSource = CGImageSourceCreateWithData(cfdata, NULL);   
207     
208   if (imageSource == nil)
209   {
210     CLog::Log(LOGERROR, "Texture manager unable to load file: %s", CSpecialProtocol::TranslatePath(texturePath).c_str());
211     CFRelease(cfdata);
212     delete [] imageBuff;
213     return false;
214   }
215
216   CGImageRef image = CGImageSourceCreateImageAtIndex(imageSource, 0, NULL);
217
218   int rotate = 0;
219   if (autoRotate)
220   { // get the orientation of the image for displaying it correctly
221     CFDictionaryRef imagePropertiesDictionary = CGImageSourceCopyPropertiesAtIndex(imageSource,0, NULL);
222     if (imagePropertiesDictionary != nil)
223     {
224       CFNumberRef orientation = (CFNumberRef)CFDictionaryGetValue(imagePropertiesDictionary, kCGImagePropertyOrientation);
225       if (orientation != nil)
226       {
227         int value = 0;
228         CFNumberGetValue(orientation, kCFNumberIntType, &value);
229         if (value)
230           rotate = value - 1;
231       }
232     }
233   }
234
235   CFRelease(imageSource);
236
237   unsigned int width  = CGImageGetWidth(image);
238   unsigned int height = CGImageGetHeight(image);
239
240   m_hasAlpha = (CGImageGetAlphaInfo(image) != kCGImageAlphaNone);
241
242   if (originalWidth)
243     *originalWidth = width;
244   if (originalHeight)
245     *originalHeight = height;
246
247   // check texture size limits and limit to screen size - preserving aspectratio of image  
248   if ( width > g_Windowing.GetMaxTextureSize() || height > g_Windowing.GetMaxTextureSize() )
249   {
250     float aspect;
251
252     if ( width > height )
253     {
254       aspect = (float)width / (float)height;
255       width  = g_Windowing.GetWidth();
256       height = (float)width / (float)aspect;
257     }
258     else
259     {
260       aspect = (float)height / (float)width;
261       height = g_Windowing.GetHeight();
262       width  = (float)height / (float)aspect;
263     }
264     CLog::Log(LOGDEBUG, "Texture manager texture clamp:new texture size: %i x %i", width, height);
265   }
266
267   Allocate(width, height, XB_FMT_A8R8G8B8);
268   m_orientation = rotate;
269     
270   CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
271
272   CGContextRef context = CGBitmapContextCreate(m_pixels,
273     width, height, 8, GetPitch(), colorSpace,
274     kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host);
275
276   CGColorSpaceRelease(colorSpace);
277
278   // Flip so that it isn't upside-down
279   //CGContextTranslateCTM(context, 0, height);
280   //CGContextScaleCTM(context, 1.0f, -1.0f);
281   #if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5
282     CGContextClearRect(context, CGRectMake(0, 0, width, height));
283   #else
284     #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
285     // (just a way of checking whether we're running in 10.5 or later)
286     if (CGContextDrawLinearGradient == 0)
287       CGContextClearRect(context, CGRectMake(0, 0, width, height));
288     else
289     #endif
290       CGContextSetBlendMode(context, kCGBlendModeCopy);
291   #endif
292   //CGContextSetBlendMode(context, kCGBlendModeCopy);
293   CGContextDrawImage(context, CGRectMake(0, 0, width, height), image);
294   CGContextRelease(context);
295   CGImageRelease(image);
296   CFRelease(cfdata);
297   delete [] imageBuff;
298 #else
299   DllImageLib dll;
300   if (!dll.Load())
301     return false;
302
303   ImageInfo image;
304   memset(&image, 0, sizeof(image));
305
306   unsigned int width = maxWidth ? std::min(maxWidth, g_Windowing.GetMaxTextureSize()) : g_Windowing.GetMaxTextureSize();
307   unsigned int height = maxHeight ? std::min(maxHeight, g_Windowing.GetMaxTextureSize()) : g_Windowing.GetMaxTextureSize();
308
309   if(!dll.LoadImage(texturePath.c_str(), width, height, &image))
310   {
311     CLog::Log(LOGERROR, "Texture manager unable to load file: %s", texturePath.c_str());
312     return false;
313   }
314
315   m_hasAlpha = NULL != image.alpha;
316
317   Allocate(image.width, image.height, XB_FMT_A8R8G8B8);
318   if (autoRotate && image.exifInfo.Orientation)
319     m_orientation = image.exifInfo.Orientation - 1;
320   if (originalWidth)
321     *originalWidth = image.originalwidth;
322   if (originalHeight)
323     *originalHeight = image.originalheight;
324
325   unsigned int destPitch = GetPitch();
326   unsigned int srcPitch = ((image.width + 1)* 3 / 4) * 4; // bitmap row length is aligned to 4 bytes
327
328   for (unsigned int y = 0; y < m_imageHeight; y++)
329   {
330     unsigned char *dst = m_pixels + y * destPitch;
331     unsigned char *src = image.texture + (m_imageHeight - 1 - y) * srcPitch;
332     unsigned char *alpha = image.alpha + (m_imageHeight - 1 - y) * m_imageWidth;
333     for (unsigned int x = 0; x < m_imageWidth; x++)
334     {
335       *dst++ = *src++;
336       *dst++ = *src++;
337       *dst++ = *src++;
338       *dst++ = (image.alpha) ? *alpha++ : 0xff;
339     }
340   }
341
342   dll.ReleaseImage(&image);
343 #endif
344
345   ClampToEdge();
346
347   return true;
348 }
349
350 bool CBaseTexture::LoadFromMemory(unsigned int width, unsigned int height, unsigned int pitch, unsigned int format, unsigned char* pixels)
351 {
352   m_imageWidth = width;
353   m_imageHeight = height;
354   m_format = format;
355   Update(width, height, pitch, format, pixels, false);
356   return true;
357 }
358
359 bool CBaseTexture::LoadPaletted(unsigned int width, unsigned int height, unsigned int pitch, unsigned int format, const unsigned char *pixels, const COLOR *palette)
360 {
361   if (pixels == NULL || palette == NULL)
362     return false;
363
364   Allocate(width, height, format);
365
366   for (unsigned int y = 0; y < m_imageHeight; y++)
367   {
368     unsigned char *dest = m_pixels + y * GetPitch();
369     const unsigned char *src = pixels + y * pitch;
370     for (unsigned int x = 0; x < m_imageWidth; x++)
371     {
372       COLOR col = palette[*src++];
373       *dest++ = col.b;
374       *dest++ = col.g;
375       *dest++ = col.r;
376       *dest++ = col.x;
377     }
378   }
379   ClampToEdge();
380   return true;
381 }
382
383 unsigned int CBaseTexture::PadPow2(unsigned int x)
384 {
385   --x;
386   x |= x >> 1;
387   x |= x >> 2;
388   x |= x >> 4;
389   x |= x >> 8;
390   x |= x >> 16;
391   return ++x;
392 }
393
394 unsigned int CBaseTexture::GetPitch(unsigned int width) const
395 {
396   switch (m_format)
397   {
398   case XB_FMT_DXT1:
399     return ((width + 3) / 4) * 8;
400   case XB_FMT_DXT3:
401   case XB_FMT_DXT5:
402   case XB_FMT_DXT5_YCoCg:
403     return ((width + 3) / 4) * 16;
404   case XB_FMT_A8:
405     return width;
406   case XB_FMT_A8R8G8B8:
407   default:
408     return width*4;
409   }
410 }
411
412 unsigned int CBaseTexture::GetRows(unsigned int height) const
413 {
414   switch (m_format)
415   {
416   case XB_FMT_DXT1:
417     return (height + 3) / 4;
418   case XB_FMT_DXT3:
419   case XB_FMT_DXT5:
420   case XB_FMT_DXT5_YCoCg:
421     return (height + 3) / 4;
422   default:
423     return height;
424   }
425 }
426
427 unsigned int CBaseTexture::GetBlockSize() const
428 {
429   switch (m_format)
430   {
431   case XB_FMT_DXT1:
432     return 8;
433   case XB_FMT_DXT3:
434   case XB_FMT_DXT5:
435   case XB_FMT_DXT5_YCoCg:
436     return 16;
437   case XB_FMT_A8:
438     return 1;
439   default:
440     return 4;
441   }
442 }
443
444 bool CBaseTexture::HasAlpha() const
445 {
446   return m_hasAlpha;
447 }