texturemanager.mm (15769B)
1/* 2 * Gearboy - Nintendo Game Boy Emulator 3 * Copyright (C) 2012 Ignacio Sanchez 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 3 of the License, or 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 this program. If not, see http://www.gnu.org/licenses/ 17 * 18 */ 19 20#include "texturemanager.h" 21#import "../../../src/gearboy.h" 22#include <OpenGLES/ES1/glext.h> 23 24////////////////////////// 25////////////////////////// 26 27TextureManager::TextureManager() 28{ 29} 30 31////////////////////////// 32////////////////////////// 33 34TextureManager::~TextureManager() 35{ 36 UnloadAll(); 37} 38 39////////////////////////// 40////////////////////////// 41 42bool TextureManager::LoadTexture(Texture* pTexture, bool mipmaps) 43{ 44 Log("+++ TextureManager::LoadTexture Loading texture: %s\n", pTexture->m_strName); 45 46 char* ind = strrchr(pTexture->m_strName, '/'); 47 char* szName = ind + 1; 48 char szPath[256] = {0}; 49 strncpy(szPath, pTexture->m_strName, ind - pTexture->m_strName); 50 szPath[ind - pTexture->m_strName] = 0; 51 52 NSString * OCname = [NSString stringWithCString : szName encoding : [NSString defaultCStringEncoding]]; 53 NSString * OCpath = [NSString stringWithCString : szPath encoding : [NSString defaultCStringEncoding]]; 54 55 NSString * RSCpath = [[NSBundle mainBundle] pathForResource : OCname ofType : @"pvr" inDirectory : OCpath]; 56 57 FILE* pFile = fopen([RSCpath cStringUsingEncoding : 1], "r"); 58 59 if (pFile != NULL) 60 { 61 pTexture->m_bIsCompressed = true; 62 63 fseek(pFile, 0, SEEK_END); 64 int len = (int)ftell(pFile); 65 fseek(pFile, 0, SEEK_SET); 66 67 GLubyte* pBuffer = new GLubyte[len]; 68 fread(pBuffer, sizeof(GLubyte), len, pFile); 69 70 fclose(pFile); 71 72 int size = 0; 73 GLenum internalformat = 0; 74 75 for (int i = 16; i <= 1024; i *= 2) 76 { 77 if (((i * i) / 2) == len) 78 { 79 size = i; 80 internalformat = GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; 81 break; 82 } 83 84 if (((i * i) / 4) == len) 85 { 86 size = i; 87 internalformat = GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; 88 break; 89 } 90 } 91 92 if (size != 0) 93 { 94 pTexture->m_iWidth = size; 95 pTexture->m_iHeight = size; 96 97 glGenTextures(1, &pTexture->m_theTexture); 98 glBindTexture(GL_TEXTURE_2D, pTexture->m_theTexture); 99 glCompressedTexImage2D(GL_TEXTURE_2D, 0, internalformat, size, size, 0, len, pBuffer); 100 } 101 else 102 { 103 Log("@@@ TextureManager::LoadTexture PVR incorrect size: %s.pvr\n", pTexture->m_strName); 104 SafeDeleteArray(pBuffer); 105 return false; 106 } 107 108 SafeDeleteArray(pBuffer); 109 110 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 111 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 112 113 Log("+++ TextureManager::LoadTexture size %dx%d\n", pTexture->m_iWidth, pTexture->m_iHeight); 114 Log("+++ TextureManager::LoadTexture PVR texture loaded: %s.pvr\n", pTexture->m_strName); 115 116 return true; 117 } 118 else 119 { 120 Log("@@@ TextureManager::LoadTexture Unable to load PVR: %s.pvr\n", pTexture->m_strName); 121 122 pTexture->m_bIsCompressed = false; 123 124 char szPathPNG[256] = {0}; 125 strcpy(szPathPNG, "/"); 126 strcat(szPathPNG, pTexture->m_strName); 127 strcat(szPathPNG, ".png"); 128 129 CGImageRef spriteImage; 130 CGContextRef spriteContext; 131 GLubyte *spriteData; 132 int width, height; 133 134 char pathSize[2048] = {0}; 135 136 137 NSString* path =[[NSBundle mainBundle]resourcePath]; 138 139 [path getCString:pathSize maxLength:2048 encoding:NSUTF8StringEncoding]; 140 141 strcat(pathSize, szPathPNG); 142 143 // Creates a Core Graphics image from an image file 144 spriteImage = [UIImage imageWithContentsOfFile : [NSString stringWithUTF8String:pathSize]].CGImage; 145 146 147 if (spriteImage) 148 { 149 // Get the width and height of the image 150 pTexture->m_iWidth = width = (int)CGImageGetWidth(spriteImage); 151 pTexture->m_iHeight = height = (int)CGImageGetHeight(spriteImage); 152 // Texture dimensions must be a power of 2. If you write an application that allows users to supply an image, 153 // you'll want to add code that checks the dimensions and takes appropriate action if they are not a power of 2. 154 155 // Allocated memory needed for the bitmap context 156 spriteData = (GLubyte *) calloc(width * height * 4, 1); 157 // Uses the bitmatp creation function provided by the Core Graphics framework. 158 spriteContext = CGBitmapContextCreate(spriteData, width, height, 8, width * 4, CGImageGetColorSpace(spriteImage), kCGImageAlphaPremultipliedLast); 159 // After you create the context, you can draw the sprite image to the context. 160 CGContextDrawImage(spriteContext, CGRectMake(0.0, 0.0, (CGFloat) width, (CGFloat) height), spriteImage); 161 // You don't need the context at this point, so you need to release it to avoid memory leaks. 162 CGContextRelease(spriteContext); 163 164 // Use OpenGL ES to generate a name for the texture. 165 glGenTextures(1, &pTexture->m_theTexture); 166 // Bind the texture name. 167 glBindTexture(GL_TEXTURE_2D, pTexture->m_theTexture); 168 // Speidfy a 2D texture image, provideing the a pointer to the image data in memory 169 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, spriteData); 170 // Release the image data 171 172 if (mipmaps) 173 { 174 Log("+++ TextureManager::LoadTexture generating mipmaps...\n"); 175 176 GLubyte *prevImage = 0; 177 GLubyte *newImage = 0; 178 179 int level = 1; 180 prevImage = &spriteData[0]; 181 182 while (width > 1 && height > 1) 183 { 184 int newWidth, newHeight; 185 186 // Generate the next mipmap level 187 GenMipMap2D(prevImage, &newImage, width, height, 188 &newWidth, &newHeight, level); 189 190 // Load the mipmap level 191 glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, 192 newWidth, newHeight, 0, GL_RGBA, 193 GL_UNSIGNED_BYTE, newImage); 194 195 // Free the previous image 196 free(prevImage); 197 198 // Set the previous image for the next iteration 199 prevImage = newImage; 200 level++; 201 202 // Half the width and height 203 width = newWidth; 204 height = newHeight; 205 } 206 207 free(newImage); 208 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); 209 } 210 else 211 { 212 free(spriteData); 213 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 214 } 215 216 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 217 218 Log("+++ TextureManager::LoadTexture size %dx%d\n", pTexture->m_iWidth, pTexture->m_iHeight); 219 Log("+++ TextureManager::LoadTexture PNG texture loaded: %s.png\n", pTexture->m_strName); 220 return true; 221 } 222 else 223 { 224 Log("@@@ TextureManager::LoadTexture Unable to load texture: %s\n", pTexture->m_strName); 225 Log("@@@ TextureManager::LoadTexture Defaulting to missing texture\n"); 226 227 pTexture->m_bIsCompressed = false; 228 229 CGImageRef spriteImage; 230 CGContextRef spriteContext; 231 GLubyte *spriteData; 232 int width, height; 233 234 // Creates a Core Graphics image from an image file 235 spriteImage = [UIImage imageNamed : [NSString stringWithCString : "missing_texture.png" encoding : [NSString defaultCStringEncoding]]].CGImage; 236 237 if (spriteImage) 238 { 239 // Get the width and height of the image 240 pTexture->m_iWidth = width = (int)CGImageGetWidth(spriteImage); 241 pTexture->m_iHeight = height = (int)CGImageGetHeight(spriteImage); 242 // Texture dimensions must be a power of 2. If you write an application that allows users to supply an image, 243 // you'll want to add code that checks the dimensions and takes appropriate action if they are not a power of 2. 244 245 // Allocated memory needed for the bitmap context 246 spriteData = (GLubyte *) calloc(width * height * 4, 1); 247 // Uses the bitmatp creation function provided by the Core Graphics framework. 248 spriteContext = CGBitmapContextCreate(spriteData, width, height, 8, width * 4, CGImageGetColorSpace(spriteImage), kCGImageAlphaPremultipliedLast); 249 // After you create the context, you can draw the sprite image to the context. 250 CGContextDrawImage(spriteContext, CGRectMake(0.0, 0.0, (CGFloat) width, (CGFloat) height), spriteImage); 251 // You don't need the context at this point, so you need to release it to avoid memory leaks. 252 CGContextRelease(spriteContext); 253 254 // Use OpenGL ES to generate a name for the texture. 255 glGenTextures(1, &pTexture->m_theTexture); 256 // Bind the texture name. 257 glBindTexture(GL_TEXTURE_2D, pTexture->m_theTexture); 258 // Speidfy a 2D texture image, provideing the a pointer to the image data in memory 259 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, spriteData); 260 // Release the image data 261 262 free(spriteData); 263 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 264 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 265 266 Log("+++ TextureManager::LoadTexture missing_texture.png loaded\n"); 267 return true; 268 } 269 else 270 { 271 Log("@@@ TextureManager::LoadTexture Unable to load default missing texture: missing_texture.png\n"); 272 273 return false; 274 } 275 } 276 } 277} 278 279////////////////////////// 280////////////////////////// 281 282GLboolean TextureManager::GenMipMap2D(GLubyte *src, GLubyte **dst, int srcWidth, int srcHeight, int *dstWidth, int *dstHeight, int level) 283{ 284 int x, y; 285 int texelSize = 4; 286 287 *dstWidth = srcWidth / 2; 288 if (*dstWidth <= 0) 289 *dstWidth = 1; 290 291 *dstHeight = srcHeight / 2; 292 if (*dstHeight <= 0) 293 *dstHeight = 1; 294 295 *dst = (GLubyte*) malloc(sizeof(GLubyte) * texelSize * (*dstWidth) * (*dstHeight)); 296 if (*dst == NULL) 297 return GL_FALSE; 298 299 for (y = 0; y < *dstHeight; y++) 300 { 301 for (x = 0; x < *dstWidth; x++) 302 { 303 int srcIndex[4]; 304 float r = 0.0f, g = 0.0f, b = 0.0f, a = 0.0f; 305 int sample; 306 307 // Compute the offsets for 2x2 grid of pixels in previous 308 // image to perform box filter 309 srcIndex[0] = (((y * 2) * srcWidth) + (x * 2)) * texelSize; 310 srcIndex[1] = (((y * 2) * srcWidth) + (x * 2 + 1)) * texelSize; 311 srcIndex[2] = ((((y * 2) + 1) * srcWidth) + (x * 2)) * texelSize; 312 srcIndex[3] = ((((y * 2) + 1) * srcWidth) + (x * 2 + 1)) * texelSize; 313 314 // Sum all pixels 315 for (sample = 0; sample < 4; sample++) 316 { 317 r += src[srcIndex[sample]]; 318 g += src[srcIndex[sample] + 1]; 319 b += src[srcIndex[sample] + 2]; 320 a += src[srcIndex[sample] + 3]; 321 } 322 323 // Average results 324 r /= 4.0; 325 g /= 4.0; 326 b /= 4.0; 327 a /= 4.0; 328 329 // Store resulting pixels 330 (*dst)[ (y * (*dstWidth) + x) * texelSize ] = (GLubyte) (r); 331 (*dst)[ (y * (*dstWidth) + x) * texelSize + 1] = (GLubyte) (g); 332 (*dst)[ (y * (*dstWidth) + x) * texelSize + 2] = (GLubyte) (b); 333 (*dst)[ (y * (*dstWidth) + x) * texelSize + 3] = (GLubyte) (a); 334 } 335 } 336 337 Log("+++ TextureManager::GenMipMap2D level %d: %dx%d\n", level, *dstWidth, *dstHeight); 338 339 return GL_TRUE; 340} 341 342////////////////////////// 343////////////////////////// 344 345Texture* TextureManager::GetTexture(const char* strTextureName, bool mipmaps) 346{ 347 Log("+++ TextureManager::GetTexture Searching for %s\n", strTextureName); 348 349 std::string stringTextureName(strTextureName); 350 351 TTextureMapIterator lowerBound = m_TextureMap.lower_bound(stringTextureName); 352 353 ///--- ya estaba 354 if (lowerBound != m_TextureMap.end() && 355 !(m_TextureMap.key_comp()(stringTextureName, lowerBound->first))) 356 { 357 Log("+++ TextureManager::GetTexture Already in use\n"); 358 359 return lowerBound->second; 360 } 361 ///--- no estaba 362 else 363 { 364 Texture* temp = new Texture(); 365 strcpy(temp->m_strName, strTextureName); 366 367 if (LoadTexture(temp, mipmaps)) 368 { 369 TTextureMapPair insertPair(stringTextureName, temp); 370 371 TTextureMapIterator result = m_TextureMap.insert(lowerBound, insertPair); 372 373 if (result->second) 374 { 375 ////--- se ha insertado la textura 376 Log("+++ TextureManager::GetTexture New texture inserted\n"); 377 return temp; 378 } 379 else 380 { 381 ///--- ya existía??? nunca debe entrar aqui 382 Log("@@@ TextureManager::GetTexture FATAL ERROR @@@\n"); 383 } 384 } 385 else 386 { 387 ///--- no se pudo cargar la textura 388 Log("@@@ TextureManager::GetTexture Unable to load texture %s\n", strTextureName); 389 } 390 391 SafeDelete(temp); 392 return NULL; 393 } 394} 395 396////////////////////////// 397////////////////////////// 398 399bool TextureManager::UnloadTexture(const char* strTextureName) 400{ 401 std::string stringTextureName(strTextureName); 402 403 TTextureMapIterator itor = m_TextureMap.find(stringTextureName); 404 405 ///--- estaba 406 if (itor != m_TextureMap.end()) 407 { 408 Log("+++ TextureManager::UnloadTexture Deleting texture: %s\n", itor->second->m_strName); 409 410 glDeleteTextures(1, &itor->second->m_theTexture); 411 412 SafeDelete(itor->second); 413 414 m_TextureMap.erase(itor); 415 416 return true; 417 } 418 ///--- no estaba 419 else 420 { 421 Log("@@@ TextureManager::UnloadTexture The texture was not found\n"); 422 return false; 423 } 424} 425 426////////////////////////// 427////////////////////////// 428 429bool TextureManager::UnloadTexture(Texture* pTexture) 430{ 431 return UnloadTexture(pTexture->m_strName); 432} 433 434////////////////////////// 435////////////////////////// 436 437int TextureManager::UnloadAll(void) 438{ 439 for (TTextureMapIterator i = m_TextureMap.begin(); i != m_TextureMap.end(); i++) 440 { 441 Log("+++ TextureManager::UnloadAll Deleting texture: %s\n", i->second->m_strName); 442 443 glDeleteTextures(1, &i->second->m_theTexture); 444 445 SafeDelete(i->second); 446 } 447 448 m_TextureMap.clear(); 449 450 return 0; 451} 452