#ifndef __CC_PLATFORM_IMAGE_CPP__
#error "CCFileUtilsCommon_cpp.h can only be included for CCFileUtils.cpp in platform/win32(android,...)"
#endif /* __CC_PLATFORM_IMAGE_CPP__ */
#include "CCImage.h"
#include "CCCommon.h"
#include "CCStdC.h"
#include "CCFileUtils.h"
#include "png.h"
#include "jpeglib.h"
#include "tiffio.h"
#include <string>
#include <ctype.h>
#ifdef EMSCRIPTEN
#include <SDL/SDL.h>
#include <SDL/SDL_image.h>
#endif // EMSCRIPTEN
NS_CC_BEGIN
// premultiply alpha, or the effect will wrong when want to use other pixel format in CCTexture2D,
// such as RGB888, RGB5A1
#define CC_RGB_PREMULTIPLY_ALPHA(vr, vg, vb, va) \
(unsigned)(((unsigned)((unsigned char)(vr) * ((unsigned char)(va) + 1)) >> 8) | \
((unsigned)((unsigned char)(vg) * ((unsigned char)(va) + 1) >> 8) << 8) | \
((unsigned)((unsigned char)(vb) * ((unsigned char)(va) + 1) >> 8) << 16) | \
((unsigned)(unsigned char)(va) << 24))
// on ios, we should use platform/ios/CCImage_ios.mm instead
typedef struct
{
unsigned char* data;
int size;
int offset;
}tImageSource;
static void pngReadCallback(png_structp png_ptr, png_bytep data, png_size_t length)
{
tImageSource* isource = (tImageSource*)png_get_io_ptr(png_ptr);
if((int)(isource->offset + length) <= isource->size)
{
memcpy(data, isource->data+isource->offset, length);
isource->offset += length;
}
else
{
png_error(png_ptr, "pngReaderCallback failed");
}
}
//////////////////////////////////////////////////////////////////////////
// Implement CCImage
//////////////////////////////////////////////////////////////////////////
CCImage::CCImage()
: m_nWidth(0)
, m_nHeight(0)
, m_nBitsPerComponent(0)
, m_pData(0)
, m_bHasAlpha(false)
, m_bPreMulti(false)
{
}
CCImage::~CCImage()
{
CC_SAFE_DELETE_ARRAY(m_pData);
}
bool CCImage::initWithImageFile(const char * strPath, EImageFormat eImgFmt/* = eFmtPng*/)
{
bool bRet = false;
#ifdef EMSCRIPTEN
// Emscripten includes a re-implementation of SDL that uses HTML5 canvas
// operations underneath. Consequently, loading images via IMG_Load (an SDL
// API) will be a lot faster than running libpng et al as compiled with
// Emscripten.
SDL_Surface *iSurf = IMG_Load(strPath);
int size = 4 * (iSurf->w * iSurf->h);
bRet = _initWithRawData((void*)iSurf->pixels, size, iSurf->w, iSurf->h, 8, true);
unsigned int *tmp = (unsigned int *)m_pData;
int nrPixels = iSurf->w * iSurf->h;
for(int i = 0; i < nrPixels; i++)
{
unsigned char *p = m_pData + i * 4;
tmp[i] = CC_RGB_PREMULTIPLY_ALPHA( p[0], p[1], p[2], p[3] );
}
SDL_FreeSurface(iSurf);
#else
unsigned long nSize = 0;
std::string fullPath = CCFileUtils::sharedFileUtils()->fullPathForFilename(strPath);
unsigned char* pBuffer = CCFileUtils::sharedFileUtils()->getFileData(fullPath.c_str(), "rb", &nSize);
if (pBuffer != NULL && nSize > 0)
{
bRet = initWithImageData(pBuffer, nSize, eImgFmt);
}
CC_SAFE_DELETE_ARRAY(pBuffer);
#endif // EMSCRIPTEN
return bRet;
}
bool CCImage::initWithImageFileThreadSafe(const char *fullpath, EImageFormat imageType)
{
bool bRet = false;
unsigned long nSize = 0;
unsigned char *pBuffer = CCFileUtils::sharedFileUtils()->getFileData(fullpath, "rb", &nSize);
if (pBuffer != NULL && nSize > 0)
{
bRet = initWithImageData(pBuffer, nSize, imageType);
}
CC_SAFE_DELETE_ARRAY(pBuffer);
return bRet;
}
bool CCImage::initWithImageData(void * pData,
int nDataLen,
EImageFormat eFmt/* = eSrcFmtPng*/,
int nWidth/* = 0*/,
int nHeight/* = 0*/,
int nBitsPerComponent/* = 8*/)
{
bool bRet = false;
do
{
CC_BREAK_IF(! pData || nDataLen <= 0);
if (kFmtPng == eFmt)
{
bRet = _initWithPngData(pData, nDataLen);
break;
}
else if (kFmtJpg == eFmt)
{
bRet = _initWithJpgData(pData, nDataLen);
break;
}
else if (kFmtTiff == eFmt)
{
bRet = _initWithTiffData(pData, nDataLen);
break;
}
else if (kFmtWebp == eFmt)
{
bRet = _initWithWebpData(pData, nDataLen);
break;
}
else if (kFmtRawData == eFmt)
{
bRet = _initWithRawData(pData, nDataLen, nWidth, nHeight, nBitsPerComponent, false);
break;
}
else
{
// if it is a png file buffer.
if (nDataLen > 8)
{
unsigned char* pHead = (unsigned char*)pData;
if ( pHead[0] == 0x89
&& pHead[1] == 0x50
&& pHead[2] == 0x4E
&& pHead[3] == 0x47
&& pHead[4] == 0x0D
&& pHead[5] == 0x0A
&& pHead[6] == 0x1A
&& pHead[7] == 0x0A)
{
bRet = _initWithPngData(pData, nDataLen);
break;
}
}
// if it is a tiff file buffer.
if (nDataLen > 2)
{
unsigned char* pHead = (unsigned char*)pData;
if ( (pHead[0] == 0x49 && pHead[1] == 0x49)
|| (pHead[0] == 0x4d && pHead[1] == 0x4d)
)
{
bRet = _initWithTiffData(pData, nDataLen);
break;
}
}
// if it is a jpeg file buffer.
if (nDataLen > 2)
{
unsigned char* pHead = (unsigned char*)pData;
if ( pHead[0] == 0xff
&& pHead[1] == 0xd8)
{
bRet = _initWithJpgData(pData, nDataLen);
break;
}
}
}
} while (0);
return bRet;
}
/*
* ERROR HANDLING:
*
* The JPEG library's standard error handler (jerror.c) is divided into
* several "methods" which you can override individually. This lets you
* adjust the behavior without duplicating a lot of code, which you might
* have to update with each future release.
*
* We override the "error_exit" method so that control is returned to the
* library's caller when a fatal error occurs, rather than calling exit()
* as the standard error_exit method does.
*
* We use C's setjmp/longjmp facility to return control. This means that the
* routine which calls the JPEG library must first execute a setjmp() call to
* establish the return point. We want the replacement error_exit to do a
* longjmp(). But we need to make the setjmp buffer accessible to the
* error_exit routine. To do this, we make a private extension of the
* standard JPEG error handler object. (If we were using C++, we'd say we
* were making a subclass of the regular error handler.)
*
* Here's the extended error handler struct:
*/
struct my_error_mgr {
struct jpeg_error_mgr pub;/* "public" fields */
jmp_buf setjmp_buffer;/* for return to caller */
};
typedef struct my_error_mgr * my_error_ptr;
/*
* Here's the routine that will replace the standard error_exit method:
*/
METHODDEF(void)
my_error_exit (j_common_ptr cinfo)
{
/* cinfo->err really points to a my_error_mgr struct, so coerce pointer */
my_error_ptr myerr = (my_error_ptr) cinfo->err;
/* Always display the message. */
/* We could postpone this until after returning, if we chose. */
(*cinfo->err->output_message) (cinfo);
/* Return control to the setjmp point */
longjmp(myerr->setjmp_buffer, 1);
}
bool CCImage::_initWithJpgData(void * data, int nSize)
{
/* these are standard libjpeg structures for reading(decompression) */
struct jpeg_decompress_struct cinfo;
/* We use our private extension JPEG error handler.
* Note that this struct must live as long as the main JPEG parameter
* struct, to avoid dangling-pointer problems.
*/
struct my_error_mgr jerr;
/* libjpeg data structure for storing one row, that is, scanline of an image */
JSAMPROW row_pointer[1] = {0};
unsigned long location = 0;
unsigned int i = 0;
bool bRet = false;
do
{
/* We set up the normal JPEG error routines, then override error_exit. */
cinfo.err = jpeg_std_error(&jerr.pub);
jerr.pub.error_exit = my_error_exit;
/* Establish the setjmp return context for my_error_exit to use. */
if (setjmp(jerr.setjmp_buffer)) {
/* If we get here, the JPEG code has signaled an error.
* We need to clean up the JPEG object, close the input file, and return.
*/
CCLog("%d", bRet);
jpeg_destroy_decompress(&cinfo);
break;
}
/* setup decompression process and source, then read JPEG header */
jpeg_create_decompress( &cinfo );
jpeg_mem_src( &cinfo, (unsigned char *) data, nSize );
/* reading the image header which contains image information */
#if (JPEG_LIB_VERSION >= 90)
// libjpeg 0.9 adds stricter types.
jpeg_read_header( &cinfo, TRUE );
#else
jpeg_read_header( &cinfo, true );
#endif
// we only support RGB or grayscale
if (cinfo.jpeg_color_space != JCS_RGB)
{
if (cinfo.jpeg_color_space == JCS_GRAYSCALE || cinfo.jpeg_color_space == JCS_YCbCr)
{
cinfo.out_color_space = JCS_RGB;
}
}
else
{
break;
}
/* Start decompression jpeg here */
jpeg_start_decompress( &cinfo );
/* init image info */
m_nWidth = (short)(cinfo.output_width);
m_nHeight = (short)(cinfo.output_height);
m_bHasAlpha = false;
m_bPreMulti = false;
m_nBitsPerComponent = 8;
row_pointer[0] = new unsigned char[cinfo.output_width*cinfo.output_components];
CC_BREAK_IF(! row_pointer[0]);
m_pData = new unsigned char[cinfo.output_width*cinfo.output_height*cinfo.output_components];
CC_BREAK_IF(! m_pData);
/* now actually read the jpeg into the raw buffer */
/* read one scan line at a time */
while( cinfo.output_scanline < cinfo.output_height )
{
jpeg_read_scanlines( &cinfo, row_pointer, 1 );
for( i=0; i<cinfo.output_width*cinfo.output_components;i++)
{
m_pData[location++] = row_pointer[0][i];
}
}
/* When read image file with broken data, jpeg_finish_decompress() may cause error.
* Besides, jpeg_destroy_decompress() shall deallocate and release all memory associated
* with the decompression object.
* So it doesn't need to call jpeg_finish_decompress().
*/
//jpeg_finish_decompress( &cinfo );
jpeg_destroy_decompress( &cinfo );
/* wrap up decompression, destroy objects, free pointers and close open files */
bRet = true;
} while (0);
CC_SAFE_DELETE_ARRAY(row_pointer[0]);
return bRet;
}
bool CCImage::_initWithPngData(void * pData, int nDatalen)
{
// length of bytes to check if it is a valid png file
#define PNGSIGSIZE 8
bool bRet = false;
png_byte header[PNGSIGSIZE] = {0};
png_structp png_ptr = 0;
png_infop info_ptr = 0;
do
{
// png header len is 8 bytes
CC_BREAK_IF(nDatalen < PNGSIGSIZE);
// check the data is png or not
memcpy(header, pData, PNGSIGSIZE);
CC_BREAK_IF(png_sig_cmp(header, 0, PNGSIGSIZE));
// init png_struct
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
CC_BREAK_IF(! png_ptr);
// init png_info
info_ptr = png_create_info_struct(png_ptr);
CC_BREAK_IF(!info_ptr);
#if (CC_TARGET_PLATFORM != CC_PLATFORM_BADA && CC_TARGET_PLATFORM != CC_PLATFORM_NACL)
CC_BREAK_IF(setjmp(png_jmpbuf(png_ptr)));
#endif
// set the read call back function
tImageSource imageSource;
imageSource.data = (unsigned char*)pData;
imageSource.size = nDatalen;
imageSource.offset = 0;
png_set_read_fn(png_ptr, &imageSource, pngReadCallback);
// read png header info
// read png file info
png_read_info(png_ptr, info_ptr);
m_nWidth = png_get_image_width(png_ptr, info_ptr);
m_nHeight = png_get_image_height(png_ptr, info_ptr);
m_nBitsPerComponent = png_get_bit_depth(png_ptr, info_ptr);
png_uint_32 color_type = png_get_color_type(png_ptr, info_ptr);
//CCLOG("color type %u", color_type);
// force palette images to be expanded to 24-bit RGB
// it may include alpha channel
if (color_type == PNG_COLOR_TYPE_PALETTE)
{
png_set_palette_to_rgb(png_ptr);
}
// low-bit-depth grayscale images are to be expanded to 8 bits
if (color_type == PNG_COLOR_TYPE_GRAY && m_nBitsPerComponent < 8)
{
png_set_expand_gray_1_2_4_to_8(png_ptr);
}
// expand any tRNS chunk data into a full alpha channel
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
{
png_set_tRNS_to_alpha(png_ptr);
}
// reduce images with 16-bit samples to 8 bits
if (m_nBitsPerComponent == 16)
{
png_set_strip_16(png_ptr);
}
// expand grayscale images to RGB
if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
{
png_set_gray_to_rgb(png_ptr);
}
// read png data
// m_nBitsPerComponent will always be 8
m_nBitsPerComponent = 8;
png_uint_32 rowbytes;
png_bytep* row_pointers = (png_bytep*)malloc( sizeof(png_bytep) * m_nHeight );
png_read_update_info(png_ptr, info_ptr);
rowbytes = png_get_rowbytes(png_ptr, info_ptr);
m_pData = new unsigned char[rowbytes * m_nHeight];
CC_BREAK_IF(!m_pData);
for (unsigned short i = 0; i < m_nHeight; ++i)
{
row_pointers[i] = m_pData + i*rowbytes;
}
png_read_image(png_ptr, row_pointers);
png_read_end(png_ptr, NULL);
png_uint_32 channel = rowbytes/m_nWidth;
if (channel == 4)
{
m_bHasAlpha = true;
unsigned int *tmp = (unsigned int *)m_pData;
for(unsigned short i = 0; i < m_nHeight; i++)
{
for(unsigned int j = 0; j < rowbytes; j += 4)
{
*tmp++ = CC_RGB_PREMULTIPLY_ALPHA( row_pointers[i][j], row_pointers[i][j + 1],
row_pointers[i][j + 2], row_pointers[i][j + 3] );
}
}
m_bPreMulti = true;
}
CC_SAFE_FREE(row_pointers);
bRet = true;
} while (0);
if (png_ptr)
{
png_destroy_read_struct(&png_ptr, (info_ptr) ? &info_ptr : 0, 0);
}
return bRet;
}
static tmsize_t _tiffReadProc(thandle_t fd, void* buf, tmsize_t size)
{
tImageSource* isource = (tImageSource*)fd;
uint8* ma;
uint64 mb;
unsigned long n;
unsigned long o;
tmsize_t p;
ma=(uint8*)buf;
mb=size;
p=0;
while (mb>0)
{
n=0x80000000UL;
if ((uint64)n>mb)
n=(unsigned long)mb;
if((int)(isource->offset + n) <= isource->size)
{
memcpy(ma, isource->data+isource->offset, n);
isource->offset += n;
o = n;
}
else
{
return 0;
}
ma+=o;
mb-=o;
p+=o;
if (o!=n)
{
break;
}
}
return p;
}
static tmsize_t _tiffWriteProc(thandle_t fd, void* buf, tmsize_t size)
{
CC_UNUSED_PARAM(fd);
CC_UNUSED_PARAM(buf);
CC_UNUSED_PARAM(size);
return 0;
}
static uint64 _tiffSeekProc(thandle_t fd, uint64 off, int whence)
{
tImageSource* isource = (tImageSource*)fd;
uint64 ret = -1;
do
{
if (whence == SEEK_SET)
{
CC_BREAK_IF(off >= (uint64)isource->size);
ret = isource->offset = (uint32)off;
}
else if (whence == SEEK_CUR)
{
CC_BREAK_IF(isource->offset + off >= (uint64)isource->size);
ret = isource->offset += (uint32)off;
}
else if (whence == SEEK_END)
{
CC_BREAK_IF(off >= (uint64)isource->size);
ret = isource->offset = (uint32)(isource->size-1 - off);
}
else
{
CC_BREAK_IF(off >= (uint64)isource->size);
ret = isource->offset = (uint32)off;
}
} while (0);
return ret;
}
static uint64 _tiffSizeProc(thandle_t fd)
{
tImageSource* pImageSrc = (tImageSource*)fd;
return pImageSrc->size;
}
static int _tiffCloseProc(thandle_t fd)
{
CC_UNUSED_PARAM(fd);
return 0;
}
static int _tiffMapProc(thandle_t fd, void** pbase, toff_t* psize)
{
CC_UNUSED_PARAM(fd);
CC_UNUSED_PARAM(pbase);
CC_UNUSED_PARAM(psize);
return 0;
}
static void _tiffUnmapProc(thandle_t fd, void* base, toff_t size)
{
CC_UNUSED_PARAM(fd);
CC_UNUSED_PARAM(base);
CC_UNUSED_PARAM(size);
}
bool CCImage::_initWithTiffData(void* pData, int nDataLen)
{
bool bRet = false;
do
{
// set the read call back function
tImageSource imageSource;
imageSource.data = (unsigned char*)pData;
imageSource.size = nDataLen;
imageSource.offset = 0;
TIFF* tif = TIFFClientOpen("file.tif", "r", (thandle_t)&imageSource,
_tiffReadProc, _tiffWriteProc,
_tiffSeekProc, _tiffCloseProc, _tiffSizeProc,
_tiffMapProc,
_tiffUnmapProc);
CC_BREAK_IF(NULL == tif);
uint32 w = 0, h = 0;
uint16 bitsPerSample = 0, samplePerPixel = 0, planarConfig = 0;
size_t npixels = 0;
TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &w);
TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &h);
TIFFGetField(tif, TIFFTAG_BITSPERSAMPLE, &bitsPerSample);
TIFFGetField(tif, TIFFTAG_SAMPLESPERPIXEL, &samplePerPixel);
TIFFGetField(tif, TIFFTAG_PLANARCONFIG, &planarConfig);
npixels = w * h;
m_bHasAlpha = true;
m_nWidth = w;
m_nHeight = h;
m_nBitsPerComponent = 8;
m_pData = new unsigned char[npixels * sizeof (uint32)];
uint32* raster = (uint32*) _TIFFmalloc(npixels * sizeof (uint32));
if (raster != NULL)
{
if (TIFFReadRGBAImageOriented(tif, w, h, raster, ORIENTATION_TOPLEFT, 0))
{
/* the raster data is pre-multiplied by the alpha component
after invoking TIFFReadRGBAImageOriented
unsigned char* src = (unsigned char*)raster;
unsigned int* tmp = (unsigned int*)m_pData;
for(int j = 0; j < m_nWidth * m_nHeight * 4; j += 4)
{
*tmp++ = CC_RGB_PREMULTIPLY_ALPHA( src[j], src[j + 1],
src[j + 2], src[j + 3] );
}
*/
m_bPreMulti = true;
memcpy(m_pData, raster, npixels*sizeof (uint32));
}
_TIFFfree(raster);
}
TIFFClose(tif);
bRet = true;
} while (0);
return bRet;
}
bool CCImage::_initWithRawData(void * pData, int nDatalen, int nWidth, int nHeight, int nBitsPerComponent, bool bPreMulti)
{
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;
m_bPreMulti = bPreMulti;
// only RGBA8888 supported
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 (NULL == png_ptr)
{
fclose(fp);
break;
}
info_ptr = png_create_info_struct(png_ptr);
if (NULL == info_ptr)
{
fclose(fp);
png_destroy_write_struct(&png_ptr, NULL);
break;
}
#if (CC_TARGET_PLATFORM != CC_PLATFORM_BADA && CC_TARGET_PLATFORM != CC_PLATFORM_NACL)
if (setjmp(png_jmpbuf(png_ptr)))
{
fclose(fp);
png_destroy_write_struct(&png_ptr, &info_ptr);
break;
}
#endif
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];
if (NULL == pTempData)
{
fclose(fp);
png_destroy_write_struct(&png_ptr, &info_ptr);
break;
}
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];
if (NULL == pTempData)
{
jpeg_finish_compress(&cinfo);
jpeg_destroy_compress(&cinfo);
fclose(outfile);
break;
}
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