mirror of https://github.com/axmolengine/axmol.git
966 lines
28 KiB
C++
966 lines
28 KiB
C++
/****************************************************************************
|
|
Copyright (c) 2010 cocos2d-x.org
|
|
|
|
http://www.cocos2d-x.org
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in
|
|
all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
THE SOFTWARE.
|
|
****************************************************************************/
|
|
|
|
#ifndef __CC_PLATFORM_IMAGE_CPP__
|
|
#error "CCFileUtilsCommon_cpp.h can only be included for FileUtils.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"
|
|
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
|
|
#include "platform/android/CCFileUtilsAndroid.h"
|
|
#endif
|
|
|
|
#include <string>
|
|
#include <ctype.h>
|
|
|
|
#ifdef EMSCRIPTEN
|
|
#include <SDL/SDL.h>
|
|
#include <SDL/SDL_image.h>
|
|
#endif // EMSCRIPTEN
|
|
|
|
NS_CC_BEGIN
|
|
|
|
// 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 Image
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
Image::Image()
|
|
: _width(0)
|
|
, _height(0)
|
|
, _bitsPerComponent(0)
|
|
, _data(0)
|
|
, _hasAlpha(false)
|
|
, _preMulti(false)
|
|
{
|
|
|
|
}
|
|
|
|
Image::~Image()
|
|
{
|
|
CC_SAFE_DELETE_ARRAY(_data);
|
|
}
|
|
|
|
bool Image::initWithImageFile(const char * strPath, Format eImgFmt/* = eFmtPng*/)
|
|
{
|
|
bool bRet = false;
|
|
std::string fullPath = FileUtils::getInstance()->fullPathForFilename(strPath);
|
|
|
|
#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(fullPath.c_str());
|
|
|
|
int size = 4 * (iSurf->w * iSurf->h);
|
|
bRet = initWithRawData((void*)iSurf->pixels, size, iSurf->w, iSurf->h, 8, true);
|
|
|
|
unsigned int *tmp = (unsigned int *)_data;
|
|
int nrPixels = iSurf->w * iSurf->h;
|
|
for(int i = 0; i < nrPixels; i++)
|
|
{
|
|
unsigned char *p = _data + i * 4;
|
|
tmp[i] = CC_RGB_PREMULTIPLY_ALPHA( p[0], p[1], p[2], p[3] );
|
|
}
|
|
|
|
SDL_FreeSurface(iSurf);
|
|
#else
|
|
unsigned long nSize = 0;
|
|
unsigned char* pBuffer = FileUtils::getInstance()->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 Image::initWithImageFileThreadSafe(const char *fullpath, Format imageType)
|
|
{
|
|
bool bRet = false;
|
|
unsigned long nSize = 0;
|
|
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
|
|
FileUtilsAndroid *fileUitls = (FileUtilsAndroid*)FileUtils::getInstance();
|
|
unsigned char *pBuffer = fileUitls->getFileDataForAsync(fullpath, "rb", &nSize);
|
|
#else
|
|
unsigned char *pBuffer = FileUtils::getInstance()->getFileData(fullpath, "rb", &nSize);
|
|
#endif
|
|
if (pBuffer != NULL && nSize > 0)
|
|
{
|
|
bRet = initWithImageData(pBuffer, nSize, imageType);
|
|
}
|
|
CC_SAFE_DELETE_ARRAY(pBuffer);
|
|
return bRet;
|
|
}
|
|
|
|
bool Image::initWithImageData(void * pData,
|
|
int nDataLen,
|
|
Format eFmt/* = eSrcFmtPng*/,
|
|
int nWidth/* = 0*/,
|
|
int nHeight/* = 0*/,
|
|
int nBitsPerComponent/* = 8*/)
|
|
{
|
|
bool bRet = false;
|
|
do
|
|
{
|
|
CC_BREAK_IF(! pData || nDataLen <= 0);
|
|
|
|
if (Format::PNG == eFmt)
|
|
{
|
|
bRet = initWithPngData(pData, nDataLen);
|
|
break;
|
|
}
|
|
else if (Format::JPG == eFmt)
|
|
{
|
|
bRet = initWithJpgData(pData, nDataLen);
|
|
break;
|
|
}
|
|
else if (Format::TIFF == eFmt)
|
|
{
|
|
bRet = initWithTiffData(pData, nDataLen);
|
|
break;
|
|
}
|
|
else if (Format::WEBP == eFmt)
|
|
{
|
|
bRet = initWithWebpData(pData, nDataLen);
|
|
break;
|
|
}
|
|
else if (Format::RAW_DATA == 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 Image::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.
|
|
*/
|
|
log("%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 */
|
|
_width = (short)(cinfo.output_width);
|
|
_height = (short)(cinfo.output_height);
|
|
_hasAlpha = false;
|
|
_preMulti = false;
|
|
_bitsPerComponent = 8;
|
|
row_pointer[0] = new unsigned char[cinfo.output_width*cinfo.output_components];
|
|
CC_BREAK_IF(! row_pointer[0]);
|
|
|
|
_data = new unsigned char[cinfo.output_width*cinfo.output_height*cinfo.output_components];
|
|
CC_BREAK_IF(! _data);
|
|
|
|
/* 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++)
|
|
{
|
|
_data[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 Image::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);
|
|
|
|
_width = png_get_image_width(png_ptr, info_ptr);
|
|
_height = png_get_image_height(png_ptr, info_ptr);
|
|
_bitsPerComponent = 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 && _bitsPerComponent < 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 (_bitsPerComponent == 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
|
|
// _bitsPerComponent will always be 8
|
|
_bitsPerComponent = 8;
|
|
png_uint_32 rowbytes;
|
|
png_bytep* row_pointers = (png_bytep*)malloc( sizeof(png_bytep) * _height );
|
|
|
|
png_read_update_info(png_ptr, info_ptr);
|
|
|
|
rowbytes = png_get_rowbytes(png_ptr, info_ptr);
|
|
|
|
_data = new unsigned char[rowbytes * _height];
|
|
CC_BREAK_IF(!_data);
|
|
|
|
for (unsigned short i = 0; i < _height; ++i)
|
|
{
|
|
row_pointers[i] = _data + i*rowbytes;
|
|
}
|
|
png_read_image(png_ptr, row_pointers);
|
|
|
|
png_read_end(png_ptr, NULL);
|
|
|
|
png_uint_32 channel = rowbytes/_width;
|
|
if (channel == 4)
|
|
{
|
|
_hasAlpha = true;
|
|
unsigned int *tmp = (unsigned int *)_data;
|
|
for(unsigned short i = 0; i < _height; 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] );
|
|
}
|
|
}
|
|
|
|
_preMulti = 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 Image::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;
|
|
|
|
_hasAlpha = true;
|
|
_width = w;
|
|
_height = h;
|
|
_bitsPerComponent = 8;
|
|
|
|
_data = 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*)_data;
|
|
|
|
for(int j = 0; j < _width * _height * 4; j += 4)
|
|
{
|
|
*tmp++ = CC_RGB_PREMULTIPLY_ALPHA( src[j], src[j + 1],
|
|
src[j + 2], src[j + 3] );
|
|
}
|
|
*/
|
|
_preMulti = true;
|
|
|
|
memcpy(_data, raster, npixels*sizeof (uint32));
|
|
}
|
|
|
|
_TIFFfree(raster);
|
|
}
|
|
|
|
|
|
TIFFClose(tif);
|
|
|
|
bRet = true;
|
|
} while (0);
|
|
return bRet;
|
|
}
|
|
|
|
bool Image::initWithRawData(void * pData, int nDatalen, int nWidth, int nHeight, int nBitsPerComponent, bool bPreMulti)
|
|
{
|
|
bool bRet = false;
|
|
do
|
|
{
|
|
CC_BREAK_IF(0 == nWidth || 0 == nHeight);
|
|
|
|
_bitsPerComponent = nBitsPerComponent;
|
|
_height = (short)nHeight;
|
|
_width = (short)nWidth;
|
|
_hasAlpha = true;
|
|
_preMulti = bPreMulti;
|
|
|
|
// only RGBA8888 supported
|
|
int nBytesPerComponent = 4;
|
|
int nSize = nHeight * nWidth * nBytesPerComponent;
|
|
_data = new unsigned char[nSize];
|
|
CC_BREAK_IF(! _data);
|
|
memcpy(_data, pData, nSize);
|
|
|
|
bRet = true;
|
|
} while (0);
|
|
|
|
return bRet;
|
|
}
|
|
|
|
bool Image::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 Image::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 && _hasAlpha)
|
|
{
|
|
png_set_IHDR(png_ptr, info_ptr, _width, _height, 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, _width, _height, 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(_height * sizeof(png_bytep));
|
|
if(row_pointers == NULL)
|
|
{
|
|
fclose(fp);
|
|
png_destroy_write_struct(&png_ptr, &info_ptr);
|
|
break;
|
|
}
|
|
|
|
if (!_hasAlpha)
|
|
{
|
|
for (int i = 0; i < (int)_height; i++)
|
|
{
|
|
row_pointers[i] = (png_bytep)_data + i * _width * 3;
|
|
}
|
|
|
|
png_write_image(png_ptr, row_pointers);
|
|
|
|
free(row_pointers);
|
|
row_pointers = NULL;
|
|
}
|
|
else
|
|
{
|
|
if (bIsToRGB)
|
|
{
|
|
unsigned char *pTempData = new unsigned char[_width * _height * 3];
|
|
if (NULL == pTempData)
|
|
{
|
|
fclose(fp);
|
|
png_destroy_write_struct(&png_ptr, &info_ptr);
|
|
break;
|
|
}
|
|
|
|
for (int i = 0; i < _height; ++i)
|
|
{
|
|
for (int j = 0; j < _width; ++j)
|
|
{
|
|
pTempData[(i * _width + j) * 3] = _data[(i * _width + j) * 4];
|
|
pTempData[(i * _width + j) * 3 + 1] = _data[(i * _width + j) * 4 + 1];
|
|
pTempData[(i * _width + j) * 3 + 2] = _data[(i * _width + j) * 4 + 2];
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < (int)_height; i++)
|
|
{
|
|
row_pointers[i] = (png_bytep)pTempData + i * _width * 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)_height; i++)
|
|
{
|
|
row_pointers[i] = (png_bytep)_data + i * _width * 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 Image::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 = _width; /* image width and height, in pixels */
|
|
cinfo.image_height = _height;
|
|
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 = _width * 3; /* JSAMPLEs per row in image_buffer */
|
|
|
|
if (_hasAlpha)
|
|
{
|
|
unsigned char *pTempData = new unsigned char[_width * _height * 3];
|
|
if (NULL == pTempData)
|
|
{
|
|
jpeg_finish_compress(&cinfo);
|
|
jpeg_destroy_compress(&cinfo);
|
|
fclose(outfile);
|
|
break;
|
|
}
|
|
|
|
for (int i = 0; i < _height; ++i)
|
|
{
|
|
for (int j = 0; j < _width; ++j)
|
|
|
|
{
|
|
pTempData[(i * _width + j) * 3] = _data[(i * _width + j) * 4];
|
|
pTempData[(i * _width + j) * 3 + 1] = _data[(i * _width + j) * 4 + 1];
|
|
pTempData[(i * _width + j) * 3 + 2] = _data[(i * _width + 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] = & _data[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
|
|
|