Code changes to make external python work in windows. Changes credit to WiSo
[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   //get the orientation of the image for displaying it correctly
219   CFDictionaryRef imagePropertiesDictionary = CGImageSourceCopyPropertiesAtIndex(imageSource,0, NULL);
220   if (imagePropertiesDictionary != nil)
221   {
222     CFNumberRef orientation = (CFNumberRef)CFDictionaryGetValue(imagePropertiesDictionary, kCGImagePropertyOrientation);
223     if (orientation != nil)
224     {
225       int switchValue = 0;
226       int rotationAngle = 0;
227         CFNumberGetValue(orientation, kCFNumberIntType, &switchValue);  
228       // possible values taken from
229       // http://developer.apple.com/library/mac/#samplecode/ImageApp/Listings/ImageDoc_m.html
230       switch(switchValue)
231       {
232         case 3: //rotated 180°
233           rotationAngle = 180;
234           break;
235         case 6: //rotated 90°
236           rotationAngle = 270;
237           break;
238         case 8: //rotated 270°
239           rotationAngle = 90;
240           break;
241       }
242       
243       //rotate the image if needed        
244       if (rotationAngle != 0)
245       {
246         CLog::Log(LOGDEBUG,"Rotating the image about %i degrees", rotationAngle);
247         CGImageRef rotatedImage = CGImageCreateRotatedByAngle(image, (float)rotationAngle);
248         CFRelease(image);
249         image = rotatedImage;
250       }
251     }
252   }
253
254   CFRelease(imageSource);
255
256   unsigned int width  = CGImageGetWidth(image);
257   unsigned int height = CGImageGetHeight(image);
258
259   m_hasAlpha = (CGImageGetAlphaInfo(image) != kCGImageAlphaNone);
260
261   if (originalWidth)
262     *originalWidth = width;
263   if (originalHeight)
264     *originalHeight = height;
265
266   // check texture size limits and limit to screen size - preserving aspectratio of image  
267   if ( width > g_Windowing.GetMaxTextureSize() || height > g_Windowing.GetMaxTextureSize() )
268   {
269     float aspect;
270
271     if ( width > height )
272     {
273       aspect = (float)width / (float)height;
274       width  = g_Windowing.GetWidth();
275       height = (float)width / (float)aspect;
276     }
277     else
278     {
279       aspect = (float)height / (float)width;
280       height = g_Windowing.GetHeight();
281       width  = (float)height / (float)aspect;
282     }
283     CLog::Log(LOGDEBUG, "Texture manager texture clamp:new texture size: %i x %i", width, height);
284   }
285
286   Allocate(width, height, XB_FMT_A8R8G8B8);    
287     
288   CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
289
290   CGContextRef context = CGBitmapContextCreate(m_pixels,
291     width, height, 8, GetPitch(), colorSpace,
292     kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host);
293
294   CGColorSpaceRelease(colorSpace);
295
296   // Flip so that it isn't upside-down
297   //CGContextTranslateCTM(context, 0, height);
298   //CGContextScaleCTM(context, 1.0f, -1.0f);
299   #if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5
300     CGContextClearRect(context, CGRectMake(0, 0, width, height));
301   #else
302     #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
303     // (just a way of checking whether we're running in 10.5 or later)
304     if (CGContextDrawLinearGradient == 0)
305       CGContextClearRect(context, CGRectMake(0, 0, width, height));
306     else
307     #endif
308       CGContextSetBlendMode(context, kCGBlendModeCopy);
309   #endif
310   //CGContextSetBlendMode(context, kCGBlendModeCopy);
311   CGContextDrawImage(context, CGRectMake(0, 0, width, height), image);
312   CGContextRelease(context);
313   CGImageRelease(image);
314   CFRelease(cfdata);
315   delete [] imageBuff;
316 #else
317   DllImageLib dll;
318   if (!dll.Load())
319     return false;
320
321   ImageInfo image;
322   memset(&image, 0, sizeof(image));
323
324   unsigned int width = maxWidth ? std::min(maxWidth, g_Windowing.GetMaxTextureSize()) : g_Windowing.GetMaxTextureSize();
325   unsigned int height = maxHeight ? std::min(maxHeight, g_Windowing.GetMaxTextureSize()) : g_Windowing.GetMaxTextureSize();
326
327   if(!dll.LoadImage(texturePath.c_str(), width, height, &image))
328   {
329     CLog::Log(LOGERROR, "Texture manager unable to load file: %s", texturePath.c_str());
330     return false;
331   }
332
333   m_hasAlpha = NULL != image.alpha;
334
335   Allocate(image.width, image.height, XB_FMT_A8R8G8B8);
336   if (autoRotate && image.exifInfo.Orientation)
337     m_orientation = image.exifInfo.Orientation - 1;
338   if (originalWidth)
339     *originalWidth = image.originalwidth;
340   if (originalHeight)
341     *originalHeight = image.originalheight;
342
343   unsigned int destPitch = GetPitch();
344   unsigned int srcPitch = ((image.width + 1)* 3 / 4) * 4; // bitmap row length is aligned to 4 bytes
345
346   for (unsigned int y = 0; y < m_imageHeight; y++)
347   {
348     unsigned char *dst = m_pixels + y * destPitch;
349     unsigned char *src = image.texture + (m_imageHeight - 1 - y) * srcPitch;
350     unsigned char *alpha = image.alpha + (m_imageHeight - 1 - y) * m_imageWidth;
351     for (unsigned int x = 0; x < m_imageWidth; x++)
352     {
353       *dst++ = *src++;
354       *dst++ = *src++;
355       *dst++ = *src++;
356       *dst++ = (image.alpha) ? *alpha++ : 0xff;
357     }
358   }
359
360   dll.ReleaseImage(&image);
361 #endif
362
363   ClampToEdge();
364
365   return true;
366 }
367
368 bool CBaseTexture::LoadFromMemory(unsigned int width, unsigned int height, unsigned int pitch, unsigned int format, unsigned char* pixels)
369 {
370   m_imageWidth = width;
371   m_imageHeight = height;
372   m_format = format;
373   Update(width, height, pitch, format, pixels, false);
374   return true;
375 }
376
377 bool CBaseTexture::LoadPaletted(unsigned int width, unsigned int height, unsigned int pitch, unsigned int format, const unsigned char *pixels, const COLOR *palette)
378 {
379   if (pixels == NULL || palette == NULL)
380     return false;
381
382   Allocate(width, height, format);
383
384   for (unsigned int y = 0; y < m_imageHeight; y++)
385   {
386     unsigned char *dest = m_pixels + y * GetPitch();
387     const unsigned char *src = pixels + y * pitch;
388     for (unsigned int x = 0; x < m_imageWidth; x++)
389     {
390       COLOR col = palette[*src++];
391       *dest++ = col.b;
392       *dest++ = col.g;
393       *dest++ = col.r;
394       *dest++ = col.x;
395     }
396   }
397   ClampToEdge();
398   return true;
399 }
400
401 unsigned int CBaseTexture::PadPow2(unsigned int x)
402 {
403   --x;
404   x |= x >> 1;
405   x |= x >> 2;
406   x |= x >> 4;
407   x |= x >> 8;
408   x |= x >> 16;
409   return ++x;
410 }
411
412 unsigned int CBaseTexture::GetPitch(unsigned int width) const
413 {
414   switch (m_format)
415   {
416   case XB_FMT_DXT1:
417     return ((width + 3) / 4) * 8;
418   case XB_FMT_DXT3:
419   case XB_FMT_DXT5:
420   case XB_FMT_DXT5_YCoCg:
421     return ((width + 3) / 4) * 16;
422   case XB_FMT_A8:
423     return width;
424   case XB_FMT_A8R8G8B8:
425   default:
426     return width*4;
427   }
428 }
429
430 unsigned int CBaseTexture::GetRows(unsigned int height) const
431 {
432   switch (m_format)
433   {
434   case XB_FMT_DXT1:
435     return (height + 3) / 4;
436   case XB_FMT_DXT3:
437   case XB_FMT_DXT5:
438   case XB_FMT_DXT5_YCoCg:
439     return (height + 3) / 4;
440   default:
441     return height;
442   }
443 }
444
445 unsigned int CBaseTexture::GetBlockSize() const
446 {
447   switch (m_format)
448   {
449   case XB_FMT_DXT1:
450     return 8;
451   case XB_FMT_DXT3:
452   case XB_FMT_DXT5:
453   case XB_FMT_DXT5_YCoCg:
454     return 16;
455   case XB_FMT_A8:
456     return 1;
457   default:
458     return 4;
459   }
460 }
461
462 bool CBaseTexture::HasAlpha() const
463 {
464   return m_hasAlpha;
465 }