2019-11-23 20:27:39 +08:00
/****************************************************************************
Copyright ( c ) 2010 - 2012 cocos2d - x . org
Copyright ( c ) 2013 - 2016 Chukong Technologies Inc .
Copyright ( c ) 2017 - 2018 Xiamen Yaji Software Co . , Ltd .
2020-09-01 13:24:45 +08:00
Copyright ( c ) 2020 c4games . com
2019-11-23 20:27:39 +08:00
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 .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# include "platform/CCImage.h"
2020-09-25 11:07:56 +08:00
# include "renderer/backend/TextureUtils.h"
2019-11-23 20:27:39 +08:00
# include <string>
# include <ctype.h>
# include "base/ccConfig.h" // CC_USE_JPEG, CC_USE_WEBP
2019-11-25 02:28:38 +08:00
# define STBI_NO_JPEG
# define STBI_NO_PNG
# define STBI_NO_GIF
# define STBI_NO_PSD
# define STBI_NO_PIC
# define STBI_NO_PNM
# define STBI_NO_HDR
# define STBI_NO_TGA
# define STB_IMAGE_IMPLEMENTATION
2020-06-14 15:52:15 +08:00
# if CC_TARGET_PLATFORM == CC_PLATFORM_IOS
# define STBI_NO_THREAD_LOCALS
# endif
2019-11-25 02:28:38 +08:00
# include "stb/stb_image.h"
2019-11-23 20:27:39 +08:00
extern " C "
{
// To resolve link error when building 32bits with Xcode 6.
// More information please refer to the discussion in https://github.com/cocos2d/cocos2d-x/pull/6986
# if defined (__unix) || (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
# ifndef __ENABLE_COMPATIBILITY_WITH_UNIX_2003__
# define __ENABLE_COMPATIBILITY_WITH_UNIX_2003__
# include <stdio.h>
# include <dirent.h>
FILE * fopen $ UNIX2003 ( const char * filename , const char * mode )
{
return fopen ( filename , mode ) ;
}
size_t fwrite $ UNIX2003 ( const void * a , size_t b , size_t c , FILE * d )
{
return fwrite ( a , b , c , d ) ;
}
int fputs $ UNIX2003 ( const char * res1 , FILE * res2 ) {
return fputs ( res1 , res2 ) ;
}
char * strerror $ UNIX2003 ( int errnum )
{
return strerror ( errnum ) ;
}
DIR * opendir $ INODE64 $ UNIX2003 ( char * dirName )
{
return opendir ( dirName ) ;
}
DIR * opendir $ INODE64 ( char * dirName )
{
return opendir ( dirName ) ;
}
int closedir $ UNIX2003 ( DIR * dir )
{
return closedir ( dir ) ;
}
struct dirent * readdir $ INODE64 ( DIR * dir )
{
return readdir ( dir ) ;
}
# endif
# endif
# if CC_USE_PNG
# include "png.h"
# endif //CC_USE_PNG
# include "base/etc1.h"
2020-08-31 21:04:47 +08:00
# include "base/etc2.h"
2020-02-12 20:24:29 +08:00
# include "base/astc.h"
2019-11-23 20:27:39 +08:00
# if CC_USE_JPEG
# include "jpeglib.h"
# include <setjmp.h>
# endif // CC_USE_JPEG
}
# include "base/s3tc.h"
# include "base/atitc.h"
# include "base/pvr.h"
# include "base/TGAlib.h"
# if CC_USE_WEBP
# include "decode.h"
# endif // CC_USE_WEBP
# include "base/ccMacros.h"
# include "platform/CCCommon.h"
# include "platform/CCStdC.h"
# include "platform/CCFileUtils.h"
# include "base/CCConfiguration.h"
# include "base/ccUtils.h"
# include "base/ZipUtils.h"
# if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
# include "platform/android/CCFileUtils-android.h"
2019-12-02 23:01:06 +08:00
# include "platform/CCGL.h"
2019-11-23 20:27:39 +08:00
# endif
# define CC_GL_ATC_RGB_AMD 0x8C92
# define CC_GL_ATC_RGBA_EXPLICIT_ALPHA_AMD 0x8C93
# define CC_GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD 0x87EE
2020-02-12 20:24:29 +08:00
# define ASTC_MAGIC_FILE_CONSTANT 0x5CA1AB13
2019-11-23 20:27:39 +08:00
NS_CC_BEGIN
//////////////////////////////////////////////////////////////////////////
//struct and data for pvr structure
namespace
{
static const int PVR_TEXTURE_FLAG_TYPE_MASK = 0xff ;
// Values taken from PVRTexture.h from http://www.imgtec.com
enum class PVR2TextureFlag
{
Mipmap = ( 1 < < 8 ) , // has mip map levels
Twiddle = ( 1 < < 9 ) , // is twiddled
Bumpmap = ( 1 < < 10 ) , // has normals encoded for a bump map
Tiling = ( 1 < < 11 ) , // is bordered for tiled pvr
Cubemap = ( 1 < < 12 ) , // is a cubemap/skybox
FalseMipCol = ( 1 < < 13 ) , // are there false colored MIP levels
Volume = ( 1 < < 14 ) , // is this a volume texture
Alpha = ( 1 < < 15 ) , // v2.1 is there transparency info in the texture
VerticalFlip = ( 1 < < 16 ) , // v2.1 is the texture vertically flipped
} ;
enum class PVR3TextureFlag
{
PremultipliedAlpha = ( 1 < < 1 ) // has premultiplied alpha
} ;
static const char gPVRTexIdentifier [ 5 ] = " PVR! " ;
// v2
2020-09-01 14:57:18 +08:00
enum class PVR2TexturePixelFormat : uint8_t
2019-11-23 20:27:39 +08:00
{
RGBA4444 = 0x10 ,
RGBA5551 ,
RGBA8888 ,
RGB565 ,
RGB555 , // unsupported
RGB888 ,
I8 ,
AI88 ,
PVRTC2BPP_RGBA ,
PVRTC4BPP_RGBA ,
BGRA8888 ,
A8 ,
} ;
// v3
enum class PVR3TexturePixelFormat : uint64_t
{
PVRTC2BPP_RGB = 0ULL ,
PVRTC2BPP_RGBA = 1ULL ,
PVRTC4BPP_RGB = 2ULL ,
PVRTC4BPP_RGBA = 3ULL ,
PVRTC2_2BPP_RGBA = 4ULL ,
PVRTC2_4BPP_RGBA = 5ULL ,
ETC1 = 6ULL ,
DXT1 = 7ULL ,
DXT2 = 8ULL ,
DXT3 = 9ULL ,
DXT4 = 10ULL ,
DXT5 = 11ULL ,
BC1 = 7ULL ,
BC2 = 9ULL ,
BC3 = 11ULL ,
BC4 = 12ULL ,
BC5 = 13ULL ,
BC6 = 14ULL ,
BC7 = 15ULL ,
UYVY = 16ULL ,
YUY2 = 17ULL ,
BW1bpp = 18ULL ,
R9G9B9E5 = 19ULL ,
RGBG8888 = 20ULL ,
GRGB8888 = 21ULL ,
ETC2_RGB = 22ULL ,
ETC2_RGBA = 23ULL ,
ETC2_RGBA1 = 24ULL ,
EAC_R11_Unsigned = 25ULL ,
EAC_R11_Signed = 26ULL ,
EAC_RG11_Unsigned = 27ULL ,
EAC_RG11_Signed = 28ULL ,
BGRA8888 = 0x0808080861726762ULL ,
RGBA8888 = 0x0808080861626772ULL ,
RGBA4444 = 0x0404040461626772ULL ,
RGBA5551 = 0x0105050561626772ULL ,
RGB565 = 0x0005060500626772ULL ,
RGB888 = 0x0008080800626772ULL ,
A8 = 0x0000000800000061ULL ,
L8 = 0x000000080000006cULL ,
LA88 = 0x000008080000616cULL ,
} ;
// v2
typedef const std : : map < PVR2TexturePixelFormat , backend : : PixelFormat > _pixel2_formathash ;
static const _pixel2_formathash : : value_type v2_pixel_formathash_value [ ] =
{
2020-09-25 11:07:56 +08:00
_pixel2_formathash : : value_type ( PVR2TexturePixelFormat : : BGRA8888 , backend : : PixelFormat : : BGRA8 ) ,
_pixel2_formathash : : value_type ( PVR2TexturePixelFormat : : RGBA8888 , backend : : PixelFormat : : RGBA8 ) ,
_pixel2_formathash : : value_type ( PVR2TexturePixelFormat : : RGBA4444 , backend : : PixelFormat : : RGBA4 ) ,
2019-11-23 20:27:39 +08:00
_pixel2_formathash : : value_type ( PVR2TexturePixelFormat : : RGBA5551 , backend : : PixelFormat : : RGB5A1 ) ,
_pixel2_formathash : : value_type ( PVR2TexturePixelFormat : : RGB565 , backend : : PixelFormat : : RGB565 ) ,
2020-09-25 11:07:56 +08:00
_pixel2_formathash : : value_type ( PVR2TexturePixelFormat : : RGB888 , backend : : PixelFormat : : RGB8 ) ,
2019-11-23 20:27:39 +08:00
_pixel2_formathash : : value_type ( PVR2TexturePixelFormat : : A8 , backend : : PixelFormat : : A8 ) ,
2020-09-25 11:07:56 +08:00
_pixel2_formathash : : value_type ( PVR2TexturePixelFormat : : I8 , backend : : PixelFormat : : L8 ) ,
_pixel2_formathash : : value_type ( PVR2TexturePixelFormat : : AI88 , backend : : PixelFormat : : LA8 ) ,
2019-11-23 20:27:39 +08:00
_pixel2_formathash : : value_type ( PVR2TexturePixelFormat : : PVRTC2BPP_RGBA , backend : : PixelFormat : : PVRTC2A ) ,
_pixel2_formathash : : value_type ( PVR2TexturePixelFormat : : PVRTC4BPP_RGBA , backend : : PixelFormat : : PVRTC4A ) ,
} ;
static const int PVR2_MAX_TABLE_ELEMENTS = sizeof ( v2_pixel_formathash_value ) / sizeof ( v2_pixel_formathash_value [ 0 ] ) ;
static const _pixel2_formathash v2_pixel_formathash ( v2_pixel_formathash_value , v2_pixel_formathash_value + PVR2_MAX_TABLE_ELEMENTS ) ;
// v3
typedef const std : : map < PVR3TexturePixelFormat , backend : : PixelFormat > _pixel3_formathash ;
static _pixel3_formathash : : value_type v3_pixel_formathash_value [ ] =
{
2020-09-25 11:07:56 +08:00
_pixel3_formathash : : value_type ( PVR3TexturePixelFormat : : BGRA8888 , backend : : PixelFormat : : BGRA8 ) ,
_pixel3_formathash : : value_type ( PVR3TexturePixelFormat : : RGBA8888 , backend : : PixelFormat : : RGBA8 ) ,
_pixel3_formathash : : value_type ( PVR3TexturePixelFormat : : RGBA4444 , backend : : PixelFormat : : RGBA4 ) ,
2019-11-23 20:27:39 +08:00
_pixel3_formathash : : value_type ( PVR3TexturePixelFormat : : RGBA5551 , backend : : PixelFormat : : RGB5A1 ) ,
_pixel3_formathash : : value_type ( PVR3TexturePixelFormat : : RGB565 , backend : : PixelFormat : : RGB565 ) ,
2020-09-25 11:07:56 +08:00
_pixel3_formathash : : value_type ( PVR3TexturePixelFormat : : RGB888 , backend : : PixelFormat : : RGB8 ) ,
2019-11-23 20:27:39 +08:00
_pixel3_formathash : : value_type ( PVR3TexturePixelFormat : : A8 , backend : : PixelFormat : : A8 ) ,
2020-09-25 11:07:56 +08:00
_pixel3_formathash : : value_type ( PVR3TexturePixelFormat : : L8 , backend : : PixelFormat : : L8 ) ,
_pixel3_formathash : : value_type ( PVR3TexturePixelFormat : : LA88 , backend : : PixelFormat : : LA8 ) ,
2019-11-23 20:27:39 +08:00
_pixel3_formathash : : value_type ( PVR3TexturePixelFormat : : PVRTC2BPP_RGB , backend : : PixelFormat : : PVRTC2 ) ,
_pixel3_formathash : : value_type ( PVR3TexturePixelFormat : : PVRTC2BPP_RGBA , backend : : PixelFormat : : PVRTC2A ) ,
_pixel3_formathash : : value_type ( PVR3TexturePixelFormat : : PVRTC4BPP_RGB , backend : : PixelFormat : : PVRTC4 ) ,
_pixel3_formathash : : value_type ( PVR3TexturePixelFormat : : PVRTC4BPP_RGBA , backend : : PixelFormat : : PVRTC4A ) ,
2020-09-01 11:43:03 +08:00
_pixel3_formathash : : value_type ( PVR3TexturePixelFormat : : ETC1 , backend : : PixelFormat : : ETC1 ) ,
2019-11-23 20:27:39 +08:00
} ;
static const int PVR3_MAX_TABLE_ELEMENTS = sizeof ( v3_pixel_formathash_value ) / sizeof ( v3_pixel_formathash_value [ 0 ] ) ;
static const _pixel3_formathash v3_pixel_formathash ( v3_pixel_formathash_value , v3_pixel_formathash_value + PVR3_MAX_TABLE_ELEMENTS ) ;
typedef struct _PVRTexHeader
{
unsigned int headerLength ;
unsigned int height ;
unsigned int width ;
unsigned int numMipmaps ;
unsigned int flags ;
unsigned int dataLength ;
unsigned int bpp ;
unsigned int bitmaskRed ;
unsigned int bitmaskGreen ;
unsigned int bitmaskBlue ;
unsigned int bitmaskAlpha ;
unsigned int pvrTag ;
unsigned int numSurfs ;
} PVRv2TexHeader ;
# ifdef _MSC_VER
# pragma pack(push,1)
# endif
typedef struct
{
uint32_t version ;
uint32_t flags ;
uint64_t pixelFormat ;
uint32_t colorSpace ;
uint32_t channelType ;
uint32_t height ;
uint32_t width ;
uint32_t depth ;
uint32_t numberOfSurfaces ;
uint32_t numberOfFaces ;
uint32_t numberOfMipmaps ;
uint32_t metadataLength ;
# ifdef _MSC_VER
} PVRv3TexHeader ;
# pragma pack(pop)
# else
} __attribute__ ( ( packed ) ) PVRv3TexHeader ;
# endif
}
//pvr structure end
//////////////////////////////////////////////////////////////////////////
//struct and data for s3tc(dds) struct
namespace
{
struct DDColorKey
{
uint32_t colorSpaceLowValue ;
uint32_t colorSpaceHighValue ;
} ;
struct DDSCaps
{
uint32_t caps ;
uint32_t caps2 ;
uint32_t caps3 ;
uint32_t caps4 ;
} ;
struct DDPixelFormat
{
uint32_t size ;
uint32_t flags ;
uint32_t fourCC ;
uint32_t RGBBitCount ;
uint32_t RBitMask ;
uint32_t GBitMask ;
uint32_t BBitMask ;
uint32_t ABitMask ;
} ;
struct DDSURFACEDESC2
{
uint32_t size ;
uint32_t flags ;
uint32_t height ;
uint32_t width ;
union
{
uint32_t pitch ;
uint32_t linearSize ;
} DUMMYUNIONNAMEN1 ;
union
{
uint32_t backBufferCount ;
uint32_t depth ;
} DUMMYUNIONNAMEN5 ;
union
{
uint32_t mipMapCount ;
uint32_t refreshRate ;
uint32_t srcVBHandle ;
} DUMMYUNIONNAMEN2 ;
uint32_t alphaBitDepth ;
uint32_t reserved ;
uint32_t surface ;
union
{
DDColorKey ddckCKDestOverlay ;
uint32_t emptyFaceColor ;
} DUMMYUNIONNAMEN3 ;
DDColorKey ddckCKDestBlt ;
DDColorKey ddckCKSrcOverlay ;
DDColorKey ddckCKSrcBlt ;
union
{
DDPixelFormat ddpfPixelFormat ;
uint32_t FVF ;
} DUMMYUNIONNAMEN4 ;
DDSCaps ddsCaps ;
uint32_t textureStage ;
} ;
# pragma pack(push,1)
struct S3TCTexHeader
{
char fileCode [ 4 ] ;
DDSURFACEDESC2 ddsd ;
} ;
# pragma pack(pop)
}
//s3tc struct end
//////////////////////////////////////////////////////////////////////////
//struct and data for atitc(ktx) struct
namespace
{
struct ATITCTexHeader
{
//HEADER
char identifier [ 12 ] ;
uint32_t endianness ;
uint32_t glType ;
uint32_t glTypeSize ;
uint32_t glFormat ;
uint32_t glInternalFormat ;
uint32_t glBaseInternalFormat ;
uint32_t pixelWidth ;
uint32_t pixelHeight ;
uint32_t pixelDepth ;
uint32_t numberOfArrayElements ;
uint32_t numberOfFaces ;
uint32_t numberOfMipmapLevels ;
uint32_t bytesOfKeyValueData ;
} ;
}
//atitc struct end
//////////////////////////////////////////////////////////////////////////
2020-02-12 20:24:29 +08:00
//struct and data for ASTC struct
namespace
{
struct ASTCTexHeader
{
uint8_t magic [ 4 ] ;
uint8_t blockdim_x ;
uint8_t blockdim_y ;
uint8_t blockdim_z ;
uint8_t xsize [ 3 ] ; // x-size = xsize[0] + xsize[1] + xsize[2]
uint8_t ysize [ 3 ] ; // x-size, y-size and z-size are given in texels;
uint8_t zsize [ 3 ] ; // block count is inferred
} ;
}
//atitc struct end
//////////////////////////////////////////////////////////////////////////
2019-11-23 20:27:39 +08:00
namespace
{
typedef struct
{
2020-09-01 14:57:18 +08:00
const uint8_t * data ;
2019-11-23 20:27:39 +08:00
ssize_t size ;
int offset ;
} tImageSource ;
# if CC_USE_PNG
2021-04-22 19:49:43 +08:00
void pngWriteCallback ( png_structp png_ptr , png_bytep data , size_t length )
{
if ( png_ptr = = NULL )
return ;
FileStream * fileStream = ( FileStream * ) png_get_io_ptr ( png_ptr ) ;
const auto check = fileStream - > write ( data , length ) ;
if ( check ! = length )
png_error ( png_ptr , " Write Error " ) ;
}
2019-11-23 20:27:39 +08:00
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 " ) ;
}
}
# endif //CC_USE_PNG
}
2020-09-01 11:43:03 +08:00
/*
2020-10-24 21:57:36 +08:00
* Notes : PVR file Specification have many pixel formats , cocos2d - x - v2 ~ v4 and engine - x only support pvrtc and etc1
2020-09-01 11:43:03 +08:00
* see : https : //cdn.imgtec.com/sdk-documentation/PVR+File+Format.Specification.pdf
*/
static backend : : PixelFormat getDevicePVRPixelFormat ( backend : : PixelFormat format )
2019-11-23 20:27:39 +08:00
{
switch ( format ) {
case backend : : PixelFormat : : PVRTC4 :
case backend : : PixelFormat : : PVRTC4A :
case backend : : PixelFormat : : PVRTC2 :
case backend : : PixelFormat : : PVRTC2A :
if ( Configuration : : getInstance ( ) - > supportsPVRTC ( ) )
return format ;
else
2020-09-25 11:07:56 +08:00
return backend : : PixelFormat : : RGBA8 ;
2020-09-01 11:43:03 +08:00
case backend : : PixelFormat : : ETC1 :
if ( Configuration : : getInstance ( ) - > supportsETC1 ( ) )
2020-08-31 23:09:42 +08:00
return format ;
2020-09-01 11:43:03 +08:00
else if ( Configuration : : getInstance ( ) - > supportsETC2 ( ) )
return backend : : PixelFormat : : ETC2_RGB ;
2020-08-31 23:09:42 +08:00
else
2020-09-25 11:07:56 +08:00
return backend : : PixelFormat : : RGBA8 ;
2019-11-23 20:27:39 +08:00
default :
return format ;
}
}
2020-08-31 23:09:42 +08:00
namespace
{
bool testFormatForPvr2TCSupport ( PVR2TexturePixelFormat /*format*/ )
{
return true ;
}
bool testFormatForPvr3TCSupport ( PVR3TexturePixelFormat format )
{
switch ( format ) {
case PVR3TexturePixelFormat : : DXT1 :
case PVR3TexturePixelFormat : : DXT3 :
case PVR3TexturePixelFormat : : DXT5 :
return Configuration : : getInstance ( ) - > supportsS3TC ( ) ;
case PVR3TexturePixelFormat : : BGRA8888 :
return Configuration : : getInstance ( ) - > supportsBGRA8888 ( ) ;
case PVR3TexturePixelFormat : : PVRTC2BPP_RGB :
case PVR3TexturePixelFormat : : PVRTC2BPP_RGBA :
case PVR3TexturePixelFormat : : PVRTC4BPP_RGB :
case PVR3TexturePixelFormat : : PVRTC4BPP_RGBA :
case PVR3TexturePixelFormat : : ETC1 :
case PVR3TexturePixelFormat : : RGBA8888 :
case PVR3TexturePixelFormat : : RGBA4444 :
case PVR3TexturePixelFormat : : RGBA5551 :
case PVR3TexturePixelFormat : : RGB565 :
case PVR3TexturePixelFormat : : RGB888 :
case PVR3TexturePixelFormat : : A8 :
case PVR3TexturePixelFormat : : L8 :
case PVR3TexturePixelFormat : : LA88 :
return true ;
default :
return false ;
}
}
}
namespace
{
static uint32_t makeFourCC ( char ch0 , char ch1 , char ch2 , char ch3 )
{
const uint32_t fourCC = ( ( uint32_t ) ( char ) ( ch0 ) | ( ( uint32_t ) ( char ) ( ch1 ) < < 8 ) | ( ( uint32_t ) ( char ) ( ch2 ) < < 16 ) | ( ( uint32_t ) ( char ) ( ch3 ) < < 24 ) ) ;
return fourCC ;
}
}
2019-11-23 20:27:39 +08:00
//////////////////////////////////////////////////////////////////////////
// Implement Image
//////////////////////////////////////////////////////////////////////////
bool Image : : PNG_PREMULTIPLIED_ALPHA_ENABLED = true ;
2020-11-16 12:21:27 +08:00
uint32_t Image : : COMPRESSED_IMAGE_PMA_FLAGS = Image : : CompressedImagePMAFlag : : ETC1 ;
void Image : : setCompressedImagesHavePMA ( uint32_t targets , bool havePMA )
{
if ( havePMA )
COMPRESSED_IMAGE_PMA_FLAGS | = targets ;
else
COMPRESSED_IMAGE_PMA_FLAGS & = ~ targets ;
}
bool Image : : isCompressedImageHavePMA ( uint32_t target )
{
return target & COMPRESSED_IMAGE_PMA_FLAGS ;
}
2019-11-23 20:27:39 +08:00
Image : : Image ( )
: _data ( nullptr )
, _dataLen ( 0 )
, _offset ( 0 )
, _width ( 0 )
, _height ( 0 )
, _unpack ( false )
, _fileType ( Format : : UNKNOWN )
, _pixelFormat ( backend : : PixelFormat : : NONE )
, _numberOfMipmaps ( 0 )
, _hasPremultipliedAlpha ( false )
{
}
Image : : ~ Image ( )
{
2020-08-31 21:04:47 +08:00
if ( ! _unpack )
2019-11-23 20:27:39 +08:00
{
2020-08-31 21:04:47 +08:00
CC_SAFE_FREE ( _data ) ;
}
else {
2019-11-23 20:27:39 +08:00
for ( int i = 0 ; i < _numberOfMipmaps ; + + i )
2020-08-31 21:04:47 +08:00
CC_SAFE_FREE ( _mipmaps [ i ] . address ) ;
2019-11-23 20:27:39 +08:00
}
}
bool Image : : initWithImageFile ( const std : : string & path )
{
bool ret = false ;
_filePath = FileUtils : : getInstance ( ) - > fullPathForFilename ( path ) ;
Data data = FileUtils : : getInstance ( ) - > getDataFromFile ( _filePath ) ;
if ( ! data . isNull ( ) )
{
ssize_t n = 0 ;
auto buf = data . takeBuffer ( & n ) ;
ret = initWithImageData ( buf , n , true ) ;
}
return ret ;
}
bool Image : : initWithImageFileThreadSafe ( const std : : string & fullpath )
{
bool ret = false ;
_filePath = fullpath ;
2020-08-18 14:29:09 +08:00
Data data = FileUtils : : getInstance ( ) - > getDataFromFile ( _filePath ) ;
2019-11-23 20:27:39 +08:00
if ( ! data . isNull ( ) )
{
ssize_t n = 0 ;
auto buf = data . takeBuffer ( & n ) ;
ret = initWithImageData ( buf , n , true ) ;
}
return ret ;
}
2021-05-24 13:26:56 +08:00
bool Image : : initWithImageData ( const uint8_t * data , ssize_t dataLen )
{
return initWithImageData ( const_cast < uint8_t * > ( data ) , dataLen , false ) ;
}
bool Image : : initWithImageData ( uint8_t * data , ssize_t dataLen , bool ownData )
2019-11-23 20:27:39 +08:00
{
bool ret = false ;
do
{
CC_BREAK_IF ( ! data | | dataLen = = 0 ) ;
2020-09-01 14:57:18 +08:00
uint8_t * unpackedData = nullptr ;
2019-11-23 20:27:39 +08:00
ssize_t unpackedLen = 0 ;
//detect and unzip the compress file
if ( ZipUtils : : isCCZBuffer ( data , dataLen ) )
{
unpackedLen = ZipUtils : : inflateCCZBuffer ( data , dataLen , & unpackedData ) ;
}
else if ( ZipUtils : : isGZipBuffer ( data , dataLen ) )
{
2020-09-01 14:57:18 +08:00
unpackedLen = ZipUtils : : inflateMemory ( const_cast < uint8_t * > ( data ) , dataLen , & unpackedData ) ;
2019-11-23 20:27:39 +08:00
}
else
{
2020-09-01 14:57:18 +08:00
unpackedData = const_cast < uint8_t * > ( data ) ;
2019-11-23 20:27:39 +08:00
unpackedLen = dataLen ;
}
2020-08-31 21:04:47 +08:00
if ( unpackedData ! = data )
{ // free old data and own the unpackedData
if ( ownData ) free ( ( void * ) data ) ;
ownData = true ;
}
2019-11-23 20:27:39 +08:00
_fileType = detectFormat ( unpackedData , unpackedLen ) ;
switch ( _fileType )
{
case Format : : PNG :
ret = initWithPngData ( unpackedData , unpackedLen ) ;
break ;
case Format : : JPG :
ret = initWithJpgData ( unpackedData , unpackedLen ) ;
break ;
case Format : : WEBP :
ret = initWithWebpData ( unpackedData , unpackedLen ) ;
break ;
case Format : : PVR :
2020-08-31 23:09:42 +08:00
ret = initWithPVRData ( unpackedData , unpackedLen , ownData ) ;
2019-11-23 20:27:39 +08:00
break ;
2020-09-01 11:43:03 +08:00
case Format : : ETC1 :
2020-08-31 23:09:42 +08:00
ret = initWithETCData ( unpackedData , unpackedLen , ownData ) ;
2020-08-31 21:04:47 +08:00
break ;
case Format : : ETC2 :
2020-08-31 23:09:42 +08:00
ret = initWithETC2Data ( unpackedData , unpackedLen , ownData ) ;
2019-11-23 20:27:39 +08:00
break ;
case Format : : S3TC :
2020-08-31 23:09:42 +08:00
ret = initWithS3TCData ( unpackedData , unpackedLen , ownData ) ;
2019-11-23 20:27:39 +08:00
break ;
case Format : : ATITC :
2020-08-31 23:09:42 +08:00
ret = initWithATITCData ( unpackedData , unpackedLen , ownData ) ;
2019-11-23 20:27:39 +08:00
break ;
2020-02-12 20:24:29 +08:00
case Format : : ASTC :
2020-08-31 23:09:42 +08:00
ret = initWithASTCData ( unpackedData , unpackedLen , ownData ) ;
2020-02-12 20:24:29 +08:00
break ;
2019-11-25 02:28:38 +08:00
case Format : : BMP :
ret = initWithBmpData ( unpackedData , unpackedLen ) ;
break ;
2019-11-23 20:27:39 +08:00
default :
{
// load and detect image format
tImageTGA * tgaData = tgaLoadBuffer ( unpackedData , unpackedLen ) ;
if ( tgaData ! = nullptr & & tgaData - > status = = TGA_OK )
{
ret = initWithTGAData ( tgaData ) ;
}
else
{
CCLOG ( " cocos2d: unsupported image format! " ) ;
}
free ( tgaData ) ;
break ;
}
}
2020-08-31 21:04:47 +08:00
if ( _data ! = unpackedData & & ownData ) free ( unpackedData ) ;
// else, the hardware texture decoder used, the compressed data was stored directly
2019-11-23 20:27:39 +08:00
} while ( 0 ) ;
return ret ;
}
2020-09-01 14:57:18 +08:00
bool Image : : initWithRawData ( const uint8_t * data , ssize_t /*dataLen*/ , int width , int height , int /*bitsPerComponent*/ , bool preMulti )
2020-09-01 11:43:03 +08:00
{
bool ret = false ;
do
{
CC_BREAK_IF ( 0 = = width | | 0 = = height ) ;
_height = height ;
_width = width ;
_hasPremultipliedAlpha = preMulti ;
2020-09-25 11:07:56 +08:00
_pixelFormat = backend : : PixelFormat : : RGBA8 ;
2020-09-01 11:43:03 +08:00
// only RGBA8888 supported
int bytesPerComponent = 4 ;
_dataLen = height * width * bytesPerComponent ;
2020-09-01 14:57:18 +08:00
_data = static_cast < uint8_t * > ( malloc ( _dataLen ) ) ;
2020-09-01 11:43:03 +08:00
CC_BREAK_IF ( ! _data ) ;
memcpy ( _data , data , _dataLen ) ;
ret = true ;
} while ( 0 ) ;
return ret ;
}
2020-09-01 14:57:18 +08:00
bool Image : : isPng ( const uint8_t * data , ssize_t dataLen )
2019-11-23 20:27:39 +08:00
{
if ( dataLen < = 8 )
{
return false ;
}
2020-09-01 14:57:18 +08:00
static const uint8_t PNG_SIGNATURE [ ] = { 0x89 , 0x50 , 0x4e , 0x47 , 0x0d , 0x0a , 0x1a , 0x0a } ;
2019-11-23 20:27:39 +08:00
return memcmp ( PNG_SIGNATURE , data , sizeof ( PNG_SIGNATURE ) ) = = 0 ;
}
2020-09-01 14:57:18 +08:00
bool Image : : isBmp ( const uint8_t * data , ssize_t dataLen )
2019-11-25 02:28:38 +08:00
{
return dataLen > 54 & & data [ 0 ] = = ' B ' & & data [ 1 ] = = ' M ' ;
}
2019-11-23 20:27:39 +08:00
2020-09-01 14:57:18 +08:00
bool Image : : isEtc1 ( const uint8_t * data , ssize_t /*dataLen*/ )
2019-11-23 20:27:39 +08:00
{
2020-08-31 21:04:47 +08:00
return ! ! etc1_pkm_is_valid ( ( etc1_byte * ) data ) ;
2019-11-23 20:27:39 +08:00
}
2020-09-01 14:57:18 +08:00
bool Image : : isEtc2 ( const uint8_t * data , ssize_t dataLen )
2020-08-31 21:04:47 +08:00
{
return ! ! etc2_pkm_is_valid ( ( etc2_byte * ) data ) ;
}
2019-11-23 20:27:39 +08:00
2020-09-01 14:57:18 +08:00
bool Image : : isS3TC ( const uint8_t * data , ssize_t /*dataLen*/ )
2019-11-23 20:27:39 +08:00
{
S3TCTexHeader * header = ( S3TCTexHeader * ) data ;
2020-08-31 21:04:47 +08:00
return ( strncmp ( header - > fileCode , " DDS " , 3 ) = = 0 ) ;
2019-11-23 20:27:39 +08:00
}
2020-09-01 14:57:18 +08:00
bool Image : : isATITC ( const uint8_t * data , ssize_t /*dataLen*/ )
2019-11-23 20:27:39 +08:00
{
ATITCTexHeader * header = ( ATITCTexHeader * ) data ;
2020-08-31 21:04:47 +08:00
return ( strncmp ( & header - > identifier [ 1 ] , " KTX " , 3 ) = = 0 ) ;
2019-11-23 20:27:39 +08:00
}
2020-09-01 14:57:18 +08:00
bool Image : : isASTC ( const uint8_t * data , ssize_t /*dataLen*/ )
2020-02-12 20:24:29 +08:00
{
ASTCTexHeader * hdr = ( ASTCTexHeader * ) data ;
uint32_t magicval = hdr - > magic [ 0 ] + 256 * ( uint32_t ) ( hdr - > magic [ 1 ] ) + 65536 * ( uint32_t ) ( hdr - > magic [ 2 ] ) + 16777216 * ( uint32_t ) ( hdr - > magic [ 3 ] ) ;
2020-08-31 21:04:47 +08:00
return ( magicval = = ASTC_MAGIC_FILE_CONSTANT ) ;
2020-02-12 20:24:29 +08:00
}
2020-09-01 14:57:18 +08:00
bool Image : : isJpg ( const uint8_t * data , ssize_t dataLen )
2019-11-23 20:27:39 +08:00
{
if ( dataLen < = 4 )
{
return false ;
}
2020-09-01 14:57:18 +08:00
static const uint8_t JPG_SOI [ ] = { 0xFF , 0xD8 } ;
2019-11-23 20:27:39 +08:00
return memcmp ( data , JPG_SOI , 2 ) = = 0 ;
}
2020-09-01 14:57:18 +08:00
bool Image : : isWebp ( const uint8_t * data , ssize_t dataLen )
2019-11-23 20:27:39 +08:00
{
if ( dataLen < = 12 )
{
return false ;
}
static const char * WEBP_RIFF = " RIFF " ;
static const char * WEBP_WEBP = " WEBP " ;
return memcmp ( data , WEBP_RIFF , 4 ) = = 0
2020-09-01 14:57:18 +08:00
& & memcmp ( static_cast < const uint8_t * > ( data ) + 8 , WEBP_WEBP , 4 ) = = 0 ;
2019-11-23 20:27:39 +08:00
}
2020-09-01 14:57:18 +08:00
bool Image : : isPvr ( const uint8_t * data , ssize_t dataLen )
2019-11-23 20:27:39 +08:00
{
if ( static_cast < size_t > ( dataLen ) < sizeof ( PVRv2TexHeader ) | | static_cast < size_t > ( dataLen ) < sizeof ( PVRv3TexHeader ) )
{
return false ;
}
const PVRv2TexHeader * headerv2 = static_cast < const PVRv2TexHeader * > ( static_cast < const void * > ( data ) ) ;
const PVRv3TexHeader * headerv3 = static_cast < const PVRv3TexHeader * > ( static_cast < const void * > ( data ) ) ;
return memcmp ( & headerv2 - > pvrTag , gPVRTexIdentifier , strlen ( gPVRTexIdentifier ) ) = = 0 | | CC_SWAP_INT32_BIG_TO_HOST ( headerv3 - > version ) = = 0x50565203 ;
}
2020-09-01 14:57:18 +08:00
Image : : Format Image : : detectFormat ( const uint8_t * data , ssize_t dataLen )
2019-11-23 20:27:39 +08:00
{
if ( isPng ( data , dataLen ) )
{
return Format : : PNG ;
}
else if ( isJpg ( data , dataLen ) )
{
return Format : : JPG ;
}
2019-11-25 02:28:38 +08:00
else if ( isBmp ( data , dataLen ) )
{
return Format : : BMP ;
}
2019-11-23 20:27:39 +08:00
else if ( isWebp ( data , dataLen ) )
{
return Format : : WEBP ;
}
else if ( isPvr ( data , dataLen ) )
{
return Format : : PVR ;
}
2020-09-01 11:43:03 +08:00
else if ( isEtc1 ( data , dataLen ) )
2019-11-23 20:27:39 +08:00
{
2020-09-01 11:43:03 +08:00
return Format : : ETC1 ;
2019-11-23 20:27:39 +08:00
}
2020-08-31 21:04:47 +08:00
else if ( isEtc2 ( data , dataLen ) )
{
return Format : : ETC2 ;
}
2019-11-23 20:27:39 +08:00
else if ( isS3TC ( data , dataLen ) )
{
return Format : : S3TC ;
}
else if ( isATITC ( data , dataLen ) )
{
return Format : : ATITC ;
}
2020-02-12 20:24:29 +08:00
else if ( isASTC ( data , dataLen ) )
{
return Format : : ASTC ;
}
2019-11-23 20:27:39 +08:00
else
{
return Format : : UNKNOWN ;
}
}
int Image : : getBitPerPixel ( )
{
2020-11-02 16:47:07 +08:00
return backend : : PixelFormatUtils : : getFormatDescriptor ( _pixelFormat ) . bpp ;
2019-11-23 20:27:39 +08:00
}
bool Image : : hasAlpha ( )
{
2020-09-25 11:07:56 +08:00
return backend : : PixelFormatUtils : : getFormatDescriptor ( _pixelFormat ) . alpha ;
2019-11-23 20:27:39 +08:00
}
bool Image : : isCompressed ( )
{
2020-09-25 11:07:56 +08:00
return backend : : PixelFormatUtils : : isCompressed ( _pixelFormat ) ;
2019-11-23 20:27:39 +08:00
}
namespace
{
/*
* 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 :
*/
# if CC_USE_JPEG
struct MyErrorMgr
{
struct jpeg_error_mgr pub ; /* "public" fields */
jmp_buf setjmp_buffer ; /* for return to caller */
} ;
typedef struct MyErrorMgr * MyErrorPtr ;
/*
* Here ' s the routine that will replace the standard error_exit method :
*/
METHODDEF ( void )
myErrorExit ( j_common_ptr cinfo )
{
/* cinfo->err really points to a MyErrorMgr struct, so coerce pointer */
MyErrorPtr myerr = ( MyErrorPtr ) cinfo - > err ;
/* Always display the message. */
/* We could postpone this until after returning, if we chose. */
/* internal message function can't show error message in some platforms, so we rewrite it here.
* edit it if has version conflict .
*/
//(*cinfo->err->output_message) (cinfo);
char buffer [ JMSG_LENGTH_MAX ] ;
( * cinfo - > err - > format_message ) ( cinfo , buffer ) ;
CCLOG ( " jpeg error: %s " , buffer ) ;
/* Return control to the setjmp point */
longjmp ( myerr - > setjmp_buffer , 1 ) ;
}
# endif // CC_USE_JPEG
}
2020-09-01 14:57:18 +08:00
bool Image : : initWithJpgData ( uint8_t * data , ssize_t dataLen )
2019-11-23 20:27:39 +08:00
{
# if CC_USE_JPEG
/* 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 MyErrorMgr jerr ;
/* libjpeg data structure for storing one row, that is, scanline of an image */
JSAMPROW row_pointer [ 1 ] = { 0 } ;
unsigned long location = 0 ;
bool ret = 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 = myErrorExit ;
/* Establish the setjmp return context for MyErrorExit 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 .
*/
jpeg_destroy_decompress ( & cinfo ) ;
break ;
}
/* setup decompression process and source, then read JPEG header */
jpeg_create_decompress ( & cinfo ) ;
# ifndef CC_TARGET_QT5
2020-09-01 14:57:18 +08:00
jpeg_mem_src ( & cinfo , const_cast < uint8_t * > ( data ) , dataLen ) ;
2019-11-23 20:27:39 +08:00
# endif /* CC_TARGET_QT5 */
/* 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 //(JPEG_LIB_VERSION >= 90)
// we only support RGB or grayscale
if ( cinfo . jpeg_color_space = = JCS_GRAYSCALE )
{
2020-09-25 11:07:56 +08:00
_pixelFormat = backend : : PixelFormat : : L8 ;
2019-11-23 20:27:39 +08:00
} else
{
cinfo . out_color_space = JCS_RGB ;
2020-09-25 11:07:56 +08:00
_pixelFormat = backend : : PixelFormat : : RGB8 ;
2019-11-23 20:27:39 +08:00
}
/* Start decompression jpeg here */
jpeg_start_decompress ( & cinfo ) ;
/* init image info */
_width = cinfo . output_width ;
_height = cinfo . output_height ;
_dataLen = cinfo . output_width * cinfo . output_height * cinfo . output_components ;
2020-09-01 14:57:18 +08:00
_data = static_cast < uint8_t * > ( malloc ( _dataLen ) ) ;
2019-11-23 20:27:39 +08:00
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 )
{
row_pointer [ 0 ] = _data + location ;
location + = cinfo . output_width * cinfo . output_components ;
jpeg_read_scanlines ( & cinfo , row_pointer , 1 ) ;
}
/* 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 */
ret = true ;
} while ( 0 ) ;
return ret ;
# else
CCLOG ( " jpeg is not enabled, please enable it in ccConfig.h " ) ;
return false ;
# endif // CC_USE_JPEG
}
2020-09-01 14:57:18 +08:00
bool Image : : initWithPngData ( uint8_t * data , ssize_t dataLen )
2019-11-23 20:27:39 +08:00
{
# if CC_USE_PNG
// length of bytes to check if it is a valid png file
# define PNGSIGSIZE 8
bool ret = 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 ( dataLen < PNGSIGSIZE ) ;
// check the data is png or not
memcpy ( header , data , 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 ) ;
CC_BREAK_IF ( setjmp ( png_jmpbuf ( png_ptr ) ) ) ;
// set the read call back function
tImageSource imageSource ;
2020-09-01 14:57:18 +08:00
imageSource . data = ( uint8_t * ) data ;
2019-11-23 20:27:39 +08:00
imageSource . size = dataLen ;
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 ) ;
png_byte bit_depth = 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 & & bit_depth < 8 )
{
bit_depth = 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 ( bit_depth = = 16 )
{
png_set_strip_16 ( png_ptr ) ;
}
// Expanded earlier for grayscale, now take care of palette and rgb
if ( bit_depth < 8 )
{
png_set_packing ( png_ptr ) ;
}
// update info
png_read_update_info ( png_ptr , info_ptr ) ;
color_type = png_get_color_type ( png_ptr , info_ptr ) ;
switch ( color_type )
{
case PNG_COLOR_TYPE_GRAY :
2020-09-25 11:07:56 +08:00
_pixelFormat = backend : : PixelFormat : : L8 ;
2019-11-23 20:27:39 +08:00
break ;
case PNG_COLOR_TYPE_GRAY_ALPHA :
2020-09-25 11:07:56 +08:00
_pixelFormat = backend : : PixelFormat : : LA8 ;
2019-11-23 20:27:39 +08:00
break ;
case PNG_COLOR_TYPE_RGB :
2020-09-25 11:07:56 +08:00
_pixelFormat = backend : : PixelFormat : : RGB8 ;
2019-11-23 20:27:39 +08:00
break ;
case PNG_COLOR_TYPE_RGB_ALPHA :
2020-09-25 11:07:56 +08:00
_pixelFormat = backend : : PixelFormat : : RGBA8 ;
2019-11-23 20:27:39 +08:00
break ;
default :
break ;
}
// read png data
png_size_t rowbytes ;
png_bytep * row_pointers = ( png_bytep * ) malloc ( sizeof ( png_bytep ) * _height ) ;
rowbytes = png_get_rowbytes ( png_ptr , info_ptr ) ;
_dataLen = rowbytes * _height ;
2020-09-01 14:57:18 +08:00
_data = static_cast < uint8_t * > ( malloc ( _dataLen ) ) ;
2019-11-23 20:27:39 +08:00
if ( ! _data )
{
if ( row_pointers ! = nullptr )
{
free ( row_pointers ) ;
}
break ;
}
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 , nullptr ) ;
// premultiplied alpha for RGBA8888
if ( color_type = = PNG_COLOR_TYPE_RGB_ALPHA )
{
if ( PNG_PREMULTIPLIED_ALPHA_ENABLED )
{
premultiplyAlpha ( ) ;
}
else
2020-11-16 12:21:27 +08:00
{
// if PNG_PREMULTIPLIED_ALPHA_ENABLED == false && CC_ENABLE_PREMULTIPLIED_ALPHA != 0,
// you must do PMA at shader, such as modify positionTextureColor.frag
_hasPremultipliedAlpha = ! ! CC_ENABLE_PREMULTIPLIED_ALPHA ;
2019-11-23 20:27:39 +08:00
}
}
if ( row_pointers ! = nullptr )
{
free ( row_pointers ) ;
}
ret = true ;
} while ( 0 ) ;
if ( png_ptr )
{
png_destroy_read_struct ( & png_ptr , ( info_ptr ) ? & info_ptr : 0 , 0 ) ;
}
return ret ;
# else
CCLOG ( " png is not enabled, please enable it in ccConfig.h " ) ;
return false ;
# endif //CC_USE_PNG
}
2020-09-01 14:57:18 +08:00
bool Image : : initWithBmpData ( uint8_t * data , ssize_t dataLen )
2019-11-25 02:28:38 +08:00
{
2020-09-01 12:06:21 +08:00
const int nrChannels = 4 ;
2019-11-25 02:28:38 +08:00
_data = stbi_load_from_memory ( data , dataLen , & _width , & _height , nullptr , nrChannels ) ;
if ( _data ) {
_dataLen = _width * _height * nrChannels ;
_fileType = Format : : BMP ;
2020-09-25 11:07:56 +08:00
_pixelFormat = backend : : PixelFormat : : RGBA8 ;
2020-09-01 12:06:21 +08:00
return true ;
2019-11-25 02:28:38 +08:00
}
2020-09-01 12:06:21 +08:00
return false ;
2019-11-25 02:28:38 +08:00
}
2020-09-01 14:57:18 +08:00
bool Image : : initWithWebpData ( uint8_t * data , ssize_t dataLen )
2020-09-01 11:43:03 +08:00
{
# if CC_USE_WEBP
bool ret = false ;
do
{
WebPDecoderConfig config ;
if ( WebPInitDecoderConfig ( & config ) = = 0 ) break ;
if ( WebPGetFeatures ( static_cast < const uint8_t * > ( data ) , dataLen , & config . input ) ! = VP8_STATUS_OK ) break ;
if ( config . input . width = = 0 | | config . input . height = = 0 ) break ;
config . output . colorspace = config . input . has_alpha ? MODE_rgbA : MODE_RGB ;
2020-09-25 11:07:56 +08:00
_pixelFormat = config . input . has_alpha ? backend : : PixelFormat : : RGBA8 : backend : : PixelFormat : : RGB8 ;
2020-09-01 11:43:03 +08:00
_width = config . input . width ;
_height = config . input . height ;
//we ask webp to give data with premultiplied alpha
_hasPremultipliedAlpha = ( config . input . has_alpha ! = 0 ) ;
_dataLen = _width * _height * ( config . input . has_alpha ? 4 : 3 ) ;
2020-09-01 14:57:18 +08:00
_data = static_cast < uint8_t * > ( malloc ( _dataLen ) ) ;
2020-09-01 11:43:03 +08:00
config . output . u . RGBA . rgba = static_cast < uint8_t * > ( _data ) ;
config . output . u . RGBA . stride = _width * ( config . input . has_alpha ? 4 : 3 ) ;
config . output . u . RGBA . size = _dataLen ;
config . output . is_external_memory = 1 ;
if ( WebPDecode ( static_cast < const uint8_t * > ( data ) , dataLen , & config ) ! = VP8_STATUS_OK )
{
free ( _data ) ;
_data = nullptr ;
break ;
}
ret = true ;
} while ( 0 ) ;
return ret ;
# else
CCLOG ( " webp is not enabled, please enable it in ccConfig.h " ) ;
return false ;
# endif // CC_USE_WEBP
}
2020-08-31 23:09:42 +08:00
bool Image : : initWithTGAData ( tImageTGA * tgaData )
2019-11-23 20:27:39 +08:00
{
2020-08-31 23:09:42 +08:00
bool ret = false ;
do
2019-11-23 20:27:39 +08:00
{
2020-08-31 23:09:42 +08:00
CC_BREAK_IF ( tgaData = = nullptr ) ;
// tgaLoadBuffer only support type 2, 3, 10
if ( 2 = = tgaData - > type | | 10 = = tgaData - > type )
{
// true color
// unsupported RGB555
if ( tgaData - > pixelDepth = = 16 )
{
_pixelFormat = backend : : PixelFormat : : RGB5A1 ;
}
else if ( tgaData - > pixelDepth = = 24 )
{
2020-09-25 11:07:56 +08:00
_pixelFormat = backend : : PixelFormat : : RGB8 ;
2020-08-31 23:09:42 +08:00
}
else if ( tgaData - > pixelDepth = = 32 )
{
2020-09-25 11:07:56 +08:00
_pixelFormat = backend : : PixelFormat : : RGBA8 ;
2020-08-31 23:09:42 +08:00
}
else
{
CCLOG ( " Image WARNING: unsupported true color tga data pixel format. FILE: %s " , _filePath . c_str ( ) ) ;
break ;
}
}
else if ( 3 = = tgaData - > type )
{
// gray
if ( 8 = = tgaData - > pixelDepth )
{
2020-09-25 11:07:56 +08:00
_pixelFormat = backend : : PixelFormat : : L8 ;
2020-08-31 23:09:42 +08:00
}
else
{
// actually this won't happen, if it happens, maybe the image file is not a tga
CCLOG ( " Image WARNING: unsupported gray tga data pixel format. FILE: %s " , _filePath . c_str ( ) ) ;
break ;
}
}
_width = tgaData - > width ;
_height = tgaData - > height ;
_data = tgaData - > imageData ;
_dataLen = _width * _height * tgaData - > pixelDepth / 8 ;
_fileType = Format : : TGA ;
ret = true ;
} while ( false ) ;
2019-11-23 20:27:39 +08:00
2020-08-31 23:09:42 +08:00
if ( ret )
2019-11-23 20:27:39 +08:00
{
2020-08-31 23:09:42 +08:00
if ( FileUtils : : getInstance ( ) - > getFileExtension ( _filePath ) ! = " .tga " )
{
CCLOG ( " Image WARNING: the image file suffix is not tga, but parsed as a tga image file. FILE: %s " , _filePath . c_str ( ) ) ;
2019-11-23 20:27:39 +08:00
}
}
2020-08-31 23:09:42 +08:00
else
{
if ( tgaData & & tgaData - > imageData ! = nullptr )
{
free ( tgaData - > imageData ) ;
_data = nullptr ;
}
}
return ret ;
2019-11-23 20:27:39 +08:00
}
2020-09-01 14:57:18 +08:00
bool Image : : initWithPVRv2Data ( uint8_t * data , ssize_t dataLen , bool ownData )
2019-11-23 20:27:39 +08:00
{
int blockSize = 0 , widthBlocks = 0 , heightBlocks = 0 ;
int width = 0 , height = 0 ;
//Cast first sizeof(PVRTexHeader) bytes of data stream as PVRTexHeader
const PVRv2TexHeader * header = static_cast < const PVRv2TexHeader * > ( static_cast < const void * > ( data ) ) ;
//Make sure that tag is in correct formatting
if ( memcmp ( & header - > pvrTag , gPVRTexIdentifier , strlen ( gPVRTexIdentifier ) ) ! = 0 )
{
return false ;
}
Configuration * configuration = Configuration : : getInstance ( ) ;
//can not detect the premultiplied alpha from pvr file, use _PVRHaveAlphaPremultiplied instead.
2020-11-16 12:21:27 +08:00
_hasPremultipliedAlpha = isCompressedImageHavePMA ( CompressedImagePMAFlag : : PVR ) ;
2019-11-23 20:27:39 +08:00
unsigned int flags = CC_SWAP_INT32_LITTLE_TO_HOST ( header - > flags ) ;
PVR2TexturePixelFormat formatFlags = static_cast < PVR2TexturePixelFormat > ( flags & PVR_TEXTURE_FLAG_TYPE_MASK ) ;
bool flipped = ( flags & ( unsigned int ) PVR2TextureFlag : : VerticalFlip ) ? true : false ;
if ( flipped )
{
CCLOG ( " cocos2d: WARNING: Image is flipped. Regenerate it using PVRTexTool " ) ;
}
if ( ! configuration - > supportsNPOT ( ) & &
( static_cast < int > ( header - > width ) ! = ccNextPOT ( header - > width )
| | static_cast < int > ( header - > height ) ! = ccNextPOT ( header - > height ) ) )
{
CCLOG ( " cocos2d: ERROR: Loading an NPOT texture (%dx%d) but is not supported on this device " , header - > width , header - > height ) ;
return false ;
}
if ( ! testFormatForPvr2TCSupport ( formatFlags ) )
{
CCLOG ( " cocos2d: WARNING: Unsupported PVR Pixel Format: 0x%02X. Re-encode it with a OpenGL pixel format variant " , ( int ) formatFlags ) ;
return false ;
}
if ( v2_pixel_formathash . find ( formatFlags ) = = v2_pixel_formathash . end ( ) )
{
CCLOG ( " cocos2d: WARNING: Unsupported PVR Pixel Format: 0x%02X. Re-encode it with a OpenGL pixel format variant " , ( int ) formatFlags ) ;
return false ;
}
2020-09-25 11:07:56 +08:00
auto pixelFormat = getDevicePVRPixelFormat ( v2_pixel_formathash . at ( formatFlags ) ) ;
auto & info = backend : : PixelFormatUtils : : getFormatDescriptor ( pixelFormat ) ;
int bpp = info . bpp ;
if ( ! bpp )
2019-11-23 20:27:39 +08:00
{
CCLOG ( " cocos2d: WARNING: Unsupported PVR Pixel Format: 0x%02X. Re-encode it with a OpenGL pixel format variant " , ( int ) formatFlags ) ;
return false ;
}
2020-09-25 11:07:56 +08:00
_pixelFormat = pixelFormat ;
2019-11-23 20:27:39 +08:00
//Reset num of mipmaps
_numberOfMipmaps = 0 ;
//Get size of mipmap
_width = width = CC_SWAP_INT32_LITTLE_TO_HOST ( header - > width ) ;
_height = height = CC_SWAP_INT32_LITTLE_TO_HOST ( header - > height ) ;
//Move by size of header
2020-08-31 21:04:47 +08:00
const int pixelOffset = sizeof ( PVRv2TexHeader ) ;
2020-09-01 14:57:18 +08:00
uint8_t * pixelData = data + pixelOffset ;
2020-08-31 21:04:47 +08:00
int dataOffset = 0 , dataSize = 0 ;
//Get ptr to where data starts..
int dataLength = CC_SWAP_INT32_LITTLE_TO_HOST ( header - > dataLength ) ;
2019-11-23 20:27:39 +08:00
// Calculate the data size for each texture level and respect the minimum number of blocks
while ( dataOffset < dataLength )
{
switch ( formatFlags ) {
case PVR2TexturePixelFormat : : PVRTC2BPP_RGBA :
if ( ! Configuration : : getInstance ( ) - > supportsPVRTC ( ) )
{
CCLOG ( " cocos2d: Hardware PVR decoder not present. Using software decoder " ) ;
_unpack = true ;
_mipmaps [ _numberOfMipmaps ] . len = width * height * 4 ;
2020-09-01 14:57:18 +08:00
_mipmaps [ _numberOfMipmaps ] . address = ( uint8_t * ) malloc ( width * height * 4 ) ;
2020-08-31 21:04:47 +08:00
PVRTDecompressPVRTC ( pixelData + dataOffset , width , height , _mipmaps [ _numberOfMipmaps ] . address , true ) ;
2019-11-23 20:27:39 +08:00
bpp = 2 ;
}
blockSize = 8 * 4 ; // Pixel by pixel block size for 2bpp
widthBlocks = width / 8 ;
heightBlocks = height / 4 ;
break ;
case PVR2TexturePixelFormat : : PVRTC4BPP_RGBA :
if ( ! Configuration : : getInstance ( ) - > supportsPVRTC ( ) )
{
CCLOG ( " cocos2d: Hardware PVR decoder not present. Using software decoder " ) ;
_unpack = true ;
_mipmaps [ _numberOfMipmaps ] . len = width * height * 4 ;
2020-09-01 14:57:18 +08:00
_mipmaps [ _numberOfMipmaps ] . address = ( uint8_t * ) malloc ( width * height * 4 ) ;
2020-08-31 21:04:47 +08:00
PVRTDecompressPVRTC ( pixelData + dataOffset , width , height , _mipmaps [ _numberOfMipmaps ] . address , false ) ;
2019-11-23 20:27:39 +08:00
bpp = 4 ;
}
blockSize = 4 * 4 ; // Pixel by pixel block size for 4bpp
widthBlocks = width / 4 ;
heightBlocks = height / 4 ;
break ;
case PVR2TexturePixelFormat : : BGRA8888 :
2020-08-31 21:04:47 +08:00
if ( ! Configuration : : getInstance ( ) - > supportsBGRA8888 ( ) )
2019-11-23 20:27:39 +08:00
{
CCLOG ( " cocos2d: Image. BGRA8888 not supported on this device " ) ;
return false ;
}
default :
blockSize = 1 ;
widthBlocks = width ;
heightBlocks = height ;
break ;
}
// Clamp to minimum number of blocks
if ( widthBlocks < 2 )
{
widthBlocks = 2 ;
}
if ( heightBlocks < 2 )
{
heightBlocks = 2 ;
}
dataSize = widthBlocks * heightBlocks * ( ( blockSize * bpp ) / 8 ) ;
int packetLength = ( dataLength - dataOffset ) ;
packetLength = packetLength > dataSize ? dataSize : packetLength ;
//Make record to the mipmaps array and increment counter
if ( ! _unpack )
{
2020-08-31 21:04:47 +08:00
_mipmaps [ _numberOfMipmaps ] . address = pixelData + dataOffset ;
2019-11-23 20:27:39 +08:00
_mipmaps [ _numberOfMipmaps ] . len = packetLength ;
}
_numberOfMipmaps + + ;
dataOffset + = packetLength ;
//Update width and height to the next lower power of two
width = MAX ( width > > 1 , 1 ) ;
height = MAX ( height > > 1 , 1 ) ;
}
2020-08-31 21:04:47 +08:00
if ( ! _unpack ) { // hardware decoder, hold data directly
2020-08-31 23:09:42 +08:00
forwardPixels ( data , dataLen , pixelOffset , ownData ) ;
2020-08-31 21:04:47 +08:00
}
else {
2019-11-23 20:27:39 +08:00
_data = _mipmaps [ 0 ] . address ;
_dataLen = _mipmaps [ 0 ] . len ;
}
return true ;
}
2020-09-01 14:57:18 +08:00
bool Image : : initWithPVRv3Data ( uint8_t * data , ssize_t dataLen , bool ownData )
2019-11-23 20:27:39 +08:00
{
if ( static_cast < size_t > ( dataLen ) < sizeof ( PVRv3TexHeader ) )
{
return false ;
}
const PVRv3TexHeader * header = static_cast < const PVRv3TexHeader * > ( static_cast < const void * > ( data ) ) ;
// validate version
if ( CC_SWAP_INT32_BIG_TO_HOST ( header - > version ) ! = 0x50565203 )
{
CCLOG ( " cocos2d: WARNING: pvr file version mismatch " ) ;
return false ;
}
// parse pixel format
PVR3TexturePixelFormat pixelFormat = static_cast < PVR3TexturePixelFormat > ( header - > pixelFormat ) ;
if ( ! testFormatForPvr3TCSupport ( pixelFormat ) )
{
CCLOG ( " cocos2d: WARNING: Unsupported PVR Pixel Format: 0x%016llX. Re-encode it with a OpenGL pixel format variant " ,
static_cast < unsigned long long > ( pixelFormat ) ) ;
return false ;
}
if ( v3_pixel_formathash . find ( pixelFormat ) = = v3_pixel_formathash . end ( ) )
{
CCLOG ( " cocos2d: WARNING: Unsupported PVR Pixel Format: 0x%016llX. Re-encode it with a OpenGL pixel format variant " ,
static_cast < unsigned long long > ( pixelFormat ) ) ;
return false ;
}
2020-09-25 11:07:56 +08:00
auto finalPixelFormat = getDevicePVRPixelFormat ( v3_pixel_formathash . at ( pixelFormat ) ) ;
auto & info = backend : : PixelFormatUtils : : getFormatDescriptor ( finalPixelFormat ) ;
int bpp = info . bpp ;
if ( ! info . bpp )
2019-11-23 20:27:39 +08:00
{
CCLOG ( " cocos2d: WARNING: Unsupported PVR Pixel Format: 0x%016llX. Re-encode it with a OpenGL pixel format variant " ,
static_cast < unsigned long long > ( pixelFormat ) ) ;
return false ;
}
2020-09-25 11:07:56 +08:00
_pixelFormat = finalPixelFormat ;
2019-11-23 20:27:39 +08:00
// flags
int flags = CC_SWAP_INT32_LITTLE_TO_HOST ( header - > flags ) ;
// PVRv3 specifies premultiply alpha in a flag -- should always respect this in PVRv3 files
if ( flags & ( unsigned int ) PVR3TextureFlag : : PremultipliedAlpha )
{
_hasPremultipliedAlpha = true ;
}
// sizing
int width = CC_SWAP_INT32_LITTLE_TO_HOST ( header - > width ) ;
int height = CC_SWAP_INT32_LITTLE_TO_HOST ( header - > height ) ;
_width = width ;
_height = height ;
2020-08-31 21:04:47 +08:00
const int pixelOffset = ( sizeof ( PVRv3TexHeader ) + header - > metadataLength ) ;
2020-09-01 14:57:18 +08:00
uint8_t * pixelData = data + pixelOffset ;
2020-08-31 21:04:47 +08:00
int pixelLen = dataLen - pixelOffset ;
2019-11-23 20:27:39 +08:00
int dataOffset = 0 , dataSize = 0 ;
int blockSize = 0 , widthBlocks = 0 , heightBlocks = 0 ;
2020-08-31 21:04:47 +08:00
2019-11-23 20:27:39 +08:00
_numberOfMipmaps = header - > numberOfMipmaps ;
CCASSERT ( _numberOfMipmaps < MIPMAP_MAX , " Image: Maximum number of mimpaps reached. Increase the CC_MIPMAP_MAX value " ) ;
for ( int i = 0 ; i < _numberOfMipmaps ; i + + )
{
switch ( ( PVR3TexturePixelFormat ) pixelFormat )
{
2020-09-25 11:07:56 +08:00
case PVR3TexturePixelFormat : : PVRTC2BPP_RGB :
case PVR3TexturePixelFormat : : PVRTC2BPP_RGBA :
2019-11-23 20:27:39 +08:00
if ( ! Configuration : : getInstance ( ) - > supportsPVRTC ( ) )
{
CCLOG ( " cocos2d: Hardware PVR decoder not present. Using software decoder " ) ;
_unpack = true ;
_mipmaps [ i ] . len = width * height * 4 ;
2020-09-01 14:57:18 +08:00
_mipmaps [ i ] . address = ( uint8_t * ) malloc ( width * height * 4 ) ;
2020-08-31 21:04:47 +08:00
PVRTDecompressPVRTC ( pixelData + dataOffset , width , height , _mipmaps [ i ] . address , true ) ;
2019-11-23 20:27:39 +08:00
bpp = 2 ;
}
blockSize = 8 * 4 ; // Pixel by pixel block size for 2bpp
widthBlocks = width / 8 ;
heightBlocks = height / 4 ;
break ;
2020-09-25 11:07:56 +08:00
case PVR3TexturePixelFormat : : PVRTC4BPP_RGB :
case PVR3TexturePixelFormat : : PVRTC4BPP_RGBA :
2019-11-23 20:27:39 +08:00
if ( ! Configuration : : getInstance ( ) - > supportsPVRTC ( ) )
{
CCLOG ( " cocos2d: Hardware PVR decoder not present. Using software decoder " ) ;
_unpack = true ;
_mipmaps [ i ] . len = width * height * 4 ;
2020-09-01 14:57:18 +08:00
_mipmaps [ i ] . address = ( uint8_t * ) malloc ( width * height * 4 ) ;
2020-08-31 21:04:47 +08:00
PVRTDecompressPVRTC ( pixelData + dataOffset , width , height , _mipmaps [ i ] . address , false ) ;
2019-11-23 20:27:39 +08:00
bpp = 4 ;
}
blockSize = 4 * 4 ; // Pixel by pixel block size for 4bpp
widthBlocks = width / 4 ;
heightBlocks = height / 4 ;
break ;
case PVR3TexturePixelFormat : : ETC1 :
2020-09-01 11:43:03 +08:00
if ( ! Configuration : : getInstance ( ) - > supportsETC1 ( ) )
2019-11-23 20:27:39 +08:00
{
CCLOG ( " cocos2d: Hardware ETC1 decoder not present. Using software decoder " ) ;
2020-09-01 11:43:03 +08:00
const int bytePerPixel = 4 ;
2019-11-23 20:27:39 +08:00
_unpack = true ;
_mipmaps [ i ] . len = width * height * bytePerPixel ;
2020-09-01 14:57:18 +08:00
_mipmaps [ i ] . address = ( uint8_t * ) malloc ( width * height * bytePerPixel ) ;
2020-09-01 11:43:03 +08:00
if ( etc2_decode_image ( ETC2_RGB_NO_MIPMAPS , pixelData + dataOffset , static_cast < etc1_byte * > ( _mipmaps [ i ] . address ) , width , height ) ! = 0 )
2019-11-23 20:27:39 +08:00
{
return false ;
}
}
blockSize = 4 * 4 ; // Pixel by pixel block size for 4bpp
widthBlocks = width / 4 ;
heightBlocks = height / 4 ;
break ;
case PVR3TexturePixelFormat : : BGRA8888 :
if ( ! Configuration : : getInstance ( ) - > supportsBGRA8888 ( ) )
{
CCLOG ( " cocos2d: Image. BGRA8888 not supported on this device " ) ;
return false ;
}
default :
blockSize = 1 ;
widthBlocks = width ;
heightBlocks = height ;
break ;
}
// Clamp to minimum number of blocks
if ( widthBlocks < 2 )
{
widthBlocks = 2 ;
}
if ( heightBlocks < 2 )
{
heightBlocks = 2 ;
}
dataSize = widthBlocks * heightBlocks * ( ( blockSize * bpp ) / 8 ) ;
2020-08-31 21:04:47 +08:00
auto packetLength = pixelLen - dataOffset ;
2019-11-23 20:27:39 +08:00
packetLength = packetLength > dataSize ? dataSize : packetLength ;
if ( ! _unpack )
{
2020-08-31 21:04:47 +08:00
_mipmaps [ i ] . address = pixelData + dataOffset ;
2019-11-23 20:27:39 +08:00
_mipmaps [ i ] . len = static_cast < int > ( packetLength ) ;
}
dataOffset + = packetLength ;
2020-08-31 21:04:47 +08:00
CCASSERT ( dataOffset < = pixelLen , " Image: Invalid length " ) ;
2019-11-23 20:27:39 +08:00
width = MAX ( width > > 1 , 1 ) ;
height = MAX ( height > > 1 , 1 ) ;
}
2020-08-31 21:04:47 +08:00
if ( ! _unpack ) {
2020-08-31 23:09:42 +08:00
forwardPixels ( data , dataLen , pixelOffset , ownData ) ;
2020-08-31 21:04:47 +08:00
}
else {
2019-11-23 20:27:39 +08:00
_data = _mipmaps [ 0 ] . address ;
_dataLen = _mipmaps [ 0 ] . len ;
}
return true ;
}
2020-09-01 14:57:18 +08:00
bool Image : : initWithETCData ( uint8_t * data , ssize_t dataLen , bool ownData )
2019-11-23 20:27:39 +08:00
{
const etc1_byte * header = static_cast < const etc1_byte * > ( data ) ;
//check the data
if ( ! etc1_pkm_is_valid ( header ) )
{
return false ;
}
_width = etc1_pkm_get_width ( header ) ;
_height = etc1_pkm_get_height ( header ) ;
if ( 0 = = _width | | 0 = = _height )
{
return false ;
}
2020-08-31 21:04:47 +08:00
// GL_ETC1_RGB8_OES is not available in any desktop GL extension but the compression
// format is forwards compatible so just use the ETC2 format.
backend : : PixelFormat compressedFormat ;
2020-09-01 11:43:03 +08:00
if ( Configuration : : getInstance ( ) - > supportsETC1 ( ) )
compressedFormat = backend : : PixelFormat : : ETC1 ;
2020-08-31 21:04:47 +08:00
else if ( Configuration : : getInstance ( ) - > supportsETC2 ( ) )
compressedFormat = backend : : PixelFormat : : ETC2_RGB ;
else
compressedFormat = backend : : PixelFormat : : NONE ;
if ( compressedFormat ! = backend : : PixelFormat : : NONE )
2019-11-23 20:27:39 +08:00
{
2020-08-31 21:04:47 +08:00
_pixelFormat = compressedFormat ;
2020-08-31 23:09:42 +08:00
forwardPixels ( data , dataLen , ETC_PKM_HEADER_SIZE , ownData ) ;
2019-11-23 20:27:39 +08:00
return true ;
}
else
{
CCLOG ( " cocos2d: Hardware ETC1 decoder not present. Using software decoder " ) ;
2020-08-31 21:04:47 +08:00
_dataLen = _width * _height * 4 ;
2020-09-01 14:57:18 +08:00
_data = static_cast < uint8_t * > ( malloc ( _dataLen ) ) ;
if ( etc2_decode_image ( ETC2_RGB_NO_MIPMAPS , static_cast < const uint8_t * > ( data ) + ETC2_PKM_HEADER_SIZE , static_cast < etc2_byte * > ( _data ) , _width , _height ) = = 0 )
2020-09-01 11:43:03 +08:00
{ // if it is not gles or device do not support ETC1, decode texture by software
// directly decode ETC1_RGB to RGBA8888
2020-09-25 11:07:56 +08:00
_pixelFormat = backend : : PixelFormat : : RGBA8 ;
2020-09-01 11:43:03 +08:00
return true ;
2019-11-23 20:27:39 +08:00
}
2020-09-01 11:43:03 +08:00
// software decode fail, release pixels data
CC_SAFE_FREE ( _data ) ;
_dataLen = 0 ;
return false ;
2019-11-23 20:27:39 +08:00
}
}
2020-09-01 14:57:18 +08:00
bool Image : : initWithETC2Data ( uint8_t * data , ssize_t dataLen , bool ownData )
2020-08-31 21:04:47 +08:00
{
const etc2_byte * header = static_cast < const etc2_byte * > ( data ) ;
2020-11-16 12:21:27 +08:00
do {
//check the data
if ( ! etc2_pkm_is_valid ( header ) )
break ;
2020-08-31 21:04:47 +08:00
2020-11-16 12:21:27 +08:00
_width = etc2_pkm_get_width ( header ) ;
_height = etc2_pkm_get_height ( header ) ;
2020-08-31 21:04:47 +08:00
2020-11-16 12:21:27 +08:00
if ( 0 = = _width | | 0 = = _height )
break ;
2020-08-31 21:04:47 +08:00
2020-11-16 12:21:27 +08:00
etc2_uint32 format = etc2_pkm_get_format ( header ) ;
2020-08-31 21:04:47 +08:00
2020-11-16 12:21:27 +08:00
// We only support ETC2_RGBA_NO_MIPMAPS and ETC2_RGB_NO_MIPMAPS
assert ( format = = ETC2_RGBA_NO_MIPMAPS | | format = = ETC2_RGB_NO_MIPMAPS ) ;
2020-02-12 20:24:29 +08:00
2020-11-16 12:21:27 +08:00
if ( Configuration : : getInstance ( ) - > supportsETC2 ( ) ) {
_pixelFormat = format = = ETC2_RGBA_NO_MIPMAPS ? backend : : PixelFormat : : ETC2_RGBA : backend : : PixelFormat : : ETC2_RGB ;
2020-08-31 21:04:47 +08:00
2020-11-16 12:21:27 +08:00
forwardPixels ( data , dataLen , ETC2_PKM_HEADER_SIZE , ownData ) ;
}
else {
CCLOG ( " cocos2d: Hardware ETC2 decoder not present. Using software decoder " ) ;
// if device do not support ETC2, decode texture by software
// etc2_decode_image always decode to RGBA8888
_dataLen = _width * _height * 4 ;
_data = static_cast < uint8_t * > ( malloc ( _dataLen ) ) ;
if ( UTILS_UNLIKELY ( etc2_decode_image ( format , static_cast < const uint8_t * > ( data ) + ETC2_PKM_HEADER_SIZE , static_cast < etc2_byte * > ( _data ) , _width , _height ) ! = 0 ) )
{
// software decode fail, release pixels data
CC_SAFE_FREE ( _data ) ;
_dataLen = 0 ;
break ;
}
2020-09-25 11:07:56 +08:00
_pixelFormat = backend : : PixelFormat : : RGBA8 ;
2020-08-31 21:04:47 +08:00
}
2020-11-16 12:21:27 +08:00
_hasPremultipliedAlpha = isCompressedImageHavePMA ( CompressedImagePMAFlag : : ETC2 ) ;
return true ;
} while ( false ) ;
return false ;
2020-08-31 21:04:47 +08:00
}
2020-09-01 14:57:18 +08:00
bool Image : : initWithASTCData ( uint8_t * data , ssize_t dataLen , bool ownData )
2020-02-12 20:24:29 +08:00
{
ASTCTexHeader * header = ( ASTCTexHeader * ) data ;
2020-11-16 12:21:27 +08:00
do {
_width = header - > xsize [ 0 ] + 256 * header - > xsize [ 1 ] + 65536 * header - > xsize [ 2 ] ;
_height = header - > ysize [ 0 ] + 256 * header - > ysize [ 1 ] + 65536 * header - > ysize [ 2 ] ;
2020-02-12 20:24:29 +08:00
2020-11-16 12:21:27 +08:00
if ( 0 = = _width | | 0 = = _height )
break ;
2020-02-12 20:24:29 +08:00
2020-11-16 12:21:27 +08:00
uint8_t xdim = header - > blockdim_x ;
uint8_t ydim = header - > blockdim_y ;
2020-02-12 20:24:29 +08:00
2020-11-16 12:21:27 +08:00
if ( xdim < 4 | | ydim < 4 )
2020-02-12 20:24:29 +08:00
{
2020-11-16 12:21:27 +08:00
CCLOG ( " cocos2d: The ASTC block with and height should be >= 4 " ) ;
break ;
2020-02-12 20:24:29 +08:00
}
2020-11-16 12:21:27 +08:00
if ( Configuration : : getInstance ( ) - > supportsASTC ( ) )
{
if ( xdim = = 4 & & ydim = = 4 ) {
_pixelFormat = backend : : PixelFormat : : ASTC4x4 ;
}
else if ( xdim = = 5 & & ydim = = 5 ) {
_pixelFormat = backend : : PixelFormat : : ASTC5x5 ;
}
else if ( xdim = = 6 & & ydim = = 6 ) {
_pixelFormat = backend : : PixelFormat : : ASTC6x6 ;
}
else if ( xdim = = 8 & & ydim = = 5 ) {
_pixelFormat = backend : : PixelFormat : : ASTC8x5 ;
}
else if ( xdim = = 8 & & ydim = = 6 ) {
_pixelFormat = backend : : PixelFormat : : ASTC8x6 ;
}
else if ( xdim = = 8 & & ydim = = 8 )
{
_pixelFormat = backend : : PixelFormat : : ASTC8x8 ;
}
else if ( xdim = = 10 & & ydim = = 5 ) {
_pixelFormat = backend : : PixelFormat : : ASTC10x5 ;
}
forwardPixels ( data , dataLen , ASTC_HEAD_SIZE , ownData ) ;
2020-09-25 11:07:56 +08:00
}
2020-11-16 12:21:27 +08:00
else
{
CCLOG ( " cocos2d: Hardware ASTC decoder not present. Using software decoder " ) ;
2020-08-31 21:04:47 +08:00
2020-11-16 12:21:27 +08:00
_dataLen = _width * _height * 4 ;
_data = static_cast < uint8_t * > ( malloc ( _dataLen ) ) ;
if ( UTILS_UNLIKELY ( decompress_astc ( static_cast < const uint8_t * > ( data ) + ASTC_HEAD_SIZE , _data , _width , _height , xdim , ydim , _dataLen ) ! = 0 ) ) {
CC_SAFE_FREE ( _data ) ;
_dataLen = 0 ;
break ;
}
2020-02-12 20:24:29 +08:00
2020-09-25 11:07:56 +08:00
_pixelFormat = backend : : PixelFormat : : RGBA8 ;
2020-02-12 20:24:29 +08:00
}
2020-11-16 12:21:27 +08:00
_hasPremultipliedAlpha = isCompressedImageHavePMA ( CompressedImagePMAFlag : : ASTC ) ;
return true ;
} while ( false ) ;
return false ;
2020-02-12 20:24:29 +08:00
}
2020-09-01 14:57:18 +08:00
bool Image : : initWithS3TCData ( uint8_t * data , ssize_t dataLen , bool ownData )
2019-11-23 20:27:39 +08:00
{
const uint32_t FOURCC_DXT1 = makeFourCC ( ' D ' , ' X ' , ' T ' , ' 1 ' ) ;
const uint32_t FOURCC_DXT3 = makeFourCC ( ' D ' , ' X ' , ' T ' , ' 3 ' ) ;
const uint32_t FOURCC_DXT5 = makeFourCC ( ' D ' , ' X ' , ' T ' , ' 5 ' ) ;
/* load the .dds file */
S3TCTexHeader * header = ( S3TCTexHeader * ) data ;
_width = header - > ddsd . width ;
_height = header - > ddsd . height ;
_numberOfMipmaps = MAX ( 1 , header - > ddsd . DUMMYUNIONNAMEN2 . mipMapCount ) ; //if dds header reports 0 mipmaps, set to 1 to force correct software decoding (if needed).
_dataLen = 0 ;
int blockSize = ( FOURCC_DXT1 = = header - > ddsd . DUMMYUNIONNAMEN4 . ddpfPixelFormat . fourCC ) ? 8 : 16 ;
/* calculate the dataLen */
int width = _width ;
int height = _height ;
2020-08-31 21:04:47 +08:00
const int pixelOffset = sizeof ( S3TCTexHeader ) ;
2020-09-01 14:57:18 +08:00
uint8_t * pixelData = data + pixelOffset ;
2019-11-23 20:27:39 +08:00
2020-08-31 21:04:47 +08:00
bool hardware = Configuration : : getInstance ( ) - > supportsS3TC ( ) ;
2019-11-23 20:27:39 +08:00
/* if hardware supports s3tc, set pixelformat before loading mipmaps, to support non-mipmapped textures */
2020-08-31 21:04:47 +08:00
if ( hardware )
2019-11-23 20:27:39 +08:00
{ //decode texture through hardware
if ( FOURCC_DXT1 = = header - > ddsd . DUMMYUNIONNAMEN4 . ddpfPixelFormat . fourCC )
{
_pixelFormat = backend : : PixelFormat : : S3TC_DXT1 ;
}
else if ( FOURCC_DXT3 = = header - > ddsd . DUMMYUNIONNAMEN4 . ddpfPixelFormat . fourCC )
{
_pixelFormat = backend : : PixelFormat : : S3TC_DXT3 ;
}
else if ( FOURCC_DXT5 = = header - > ddsd . DUMMYUNIONNAMEN4 . ddpfPixelFormat . fourCC )
{
_pixelFormat = backend : : PixelFormat : : S3TC_DXT5 ;
}
} else { //will software decode
2020-09-25 11:07:56 +08:00
_pixelFormat = backend : : PixelFormat : : RGBA8 ;
2020-08-31 21:04:47 +08:00
// prepare data for software decompress
for ( int i = 0 ; i < _numberOfMipmaps & & ( width | | height ) ; + + i )
{
if ( width = = 0 ) width = 1 ;
if ( height = = 0 ) height = 1 ;
_dataLen + = ( height * width * 4 ) ;
width > > = 1 ;
height > > = 1 ;
}
2020-09-01 14:57:18 +08:00
_data = static_cast < uint8_t * > ( malloc ( _dataLen ) ) ;
2019-11-23 20:27:39 +08:00
}
/* load the mipmaps */
int encodeOffset = 0 ;
int decodeOffset = 0 ;
width = _width ; height = _height ;
for ( int i = 0 ; i < _numberOfMipmaps & & ( width | | height ) ; + + i )
{
if ( width = = 0 ) width = 1 ;
if ( height = = 0 ) height = 1 ;
int size = ( ( width + 3 ) / 4 ) * ( ( height + 3 ) / 4 ) * blockSize ;
if ( Configuration : : getInstance ( ) - > supportsS3TC ( ) )
{ //decode texture through hardware
2020-09-01 14:57:18 +08:00
_mipmaps [ i ] . address = ( uint8_t * ) pixelData + encodeOffset ;
2019-11-23 20:27:39 +08:00
_mipmaps [ i ] . len = size ;
}
else
{ //if it is not gles or device do not support S3TC, decode texture by software
CCLOG ( " cocos2d: Hardware S3TC decoder not present. Using software decoder " ) ;
int bytePerPixel = 4 ;
unsigned int stride = width * bytePerPixel ;
2020-09-01 14:57:18 +08:00
std : : vector < uint8_t > decodeImageData ( stride * height ) ;
2019-11-23 20:27:39 +08:00
if ( FOURCC_DXT1 = = header - > ddsd . DUMMYUNIONNAMEN4 . ddpfPixelFormat . fourCC )
{
s3tc_decode ( pixelData + encodeOffset , & decodeImageData [ 0 ] , width , height , S3TCDecodeFlag : : DXT1 ) ;
}
else if ( FOURCC_DXT3 = = header - > ddsd . DUMMYUNIONNAMEN4 . ddpfPixelFormat . fourCC )
{
s3tc_decode ( pixelData + encodeOffset , & decodeImageData [ 0 ] , width , height , S3TCDecodeFlag : : DXT3 ) ;
}
else if ( FOURCC_DXT5 = = header - > ddsd . DUMMYUNIONNAMEN4 . ddpfPixelFormat . fourCC )
{
s3tc_decode ( pixelData + encodeOffset , & decodeImageData [ 0 ] , width , height , S3TCDecodeFlag : : DXT5 ) ;
}
2020-09-01 14:57:18 +08:00
_mipmaps [ i ] . address = ( uint8_t * ) _data + decodeOffset ;
2019-11-23 20:27:39 +08:00
_mipmaps [ i ] . len = ( stride * height ) ;
memcpy ( ( void * ) _mipmaps [ i ] . address , ( void * ) & decodeImageData [ 0 ] , _mipmaps [ i ] . len ) ;
decodeOffset + = stride * height ;
}
2020-08-31 21:04:47 +08:00
2019-11-23 20:27:39 +08:00
encodeOffset + = size ;
width > > = 1 ;
height > > = 1 ;
}
/* end load the mipmaps */
2020-08-31 21:04:47 +08:00
if ( hardware ) {
2020-08-31 23:09:42 +08:00
forwardPixels ( data , dataLen , pixelOffset , ownData ) ;
2020-08-31 21:04:47 +08:00
}
2019-11-23 20:27:39 +08:00
return true ;
}
2020-09-01 14:57:18 +08:00
bool Image : : initWithATITCData ( uint8_t * data , ssize_t dataLen , bool ownData )
2019-11-23 20:27:39 +08:00
{
/* load the .ktx file */
ATITCTexHeader * header = ( ATITCTexHeader * ) data ;
_width = header - > pixelWidth ;
_height = header - > pixelHeight ;
_numberOfMipmaps = header - > numberOfMipmapLevels ;
int blockSize = 0 ;
switch ( header - > glInternalFormat )
{
case CC_GL_ATC_RGB_AMD :
blockSize = 8 ;
break ;
case CC_GL_ATC_RGBA_EXPLICIT_ALPHA_AMD :
blockSize = 16 ;
break ;
case CC_GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD :
blockSize = 16 ;
break ;
default :
break ;
}
/* pixelData point to the compressed data address */
2020-08-31 21:04:47 +08:00
int pixelOffset = sizeof ( ATITCTexHeader ) + header - > bytesOfKeyValueData + 4 ;
2020-09-01 14:57:18 +08:00
uint8_t * pixelData = ( uint8_t * ) data + pixelOffset ;
2019-11-23 20:27:39 +08:00
/* calculate the dataLen */
int width = _width ;
int height = _height ;
2020-08-31 21:04:47 +08:00
bool hardware = Configuration : : getInstance ( ) - > supportsATITC ( ) ;
if ( hardware ) //compressed data length
2019-11-23 20:27:39 +08:00
{
2020-08-31 21:04:47 +08:00
CCLOG ( " this is atitc H decode " ) ;
switch ( header - > glInternalFormat )
{
case CC_GL_ATC_RGB_AMD :
_pixelFormat = backend : : PixelFormat : : ATC_RGB ;
break ;
case CC_GL_ATC_RGBA_EXPLICIT_ALPHA_AMD :
_pixelFormat = backend : : PixelFormat : : ATC_EXPLICIT_ALPHA ;
break ;
case CC_GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD :
_pixelFormat = backend : : PixelFormat : : ATC_INTERPOLATED_ALPHA ;
break ;
default :
break ;
}
2019-11-23 20:27:39 +08:00
}
2020-08-31 21:04:47 +08:00
else // decompressed data length
{ /* if it is not gles or device do not support ATITC, decode texture by software */
CCLOG ( " cocos2d: Hardware ATITC decoder not present. Using software decoder " ) ;
2020-09-25 11:07:56 +08:00
_pixelFormat = backend : : PixelFormat : : RGBA8 ;
2020-08-31 21:04:47 +08:00
2019-11-23 20:27:39 +08:00
for ( int i = 0 ; i < _numberOfMipmaps & & ( width | | height ) ; + + i )
{
if ( width = = 0 ) width = 1 ;
if ( height = = 0 ) height = 1 ;
_dataLen + = ( height * width * 4 ) ;
width > > = 1 ;
height > > = 1 ;
}
2020-09-01 14:57:18 +08:00
_data = static_cast < uint8_t * > ( malloc ( _dataLen ) ) ;
2019-11-23 20:27:39 +08:00
}
/* load the mipmaps */
int encodeOffset = 0 ;
int decodeOffset = 0 ;
width = _width ; height = _height ;
for ( int i = 0 ; i < _numberOfMipmaps & & ( width | | height ) ; + + i )
{
if ( width = = 0 ) width = 1 ;
if ( height = = 0 ) height = 1 ;
int size = ( ( width + 3 ) / 4 ) * ( ( height + 3 ) / 4 ) * blockSize ;
2020-08-31 21:04:47 +08:00
if ( hardware )
2019-11-23 20:27:39 +08:00
{
/* decode texture through hardware */
2020-09-01 14:57:18 +08:00
_mipmaps [ i ] . address = ( uint8_t * ) pixelData + encodeOffset ;
2019-11-23 20:27:39 +08:00
_mipmaps [ i ] . len = size ;
}
else
{
int bytePerPixel = 4 ;
unsigned int stride = width * bytePerPixel ;
2020-08-31 21:04:47 +08:00
2020-09-01 14:57:18 +08:00
std : : vector < uint8_t > decodeImageData ( stride * height ) ;
2019-11-23 20:27:39 +08:00
switch ( header - > glInternalFormat )
{
case CC_GL_ATC_RGB_AMD :
atitc_decode ( pixelData + encodeOffset , & decodeImageData [ 0 ] , width , height , ATITCDecodeFlag : : ATC_RGB ) ;
break ;
case CC_GL_ATC_RGBA_EXPLICIT_ALPHA_AMD :
atitc_decode ( pixelData + encodeOffset , & decodeImageData [ 0 ] , width , height , ATITCDecodeFlag : : ATC_EXPLICIT_ALPHA ) ;
break ;
case CC_GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD :
atitc_decode ( pixelData + encodeOffset , & decodeImageData [ 0 ] , width , height , ATITCDecodeFlag : : ATC_INTERPOLATED_ALPHA ) ;
break ;
default :
break ;
}
2020-09-01 14:57:18 +08:00
_mipmaps [ i ] . address = ( uint8_t * ) _data + decodeOffset ;
2019-11-23 20:27:39 +08:00
_mipmaps [ i ] . len = ( stride * height ) ;
memcpy ( ( void * ) _mipmaps [ i ] . address , ( void * ) & decodeImageData [ 0 ] , _mipmaps [ i ] . len ) ;
decodeOffset + = stride * height ;
}
encodeOffset + = ( size + 4 ) ;
width > > = 1 ;
height > > = 1 ;
}
/* end load the mipmaps */
2020-08-31 21:04:47 +08:00
if ( hardware ) {
2020-08-31 23:09:42 +08:00
forwardPixels ( data , dataLen , pixelOffset , ownData ) ;
2020-08-31 21:04:47 +08:00
}
2019-11-23 20:27:39 +08:00
return true ;
}
2020-09-01 14:57:18 +08:00
bool Image : : initWithPVRData ( uint8_t * data , ssize_t dataLen , bool ownData )
2019-11-23 20:27:39 +08:00
{
2020-08-31 23:09:42 +08:00
return initWithPVRv2Data ( data , dataLen , ownData ) | | initWithPVRv3Data ( data , dataLen , ownData ) ;
}
2020-09-01 14:57:18 +08:00
void Image : : forwardPixels ( uint8_t * data , ssize_t dataLen , int offset , bool ownData )
2020-08-31 23:09:42 +08:00
{
if ( ownData ) {
_data = data ;
_dataLen = dataLen ;
_offset = offset ;
}
else {
_dataLen = dataLen - offset ;
2020-09-01 14:57:18 +08:00
_data = ( uint8_t * ) malloc ( _dataLen ) ;
2020-08-31 23:09:42 +08:00
memcpy ( _data , data + offset , _dataLen ) ;
}
2019-11-23 20:27:39 +08:00
}
# if (CC_TARGET_PLATFORM != CC_PLATFORM_IOS)
bool Image : : saveToFile ( const std : : string & filename , bool isToRGB )
{
2020-09-25 11:07:56 +08:00
//only support for backend::PixelFormat::RGB8 or backend::PixelFormat::RGBA8 uncompressed data
if ( isCompressed ( ) | | ( _pixelFormat ! = backend : : PixelFormat : : RGB8 & & _pixelFormat ! = backend : : PixelFormat : : RGBA8 ) )
2019-11-23 20:27:39 +08:00
{
2020-09-25 11:07:56 +08:00
CCLOG ( " cocos2d: Image: saveToFile is only support for backend::PixelFormat::RGB8 or backend::PixelFormat::RGBA8 uncompressed data for now " ) ;
2019-11-23 20:27:39 +08:00
return false ;
}
std : : string fileExtension = FileUtils : : getInstance ( ) - > getFileExtension ( filename ) ;
if ( fileExtension = = " .png " )
{
return saveImageToPNG ( filename , isToRGB ) ;
}
else if ( fileExtension = = " .jpg " )
{
return saveImageToJPG ( filename ) ;
}
else
{
CCLOG ( " cocos2d: Image: saveToFile no support file extension(only .png or .jpg) for file: %s " , filename . c_str ( ) ) ;
return false ;
}
}
# endif
bool Image : : saveImageToPNG ( const std : : string & filePath , bool isToRGB )
{
# if CC_USE_PNG
bool ret = false ;
do
{
png_structp png_ptr ;
png_infop info_ptr ;
png_bytep * row_pointers ;
2021-04-22 19:49:43 +08:00
2021-05-05 19:49:30 +08:00
auto outStream = FileUtils : : getInstance ( ) - > openFileStream ( filePath , FileStream : : Mode : : WRITE ) ;
2019-11-23 20:27:39 +08:00
2021-04-24 07:22:27 +08:00
CC_BREAK_IF ( nullptr = = outStream ) ;
2019-11-23 20:27:39 +08:00
png_ptr = png_create_write_struct ( PNG_LIBPNG_VER_STRING , nullptr , nullptr , nullptr ) ;
if ( nullptr = = png_ptr )
{
2021-05-05 19:49:30 +08:00
outStream . reset ( ) ;
2019-11-23 20:27:39 +08:00
break ;
}
info_ptr = png_create_info_struct ( png_ptr ) ;
if ( nullptr = = info_ptr )
{
2021-05-05 19:49:30 +08:00
outStream . reset ( ) ;
2019-11-23 20:27:39 +08:00
png_destroy_write_struct ( & png_ptr , nullptr ) ;
break ;
}
if ( setjmp ( png_jmpbuf ( png_ptr ) ) )
{
2021-05-05 19:49:30 +08:00
outStream . reset ( ) ;
2019-11-23 20:27:39 +08:00
png_destroy_write_struct ( & png_ptr , & info_ptr ) ;
break ;
}
2021-04-22 19:49:43 +08:00
//png_init_io(png_ptr, outStream);
2021-05-05 19:49:30 +08:00
png_set_write_fn ( png_ptr , outStream . get ( ) , pngWriteCallback , nullptr ) ;
2019-11-23 20:27:39 +08:00
if ( ! isToRGB & & 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 ) ;
}
png_write_info ( png_ptr , info_ptr ) ;
png_set_packing ( png_ptr ) ;
row_pointers = ( png_bytep * ) malloc ( _height * sizeof ( png_bytep ) ) ;
if ( row_pointers = = nullptr )
{
2021-05-05 19:49:30 +08:00
outStream . reset ( ) ;
2019-11-23 20:27:39 +08:00
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 = nullptr ;
}
else
{
if ( isToRGB )
{
2020-09-01 14:57:18 +08:00
uint8_t * tempData = static_cast < uint8_t * > ( malloc ( _width * _height * 3 ) ) ;
2019-11-23 20:27:39 +08:00
if ( nullptr = = tempData )
{
2021-05-05 19:49:30 +08:00
outStream . reset ( ) ;
2019-11-23 20:27:39 +08:00
png_destroy_write_struct ( & png_ptr , & info_ptr ) ;
free ( row_pointers ) ;
row_pointers = nullptr ;
break ;
}
for ( int i = 0 ; i < _height ; + + i )
{
for ( int j = 0 ; j < _width ; + + j )
{
tempData [ ( i * _width + j ) * 3 ] = _data [ ( i * _width + j ) * 4 ] ;
tempData [ ( i * _width + j ) * 3 + 1 ] = _data [ ( i * _width + j ) * 4 + 1 ] ;
tempData [ ( i * _width + j ) * 3 + 2 ] = _data [ ( i * _width + j ) * 4 + 2 ] ;
}
}
for ( int i = 0 ; i < ( int ) _height ; i + + )
{
row_pointers [ i ] = ( png_bytep ) tempData + i * _width * 3 ;
}
png_write_image ( png_ptr , row_pointers ) ;
free ( row_pointers ) ;
row_pointers = nullptr ;
if ( tempData ! = nullptr )
{
free ( tempData ) ;
}
}
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 = nullptr ;
}
}
png_write_end ( png_ptr , info_ptr ) ;
png_destroy_write_struct ( & png_ptr , & info_ptr ) ;
2021-05-05 19:49:30 +08:00
outStream . reset ( ) ;
2019-11-23 20:27:39 +08:00
ret = true ;
} while ( 0 ) ;
return ret ;
# else
CCLOG ( " png is not enabled, please enable it in ccConfig.h " ) ;
return false ;
# endif // CC_USE_PNG
}
bool Image : : saveImageToJPG ( const std : : string & filePath )
{
# if CC_USE_JPEG
bool ret = false ;
do
{
struct jpeg_compress_struct cinfo ;
struct jpeg_error_mgr jerr ;
2021-05-05 19:49:30 +08:00
std : : unique_ptr < FileStream > outfile ; /* target file */
2019-11-23 20:27:39 +08:00
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 ) ;
2021-04-22 19:49:43 +08:00
outfile = FileUtils : : getInstance ( ) - > openFileStream ( filePath , FileStream : : Mode : : WRITE ) ;
2021-04-24 07:22:27 +08:00
CC_BREAK_IF ( nullptr = = outfile ) ;
2021-04-22 19:49:43 +08:00
unsigned char * outputBuffer = nullptr ;
unsigned long outputSize = 0 ;
jpeg_mem_dest ( & cinfo , & outputBuffer , & outputSize ) ;
2019-11-23 20:27:39 +08:00
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_set_quality ( & cinfo , 90 , TRUE ) ;
jpeg_start_compress ( & cinfo , TRUE ) ;
row_stride = _width * 3 ; /* JSAMPLEs per row in image_buffer */
if ( hasAlpha ( ) )
{
2020-09-01 14:57:18 +08:00
uint8_t * tempData = static_cast < uint8_t * > ( malloc ( _width * _height * 3 ) ) ;
2019-11-23 20:27:39 +08:00
if ( nullptr = = tempData )
{
jpeg_finish_compress ( & cinfo ) ;
jpeg_destroy_compress ( & cinfo ) ;
2021-04-22 19:49:43 +08:00
2021-05-05 19:49:30 +08:00
outfile . reset ( ) ;
2021-04-22 19:49:43 +08:00
if ( outputBuffer )
{
free ( outputBuffer ) ;
outputBuffer = nullptr ;
}
2019-11-23 20:27:39 +08:00
break ;
}
for ( int i = 0 ; i < _height ; + + i )
{
for ( int j = 0 ; j < _width ; + + j )
{
tempData [ ( i * _width + j ) * 3 ] = _data [ ( i * _width + j ) * 4 ] ;
tempData [ ( i * _width + j ) * 3 + 1 ] = _data [ ( i * _width + j ) * 4 + 1 ] ;
tempData [ ( i * _width + j ) * 3 + 2 ] = _data [ ( i * _width + j ) * 4 + 2 ] ;
}
}
while ( cinfo . next_scanline < cinfo . image_height )
{
row_pointer [ 0 ] = & tempData [ cinfo . next_scanline * row_stride ] ;
( void ) jpeg_write_scanlines ( & cinfo , row_pointer , 1 ) ;
}
if ( tempData ! = nullptr )
{
free ( tempData ) ;
}
}
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 ) ;
2021-04-22 19:49:43 +08:00
outfile - > write ( outputBuffer , outputSize ) ;
2021-05-05 19:49:30 +08:00
outfile . reset ( ) ;
2021-04-22 19:49:43 +08:00
if ( outputBuffer )
{
free ( outputBuffer ) ;
outputBuffer = nullptr ;
}
2019-11-23 20:27:39 +08:00
jpeg_destroy_compress ( & cinfo ) ;
ret = true ;
} while ( 0 ) ;
return ret ;
# else
CCLOG ( " jpeg is not enabled, please enable it in ccConfig.h " ) ;
return false ;
# endif // CC_USE_JPEG
}
void Image : : premultiplyAlpha ( )
{
2020-11-16 12:21:27 +08:00
# if CC_ENABLE_PREMULTIPLIED_ALPHA
2020-09-25 11:07:56 +08:00
CCASSERT ( _pixelFormat = = backend : : PixelFormat : : RGBA8 , " The pixel format should be RGBA8888! " ) ;
2020-11-16 12:21:27 +08:00
2019-11-23 20:27:39 +08:00
unsigned int * fourBytes = ( unsigned int * ) _data ;
2020-11-16 12:21:27 +08:00
for ( int i = 0 ; i < _width * _height ; i + + )
2019-11-23 20:27:39 +08:00
{
2020-09-01 14:57:18 +08:00
uint8_t * p = _data + i * 4 ;
2019-11-23 20:27:39 +08:00
fourBytes [ i ] = CC_RGB_PREMULTIPLY_ALPHA ( p [ 0 ] , p [ 1 ] , p [ 2 ] , p [ 3 ] ) ;
}
2020-11-16 12:21:27 +08:00
2019-11-23 20:27:39 +08:00
_hasPremultipliedAlpha = true ;
2020-11-16 12:21:27 +08:00
# else
_hasPremultipliedAlpha = false ;
2019-11-23 20:27:39 +08:00
# endif
}
2020-09-01 14:57:18 +08:00
static inline uint8_t clamp ( int x ) {
return ( uint8_t ) ( x > = 0 ? ( x < 255 ? x : 255 ) : 0 ) ;
2019-11-23 20:27:39 +08:00
}
void Image : : reversePremultipliedAlpha ( )
{
2020-09-25 11:07:56 +08:00
CCASSERT ( _pixelFormat = = backend : : PixelFormat : : RGBA8 , " The pixel format should be RGBA8888! " ) ;
2019-11-23 20:27:39 +08:00
unsigned int * fourBytes = ( unsigned int * ) _data ;
for ( int i = 0 ; i < _width * _height ; i + + )
{
2020-09-01 14:57:18 +08:00
uint8_t * p = _data + i * 4 ;
2019-11-23 20:27:39 +08:00
if ( p [ 3 ] > 0 )
{
fourBytes [ i ] = clamp ( int ( std : : ceil ( ( p [ 0 ] * 255.0f ) / p [ 3 ] ) ) ) |
clamp ( int ( std : : ceil ( ( p [ 1 ] * 255.0f ) / p [ 3 ] ) ) ) < < 8 |
clamp ( int ( std : : ceil ( ( p [ 2 ] * 255.0f ) / p [ 3 ] ) ) ) < < 16 |
p [ 3 ] < < 24 ;
}
}
_hasPremultipliedAlpha = false ;
}
void Image : : setPVRImagesHavePremultipliedAlpha ( bool haveAlphaPremultiplied )
{
2020-11-16 12:21:27 +08:00
setCompressedImagesHavePMA ( CompressedImagePMAFlag : : PVR , haveAlphaPremultiplied ) ;
2019-11-23 20:27:39 +08:00
}
NS_CC_END