diff --git a/cocos2dx/include/CCRenderTexture.h b/cocos2dx/include/CCRenderTexture.h index 99a6d10b3e..15c6bc4c77 100644 --- a/cocos2dx/include/CCRenderTexture.h +++ b/cocos2dx/include/CCRenderTexture.h @@ -91,6 +91,9 @@ public: /* get buffer as UIImage, can only save a render buffer which has a RGBA8888 pixel format */ CCData *getUIImageAsDataFromBuffer(int format); + + bool getUIImageFromBuffer(CCImage *pImage); + protected: GLuint m_uFBO; GLint m_nOldFBO; diff --git a/cocos2dx/misc_nodes/CCRenderTexture.cpp b/cocos2dx/misc_nodes/CCRenderTexture.cpp index 146fc21267..8024a5e9ef 100644 --- a/cocos2dx/misc_nodes/CCRenderTexture.cpp +++ b/cocos2dx/misc_nodes/CCRenderTexture.cpp @@ -29,7 +29,7 @@ THE SOFTWARE. #include "platform/platform.h" #include "CCImage.h" #include "support/ccUtils.h" - +#include "CCFileUtils.h" #include "CCGL.h" namespace cocos2d { @@ -172,13 +172,13 @@ void CCRenderTexture::begin() glGetIntegerv(CC_GL_FRAMEBUFFER_BINDING, &m_nOldFBO); ccglBindFramebuffer(CC_GL_FRAMEBUFFER, m_uFBO);//Will direct drawing to the frame buffer created above - // Issue #1145 - // There is no need to enable the default GL states here - // but since CCRenderTexture is mostly used outside the "render" loop - // these states needs to be enabled. - // Since this bug was discovered in API-freeze (very close of 1.0 release) - // This bug won't be fixed to prevent incompatibilities with code. - // + // Issue #1145 + // There is no need to enable the default GL states here + // but since CCRenderTexture is mostly used outside the "render" loop + // these states needs to be enabled. + // Since this bug was discovered in API-freeze (very close of 1.0 release) + // This bug won't be fixed to prevent incompatibilities with code. + // // If you understand the above mentioned message, then you can comment the following line // and enable the gl states manually, in case you need them. @@ -222,25 +222,70 @@ bool CCRenderTexture::saveBuffer(const char *name) } bool CCRenderTexture::saveBuffer(const char *fileName, int format) { - CC_UNUSED_PARAM(fileName); - CC_UNUSED_PARAM(format); - bool bRet = false; -//@ todo CCRenderTexture::saveBuffer -// UIImage *myImage = this->getUIImageFromBuffer(format); -// CCMutableArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); -// CCString *documentsDirectory = [paths objectAtIndex:0]; -// CCString *fullPath = [documentsDirectory stringByAppendingPathComponent:fileName]; -// CCData * data = this->getUIImageAsDataFromBuffer(format); -// if (data) -// { -// bRet = data->writeToFile(path, true); -// delete data; -// bRet = true; -// } + bool bRet = false; + CCAssert(format == kCCImageFormatJPG || format == kCCImageFormatPNG, + @"the image can only be saved as JPG or PNG format"); + + CCImage *pImage = new CCImage(); + if (pImage != NULL && getUIImageFromBuffer(pImage)) + { + std::string fullpath = CCFileUtils::getWriteablePath() + fileName; + if (kCCImageFormatPNG == format) + { + fullpath += ".png"; + } + else + { + fullpath += ".jpg"; + } + + bRet = pImage->saveToFile(fullpath.c_str()); + } + + CC_SAFE_DELETE(pImage); + return bRet; } +/* get buffer as UIImage */ +bool CCRenderTexture::getUIImageFromBuffer(CCImage *pImage) +{ + if (NULL == pImage) + { + return false; + } + + GLubyte * pBuffer = NULL; + bool bRet = false; + + do + { + CCAssert(m_ePixelFormat == kCCTexture2DPixelFormat_RGBA8888,@"only RGBA8888 can be saved as image"); + + CCSize s = m_pTexture->getContentSizeInPixels(); + int tx = (int)s.width; + int ty = (int)s.height; + + CC_BREAK_IF(! (pBuffer = new GLubyte[tx * ty * 4])); + + this->begin(); + glPixelStorei(GL_PACK_ALIGNMENT, 1); + glReadPixels(0,0,tx,ty,GL_RGBA,GL_UNSIGNED_BYTE, pBuffer); + this->end(); + + pImage->initWithImageData(pBuffer, tx * ty * 4, CCImage::kFmtRawData, tx, ty, 8); + + bRet = true; + + } while (0); + + CC_SAFE_DELETE_ARRAY(pBuffer); + + return bRet; +} + + CCData * CCRenderTexture::getUIImageAsDataFromBuffer(int format) { CC_UNUSED_PARAM(format); diff --git a/cocos2dx/platform/CCImage.cpp b/cocos2dx/platform/CCImage.cpp index 8e4f3afb14..b488d9bac7 100644 --- a/cocos2dx/platform/CCImage.cpp +++ b/cocos2dx/platform/CCImage.cpp @@ -94,7 +94,12 @@ bool CCImage::initWithImageFile(const char * strPath, EImageFormat eImgFmt/* = e return initWithImageData(data.getBuffer(), data.getSize(), eImgFmt); } -bool CCImage::initWithImageData(void * pData, int nDataLen, EImageFormat eFmt/* = eSrcFmtPng*/) +bool CCImage::initWithImageData(void * pData, + int nDataLen, + EImageFormat eFmt/* = eSrcFmtPng*/, + int nWidth/* = 0*/, + int nHeight/* = 0*/, + int nBitsPerComponent/* = 8*/) { bool bRet = false; do @@ -111,6 +116,11 @@ bool CCImage::initWithImageData(void * pData, int nDataLen, EImageFormat eFmt/* bRet = _initWithJpgData(pData, nDataLen); break; } + else if (kFmtRawData == eFmt) + { + bRet = _initWithRawData(pData, nDataLen, nWidth, nHeight, nBitsPerComponent); + break; + } } while (0); return bRet; } @@ -287,6 +297,272 @@ bool CCImage::_initWithPngData(void * pData, int nDatalen) 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::saveToFile(const char *pszFilePath, bool bIsToRGB) +{ + bool bRet = false; + + do + { + CC_BREAK_IF(NULL == pszFilePath); + + std::string strFilePath(pszFilePath); + CC_BREAK_IF(strFilePath.size() <= 4); + + std::string strLowerCasePath(strFilePath); + for (unsigned int i = 0; i < strLowerCasePath.length(); ++i) + { + strLowerCasePath[i] = tolower(strFilePath[i]); + } + + if (std::string::npos != strLowerCasePath.find(".png")) + { + CC_BREAK_IF(!_saveImageToPNG(pszFilePath, bIsToRGB)); + } + else if (std::string::npos != strLowerCasePath.find(".jpg")) + { + CC_BREAK_IF(!_saveImageToJPG(pszFilePath)); + } + else + { + break; + } + + bRet = true; + } while (0); + + return bRet; +} + +bool CCImage::_saveImageToPNG(const char * pszFilePath, bool bIsToRGB) +{ + bool bRet = false; + do + { + CC_BREAK_IF(NULL == pszFilePath); + + FILE *fp; + png_structp png_ptr; + png_infop info_ptr; + png_colorp palette; + png_bytep *row_pointers; + + fp = fopen(pszFilePath, "wb"); + CC_BREAK_IF(NULL == fp); + + png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + + if (png_ptr == NULL) + { + fclose(fp); + break; + } + + info_ptr = png_create_info_struct(png_ptr); + if (info_ptr == NULL) + { + fclose(fp); + png_destroy_write_struct(&png_ptr, NULL); + break; + } + + if (setjmp(png_jmpbuf(png_ptr))) + { + fclose(fp); + png_destroy_write_struct(&png_ptr, &info_ptr); + break; + } + + png_init_io(png_ptr, fp); + + if (!bIsToRGB && m_bHasAlpha) + { + png_set_IHDR(png_ptr, info_ptr, m_nWidth, m_nHeight, 8, PNG_COLOR_TYPE_RGB_ALPHA, + PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + } + else + { + png_set_IHDR(png_ptr, info_ptr, m_nWidth, m_nHeight, 8, PNG_COLOR_TYPE_RGB, + PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + } + + palette = (png_colorp)png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH * sizeof (png_color)); + png_set_PLTE(png_ptr, info_ptr, palette, PNG_MAX_PALETTE_LENGTH); + + png_write_info(png_ptr, info_ptr); + + png_set_packing(png_ptr); + + row_pointers = (png_bytep *)malloc(m_nHeight * sizeof(png_bytep)); + if(row_pointers == NULL) + { + fclose(fp); + png_destroy_write_struct(&png_ptr, &info_ptr); + break; + } + + if (!m_bHasAlpha) + { + for (int i = 0; i < (int)m_nHeight; i++) + { + row_pointers[i] = (png_bytep)m_pData + i * m_nWidth * 3; + } + + png_write_image(png_ptr, row_pointers); + + free(row_pointers); + row_pointers = NULL; + } + else + { + if (bIsToRGB) + { + unsigned char *pTempData = new unsigned char[m_nWidth * m_nHeight * 3]; + CC_BREAK_IF(NULL == pTempData); + + for (int i = 0; i < m_nHeight; ++i) + { + for (int j = 0; j < m_nWidth; ++j) + { + pTempData[(i * m_nWidth + j) * 3] = m_pData[(i * m_nWidth + j) * 4]; + pTempData[(i * m_nWidth + j) * 3 + 1] = m_pData[(i * m_nWidth + j) * 4 + 1]; + pTempData[(i * m_nWidth + j) * 3 + 2] = m_pData[(i * m_nWidth + j) * 4 + 2]; + } + } + + for (int i = 0; i < (int)m_nHeight; i++) + { + row_pointers[i] = (png_bytep)pTempData + i * m_nWidth * 3; + } + + png_write_image(png_ptr, row_pointers); + + free(row_pointers); + row_pointers = NULL; + + CC_SAFE_DELETE_ARRAY(pTempData); + } + else + { + for (int i = 0; i < (int)m_nHeight; i++) + { + row_pointers[i] = (png_bytep)m_pData + i * m_nWidth * 4; + } + + png_write_image(png_ptr, row_pointers); + + free(row_pointers); + row_pointers = NULL; + } + } + + png_write_end(png_ptr, info_ptr); + + png_free(png_ptr, palette); + palette = NULL; + + png_destroy_write_struct(&png_ptr, &info_ptr); + + fclose(fp); + + bRet = true; + } while (0); + return bRet; +} +bool CCImage::_saveImageToJPG(const char * pszFilePath) +{ + bool bRet = false; + do + { + CC_BREAK_IF(NULL == pszFilePath); + + struct jpeg_compress_struct cinfo; + struct jpeg_error_mgr jerr; + FILE * outfile; /* target file */ + JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */ + int row_stride; /* physical row width in image buffer */ + + cinfo.err = jpeg_std_error(&jerr); + /* Now we can initialize the JPEG compression object. */ + jpeg_create_compress(&cinfo); + + CC_BREAK_IF((outfile = fopen(pszFilePath, "wb")) == NULL); + + jpeg_stdio_dest(&cinfo, outfile); + + cinfo.image_width = m_nWidth; /* image width and height, in pixels */ + cinfo.image_height = m_nHeight; + cinfo.input_components = 3; /* # of color components per pixel */ + cinfo.in_color_space = JCS_RGB; /* colorspace of input image */ + + jpeg_set_defaults(&cinfo); + + jpeg_start_compress(&cinfo, TRUE); + + row_stride = m_nWidth * 3; /* JSAMPLEs per row in image_buffer */ + + if (m_bHasAlpha) + { + unsigned char *pTempData = new unsigned char[m_nWidth * m_nHeight * 3]; + CC_BREAK_IF(NULL == pTempData); + + for (int i = 0; i < m_nHeight; ++i) + { + for (int j = 0; j < m_nWidth; ++j) + { + pTempData[(i * m_nWidth + j) * 3] = m_pData[(i * m_nWidth + j) * 4]; + pTempData[(i * m_nWidth + j) * 3 + 1] = m_pData[(i * m_nWidth + j) * 4 + 1]; + pTempData[(i * m_nWidth + j) * 3 + 2] = m_pData[(i * m_nWidth + j) * 4 + 2]; + } + } + + while (cinfo.next_scanline < cinfo.image_height) { + row_pointer[0] = & pTempData[cinfo.next_scanline * row_stride]; + (void) jpeg_write_scanlines(&cinfo, row_pointer, 1); + } + + CC_SAFE_DELETE_ARRAY(pTempData); + } + else + { + while (cinfo.next_scanline < cinfo.image_height) { + row_pointer[0] = & m_pData[cinfo.next_scanline * row_stride]; + (void) jpeg_write_scanlines(&cinfo, row_pointer, 1); + } + } + + jpeg_finish_compress(&cinfo); + fclose(outfile); + jpeg_destroy_compress(&cinfo); + + bRet = true; + } while (0); + return bRet; +} + NS_CC_END; #endif // (CC_TARGET_PLATFORM != TARGET_OS_IPHONE) diff --git a/cocos2dx/platform/CCImage.h b/cocos2dx/platform/CCImage.h index 2fb3e2e422..701cc449ef 100644 --- a/cocos2dx/platform/CCImage.h +++ b/cocos2dx/platform/CCImage.h @@ -39,6 +39,7 @@ public: { kFmtJpg = 0, kFmtPng, + kFmtRawData, }EImageFormat; typedef enum @@ -65,12 +66,18 @@ public: /** @brief Load image from stream buffer. - @warning Only support png data now. + @warning kFmtRawData only support RGBA8888 @param pBuffer stream buffer that hold the image data @param nLength the length of data(managed in byte) + @param nWidth, nHeight, nBitsPerComponent are used for kFmtRawData @return true if load correctly */ - bool initWithImageData(void * pData, int nDataLen, EImageFormat eFmt = kFmtPng); + bool initWithImageData(void * pData, + int nDataLen, + EImageFormat eFmt = kFmtPng, + int nWidth = 0, + int nHeight = 0, + int nBitsPerComponent = 8); /** @brief Create image with specified string. @@ -99,18 +106,26 @@ public: /** @brief Save the CCImage data to specified file with specified format. + @param pszFilePath the file's absolute path, including file subfix + @param bIsToRGB if the image is saved as RGB format */ - bool saveToFile(const char * pszFilePath) { CC_UNUSED_PARAM(pszFilePath);return false; } + bool saveToFile(const char *pszFilePath, bool bIsToRGB = true); CC_SYNTHESIZE_READONLY(short, m_nWidth, Width); CC_SYNTHESIZE_READONLY(short, m_nHeight, Height); - CC_SYNTHESIZE_READONLY(int, m_nBitsPerComponent, BitsPerComponent); + CC_SYNTHESIZE_READONLY(int, m_nBitsPerComponent, BitsPerComponent); protected: - bool _initWithJpgData(void * pData, int nDatalen); - bool _initWithPngData(void * pData, int nDatalen); + bool _initWithJpgData(void *pData, int nDatalen); + bool _initWithPngData(void *pData, int nDatalen); - unsigned char * m_pData; + // @warning kFmtRawData only support RGBA8888 + bool _initWithRawData(void *pData, int nDatalen, int nWidth, int nHeight, int nBitsPerComponent); + + bool _saveImageToPNG(const char *pszFilePath, bool bIsToRGB = true); + bool _saveImageToJPG(const char *pszFilePath); + + unsigned char *m_pData; bool m_bHasAlpha; bool m_bPreMulti;