2 * Copyright (C) 2005-2008 Team XBMC
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)
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.
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
23 #include "windowing/WindowingFactory.h"
24 #include "utils/log.h"
25 #include "utils/URIUtils.h"
26 #include "pictures/DllImageLib.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"
35 /************************************************************************/
37 /************************************************************************/
38 CBaseTexture::CBaseTexture(unsigned int width, unsigned int height, unsigned int format)
45 m_loadedToGPU = false;
46 Allocate(width, height, format);
49 CBaseTexture::~CBaseTexture()
54 void CBaseTexture::Allocate(unsigned int width, unsigned int height, unsigned int format)
57 m_imageHeight = height;
61 m_textureWidth = m_imageWidth;
62 m_textureHeight = m_imageHeight;
64 if (m_format & XB_FMT_DXT_MASK)
65 while (GetPitch() < g_Windowing.GetMinDXTPitch())
66 m_textureWidth += GetBlockSize();
68 if (!g_Windowing.SupportsNPOT((m_format & XB_FMT_DXT_MASK) != 0))
70 m_textureWidth = PadPow2(m_textureWidth);
71 m_textureHeight = PadPow2(m_textureHeight);
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;
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);
87 m_pixels = new unsigned char[GetPitch() * GetRows()];
90 void CBaseTexture::Update(unsigned int width, unsigned int height, unsigned int pitch, unsigned int format, const unsigned char *pixels, bool loadToGPU)
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);
102 Allocate(width, height, format);
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);
109 if (srcPitch == dstPitch)
110 memcpy(m_pixels, pixels, srcPitch * std::min(srcRows, dstRows));
113 const unsigned char *src = pixels;
114 unsigned char* dst = m_pixels;
115 for (unsigned int y = 0; y < srcRows && y < dstRows; y++)
117 memcpy(dst, src, std::min(srcPitch, dstPitch));
129 void CBaseTexture::ClampToEdge()
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)
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++)
142 for (unsigned int x = imagePitch; x < texturePitch; x += blockSize)
143 memcpy(dst + x, src, blockSize);
148 if (imageRows < textureRows)
150 unsigned char *dst = m_pixels + imageRows * texturePitch;
151 for (unsigned int y = imageRows; y < textureRows; y++)
153 memcpy(dst, dst - texturePitch, texturePitch);
159 bool CBaseTexture::LoadFromFile(const CStdString& texturePath, unsigned int maxWidth, unsigned int maxHeight,
160 bool autoRotate, unsigned int *originalWidth, unsigned int *originalHeight)
162 if (URIUtils::GetExtension(texturePath).Equals(".dds"))
163 { // special case for DDS images
165 if (image.ReadFile(texturePath))
167 Update(image.GetWidth(), image.GetHeight(), 0, image.GetFormat(), image.GetData(), false);
173 #if defined(__APPLE__) && defined(__arm__)
175 UInt8 *imageBuff = NULL;
176 int64_t imageBuffSize = 0;
178 //open path and read data to buffer
179 //this handles advancedsettings.xml pathsubstitution
180 //and resulting networking
181 if (file.Open(texturePath, 0))
183 imageBuffSize =file.GetLength();
184 imageBuff = new UInt8[imageBuffSize];
185 imageBuffSize = file.Read(imageBuff, imageBuffSize);
190 CLog::Log(LOGERROR, "Texture manager unable to open file %s", texturePath.c_str());
194 if (imageBuffSize <= 0)
196 CLog::Log(LOGERROR, "Texture manager read texture file failed.");
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);
208 if (imageSource == nil)
210 CLog::Log(LOGERROR, "Texture manager unable to load file: %s", CSpecialProtocol::TranslatePath(texturePath).c_str());
216 CGImageRef image = CGImageSourceCreateImageAtIndex(imageSource, 0, NULL);
218 //get the orientation of the image for displaying it correctly
219 CFDictionaryRef imagePropertiesDictionary = CGImageSourceCopyPropertiesAtIndex(imageSource,0, NULL);
220 if (imagePropertiesDictionary != nil)
222 CFNumberRef orientation = (CFNumberRef)CFDictionaryGetValue(imagePropertiesDictionary, kCGImagePropertyOrientation);
223 if (orientation != nil)
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
232 case 3: //rotated 180°
235 case 6: //rotated 90°
238 case 8: //rotated 270°
243 //rotate the image if needed
244 if (rotationAngle != 0)
246 CLog::Log(LOGDEBUG,"Rotating the image about %i degrees", rotationAngle);
247 CGImageRef rotatedImage = CGImageCreateRotatedByAngle(image, (float)rotationAngle);
249 image = rotatedImage;
254 CFRelease(imageSource);
256 unsigned int width = CGImageGetWidth(image);
257 unsigned int height = CGImageGetHeight(image);
259 m_hasAlpha = (CGImageGetAlphaInfo(image) != kCGImageAlphaNone);
262 *originalWidth = width;
264 *originalHeight = height;
266 // check texture size limits and limit to screen size - preserving aspectratio of image
267 if ( width > g_Windowing.GetMaxTextureSize() || height > g_Windowing.GetMaxTextureSize() )
271 if ( width > height )
273 aspect = (float)width / (float)height;
274 width = g_Windowing.GetWidth();
275 height = (float)width / (float)aspect;
279 aspect = (float)height / (float)width;
280 height = g_Windowing.GetHeight();
281 width = (float)height / (float)aspect;
283 CLog::Log(LOGDEBUG, "Texture manager texture clamp:new texture size: %i x %i", width, height);
286 Allocate(width, height, XB_FMT_A8R8G8B8);
288 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
290 CGContextRef context = CGBitmapContextCreate(m_pixels,
291 width, height, 8, GetPitch(), colorSpace,
292 kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host);
294 CGColorSpaceRelease(colorSpace);
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));
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));
308 CGContextSetBlendMode(context, kCGBlendModeCopy);
310 //CGContextSetBlendMode(context, kCGBlendModeCopy);
311 CGContextDrawImage(context, CGRectMake(0, 0, width, height), image);
312 CGContextRelease(context);
313 CGImageRelease(image);
322 memset(&image, 0, sizeof(image));
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();
327 if(!dll.LoadImage(texturePath.c_str(), width, height, &image))
329 CLog::Log(LOGERROR, "Texture manager unable to load file: %s", texturePath.c_str());
333 m_hasAlpha = NULL != image.alpha;
335 Allocate(image.width, image.height, XB_FMT_A8R8G8B8);
336 if (autoRotate && image.exifInfo.Orientation)
337 m_orientation = image.exifInfo.Orientation - 1;
339 *originalWidth = image.originalwidth;
341 *originalHeight = image.originalheight;
343 unsigned int destPitch = GetPitch();
344 unsigned int srcPitch = ((image.width + 1)* 3 / 4) * 4; // bitmap row length is aligned to 4 bytes
346 for (unsigned int y = 0; y < m_imageHeight; y++)
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++)
356 *dst++ = (image.alpha) ? *alpha++ : 0xff;
360 dll.ReleaseImage(&image);
368 bool CBaseTexture::LoadFromMemory(unsigned int width, unsigned int height, unsigned int pitch, unsigned int format, unsigned char* pixels)
370 m_imageWidth = width;
371 m_imageHeight = height;
373 Update(width, height, pitch, format, pixels, false);
377 bool CBaseTexture::LoadPaletted(unsigned int width, unsigned int height, unsigned int pitch, unsigned int format, const unsigned char *pixels, const COLOR *palette)
379 if (pixels == NULL || palette == NULL)
382 Allocate(width, height, format);
384 for (unsigned int y = 0; y < m_imageHeight; y++)
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++)
390 COLOR col = palette[*src++];
401 unsigned int CBaseTexture::PadPow2(unsigned int x)
412 unsigned int CBaseTexture::GetPitch(unsigned int width) const
417 return ((width + 3) / 4) * 8;
420 case XB_FMT_DXT5_YCoCg:
421 return ((width + 3) / 4) * 16;
424 case XB_FMT_A8R8G8B8:
430 unsigned int CBaseTexture::GetRows(unsigned int height) const
435 return (height + 3) / 4;
438 case XB_FMT_DXT5_YCoCg:
439 return (height + 3) / 4;
445 unsigned int CBaseTexture::GetBlockSize() const
453 case XB_FMT_DXT5_YCoCg:
462 bool CBaseTexture::HasAlpha() const