fixed #642: CCRenderTexture::saveBuffer() works ok on iOS

This commit is contained in:
minggo 2011-08-08 11:01:47 +08:00
parent 08b25229ac
commit 41ea2e98a1
2 changed files with 186 additions and 62 deletions

View File

@ -105,54 +105,53 @@ static bool _initPremultipliedATextureWithImage(CGImageRef image, NSUInteger POT
size_t bpp = CGImageGetBitsPerComponent(image); size_t bpp = CGImageGetBitsPerComponent(image);
colorSpace = CGImageGetColorSpace(image); colorSpace = CGImageGetColorSpace(image);
if(colorSpace) { if(colorSpace)
{
if(hasAlpha || bpp >= 8) if(hasAlpha || bpp >= 8)
{
pixelFormat = kCCTexture2DPixelFormat_Default; pixelFormat = kCCTexture2DPixelFormat_Default;
else { }
//CCLOG(@"cocos2d: CCTexture2D: Using RGB565 texture since image has no alpha"); else
{
pixelFormat = kCCTexture2DPixelFormat_RGB565; pixelFormat = kCCTexture2DPixelFormat_RGB565;
} }
} else { }
else
{
// NOTE: No colorspace means a mask image // NOTE: No colorspace means a mask image
//CCLOG(@"cocos2d: CCTexture2D: Using A8 texture since image is a mask");
pixelFormat = kCCTexture2DPixelFormat_A8; pixelFormat = kCCTexture2DPixelFormat_A8;
} }
imageSize.width = CGImageGetWidth(image); imageSize.width = CGImageGetWidth(image);
imageSize.height = CGImageGetHeight(image); imageSize.height = CGImageGetHeight(image);
//imageSize = CGSizeMake(CGImageGetWidth(image), CGImageGetHeight(image));
// Create the bitmap graphics context // Create the bitmap graphics context
switch(pixelFormat) { switch(pixelFormat)
{
case kCCTexture2DPixelFormat_RGBA8888: case kCCTexture2DPixelFormat_RGBA8888:
case kCCTexture2DPixelFormat_RGBA4444: case kCCTexture2DPixelFormat_RGBA4444:
case kCCTexture2DPixelFormat_RGB5A1: case kCCTexture2DPixelFormat_RGB5A1:
colorSpace = CGColorSpaceCreateDeviceRGB(); colorSpace = CGColorSpaceCreateDeviceRGB();
//data = malloc(POTHigh * POTWide * 4);
data = new unsigned char[POTHigh * POTWide * 4]; data = new unsigned char[POTHigh * POTWide * 4];
info = hasAlpha ? kCGImageAlphaPremultipliedLast : kCGImageAlphaNoneSkipLast; info = hasAlpha ? kCGImageAlphaPremultipliedLast : kCGImageAlphaNoneSkipLast;
// info = kCGImageAlphaPremultipliedLast; // issue #886. This patch breaks BMP images.
context = CGBitmapContextCreate(data, POTWide, POTHigh, 8, 4 * POTWide, colorSpace, info | kCGBitmapByteOrder32Big); context = CGBitmapContextCreate(data, POTWide, POTHigh, 8, 4 * POTWide, colorSpace, info | kCGBitmapByteOrder32Big);
CGColorSpaceRelease(colorSpace); CGColorSpaceRelease(colorSpace);
break; break;
case kCCTexture2DPixelFormat_RGB565: case kCCTexture2DPixelFormat_RGB565:
colorSpace = CGColorSpaceCreateDeviceRGB(); colorSpace = CGColorSpaceCreateDeviceRGB();
//data = malloc(POTHigh * POTWide * 4);
data = new unsigned char[POTHigh * POTWide * 4]; data = new unsigned char[POTHigh * POTWide * 4];
info = kCGImageAlphaNoneSkipLast; info = kCGImageAlphaNoneSkipLast;
context = CGBitmapContextCreate(data, POTWide, POTHigh, 8, 4 * POTWide, colorSpace, info | kCGBitmapByteOrder32Big); context = CGBitmapContextCreate(data, POTWide, POTHigh, 8, 4 * POTWide, colorSpace, info | kCGBitmapByteOrder32Big);
CGColorSpaceRelease(colorSpace); CGColorSpaceRelease(colorSpace);
break; break;
case kCCTexture2DPixelFormat_A8: case kCCTexture2DPixelFormat_A8:
//data = malloc(POTHigh * POTWide);
data = new unsigned char[POTHigh * POTWide]; data = new unsigned char[POTHigh * POTWide];
info = kCGImageAlphaOnly; info = kCGImageAlphaOnly;
context = CGBitmapContextCreate(data, POTWide, POTHigh, 8, POTWide, NULL, info); context = CGBitmapContextCreate(data, POTWide, POTHigh, 8, POTWide, NULL, info);
break; break;
default: default:
//[NSException raise:NSInternalInconsistencyException format:@"Invalid pixel format"];
return false; return false;
} }
@ -162,82 +161,79 @@ static bool _initPremultipliedATextureWithImage(CGImageRef image, NSUInteger POT
rect.origin.x = 0; rect.origin.x = 0;
rect.origin.y = 0; rect.origin.y = 0;
//CGContextClearRect(context, CGRectMake(0, 0, POTWide, POTHigh));
CGContextClearRect(context, rect); CGContextClearRect(context, rect);
CGContextTranslateCTM(context, 0, POTHigh - imageSize.height); CGContextTranslateCTM(context, 0, POTHigh - imageSize.height);
//CGContextDrawImage(context, CGRectMake(0, 0, CGImageGetWidth(image), CGImageGetHeight(image)), image);
rect.size.width = CGImageGetWidth(image); rect.size.width = CGImageGetWidth(image);
rect.size.height = CGImageGetHeight(image); rect.size.height = CGImageGetHeight(image);
rect.origin.x = 0; rect.origin.x = 0;
rect.origin.y = 0; rect.origin.y = 0;
//CGContextDrawImage(context, CGRectMake(0, 0, CGImageGetWidth(image), CGImageGetHeight(image)), image);
CGContextDrawImage(context, rect, image); CGContextDrawImage(context, rect, image);
// Repack the pixel data into the right format // Repack the pixel data into the right format
if(pixelFormat == kCCTexture2DPixelFormat_RGB565) { if(pixelFormat == kCCTexture2DPixelFormat_RGB565)
{
//Convert "RRRRRRRRRGGGGGGGGBBBBBBBBAAAAAAAA" to "RRRRRGGGGGGBBBBB" //Convert "RRRRRRRRRGGGGGGGGBBBBBBBBAAAAAAAA" to "RRRRRGGGGGGBBBBB"
//tempData = malloc(POTHigh * POTWide * 2);
tempData = new unsigned char[POTHigh * POTWide * 2]; tempData = new unsigned char[POTHigh * POTWide * 2];
inPixel32 = (unsigned int*)data; inPixel32 = (unsigned int*)data;
outPixel16 = (unsigned short*)tempData; outPixel16 = (unsigned short*)tempData;
for(i = 0; i < POTWide * POTHigh; ++i, ++inPixel32) for(i = 0; i < POTWide * POTHigh; ++i, ++inPixel32)
{
*outPixel16++ = ((((*inPixel32 >> 0) & 0xFF) >> 3) << 11) | ((((*inPixel32 >> 8) & 0xFF) >> 2) << 5) | ((((*inPixel32 >> 16) & 0xFF) >> 3) << 0); *outPixel16++ = ((((*inPixel32 >> 0) & 0xFF) >> 3) << 11) | ((((*inPixel32 >> 8) & 0xFF) >> 2) << 5) | ((((*inPixel32 >> 16) & 0xFF) >> 3) << 0);
//free(data); }
delete[] data; delete[] data;
data = tempData; data = tempData;
} }
else if (pixelFormat == kCCTexture2DPixelFormat_RGBA4444) { else if (pixelFormat == kCCTexture2DPixelFormat_RGBA4444)
{
//Convert "RRRRRRRRRGGGGGGGGBBBBBBBBAAAAAAAA" to "RRRRGGGGBBBBAAAA" //Convert "RRRRRRRRRGGGGGGGGBBBBBBBBAAAAAAAA" to "RRRRGGGGBBBBAAAA"
//tempData = malloc(POTHigh * POTWide * 2);
tempData = new unsigned char[POTHigh * POTWide * 2]; tempData = new unsigned char[POTHigh * POTWide * 2];
inPixel32 = (unsigned int*)data; inPixel32 = (unsigned int*)data;
outPixel16 = (unsigned short*)tempData; outPixel16 = (unsigned short*)tempData;
for(i = 0; i < POTWide * POTHigh; ++i, ++inPixel32) for(i = 0; i < POTWide * POTHigh; ++i, ++inPixel32)
{
*outPixel16++ = *outPixel16++ =
((((*inPixel32 >> 0) & 0xFF) >> 4) << 12) | // R ((((*inPixel32 >> 0) & 0xFF) >> 4) << 12) | // R
((((*inPixel32 >> 8) & 0xFF) >> 4) << 8) | // G ((((*inPixel32 >> 8) & 0xFF) >> 4) << 8) | // G
((((*inPixel32 >> 16) & 0xFF) >> 4) << 4) | // B ((((*inPixel32 >> 16) & 0xFF) >> 4) << 4) | // B
((((*inPixel32 >> 24) & 0xFF) >> 4) << 0); // A ((((*inPixel32 >> 24) & 0xFF) >> 4) << 0); // A
}
//free(data);
delete[] data; delete[] data;
data = tempData; data = tempData;
} }
else if (pixelFormat == kCCTexture2DPixelFormat_RGB5A1) { else if (pixelFormat == kCCTexture2DPixelFormat_RGB5A1)
{
//Convert "RRRRRRRRRGGGGGGGGBBBBBBBBAAAAAAAA" to "RRRRRGGGGGBBBBBA" //Convert "RRRRRRRRRGGGGGGGGBBBBBBBBAAAAAAAA" to "RRRRRGGGGGBBBBBA"
//tempData = malloc(POTHigh * POTWide * 2);
tempData = new unsigned char[POTHigh * POTWide * 2]; tempData = new unsigned char[POTHigh * POTWide * 2];
inPixel32 = (unsigned int*)data; inPixel32 = (unsigned int*)data;
outPixel16 = (unsigned short*)tempData; outPixel16 = (unsigned short*)tempData;
for(i = 0; i < POTWide * POTHigh; ++i, ++inPixel32) for(i = 0; i < POTWide * POTHigh; ++i, ++inPixel32)
{
*outPixel16++ = *outPixel16++ =
((((*inPixel32 >> 0) & 0xFF) >> 3) << 11) | // R ((((*inPixel32 >> 0) & 0xFF) >> 3) << 11) | // R
((((*inPixel32 >> 8) & 0xFF) >> 3) << 6) | // G ((((*inPixel32 >> 8) & 0xFF) >> 3) << 6) | // G
((((*inPixel32 >> 16) & 0xFF) >> 3) << 1) | // B ((((*inPixel32 >> 16) & 0xFF) >> 3) << 1) | // B
((((*inPixel32 >> 24) & 0xFF) >> 7) << 0); // A ((((*inPixel32 >> 24) & 0xFF) >> 7) << 0); // A
}
//free(data);
delete[] data; delete[] data;
data = tempData; data = tempData;
} }
//self = [self initWithData:data pixelFormat:pixelFormat pixelsWide:POTWide pixelsHigh:POTHigh contentSize:imageSize];
// should be after calling super init // should be after calling super init
//s_imageInfo.isPremultipliedAlpha = (info == kCGImageAlphaPremultipliedLast || info == kCGImageAlphaPremultipliedFirst);
pImageInfo->isPremultipliedAlpha = true; pImageInfo->isPremultipliedAlpha = true;
pImageInfo->hasAlpha = true; pImageInfo->hasAlpha = true;
//s_imageInfo.hasAlpha = hasAlpha;
pImageInfo->bitsPerComponent = bpp; pImageInfo->bitsPerComponent = bpp;
pImageInfo->width = imageSize.width; pImageInfo->width = imageSize.width;
pImageInfo->height = imageSize.height; pImageInfo->height = imageSize.height;
if (pImageInfo->data) if (pImageInfo->data)
{ {
delete []pImageInfo->data; delete [] pImageInfo->data;
} }
pImageInfo->data = data; pImageInfo->data = data;
@ -247,25 +243,17 @@ static bool _initPremultipliedATextureWithImage(CGImageRef image, NSUInteger POT
static bool _initWithImage(CGImageRef CGImage, tImageInfo *pImageinfo) static bool _initWithImage(CGImageRef CGImage, tImageInfo *pImageinfo)
{ {
NSUInteger POTWide, POTHigh; NSUInteger POTWide, POTHigh;
if(CGImage == NULL) { if(CGImage == NULL)
//CCLOG(@"cocos2d: CCTexture2D. Can't create Texture. ccxImage is nil"); {
return false; return false;
} }
//CCConfiguration *conf = [CCConfiguration sharedConfiguration];
// cocos2d::CCConfiguration *conf = cocos2d::CCConfiguration::sharedConfiguration();
POTWide = CGImageGetWidth(CGImage); POTWide = CGImageGetWidth(CGImage);
POTHigh = CGImageGetHeight(CGImage); POTHigh = CGImageGetHeight(CGImage);
// unsigned maxTextureSize = conf->getMaxTextureSize(); // always load premultiplied images
// if( POTHigh > maxTextureSize || POTWide > maxTextureSize ) {
// return false;
// }
// always load premultiplied images
_initPremultipliedATextureWithImage(CGImage, POTWide, POTHigh, pImageinfo); _initPremultipliedATextureWithImage(CGImage, POTWide, POTHigh, pImageinfo);
return true; return true;
@ -278,7 +266,7 @@ static bool _initWithFile(const char* path, tImageInfo *pImageinfo)
UIImage *png; UIImage *png;
bool ret; bool ret;
// convert jpg to png before loading the texture // convert jpg to png before loading the texture
NSString *fullPath = [NSString stringWithUTF8String:path]; NSString *fullPath = [NSString stringWithUTF8String:path];
jpg = [[UIImage alloc] initWithContentsOfFile: fullPath]; jpg = [[UIImage alloc] initWithContentsOfFile: fullPath];
@ -298,9 +286,10 @@ static bool _initWithData(void * pBuffer, int length, tImageInfo *pImageinfo)
{ {
bool ret = false; bool ret = false;
if (pBuffer) { if (pBuffer)
CGImageRef CGImage; {
NSData *data; CGImageRef CGImage;
NSData *data;
data = [NSData dataWithBytes:pBuffer length:length]; data = [NSData dataWithBytes:pBuffer length:length];
CGImage = [[UIImage imageWithData:data] CGImage]; CGImage = [[UIImage imageWithData:data] CGImage];
@ -317,14 +306,18 @@ static bool _isValidFontName(const char *fontName)
NSString *fontNameNS = [NSString stringWithUTF8String:fontName]; NSString *fontNameNS = [NSString stringWithUTF8String:fontName];
for (NSString *familiName in [UIFont familyNames]) { for (NSString *familiName in [UIFont familyNames])
if ([familiName isEqualToString:fontNameNS]) { {
if ([familiName isEqualToString:fontNameNS])
{
ret = true; ret = true;
goto out; goto out;
} }
for(NSString *font in [UIFont fontNamesForFamilyName: familiName]){ for(NSString *font in [UIFont fontNamesForFamilyName: familiName])
if ([font isEqualToString: fontNameNS]){ {
if ([font isEqualToString: fontNameNS])
{
ret = true; ret = true;
goto out; goto out;
} }
@ -429,7 +422,6 @@ static bool _initWithString(const char * pText, cocos2d::CCImage::ETextAlign eAl
#if CC_FONT_LABEL_SUPPORT #if CC_FONT_LABEL_SUPPORT
else // ZFont class else // ZFont class
{ {
//[str drawInRect:CGRectMake(0, 0, dim.width, dim.height) withZFont:font lineBreakMode:UILineBreakModeWordWrap alignment:align];
[FontLabelStringDrawingHelper drawInRect:str rect:CGRectMake(0, 0, dim.width, dim.height) withZFont:font lineBreakMode:UILineBreakModeWordWrap alignment:align]; [FontLabelStringDrawingHelper drawInRect:str rect:CGRectMake(0, 0, dim.width, dim.height) withZFont:font lineBreakMode:UILineBreakModeWordWrap alignment:align];
} }
#endif #endif
@ -485,20 +477,72 @@ bool CCImage::initWithImageData(void * pData,
do do
{ {
CC_BREAK_IF(! pData || nDataLen <= 0); CC_BREAK_IF(! pData || nDataLen <= 0);
bRet = _initWithData(pData, nDataLen, &info); if (eFmt == kFmtRawData)
{
bRet = _initWithRawData(pData, nDataLen, nWidth, nHeight, nBitsPerComponent);
}
else // init with png or jpg file data
{
bRet = _initWithData(pData, nDataLen, &info);
if (bRet)
{
m_nHeight = (short)info.height;
m_nWidth = (short)info.width;
m_nBitsPerComponent = info.bitsPerComponent;
m_bHasAlpha = info.hasAlpha;
m_bPreMulti = info.isPremultipliedAlpha;
m_pData = info.data;
}
}
} while (0); } while (0);
if (bRet)
{
m_nHeight = (short)info.height;
m_nWidth = (short)info.width;
m_nBitsPerComponent = info.bitsPerComponent;
m_bHasAlpha = info.hasAlpha;
m_bPreMulti = info.isPremultipliedAlpha;
m_pData = info.data;
}
return bRet; return bRet;
} }
bool CCImage::_initWithRawData(void *pData, int nDatalen, int nWidth, int nHeight, int nBitsPerComponent)
{
bool bRet = false;
do
{
CC_BREAK_IF(0 == nWidth || 0 == nHeight);
m_nBitsPerComponent = nBitsPerComponent;
m_nHeight = (short)nHeight;
m_nWidth = (short)nWidth;
m_bHasAlpha = true;
// only RGBA8888 surported
int nBytesPerComponent = 4;
int nSize = nHeight * nWidth * nBytesPerComponent;
m_pData = new unsigned char[nSize];
CC_BREAK_IF(! m_pData);
memcpy(m_pData, pData, nSize);
bRet = true;
} while (0);
return bRet;
}
bool CCImage::_initWithJpgData(void *pData, int nDatalen)
{
assert(0);
}
bool CCImage::_initWithPngData(void *pData, int nDatalen)
{
assert(0);
}
bool CCImage::_saveImageToPNG(const char *pszFilePath, bool bIsToRGB)
{
assert(0);
}
bool CCImage::_saveImageToJPG(const char *pszFilePath)
{
assert(0);
}
bool CCImage::initWithString( bool CCImage::initWithString(
const char * pText, const char * pText,
int nWidth /* = 0 */, int nWidth /* = 0 */,
@ -527,7 +571,87 @@ bool CCImage::initWithString(
bool CCImage::saveToFile(const char *pszFilePath, bool bIsToRGB) bool CCImage::saveToFile(const char *pszFilePath, bool bIsToRGB)
{ {
return false; bool saveToPNG = false;
bool needToCopyPixels = false;
std::string filePath(pszFilePath);
if (std::string::npos != filePath.find(".png"))
{
saveToPNG = true;
}
int bitsPerComponent = 8;
int bitsPerPixel = m_bHasAlpha ? 32 : 24;
if ((! saveToPNG) || bIsToRGB)
{
bitsPerPixel = 24;
}
int bytesPerRow = (bitsPerPixel/8) * m_nWidth;
int myDataLength = bytesPerRow * m_nHeight;
unsigned char *pixels = m_pData;
// The data has alpha channel, and want to save it with an RGB png file,
// or want to save as jpg, remove the alpha channel.
if ((saveToPNG && m_bHasAlpha && bIsToRGB)
|| (! saveToPNG))
{
pixels = new unsigned char[myDataLength];
for (int i = 0; i < m_nHeight; ++i)
{
for (int j = 0; j < m_nWidth; ++j)
{
pixels[(i * m_nWidth + j) * 3] = m_pData[(i * m_nWidth + j) * 4];
pixels[(i * m_nWidth + j) * 3 + 1] = m_pData[(i * m_nWidth + j) * 4 + 1];
pixels[(i * m_nWidth + j) * 3 + 2] = m_pData[(i * m_nWidth + j) * 4 + 2];
}
}
needToCopyPixels = true;
}
// make data provider with data.
CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault;
if (saveToPNG && m_bHasAlpha && (! bIsToRGB))
{
bitmapInfo |= kCGImageAlphaPremultipliedLast;
}
CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, pixels, myDataLength, NULL);
CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
CGImageRef iref = CGImageCreate(m_nWidth, m_nHeight,
bitsPerComponent, bitsPerPixel, bytesPerRow,
colorSpaceRef, bitmapInfo, provider,
NULL, false,
kCGRenderingIntentDefault);
UIImage* image = [[UIImage alloc] initWithCGImage:iref];
CGImageRelease(iref);
CGColorSpaceRelease(colorSpaceRef);
CGDataProviderRelease(provider);
NSData *data;
if (saveToPNG)
{
data = UIImagePNGRepresentation(image);
}
else
{
data = UIImageJPEGRepresentation(image, 1.0f);
}
[data writeToFile:[NSString stringWithUTF8String:pszFilePath] atomically:YES];
[image release];
if (needToCopyPixels)
{
delete [] pixels;
}
return true;
} }
NS_CC_END; NS_CC_END;

View File

@ -1 +1 @@
089538e21098633ff475d29aa721c886c2eed848 b21283100ca056134301332a759b4d1ad747f4e2