mirror of
https://github.com/smallmain/cocos-enhance-kit.git
synced 2026-01-09 03:16:52 +00:00
初始化
This commit is contained in:
171
cocos2d-x/cocos/base/CCAutoreleasePool.cpp
Normal file
171
cocos2d-x/cocos/base/CCAutoreleasePool.cpp
Normal file
@@ -0,0 +1,171 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2010-2012 cocos2d-x.org
|
||||
Copyright (c) 2013-2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
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 "base/CCAutoreleasePool.h"
|
||||
#include "base/ccMacros.h"
|
||||
|
||||
NS_CC_BEGIN
|
||||
|
||||
AutoreleasePool::AutoreleasePool()
|
||||
: _name("")
|
||||
#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
|
||||
, _isClearing(false)
|
||||
#endif
|
||||
{
|
||||
_managedObjectArray.reserve(150);
|
||||
PoolManager::getInstance()->push(this);
|
||||
}
|
||||
|
||||
AutoreleasePool::AutoreleasePool(const std::string &name)
|
||||
: _name(name)
|
||||
#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
|
||||
, _isClearing(false)
|
||||
#endif
|
||||
{
|
||||
_managedObjectArray.reserve(150);
|
||||
PoolManager::getInstance()->push(this);
|
||||
}
|
||||
|
||||
AutoreleasePool::~AutoreleasePool()
|
||||
{
|
||||
CCLOGINFO("deallocing AutoreleasePool: %p", this);
|
||||
clear();
|
||||
|
||||
PoolManager::getInstance()->pop();
|
||||
}
|
||||
|
||||
void AutoreleasePool::addObject(Ref* object)
|
||||
{
|
||||
_managedObjectArray.push_back(object);
|
||||
}
|
||||
|
||||
void AutoreleasePool::clear()
|
||||
{
|
||||
#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
|
||||
_isClearing = true;
|
||||
#endif
|
||||
std::vector<Ref*> releasings;
|
||||
releasings.swap(_managedObjectArray);
|
||||
for (const auto &obj : releasings)
|
||||
{
|
||||
obj->release();
|
||||
}
|
||||
#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
|
||||
_isClearing = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool AutoreleasePool::contains(Ref* object) const
|
||||
{
|
||||
for (const auto& obj : _managedObjectArray)
|
||||
{
|
||||
if (obj == object)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void AutoreleasePool::dump()
|
||||
{
|
||||
CCLOG("autorelease pool: %s, number of managed object %d\n", _name.c_str(), static_cast<int>(_managedObjectArray.size()));
|
||||
CCLOG("%20s%20s%20s", "Object pointer", "Object id", "reference count");
|
||||
for (const auto &obj : _managedObjectArray)
|
||||
{
|
||||
CC_UNUSED_PARAM(obj);
|
||||
CCLOG("%20p%20u\n", obj, obj->getReferenceCount());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
//
|
||||
// PoolManager
|
||||
//
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
PoolManager* PoolManager::s_singleInstance = nullptr;
|
||||
|
||||
PoolManager* PoolManager::getInstance()
|
||||
{
|
||||
if (s_singleInstance == nullptr)
|
||||
{
|
||||
s_singleInstance = new (std::nothrow) PoolManager();
|
||||
// Add the first auto release pool
|
||||
new (std::nothrow) AutoreleasePool("autorelease pool");
|
||||
}
|
||||
return s_singleInstance;
|
||||
}
|
||||
|
||||
void PoolManager::destroyInstance()
|
||||
{
|
||||
delete s_singleInstance;
|
||||
s_singleInstance = nullptr;
|
||||
}
|
||||
|
||||
PoolManager::PoolManager()
|
||||
{
|
||||
_releasePoolStack.reserve(10);
|
||||
}
|
||||
|
||||
PoolManager::~PoolManager()
|
||||
{
|
||||
CCLOGINFO("deallocing PoolManager: %p", this);
|
||||
|
||||
while (!_releasePoolStack.empty())
|
||||
{
|
||||
AutoreleasePool* pool = _releasePoolStack.back();
|
||||
|
||||
delete pool;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
AutoreleasePool* PoolManager::getCurrentPool() const
|
||||
{
|
||||
return _releasePoolStack.back();
|
||||
}
|
||||
|
||||
bool PoolManager::isObjectInPools(Ref* obj) const
|
||||
{
|
||||
for (const auto& pool : _releasePoolStack)
|
||||
{
|
||||
if (pool->contains(obj))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void PoolManager::push(AutoreleasePool *pool)
|
||||
{
|
||||
_releasePoolStack.push_back(pool);
|
||||
}
|
||||
|
||||
void PoolManager::pop()
|
||||
{
|
||||
CC_ASSERT(!_releasePoolStack.empty());
|
||||
_releasePoolStack.pop_back();
|
||||
}
|
||||
|
||||
NS_CC_END
|
||||
188
cocos2d-x/cocos/base/CCAutoreleasePool.h
Normal file
188
cocos2d-x/cocos/base/CCAutoreleasePool.h
Normal file
@@ -0,0 +1,188 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2010-2012 cocos2d-x.org
|
||||
Copyright (c) 2013-2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
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 __AUTORELEASEPOOL_H__
|
||||
#define __AUTORELEASEPOOL_H__
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include "base/CCRef.h"
|
||||
|
||||
/**
|
||||
* @addtogroup base
|
||||
* @{
|
||||
*/
|
||||
NS_CC_BEGIN
|
||||
|
||||
|
||||
/**
|
||||
* A pool for managing autorelease objects.
|
||||
* @js NA
|
||||
*/
|
||||
class CC_DLL AutoreleasePool
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @warning Don't create an autorelease pool in heap, create it in stack.
|
||||
* @js NA
|
||||
* @lua NA
|
||||
*/
|
||||
AutoreleasePool();
|
||||
|
||||
/**
|
||||
* Create an autorelease pool with specific name. This name is useful for debugging.
|
||||
* @warning Don't create an autorelease pool in heap, create it in stack.
|
||||
* @js NA
|
||||
* @lua NA
|
||||
*
|
||||
* @param name The name of created autorelease pool.
|
||||
*/
|
||||
AutoreleasePool(const std::string &name);
|
||||
|
||||
/**
|
||||
* @js NA
|
||||
* @lua NA
|
||||
*/
|
||||
~AutoreleasePool();
|
||||
|
||||
/**
|
||||
* Add a given object to this autorelease pool.
|
||||
*
|
||||
* The same object may be added several times to an autorelease pool. When the
|
||||
* pool is destructed, the object's `Ref::release()` method will be called
|
||||
* the same times as it was added.
|
||||
*
|
||||
* @param object The object to be added into the autorelease pool.
|
||||
* @js NA
|
||||
* @lua NA
|
||||
*/
|
||||
void addObject(Ref *object);
|
||||
|
||||
/**
|
||||
* Clear the autorelease pool.
|
||||
*
|
||||
* It will invoke each element's `release()` function.
|
||||
*
|
||||
* @js NA
|
||||
* @lua NA
|
||||
*/
|
||||
void clear();
|
||||
|
||||
#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
|
||||
/**
|
||||
* Whether the autorelease pool is doing `clear` operation.
|
||||
*
|
||||
* @return True if autorelease pool is clearing, false if not.
|
||||
*
|
||||
* @js NA
|
||||
* @lua NA
|
||||
*/
|
||||
bool isClearing() const { return _isClearing; };
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Checks whether the autorelease pool contains the specified object.
|
||||
*
|
||||
* @param object The object to be checked.
|
||||
* @return True if the autorelease pool contains the object, false if not
|
||||
* @js NA
|
||||
* @lua NA
|
||||
*/
|
||||
bool contains(Ref* object) const;
|
||||
|
||||
/**
|
||||
* Dump the objects that are put into the autorelease pool. It is used for debugging.
|
||||
*
|
||||
* The result will look like:
|
||||
* Object pointer address object id reference count
|
||||
*
|
||||
* @js NA
|
||||
* @lua NA
|
||||
*/
|
||||
void dump();
|
||||
|
||||
private:
|
||||
/**
|
||||
* The underlying array of object managed by the pool.
|
||||
*
|
||||
* Although Array retains the object once when an object is added, proper
|
||||
* Ref::release() is called outside the array to make sure that the pool
|
||||
* does not affect the managed object's reference count. So an object can
|
||||
* be destructed properly by calling Ref::release() even if the object
|
||||
* is in the pool.
|
||||
*/
|
||||
std::vector<Ref*> _managedObjectArray;
|
||||
std::string _name;
|
||||
|
||||
#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
|
||||
/**
|
||||
* The flag for checking whether the pool is doing `clear` operation.
|
||||
*/
|
||||
bool _isClearing;
|
||||
#endif
|
||||
};
|
||||
|
||||
// end of base group
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @cond
|
||||
*/
|
||||
class CC_DLL PoolManager
|
||||
{
|
||||
public:
|
||||
static PoolManager* getInstance();
|
||||
|
||||
static void destroyInstance();
|
||||
|
||||
/**
|
||||
* Get current auto release pool, there is at least one auto release pool that created by engine.
|
||||
* You can create your own auto release pool at demand, which will be put into auto release pool stack.
|
||||
*/
|
||||
AutoreleasePool *getCurrentPool() const;
|
||||
|
||||
bool isObjectInPools(Ref* obj) const;
|
||||
|
||||
|
||||
friend class AutoreleasePool;
|
||||
|
||||
private:
|
||||
PoolManager();
|
||||
~PoolManager();
|
||||
|
||||
void push(AutoreleasePool *pool);
|
||||
void pop();
|
||||
|
||||
static PoolManager* s_singleInstance;
|
||||
|
||||
std::vector<AutoreleasePool*> _releasePoolStack;
|
||||
};
|
||||
/**
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
NS_CC_END
|
||||
|
||||
#endif //__AUTORELEASEPOOL_H__
|
||||
437
cocos2d-x/cocos/base/CCConfiguration.cpp
Normal file
437
cocos2d-x/cocos/base/CCConfiguration.cpp
Normal file
@@ -0,0 +1,437 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2010 Ricardo Quesada
|
||||
Copyright (c) 2010-2012 cocos2d-x.org
|
||||
Copyright (c) 2013-2016 Chukong Technologies Inc.
|
||||
|
||||
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 "base/CCConfiguration.h"
|
||||
#include "platform/CCFileUtils.h"
|
||||
#include "base/CCLog.h"
|
||||
|
||||
#include "base/etc2.h"
|
||||
|
||||
#include <regex>
|
||||
|
||||
NS_CC_BEGIN
|
||||
|
||||
//cjh extern const char* cocos2dVersion();
|
||||
|
||||
Configuration* Configuration::s_sharedConfiguration = nullptr;
|
||||
|
||||
const char* Configuration::CONFIG_FILE_LOADED = "config_file_loaded";
|
||||
|
||||
Configuration::Configuration()
|
||||
: _maxTextureSize(0)
|
||||
, _maxModelviewStackDepth(0)
|
||||
, _supportsPVRTC(false)
|
||||
, _supportsETC1(false)
|
||||
, _supportsETC2(false)
|
||||
, _supportsS3TC(false)
|
||||
, _supportsATITC(false)
|
||||
, _supportsNPOT(false)
|
||||
, _supportsBGRA8888(false)
|
||||
, _supportsDiscardFramebuffer(false)
|
||||
, _supportsShareableVAO(false)
|
||||
, _supportsOESDepth24(false)
|
||||
, _supportsOESPackedDepthStencil(false)
|
||||
, _supportsOESMapBuffer(false)
|
||||
, _supportsFloatTexture(false)
|
||||
, _isOpenglES3(false)
|
||||
, _maxSamplesAllowed(0)
|
||||
, _maxTextureUnits(0)
|
||||
, _glExtensions(nullptr)
|
||||
, _maxDirLightInShader(1)
|
||||
, _maxPointLightInShader(1)
|
||||
, _maxSpotLightInShader(1)
|
||||
{
|
||||
}
|
||||
|
||||
bool Configuration::init()
|
||||
{
|
||||
gatherGPUInfo();
|
||||
|
||||
#if CC_ENABLE_PROFILERS
|
||||
_valueDict["compiled_with_profiler"] = Value(true);
|
||||
#else
|
||||
_valueDict["compiled_with_profiler"] = Value(false);
|
||||
#endif
|
||||
|
||||
#if CC_ENABLE_GL_STATE_CACHE == 0
|
||||
_valueDict["compiled_with_gl_state_cache"] = Value(false);
|
||||
#else
|
||||
_valueDict["compiled_with_gl_state_cache"] = Value(true);
|
||||
#endif
|
||||
|
||||
#if COCOS2D_DEBUG
|
||||
_valueDict["build_type"] = Value("DEBUG");
|
||||
#else
|
||||
_valueDict["build_type"] = Value("RELEASE");
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Configuration::~Configuration()
|
||||
{
|
||||
}
|
||||
|
||||
std::string Configuration::getInfo() const
|
||||
{
|
||||
// And Dump some warnings as well
|
||||
#if CC_ENABLE_PROFILERS
|
||||
CCLOG("**** WARNING **** CC_ENABLE_PROFILERS is defined. Disable it when you finish profiling (from ccConfig.h)\n");
|
||||
#endif
|
||||
|
||||
#if CC_ENABLE_GL_STATE_CACHE == 0
|
||||
CCLOG("**** WARNING **** CC_ENABLE_GL_STATE_CACHE is disabled. To improve performance, enable it (from ccConfig.h)\n");
|
||||
#endif
|
||||
|
||||
// Dump
|
||||
Value forDump = Value(_valueDict);
|
||||
return forDump.getDescription();
|
||||
}
|
||||
|
||||
void Configuration::gatherGPUInfo()
|
||||
{
|
||||
_valueDict["gl.vendor"] = Value((const char*)glGetString(GL_VENDOR));
|
||||
_valueDict["gl.renderer"] = Value((const char*)glGetString(GL_RENDERER));
|
||||
|
||||
const char* version = (const char*)glGetString(GL_VERSION);
|
||||
_valueDict["gl.version"] = Value(version);
|
||||
|
||||
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
|
||||
if (std::regex_match(version, std::regex("OpenGL ES 3.*"))) {
|
||||
_isOpenglES3 = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
_glExtensions = (char *)glGetString(GL_EXTENSIONS);
|
||||
|
||||
_supportsETC2 = checkForETC2();
|
||||
_valueDict["gl.supports_ETC2"] = Value(_supportsETC2);
|
||||
|
||||
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &_maxTextureSize);
|
||||
_valueDict["gl.max_texture_size"] = Value((int)_maxTextureSize);
|
||||
|
||||
glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &_maxTextureUnits);
|
||||
_valueDict["gl.max_texture_units"] = Value((int)_maxTextureUnits);
|
||||
|
||||
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
|
||||
glGetIntegerv(GL_MAX_SAMPLES_APPLE, &_maxSamplesAllowed);
|
||||
_valueDict["gl.max_samples_allowed"] = Value((int)_maxSamplesAllowed);
|
||||
#endif
|
||||
|
||||
_supportsETC1 = checkForGLExtension("GL_OES_compressed_ETC1_RGB8_texture");
|
||||
_valueDict["gl.supports_ETC1"] = Value(_supportsETC1);
|
||||
|
||||
_supportsS3TC = checkForGLExtension("GL_EXT_texture_compression_s3tc");
|
||||
_valueDict["gl.supports_S3TC"] = Value(_supportsS3TC);
|
||||
|
||||
_supportsATITC = checkForGLExtension("GL_AMD_compressed_ATC_texture");
|
||||
_valueDict["gl.supports_ATITC"] = Value(_supportsATITC);
|
||||
|
||||
_supportsPVRTC = checkForGLExtension("GL_IMG_texture_compression_pvrtc");
|
||||
_valueDict["gl.supports_PVRTC"] = Value(_supportsPVRTC);
|
||||
|
||||
_supportsNPOT = true;
|
||||
_valueDict["gl.supports_NPOT"] = Value(_supportsNPOT);
|
||||
|
||||
_supportsBGRA8888 = checkForGLExtension("GL_IMG_texture_format_BGRA888");
|
||||
_valueDict["gl.supports_BGRA8888"] = Value(_supportsBGRA8888);
|
||||
|
||||
_supportsDiscardFramebuffer = checkForGLExtension("GL_EXT_discard_framebuffer");
|
||||
_valueDict["gl.supports_discard_framebuffer"] = Value(_supportsDiscardFramebuffer);
|
||||
|
||||
|
||||
_supportsOESMapBuffer = checkForGLExtension("GL_OES_mapbuffer");
|
||||
_valueDict["gl.supports_OES_map_buffer"] = Value(_supportsOESMapBuffer);
|
||||
|
||||
_supportsOESDepth24 = checkForGLExtension("GL_OES_depth24");
|
||||
_valueDict["gl.supports_OES_depth24"] = Value(_supportsOESDepth24);
|
||||
|
||||
_supportsOESPackedDepthStencil = checkForGLExtension("GL_OES_packed_depth_stencil");
|
||||
_valueDict["gl.supports_OES_packed_depth_stencil"] = Value(_supportsOESPackedDepthStencil);
|
||||
|
||||
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32 || CC_TARGET_PLATFORM == CC_PLATFORM_MAC)
|
||||
_supportsStandardDerivatives = true;
|
||||
#else
|
||||
if (_isOpenglES3) {
|
||||
_supportsStandardDerivatives = true;
|
||||
}
|
||||
else {
|
||||
_supportsStandardDerivatives = checkForGLExtension("OES_standard_derivatives");
|
||||
_valueDict["gl.supports_standard_derivatives"] = Value(_supportsStandardDerivatives);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (_isOpenglES3) {
|
||||
_supportsFloatTexture = true;
|
||||
_supportsShareableVAO = true;
|
||||
}
|
||||
else {
|
||||
_supportsFloatTexture = checkForGLExtension("GL_ARB_texture_float");
|
||||
_valueDict["gl.supports_float_texture"] = Value(_supportsFloatTexture);
|
||||
|
||||
_supportsShareableVAO = checkForGLExtension("vertex_array_object");
|
||||
_valueDict["gl.supports_vertex_array_object"] = Value(_supportsShareableVAO);
|
||||
}
|
||||
|
||||
CHECK_GL_ERROR_DEBUG();
|
||||
}
|
||||
|
||||
Configuration* Configuration::getInstance()
|
||||
{
|
||||
if (! s_sharedConfiguration)
|
||||
{
|
||||
s_sharedConfiguration = new (std::nothrow) Configuration();
|
||||
s_sharedConfiguration->init();
|
||||
}
|
||||
|
||||
return s_sharedConfiguration;
|
||||
}
|
||||
|
||||
void Configuration::destroyInstance()
|
||||
{
|
||||
CC_SAFE_RELEASE_NULL(s_sharedConfiguration);
|
||||
}
|
||||
|
||||
bool Configuration::checkForGLExtension(const std::string &searchName) const
|
||||
{
|
||||
return (_glExtensions && strstr(_glExtensions, searchName.c_str() ) ) ? true : false;
|
||||
}
|
||||
|
||||
//
|
||||
// getters for specific variables.
|
||||
// Maintained for backward compatibility reasons only.
|
||||
//
|
||||
int Configuration::getMaxTextureSize() const
|
||||
{
|
||||
return _maxTextureSize;
|
||||
}
|
||||
|
||||
int Configuration::getMaxModelviewStackDepth() const
|
||||
{
|
||||
return _maxModelviewStackDepth;
|
||||
}
|
||||
|
||||
int Configuration::getMaxTextureUnits() const
|
||||
{
|
||||
return _maxTextureUnits;
|
||||
}
|
||||
|
||||
bool Configuration::supportsNPOT() const
|
||||
{
|
||||
return _supportsNPOT;
|
||||
}
|
||||
|
||||
bool Configuration::supportsPVRTC() const
|
||||
{
|
||||
return _supportsPVRTC;
|
||||
}
|
||||
|
||||
bool Configuration::supportsETC() const
|
||||
{
|
||||
//GL_ETC1_RGB8_OES is not defined in old opengl version
|
||||
#ifdef GL_ETC1_RGB8_OES
|
||||
return _supportsETC1;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Configuration::checkForETC2() const
|
||||
{
|
||||
GLint numFormats = 0;
|
||||
glGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS, &numFormats);
|
||||
GLint* formats = new GLint[numFormats];
|
||||
glGetIntegerv(GL_COMPRESSED_TEXTURE_FORMATS, formats);
|
||||
|
||||
int supportNum = 0;
|
||||
for (GLint i = 0; i < numFormats; ++i)
|
||||
{
|
||||
if (formats[i] == GL_COMPRESSED_RGB8_ETC2 || formats[i] == GL_COMPRESSED_RGBA8_ETC2_EAC)
|
||||
supportNum++;
|
||||
}
|
||||
delete [] formats;
|
||||
|
||||
return supportNum >= 2;
|
||||
}
|
||||
|
||||
bool Configuration::supportsETC2() const
|
||||
{
|
||||
return _supportsETC2;
|
||||
}
|
||||
|
||||
bool Configuration::supportsS3TC() const
|
||||
{
|
||||
#ifdef GL_EXT_texture_compression_s3tc
|
||||
return _supportsS3TC;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Configuration::supportsATITC() const
|
||||
{
|
||||
return _supportsATITC;
|
||||
}
|
||||
|
||||
bool Configuration::supportsBGRA8888() const
|
||||
{
|
||||
return _supportsBGRA8888;
|
||||
}
|
||||
|
||||
bool Configuration::supportsDiscardFramebuffer() const
|
||||
{
|
||||
return _supportsDiscardFramebuffer;
|
||||
}
|
||||
|
||||
bool Configuration::supportsShareableVAO() const
|
||||
{
|
||||
#if CC_TEXTURE_ATLAS_USE_VAO
|
||||
return _supportsShareableVAO;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Configuration::supportsMapBuffer() const
|
||||
{
|
||||
// Fixes Github issue #16123
|
||||
//
|
||||
// XXX: Fixme. Should check GL ES and not iOS or Android
|
||||
// For example, linux could be compiled with GL ES. Or perhaps in the future Android will
|
||||
// support OpenGL. This is because glMapBufferOES() is an extension of OpenGL ES. And glMapBuffer()
|
||||
// is always implemented in OpenGL.
|
||||
|
||||
// XXX: Warning. On iOS this is always `true`. Avoiding the comparison.
|
||||
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
|
||||
return _supportsOESMapBuffer;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Configuration::supportsOESDepth24() const
|
||||
{
|
||||
return _supportsOESDepth24;
|
||||
}
|
||||
|
||||
bool Configuration::supportsFloatTexture() const
|
||||
{
|
||||
return _supportsFloatTexture;
|
||||
}
|
||||
|
||||
bool Configuration::supportsStandardDerivatives() const
|
||||
{
|
||||
return _supportsStandardDerivatives;
|
||||
}
|
||||
|
||||
bool Configuration::supportsOESPackedDepthStencil() const
|
||||
{
|
||||
return _supportsOESPackedDepthStencil;
|
||||
}
|
||||
|
||||
int Configuration::getMaxSupportDirLightInShader() const
|
||||
{
|
||||
return _maxDirLightInShader;
|
||||
}
|
||||
|
||||
int Configuration::getMaxSupportPointLightInShader() const
|
||||
{
|
||||
return _maxPointLightInShader;
|
||||
}
|
||||
|
||||
int Configuration::getMaxSupportSpotLightInShader() const
|
||||
{
|
||||
return _maxSpotLightInShader;
|
||||
}
|
||||
|
||||
const Value& Configuration::getValue(const std::string& key, const Value& defaultValue) const
|
||||
{
|
||||
auto iter = _valueDict.find(key);
|
||||
if (iter != _valueDict.cend())
|
||||
return _valueDict.at(key);
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
void Configuration::setValue(const std::string& key, const Value& value)
|
||||
{
|
||||
_valueDict[key] = value;
|
||||
}
|
||||
|
||||
|
||||
void Configuration::loadConfigFile(const std::string& filename)
|
||||
{
|
||||
ValueMap dict = FileUtils::getInstance()->getValueMapFromFile(filename);
|
||||
CCASSERT(!dict.empty(), "cannot create dictionary");
|
||||
|
||||
// search for metadata
|
||||
bool validMetadata = false;
|
||||
auto metadataIter = dict.find("metadata");
|
||||
if (metadataIter != dict.cend() && metadataIter->second.getType() == Value::Type::MAP)
|
||||
{
|
||||
const auto& metadata = metadataIter->second.asValueMap();
|
||||
auto formatIter = metadata.find("format");
|
||||
|
||||
if (formatIter != metadata.cend())
|
||||
{
|
||||
int format = formatIter->second.asInt();
|
||||
|
||||
// Support format: 1
|
||||
if (format == 1)
|
||||
{
|
||||
validMetadata = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (! validMetadata)
|
||||
{
|
||||
CCLOG("Invalid config format for file: %s", filename.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
auto dataIter = dict.find("data");
|
||||
if (dataIter == dict.cend() || dataIter->second.getType() != Value::Type::MAP)
|
||||
{
|
||||
CCLOG("Expected 'data' dict, but not found. Config file: %s", filename.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// Add all keys in the existing dictionary
|
||||
|
||||
const auto& dataMap = dataIter->second.asValueMap();
|
||||
for (auto dataMapIter = dataMap.cbegin(); dataMapIter != dataMap.cend(); ++dataMapIter)
|
||||
{
|
||||
if (_valueDict.find(dataMapIter->first) == _valueDict.cend())
|
||||
_valueDict[dataMapIter->first] = dataMapIter->second;
|
||||
else
|
||||
CCLOG("Key already present. Ignoring '%s'",dataMapIter->first.c_str());
|
||||
}
|
||||
|
||||
|
||||
//cjh Director::getInstance()->getEventDispatcher()->dispatchEvent(_loadedEvent);
|
||||
}
|
||||
|
||||
NS_CC_END
|
||||
294
cocos2d-x/cocos/base/CCConfiguration.h
Normal file
294
cocos2d-x/cocos/base/CCConfiguration.h
Normal file
@@ -0,0 +1,294 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2010 Ricardo Quesada
|
||||
Copyright (c) 2010-2012 cocos2d-x.org
|
||||
Copyright (c) 2013-2016 Chukong Technologies Inc.
|
||||
|
||||
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 __CCCONFIGURATION_H__
|
||||
#define __CCCONFIGURATION_H__
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "base/CCRef.h"
|
||||
#include "base/CCValue.h"
|
||||
#include "platform/CCGL.h"
|
||||
|
||||
/**
|
||||
* @addtogroup base
|
||||
* @{
|
||||
*/
|
||||
NS_CC_BEGIN
|
||||
|
||||
/** @class Configuration
|
||||
* @brief Configuration contains some openGL variables
|
||||
* @since v0.99.0
|
||||
* @js NA
|
||||
*/
|
||||
class CC_DLL Configuration : public Ref
|
||||
{
|
||||
public:
|
||||
|
||||
/** Returns a shared instance of Configuration.
|
||||
*
|
||||
* @return An autoreleased Configuration object.
|
||||
*/
|
||||
static Configuration *getInstance();
|
||||
|
||||
/** Purge the shared instance of Configuration.
|
||||
*/
|
||||
static void destroyInstance();
|
||||
|
||||
public:
|
||||
/** Destructor
|
||||
* @js NA
|
||||
* @lua NA
|
||||
*/
|
||||
virtual ~Configuration();
|
||||
|
||||
/** OpenGL Max texture size.
|
||||
*
|
||||
* @return The OpenGL Max texture size.
|
||||
*/
|
||||
int getMaxTextureSize() const;
|
||||
|
||||
/** OpenGL Max Modelview Stack Depth.
|
||||
*
|
||||
* @return The OpenGL Max Modelview Stack Depth.
|
||||
*/
|
||||
int getMaxModelviewStackDepth() const;
|
||||
|
||||
/** Returns the maximum texture units.
|
||||
*
|
||||
* @return The maximum texture units.
|
||||
* @since v2.0.0
|
||||
*/
|
||||
int getMaxTextureUnits() const;
|
||||
|
||||
/** Whether or not the GPU supports NPOT (Non Power Of Two) textures.
|
||||
OpenGL ES 2.0 already supports NPOT (iOS).
|
||||
*
|
||||
* @return Is true if supports NPOT.
|
||||
* @since v0.99.2
|
||||
*/
|
||||
bool supportsNPOT() const;
|
||||
|
||||
/** Whether or not PVR Texture Compressed is supported.
|
||||
*
|
||||
* @return Is true if supports PVR Texture Compressed.
|
||||
*/
|
||||
bool supportsPVRTC() const;
|
||||
|
||||
/** Whether or not ETC1 Texture Compressed is supported.
|
||||
*
|
||||
*
|
||||
* @return Is true if supports ETC Texture Compressed.
|
||||
*/
|
||||
bool supportsETC() const;
|
||||
|
||||
/** Whether or not ETC2 Texture Compressed is supported.
|
||||
*
|
||||
*
|
||||
* @return Is true if supports ETC2 Texture Compressed.
|
||||
*/
|
||||
bool supportsETC2() const;
|
||||
|
||||
/** Whether or not S3TC Texture Compressed is supported.
|
||||
*
|
||||
* @return Is true if supports S3TC Texture Compressed.
|
||||
*/
|
||||
bool supportsS3TC() const;
|
||||
|
||||
/** Whether or not ATITC Texture Compressed is supported.
|
||||
*
|
||||
* @return Is true if supports ATITC Texture Compressed.
|
||||
*/
|
||||
bool supportsATITC() const;
|
||||
|
||||
/** Whether or not BGRA8888 textures are supported.
|
||||
*
|
||||
* @return Is true if supports BGRA8888 textures.
|
||||
* @since v0.99.2
|
||||
*/
|
||||
bool supportsBGRA8888() const;
|
||||
|
||||
/** Whether or not glDiscardFramebufferEXT is supported.
|
||||
* @return Is true if supports glDiscardFramebufferEXT.
|
||||
* @since v0.99.2
|
||||
*/
|
||||
bool supportsDiscardFramebuffer() const;
|
||||
|
||||
/** Whether or not shareable VAOs are supported.
|
||||
*
|
||||
* @return Is true if supports shareable VAOs.
|
||||
* @since v2.0.0
|
||||
*/
|
||||
bool supportsShareableVAO() const;
|
||||
|
||||
/** Whether or not OES_depth24 is supported.
|
||||
*
|
||||
* @return Is true if supports OES_depth24.
|
||||
* @since v3.9.0
|
||||
*/
|
||||
bool supportsOESDepth24() const;
|
||||
|
||||
/** Whether or not OES_Packed_depth_stencil is supported.
|
||||
*
|
||||
* @return Is true if supports OES_Packed_depth_stencil.
|
||||
* @since v3.9.0
|
||||
*/
|
||||
bool supportsOESPackedDepthStencil() const;
|
||||
|
||||
/** Whether or not float texture is supported.
|
||||
*
|
||||
* @return Is true if supports texture.
|
||||
* @since v3.9.0
|
||||
*/
|
||||
bool supportsFloatTexture() const;
|
||||
|
||||
/** Whether or not standard derivatives is supported.
|
||||
*
|
||||
* @return Is true if tandard derivatives.
|
||||
* @since v3.9.0
|
||||
*/
|
||||
bool supportsStandardDerivatives() const;
|
||||
|
||||
/** Whether or not glMapBuffer() is supported.
|
||||
*
|
||||
* On Desktop it returns `true`.
|
||||
* On Mobile it checks for the extension `GL_OES_mapbuffer`
|
||||
*
|
||||
* @return Whether or not `glMapBuffer()` is supported.
|
||||
* @since v3.13
|
||||
*/
|
||||
bool supportsMapBuffer() const;
|
||||
|
||||
/** Max support directional light in shader, for Sprite3D.
|
||||
*
|
||||
* @return Maximum supports directional light in shader.
|
||||
* @since v3.3
|
||||
*/
|
||||
int getMaxSupportDirLightInShader() const;
|
||||
|
||||
/** Max support point light in shader, for Sprite3D.
|
||||
*
|
||||
* @return Maximum supports point light in shader.
|
||||
* @since v3.3
|
||||
*/
|
||||
int getMaxSupportPointLightInShader() const;
|
||||
|
||||
/** Max support spot light in shader, for Sprite3D.
|
||||
*
|
||||
* @return Maximum supports spot light in shader.
|
||||
* @since v3.3
|
||||
*/
|
||||
int getMaxSupportSpotLightInShader() const;
|
||||
|
||||
/** Returns whether or not an OpenGL is supported.
|
||||
*
|
||||
* @param searchName A given search name.
|
||||
* @return Is true if an OpenGL is supported.
|
||||
*/
|
||||
bool checkForGLExtension(const std::string &searchName) const;
|
||||
|
||||
/** Initialize method.
|
||||
*
|
||||
* @return Is true if initialize success.
|
||||
*/
|
||||
bool init();
|
||||
|
||||
/** Returns the value of a given key as a double.
|
||||
*
|
||||
* @param key A given key.
|
||||
* @param defaultValue if not find the value, return the defaultValue.
|
||||
* @return
|
||||
*/
|
||||
const Value& getValue(const std::string& key, const Value& defaultValue = Value::Null) const;
|
||||
|
||||
/** Sets a new key/value pair in the configuration dictionary.
|
||||
*
|
||||
* @param key A given key.
|
||||
* @param value A given value.
|
||||
*/
|
||||
void setValue(const std::string& key, const Value& value);
|
||||
|
||||
/** Returns the Configuration info.
|
||||
*
|
||||
* @return The Configuration info.
|
||||
*/
|
||||
std::string getInfo() const;
|
||||
|
||||
/** Gathers OpenGL / GPU information.
|
||||
*/
|
||||
void gatherGPUInfo();
|
||||
|
||||
/** Loads a config file. If the keys are already present, then they are going to be replaced. Otherwise the new keys are added.
|
||||
*
|
||||
* @param filename Config file name.
|
||||
*/
|
||||
void loadConfigFile(const std::string& filename);
|
||||
|
||||
static const char* CONFIG_FILE_LOADED;
|
||||
|
||||
private:
|
||||
Configuration(void);
|
||||
static Configuration *s_sharedConfiguration;
|
||||
static std::string s_configfile;
|
||||
|
||||
/**
|
||||
* Check whether or not ETC2 Texture Compressed is supported.
|
||||
*/
|
||||
bool checkForETC2() const;
|
||||
protected:
|
||||
GLint _maxTextureSize;
|
||||
GLint _maxModelviewStackDepth;
|
||||
bool _supportsPVRTC;
|
||||
bool _supportsETC1;
|
||||
bool _supportsETC2;
|
||||
bool _supportsS3TC;
|
||||
bool _supportsATITC;
|
||||
bool _supportsNPOT;
|
||||
bool _supportsBGRA8888;
|
||||
bool _supportsDiscardFramebuffer;
|
||||
bool _supportsShareableVAO;
|
||||
bool _supportsOESMapBuffer;
|
||||
bool _supportsOESDepth24;
|
||||
bool _supportsOESPackedDepthStencil;
|
||||
bool _supportsFloatTexture;
|
||||
bool _supportsStandardDerivatives;
|
||||
bool _isOpenglES3;
|
||||
GLint _maxSamplesAllowed;
|
||||
GLint _maxTextureUnits;
|
||||
char * _glExtensions;
|
||||
int _maxDirLightInShader; //max support directional light in shader
|
||||
int _maxPointLightInShader; // max support point light in shader
|
||||
int _maxSpotLightInShader; // max support spot light in shader
|
||||
|
||||
ValueMap _valueDict;
|
||||
};
|
||||
|
||||
|
||||
NS_CC_END
|
||||
// end of base group
|
||||
/// @}
|
||||
|
||||
#endif // __CCCONFIGURATION_H__
|
||||
139
cocos2d-x/cocos/base/CCData.cpp
Normal file
139
cocos2d-x/cocos/base/CCData.cpp
Normal file
@@ -0,0 +1,139 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2010-2012 cocos2d-x.org
|
||||
Copyright (c) 2013-2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
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 "base/CCData.h"
|
||||
|
||||
NS_CC_BEGIN
|
||||
|
||||
const Data Data::Null;
|
||||
|
||||
Data::Data() :
|
||||
_bytes(nullptr),
|
||||
_size(0)
|
||||
{
|
||||
CCLOGINFO("In the empty constructor of Data.");
|
||||
}
|
||||
|
||||
Data::Data(Data&& other) :
|
||||
_bytes(nullptr),
|
||||
_size(0)
|
||||
{
|
||||
CCLOGINFO("In the move constructor of Data.");
|
||||
move(other);
|
||||
}
|
||||
|
||||
Data::Data(const Data& other) :
|
||||
_bytes(nullptr),
|
||||
_size(0)
|
||||
{
|
||||
CCLOGINFO("In the copy constructor of Data.");
|
||||
copy(other._bytes, other._size);
|
||||
}
|
||||
|
||||
Data::~Data()
|
||||
{
|
||||
CCLOGINFO("deallocing Data: %p", this);
|
||||
clear();
|
||||
}
|
||||
|
||||
Data& Data::operator= (const Data& other)
|
||||
{
|
||||
CCLOGINFO("In the copy assignment of Data.");
|
||||
copy(other._bytes, other._size);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Data& Data::operator= (Data&& other)
|
||||
{
|
||||
CCLOGINFO("In the move assignment of Data.");
|
||||
move(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Data::move(Data& other)
|
||||
{
|
||||
clear();
|
||||
|
||||
_bytes = other._bytes;
|
||||
_size = other._size;
|
||||
|
||||
other._bytes = nullptr;
|
||||
other._size = 0;
|
||||
}
|
||||
|
||||
bool Data::isNull() const
|
||||
{
|
||||
return (_bytes == nullptr || _size == 0);
|
||||
}
|
||||
|
||||
unsigned char* Data::getBytes() const
|
||||
{
|
||||
return _bytes;
|
||||
}
|
||||
|
||||
ssize_t Data::getSize() const
|
||||
{
|
||||
return _size;
|
||||
}
|
||||
|
||||
void Data::copy(const unsigned char* bytes, const ssize_t size)
|
||||
{
|
||||
clear();
|
||||
|
||||
if (size > 0)
|
||||
{
|
||||
_size = size;
|
||||
_bytes = (unsigned char*)malloc(sizeof(unsigned char) * _size);
|
||||
memcpy(_bytes, bytes, _size);
|
||||
}
|
||||
}
|
||||
|
||||
void Data::fastSet(unsigned char* bytes, const ssize_t size)
|
||||
{
|
||||
free(_bytes);
|
||||
_bytes = bytes;
|
||||
_size = size;
|
||||
}
|
||||
|
||||
void Data::clear()
|
||||
{
|
||||
free(_bytes);
|
||||
_bytes = nullptr;
|
||||
_size = 0;
|
||||
}
|
||||
|
||||
unsigned char* Data::takeBuffer(ssize_t* size)
|
||||
{
|
||||
auto buffer = getBytes();
|
||||
if (size)
|
||||
*size = getSize();
|
||||
|
||||
_bytes = nullptr;
|
||||
_size = 0;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
NS_CC_END
|
||||
157
cocos2d-x/cocos/base/CCData.h
Normal file
157
cocos2d-x/cocos/base/CCData.h
Normal file
@@ -0,0 +1,157 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2010-2012 cocos2d-x.org
|
||||
Copyright (c) 2013-2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
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 __CCDATA_H__
|
||||
#define __CCDATA_H__
|
||||
|
||||
#include "base/ccMacros.h"
|
||||
#include <stdint.h> // for ssize_t on android
|
||||
#include <string> // for ssize_t on linux
|
||||
#include "platform/CCStdC.h" // for ssize_t on window
|
||||
|
||||
/**
|
||||
* @addtogroup base
|
||||
* @js NA
|
||||
* @lua NA
|
||||
*/
|
||||
NS_CC_BEGIN
|
||||
|
||||
class CC_DLL Data
|
||||
{
|
||||
friend class Properties;
|
||||
|
||||
public:
|
||||
/**
|
||||
* This parameter is defined for convenient reference if a null Data object is needed.
|
||||
*/
|
||||
static const Data Null;
|
||||
|
||||
/**
|
||||
* Constructor of Data.
|
||||
*/
|
||||
Data();
|
||||
|
||||
/**
|
||||
* Copy constructor of Data.
|
||||
*/
|
||||
Data(const Data& other);
|
||||
|
||||
/**
|
||||
* Copy constructor of Data.
|
||||
*/
|
||||
Data(Data&& other);
|
||||
|
||||
/**
|
||||
* Destructor of Data.
|
||||
*/
|
||||
~Data();
|
||||
|
||||
/**
|
||||
* Overloads of operator=.
|
||||
*/
|
||||
Data& operator= (const Data& other);
|
||||
|
||||
/**
|
||||
* Overloads of operator=.
|
||||
*/
|
||||
Data& operator= (Data&& other);
|
||||
|
||||
/**
|
||||
* Gets internal bytes of Data. It will return the pointer directly used in Data, so don't delete it.
|
||||
*
|
||||
* @return Pointer of bytes used internal in Data.
|
||||
*/
|
||||
unsigned char* getBytes() const;
|
||||
|
||||
/**
|
||||
* Gets the size of the bytes.
|
||||
*
|
||||
* @return The size of bytes of Data.
|
||||
*/
|
||||
ssize_t getSize() const;
|
||||
|
||||
/** Copies the buffer pointer and its size.
|
||||
* @note This method will copy the whole buffer.
|
||||
* Developer should free the pointer after invoking this method.
|
||||
* @see Data::fastSet
|
||||
*/
|
||||
void copy(const unsigned char* bytes, const ssize_t size);
|
||||
|
||||
/** Fast set the buffer pointer and its size. Please use it carefully.
|
||||
* @param bytes The buffer pointer, note that it have to be allocated by 'malloc' or 'calloc',
|
||||
* since in the destructor of Data, the buffer will be deleted by 'free'.
|
||||
* @note 1. This method will move the ownship of 'bytes'pointer to Data,
|
||||
* 2. The pointer should not be used outside after it was passed to this method.
|
||||
* @see Data::copy
|
||||
*/
|
||||
void fastSet(unsigned char* bytes, const ssize_t size);
|
||||
|
||||
/**
|
||||
* Clears data, free buffer and reset data size.
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* Check whether the data is null.
|
||||
*
|
||||
* @return True if the Data is null, false if not.
|
||||
*/
|
||||
bool isNull() const;
|
||||
|
||||
/**
|
||||
* Get the internal buffer of data and set data to empty state.
|
||||
*
|
||||
* The ownership of the buffer removed from the data object.
|
||||
* That is the user have to free the returned buffer.
|
||||
* The data object is set to empty state, that is internal buffer is set to nullptr
|
||||
* and size is set to zero.
|
||||
* Usage:
|
||||
* @code
|
||||
* Data d;
|
||||
* // ...
|
||||
* ssize_t size;
|
||||
* unsigned char* buffer = d.takeBuffer(&size);
|
||||
* // use buffer and size
|
||||
* free(buffer);
|
||||
* @endcode
|
||||
*
|
||||
* @param size Will fill with the data buffer size in bytes, if you do not care buffer size, pass nullptr.
|
||||
* @return the internal data buffer, free it after use.
|
||||
*/
|
||||
unsigned char* takeBuffer(ssize_t* size = nullptr);
|
||||
private:
|
||||
void move(Data& other);
|
||||
|
||||
private:
|
||||
unsigned char* _bytes;
|
||||
ssize_t _size;
|
||||
};
|
||||
|
||||
|
||||
NS_CC_END
|
||||
|
||||
/** @} */
|
||||
#endif // __CCDATA_H__
|
||||
495
cocos2d-x/cocos/base/CCGLUtils.cpp
Normal file
495
cocos2d-x/cocos/base/CCGLUtils.cpp
Normal file
@@ -0,0 +1,495 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
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 "CCGLUtils.h"
|
||||
#include "platform/CCApplication.h"
|
||||
#include <stdio.h>
|
||||
#include <cfloat>
|
||||
#include <cassert>
|
||||
#include <array>
|
||||
|
||||
NS_CC_BEGIN
|
||||
|
||||
// todo: use gl to get the supported number
|
||||
#define MAX_ATTRIBUTE_UNIT 16
|
||||
#define MAX_TEXTURE_UNIT 32
|
||||
|
||||
//IDEA: Consider to use variable to enable/disable cache state since using macro will not be able to close it if there're serious bugs.
|
||||
//#undef CC_ENABLE_GL_STATE_CACHE
|
||||
//#define CC_ENABLE_GL_STATE_CACHE 0
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
GLint __currentVertexBuffer = -1;
|
||||
GLint __currentIndexBuffer = -1;
|
||||
GLint __currentVertexArray = -1;
|
||||
|
||||
uint32_t __enabledVertexAttribArrayFlag = 0;
|
||||
VertexAttributePointerInfo __enabledVertexAttribArrayInfo[MAX_ATTRIBUTE_UNIT];
|
||||
|
||||
uint8_t __currentActiveTextureUnit = 0;
|
||||
std::array<BoundTextureInfo, MAX_TEXTURE_UNIT> __boundTextureInfos;
|
||||
|
||||
GLint _currentUnpackAlignment = -1;
|
||||
|
||||
bool __unpackFlipY = false;
|
||||
bool __premultiplyAlpha = false;
|
||||
|
||||
GLuint __currentOffScreenFbo = 0;
|
||||
}
|
||||
|
||||
|
||||
//IDEA: need to consider invoking this after restarting game.
|
||||
void ccInvalidateStateCache()
|
||||
{
|
||||
__currentVertexBuffer = -1;
|
||||
__currentIndexBuffer = -1;
|
||||
__currentVertexArray = -1;
|
||||
|
||||
__enabledVertexAttribArrayFlag = 0;
|
||||
for (int i = 0; i < MAX_ATTRIBUTE_UNIT; ++i)
|
||||
__enabledVertexAttribArrayInfo[i] = VertexAttributePointerInfo();
|
||||
|
||||
_currentUnpackAlignment = -1;
|
||||
__unpackFlipY = false;
|
||||
__premultiplyAlpha = false;
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
Texture related
|
||||
***************************************************************************************/
|
||||
void ccActiveTexture(GLenum texture)
|
||||
{
|
||||
#if CC_ENABLE_GL_STATE_CACHE
|
||||
auto activeTextureUnit = texture - GL_TEXTURE0;
|
||||
if(activeTextureUnit < MAX_TEXTURE_UNIT && activeTextureUnit >= 0)
|
||||
{
|
||||
__currentActiveTextureUnit = activeTextureUnit;
|
||||
}
|
||||
#endif
|
||||
glActiveTexture(texture);
|
||||
}
|
||||
|
||||
void ccBindTexture(GLenum target, GLuint texture)
|
||||
{
|
||||
#if CC_ENABLE_GL_STATE_CACHE
|
||||
auto& boundTextureInfo = __boundTextureInfos[__currentActiveTextureUnit];
|
||||
//todo: support cache
|
||||
if (boundTextureInfo.texture != texture || boundTextureInfo.target != target) {
|
||||
boundTextureInfo.texture = texture;
|
||||
boundTextureInfo.target = target;
|
||||
}
|
||||
glBindTexture(target, texture);
|
||||
#else
|
||||
glBindTexture(target, texture);
|
||||
#endif
|
||||
}
|
||||
|
||||
BoundTextureInfo* getBoundTextureInfo(uint32_t textureUnit)
|
||||
{
|
||||
return &__boundTextureInfos[textureUnit];
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
FrameBuffer related
|
||||
***************************************************************************************/
|
||||
|
||||
void ccBindFramebuffer(GLenum target,GLuint buffer)
|
||||
{
|
||||
if(Application::getInstance()->isDownsampleEnabled())
|
||||
{
|
||||
if(target == GL_FRAMEBUFFER && buffer == Application::getInstance()->getMainFBO())
|
||||
{
|
||||
buffer = __currentOffScreenFbo;
|
||||
}
|
||||
}
|
||||
|
||||
glBindFramebuffer(target , buffer);
|
||||
}
|
||||
|
||||
void ccActiveOffScreenFramebuffer(GLuint offscreenFbo)
|
||||
{
|
||||
__currentOffScreenFbo = offscreenFbo;
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
Buffer related
|
||||
***************************************************************************************/
|
||||
|
||||
void ccBindBuffer(GLenum target, GLuint buffer)
|
||||
{
|
||||
#if CC_ENABLE_GL_STATE_CACHE
|
||||
if (target == GL_ARRAY_BUFFER)
|
||||
{
|
||||
if (__currentVertexBuffer != buffer)
|
||||
{
|
||||
__currentVertexBuffer = buffer;
|
||||
glBindBuffer(target, buffer);
|
||||
}
|
||||
}
|
||||
else if (target == GL_ELEMENT_ARRAY_BUFFER)
|
||||
{
|
||||
if (__currentIndexBuffer != buffer)
|
||||
{
|
||||
__currentIndexBuffer = buffer;
|
||||
glBindBuffer(target, buffer);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
glBindBuffer(target, buffer);
|
||||
}
|
||||
#else
|
||||
// Should cache it, ccVertexAttribPointer depends on it.
|
||||
__currentVertexBuffer = buffer;
|
||||
|
||||
glBindBuffer(target, buffer);
|
||||
#endif
|
||||
}
|
||||
|
||||
void ccDeleteBuffers(GLsizei n, const GLuint * buffers)
|
||||
{
|
||||
for (GLsizei i = 0; i < n; ++i)
|
||||
{
|
||||
if (buffers[i] == __currentVertexBuffer)
|
||||
__currentVertexBuffer = -1;
|
||||
else if (buffers[i] == __currentIndexBuffer)
|
||||
__currentIndexBuffer = -1;
|
||||
}
|
||||
glDeleteBuffers(n, buffers);
|
||||
}
|
||||
|
||||
GLint ccGetBoundVertexBuffer()
|
||||
{
|
||||
return __currentVertexBuffer;
|
||||
}
|
||||
|
||||
GLint ccGetBoundIndexBuffer()
|
||||
{
|
||||
return __currentIndexBuffer;
|
||||
}
|
||||
|
||||
void ccBindVertexArray(GLuint VAO)
|
||||
{
|
||||
#if CC_ENABLE_GL_STATE_CACHE
|
||||
if (__currentVertexArray != VAO)
|
||||
{
|
||||
__currentVertexArray = VAO;
|
||||
glBindVertexArray(VAO);
|
||||
}
|
||||
#else
|
||||
__currentVertexArray = VAO;
|
||||
glBindVertexArray(VAO);
|
||||
#endif
|
||||
}
|
||||
|
||||
GLint ccGetBoundVertexArray()
|
||||
{
|
||||
return __currentVertexArray;
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
Vertex attribute related
|
||||
***************************************************************************************/
|
||||
|
||||
void ccEnableVertexAttribArray(GLuint index)
|
||||
{
|
||||
assert(index < MAX_ATTRIBUTE_UNIT);
|
||||
if (index >= MAX_ATTRIBUTE_UNIT)
|
||||
return;
|
||||
|
||||
uint32_t flag = 1 << index;
|
||||
if (__enabledVertexAttribArrayFlag & flag)
|
||||
return;
|
||||
|
||||
__enabledVertexAttribArrayFlag |= flag;
|
||||
glEnableVertexAttribArray(index);
|
||||
}
|
||||
|
||||
void ccDisableVertexAttribArray(GLuint index)
|
||||
{
|
||||
if (index >= MAX_ATTRIBUTE_UNIT)
|
||||
return;
|
||||
uint32_t flag = 1 << index;
|
||||
if (__enabledVertexAttribArrayFlag & flag)
|
||||
{
|
||||
glDisableVertexAttribArray(index);
|
||||
__enabledVertexAttribArrayFlag &= ~(1 << index);
|
||||
}
|
||||
}
|
||||
|
||||
void ccVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* pointer)
|
||||
{
|
||||
assert(index < MAX_ATTRIBUTE_UNIT);
|
||||
if (index >= MAX_ATTRIBUTE_UNIT)
|
||||
return;
|
||||
|
||||
__enabledVertexAttribArrayInfo[index] = VertexAttributePointerInfo(__currentVertexBuffer, index, size, type, normalized, stride, pointer);
|
||||
|
||||
// IDEA: should check all the values to determine if need to invoke glVertexAttribPointer or not?
|
||||
// We don't know if it is a good idea to do it because it needs to compare so many parameters.
|
||||
glVertexAttribPointer(index, size, type, normalized, stride, pointer);
|
||||
}
|
||||
|
||||
const VertexAttributePointerInfo* getVertexAttribPointerInfo(GLuint index)
|
||||
{
|
||||
assert(index < MAX_ATTRIBUTE_UNIT);
|
||||
if (index >= MAX_ATTRIBUTE_UNIT)
|
||||
return nullptr;
|
||||
|
||||
// The index is not enabled, return null.
|
||||
if (! (__enabledVertexAttribArrayFlag & (1 << index)) )
|
||||
return nullptr;
|
||||
|
||||
return &__enabledVertexAttribArrayInfo[index];
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
Other functions.
|
||||
***************************************************************************************/
|
||||
|
||||
void ccViewport(GLint x, GLint y, GLsizei width, GLsizei height)
|
||||
{
|
||||
glViewport(x, y, width, height);
|
||||
}
|
||||
|
||||
void ccScissor(GLint x, GLint y, GLsizei width, GLsizei height)
|
||||
{
|
||||
glScissor(x, y, width, height);
|
||||
}
|
||||
|
||||
//IDEA:: ONLY SUPPORT RGBA format now.
|
||||
static void flipPixelsY(GLubyte *pixels, int bytesPerRow, int rows)
|
||||
{
|
||||
if( !pixels ) { return; }
|
||||
|
||||
GLuint middle = rows/2;
|
||||
GLuint intsPerRow = bytesPerRow / sizeof(GLuint);
|
||||
GLuint remainingBytes = bytesPerRow - intsPerRow * sizeof(GLuint);
|
||||
|
||||
for( GLuint rowTop = 0, rowBottom = rows-1; rowTop < middle; rowTop++, rowBottom-- ) {
|
||||
|
||||
// Swap bytes in packs of sizeof(GLuint) bytes
|
||||
GLuint *iTop = (GLuint *)(pixels + rowTop * bytesPerRow);
|
||||
GLuint *iBottom = (GLuint *)(pixels + rowBottom * bytesPerRow);
|
||||
|
||||
GLuint itmp;
|
||||
GLint n = intsPerRow;
|
||||
do {
|
||||
itmp = *iTop;
|
||||
*iTop++ = *iBottom;
|
||||
*iBottom++ = itmp;
|
||||
} while(--n > 0);
|
||||
|
||||
// Swap the remaining bytes
|
||||
GLubyte *bTop = (GLubyte *)iTop;
|
||||
GLubyte *bBottom = (GLubyte *)iBottom;
|
||||
|
||||
GLubyte btmp;
|
||||
switch( remainingBytes ) {
|
||||
case 3: btmp = *bTop; *bTop++ = *bBottom; *bBottom++ = btmp;
|
||||
case 2: btmp = *bTop; *bTop++ = *bBottom; *bBottom++ = btmp;
|
||||
case 1: btmp = *bTop; *bTop = *bBottom; *bBottom = btmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void flipPixelsYByFormat(GLubyte *pixels, GLenum format, uint32_t width, uint32_t height, uint32_t expectedTotalBytes)
|
||||
{
|
||||
bool isSupportFlipY = true;
|
||||
GLsizei bytesPerRow = 0;
|
||||
switch (format) {
|
||||
case GL_RGBA:
|
||||
bytesPerRow = width * 4;
|
||||
break;
|
||||
case GL_RGB:
|
||||
bytesPerRow = width * 3;
|
||||
break;
|
||||
case GL_LUMINANCE_ALPHA:
|
||||
bytesPerRow = width * 2;
|
||||
break;
|
||||
case GL_LUMINANCE:
|
||||
bytesPerRow = width;
|
||||
break;
|
||||
default:
|
||||
isSupportFlipY = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (isSupportFlipY)
|
||||
{
|
||||
assert(expectedTotalBytes == bytesPerRow * height);
|
||||
flipPixelsY((GLubyte*)pixels, bytesPerRow, height);
|
||||
}
|
||||
else
|
||||
{
|
||||
CCLOGERROR("flipPixelsYByFormat: format: 0x%X doesn't support upackFlipY!\n", format);
|
||||
}
|
||||
}
|
||||
|
||||
// Lookup tables for fast [un]premultiplied alpha color values
|
||||
// From https://bugzilla.mozilla.org/show_bug.cgi?id=662130
|
||||
static GLubyte* __premultiplyTable = nullptr;
|
||||
|
||||
static const GLubyte* premultiplyTable()
|
||||
{
|
||||
if( !__premultiplyTable ) {
|
||||
__premultiplyTable = (GLubyte*)malloc(256*256);
|
||||
|
||||
unsigned char *data = __premultiplyTable;
|
||||
for( int a = 0; a <= 255; a++ ) {
|
||||
for( int c = 0; c <= 255; c++ ) {
|
||||
data[a*256+c] = (a * c + 254) / 255;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return __premultiplyTable;
|
||||
}
|
||||
|
||||
void premultiplyPixels(const GLubyte *inPixels, GLubyte *outPixels, GLenum format, uint32_t width, uint32_t height, uint32_t expectedTotalBytes)
|
||||
{
|
||||
const GLubyte *table = premultiplyTable();
|
||||
int byteLength = 0;
|
||||
if( format == GL_RGBA )
|
||||
{
|
||||
byteLength = width * height * 4;
|
||||
assert(byteLength == expectedTotalBytes);
|
||||
for( int i = 0; i < byteLength; i += 4 ) {
|
||||
unsigned short a = inPixels[i+3] * 256;
|
||||
outPixels[i+0] = table[ a + inPixels[i+0] ];
|
||||
outPixels[i+1] = table[ a + inPixels[i+1] ];
|
||||
outPixels[i+2] = table[ a + inPixels[i+2] ];
|
||||
outPixels[i+3] = inPixels[i+3];
|
||||
}
|
||||
}
|
||||
else if ( format == GL_LUMINANCE_ALPHA )
|
||||
{
|
||||
byteLength = width * height * 2;
|
||||
assert(byteLength == expectedTotalBytes);
|
||||
for( int i = 0; i < byteLength; i += 2 ) {
|
||||
unsigned short a = inPixels[i+1] * 256;
|
||||
outPixels[i+0] = table[ a + inPixels[i+0] ];
|
||||
outPixels[i+1] = inPixels[i+1];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CCLOGERROR("premultiplyPixels: format: 0x%X doesn't support upackFlipY!\n", format);
|
||||
}
|
||||
}
|
||||
|
||||
bool ccIsUnpackFlipY()
|
||||
{
|
||||
return __unpackFlipY;
|
||||
}
|
||||
|
||||
bool ccIsPremultiplyAlpha()
|
||||
{
|
||||
return __premultiplyAlpha;
|
||||
}
|
||||
|
||||
void ccPixelStorei(GLenum pname, GLint param)
|
||||
{
|
||||
if (pname == GL_UNPACK_FLIP_Y_WEBGL)
|
||||
{
|
||||
__unpackFlipY = param == 0 ? false : true;
|
||||
return;
|
||||
}
|
||||
else if (pname == GL_UNPACK_PREMULTIPLY_ALPHA_WEBGL)
|
||||
{
|
||||
__premultiplyAlpha = param == 0 ? false : true;
|
||||
return;
|
||||
}
|
||||
else if (pname == GL_UNPACK_COLORSPACE_CONVERSION_WEBGL)
|
||||
{
|
||||
CCLOGERROR("Warning: UNPACK_COLORSPACE_CONVERSION_WEBGL is unsupported\n");
|
||||
return;
|
||||
}
|
||||
else if (pname == GL_UNPACK_ALIGNMENT)
|
||||
{
|
||||
#if CC_ENABLE_GL_STATE_CACHE
|
||||
if (_currentUnpackAlignment != param)
|
||||
{
|
||||
glPixelStorei(pname, param);
|
||||
_currentUnpackAlignment = param;
|
||||
}
|
||||
#else
|
||||
glPixelStorei(pname, param);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
glPixelStorei(pname, param);
|
||||
}
|
||||
}
|
||||
|
||||
void ccFlipYOrPremultiptyAlphaIfNeeded(GLenum format, GLsizei width, GLsizei height, uint32_t pixelBytes, GLvoid* pixels)
|
||||
{
|
||||
if (pixels != nullptr)
|
||||
{
|
||||
if (__unpackFlipY)
|
||||
{
|
||||
flipPixelsYByFormat((GLubyte*)pixels, format, width, height, pixelBytes);
|
||||
}
|
||||
|
||||
if (__premultiplyAlpha)
|
||||
{
|
||||
premultiplyPixels((GLubyte*)pixels, (GLubyte*)pixels, format, width, height, pixelBytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GLint ccGetBufferDataSize()
|
||||
{
|
||||
GLint result = 0, size = 0;
|
||||
for( int i = 0; i < MAX_ATTRIBUTE_UNIT; i++ ) {
|
||||
const VertexAttributePointerInfo *info = getVertexAttribPointerInfo(i);
|
||||
if (info != nullptr && info->VBO == __currentVertexBuffer) {
|
||||
switch (info->type)
|
||||
{
|
||||
case GL_BYTE:
|
||||
case GL_UNSIGNED_BYTE:
|
||||
size = info->size * sizeof(GLbyte);
|
||||
break;
|
||||
case GL_SHORT:
|
||||
case GL_UNSIGNED_SHORT:
|
||||
size = info->size * sizeof(GLshort);
|
||||
break;
|
||||
case GL_FLOAT:
|
||||
size = info->size * sizeof(GLclampf);
|
||||
break;
|
||||
default:
|
||||
size = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
result += size;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
NS_CC_END
|
||||
92
cocos2d-x/cocos/base/CCGLUtils.h
Normal file
92
cocos2d-x/cocos/base/CCGLUtils.h
Normal file
@@ -0,0 +1,92 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
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.
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "base/ccMacros.h"
|
||||
#include "platform/CCGL.h"
|
||||
|
||||
NS_CC_BEGIN
|
||||
|
||||
struct BoundTextureInfo
|
||||
{
|
||||
GLenum target = GL_TEXTURE_2D;
|
||||
GLuint texture = 0;
|
||||
};
|
||||
|
||||
void ccInvalidateStateCache();
|
||||
|
||||
void ccActiveTexture(GLenum texture);
|
||||
void ccBindTexture(GLenum target, GLuint texture);
|
||||
BoundTextureInfo* getBoundTextureInfo(uint32_t textureUnit);
|
||||
void ccBindFramebuffer(GLenum target,GLuint buffer);
|
||||
void ccActiveOffScreenFramebuffer(GLuint offscreenFbo);
|
||||
void ccBindBuffer(GLenum target, GLuint buffer);
|
||||
void ccDeleteBuffers(GLsizei, const GLuint * buffers);
|
||||
GLint ccGetBoundVertexBuffer();
|
||||
GLint ccGetBoundIndexBuffer();
|
||||
void ccBindVertexArray(GLuint VAO);
|
||||
GLint ccGetBoundVertexArray();
|
||||
|
||||
void ccViewport(GLint x, GLint y, GLsizei width, GLsizei height);
|
||||
void ccScissor(GLint x, GLint y, GLsizei width, GLsizei height);
|
||||
|
||||
struct VertexAttributePointerInfo
|
||||
{
|
||||
VertexAttributePointerInfo(GLuint VBO, GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* pointer)
|
||||
: VBO(VBO)
|
||||
, index(index)
|
||||
, size(size)
|
||||
, type(type)
|
||||
, normalized(normalized)
|
||||
, stride(stride)
|
||||
, pointer(pointer)
|
||||
{}
|
||||
|
||||
VertexAttributePointerInfo() {}
|
||||
|
||||
GLuint index = 0;
|
||||
// The VBO this attribute bind to.
|
||||
GLuint VBO = 0;
|
||||
GLint size = 0;
|
||||
GLenum type = GL_UNSIGNED_BYTE;
|
||||
GLboolean normalized = false;
|
||||
GLsizei stride = 0;
|
||||
const GLvoid* pointer = nullptr;
|
||||
};
|
||||
void ccEnableVertexAttribArray(GLuint index);
|
||||
void ccDisableVertexAttribArray(GLuint index);
|
||||
void ccVertexAttribPointer(GLuint, GLint, GLenum, GLboolean, GLsizei, const GLvoid*);
|
||||
const VertexAttributePointerInfo* getVertexAttribPointerInfo(GLuint index);
|
||||
|
||||
// Converts pixel if unpackFlipY or premultiplyAlpha is true.
|
||||
void ccFlipYOrPremultiptyAlphaIfNeeded(GLenum format, GLsizei width, GLsizei height, uint32_t pixelBytes, GLvoid* pixels);
|
||||
|
||||
bool ccIsUnpackFlipY();
|
||||
bool ccIsPremultiplyAlpha();
|
||||
void ccPixelStorei(GLenum pname, GLint param);
|
||||
GLint ccGetBufferDataSize();
|
||||
|
||||
NS_CC_END
|
||||
120
cocos2d-x/cocos/base/CCLog.cpp
Normal file
120
cocos2d-x/cocos/base/CCLog.cpp
Normal file
@@ -0,0 +1,120 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2013-2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
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 "base/CCLog.h"
|
||||
#include <stdio.h>
|
||||
#include <new>
|
||||
#include <algorithm>
|
||||
#include <string.h>
|
||||
|
||||
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
|
||||
#include <io.h>
|
||||
#include <WS2tcpip.h>
|
||||
#include <Winsock2.h>
|
||||
|
||||
#endif // (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
|
||||
|
||||
/** private functions */
|
||||
namespace
|
||||
{
|
||||
const size_t SEND_BUFSIZ = 512;
|
||||
const int MAX_LOG_LENGTH = 16*1024;
|
||||
|
||||
//
|
||||
// Free functions to log
|
||||
//
|
||||
|
||||
void _log(const char *format, va_list args)
|
||||
{
|
||||
int bufferSize = MAX_LOG_LENGTH;
|
||||
char* buf = nullptr;
|
||||
|
||||
do
|
||||
{
|
||||
buf = new (std::nothrow) char[bufferSize];
|
||||
if (buf == nullptr)
|
||||
return; // not enough memory
|
||||
|
||||
int ret = vsnprintf(buf, bufferSize - 3, format, args);
|
||||
if (ret < 0)
|
||||
{
|
||||
bufferSize *= 2;
|
||||
|
||||
delete [] buf;
|
||||
}
|
||||
else
|
||||
break;
|
||||
|
||||
} while (true);
|
||||
|
||||
strcat(buf, "\n");
|
||||
|
||||
#if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID
|
||||
#include <android/log.h>
|
||||
__android_log_print(ANDROID_LOG_DEBUG, "debug info", "%s", buf);
|
||||
#elif CC_TARGET_PLATFORM == CC_PLATFORM_WIN32
|
||||
int pos = 0;
|
||||
int len = strlen(buf);
|
||||
char tempBuf[MAX_LOG_LENGTH + 1] = { 0 };
|
||||
WCHAR wszBuf[MAX_LOG_LENGTH + 1] = { 0 };
|
||||
|
||||
do
|
||||
{
|
||||
std::copy(buf + pos, buf + pos + MAX_LOG_LENGTH, tempBuf);
|
||||
|
||||
tempBuf[MAX_LOG_LENGTH] = 0;
|
||||
|
||||
MultiByteToWideChar(CP_UTF8, 0, tempBuf, -1, wszBuf, sizeof(wszBuf));
|
||||
OutputDebugStringW(wszBuf);
|
||||
WideCharToMultiByte(CP_ACP, 0, wszBuf, -1, tempBuf, sizeof(tempBuf), nullptr, FALSE);
|
||||
printf("%s", tempBuf);
|
||||
|
||||
pos += MAX_LOG_LENGTH;
|
||||
|
||||
} while (pos < len);
|
||||
fflush(stdout);
|
||||
#else
|
||||
// Linux, Mac, iOS, etc
|
||||
fprintf(stdout, "%s", buf);
|
||||
fflush(stdout);
|
||||
#endif
|
||||
|
||||
//cocos2d::log(buf);
|
||||
delete [] buf;
|
||||
}
|
||||
}
|
||||
|
||||
namespace cocos2d
|
||||
{
|
||||
|
||||
void log(const char * format, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
_log(format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
62
cocos2d-x/cocos/base/CCLog.h
Normal file
62
cocos2d-x/cocos/base/CCLog.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2013-2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
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.
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "platform/CCPlatformDefine.h"
|
||||
|
||||
/// @cond DO_NOT_SHOW
|
||||
|
||||
// Because ccMacros.h includes this header file, and CC_FORMAT_PRINTF is defined in ccMacros.h, so
|
||||
// we define it here too.
|
||||
|
||||
/** @def CC_FORMAT_PRINTF(formatPos, argPos)
|
||||
* Only certain compiler support __attribute__((format))
|
||||
*
|
||||
* @param formatPos 1-based position of format string argument.
|
||||
* @param argPos 1-based position of first format-dependent argument.
|
||||
*/
|
||||
#if defined(__GNUC__) && (__GNUC__ >= 4)
|
||||
#define CC_FORMAT_PRINTF(formatPos, argPos) __attribute__((__format__(printf, formatPos, argPos)))
|
||||
#elif defined(__has_attribute)
|
||||
#if __has_attribute(format)
|
||||
#define CC_FORMAT_PRINTF(formatPos, argPos) __attribute__((__format__(printf, formatPos, argPos)))
|
||||
#endif // __has_attribute(format)
|
||||
#else
|
||||
#define CC_FORMAT_PRINTF(formatPos, argPos)
|
||||
#endif
|
||||
|
||||
namespace cocos2d
|
||||
{
|
||||
|
||||
/**
|
||||
@brief Output Debug message.
|
||||
*/
|
||||
void CC_DLL log(const char * format, ...) CC_FORMAT_PRINTF(1, 2);
|
||||
|
||||
}
|
||||
|
||||
/// @endcond
|
||||
|
||||
422
cocos2d-x/cocos/base/CCMap.h
Normal file
422
cocos2d-x/cocos/base/CCMap.h
Normal file
@@ -0,0 +1,422 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2013-2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
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.
|
||||
****************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#define USE_STD_UNORDERED_MAP 1
|
||||
|
||||
#include <vector>
|
||||
|
||||
#if USE_STD_UNORDERED_MAP
|
||||
#include <unordered_map>
|
||||
#else
|
||||
#include <map>
|
||||
#endif
|
||||
|
||||
#include "base/ccMacros.h"
|
||||
#include "base/ccRandom.h"
|
||||
#include "base/CCRef.h"
|
||||
|
||||
/**
|
||||
* @addtogroup base
|
||||
* @{
|
||||
*/
|
||||
NS_CC_BEGIN
|
||||
|
||||
/**
|
||||
* Similar to std::unordered_map, but it will manage reference count automatically internally.
|
||||
* Which means it will invoke Ref::retain() when adding an element, and invoke Ref::release() when removing an element.
|
||||
* @warning The element should be `Ref` or its sub-class.
|
||||
* @js NA
|
||||
* @lua NA
|
||||
*/
|
||||
template <class K, class V>
|
||||
class Map
|
||||
{
|
||||
public:
|
||||
#if USE_STD_UNORDERED_MAP
|
||||
typedef std::unordered_map<K, V> RefMap;
|
||||
#else
|
||||
typedef std::map<K, V> RefMap;
|
||||
#endif
|
||||
|
||||
// ------------------------------------------
|
||||
// Iterators
|
||||
// ------------------------------------------
|
||||
|
||||
/** Iterator, can be used to loop the Map. */
|
||||
typedef typename RefMap::iterator iterator;
|
||||
/** Const iterator, can be used to loop the Map. */
|
||||
typedef typename RefMap::const_iterator const_iterator;
|
||||
|
||||
/** Return iterator to beginning. */
|
||||
iterator begin() { return _data.begin(); }
|
||||
/** Return const_iterator to beginning. */
|
||||
const_iterator begin() const { return _data.begin(); }
|
||||
|
||||
/** Return iterator to end.*/
|
||||
iterator end() { return _data.end(); }
|
||||
/** Return const_iterator to end.*/
|
||||
const_iterator end() const { return _data.end(); }
|
||||
|
||||
/** Return const_iterator to beginning.*/
|
||||
const_iterator cbegin() const { return _data.cbegin(); }
|
||||
/** Return const_iterator to end.*/
|
||||
const_iterator cend() const { return _data.cend(); }
|
||||
|
||||
/** Default constructor */
|
||||
Map<K, V>()
|
||||
: _data()
|
||||
{
|
||||
static_assert(std::is_convertible<V, Ref*>::value, "Invalid Type for cocos2d::Map<K, V>!");
|
||||
CCLOGINFO("In the default constructor of Map!");
|
||||
}
|
||||
|
||||
/** Constructor with capacity. */
|
||||
explicit Map<K, V>(ssize_t capacity)
|
||||
: _data()
|
||||
{
|
||||
static_assert(std::is_convertible<V, Ref*>::value, "Invalid Type for cocos2d::Map<K, V>!");
|
||||
CCLOGINFO("In the constructor with capacity of Map!");
|
||||
_data.reserve(capacity);
|
||||
}
|
||||
|
||||
/** Copy constructor. */
|
||||
Map<K, V>(const Map<K, V>& other)
|
||||
{
|
||||
static_assert(std::is_convertible<V, Ref*>::value, "Invalid Type for cocos2d::Map<K, V>!");
|
||||
CCLOGINFO("In the copy constructor of Map!");
|
||||
_data = other._data;
|
||||
addRefForAllObjects();
|
||||
}
|
||||
|
||||
/** Move constructor. */
|
||||
Map<K, V>(Map<K, V>&& other)
|
||||
{
|
||||
static_assert(std::is_convertible<V, Ref*>::value, "Invalid Type for cocos2d::Map<K, V>!");
|
||||
CCLOGINFO("In the move constructor of Map!");
|
||||
_data = std::move(other._data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
* It will release all objects in map.
|
||||
*/
|
||||
~Map<K, V>()
|
||||
{
|
||||
CCLOGINFO("In the destructor of Map!");
|
||||
clear();
|
||||
}
|
||||
|
||||
/** Sets capacity of the map. */
|
||||
void reserve(ssize_t capacity)
|
||||
{
|
||||
#if USE_STD_UNORDERED_MAP
|
||||
_data.reserve(capacity);
|
||||
#endif
|
||||
}
|
||||
|
||||
/** Returns the number of buckets in the Map container. */
|
||||
ssize_t bucketCount() const
|
||||
{
|
||||
#if USE_STD_UNORDERED_MAP
|
||||
return _data.bucket_count();
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/** Returns the number of elements in bucket n. */
|
||||
ssize_t bucketSize(ssize_t n) const
|
||||
{
|
||||
#if USE_STD_UNORDERED_MAP
|
||||
return _data.bucket_size(n);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/** Returns the bucket number where the element with key k is located. */
|
||||
ssize_t bucket(const K& k) const
|
||||
{
|
||||
#if USE_STD_UNORDERED_MAP
|
||||
return _data.bucket(k);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/** The number of elements in the map. */
|
||||
ssize_t size() const
|
||||
{
|
||||
return _data.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a bool value indicating whether the map container is empty, i.e. whether its size is 0.
|
||||
* @note This function does not modify the content of the container in any way.
|
||||
* To clear the content of an array object, member function unordered_map::clear exists.
|
||||
*/
|
||||
bool empty() const
|
||||
{
|
||||
return _data.empty();
|
||||
}
|
||||
|
||||
/** Returns all keys in the map. */
|
||||
std::vector<K> keys() const
|
||||
{
|
||||
std::vector<K> keys;
|
||||
|
||||
if (!_data.empty())
|
||||
{
|
||||
keys.reserve(_data.size());
|
||||
|
||||
for (auto iter = _data.cbegin(); iter != _data.cend(); ++iter)
|
||||
{
|
||||
keys.push_back(iter->first);
|
||||
}
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
/** Returns all keys that matches the object. */
|
||||
std::vector<K> keys(V object) const
|
||||
{
|
||||
std::vector<K> keys;
|
||||
|
||||
if (!_data.empty())
|
||||
{
|
||||
keys.reserve(_data.size() / 10);
|
||||
|
||||
for (auto iter = _data.cbegin(); iter != _data.cend(); ++iter)
|
||||
{
|
||||
if (iter->second == object)
|
||||
{
|
||||
keys.push_back(iter->first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
keys.shrink_to_fit();
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reference to the mapped value of the element with key k in the map.
|
||||
*
|
||||
* @note If key does not match the key of any element in the container, the function return nullptr.
|
||||
* @param key Key value of the element whose mapped value is accessed.
|
||||
* Member type K is the keys for the elements in the container. defined in Map<K, V> as an alias of its first template parameter (Key).
|
||||
*/
|
||||
const V at(const K& key) const
|
||||
{
|
||||
auto iter = _data.find(key);
|
||||
if (iter != _data.end())
|
||||
return iter->second;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
V at(const K& key)
|
||||
{
|
||||
auto iter = _data.find(key);
|
||||
if (iter != _data.end())
|
||||
return iter->second;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches the container for an element with 'key' as key and returns an iterator to it if found,
|
||||
* otherwise it returns an iterator to Map<K, V>::end (the element past the end of the container).
|
||||
*
|
||||
* @param key Key to be searched for.
|
||||
* Member type 'K' is the type of the keys for the elements in the container,
|
||||
* defined in Map<K, V> as an alias of its first template parameter (Key).
|
||||
*/
|
||||
const_iterator find(const K& key) const
|
||||
{
|
||||
return _data.find(key);
|
||||
}
|
||||
|
||||
iterator find(const K& key)
|
||||
{
|
||||
return _data.find(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts new elements in the map.
|
||||
*
|
||||
* @note If the container has already contained the key, this function will erase the old pair(key, object) and insert the new pair.
|
||||
* @param key The key to be inserted.
|
||||
* @param object The object to be inserted.
|
||||
*/
|
||||
void insert(const K& key, V object)
|
||||
{
|
||||
CCASSERT(object != nullptr, "Object is nullptr!");
|
||||
object->retain();
|
||||
erase(key);
|
||||
_data.insert(std::make_pair(key, object));
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an element with an iterator from the Map<K, V> container.
|
||||
*
|
||||
* @param position Iterator pointing to a single element to be removed from the Map<K, V>.
|
||||
* Member type const_iterator is a forward iterator type.
|
||||
*/
|
||||
iterator erase(const_iterator position)
|
||||
{
|
||||
CCASSERT(position != _data.cend(), "Invalid iterator!");
|
||||
position->second->release();
|
||||
return _data.erase(position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an element with an iterator from the Map<K, V> container.
|
||||
*
|
||||
* @param k Key of the element to be erased.
|
||||
* Member type 'K' is the type of the keys for the elements in the container,
|
||||
* defined in Map<K, V> as an alias of its first template parameter (Key).
|
||||
*/
|
||||
size_t erase(const K& k)
|
||||
{
|
||||
auto iter = _data.find(k);
|
||||
if (iter != _data.end())
|
||||
{
|
||||
iter->second->release();
|
||||
_data.erase(iter);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes some elements with a vector which contains keys in the map.
|
||||
*
|
||||
* @param keys Keys of elements to be erased.
|
||||
*/
|
||||
void erase(const std::vector<K>& keys)
|
||||
{
|
||||
for(const auto &key : keys) {
|
||||
this->erase(key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* All the elements in the Map<K,V> container are dropped:
|
||||
* their reference count will be decreased, and they are removed from the container,
|
||||
* leaving it with a size of 0.
|
||||
*/
|
||||
void clear()
|
||||
{
|
||||
for (auto iter = _data.cbegin(); iter != _data.cend(); ++iter)
|
||||
{
|
||||
iter->second->release();
|
||||
}
|
||||
|
||||
_data.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a random object in the map.
|
||||
* @return Returns the random object if the map isn't empty, otherwise it returns nullptr.
|
||||
*/
|
||||
V getRandomObject() const
|
||||
{
|
||||
if (!_data.empty())
|
||||
{
|
||||
ssize_t randIdx = RandomHelper::random_int<int>(0, static_cast<int>(_data.size()) - 1);
|
||||
const_iterator randIter = _data.begin();
|
||||
std::advance(randIter , randIdx);
|
||||
return randIter->second;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Don't uses operator since we could not decide whether it needs 'retain'/'release'.
|
||||
// V& operator[] ( const K& key )
|
||||
// {
|
||||
// CCLOG("copy: [] ref");
|
||||
// return _data[key];
|
||||
// }
|
||||
//
|
||||
// V& operator[] ( K&& key )
|
||||
// {
|
||||
// CCLOG("move [] ref");
|
||||
// return _data[key];
|
||||
// }
|
||||
|
||||
// const V& operator[] ( const K& key ) const
|
||||
// {
|
||||
// CCLOG("const copy []");
|
||||
// return _data.at(key);
|
||||
// }
|
||||
//
|
||||
// const V& operator[] ( K&& key ) const
|
||||
// {
|
||||
// CCLOG("const move []");
|
||||
// return _data.at(key);
|
||||
// }
|
||||
|
||||
/** Copy assignment operator. */
|
||||
Map<K, V>& operator= ( const Map<K, V>& other )
|
||||
{
|
||||
if (this != &other) {
|
||||
CCLOGINFO("In the copy assignment operator of Map!");
|
||||
clear();
|
||||
_data = other._data;
|
||||
addRefForAllObjects();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Move assignment operator. */
|
||||
Map<K, V>& operator= ( Map<K, V>&& other )
|
||||
{
|
||||
if (this != &other) {
|
||||
CCLOGINFO("In the move assignment operator of Map!");
|
||||
clear();
|
||||
_data = std::move(other._data);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
/** Retains all the objects in the map */
|
||||
void addRefForAllObjects()
|
||||
{
|
||||
for (auto iter = _data.begin(); iter != _data.end(); ++iter)
|
||||
{
|
||||
iter->second->retain();
|
||||
}
|
||||
}
|
||||
|
||||
RefMap _data;
|
||||
};
|
||||
|
||||
|
||||
NS_CC_END
|
||||
// end group
|
||||
/// @}
|
||||
171
cocos2d-x/cocos/base/CCRef.cpp
Normal file
171
cocos2d-x/cocos/base/CCRef.cpp
Normal file
@@ -0,0 +1,171 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2010-2012 cocos2d-x.org
|
||||
Copyright (c) 2013-2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
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 "base/CCRef.h"
|
||||
#include "base/CCAutoreleasePool.h"
|
||||
#include "base/ccMacros.h"
|
||||
|
||||
#if CC_REF_LEAK_DETECTION
|
||||
#include <algorithm> // std::find
|
||||
#endif
|
||||
|
||||
NS_CC_BEGIN
|
||||
|
||||
#if CC_REF_LEAK_DETECTION
|
||||
static void trackRef(Ref* ref);
|
||||
static void untrackRef(Ref* ref);
|
||||
#endif
|
||||
|
||||
Ref::Ref()
|
||||
: _referenceCount(1) // when the Ref is created, the reference count of it is 1
|
||||
{
|
||||
#if CC_REF_LEAK_DETECTION
|
||||
trackRef(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
Ref::~Ref()
|
||||
{
|
||||
#if CC_REF_LEAK_DETECTION
|
||||
if (_referenceCount != 0)
|
||||
untrackRef(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Ref::retain()
|
||||
{
|
||||
CCASSERT(_referenceCount > 0, "reference count should be greater than 0");
|
||||
++_referenceCount;
|
||||
}
|
||||
|
||||
void Ref::release()
|
||||
{
|
||||
CCASSERT(_referenceCount > 0, "reference count should be greater than 0");
|
||||
--_referenceCount;
|
||||
|
||||
if (_referenceCount == 0)
|
||||
{
|
||||
#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
|
||||
auto poolManager = PoolManager::getInstance();
|
||||
if (!poolManager->getCurrentPool()->isClearing() && poolManager->isObjectInPools(this))
|
||||
{
|
||||
// Trigger an assert if the reference count is 0 but the Ref is still in autorelease pool.
|
||||
// This happens when 'autorelease/release' were not used in pairs with 'new/retain'.
|
||||
//
|
||||
// Wrong usage (1):
|
||||
//
|
||||
// auto obj = Node::create(); // Ref = 1, but it's an autorelease Ref which means it was in the autorelease pool.
|
||||
// obj->autorelease(); // Wrong: If you wish to invoke autorelease several times, you should retain `obj` first.
|
||||
//
|
||||
// Wrong usage (2):
|
||||
//
|
||||
// auto obj = Node::create();
|
||||
// obj->release(); // Wrong: obj is an autorelease Ref, it will be released when clearing current pool.
|
||||
//
|
||||
// Correct usage (1):
|
||||
//
|
||||
// auto obj = Node::create();
|
||||
// |- new Node(); // `new` is the pair of the `autorelease` of next line
|
||||
// |- autorelease(); // The pair of `new Node`.
|
||||
//
|
||||
// obj->retain();
|
||||
// obj->autorelease(); // This `autorelease` is the pair of `retain` of previous line.
|
||||
//
|
||||
// Correct usage (2):
|
||||
//
|
||||
// auto obj = Node::create();
|
||||
// obj->retain();
|
||||
// obj->release(); // This `release` is the pair of `retain` of previous line.
|
||||
CCASSERT(false, "The reference shouldn't be 0 because it is still in autorelease pool.");
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CC_REF_LEAK_DETECTION
|
||||
untrackRef(this);
|
||||
#endif
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
Ref* Ref::autorelease()
|
||||
{
|
||||
PoolManager::getInstance()->getCurrentPool()->addObject(this);
|
||||
return this;
|
||||
}
|
||||
|
||||
unsigned int Ref::getReferenceCount() const
|
||||
{
|
||||
return _referenceCount;
|
||||
}
|
||||
|
||||
#if CC_REF_LEAK_DETECTION
|
||||
|
||||
static std::list<Ref*> __refAllocationList;
|
||||
|
||||
void Ref::printLeaks()
|
||||
{
|
||||
// Dump Ref object memory leaks
|
||||
if (__refAllocationList.empty())
|
||||
{
|
||||
log("[memory] All Ref objects successfully cleaned up (no leaks detected).\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
log("[memory] WARNING: %d Ref objects still active in memory.\n", (int)__refAllocationList.size());
|
||||
|
||||
for (const auto& ref : __refAllocationList)
|
||||
{
|
||||
CC_ASSERT(ref);
|
||||
const char* type = typeid(*ref).name();
|
||||
log("[memory] LEAK: Ref object '%s' still active with reference count %d.\n", (type ? type : ""), ref->getReferenceCount());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void trackRef(Ref* ref)
|
||||
{
|
||||
CCASSERT(ref, "Invalid parameter, ref should not be null!");
|
||||
|
||||
// Create memory allocation record.
|
||||
__refAllocationList.push_back(ref);
|
||||
}
|
||||
|
||||
static void untrackRef(Ref* ref)
|
||||
{
|
||||
auto iter = std::find(__refAllocationList.begin(), __refAllocationList.end(), ref);
|
||||
if (iter == __refAllocationList.end())
|
||||
{
|
||||
log("[memory] CORRUPTION: Attempting to free (%s) with invalid ref tracking record.\n", typeid(*ref).name());
|
||||
return;
|
||||
}
|
||||
|
||||
__refAllocationList.erase(iter);
|
||||
}
|
||||
|
||||
#endif // #if CC_REF_LEAK_DETECTION
|
||||
|
||||
|
||||
NS_CC_END
|
||||
154
cocos2d-x/cocos/base/CCRef.h
Normal file
154
cocos2d-x/cocos/base/CCRef.h
Normal file
@@ -0,0 +1,154 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2010-2012 cocos2d-x.org
|
||||
Copyright (c) 2013-2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
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 __BASE_CCREF_H__
|
||||
#define __BASE_CCREF_H__
|
||||
|
||||
#include "base/ccMacros.h"
|
||||
#include "base/ccConfig.h"
|
||||
|
||||
#define CC_REF_LEAK_DETECTION 0
|
||||
|
||||
/**
|
||||
* @addtogroup base
|
||||
* @{
|
||||
*/
|
||||
NS_CC_BEGIN
|
||||
|
||||
|
||||
class Ref;
|
||||
|
||||
/**
|
||||
* Interface that defines how to clone an Ref.
|
||||
* @lua NA
|
||||
* @js NA
|
||||
*/
|
||||
class CC_DLL Clonable
|
||||
{
|
||||
public:
|
||||
/** Returns a copy of the Ref. */
|
||||
virtual Clonable* clone() const = 0;
|
||||
|
||||
/**
|
||||
* @js NA
|
||||
* @lua NA
|
||||
*/
|
||||
virtual ~Clonable() {};
|
||||
};
|
||||
|
||||
/**
|
||||
* Ref is used for reference count management. If a class inherits from Ref,
|
||||
* then it is easy to be shared in different places.
|
||||
* @js NA
|
||||
*/
|
||||
class CC_DLL Ref
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Retains the ownership.
|
||||
*
|
||||
* This increases the Ref's reference count.
|
||||
*
|
||||
* @see release, autorelease
|
||||
* @js NA
|
||||
*/
|
||||
void retain();
|
||||
|
||||
/**
|
||||
* Releases the ownership immediately.
|
||||
*
|
||||
* This decrements the Ref's reference count.
|
||||
*
|
||||
* If the reference count reaches 0 after the decrement, this Ref is
|
||||
* destructed.
|
||||
*
|
||||
* @see retain, autorelease
|
||||
* @js NA
|
||||
*/
|
||||
void release();
|
||||
|
||||
/**
|
||||
* Releases the ownership sometime soon automatically.
|
||||
*
|
||||
* This decrements the Ref's reference count at the end of current
|
||||
* autorelease pool block.
|
||||
*
|
||||
* If the reference count reaches 0 after the decrement, this Ref is
|
||||
* destructed.
|
||||
*
|
||||
* @returns The Ref itself.
|
||||
*
|
||||
* @see AutoreleasePool, retain, release
|
||||
* @js NA
|
||||
* @lua NA
|
||||
*/
|
||||
Ref* autorelease();
|
||||
|
||||
/**
|
||||
* Returns the Ref's current reference count.
|
||||
*
|
||||
* @returns The Ref's reference count.
|
||||
* @js NA
|
||||
*/
|
||||
unsigned int getReferenceCount() const;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* The Ref's reference count is 1 after construction.
|
||||
* @js NA
|
||||
*/
|
||||
Ref();
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*
|
||||
* @js NA
|
||||
* @lua NA
|
||||
*/
|
||||
virtual ~Ref();
|
||||
|
||||
protected:
|
||||
/// count of references
|
||||
unsigned int _referenceCount;
|
||||
|
||||
friend class AutoreleasePool;
|
||||
|
||||
// Memory leak diagnostic data (only included when CC_REF_LEAK_DETECTION is defined and its value isn't zero)
|
||||
#if CC_REF_LEAK_DETECTION
|
||||
public:
|
||||
static void printLeaks();
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
NS_CC_END
|
||||
// end of base group
|
||||
/** @} */
|
||||
|
||||
#endif // __BASE_CCREF_H__
|
||||
|
||||
348
cocos2d-x/cocos/base/CCRefPtr.h
Normal file
348
cocos2d-x/cocos/base/CCRefPtr.h
Normal file
@@ -0,0 +1,348 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2014 PlayFirst Inc.
|
||||
Copyright (c) 2014-2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
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_REF_PTR_H__
|
||||
#define __CC_REF_PTR_H__
|
||||
/// @cond DO_NOT_SHOW
|
||||
|
||||
#include "base/CCRef.h"
|
||||
#include "base/ccMacros.h"
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
|
||||
NS_CC_BEGIN
|
||||
|
||||
/**
|
||||
* Utility/support macros. Defined to enable RefPtr<T> to contain types like 'const T' because we do not
|
||||
* regard retain()/release() as affecting mutability of state.
|
||||
*/
|
||||
#define CC_REF_PTR_SAFE_RETAIN(ptr)\
|
||||
\
|
||||
do\
|
||||
{\
|
||||
if (ptr)\
|
||||
{\
|
||||
const_cast<Ref*>(static_cast<const Ref*>(ptr))->retain();\
|
||||
}\
|
||||
\
|
||||
} while (0);
|
||||
|
||||
#define CC_REF_PTR_SAFE_RELEASE(ptr)\
|
||||
\
|
||||
do\
|
||||
{\
|
||||
if (ptr)\
|
||||
{\
|
||||
const_cast<Ref*>(static_cast<const Ref*>(ptr))->release();\
|
||||
}\
|
||||
\
|
||||
} while (0);
|
||||
|
||||
#define CC_REF_PTR_SAFE_RELEASE_NULL(ptr)\
|
||||
\
|
||||
do\
|
||||
{\
|
||||
if (ptr)\
|
||||
{\
|
||||
const_cast<Ref*>(static_cast<const Ref*>(ptr))->release();\
|
||||
ptr = nullptr;\
|
||||
}\
|
||||
\
|
||||
} while (0);
|
||||
|
||||
/**
|
||||
* Wrapper class which maintains a strong reference to a cocos2dx cocos2d::Ref* type object.
|
||||
* Similar in concept to a boost smart pointer.
|
||||
*
|
||||
* Enables the use of the RAII idiom with Cocos2dx objects and helps automate some of the more
|
||||
* mundane tasks of pointer initialization and cleanup.
|
||||
*
|
||||
* The class itself is modelled on C++ 11 std::shared_ptr, and trys to keep some of the methods
|
||||
* and functionality consistent with std::shared_ptr.
|
||||
*/
|
||||
template <typename T> class RefPtr
|
||||
{
|
||||
public:
|
||||
|
||||
inline RefPtr()
|
||||
: _ptr(nullptr)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
inline RefPtr(RefPtr<T> && other)
|
||||
{
|
||||
_ptr = other._ptr;
|
||||
other._ptr = nullptr;
|
||||
}
|
||||
|
||||
inline RefPtr(T * ptr)
|
||||
: _ptr(ptr)
|
||||
{
|
||||
CC_REF_PTR_SAFE_RETAIN(_ptr);
|
||||
}
|
||||
|
||||
inline RefPtr(std::nullptr_t ptr)
|
||||
: _ptr(nullptr)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
inline RefPtr(const RefPtr<T> & other)
|
||||
: _ptr(other._ptr)
|
||||
{
|
||||
CC_REF_PTR_SAFE_RETAIN(_ptr);
|
||||
}
|
||||
|
||||
inline ~RefPtr()
|
||||
{
|
||||
CC_REF_PTR_SAFE_RELEASE_NULL(_ptr);
|
||||
}
|
||||
|
||||
inline RefPtr<T> & operator = (const RefPtr<T> & other)
|
||||
{
|
||||
if (other._ptr != _ptr)
|
||||
{
|
||||
CC_REF_PTR_SAFE_RETAIN(other._ptr);
|
||||
CC_REF_PTR_SAFE_RELEASE(_ptr);
|
||||
_ptr = other._ptr;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline RefPtr<T> & operator = (RefPtr<T> && other)
|
||||
{
|
||||
if (&other != this)
|
||||
{
|
||||
CC_REF_PTR_SAFE_RELEASE(_ptr);
|
||||
_ptr = other._ptr;
|
||||
other._ptr = nullptr;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline RefPtr<T> & operator = (T * other)
|
||||
{
|
||||
if (other != _ptr)
|
||||
{
|
||||
CC_REF_PTR_SAFE_RETAIN(other);
|
||||
CC_REF_PTR_SAFE_RELEASE(_ptr);
|
||||
_ptr = other;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline RefPtr<T> & operator = (std::nullptr_t other)
|
||||
{
|
||||
CC_REF_PTR_SAFE_RELEASE_NULL(_ptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline operator T * () const { return _ptr; }
|
||||
|
||||
inline T & operator * () const
|
||||
{
|
||||
CCASSERT(_ptr, "Attempt to dereference a null pointer!");
|
||||
return *_ptr;
|
||||
}
|
||||
|
||||
inline T * operator->() const
|
||||
{
|
||||
CCASSERT(_ptr, "Attempt to dereference a null pointer!");
|
||||
return _ptr;
|
||||
}
|
||||
|
||||
inline T * get() const { return _ptr; }
|
||||
|
||||
|
||||
inline bool operator == (const RefPtr<T> & other) const { return _ptr == other._ptr; }
|
||||
|
||||
inline bool operator == (const T * other) const { return _ptr == other; }
|
||||
|
||||
inline bool operator == (typename std::remove_const<T>::type * other) const { return _ptr == other; }
|
||||
|
||||
inline bool operator == (const std::nullptr_t other) const { return _ptr == other; }
|
||||
|
||||
|
||||
inline bool operator != (const RefPtr<T> & other) const { return _ptr != other._ptr; }
|
||||
|
||||
inline bool operator != (const T * other) const { return _ptr != other; }
|
||||
|
||||
inline bool operator != (typename std::remove_const<T>::type * other) const { return _ptr != other; }
|
||||
|
||||
inline bool operator != (const std::nullptr_t other) const { return _ptr != other; }
|
||||
|
||||
|
||||
inline bool operator > (const RefPtr<T> & other) const { return _ptr > other._ptr; }
|
||||
|
||||
inline bool operator > (const T * other) const { return _ptr > other; }
|
||||
|
||||
inline bool operator > (typename std::remove_const<T>::type * other) const { return _ptr > other; }
|
||||
|
||||
|
||||
inline bool operator < (const RefPtr<T> & other) const { return _ptr < other._ptr; }
|
||||
|
||||
inline bool operator < (const T * other) const { return _ptr < other; }
|
||||
|
||||
inline bool operator < (typename std::remove_const<T>::type * other) const { return _ptr < other; }
|
||||
|
||||
|
||||
inline bool operator >= (const RefPtr<T> & other) const { return _ptr >= other._ptr; }
|
||||
|
||||
inline bool operator >= (const T * other) const { return _ptr >= other; }
|
||||
|
||||
inline bool operator >= (typename std::remove_const<T>::type * other) const { return _ptr >= other; }
|
||||
|
||||
|
||||
inline bool operator <= (const RefPtr<T> & other) const { return _ptr <= other._ptr; }
|
||||
|
||||
inline bool operator <= (const T * other) const { return _ptr <= other; }
|
||||
|
||||
inline bool operator <= (typename std::remove_const<T>::type * other) const { return _ptr <= other; }
|
||||
|
||||
|
||||
inline operator bool() const { return _ptr != nullptr; }
|
||||
|
||||
inline void reset()
|
||||
{
|
||||
CC_REF_PTR_SAFE_RELEASE_NULL(_ptr);
|
||||
}
|
||||
|
||||
inline void swap(RefPtr<T> & other)
|
||||
{
|
||||
if (&other != this)
|
||||
{
|
||||
T * tmp = _ptr;
|
||||
_ptr = other._ptr;
|
||||
other._ptr = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function assigns to this RefPtr<T> but does not increase the reference count of the object pointed to.
|
||||
* Useful for assigning an object created through the 'new' operator to a RefPtr<T>. Basically used in scenarios
|
||||
* where the RefPtr<T> has the initial ownership of the object.
|
||||
*
|
||||
* E.G:
|
||||
* RefPtr<cocos2d::Image> image;
|
||||
* image.weakAssign(new cocos2d::Image());
|
||||
*
|
||||
* Instead of:
|
||||
* RefPtr<cocos2d::Image> image;
|
||||
* image = new cocos2d::Image();
|
||||
* image->release(); // Required because new'd object already has a reference count of '1'.
|
||||
*/
|
||||
inline void weakAssign(const RefPtr<T> & other)
|
||||
{
|
||||
CC_REF_PTR_SAFE_RELEASE(_ptr);
|
||||
_ptr = other._ptr;
|
||||
}
|
||||
|
||||
private:
|
||||
T * _ptr;
|
||||
|
||||
// NOTE: We can ensure T is derived from cocos2d::Ref at compile time here.
|
||||
static_assert(std::is_base_of<Ref, typename std::remove_const<T>::type>::value, "T must be derived from Ref");
|
||||
};
|
||||
|
||||
template<class T> inline
|
||||
bool operator<(const RefPtr<T>& r, std::nullptr_t)
|
||||
{
|
||||
return std::less<T*>()(r.get(), nullptr);
|
||||
}
|
||||
|
||||
template<class T> inline
|
||||
bool operator<(std::nullptr_t, const RefPtr<T>& r)
|
||||
{
|
||||
return std::less<T*>()(nullptr, r.get());
|
||||
}
|
||||
|
||||
template<class T> inline
|
||||
bool operator>(const RefPtr<T>& r, std::nullptr_t)
|
||||
{
|
||||
return nullptr < r;
|
||||
}
|
||||
|
||||
template<class T> inline
|
||||
bool operator>(std::nullptr_t, const RefPtr<T>& r)
|
||||
{
|
||||
return r < nullptr;
|
||||
}
|
||||
|
||||
template<class T> inline
|
||||
bool operator<=(const RefPtr<T>& r, std::nullptr_t)
|
||||
{
|
||||
return !(nullptr < r);
|
||||
}
|
||||
|
||||
template<class T> inline
|
||||
bool operator<=(std::nullptr_t, const RefPtr<T>& r)
|
||||
{
|
||||
return !(r < nullptr);
|
||||
}
|
||||
|
||||
template<class T> inline
|
||||
bool operator>=(const RefPtr<T>& r, std::nullptr_t)
|
||||
{
|
||||
return !(r < nullptr);
|
||||
}
|
||||
|
||||
template<class T> inline
|
||||
bool operator>=(std::nullptr_t, const RefPtr<T>& r)
|
||||
{
|
||||
return !(nullptr < r);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cast between RefPtr types statically.
|
||||
*/
|
||||
template<class T, class U> RefPtr<T> static_pointer_cast(const RefPtr<U> & r)
|
||||
{
|
||||
return RefPtr<T>(static_cast<T*>(r.get()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Cast between RefPtr types dynamically.
|
||||
*/
|
||||
template<class T, class U> RefPtr<T> dynamic_pointer_cast(const RefPtr<U> & r)
|
||||
{
|
||||
return RefPtr<T>(dynamic_cast<T*>(r.get()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Done with these macros.
|
||||
*/
|
||||
#undef CC_REF_PTR_SAFE_RETAIN
|
||||
#undef CC_REF_PTR_SAFE_RELEASE
|
||||
#undef CC_REF_PTR_SAFE_RELEASE_NULL
|
||||
|
||||
NS_CC_END
|
||||
|
||||
/// @endcond
|
||||
#endif // __CC_REF_PTR_H__
|
||||
524
cocos2d-x/cocos/base/CCRenderTexture.cpp
Normal file
524
cocos2d-x/cocos/base/CCRenderTexture.cpp
Normal file
@@ -0,0 +1,524 @@
|
||||
|
||||
/****************************************************************************
|
||||
Copyright (c) 2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
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 "CCRenderTexture.h"
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
#include "base/ccMacros.h"
|
||||
#include "base/CCConfiguration.h"
|
||||
#include "platform/CCPlatformConfig.h"
|
||||
#include "platform/CCApplication.h"
|
||||
|
||||
NS_CC_BEGIN
|
||||
|
||||
RenderTexture::RenderTexture(int width, int height)
|
||||
: _deviceResolution( {(float)width, (float)height} )
|
||||
{
|
||||
}
|
||||
|
||||
RenderTexture::~RenderTexture()
|
||||
{
|
||||
if (_program)
|
||||
{
|
||||
glDeleteProgram(_program);
|
||||
_program = 0;
|
||||
}
|
||||
|
||||
if (_VBO[0])
|
||||
{
|
||||
glDeleteBuffers(1, _VBO);
|
||||
_VBO[0] = _VBO[1] = 0;
|
||||
}
|
||||
|
||||
if (_texture)
|
||||
{
|
||||
glDeleteTextures(1, &_texture);
|
||||
_texture = 0;
|
||||
}
|
||||
|
||||
if (_FBO)
|
||||
{
|
||||
glDeleteFramebuffers(1, &_FBO);
|
||||
_FBO = 0;
|
||||
}
|
||||
|
||||
if (_depthBuffer)
|
||||
{
|
||||
glDeleteRenderbuffers(1, &_depthBuffer);
|
||||
_depthBuffer = 0;
|
||||
}
|
||||
|
||||
if (_stencilBuffer)
|
||||
{
|
||||
glDeleteRenderbuffers(1, &_stencilBuffer);
|
||||
_stencilBuffer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void RenderTexture::init(int factor)
|
||||
{
|
||||
_width = _deviceResolution.x / factor;
|
||||
_height = _deviceResolution.y / factor;
|
||||
|
||||
if (! initProgram())
|
||||
return;
|
||||
|
||||
initTexture();
|
||||
|
||||
if (Configuration::getInstance()->supportsShareableVAO())
|
||||
initVBOAndVAO();
|
||||
else
|
||||
initVBO();
|
||||
|
||||
initFramebuffer();
|
||||
}
|
||||
|
||||
void RenderTexture::prepare()
|
||||
{
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, _FBO);
|
||||
ccViewport(0, 0, _width, _height);
|
||||
}
|
||||
|
||||
void RenderTexture::draw()
|
||||
{
|
||||
bool supportsVAO = Configuration::getInstance()->supportsShareableVAO();
|
||||
|
||||
recordPreviousGLStates(supportsVAO);
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, _mainFBO);
|
||||
ccViewport(0, 0, _deviceResolution.x, _deviceResolution.y);
|
||||
|
||||
glClearColor(0, 0, 0, 1);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
glDisable(GL_BLEND);
|
||||
glDisable(GL_CULL_FACE);
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0 + 0);
|
||||
glBindTexture(GL_TEXTURE_2D, _texture);
|
||||
|
||||
glUseProgram(_program);
|
||||
glUniform1i(_fragUniformTextureLocation, 0);
|
||||
|
||||
if (supportsVAO)
|
||||
glBindVertexArray(_VAO);
|
||||
else
|
||||
{
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _VBO[0]);
|
||||
glEnableVertexAttribArray(_vertAttributePositionLocation);
|
||||
glVertexAttribPointer(_vertAttributePositionLocation, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (GLvoid*)0);
|
||||
glEnableVertexAttribArray(_vertAttributeTextureCoordLocation);
|
||||
glVertexAttribPointer(_vertAttributeTextureCoordLocation, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (GLvoid*)(2 * sizeof(float)));
|
||||
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _VBO[1]);
|
||||
}
|
||||
|
||||
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, (GLvoid*)0);
|
||||
|
||||
CHECK_GL_ERROR_DEBUG();
|
||||
|
||||
resetPreviousGLStates(supportsVAO);
|
||||
}
|
||||
|
||||
//
|
||||
// Private functions
|
||||
//
|
||||
|
||||
void RenderTexture::recordPreviousGLStates(bool supportsVOA)
|
||||
{
|
||||
if (!supportsVOA)
|
||||
{
|
||||
_prevPosLocInfo = getVertexAttribPointerInfo(_vertAttributePositionLocation);
|
||||
_prevTexCoordLocInfo = getVertexAttribPointerInfo(_vertAttributeTextureCoordLocation);
|
||||
_prevVBO = ccGetBoundVertexBuffer();
|
||||
_prevVIO = ccGetBoundIndexBuffer();
|
||||
}
|
||||
|
||||
_preveBoundTextureInfo = getBoundTextureInfo(0);
|
||||
|
||||
glGetBooleanv(GL_COLOR_WRITEMASK, _prevColorWriteMask);
|
||||
glGetBooleanv(GL_DEPTH_TEST, &_prevDepthTest);
|
||||
glGetBooleanv(GL_BLEND, &_prevBlendTest);
|
||||
glGetBooleanv(GL_CULL_FACE, &_prevCullFase);
|
||||
glGetBooleanv(GL_STENCIL_TEST, &_prevStencilTest);
|
||||
glGetBooleanv(GL_SCISSOR_TEST,&_prevScissorTest);
|
||||
glGetIntegerv(GL_CURRENT_PROGRAM, &_prevProgram);
|
||||
}
|
||||
|
||||
void RenderTexture::resetPreviousGLStates(bool supportsVAO) const
|
||||
{
|
||||
glUseProgram(_prevProgram);
|
||||
if (supportsVAO)
|
||||
glBindVertexArray(0);
|
||||
else
|
||||
{
|
||||
if (_prevPosLocInfo)
|
||||
{
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _prevPosLocInfo->VBO);
|
||||
glVertexAttribPointer(_prevPosLocInfo->index,
|
||||
_prevPosLocInfo->size,
|
||||
_prevPosLocInfo->type,
|
||||
_prevPosLocInfo->normalized,
|
||||
_prevPosLocInfo->stride,
|
||||
_prevPosLocInfo->pointer);
|
||||
}
|
||||
if (_prevTexCoordLocInfo)
|
||||
{
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _prevTexCoordLocInfo->VBO);
|
||||
glVertexAttribPointer(_prevTexCoordLocInfo->index,
|
||||
_prevTexCoordLocInfo->size,
|
||||
_prevTexCoordLocInfo->type,
|
||||
_prevTexCoordLocInfo->normalized,
|
||||
_prevTexCoordLocInfo->stride,
|
||||
_prevTexCoordLocInfo->pointer);
|
||||
}
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _prevVBO);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _prevVIO);
|
||||
}
|
||||
glColorMask(_prevColorWriteMask[0], _prevColorWriteMask[1], _prevColorWriteMask[2], _prevColorWriteMask[3]);
|
||||
if (GL_TRUE == _prevDepthTest) glEnable(GL_DEPTH_TEST);
|
||||
if (GL_TRUE == _prevBlendTest) glEnable(GL_BLEND);
|
||||
if (GL_TRUE == _prevCullFase) glEnable(GL_CULL_FACE);
|
||||
if (GL_TRUE == _prevStencilTest) glEnable(GL_STENCIL_TEST);
|
||||
if (GL_TRUE == _prevScissorTest) glEnable(GL_SCISSOR_TEST);
|
||||
|
||||
if(_preveBoundTextureInfo != nullptr) {
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(_preveBoundTextureInfo->target, _preveBoundTextureInfo->texture);
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
std::string logForOpenGLShader(GLuint shader)
|
||||
{
|
||||
GLint logLength = 0;
|
||||
|
||||
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logLength);
|
||||
if (logLength < 1)
|
||||
return "";
|
||||
|
||||
char *logBytes = (char*)malloc(sizeof(char) * logLength);
|
||||
glGetShaderInfoLog(shader, logLength, nullptr, logBytes);
|
||||
std::string ret(logBytes);
|
||||
|
||||
free(logBytes);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
bool RenderTexture::parseVertexAttribs()
|
||||
{
|
||||
_vertAttributePositionLocation = glGetAttribLocation(_program, "a_position");
|
||||
if (-1 == _vertAttributePositionLocation)
|
||||
{
|
||||
CCLOG("RenderTexture: %s: can not find vertex attribute of a_position", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
_vertAttributeTextureCoordLocation = glGetAttribLocation(_program, "a_texCoord");
|
||||
if (-1 == _vertAttributeTextureCoordLocation)
|
||||
{
|
||||
CCLOG("RenderTexture: %s: can not find vertex attribute of a_texCoord", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RenderTexture::parseUniforms()
|
||||
{
|
||||
_fragUniformTextureLocation = glGetUniformLocation(_program, "u_texture");
|
||||
if (-1 == _fragUniformTextureLocation)
|
||||
{
|
||||
CCLOG("RenderTexture: %s: can not find uniform location of u_texture", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RenderTexture::compileShader(GLuint& shader, GLenum type, const GLchar* source) const
|
||||
{
|
||||
shader = glCreateShader(type);
|
||||
glShaderSource(shader, 1, &source, nullptr);
|
||||
glCompileShader(shader);
|
||||
|
||||
GLint status = 0;
|
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
|
||||
if (! status)
|
||||
{
|
||||
CCLOG("RenderTexture: ERROR: Failed to compile shader:\n%s", source);
|
||||
CCLOG("RenderTexture: %s", logForOpenGLShader(shader).c_str());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return (GL_TRUE == status);
|
||||
}
|
||||
|
||||
bool RenderTexture::initProgram()
|
||||
{
|
||||
GLuint vertShader = 0;
|
||||
GLuint fragShader = 0;
|
||||
|
||||
// compile vertex shader
|
||||
const char* vert = R"(
|
||||
#ifdef GL_ES
|
||||
precision highp float;
|
||||
#endif
|
||||
attribute vec2 a_position;
|
||||
attribute vec2 a_texCoord;
|
||||
varying vec2 v_texCoord;
|
||||
void main()
|
||||
{
|
||||
gl_Position = vec4(a_position, 0, 1);
|
||||
v_texCoord = a_texCoord;
|
||||
}
|
||||
)";
|
||||
if (! compileShader(vertShader, GL_VERTEX_SHADER, vert))
|
||||
{
|
||||
CCLOG("RenderTexture: ERROR: Failed to compile vertex shader");
|
||||
return false;
|
||||
}
|
||||
|
||||
// compile fragment shader
|
||||
const char* frag = R"(
|
||||
#ifdef GL_ES
|
||||
precision highp float;
|
||||
#endif
|
||||
varying vec2 v_texCoord;
|
||||
uniform sampler2D u_texture;
|
||||
void main()
|
||||
{
|
||||
gl_FragColor = texture2D(u_texture, v_texCoord);
|
||||
}
|
||||
)";
|
||||
if (! compileShader(fragShader, GL_FRAGMENT_SHADER, frag))
|
||||
{
|
||||
CCLOG("RenderTexture: ERROR: Failed to compile fragment shader");
|
||||
glDeleteShader(vertShader);
|
||||
return false;
|
||||
}
|
||||
|
||||
// create program
|
||||
_program = glCreateProgram();
|
||||
if (! _program)
|
||||
{
|
||||
glDeleteShader(vertShader);
|
||||
glDeleteShader(fragShader);
|
||||
return false;
|
||||
}
|
||||
glAttachShader(_program, vertShader);
|
||||
glAttachShader(_program, fragShader);
|
||||
glLinkProgram(_program);
|
||||
|
||||
glDeleteShader(vertShader);
|
||||
glDeleteShader(fragShader);
|
||||
|
||||
GLint status = 0;
|
||||
glGetProgramiv(_program, GL_LINK_STATUS, &status);
|
||||
if (GL_FALSE == status)
|
||||
{
|
||||
CCLOG("RenderTexture: ERROR: %s: failed to link program ", __FUNCTION__);
|
||||
glDeleteProgram(_program);
|
||||
_program = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! parseVertexAttribs())
|
||||
return false;
|
||||
|
||||
if (! parseUniforms())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RenderTexture::initTexture()
|
||||
{
|
||||
glGenTextures(1, &_texture);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, _texture);
|
||||
unsigned char* texData = (unsigned char*)malloc(_width * _height * 3);
|
||||
memset(texData, 0, _width * _height * 3);
|
||||
|
||||
GLint alignment = 1;
|
||||
int bytesPerRow = _width * 3;
|
||||
if (bytesPerRow % 8 == 0)
|
||||
alignment = 8;
|
||||
else if (bytesPerRow % 4 == 0)
|
||||
alignment = 4;
|
||||
else if (bytesPerRow % 2 == 0)
|
||||
alignment = 2;
|
||||
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, alignment);
|
||||
glTexImage2D(GL_TEXTURE_2D,
|
||||
0,
|
||||
GL_RGB,
|
||||
_width,
|
||||
_height,
|
||||
0,
|
||||
GL_RGB,
|
||||
GL_UNSIGNED_BYTE,
|
||||
texData);
|
||||
|
||||
free(texData);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
CHECK_GL_ERROR_DEBUG();
|
||||
}
|
||||
|
||||
void RenderTexture::initVBO()
|
||||
{
|
||||
glGenBuffers(2, _VBO);
|
||||
|
||||
// vertex buffer
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _VBO[0]);
|
||||
float vertices[] = {
|
||||
1.f, 1.f, 1.f, 1.f, // top right
|
||||
1.f, -1.f, 1.f, 0.f, // bottom right
|
||||
-1.f, -1.f, 0.f, 0.f, // bottom left
|
||||
-1.f, 1.f, 0.f, 1.f, // top left
|
||||
};
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
|
||||
// index buffer
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _VBO[1]);
|
||||
unsigned int indices[] = { 0, 1, 3,
|
||||
1, 2, 3
|
||||
};
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
|
||||
CHECK_GL_ERROR_DEBUG();
|
||||
}
|
||||
|
||||
void RenderTexture::initVBOAndVAO()
|
||||
{
|
||||
glGenVertexArrays(1, &_VAO);
|
||||
glGenBuffers(2, _VBO);
|
||||
glBindVertexArray(_VAO);
|
||||
|
||||
// vertex buffer
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _VBO[0]);
|
||||
float vertices[] = {
|
||||
1.f, 1.f, 1.f, 1.f, // top right
|
||||
1.f, -1.f, 1.f, 0.f, // bottom right
|
||||
-1.f, -1.f, 0.f, 0.f, // bottom left
|
||||
-1.f, 1.f, 0.f, 1.f, // top left
|
||||
};
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
|
||||
|
||||
// index buffer
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _VBO[1]);
|
||||
unsigned int indices[] = { 0, 1, 3,
|
||||
1, 2, 3
|
||||
};
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
|
||||
|
||||
// position attribute
|
||||
glEnableVertexAttribArray(_vertAttributePositionLocation);
|
||||
glVertexAttribPointer(_vertAttributePositionLocation, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (GLvoid*)0);
|
||||
// texture coord attribute
|
||||
glEnableVertexAttribArray(_vertAttributeTextureCoordLocation);
|
||||
glVertexAttribPointer(_vertAttributeTextureCoordLocation, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (GLvoid*)(2 * sizeof(float)));
|
||||
|
||||
glBindVertexArray(0);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
|
||||
CHECK_GL_ERROR_DEBUG();
|
||||
}
|
||||
|
||||
void RenderTexture::initFramebuffer()
|
||||
{
|
||||
_mainFBO = Application::getInstance()->getMainFBO();
|
||||
glGenFramebuffers(1, &_FBO);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, _FBO);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _texture, 0);
|
||||
|
||||
ccActiveOffScreenFramebuffer(_FBO);
|
||||
|
||||
// set up depth buffer and stencil buffer
|
||||
#if(CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
|
||||
if(Configuration::getInstance()->supportsOESPackedDepthStencil())
|
||||
{
|
||||
//create and attach depth buffer
|
||||
glGenRenderbuffers(1, &_depthBuffer);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, _depthBuffer);
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, (GLsizei)_width, (GLsizei)_height);
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _depthBuffer);
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, _depthBuffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
glGenRenderbuffers(1, &_depthBuffer);
|
||||
glGenRenderbuffers(1, &_stencilBuffer);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, _depthBuffer);
|
||||
|
||||
if(Configuration::getInstance()->supportsOESDepth24())
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24_OES, (GLsizei)_width, (GLsizei)_height);
|
||||
else
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, (GLsizei)_width, (GLsizei)_height);
|
||||
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, _stencilBuffer);
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, (GLsizei)_width, (GLsizei)_height);
|
||||
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _depthBuffer);
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, _stencilBuffer);
|
||||
}
|
||||
#else
|
||||
//create and attach depth buffer
|
||||
glGenRenderbuffers(1, &_depthBuffer);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, _depthBuffer);
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, (GLsizei)_width, (GLsizei)_height);
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _depthBuffer);
|
||||
|
||||
// if depth format is the one with stencil part, bind same render buffer as stencil attachment
|
||||
// if (_depthAndStencilFormat == GL_DEPTH24_STENCIL8)
|
||||
{
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, _depthBuffer);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
||||
CCLOG("RenderTexture: %s : frame buffer is not complete.", __FUNCTION__);
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, _mainFBO);
|
||||
}
|
||||
|
||||
NS_CC_END
|
||||
98
cocos2d-x/cocos/base/CCRenderTexture.h
Normal file
98
cocos2d-x/cocos/base/CCRenderTexture.h
Normal file
@@ -0,0 +1,98 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
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.
|
||||
****************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include "base/ccMacros.h"
|
||||
#include "math/Vec2.h"
|
||||
#include "platform/CCGL.h"
|
||||
#include "base/CCGLUtils.h"
|
||||
|
||||
NS_CC_BEGIN
|
||||
|
||||
struct VertexAttributePointerInfo;
|
||||
|
||||
class RenderTexture
|
||||
{
|
||||
public:
|
||||
RenderTexture(int width, int height);
|
||||
~RenderTexture();
|
||||
|
||||
void init(int factor);
|
||||
|
||||
/** Do some work before draw, such as:
|
||||
* - bind frame buffer
|
||||
* - set viewport
|
||||
* This function should be invoked at the begin of rendering the frame.
|
||||
*/
|
||||
void prepare();
|
||||
void draw();
|
||||
|
||||
private:
|
||||
bool initProgram();
|
||||
void initTexture();
|
||||
bool compileShader(GLuint& shader, GLenum type, const GLchar* source) const;
|
||||
bool parseVertexAttribs();
|
||||
bool parseUniforms();
|
||||
void initVBOAndVAO();
|
||||
void initVBO();
|
||||
void initFramebuffer();
|
||||
void recordPreviousGLStates(bool supportsVAO);
|
||||
void resetPreviousGLStates(bool supportsVAO) const;
|
||||
|
||||
GLuint _texture = 0;
|
||||
GLint _mainFBO = -1;
|
||||
GLuint _FBO = 0;
|
||||
GLuint _VBO[2] = {0, 0};
|
||||
GLuint _VAO = 0;
|
||||
GLuint _depthBuffer = 0;
|
||||
GLuint _stencilBuffer = 0;
|
||||
|
||||
GLuint _program = 0;
|
||||
GLint _vertAttributePositionLocation = -1;
|
||||
GLint _vertAttributeTextureCoordLocation = -1;
|
||||
GLint _fragUniformTextureLocation = -1;
|
||||
|
||||
// the size of the render texture
|
||||
int _width = 0;
|
||||
int _height = 0;
|
||||
|
||||
// device resolution
|
||||
Vec2 _deviceResolution;
|
||||
|
||||
// record previous gl states
|
||||
GLint _prevVBO = 0;
|
||||
GLint _prevVIO = 0;
|
||||
const VertexAttributePointerInfo* _prevPosLocInfo = nullptr;
|
||||
const VertexAttributePointerInfo* _prevTexCoordLocInfo = nullptr;
|
||||
GLboolean _prevColorWriteMask[4] = {GL_FALSE};
|
||||
GLboolean _prevDepthTest = GL_FALSE;
|
||||
GLboolean _prevBlendTest = GL_FALSE;
|
||||
GLboolean _prevCullFase = GL_FALSE;
|
||||
GLboolean _prevStencilTest = GL_FALSE;
|
||||
GLboolean _prevScissorTest = GL_FALSE;
|
||||
GLint _prevProgram = 0;
|
||||
BoundTextureInfo* _preveBoundTextureInfo = nullptr;
|
||||
};
|
||||
|
||||
NS_CC_END
|
||||
523
cocos2d-x/cocos/base/CCScheduler.cpp
Normal file
523
cocos2d-x/cocos/base/CCScheduler.cpp
Normal file
@@ -0,0 +1,523 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2008-2010 Ricardo Quesada
|
||||
Copyright (c) 2010-2012 cocos2d-x.org
|
||||
Copyright (c) 2011 Zynga Inc.
|
||||
Copyright (c) 2013-2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
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 "base/CCScheduler.h"
|
||||
#include "base/ccMacros.h"
|
||||
#include "base/utlist.h"
|
||||
#include "base/ccCArray.h"
|
||||
|
||||
#define CC_REPEAT_FOREVER (UINT_MAX -1)
|
||||
|
||||
NS_CC_BEGIN
|
||||
|
||||
// data structures
|
||||
|
||||
// A list double-linked list used for "updates with priority"
|
||||
typedef struct _listEntry
|
||||
{
|
||||
struct _listEntry *prev, *next;
|
||||
ccSchedulerFunc callback;
|
||||
void *target;
|
||||
int priority;
|
||||
bool paused;
|
||||
bool markedForDeletion; // selector will no longer be called and entry will be removed at end of the next tick
|
||||
} tListEntry;
|
||||
|
||||
typedef struct _hashUpdateEntry
|
||||
{
|
||||
tListEntry **list; // Which list does it belong to ?
|
||||
tListEntry *entry; // entry in the list
|
||||
void *target;
|
||||
ccSchedulerFunc callback;
|
||||
UT_hash_handle hh;
|
||||
} tHashUpdateEntry;
|
||||
|
||||
// Hash Element used for "selectors with interval"
|
||||
typedef struct _hashSelectorEntry
|
||||
{
|
||||
ccArray *timers;
|
||||
void *target;
|
||||
int timerIndex;
|
||||
Timer *currentTimer;
|
||||
bool currentTimerSalvaged;
|
||||
bool paused;
|
||||
UT_hash_handle hh;
|
||||
} tHashTimerEntry;
|
||||
|
||||
// implementation Timer
|
||||
|
||||
Timer::Timer()
|
||||
{
|
||||
}
|
||||
|
||||
void Timer::setupTimerWithInterval(float seconds, unsigned int repeat, float delay)
|
||||
{
|
||||
_elapsed = -1;
|
||||
_interval = seconds;
|
||||
_delay = delay;
|
||||
_useDelay = (_delay > 0.0f) ? true : false;
|
||||
_repeat = repeat;
|
||||
_runForever = (_repeat == CC_REPEAT_FOREVER) ? true : false;
|
||||
}
|
||||
|
||||
void Timer::update(float dt)
|
||||
{
|
||||
if (_elapsed == -1)
|
||||
{
|
||||
_elapsed = 0;
|
||||
_timesExecuted = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// accumulate elapsed time
|
||||
_elapsed += dt;
|
||||
|
||||
// deal with delay
|
||||
if (_useDelay)
|
||||
{
|
||||
if (_elapsed < _delay)
|
||||
{
|
||||
return;
|
||||
}
|
||||
trigger(_delay);
|
||||
_elapsed = _elapsed - _delay;
|
||||
_timesExecuted += 1;
|
||||
_useDelay = false;
|
||||
// after delay, the rest time should compare with interval
|
||||
if (!_runForever && _timesExecuted > _repeat)
|
||||
{ //unschedule timer
|
||||
cancel();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// if _interval == 0, should trigger once every frame
|
||||
float interval = (_interval > 0) ? _interval : _elapsed;
|
||||
while (_elapsed >= interval)
|
||||
{
|
||||
trigger(interval);
|
||||
_elapsed -= interval;
|
||||
_timesExecuted += 1;
|
||||
|
||||
if (!_runForever && _timesExecuted > _repeat)
|
||||
{
|
||||
cancel();
|
||||
break;
|
||||
}
|
||||
|
||||
if (_elapsed <= 0.f)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (_scheduler->isCurrentTargetSalvaged())
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TimerTargetCallback
|
||||
|
||||
TimerTargetCallback::TimerTargetCallback()
|
||||
{
|
||||
}
|
||||
|
||||
bool TimerTargetCallback::initWithCallback(Scheduler* scheduler, const ccSchedulerFunc& callback, void *target, const std::string& key, float seconds, unsigned int repeat, float delay)
|
||||
{
|
||||
_scheduler = scheduler;
|
||||
_target = target;
|
||||
_callback = callback;
|
||||
_key = key;
|
||||
setupTimerWithInterval(seconds, repeat, delay);
|
||||
return true;
|
||||
}
|
||||
|
||||
void TimerTargetCallback::trigger(float dt)
|
||||
{
|
||||
if (_callback)
|
||||
{
|
||||
_callback(dt);
|
||||
}
|
||||
}
|
||||
|
||||
void TimerTargetCallback::cancel()
|
||||
{
|
||||
_scheduler->unschedule(_key, _target);
|
||||
}
|
||||
|
||||
// implementation of Scheduler
|
||||
|
||||
Scheduler::Scheduler()
|
||||
{
|
||||
// I don't expect to have more than 30 functions to all per frame
|
||||
_functionsToPerform.reserve(30);
|
||||
}
|
||||
|
||||
Scheduler::~Scheduler(void)
|
||||
{
|
||||
unscheduleAll();
|
||||
}
|
||||
|
||||
void Scheduler::removeHashElement(_hashSelectorEntry *element)
|
||||
{
|
||||
ccArrayFree(element->timers);
|
||||
HASH_DEL(_hashForTimers, element);
|
||||
free(element);
|
||||
}
|
||||
|
||||
void Scheduler::schedule(const ccSchedulerFunc& callback, void *target, float interval, bool paused, const std::string& key)
|
||||
{
|
||||
this->schedule(callback, target, interval, CC_REPEAT_FOREVER, 0.0f, paused, key);
|
||||
}
|
||||
|
||||
void Scheduler::schedule(const ccSchedulerFunc& callback, void *target, float interval, unsigned int repeat, float delay, bool paused, const std::string& key)
|
||||
{
|
||||
CCASSERT(target, "Argument target must be non-nullptr");
|
||||
CCASSERT(!key.empty(), "key should not be empty!");
|
||||
|
||||
tHashTimerEntry *element = nullptr;
|
||||
HASH_FIND_PTR(_hashForTimers, &target, element);
|
||||
|
||||
if (! element)
|
||||
{
|
||||
element = (tHashTimerEntry *)calloc(sizeof(*element), 1);
|
||||
element->target = target;
|
||||
|
||||
HASH_ADD_PTR(_hashForTimers, target, element);
|
||||
|
||||
// Is this the 1st element ? Then set the pause level to all the selectors of this target
|
||||
element->paused = paused;
|
||||
}
|
||||
else
|
||||
{
|
||||
CCASSERT(element->paused == paused, "element's paused should be paused!");
|
||||
}
|
||||
|
||||
if (element->timers == nullptr)
|
||||
{
|
||||
element->timers = ccArrayNew(10);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < element->timers->num; ++i)
|
||||
{
|
||||
TimerTargetCallback *timer = dynamic_cast<TimerTargetCallback*>(element->timers->arr[i]);
|
||||
|
||||
if (timer && key == timer->getKey())
|
||||
{
|
||||
CCLOG("CCScheduler#scheduleSelector. Selector already scheduled. Updating interval from: %.4f to %.4f", timer->getInterval(), interval);
|
||||
timer->setInterval(interval);
|
||||
return;
|
||||
}
|
||||
}
|
||||
ccArrayEnsureExtraCapacity(element->timers, 1);
|
||||
}
|
||||
|
||||
TimerTargetCallback *timer = new (std::nothrow) TimerTargetCallback();
|
||||
timer->initWithCallback(this, callback, target, key, interval, repeat, delay);
|
||||
ccArrayAppendObject(element->timers, timer);
|
||||
timer->release();
|
||||
}
|
||||
|
||||
void Scheduler::unschedule(const std::string &key, void *target)
|
||||
{
|
||||
// explicit handle nil arguments when removing an object
|
||||
if (target == nullptr || key.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//CCASSERT(target);
|
||||
//CCASSERT(selector);
|
||||
|
||||
tHashTimerEntry *element = nullptr;
|
||||
HASH_FIND_PTR(_hashForTimers, &target, element);
|
||||
|
||||
if (element)
|
||||
{
|
||||
for (int i = 0; i < element->timers->num; ++i)
|
||||
{
|
||||
TimerTargetCallback *timer = dynamic_cast<TimerTargetCallback*>(element->timers->arr[i]);
|
||||
|
||||
if (timer && key == timer->getKey())
|
||||
{
|
||||
if (timer == element->currentTimer && (! element->currentTimerSalvaged))
|
||||
{
|
||||
element->currentTimer->retain();
|
||||
element->currentTimerSalvaged = true;
|
||||
}
|
||||
|
||||
ccArrayRemoveObjectAtIndex(element->timers, i, true);
|
||||
|
||||
// update timerIndex in case we are in tick:, looping over the actions
|
||||
if (element->timerIndex >= i)
|
||||
{
|
||||
element->timerIndex--;
|
||||
}
|
||||
|
||||
if (element->timers->num == 0)
|
||||
{
|
||||
if (_currentTarget == element)
|
||||
{
|
||||
_currentTargetSalvaged = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
removeHashElement(element);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Scheduler::isScheduled(const std::string& key, void *target)
|
||||
{
|
||||
CCASSERT(!key.empty(), "Argument key must not be empty");
|
||||
CCASSERT(target, "Argument target must be non-nullptr");
|
||||
|
||||
tHashTimerEntry *element = nullptr;
|
||||
HASH_FIND_PTR(_hashForTimers, &target, element);
|
||||
|
||||
if (!element)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (element->timers == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < element->timers->num; ++i)
|
||||
{
|
||||
TimerTargetCallback *timer = dynamic_cast<TimerTargetCallback*>(element->timers->arr[i]);
|
||||
|
||||
if (timer && key == timer->getKey())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return false; // should never get here
|
||||
}
|
||||
|
||||
void Scheduler::unscheduleAll()
|
||||
{
|
||||
for (tHashTimerEntry *element = _hashForTimers, *nextElement = nullptr; element != nullptr;)
|
||||
{
|
||||
// element may be removed in unscheduleAllSelectorsForTarget
|
||||
nextElement = (tHashTimerEntry *)element->hh.next;
|
||||
unscheduleAllForTarget(element->target);
|
||||
|
||||
element = nextElement;
|
||||
}
|
||||
}
|
||||
|
||||
void Scheduler::unscheduleAllForTarget(void *target)
|
||||
{
|
||||
// explicit nullptr handling
|
||||
if (target == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Custom Selectors
|
||||
tHashTimerEntry *element = nullptr;
|
||||
HASH_FIND_PTR(_hashForTimers, &target, element);
|
||||
|
||||
if (element)
|
||||
{
|
||||
if (ccArrayContainsObject(element->timers, element->currentTimer)
|
||||
&& (! element->currentTimerSalvaged))
|
||||
{
|
||||
element->currentTimer->retain();
|
||||
element->currentTimerSalvaged = true;
|
||||
}
|
||||
ccArrayRemoveAllObjects(element->timers);
|
||||
|
||||
if (_currentTarget == element)
|
||||
{
|
||||
_currentTargetSalvaged = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
removeHashElement(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Scheduler::resumeTarget(void *target)
|
||||
{
|
||||
CCASSERT(target != nullptr, "target can't be nullptr!");
|
||||
|
||||
// custom selectors
|
||||
tHashTimerEntry *element = nullptr;
|
||||
HASH_FIND_PTR(_hashForTimers, &target, element);
|
||||
if (element)
|
||||
{
|
||||
element->paused = false;
|
||||
}
|
||||
}
|
||||
|
||||
void Scheduler::pauseTarget(void *target)
|
||||
{
|
||||
CCASSERT(target != nullptr, "target can't be nullptr!");
|
||||
|
||||
// custom selectors
|
||||
tHashTimerEntry *element = nullptr;
|
||||
HASH_FIND_PTR(_hashForTimers, &target, element);
|
||||
if (element)
|
||||
{
|
||||
element->paused = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool Scheduler::isTargetPaused(void *target)
|
||||
{
|
||||
CCASSERT( target != nullptr, "target must be non nil" );
|
||||
|
||||
// Custom selectors
|
||||
tHashTimerEntry *element = nullptr;
|
||||
HASH_FIND_PTR(_hashForTimers, &target, element);
|
||||
if( element )
|
||||
{
|
||||
return element->paused;
|
||||
}
|
||||
|
||||
return false; // should never get here
|
||||
}
|
||||
|
||||
std::set<void*> Scheduler::pauseAllTargets()
|
||||
{
|
||||
std::set<void*> idsWithSelectors;
|
||||
|
||||
// Custom Selectors
|
||||
for(tHashTimerEntry *element = _hashForTimers; element != nullptr;
|
||||
element = (tHashTimerEntry*)element->hh.next)
|
||||
{
|
||||
element->paused = true;
|
||||
idsWithSelectors.insert(element->target);
|
||||
}
|
||||
|
||||
return idsWithSelectors;
|
||||
}
|
||||
|
||||
void Scheduler::resumeTargets(const std::set<void*>& targetsToResume)
|
||||
{
|
||||
for(const auto &obj : targetsToResume) {
|
||||
this->resumeTarget(obj);
|
||||
}
|
||||
}
|
||||
|
||||
void Scheduler::performFunctionInCocosThread(const std::function<void ()> &function)
|
||||
{
|
||||
_performMutex.lock();
|
||||
_functionsToPerform.push_back(function);
|
||||
_performMutex.unlock();
|
||||
}
|
||||
|
||||
void Scheduler::removeAllFunctionsToBePerformedInCocosThread()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(_performMutex);
|
||||
_functionsToPerform.clear();
|
||||
}
|
||||
|
||||
// main loop
|
||||
void Scheduler::update(float dt)
|
||||
{
|
||||
_updateHashLocked = true;
|
||||
|
||||
// Iterate over all the custom selectors
|
||||
for (tHashTimerEntry *elt = _hashForTimers; elt != nullptr; )
|
||||
{
|
||||
_currentTarget = elt;
|
||||
_currentTargetSalvaged = false;
|
||||
|
||||
if (! _currentTarget->paused)
|
||||
{
|
||||
// The 'timers' array may change while inside this loop
|
||||
for (elt->timerIndex = 0; elt->timerIndex < elt->timers->num; ++(elt->timerIndex))
|
||||
{
|
||||
elt->currentTimer = (Timer*)(elt->timers->arr[elt->timerIndex]);
|
||||
elt->currentTimerSalvaged = false;
|
||||
|
||||
elt->currentTimer->update(dt);
|
||||
|
||||
if (elt->currentTimerSalvaged)
|
||||
{
|
||||
// The currentTimer told the remove itself. To prevent the timer from
|
||||
// accidentally deallocating itself before finishing its step, we retained
|
||||
// it. Now that step is done, it's safe to release it.
|
||||
elt->currentTimer->release();
|
||||
}
|
||||
|
||||
elt->currentTimer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// elt, at this moment, is still valid
|
||||
// so it is safe to ask this here (issue #490)
|
||||
elt = (tHashTimerEntry *)elt->hh.next;
|
||||
|
||||
// only delete currentTarget if no actions were scheduled during the cycle (issue #481)
|
||||
if (_currentTargetSalvaged && _currentTarget->timers->num == 0)
|
||||
{
|
||||
removeHashElement(_currentTarget);
|
||||
}
|
||||
}
|
||||
|
||||
_updateHashLocked = false;
|
||||
_currentTarget = nullptr;
|
||||
|
||||
//
|
||||
// Functions allocated from another thread
|
||||
//
|
||||
|
||||
// Testing size is faster than locking / unlocking.
|
||||
// And almost never there will be functions scheduled to be called.
|
||||
if( !_functionsToPerform.empty() ) {
|
||||
_performMutex.lock();
|
||||
// fixed #4123: Save the callback functions, they must be invoked after '_performMutex.unlock()', otherwise if new functions are added in callback, it will cause thread deadlock.
|
||||
auto temp = _functionsToPerform;
|
||||
_functionsToPerform.clear();
|
||||
_performMutex.unlock();
|
||||
for( const auto &function : temp ) {
|
||||
function();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
NS_CC_END
|
||||
312
cocos2d-x/cocos/base/CCScheduler.h
Normal file
312
cocos2d-x/cocos/base/CCScheduler.h
Normal file
@@ -0,0 +1,312 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2008-2010 Ricardo Quesada
|
||||
Copyright (c) 2010-2012 cocos2d-x.org
|
||||
Copyright (c) 2011 Zynga Inc.
|
||||
Copyright (c) 2013-2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
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.
|
||||
****************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
|
||||
#include "base/CCRef.h"
|
||||
#include "base/CCVector.h"
|
||||
#include "base/uthash.h"
|
||||
|
||||
NS_CC_BEGIN
|
||||
|
||||
class Scheduler;
|
||||
|
||||
typedef std::function<void(float)> ccSchedulerFunc;
|
||||
|
||||
/**
|
||||
* @cond
|
||||
*/
|
||||
class CC_DLL Timer : public Ref
|
||||
{
|
||||
public:
|
||||
/** get interval in seconds */
|
||||
inline float getInterval() const { return _interval; };
|
||||
/** set interval in seconds */
|
||||
inline void setInterval(float interval) { _interval = interval; };
|
||||
|
||||
void setupTimerWithInterval(float seconds, unsigned int repeat, float delay);
|
||||
|
||||
virtual void trigger(float dt) = 0;
|
||||
virtual void cancel() = 0;
|
||||
|
||||
/** triggers the timer */
|
||||
void update(float dt);
|
||||
|
||||
protected:
|
||||
Timer();
|
||||
|
||||
protected:
|
||||
|
||||
Scheduler* _scheduler = nullptr;
|
||||
float _elapsed = 0.f;
|
||||
bool _runForever = false;
|
||||
bool _useDelay = false;
|
||||
unsigned int _timesExecuted = 0;
|
||||
unsigned int _repeat = 0; //0 = once, 1 is 2 x executed
|
||||
float _delay = 0.f;
|
||||
float _interval = 0.f;
|
||||
};
|
||||
|
||||
class CC_DLL TimerTargetCallback final : public Timer
|
||||
{
|
||||
public:
|
||||
TimerTargetCallback();
|
||||
|
||||
// Initializes a timer with a target, a lambda and an interval in seconds, repeat in number of times to repeat, delay in seconds.
|
||||
bool initWithCallback(Scheduler* scheduler, const ccSchedulerFunc& callback, void *target, const std::string& key, float seconds, unsigned int repeat, float delay);
|
||||
|
||||
inline const ccSchedulerFunc& getCallback() const { return _callback; };
|
||||
inline const std::string& getKey() const { return _key; };
|
||||
|
||||
virtual void trigger(float dt) override;
|
||||
virtual void cancel() override;
|
||||
|
||||
protected:
|
||||
void* _target = nullptr;
|
||||
ccSchedulerFunc _callback = nullptr;
|
||||
std::string _key;
|
||||
};
|
||||
|
||||
/**
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
/**
|
||||
* @addtogroup base
|
||||
* @{
|
||||
*/
|
||||
|
||||
struct _listEntry;
|
||||
struct _hashSelectorEntry;
|
||||
struct _hashUpdateEntry;
|
||||
|
||||
/** @brief Scheduler is responsible for triggering the scheduled callbacks.
|
||||
You should not use system timer for your game logic. Instead, use this class.
|
||||
|
||||
There are 2 different types of callbacks (selectors):
|
||||
|
||||
- update selector: the 'update' selector will be called every frame. You can customize the priority.
|
||||
- custom selector: A custom selector will be called every frame, or with a custom interval of time
|
||||
|
||||
The 'custom selectors' should be avoided when possible. It is faster, and consumes less memory to use the 'update selector'.
|
||||
|
||||
*/
|
||||
class CC_DLL Scheduler final
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @js ctor
|
||||
*/
|
||||
Scheduler();
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*
|
||||
* @js NA
|
||||
* @lua NA
|
||||
*/
|
||||
~Scheduler();
|
||||
|
||||
|
||||
/** 'update' the scheduler.
|
||||
* You should NEVER call this method, unless you know what you are doing.
|
||||
* @lua NA
|
||||
*/
|
||||
void update(float dt);
|
||||
|
||||
/////////////////////////////////////
|
||||
|
||||
// schedule
|
||||
|
||||
/** The scheduled method will be called every 'interval' seconds.
|
||||
If paused is true, then it won't be called until it is resumed.
|
||||
If 'interval' is 0, it will be called every frame, but if so, it's recommended to use 'scheduleUpdate' instead.
|
||||
If the 'callback' is already scheduled, then only the interval parameter will be updated without re-scheduling it again.
|
||||
repeat let the action be repeated repeat + 1 times, use CC_REPEAT_FOREVER to let the action run continuously
|
||||
delay is the amount of time the action will wait before it'll start.
|
||||
@param callback The callback function.
|
||||
@param target The target of the callback function.
|
||||
@param interval The interval to schedule the callback. If the value is 0, then the callback will be scheduled every frame.
|
||||
@param repeat repeat+1 times to schedule the callback.
|
||||
@param delay Schedule call back after `delay` seconds. If the value is not 0, the first schedule will happen after `delay` seconds.
|
||||
But it will only affect first schedule. After first schedule, the delay time is determined by `interval`.
|
||||
@param paused Whether or not to pause the schedule.
|
||||
@param key The key to identify the callback function, because there is not way to identify a std::function<>.
|
||||
@since v3.0
|
||||
*/
|
||||
void schedule(const ccSchedulerFunc& callback, void *target, float interval, unsigned int repeat, float delay, bool paused, const std::string& key);
|
||||
|
||||
/** The scheduled method will be called every 'interval' seconds for ever.
|
||||
@param callback The callback function.
|
||||
@param target The target of the callback function.
|
||||
@param interval The interval to schedule the callback. If the value is 0, then the callback will be scheduled every frame.
|
||||
@param paused Whether or not to pause the schedule.
|
||||
@param key The key to identify the callback function, because there is not way to identify a std::function<>.
|
||||
@since v3.0
|
||||
*/
|
||||
void schedule(const ccSchedulerFunc& callback, void *target, float interval, bool paused, const std::string& key);
|
||||
|
||||
/////////////////////////////////////
|
||||
|
||||
// unschedule
|
||||
|
||||
/** Unschedules a callback for a key and a given target.
|
||||
If you want to unschedule the 'callbackPerFrame', use unscheduleUpdate.
|
||||
@param key The key to identify the callback function, because there is not way to identify a std::function<>.
|
||||
@param target The target to be unscheduled.
|
||||
@since v3.0
|
||||
*/
|
||||
void unschedule(const std::string& key, void *target);
|
||||
|
||||
/** Unschedules all selectors for a given target.
|
||||
This also includes the "update" selector.
|
||||
@param target The target to be unscheduled.
|
||||
@since v0.99.3
|
||||
@lua NA
|
||||
*/
|
||||
void unscheduleAllForTarget(void *target);
|
||||
|
||||
/** Unschedules all selectors from all targets.
|
||||
You should NEVER call this method, unless you know what you are doing.
|
||||
@since v0.99.3
|
||||
*/
|
||||
void unscheduleAll();
|
||||
|
||||
/** Unschedules all selectors from all targets with a minimum priority.
|
||||
You should only call this with `PRIORITY_NON_SYSTEM_MIN` or higher.
|
||||
@param minPriority The minimum priority of selector to be unscheduled. Which means, all selectors which
|
||||
priority is higher than minPriority will be unscheduled.
|
||||
@since v2.0.0
|
||||
*/
|
||||
void unscheduleAllWithMinPriority(int minPriority);
|
||||
|
||||
/////////////////////////////////////
|
||||
|
||||
// isScheduled
|
||||
|
||||
/** Checks whether a callback associated with 'key' and 'target' is scheduled.
|
||||
@param key The key to identify the callback function, because there is not way to identify a std::function<>.
|
||||
@param target The target of the callback.
|
||||
@return True if the specified callback is invoked, false if not.
|
||||
@since v3.0.0
|
||||
*/
|
||||
bool isScheduled(const std::string& key, void *target);
|
||||
|
||||
/////////////////////////////////////
|
||||
|
||||
/** Pauses the target.
|
||||
All scheduled selectors/update for a given target won't be 'ticked' until the target is resumed.
|
||||
If the target is not present, nothing happens.
|
||||
@param target The target to be paused.
|
||||
@since v0.99.3
|
||||
*/
|
||||
void pauseTarget(void *target);
|
||||
|
||||
/** Resumes the target.
|
||||
The 'target' will be unpaused, so all schedule selectors/update will be 'ticked' again.
|
||||
If the target is not present, nothing happens.
|
||||
@param target The target to be resumed.
|
||||
@since v0.99.3
|
||||
*/
|
||||
void resumeTarget(void *target);
|
||||
|
||||
/** Returns whether or not the target is paused.
|
||||
* @param target The target to be checked.
|
||||
* @return True if the target is paused, false if not.
|
||||
* @since v1.0.0
|
||||
* @lua NA
|
||||
*/
|
||||
bool isTargetPaused(void *target);
|
||||
|
||||
/** Pause all selectors from all targets.
|
||||
You should NEVER call this method, unless you know what you are doing.
|
||||
@since v2.0.0
|
||||
*/
|
||||
std::set<void*> pauseAllTargets();
|
||||
|
||||
/** Pause all selectors from all targets with a minimum priority.
|
||||
You should only call this with PRIORITY_NON_SYSTEM_MIN or higher.
|
||||
@param minPriority The minimum priority of selector to be paused. Which means, all selectors which
|
||||
priority is higher than minPriority will be paused.
|
||||
@since v2.0.0
|
||||
*/
|
||||
std::set<void*> pauseAllTargetsWithMinPriority(int minPriority);
|
||||
|
||||
/** Resume selectors on a set of targets.
|
||||
This can be useful for undoing a call to pauseAllSelectors.
|
||||
@param targetsToResume The set of targets to be resumed.
|
||||
@since v2.0.0
|
||||
*/
|
||||
void resumeTargets(const std::set<void*>& targetsToResume);
|
||||
|
||||
/** Calls a function on the cocos2d thread. Useful when you need to call a cocos2d function from another thread.
|
||||
This function is thread safe.
|
||||
@param function The function to be run in cocos2d thread.
|
||||
@since v3.0
|
||||
@js NA
|
||||
*/
|
||||
void performFunctionInCocosThread( const std::function<void()> &function);
|
||||
|
||||
/**
|
||||
* Remove all pending functions queued to be performed with Scheduler::performFunctionInCocosThread
|
||||
* Functions unscheduled in this manner will not be executed
|
||||
* This function is thread safe
|
||||
* @since v3.14
|
||||
* @js NA
|
||||
*/
|
||||
void removeAllFunctionsToBePerformedInCocosThread();
|
||||
|
||||
bool isCurrentTargetSalvaged () const { return _currentTargetSalvaged; };
|
||||
|
||||
private:
|
||||
void removeHashElement(struct _hashSelectorEntry *element);
|
||||
void removeUpdateFromHash(struct _listEntry *entry);
|
||||
|
||||
// update specific
|
||||
|
||||
// Used for "selectors with interval"
|
||||
struct _hashSelectorEntry *_hashForTimers = nullptr;
|
||||
struct _hashSelectorEntry *_currentTarget = nullptr;
|
||||
bool _currentTargetSalvaged = false;
|
||||
// If true unschedule will not remove anything from a hash. Elements will only be marked for deletion.
|
||||
bool _updateHashLocked = false;
|
||||
|
||||
// Used for "perform Function"
|
||||
std::vector<std::function<void()>> _functionsToPerform;
|
||||
std::mutex _performMutex;
|
||||
};
|
||||
|
||||
// end of base group
|
||||
/** @} */
|
||||
|
||||
NS_CC_END
|
||||
442
cocos2d-x/cocos/base/CCThreadPool.cpp
Normal file
442
cocos2d-x/cocos/base/CCThreadPool.cpp
Normal file
@@ -0,0 +1,442 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2016-2017 Chukong Technologies Inc.
|
||||
|
||||
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.
|
||||
|
||||
Inspired by https://github.com/vit-vit/CTPL
|
||||
|
||||
****************************************************************************/
|
||||
|
||||
#include "base/CCThreadPool.h"
|
||||
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include <android/log.h>
|
||||
#define LOG_TAG "ThreadPool"
|
||||
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,__VA_ARGS__)
|
||||
#else
|
||||
#define LOGD(...) printf(__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
namespace cocos2d {
|
||||
|
||||
#define DEFAULT_THREAD_POOL_MIN_NUM (4)
|
||||
#define DEFAULT_THREAD_POOL_MAX_NUM (20)
|
||||
|
||||
#define DEFAULT_SHRINK_INTERVAL (5.0f)
|
||||
#define DEFAULT_SHRINK_STEP (2)
|
||||
#define DEFAULT_STRETCH_STEP (2)
|
||||
|
||||
static ThreadPool *__defaultThreadPool = nullptr;
|
||||
|
||||
ThreadPool *ThreadPool::getDefaultThreadPool()
|
||||
{
|
||||
if (__defaultThreadPool == nullptr)
|
||||
{
|
||||
__defaultThreadPool = newCachedThreadPool(DEFAULT_THREAD_POOL_MIN_NUM,
|
||||
DEFAULT_THREAD_POOL_MAX_NUM,
|
||||
DEFAULT_SHRINK_INTERVAL, DEFAULT_SHRINK_STEP,
|
||||
DEFAULT_STRETCH_STEP);
|
||||
}
|
||||
|
||||
return __defaultThreadPool;
|
||||
}
|
||||
|
||||
void ThreadPool::destroyDefaultThreadPool()
|
||||
{
|
||||
delete __defaultThreadPool;
|
||||
__defaultThreadPool = nullptr;
|
||||
}
|
||||
|
||||
ThreadPool *ThreadPool::newCachedThreadPool(int minThreadNum, int maxThreadNum, int shrinkInterval,
|
||||
int shrinkStep, int stretchStep)
|
||||
{
|
||||
ThreadPool *pool = new(std::nothrow) ThreadPool(minThreadNum, maxThreadNum);
|
||||
if (pool != nullptr)
|
||||
{
|
||||
pool->setFixedSize(false);
|
||||
pool->setShrinkInterval(shrinkInterval);
|
||||
pool->setShrinkStep(shrinkStep);
|
||||
pool->setStretchStep(stretchStep);
|
||||
}
|
||||
return pool;
|
||||
}
|
||||
|
||||
ThreadPool *ThreadPool::newFixedThreadPool(int threadNum)
|
||||
{
|
||||
ThreadPool *pool = new(std::nothrow) ThreadPool(threadNum, threadNum);
|
||||
if (pool != nullptr)
|
||||
{
|
||||
pool->setFixedSize(true);
|
||||
}
|
||||
return pool;
|
||||
}
|
||||
|
||||
ThreadPool *ThreadPool::newSingleThreadPool()
|
||||
{
|
||||
ThreadPool *pool = new(std::nothrow) ThreadPool(1, 1);
|
||||
if (pool != nullptr)
|
||||
{
|
||||
pool->setFixedSize(true);
|
||||
}
|
||||
return pool;
|
||||
}
|
||||
|
||||
ThreadPool::ThreadPool(int minNum, int maxNum)
|
||||
: _isDone(false), _isStop(false), _idleThreadNum(0), _minThreadNum(minNum),
|
||||
_maxThreadNum(maxNum), _initedThreadNum(0), _shrinkInterval(DEFAULT_SHRINK_INTERVAL),
|
||||
_shrinkStep(DEFAULT_SHRINK_STEP), _stretchStep(DEFAULT_STRETCH_STEP),
|
||||
_isFixedSize(false)
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
// the destructor waits for all the functions in the queue to be finished
|
||||
ThreadPool::~ThreadPool()
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
// number of idle threads
|
||||
int ThreadPool::getIdleThreadNum() const
|
||||
{
|
||||
ThreadPool* thiz = const_cast<ThreadPool*>(this);
|
||||
std::lock_guard<std::mutex> lk(thiz->_idleThreadNumMutex);
|
||||
return _idleThreadNum;
|
||||
}
|
||||
|
||||
void ThreadPool::init()
|
||||
{
|
||||
gettimeofday(&_lastShrinkTime, nullptr);
|
||||
|
||||
_maxThreadNum = std::max(_minThreadNum, _maxThreadNum);
|
||||
|
||||
_threads.resize(_maxThreadNum);
|
||||
_abortFlags.resize(_maxThreadNum);
|
||||
_idleFlags.resize(_maxThreadNum);
|
||||
_initedFlags.resize(_maxThreadNum);
|
||||
|
||||
for (int i = 0; i < _maxThreadNum; ++i)
|
||||
{
|
||||
_idleFlags[i] = std::make_shared<std::atomic<bool>>(false);
|
||||
if (i < _minThreadNum)
|
||||
{
|
||||
_abortFlags[i] = std::make_shared<std::atomic<bool>>(false);
|
||||
setThread(i);
|
||||
_initedFlags[i] = std::make_shared<std::atomic<bool>>(true);
|
||||
++_initedThreadNum;
|
||||
}
|
||||
else
|
||||
{
|
||||
_abortFlags[i] = std::make_shared<std::atomic<bool>>(true);
|
||||
_initedFlags[i] = std::make_shared<std::atomic<bool>>(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ThreadPool::tryShrinkPool()
|
||||
{
|
||||
LOGD("shrink pool, _idleThreadNum = %d \n", getIdleThreadNum());
|
||||
|
||||
struct timeval before;
|
||||
gettimeofday(&before, nullptr);
|
||||
|
||||
std::vector<int> threadIDsToJoin;
|
||||
int maxThreadNumToJoin = std::min(_initedThreadNum - _minThreadNum, _shrinkStep);
|
||||
|
||||
for (int i = 0; i < _maxThreadNum; ++i)
|
||||
{
|
||||
if ((int)threadIDsToJoin.size() >= maxThreadNumToJoin)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (*_idleFlags[i])
|
||||
{
|
||||
*_abortFlags[i] = true;
|
||||
threadIDsToJoin.push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// stop the detached threads that were waiting
|
||||
std::unique_lock<std::mutex> lock(_mutex);
|
||||
_cv.notify_all();
|
||||
}
|
||||
|
||||
for (const auto& threadID : threadIDsToJoin)
|
||||
{ // wait for the computing threads to finish
|
||||
if (_threads[threadID]->joinable())
|
||||
{
|
||||
_threads[threadID]->join();
|
||||
}
|
||||
|
||||
_threads[threadID].reset();
|
||||
*_initedFlags[threadID] = false;
|
||||
--_initedThreadNum;
|
||||
}
|
||||
|
||||
struct timeval after;
|
||||
gettimeofday(&after, nullptr);
|
||||
|
||||
float seconds = (after.tv_sec - before.tv_sec) + (after.tv_usec - before.tv_usec) / 1000000.0f;
|
||||
|
||||
LOGD("shrink %d threads, waste: %f seconds\n", (int) threadIDsToJoin.size(), seconds);
|
||||
|
||||
if (_initedThreadNum <= _minThreadNum)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ThreadPool::stretchPool(int count)
|
||||
{
|
||||
struct timeval before;
|
||||
gettimeofday(&before, nullptr);
|
||||
|
||||
int oldThreadCount = _initedThreadNum;
|
||||
int newThreadCount = 0;
|
||||
|
||||
for (int i = 0; i < _maxThreadNum; ++i)
|
||||
{
|
||||
if (!*_initedFlags[i])
|
||||
{
|
||||
*_abortFlags[i] = false;
|
||||
setThread(i);
|
||||
*_initedFlags[i] = true;
|
||||
++_initedThreadNum;
|
||||
|
||||
if (++newThreadCount >= count)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (newThreadCount > 0)
|
||||
{
|
||||
struct timeval after;
|
||||
gettimeofday(&after, nullptr);
|
||||
|
||||
float seconds =
|
||||
(after.tv_sec - before.tv_sec) + (after.tv_usec - before.tv_usec) / 1000000.0f;
|
||||
|
||||
LOGD("stretch pool from %d to %d, waste %f seconds\n", oldThreadCount, _initedThreadNum,
|
||||
seconds);
|
||||
}
|
||||
}
|
||||
|
||||
void ThreadPool::pushTask(const std::function<void(int)>& runnable,
|
||||
TaskType type/* = DEFAULT*/)
|
||||
{
|
||||
if (!_isFixedSize)
|
||||
{
|
||||
_idleThreadNumMutex.lock();
|
||||
int idleNum = _idleThreadNum;
|
||||
_idleThreadNumMutex.unlock();
|
||||
|
||||
if (idleNum > _minThreadNum)
|
||||
{
|
||||
if (_taskQueue.empty())
|
||||
{
|
||||
struct timeval now;
|
||||
gettimeofday(&now, nullptr);
|
||||
|
||||
float seconds = (now.tv_sec - _lastShrinkTime.tv_sec) +
|
||||
(now.tv_usec - _lastShrinkTime.tv_usec) / 1000000.0f;
|
||||
if (seconds > _shrinkInterval)
|
||||
{
|
||||
tryShrinkPool();
|
||||
_lastShrinkTime = now;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (idleNum == 0)
|
||||
{
|
||||
stretchPool(_stretchStep);
|
||||
}
|
||||
}
|
||||
|
||||
auto callback = new(std::nothrow) std::function<void(int)>([runnable](int tid) {
|
||||
runnable(tid);
|
||||
});
|
||||
|
||||
Task task;
|
||||
task.type = type;
|
||||
task.callback = callback;
|
||||
_taskQueue.push(std::move(task));
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(_mutex);
|
||||
_cv.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
void ThreadPool::stopAllTasks()
|
||||
{
|
||||
Task task;
|
||||
while (_taskQueue.pop(task))
|
||||
{
|
||||
delete task.callback; // empty the queue
|
||||
}
|
||||
}
|
||||
|
||||
void ThreadPool::stopTasksByType(TaskType type)
|
||||
{
|
||||
Task task;
|
||||
|
||||
std::vector<Task> notStopTasks;
|
||||
notStopTasks.reserve(_taskQueue.size());
|
||||
|
||||
while (_taskQueue.pop(task))
|
||||
{
|
||||
if (task.type == type)
|
||||
{// Delete the task from queue
|
||||
delete task.callback;
|
||||
}
|
||||
else
|
||||
{// If task type isn't match, push it into a vector, then insert to task queue again
|
||||
notStopTasks.push_back(task);
|
||||
}
|
||||
}
|
||||
|
||||
if (!notStopTasks.empty())
|
||||
{
|
||||
for (const auto& t : notStopTasks)
|
||||
{
|
||||
_taskQueue.push(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ThreadPool::joinThread(int tid)
|
||||
{
|
||||
if (tid < 0 || tid >= (int)_threads.size())
|
||||
{
|
||||
LOGD("Invalid thread id %d\n", tid);
|
||||
return;
|
||||
}
|
||||
|
||||
// wait for the computing threads to finish
|
||||
if (*_initedFlags[tid] && _threads[tid]->joinable())
|
||||
{
|
||||
_threads[tid]->join();
|
||||
*_initedFlags[tid] = false;
|
||||
--_initedThreadNum;
|
||||
}
|
||||
}
|
||||
|
||||
int ThreadPool::getTaskNum() const
|
||||
{
|
||||
return (int) _taskQueue.size();
|
||||
}
|
||||
|
||||
void ThreadPool::setFixedSize(bool isFixedSize)
|
||||
{
|
||||
_isFixedSize = isFixedSize;
|
||||
}
|
||||
|
||||
void ThreadPool::setShrinkInterval(int seconds)
|
||||
{
|
||||
if (seconds >= 0)
|
||||
_shrinkInterval = seconds;
|
||||
}
|
||||
|
||||
void ThreadPool::setShrinkStep(int step)
|
||||
{
|
||||
if (step > 0)
|
||||
_shrinkStep = step;
|
||||
}
|
||||
|
||||
void ThreadPool::setStretchStep(int step)
|
||||
{
|
||||
if (step > 0)
|
||||
_stretchStep = step;
|
||||
}
|
||||
|
||||
void ThreadPool::stop()
|
||||
{
|
||||
if (_isDone || _isStop)
|
||||
return;
|
||||
_isDone = true; // give the waiting threads a command to finish
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(_mutex);
|
||||
_cv.notify_all(); // stop all waiting threads
|
||||
}
|
||||
|
||||
for (int i = 0, n = static_cast<int>(_threads.size()); i < n; ++i)
|
||||
{
|
||||
joinThread(i);
|
||||
_threads[i].reset();
|
||||
}
|
||||
// if there were no threads in the pool but some functors in the queue, the functors are not deleted by the threads
|
||||
// therefore delete them here
|
||||
stopAllTasks();
|
||||
_threads.clear();
|
||||
_abortFlags.clear();
|
||||
}
|
||||
|
||||
void ThreadPool::setThread(int tid)
|
||||
{
|
||||
std::shared_ptr<std::atomic<bool>> abort_ptr(
|
||||
_abortFlags[tid]); // a copy of the shared ptr to the flag
|
||||
auto f = [this, tid, abort_ptr/* a copy of the shared ptr to the abort */]() {
|
||||
std::atomic<bool>& abort = *abort_ptr;
|
||||
Task task;
|
||||
bool isPop = _taskQueue.pop(task);
|
||||
while (true)
|
||||
{
|
||||
while (isPop)
|
||||
{ // if there is anything in the queue
|
||||
std::unique_ptr<std::function<void(int)>> func(
|
||||
task.callback); // at return, delete the function even if an exception occurred
|
||||
(*task.callback)(tid);
|
||||
if (abort)
|
||||
return; // the thread is wanted to stop, return even if the queue is not empty yet
|
||||
else
|
||||
isPop = _taskQueue.pop(task);
|
||||
}
|
||||
// the queue is empty here, wait for the next command
|
||||
std::unique_lock<std::mutex> lock(_mutex);
|
||||
_idleThreadNumMutex.lock();
|
||||
++_idleThreadNum;
|
||||
_idleThreadNumMutex.unlock();
|
||||
|
||||
*_idleFlags[tid] = true;
|
||||
_cv.wait(lock, [this, &task, &isPop, &abort]() {
|
||||
isPop = _taskQueue.pop(task);
|
||||
return isPop || _isDone || abort;
|
||||
});
|
||||
*_idleFlags[tid] = false;
|
||||
_idleThreadNumMutex.lock();
|
||||
--_idleThreadNum;
|
||||
_idleThreadNumMutex.unlock();
|
||||
|
||||
if (!isPop)
|
||||
return; // if the queue is empty and isDone == true or *flag then return
|
||||
}
|
||||
};
|
||||
_threads[tid].reset(
|
||||
new(std::nothrow) std::thread(f)); // compiler may not support std::make_unique()
|
||||
}
|
||||
|
||||
} // namespace cocos2d {
|
||||
238
cocos2d-x/cocos/base/CCThreadPool.h
Normal file
238
cocos2d-x/cocos/base/CCThreadPool.h
Normal file
@@ -0,0 +1,238 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2016-2017 Chukong Technologies Inc.
|
||||
|
||||
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.
|
||||
|
||||
Inspired by https://github.com/vit-vit/CTPL
|
||||
|
||||
****************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include "base/ccUtils.h"
|
||||
#include "platform/CCStdC.h"
|
||||
#include "platform/CCPlatformDefine.h"
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <queue>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <vector>
|
||||
#include <atomic>
|
||||
|
||||
namespace cocos2d {
|
||||
|
||||
/**
|
||||
* @addtogroup base
|
||||
* @{
|
||||
*/
|
||||
|
||||
class CC_DLL ThreadPool
|
||||
{
|
||||
public:
|
||||
|
||||
enum class TaskType
|
||||
{
|
||||
DEFAULT = 0,
|
||||
NETWORK,
|
||||
IO,
|
||||
AUDIO,
|
||||
USER = 1000
|
||||
};
|
||||
|
||||
/*
|
||||
* Gets the default thread pool which is a cached thread pool with default parameters.
|
||||
*/
|
||||
static ThreadPool *getDefaultThreadPool();
|
||||
|
||||
/*
|
||||
* Destroys the default thread pool
|
||||
*/
|
||||
static void destroyDefaultThreadPool();
|
||||
|
||||
/*
|
||||
* Creates a cached thread pool
|
||||
* @note The return value has to be delete while it doesn't needed
|
||||
*/
|
||||
static ThreadPool *newCachedThreadPool(int minThreadNum, int maxThreadNum, int shrinkInterval,
|
||||
int shrinkStep, int stretchStep);
|
||||
|
||||
/*
|
||||
* Creates a thread pool with fixed thread count
|
||||
* @note The return value has to be delete while it doesn't needed
|
||||
*/
|
||||
static ThreadPool *newFixedThreadPool(int threadNum);
|
||||
|
||||
/*
|
||||
* Creates a thread pool with only one thread in the pool, it could be used to execute multiply tasks serially in just one thread.
|
||||
* @note The return value has to be delete while it doesn't needed
|
||||
*/
|
||||
static ThreadPool *newSingleThreadPool();
|
||||
|
||||
// the destructor waits for all the functions in the queue to be finished
|
||||
~ThreadPool();
|
||||
|
||||
/* Pushs a task to thread pool
|
||||
* @param runnable The callback of the task executed in sub thread
|
||||
* @param type The task type, it's TASK_TYPE_DEFAULT if this argument isn't assigned
|
||||
* @note This function has to be invoked in cocos thread
|
||||
*/
|
||||
void pushTask(const std::function<void(int /*threadId*/)>& runnable, TaskType type = TaskType::DEFAULT);
|
||||
|
||||
// Stops all tasks, it will remove all tasks in queue
|
||||
void stopAllTasks();
|
||||
|
||||
// Stops some tasks by type
|
||||
void stopTasksByType(TaskType type);
|
||||
|
||||
// Gets the minimum thread numbers
|
||||
inline int getMinThreadNum() const
|
||||
{ return _minThreadNum; };
|
||||
|
||||
// Gets the maximum thread numbers
|
||||
inline int getMaxThreadNum() const
|
||||
{ return _maxThreadNum; };
|
||||
|
||||
// Gets the number of idle threads
|
||||
int getIdleThreadNum() const;
|
||||
|
||||
// Gets the number of initialized threads
|
||||
inline int getInitedThreadNum() const
|
||||
{ return _initedThreadNum; };
|
||||
|
||||
// Gets the task number
|
||||
int getTaskNum() const;
|
||||
|
||||
/*
|
||||
* Trys to shrink pool
|
||||
* @note This method is only available for cached thread pool
|
||||
*/
|
||||
bool tryShrinkPool();
|
||||
|
||||
private:
|
||||
ThreadPool(int minNum, int maxNum);
|
||||
|
||||
ThreadPool(const ThreadPool&);
|
||||
|
||||
ThreadPool(ThreadPool&&);
|
||||
|
||||
ThreadPool& operator=(const ThreadPool&);
|
||||
|
||||
ThreadPool& operator=(ThreadPool&&);
|
||||
|
||||
void init();
|
||||
|
||||
void stop();
|
||||
|
||||
void setThread(int tid);
|
||||
|
||||
void joinThread(int tid);
|
||||
|
||||
void setFixedSize(bool isFixedSize);
|
||||
|
||||
void setShrinkInterval(int seconds);
|
||||
|
||||
void setShrinkStep(int step);
|
||||
|
||||
void setStretchStep(int step);
|
||||
|
||||
void stretchPool(int count);
|
||||
|
||||
std::vector<std::unique_ptr<std::thread>> _threads;
|
||||
std::vector<std::shared_ptr<std::atomic<bool>>> _abortFlags;
|
||||
std::vector<std::shared_ptr<std::atomic<bool>>> _idleFlags;
|
||||
std::vector<std::shared_ptr<std::atomic<bool>>> _initedFlags;
|
||||
|
||||
template<typename T>
|
||||
class ThreadSafeQueue
|
||||
{
|
||||
public:
|
||||
bool push(T const& value)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(this->mutex);
|
||||
this->q.push(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
// deletes the retrieved element, do not use for non integral types
|
||||
bool pop(T& v)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(this->mutex);
|
||||
if (this->q.empty())
|
||||
return false;
|
||||
v = this->q.front();
|
||||
this->q.pop();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool empty() const
|
||||
{
|
||||
auto thiz = const_cast<ThreadSafeQueue*>(this);
|
||||
std::unique_lock<std::mutex> lock(thiz->mutex);
|
||||
return this->q.empty();
|
||||
}
|
||||
|
||||
size_t size() const
|
||||
{
|
||||
auto thiz = const_cast<ThreadSafeQueue*>(this);
|
||||
std::unique_lock<std::mutex> lock(thiz->mutex);
|
||||
return this->q.size();
|
||||
}
|
||||
|
||||
private:
|
||||
std::queue<T> q;
|
||||
std::mutex mutex;
|
||||
};
|
||||
|
||||
struct Task
|
||||
{
|
||||
TaskType type;
|
||||
std::function<void(int)> *callback;
|
||||
};
|
||||
|
||||
ThreadSafeQueue<Task> _taskQueue;
|
||||
std::atomic<bool> _isDone;
|
||||
std::atomic<bool> _isStop;
|
||||
|
||||
//IDEA: std::atomic<int> isn't supported by ndk-r10e while compiling with `armeabi` arch.
|
||||
// So using a mutex here instead.
|
||||
int _idleThreadNum; // how many threads are waiting
|
||||
std::mutex _idleThreadNumMutex;
|
||||
|
||||
std::mutex _mutex;
|
||||
std::condition_variable _cv;
|
||||
|
||||
int _minThreadNum;
|
||||
int _maxThreadNum;
|
||||
int _initedThreadNum;
|
||||
|
||||
struct timeval _lastShrinkTime;
|
||||
float _shrinkInterval;
|
||||
int _shrinkStep;
|
||||
int _stretchStep;
|
||||
bool _isFixedSize;
|
||||
};
|
||||
|
||||
// end of base group
|
||||
/// @}
|
||||
|
||||
} // namespace cocos2d {
|
||||
940
cocos2d-x/cocos/base/CCValue.cpp
Normal file
940
cocos2d-x/cocos/base/CCValue.cpp
Normal file
@@ -0,0 +1,940 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2013-2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
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 "base/CCValue.h"
|
||||
#include "base/ccUtils.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <float.h>
|
||||
#include <stdlib.h>
|
||||
#include <memory.h>
|
||||
|
||||
|
||||
NS_CC_BEGIN
|
||||
|
||||
const ValueVector ValueVectorNull;
|
||||
const ValueMap ValueMapNull;
|
||||
const ValueMapIntKey ValueMapIntKeyNull;
|
||||
|
||||
const Value Value::Null;
|
||||
|
||||
Value::Value()
|
||||
: _type(Type::NONE)
|
||||
{
|
||||
memset(&_field, 0, sizeof(_field));
|
||||
}
|
||||
|
||||
Value::Value(unsigned char v)
|
||||
: _type(Type::BYTE)
|
||||
{
|
||||
_field.byteVal = v;
|
||||
}
|
||||
|
||||
Value::Value(int v)
|
||||
: _type(Type::INTEGER)
|
||||
{
|
||||
_field.intVal = v;
|
||||
}
|
||||
|
||||
Value::Value(unsigned int v)
|
||||
: _type(Type::UNSIGNED)
|
||||
{
|
||||
_field.unsignedVal = v;
|
||||
}
|
||||
|
||||
Value::Value(float v)
|
||||
: _type(Type::FLOAT)
|
||||
{
|
||||
_field.floatVal = v;
|
||||
}
|
||||
|
||||
Value::Value(double v)
|
||||
: _type(Type::DOUBLE)
|
||||
{
|
||||
_field.doubleVal = v;
|
||||
}
|
||||
|
||||
Value::Value(bool v)
|
||||
: _type(Type::BOOLEAN)
|
||||
{
|
||||
_field.boolVal = v;
|
||||
}
|
||||
|
||||
Value::Value(const char* v)
|
||||
: _type(Type::STRING)
|
||||
{
|
||||
_field.strVal = new (std::nothrow) std::string();
|
||||
if (v)
|
||||
{
|
||||
*_field.strVal = v;
|
||||
}
|
||||
}
|
||||
|
||||
Value::Value(const std::string& v)
|
||||
: _type(Type::STRING)
|
||||
{
|
||||
_field.strVal = new (std::nothrow) std::string();
|
||||
*_field.strVal = v;
|
||||
}
|
||||
|
||||
Value::Value(const ValueVector& v)
|
||||
: _type(Type::VECTOR)
|
||||
{
|
||||
_field.vectorVal = new (std::nothrow) ValueVector();
|
||||
*_field.vectorVal = v;
|
||||
}
|
||||
|
||||
Value::Value(ValueVector&& v)
|
||||
: _type(Type::VECTOR)
|
||||
{
|
||||
_field.vectorVal = new (std::nothrow) ValueVector();
|
||||
*_field.vectorVal = std::move(v);
|
||||
}
|
||||
|
||||
Value::Value(const ValueMap& v)
|
||||
: _type(Type::MAP)
|
||||
{
|
||||
_field.mapVal = new (std::nothrow) ValueMap();
|
||||
*_field.mapVal = v;
|
||||
}
|
||||
|
||||
Value::Value(ValueMap&& v)
|
||||
: _type(Type::MAP)
|
||||
{
|
||||
_field.mapVal = new (std::nothrow) ValueMap();
|
||||
*_field.mapVal = std::move(v);
|
||||
}
|
||||
|
||||
Value::Value(const ValueMapIntKey& v)
|
||||
: _type(Type::INT_KEY_MAP)
|
||||
{
|
||||
_field.intKeyMapVal = new (std::nothrow) ValueMapIntKey();
|
||||
*_field.intKeyMapVal = v;
|
||||
}
|
||||
|
||||
Value::Value(ValueMapIntKey&& v)
|
||||
: _type(Type::INT_KEY_MAP)
|
||||
{
|
||||
_field.intKeyMapVal = new (std::nothrow) ValueMapIntKey();
|
||||
*_field.intKeyMapVal = std::move(v);
|
||||
}
|
||||
|
||||
Value::Value(const Value& other)
|
||||
: _type(Type::NONE)
|
||||
{
|
||||
*this = other;
|
||||
}
|
||||
|
||||
Value::Value(Value&& other)
|
||||
: _type(Type::NONE)
|
||||
{
|
||||
*this = std::move(other);
|
||||
}
|
||||
|
||||
Value::~Value()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
Value& Value::operator= (const Value& other)
|
||||
{
|
||||
if (this != &other) {
|
||||
reset(other._type);
|
||||
|
||||
switch (other._type) {
|
||||
case Type::BYTE:
|
||||
_field.byteVal = other._field.byteVal;
|
||||
break;
|
||||
case Type::INTEGER:
|
||||
_field.intVal = other._field.intVal;
|
||||
break;
|
||||
case Type::UNSIGNED:
|
||||
_field.unsignedVal = other._field.unsignedVal;
|
||||
break;
|
||||
case Type::FLOAT:
|
||||
_field.floatVal = other._field.floatVal;
|
||||
break;
|
||||
case Type::DOUBLE:
|
||||
_field.doubleVal = other._field.doubleVal;
|
||||
break;
|
||||
case Type::BOOLEAN:
|
||||
_field.boolVal = other._field.boolVal;
|
||||
break;
|
||||
case Type::STRING:
|
||||
if (_field.strVal == nullptr)
|
||||
{
|
||||
_field.strVal = new std::string();
|
||||
}
|
||||
*_field.strVal = *other._field.strVal;
|
||||
break;
|
||||
case Type::VECTOR:
|
||||
if (_field.vectorVal == nullptr)
|
||||
{
|
||||
_field.vectorVal = new (std::nothrow) ValueVector();
|
||||
}
|
||||
*_field.vectorVal = *other._field.vectorVal;
|
||||
break;
|
||||
case Type::MAP:
|
||||
if (_field.mapVal == nullptr)
|
||||
{
|
||||
_field.mapVal = new (std::nothrow) ValueMap();
|
||||
}
|
||||
*_field.mapVal = *other._field.mapVal;
|
||||
break;
|
||||
case Type::INT_KEY_MAP:
|
||||
if (_field.intKeyMapVal == nullptr)
|
||||
{
|
||||
_field.intKeyMapVal = new (std::nothrow) ValueMapIntKey();
|
||||
}
|
||||
*_field.intKeyMapVal = *other._field.intKeyMapVal;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Value& Value::operator= (Value&& other)
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
clear();
|
||||
switch (other._type)
|
||||
{
|
||||
case Type::BYTE:
|
||||
_field.byteVal = other._field.byteVal;
|
||||
break;
|
||||
case Type::INTEGER:
|
||||
_field.intVal = other._field.intVal;
|
||||
break;
|
||||
case Type::UNSIGNED:
|
||||
_field.unsignedVal = other._field.unsignedVal;
|
||||
break;
|
||||
case Type::FLOAT:
|
||||
_field.floatVal = other._field.floatVal;
|
||||
break;
|
||||
case Type::DOUBLE:
|
||||
_field.doubleVal = other._field.doubleVal;
|
||||
break;
|
||||
case Type::BOOLEAN:
|
||||
_field.boolVal = other._field.boolVal;
|
||||
break;
|
||||
case Type::STRING:
|
||||
_field.strVal = other._field.strVal;
|
||||
break;
|
||||
case Type::VECTOR:
|
||||
_field.vectorVal = other._field.vectorVal;
|
||||
break;
|
||||
case Type::MAP:
|
||||
_field.mapVal = other._field.mapVal;
|
||||
break;
|
||||
case Type::INT_KEY_MAP:
|
||||
_field.intKeyMapVal = other._field.intKeyMapVal;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
_type = other._type;
|
||||
|
||||
memset(&other._field, 0, sizeof(other._field));
|
||||
other._type = Type::NONE;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Value& Value::operator= (unsigned char v)
|
||||
{
|
||||
reset(Type::BYTE);
|
||||
_field.byteVal = v;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Value& Value::operator= (int v)
|
||||
{
|
||||
reset(Type::INTEGER);
|
||||
_field.intVal = v;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Value& Value::operator= (unsigned int v)
|
||||
{
|
||||
reset(Type::UNSIGNED);
|
||||
_field.unsignedVal = v;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Value& Value::operator= (float v)
|
||||
{
|
||||
reset(Type::FLOAT);
|
||||
_field.floatVal = v;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Value& Value::operator= (double v)
|
||||
{
|
||||
reset(Type::DOUBLE);
|
||||
_field.doubleVal = v;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Value& Value::operator= (bool v)
|
||||
{
|
||||
reset(Type::BOOLEAN);
|
||||
_field.boolVal = v;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Value& Value::operator= (const char* v)
|
||||
{
|
||||
reset(Type::STRING);
|
||||
*_field.strVal = v ? v : "";
|
||||
return *this;
|
||||
}
|
||||
|
||||
Value& Value::operator= (const std::string& v)
|
||||
{
|
||||
reset(Type::STRING);
|
||||
*_field.strVal = v;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Value& Value::operator= (const ValueVector& v)
|
||||
{
|
||||
reset(Type::VECTOR);
|
||||
*_field.vectorVal = v;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Value& Value::operator= (ValueVector&& v)
|
||||
{
|
||||
reset(Type::VECTOR);
|
||||
*_field.vectorVal = std::move(v);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Value& Value::operator= (const ValueMap& v)
|
||||
{
|
||||
reset(Type::MAP);
|
||||
*_field.mapVal = v;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Value& Value::operator= (ValueMap&& v)
|
||||
{
|
||||
reset(Type::MAP);
|
||||
*_field.mapVal = std::move(v);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Value& Value::operator= (const ValueMapIntKey& v)
|
||||
{
|
||||
reset(Type::INT_KEY_MAP);
|
||||
*_field.intKeyMapVal = v;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Value& Value::operator= (ValueMapIntKey&& v)
|
||||
{
|
||||
reset(Type::INT_KEY_MAP);
|
||||
*_field.intKeyMapVal = std::move(v);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool Value::operator!= (const Value& v)
|
||||
{
|
||||
return !(*this == v);
|
||||
}
|
||||
bool Value::operator!= (const Value& v) const
|
||||
{
|
||||
return !(*this == v);
|
||||
}
|
||||
|
||||
bool Value::operator== (const Value& v)
|
||||
{
|
||||
const auto &t = *this;
|
||||
return t == v;
|
||||
}
|
||||
bool Value::operator== (const Value& v) const
|
||||
{
|
||||
if (this == &v) return true;
|
||||
if (v._type != this->_type) return false;
|
||||
if (this->isNull()) return true;
|
||||
switch (_type)
|
||||
{
|
||||
case Type::BYTE: return v._field.byteVal == this->_field.byteVal;
|
||||
case Type::INTEGER: return v._field.intVal == this->_field.intVal;
|
||||
case Type::UNSIGNED:return v._field.unsignedVal == this->_field.unsignedVal;
|
||||
case Type::BOOLEAN: return v._field.boolVal == this->_field.boolVal;
|
||||
case Type::STRING: return *v._field.strVal == *this->_field.strVal;
|
||||
case Type::FLOAT: return std::abs(v._field.floatVal - this->_field.floatVal) <= FLT_EPSILON;
|
||||
case Type::DOUBLE: return std::abs(v._field.doubleVal - this->_field.doubleVal) <= DBL_EPSILON;
|
||||
case Type::VECTOR:
|
||||
{
|
||||
const auto &v1 = *(this->_field.vectorVal);
|
||||
const auto &v2 = *(v._field.vectorVal);
|
||||
const auto size = v1.size();
|
||||
if (size == v2.size())
|
||||
{
|
||||
for (size_t i = 0; i < size; i++)
|
||||
{
|
||||
if (v1[i] != v2[i]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
case Type::MAP:
|
||||
{
|
||||
const auto &map1 = *(this->_field.mapVal);
|
||||
const auto &map2 = *(v._field.mapVal);
|
||||
for (const auto &kvp : map1)
|
||||
{
|
||||
auto it = map2.find(kvp.first);
|
||||
if (it == map2.end() || it->second != kvp.second)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case Type::INT_KEY_MAP:
|
||||
{
|
||||
const auto &map1 = *(this->_field.intKeyMapVal);
|
||||
const auto &map2 = *(v._field.intKeyMapVal);
|
||||
for (const auto &kvp : map1)
|
||||
{
|
||||
auto it = map2.find(kvp.first);
|
||||
if (it == map2.end() || it->second != kvp.second)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
};
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Convert value to a specified type
|
||||
unsigned char Value::asByte() const
|
||||
{
|
||||
CCASSERT(_type != Type::VECTOR && _type != Type::MAP && _type != Type::INT_KEY_MAP, "Only base type (bool, string, float, double, int) could be converted");
|
||||
|
||||
if (_type == Type::BYTE)
|
||||
{
|
||||
return _field.byteVal;
|
||||
}
|
||||
|
||||
if (_type == Type::INTEGER)
|
||||
{
|
||||
return static_cast<unsigned char>(_field.intVal);
|
||||
}
|
||||
|
||||
if (_type == Type::UNSIGNED)
|
||||
{
|
||||
return static_cast<unsigned char>(_field.unsignedVal);
|
||||
}
|
||||
|
||||
if (_type == Type::STRING)
|
||||
{
|
||||
return static_cast<unsigned char>(atoi(_field.strVal->c_str()));
|
||||
}
|
||||
|
||||
if (_type == Type::FLOAT)
|
||||
{
|
||||
return static_cast<unsigned char>(_field.floatVal);
|
||||
}
|
||||
|
||||
if (_type == Type::DOUBLE)
|
||||
{
|
||||
return static_cast<unsigned char>(_field.doubleVal);
|
||||
}
|
||||
|
||||
if (_type == Type::BOOLEAN)
|
||||
{
|
||||
return _field.boolVal ? 1 : 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Value::asInt() const
|
||||
{
|
||||
CCASSERT(_type != Type::VECTOR && _type != Type::MAP && _type != Type::INT_KEY_MAP, "Only base type (bool, string, float, double, int) could be converted");
|
||||
if (_type == Type::INTEGER)
|
||||
{
|
||||
return _field.intVal;
|
||||
}
|
||||
|
||||
if (_type == Type::UNSIGNED)
|
||||
{
|
||||
CCASSERT(_field.unsignedVal < INT_MAX, "Can only convert values < INT_MAX");
|
||||
return (int)_field.unsignedVal;
|
||||
}
|
||||
|
||||
if (_type == Type::BYTE)
|
||||
{
|
||||
return _field.byteVal;
|
||||
}
|
||||
|
||||
if (_type == Type::STRING)
|
||||
{
|
||||
return atoi(_field.strVal->c_str());
|
||||
}
|
||||
|
||||
if (_type == Type::FLOAT)
|
||||
{
|
||||
return static_cast<int>(_field.floatVal);
|
||||
}
|
||||
|
||||
if (_type == Type::DOUBLE)
|
||||
{
|
||||
return static_cast<int>(_field.doubleVal);
|
||||
}
|
||||
|
||||
if (_type == Type::BOOLEAN)
|
||||
{
|
||||
return _field.boolVal ? 1 : 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
unsigned int Value::asUnsignedInt() const
|
||||
{
|
||||
CCASSERT(_type != Type::VECTOR && _type != Type::MAP && _type != Type::INT_KEY_MAP, "Only base type (bool, string, float, double, int) could be converted");
|
||||
if (_type == Type::UNSIGNED)
|
||||
{
|
||||
return _field.unsignedVal;
|
||||
}
|
||||
|
||||
if (_type == Type::INTEGER)
|
||||
{
|
||||
CCASSERT(_field.intVal >= 0, "Only values >= 0 can be converted to unsigned");
|
||||
return static_cast<unsigned int>(_field.intVal);
|
||||
}
|
||||
|
||||
if (_type == Type::BYTE)
|
||||
{
|
||||
return static_cast<unsigned int>(_field.byteVal);
|
||||
}
|
||||
|
||||
if (_type == Type::STRING)
|
||||
{
|
||||
// NOTE: strtoul is required (need to augment on unsupported platforms)
|
||||
return static_cast<unsigned int>(strtoul(_field.strVal->c_str(), nullptr, 10));
|
||||
}
|
||||
|
||||
if (_type == Type::FLOAT)
|
||||
{
|
||||
return static_cast<unsigned int>(_field.floatVal);
|
||||
}
|
||||
|
||||
if (_type == Type::DOUBLE)
|
||||
{
|
||||
return static_cast<unsigned int>(_field.doubleVal);
|
||||
}
|
||||
|
||||
if (_type == Type::BOOLEAN)
|
||||
{
|
||||
return _field.boolVal ? 1u : 0u;
|
||||
}
|
||||
|
||||
return 0u;
|
||||
}
|
||||
|
||||
float Value::asFloat() const
|
||||
{
|
||||
CCASSERT(_type != Type::VECTOR && _type != Type::MAP && _type != Type::INT_KEY_MAP, "Only base type (bool, string, float, double, int) could be converted");
|
||||
if (_type == Type::FLOAT)
|
||||
{
|
||||
return _field.floatVal;
|
||||
}
|
||||
|
||||
if (_type == Type::BYTE)
|
||||
{
|
||||
return static_cast<float>(_field.byteVal);
|
||||
}
|
||||
|
||||
if (_type == Type::STRING)
|
||||
{
|
||||
return utils::atof(_field.strVal->c_str());
|
||||
}
|
||||
|
||||
if (_type == Type::INTEGER)
|
||||
{
|
||||
return static_cast<float>(_field.intVal);
|
||||
}
|
||||
|
||||
if (_type == Type::UNSIGNED)
|
||||
{
|
||||
return static_cast<float>(_field.unsignedVal);
|
||||
}
|
||||
|
||||
if (_type == Type::DOUBLE)
|
||||
{
|
||||
return static_cast<float>(_field.doubleVal);
|
||||
}
|
||||
|
||||
if (_type == Type::BOOLEAN)
|
||||
{
|
||||
return _field.boolVal ? 1.0f : 0.0f;
|
||||
}
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
double Value::asDouble() const
|
||||
{
|
||||
CCASSERT(_type != Type::VECTOR && _type != Type::MAP && _type != Type::INT_KEY_MAP, "Only base type (bool, string, float, double, int) could be converted");
|
||||
if (_type == Type::DOUBLE)
|
||||
{
|
||||
return _field.doubleVal;
|
||||
}
|
||||
|
||||
if (_type == Type::BYTE)
|
||||
{
|
||||
return static_cast<double>(_field.byteVal);
|
||||
}
|
||||
|
||||
if (_type == Type::STRING)
|
||||
{
|
||||
return static_cast<double>(utils::atof(_field.strVal->c_str()));
|
||||
}
|
||||
|
||||
if (_type == Type::INTEGER)
|
||||
{
|
||||
return static_cast<double>(_field.intVal);
|
||||
}
|
||||
|
||||
if (_type == Type::UNSIGNED)
|
||||
{
|
||||
return static_cast<double>(_field.unsignedVal);
|
||||
}
|
||||
|
||||
if (_type == Type::FLOAT)
|
||||
{
|
||||
return static_cast<double>(_field.floatVal);
|
||||
}
|
||||
|
||||
if (_type == Type::BOOLEAN)
|
||||
{
|
||||
return _field.boolVal ? 1.0 : 0.0;
|
||||
}
|
||||
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
bool Value::asBool() const
|
||||
{
|
||||
CCASSERT(_type != Type::VECTOR && _type != Type::MAP && _type != Type::INT_KEY_MAP, "Only base type (bool, string, float, double, int) could be converted");
|
||||
if (_type == Type::BOOLEAN)
|
||||
{
|
||||
return _field.boolVal;
|
||||
}
|
||||
|
||||
if (_type == Type::BYTE)
|
||||
{
|
||||
return _field.byteVal == 0 ? false : true;
|
||||
}
|
||||
|
||||
if (_type == Type::STRING)
|
||||
{
|
||||
return (*_field.strVal == "0" || *_field.strVal == "false") ? false : true;
|
||||
}
|
||||
|
||||
if (_type == Type::INTEGER)
|
||||
{
|
||||
return _field.intVal == 0 ? false : true;
|
||||
}
|
||||
|
||||
if (_type == Type::UNSIGNED)
|
||||
{
|
||||
return _field.unsignedVal == 0 ? false : true;
|
||||
}
|
||||
|
||||
if (_type == Type::FLOAT)
|
||||
{
|
||||
return _field.floatVal == 0.0f ? false : true;
|
||||
}
|
||||
|
||||
if (_type == Type::DOUBLE)
|
||||
{
|
||||
return _field.doubleVal == 0.0 ? false : true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string Value::asString() const
|
||||
{
|
||||
CCASSERT(_type != Type::VECTOR && _type != Type::MAP && _type != Type::INT_KEY_MAP, "Only base type (bool, string, float, double, int) could be converted");
|
||||
|
||||
if (_type == Type::STRING)
|
||||
{
|
||||
return *_field.strVal;
|
||||
}
|
||||
|
||||
std::stringstream ret;
|
||||
|
||||
switch (_type)
|
||||
{
|
||||
case Type::BYTE:
|
||||
ret << _field.byteVal;
|
||||
break;
|
||||
case Type::INTEGER:
|
||||
ret << _field.intVal;
|
||||
break;
|
||||
case Type::UNSIGNED:
|
||||
ret << _field.unsignedVal;
|
||||
break;
|
||||
case Type::FLOAT:
|
||||
ret << std::fixed << std::setprecision( 7 )<< _field.floatVal;
|
||||
break;
|
||||
case Type::DOUBLE:
|
||||
ret << std::fixed << std::setprecision( 16 ) << _field.doubleVal;
|
||||
break;
|
||||
case Type::BOOLEAN:
|
||||
ret << (_field.boolVal ? "true" : "false");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return ret.str();
|
||||
}
|
||||
|
||||
ValueVector& Value::asValueVector()
|
||||
{
|
||||
CCASSERT(_type == Type::VECTOR, "The value type isn't Type::VECTOR");
|
||||
return *_field.vectorVal;
|
||||
}
|
||||
|
||||
const ValueVector& Value::asValueVector() const
|
||||
{
|
||||
CCASSERT(_type == Type::VECTOR, "The value type isn't Type::VECTOR");
|
||||
return *_field.vectorVal;
|
||||
}
|
||||
|
||||
ValueMap& Value::asValueMap()
|
||||
{
|
||||
CCASSERT(_type == Type::MAP, "The value type isn't Type::MAP");
|
||||
return *_field.mapVal;
|
||||
}
|
||||
|
||||
const ValueMap& Value::asValueMap() const
|
||||
{
|
||||
CCASSERT(_type == Type::MAP, "The value type isn't Type::MAP");
|
||||
return *_field.mapVal;
|
||||
}
|
||||
|
||||
ValueMapIntKey& Value::asIntKeyMap()
|
||||
{
|
||||
CCASSERT(_type == Type::INT_KEY_MAP, "The value type isn't Type::INT_KEY_MAP");
|
||||
return *_field.intKeyMapVal;
|
||||
}
|
||||
|
||||
const ValueMapIntKey& Value::asIntKeyMap() const
|
||||
{
|
||||
CCASSERT(_type == Type::INT_KEY_MAP, "The value type isn't Type::INT_KEY_MAP");
|
||||
return *_field.intKeyMapVal;
|
||||
}
|
||||
|
||||
static std::string getTabs(int depth)
|
||||
{
|
||||
std::string tabWidth;
|
||||
|
||||
for (int i = 0; i < depth; ++i)
|
||||
{
|
||||
tabWidth += "\t";
|
||||
}
|
||||
|
||||
return tabWidth;
|
||||
}
|
||||
|
||||
static std::string visit(const Value& v, int depth);
|
||||
|
||||
static std::string visitVector(const ValueVector& v, int depth)
|
||||
{
|
||||
std::stringstream ret;
|
||||
|
||||
if (depth > 0)
|
||||
ret << "\n";
|
||||
|
||||
ret << getTabs(depth) << "[\n";
|
||||
|
||||
int i = 0;
|
||||
for (const auto& child : v)
|
||||
{
|
||||
ret << getTabs(depth+1) << i << ": " << visit(child, depth + 1);
|
||||
++i;
|
||||
}
|
||||
|
||||
ret << getTabs(depth) << "]\n";
|
||||
|
||||
return ret.str();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
static std::string visitMap(const T& v, int depth)
|
||||
{
|
||||
std::stringstream ret;
|
||||
|
||||
if (depth > 0)
|
||||
ret << "\n";
|
||||
|
||||
ret << getTabs(depth) << "{\n";
|
||||
|
||||
for (auto iter = v.begin(); iter != v.end(); ++iter)
|
||||
{
|
||||
ret << getTabs(depth + 1) << iter->first << ": ";
|
||||
ret << visit(iter->second, depth + 1);
|
||||
}
|
||||
|
||||
ret << getTabs(depth) << "}\n";
|
||||
|
||||
return ret.str();
|
||||
}
|
||||
|
||||
static std::string visit(const Value& v, int depth)
|
||||
{
|
||||
std::stringstream ret;
|
||||
|
||||
switch (v.getType())
|
||||
{
|
||||
case Value::Type::NONE:
|
||||
case Value::Type::BYTE:
|
||||
case Value::Type::INTEGER:
|
||||
case Value::Type::UNSIGNED:
|
||||
case Value::Type::FLOAT:
|
||||
case Value::Type::DOUBLE:
|
||||
case Value::Type::BOOLEAN:
|
||||
case Value::Type::STRING:
|
||||
ret << v.asString() << "\n";
|
||||
break;
|
||||
case Value::Type::VECTOR:
|
||||
ret << visitVector(v.asValueVector(), depth);
|
||||
break;
|
||||
case Value::Type::MAP:
|
||||
ret << visitMap(v.asValueMap(), depth);
|
||||
break;
|
||||
case Value::Type::INT_KEY_MAP:
|
||||
ret << visitMap(v.asIntKeyMap(), depth);
|
||||
break;
|
||||
default:
|
||||
CCASSERT(false, "Invalid type!");
|
||||
break;
|
||||
}
|
||||
|
||||
return ret.str();
|
||||
}
|
||||
|
||||
std::string Value::getDescription() const
|
||||
{
|
||||
std::string ret("\n");
|
||||
ret += visit(*this, 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Value::clear()
|
||||
{
|
||||
// Free memory the old value allocated
|
||||
switch (_type)
|
||||
{
|
||||
case Type::BYTE:
|
||||
_field.byteVal = 0;
|
||||
break;
|
||||
case Type::INTEGER:
|
||||
_field.intVal = 0;
|
||||
break;
|
||||
case Type::UNSIGNED:
|
||||
_field.unsignedVal = 0u;
|
||||
break;
|
||||
case Type::FLOAT:
|
||||
_field.floatVal = 0.0f;
|
||||
break;
|
||||
case Type::DOUBLE:
|
||||
_field.doubleVal = 0.0;
|
||||
break;
|
||||
case Type::BOOLEAN:
|
||||
_field.boolVal = false;
|
||||
break;
|
||||
case Type::STRING:
|
||||
CC_SAFE_DELETE(_field.strVal);
|
||||
break;
|
||||
case Type::VECTOR:
|
||||
CC_SAFE_DELETE(_field.vectorVal);
|
||||
break;
|
||||
case Type::MAP:
|
||||
CC_SAFE_DELETE(_field.mapVal);
|
||||
break;
|
||||
case Type::INT_KEY_MAP:
|
||||
CC_SAFE_DELETE(_field.intKeyMapVal);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
_type = Type::NONE;
|
||||
}
|
||||
|
||||
void Value::reset(Type type)
|
||||
{
|
||||
if (_type == type)
|
||||
return;
|
||||
|
||||
clear();
|
||||
|
||||
// Allocate memory for the new value
|
||||
switch (type)
|
||||
{
|
||||
case Type::STRING:
|
||||
_field.strVal = new (std::nothrow) std::string();
|
||||
break;
|
||||
case Type::VECTOR:
|
||||
_field.vectorVal = new (std::nothrow) ValueVector();
|
||||
break;
|
||||
case Type::MAP:
|
||||
_field.mapVal = new (std::nothrow) ValueMap();
|
||||
break;
|
||||
case Type::INT_KEY_MAP:
|
||||
_field.intKeyMapVal = new (std::nothrow) ValueMapIntKey();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
_type = type;
|
||||
}
|
||||
|
||||
NS_CC_END
|
||||
|
||||
255
cocos2d-x/cocos/base/CCValue.h
Normal file
255
cocos2d-x/cocos/base/CCValue.h
Normal file
@@ -0,0 +1,255 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2013-2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
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 __cocos2d_libs__CCValue__
|
||||
#define __cocos2d_libs__CCValue__
|
||||
|
||||
#include "base/ccMacros.h"
|
||||
#include "base/ccMacros.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <map>
|
||||
|
||||
/**
|
||||
* @addtogroup base
|
||||
* @{
|
||||
*/
|
||||
|
||||
NS_CC_BEGIN
|
||||
|
||||
class Value;
|
||||
|
||||
typedef std::vector<Value> ValueVector;
|
||||
typedef std::map<std::string, Value> OrderedValueMap;
|
||||
typedef std::unordered_map<std::string, Value> ValueMap;
|
||||
typedef std::unordered_map<int, Value> ValueMapIntKey;
|
||||
|
||||
CC_DLL extern const ValueVector ValueVectorNull;
|
||||
CC_DLL extern const ValueMap ValueMapNull;
|
||||
CC_DLL extern const ValueMapIntKey ValueMapIntKeyNull;
|
||||
|
||||
/*
|
||||
* This class is provide as a wrapper of basic types, such as int and bool.
|
||||
*/
|
||||
class CC_DLL Value
|
||||
{
|
||||
public:
|
||||
/** A predefined Value that has not value. */
|
||||
static const Value Null;
|
||||
|
||||
/** Default constructor. */
|
||||
Value();
|
||||
|
||||
/** Create a Value by an unsigned char value. */
|
||||
explicit Value(unsigned char v);
|
||||
|
||||
/** Create a Value by an integer value. */
|
||||
explicit Value(int v);
|
||||
|
||||
/** Create a Value by an unsigned value. */
|
||||
explicit Value(unsigned int v);
|
||||
|
||||
/** Create a Value by a float value. */
|
||||
explicit Value(float v);
|
||||
|
||||
/** Create a Value by a double value. */
|
||||
explicit Value(double v);
|
||||
|
||||
/** Create a Value by a bool value. */
|
||||
explicit Value(bool v);
|
||||
|
||||
/** Create a Value by a char pointer. It will copy the chars internally. */
|
||||
explicit Value(const char* v);
|
||||
|
||||
/** Create a Value by a string. */
|
||||
explicit Value(const std::string& v);
|
||||
|
||||
/** Create a Value by a ValueVector object. */
|
||||
explicit Value(const ValueVector& v);
|
||||
/** Create a Value by a ValueVector object. It will use std::move internally. */
|
||||
explicit Value(ValueVector&& v);
|
||||
|
||||
/** Create a Value by a ValueMap object. */
|
||||
explicit Value(const ValueMap& v);
|
||||
/** Create a Value by a ValueMap object. It will use std::move internally. */
|
||||
explicit Value(ValueMap&& v);
|
||||
|
||||
/** Create a Value by a ValueMapIntKey object. */
|
||||
explicit Value(const ValueMapIntKey& v);
|
||||
/** Create a Value by a ValueMapIntKey object. It will use std::move internally. */
|
||||
explicit Value(ValueMapIntKey&& v);
|
||||
|
||||
/** Create a Value by another Value object. */
|
||||
Value(const Value& other);
|
||||
/** Create a Value by a Value object. It will use std::move internally. */
|
||||
Value(Value&& other);
|
||||
|
||||
/** Destructor. */
|
||||
~Value();
|
||||
|
||||
/** Assignment operator, assign from Value to Value. */
|
||||
Value& operator= (const Value& other);
|
||||
/** Assignment operator, assign from Value to Value. It will use std::move internally. */
|
||||
Value& operator= (Value&& other);
|
||||
|
||||
/** Assignment operator, assign from unsigned char to Value. */
|
||||
Value& operator= (unsigned char v);
|
||||
/** Assignment operator, assign from integer to Value. */
|
||||
Value& operator= (int v);
|
||||
/** Assignment operator, assign from integer to Value. */
|
||||
Value& operator= (unsigned int v);
|
||||
/** Assignment operator, assign from float to Value. */
|
||||
Value& operator= (float v);
|
||||
/** Assignment operator, assign from double to Value. */
|
||||
Value& operator= (double v);
|
||||
/** Assignment operator, assign from bool to Value. */
|
||||
Value& operator= (bool v);
|
||||
/** Assignment operator, assign from char* to Value. */
|
||||
Value& operator= (const char* v);
|
||||
/** Assignment operator, assign from string to Value. */
|
||||
Value& operator= (const std::string& v);
|
||||
|
||||
/** Assignment operator, assign from ValueVector to Value. */
|
||||
Value& operator= (const ValueVector& v);
|
||||
/** Assignment operator, assign from ValueVector to Value. */
|
||||
Value& operator= (ValueVector&& v);
|
||||
|
||||
/** Assignment operator, assign from ValueMap to Value. */
|
||||
Value& operator= (const ValueMap& v);
|
||||
/** Assignment operator, assign from ValueMap to Value. It will use std::move internally. */
|
||||
Value& operator= (ValueMap&& v);
|
||||
|
||||
/** Assignment operator, assign from ValueMapIntKey to Value. */
|
||||
Value& operator= (const ValueMapIntKey& v);
|
||||
/** Assignment operator, assign from ValueMapIntKey to Value. It will use std::move internally. */
|
||||
Value& operator= (ValueMapIntKey&& v);
|
||||
|
||||
/** != operator overloading */
|
||||
bool operator!= (const Value& v);
|
||||
/** != operator overloading */
|
||||
bool operator!= (const Value& v) const;
|
||||
/** == operator overloading */
|
||||
bool operator== (const Value& v);
|
||||
/** == operator overloading */
|
||||
bool operator== (const Value& v) const;
|
||||
|
||||
/** Gets as a byte value. Will convert to unsigned char if possible, or will trigger assert error. */
|
||||
unsigned char asByte() const;
|
||||
/** Gets as an integer value. Will convert to integer if possible, or will trigger assert error. */
|
||||
int asInt() const;
|
||||
/** Gets as an unsigned value. Will convert to unsigned if possible, or will trigger assert error. */
|
||||
unsigned int asUnsignedInt() const;
|
||||
/** Gets as a float value. Will convert to float if possible, or will trigger assert error. */
|
||||
float asFloat() const;
|
||||
/** Gets as a double value. Will convert to double if possible, or will trigger assert error. */
|
||||
double asDouble() const;
|
||||
/** Gets as a bool value. Will convert to bool if possible, or will trigger assert error. */
|
||||
bool asBool() const;
|
||||
/** Gets as a string value. Will convert to string if possible, or will trigger assert error. */
|
||||
std::string asString() const;
|
||||
|
||||
/** Gets as a ValueVector reference. Will convert to ValueVector if possible, or will trigger assert error. */
|
||||
ValueVector& asValueVector();
|
||||
/** Gets as a const ValueVector reference. Will convert to ValueVector if possible, or will trigger assert error. */
|
||||
const ValueVector& asValueVector() const;
|
||||
|
||||
/** Gets as a ValueMap reference. Will convert to ValueMap if possible, or will trigger assert error. */
|
||||
ValueMap& asValueMap();
|
||||
/** Gets as a const ValueMap reference. Will convert to ValueMap if possible, or will trigger assert error. */
|
||||
const ValueMap& asValueMap() const;
|
||||
|
||||
/** Gets as a ValueMapIntKey reference. Will convert to ValueMapIntKey if possible, or will trigger assert error. */
|
||||
ValueMapIntKey& asIntKeyMap();
|
||||
/** Gets as a const ValueMapIntKey reference. Will convert to ValueMapIntKey if possible, or will trigger assert error. */
|
||||
const ValueMapIntKey& asIntKeyMap() const;
|
||||
|
||||
/**
|
||||
* Checks if the Value is null.
|
||||
* @return True if the Value is null, false if not.
|
||||
*/
|
||||
inline bool isNull() const { return _type == Type::NONE; }
|
||||
|
||||
/** Value type wrapped by Value. */
|
||||
enum class Type
|
||||
{
|
||||
/// no value is wrapped, an empty Value
|
||||
NONE = 0,
|
||||
/// wrap byte
|
||||
BYTE,
|
||||
/// wrap integer
|
||||
INTEGER,
|
||||
/// wrap unsigned
|
||||
UNSIGNED,
|
||||
/// wrap float
|
||||
FLOAT,
|
||||
/// wrap double
|
||||
DOUBLE,
|
||||
/// wrap bool
|
||||
BOOLEAN,
|
||||
/// wrap string
|
||||
STRING,
|
||||
/// wrap vector
|
||||
VECTOR,
|
||||
/// wrap ValueMap
|
||||
MAP,
|
||||
/// wrap ValueMapIntKey
|
||||
INT_KEY_MAP
|
||||
};
|
||||
|
||||
/** Gets the value type. */
|
||||
inline Type getType() const { return _type; }
|
||||
|
||||
/** Gets the description of the class. */
|
||||
std::string getDescription() const;
|
||||
|
||||
private:
|
||||
void clear();
|
||||
void reset(Type type);
|
||||
|
||||
union
|
||||
{
|
||||
unsigned char byteVal;
|
||||
int intVal;
|
||||
unsigned int unsignedVal;
|
||||
float floatVal;
|
||||
double doubleVal;
|
||||
bool boolVal;
|
||||
|
||||
std::string* strVal;
|
||||
ValueVector* vectorVal;
|
||||
ValueMap* mapVal;
|
||||
ValueMapIntKey* intKeyMapVal;
|
||||
}_field;
|
||||
|
||||
Type _type;
|
||||
};
|
||||
|
||||
/** @} */
|
||||
|
||||
NS_CC_END
|
||||
|
||||
|
||||
#endif /* defined(__cocos2d_libs__CCValue__) */
|
||||
519
cocos2d-x/cocos/base/CCVector.h
Normal file
519
cocos2d-x/cocos/base/CCVector.h
Normal file
@@ -0,0 +1,519 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2010 ForzeField Studios S.L. http://forzefield.com
|
||||
Copyright (c) 2010-2012 cocos2d-x.org
|
||||
Copyright (c) 2013-2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
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.
|
||||
****************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <algorithm> // for std::find
|
||||
|
||||
#include "base/ccMacros.h"
|
||||
#include "base/ccRandom.h"
|
||||
#include "base/CCRef.h"
|
||||
|
||||
/**
|
||||
* @addtogroup base
|
||||
* @{
|
||||
*/
|
||||
NS_CC_BEGIN
|
||||
|
||||
/*
|
||||
* Similar to std::vector, but it will manage reference count automatically internally.
|
||||
* Which means it will invoke Ref::retain() when adding an element, and invoke Ref::release() when removing an element.
|
||||
* @warn The element should be `Ref` or its sub-class.
|
||||
* @lua NA
|
||||
*/
|
||||
template<class T>
|
||||
class Vector
|
||||
{
|
||||
public:
|
||||
// ------------------------------------------
|
||||
// Iterators
|
||||
// ------------------------------------------
|
||||
|
||||
/** Iterator, can be used to loop the Vector. */
|
||||
using iterator = typename std::vector<T>::iterator;
|
||||
/** Const iterator, can be used to loop the Vector. */
|
||||
using const_iterator = typename std::vector<T>::const_iterator;
|
||||
|
||||
/** Reversed iterator, can be used to loop the Vector in reverse sequence. */
|
||||
using reverse_iterator = typename std::vector<T>::reverse_iterator;
|
||||
/** Reversed iterator, can be used to loop the Vector in reverse sequence. */
|
||||
using const_reverse_iterator = typename std::vector<T>::const_reverse_iterator;
|
||||
|
||||
/** Returns an iterator pointing the first element of the Vector. */
|
||||
iterator begin() { return _data.begin(); }
|
||||
/** Returns an iterator pointing the first element of the Vector. */
|
||||
const_iterator begin() const { return _data.begin(); }
|
||||
|
||||
/**
|
||||
* Returns an iterator referring to the `past-the-end` element in the Vector container.
|
||||
* The past-the-end element is the theoretical element that would follow the last element in the Vector.
|
||||
* It does not point to any element, and thus shall not be dereferenced.
|
||||
*/
|
||||
iterator end() { return _data.end(); }
|
||||
/**
|
||||
* Returns iterator referring to the `past-the-end` element in the Vector container.
|
||||
* The past-the-end element is the theoretical element that would follow the last element in the Vector.
|
||||
* It does not point to any element, and thus shall not be dereferenced.
|
||||
*/
|
||||
const_iterator end() const { return _data.end(); }
|
||||
|
||||
/** Returns a const_iterator pointing the first element of the Vector. */
|
||||
const_iterator cbegin() const { return _data.cbegin(); }
|
||||
/** Returns a const_iterator pointing the `past-the-end` element of the Vector. */
|
||||
const_iterator cend() const { return _data.cend(); }
|
||||
|
||||
/** Returns a reverse iterator pointing to the last element of the Vector. */
|
||||
reverse_iterator rbegin() { return _data.rbegin(); }
|
||||
/** Returns a reverse iterator pointing to the last element of the Vector. */
|
||||
const_reverse_iterator rbegin() const { return _data.rbegin(); }
|
||||
|
||||
/** Returns a reverse iterator pointing to the theoretical element preceding the
|
||||
* first element of the vector (which is considered its reverse end).
|
||||
*/
|
||||
reverse_iterator rend() { return _data.rend(); }
|
||||
/** Returns a reverse iterator pointing to the theoretical element preceding the
|
||||
* first element of the vector (which is considered its reverse end).
|
||||
*/
|
||||
const_reverse_iterator rend() const { return _data.rend(); }
|
||||
|
||||
/** Returns a const_reverse_iterator pointing to the last element in the container (i.e., its reverse beginning). */
|
||||
const_reverse_iterator crbegin() const { return _data.crbegin(); }
|
||||
/** Returns a const_reverse_iterator pointing to the theoretical element preceding the first element in
|
||||
* the container (which is considered its reverse end).
|
||||
*/
|
||||
const_reverse_iterator crend() const { return _data.crend(); }
|
||||
|
||||
/** Constructor. */
|
||||
Vector<T>()
|
||||
: _data()
|
||||
{
|
||||
static_assert(std::is_convertible<T, Ref*>::value, "Invalid Type for cocos2d::Vector<T>!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor with a capacity.
|
||||
* @param capacity Capacity of the Vector.
|
||||
*/
|
||||
explicit Vector<T>(ssize_t capacity)
|
||||
: _data()
|
||||
{
|
||||
static_assert(std::is_convertible<T, Ref*>::value, "Invalid Type for cocos2d::Vector<T>!");
|
||||
CCLOGINFO("In the default constructor with capacity of Vector.");
|
||||
reserve(capacity);
|
||||
}
|
||||
|
||||
/** Constructor with initializer list. */
|
||||
Vector<T>(std::initializer_list<T> list)
|
||||
{
|
||||
for (auto& element : list)
|
||||
{
|
||||
pushBack(element);
|
||||
}
|
||||
}
|
||||
|
||||
/** Destructor. */
|
||||
~Vector<T>()
|
||||
{
|
||||
CCLOGINFO("In the destructor of Vector.");
|
||||
clear();
|
||||
}
|
||||
|
||||
/** Copy constructor. */
|
||||
Vector<T>(const Vector<T>& other)
|
||||
{
|
||||
static_assert(std::is_convertible<T, Ref*>::value, "Invalid Type for cocos2d::Vector<T>!");
|
||||
CCLOGINFO("In the copy constructor!");
|
||||
_data = other._data;
|
||||
addRefForAllObjects();
|
||||
}
|
||||
|
||||
/** Constructor with std::move semantic. */
|
||||
Vector<T>(Vector<T>&& other)
|
||||
{
|
||||
static_assert(std::is_convertible<T, Ref*>::value, "Invalid Type for cocos2d::Vector<T>!");
|
||||
CCLOGINFO("In the move constructor of Vector!");
|
||||
_data = std::move(other._data);
|
||||
}
|
||||
|
||||
/** Copy assignment operator. */
|
||||
Vector<T>& operator=(const Vector<T>& other)
|
||||
{
|
||||
if (this != &other) {
|
||||
CCLOGINFO("In the copy assignment operator!");
|
||||
clear();
|
||||
_data = other._data;
|
||||
addRefForAllObjects();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Copy assignment operator with std::move semantic. */
|
||||
Vector<T>& operator=(Vector<T>&& other)
|
||||
{
|
||||
if (this != &other) {
|
||||
CCLOGINFO("In the move assignment operator!");
|
||||
clear();
|
||||
_data = std::move(other._data);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Don't uses operator since we could not decide whether it needs 'retain'/'release'.
|
||||
// T& operator[](int index)
|
||||
// {
|
||||
// return _data[index];
|
||||
// }
|
||||
//
|
||||
// const T& operator[](int index) const
|
||||
// {
|
||||
// return _data[index];
|
||||
// }
|
||||
|
||||
/**
|
||||
* Requests that the vector capacity be at least enough to contain n elements.
|
||||
* @param capacity Minimum capacity requested of the Vector.
|
||||
*/
|
||||
void reserve(ssize_t n)
|
||||
{
|
||||
_data.reserve(n);
|
||||
}
|
||||
|
||||
/** @brief Returns the size of the storage space currently allocated for the Vector, expressed in terms of elements.
|
||||
* @note This capacity is not necessarily equal to the Vector size.
|
||||
* It can be equal or greater, with the extra space allowing to accommodate for growth without the need to reallocate on each insertion.
|
||||
* @return The size of the currently allocated storage capacity in the Vector, measured in terms of the number elements it can hold.
|
||||
*/
|
||||
ssize_t capacity() const
|
||||
{
|
||||
return _data.capacity();
|
||||
}
|
||||
|
||||
/** @brief Returns the number of elements in the Vector.
|
||||
* @note This is the number of actual objects held in the Vector, which is not necessarily equal to its storage capacity.
|
||||
* @return The number of elements in the Vector.
|
||||
*/
|
||||
ssize_t size() const
|
||||
{
|
||||
return _data.size();
|
||||
}
|
||||
|
||||
/** @brief Returns whether the Vector is empty (i.e. whether its size is 0).
|
||||
* @note This function does not modify the container in any way. To clear the content of a vector, see Vector<T>::clear.
|
||||
*/
|
||||
bool empty() const
|
||||
{
|
||||
return _data.empty();
|
||||
}
|
||||
|
||||
/** Returns the maximum number of elements that the Vector can hold. */
|
||||
ssize_t max_size() const
|
||||
{
|
||||
return _data.max_size();
|
||||
}
|
||||
|
||||
/** Returns index of a certain object, return UINT_MAX if doesn't contain the object */
|
||||
ssize_t getIndex(T object) const
|
||||
{
|
||||
auto iter = std::find(_data.begin(), _data.end(), object);
|
||||
if (iter != _data.end())
|
||||
return iter - _data.begin();
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/** @brief Find the object in the Vector.
|
||||
* @param object The object to find.
|
||||
* @return Returns an iterator which refers to the element that its value is equals to object.
|
||||
* Returns Vector::end() if not found.
|
||||
*/
|
||||
const_iterator find(T object) const
|
||||
{
|
||||
return std::find(_data.begin(), _data.end(), object);
|
||||
}
|
||||
|
||||
/** @brief Find the object in the Vector.
|
||||
* @param object The object to find.
|
||||
* @return Returns an iterator which refers to the element that its value is equals to object.
|
||||
* Returns Vector::end() if not found.
|
||||
*/
|
||||
iterator find(T object)
|
||||
{
|
||||
return std::find(_data.begin(), _data.end(), object);
|
||||
}
|
||||
|
||||
/** Returns the element at position 'index' in the Vector. */
|
||||
T at(ssize_t index) const
|
||||
{
|
||||
CCASSERT( index >= 0 && index < size(), "index out of range in getObjectAtIndex()");
|
||||
return _data[index];
|
||||
}
|
||||
|
||||
/** Returns the first element in the Vector. */
|
||||
T front() const
|
||||
{
|
||||
return _data.front();
|
||||
}
|
||||
|
||||
/** Returns the last element of the Vector. */
|
||||
T back() const
|
||||
{
|
||||
return _data.back();
|
||||
}
|
||||
|
||||
/** Returns a random element of the Vector. */
|
||||
T getRandomObject() const
|
||||
{
|
||||
if (!_data.empty())
|
||||
{
|
||||
ssize_t randIdx = RandomHelper::random_int<int>(0, static_cast<int>(_data.size()) - 1);
|
||||
return *(_data.begin() + randIdx);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether an object is in the container.
|
||||
* @param object The object to be checked.
|
||||
* @return True if the object is in the container, false if not.
|
||||
*/
|
||||
bool contains(T object) const
|
||||
{
|
||||
return( std::find(_data.begin(), _data.end(), object) != _data.end() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether two vectors are equal.
|
||||
* @param other The vector to be compared.
|
||||
* @return True if two vectors are equal, false if not.
|
||||
*/
|
||||
bool equals(const Vector<T> &other) const
|
||||
{
|
||||
ssize_t s = this->size();
|
||||
if (s != other.size())
|
||||
return false;
|
||||
|
||||
for (ssize_t i = 0; i < s; i++)
|
||||
{
|
||||
if (this->at(i) != other.at(i))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Adds objects
|
||||
|
||||
/** Adds a new element at the end of the Vector. */
|
||||
void pushBack(T object)
|
||||
{
|
||||
CCASSERT(object != nullptr, "The object should not be nullptr");
|
||||
_data.push_back( object );
|
||||
object->retain();
|
||||
}
|
||||
|
||||
/** Push all elements of an existing Vector to the end of current Vector. */
|
||||
void pushBack(const Vector<T>& other)
|
||||
{
|
||||
for(const auto &obj : other) {
|
||||
_data.push_back(obj);
|
||||
obj->retain();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert an object at certain index.
|
||||
* @param index The index to be inserted at.
|
||||
* @param object The object to be inserted.
|
||||
*/
|
||||
void insert(ssize_t index, T object)
|
||||
{
|
||||
CCASSERT(index >= 0 && index <= size(), "Invalid index!");
|
||||
CCASSERT(object != nullptr, "The object should not be nullptr");
|
||||
_data.insert((std::begin(_data) + index), object);
|
||||
object->retain();
|
||||
}
|
||||
|
||||
// Removes Objects
|
||||
|
||||
/** Removes the last element in the Vector. */
|
||||
void popBack()
|
||||
{
|
||||
CCASSERT(!_data.empty(), "no objects added");
|
||||
auto last = _data.back();
|
||||
_data.pop_back();
|
||||
last->release();
|
||||
}
|
||||
|
||||
/** Remove a certain object in Vector.
|
||||
* @param object The object to be removed.
|
||||
* @param removeAll Whether to remove all elements with the same value.
|
||||
* If its value is 'false', it will just erase the first occurrence.
|
||||
*/
|
||||
void eraseObject(T object, bool removeAll = false)
|
||||
{
|
||||
CCASSERT(object != nullptr, "The object should not be nullptr");
|
||||
|
||||
if (removeAll)
|
||||
{
|
||||
for (auto iter = _data.begin(); iter != _data.end();)
|
||||
{
|
||||
if ((*iter) == object)
|
||||
{
|
||||
iter = _data.erase(iter);
|
||||
object->release();
|
||||
}
|
||||
else
|
||||
{
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto iter = std::find(_data.begin(), _data.end(), object);
|
||||
if (iter != _data.end())
|
||||
{
|
||||
_data.erase(iter);
|
||||
object->release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @brief Removes from the vector with an iterator.
|
||||
* @param position Iterator pointing to a single element to be removed from the Vector.
|
||||
* @return An iterator pointing to the new location of the element that followed the last element erased by the function call.
|
||||
* This is the container end if the operation erased the last element in the sequence.
|
||||
*/
|
||||
iterator erase(iterator position)
|
||||
{
|
||||
CCASSERT(position >= _data.begin() && position < _data.end(), "Invalid position!");
|
||||
(*position)->release();
|
||||
return _data.erase(position);
|
||||
}
|
||||
|
||||
/** @brief Removes from the Vector with a range of elements ( [first, last) ).
|
||||
* @param first The beginning of the range.
|
||||
* @param last The end of the range, the 'last' will not be removed, it's only for indicating the end of range.
|
||||
* @return An iterator pointing to the new location of the element that followed the last element erased by the function call.
|
||||
* This is the container end if the operation erased the last element in the sequence.
|
||||
*/
|
||||
iterator erase(iterator first, iterator last)
|
||||
{
|
||||
for (auto iter = first; iter != last; ++iter)
|
||||
{
|
||||
(*iter)->release();
|
||||
}
|
||||
|
||||
return _data.erase(first, last);
|
||||
}
|
||||
|
||||
/** @brief Removes from the Vector by index.
|
||||
* @param index The index of the element to be removed from the Vector.
|
||||
* @return An iterator pointing to the successor of Vector[index].
|
||||
*/
|
||||
iterator erase(ssize_t index)
|
||||
{
|
||||
CCASSERT(!_data.empty() && index >=0 && index < size(), "Invalid index!");
|
||||
auto it = std::next( begin(), index );
|
||||
(*it)->release();
|
||||
return _data.erase(it);
|
||||
}
|
||||
|
||||
/** @brief Removes all elements from the Vector (which are destroyed), leaving the container with a size of 0.
|
||||
* @note All the elements in the Vector will be released (reference count will be decreased).
|
||||
*/
|
||||
void clear()
|
||||
{
|
||||
for( auto it = std::begin(_data); it != std::end(_data); ++it ) {
|
||||
(*it)->release();
|
||||
}
|
||||
_data.clear();
|
||||
}
|
||||
|
||||
// Rearranging Content
|
||||
|
||||
/** Swap the values object1 and object2. */
|
||||
void swap(T object1, T object2)
|
||||
{
|
||||
ssize_t idx1 = getIndex(object1);
|
||||
ssize_t idx2 = getIndex(object2);
|
||||
|
||||
CCASSERT(idx1>=0 && idx2>=0, "invalid object index");
|
||||
|
||||
std::swap( _data[idx1], _data[idx2] );
|
||||
}
|
||||
|
||||
/** Swap two elements by indexes. */
|
||||
void swap(ssize_t index1, ssize_t index2)
|
||||
{
|
||||
CCASSERT(index1 >=0 && index1 < size() && index2 >= 0 && index2 < size(), "Invalid indices");
|
||||
|
||||
std::swap( _data[index1], _data[index2] );
|
||||
}
|
||||
|
||||
/** Replace value at index with given object. */
|
||||
void replace(ssize_t index, T object)
|
||||
{
|
||||
CCASSERT(index >= 0 && index < size(), "Invalid index!");
|
||||
CCASSERT(object != nullptr, "The object should not be nullptr");
|
||||
|
||||
_data[index]->release();
|
||||
_data[index] = object;
|
||||
object->retain();
|
||||
}
|
||||
|
||||
/** Reverses the Vector. */
|
||||
void reverse()
|
||||
{
|
||||
std::reverse( std::begin(_data), std::end(_data) );
|
||||
}
|
||||
|
||||
/** Requests the container to reduce its capacity to fit its size. */
|
||||
void shrinkToFit()
|
||||
{
|
||||
_data.shrink_to_fit();
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
/** Retains all the objects in the vector */
|
||||
void addRefForAllObjects()
|
||||
{
|
||||
for(const auto &obj : _data) {
|
||||
obj->retain();
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<T> _data;
|
||||
};
|
||||
|
||||
// end of base group
|
||||
/** @} */
|
||||
|
||||
NS_CC_END
|
||||
339
cocos2d-x/cocos/base/TGAlib.cpp
Normal file
339
cocos2d-x/cocos/base/TGAlib.cpp
Normal file
@@ -0,0 +1,339 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2010-2012 cocos2d-x.org
|
||||
Copyright (c) 2013-2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
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 <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "base/TGAlib.h"
|
||||
#include "base/CCData.h"
|
||||
#include "platform/CCFileUtils.h"
|
||||
|
||||
NS_CC_BEGIN
|
||||
|
||||
static bool tgaLoadRLEImageData(unsigned char* Buffer, unsigned long bufSize, tImageTGA *info);
|
||||
void tgaFlipImage( tImageTGA *info );
|
||||
|
||||
// load the image header field from stream
|
||||
bool tgaLoadHeader(unsigned char* buffer, unsigned long bufSize, tImageTGA *info)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
do
|
||||
{
|
||||
size_t step = sizeof(unsigned char) * 2;
|
||||
CC_BREAK_IF((step + sizeof(unsigned char)) > bufSize);
|
||||
memcpy(&info->type, buffer + step, sizeof(unsigned char));
|
||||
|
||||
step += sizeof(unsigned char) * 2;
|
||||
step += sizeof(signed short) * 4;
|
||||
CC_BREAK_IF((step + sizeof(signed short) * 2 + sizeof(unsigned char)) > bufSize);
|
||||
memcpy(&info->width, buffer + step, sizeof(signed short));
|
||||
memcpy(&info->height, buffer + step + sizeof(signed short), sizeof(signed short));
|
||||
memcpy(&info->pixelDepth, buffer + step + sizeof(signed short) * 2, sizeof(unsigned char));
|
||||
|
||||
step += sizeof(unsigned char);
|
||||
step += sizeof(signed short) * 2;
|
||||
CC_BREAK_IF((step + sizeof(unsigned char)) > bufSize);
|
||||
unsigned char cGarbage;
|
||||
memcpy(&cGarbage, buffer + step, sizeof(unsigned char));
|
||||
|
||||
info->flipped = 0;
|
||||
if ( cGarbage & 0x20 )
|
||||
{
|
||||
info->flipped = 1;
|
||||
}
|
||||
ret = true;
|
||||
} while (0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool tgaLoadImageData(unsigned char *Buffer, unsigned long bufSize, tImageTGA *info)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
do
|
||||
{
|
||||
int mode,total,i;
|
||||
unsigned char aux;
|
||||
size_t step = (sizeof(unsigned char) + sizeof(signed short)) * 6;
|
||||
|
||||
// mode equal the number of components for each pixel
|
||||
mode = info->pixelDepth / 8;
|
||||
// total is the number of unsigned chars we'll have to read
|
||||
total = info->height * info->width * mode;
|
||||
|
||||
size_t dataSize = sizeof(unsigned char) * total;
|
||||
CC_BREAK_IF((step + dataSize) > bufSize);
|
||||
memcpy(info->imageData, Buffer + step, dataSize);
|
||||
|
||||
// mode=3 or 4 implies that the image is RGB(A). However TGA
|
||||
// stores it as BGR(A) so we'll have to swap R and B.
|
||||
if (mode >= 3)
|
||||
{
|
||||
for (i=0; i < total; i+= mode)
|
||||
{
|
||||
aux = info->imageData[i];
|
||||
info->imageData[i] = info->imageData[i+2];
|
||||
info->imageData[i+2] = aux;
|
||||
}
|
||||
}
|
||||
|
||||
ret = true;
|
||||
} while (0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool tgaLoadRLEImageData(unsigned char* buffer, unsigned long bufSize, tImageTGA *info)
|
||||
{
|
||||
unsigned int mode,total,i, index = 0;
|
||||
unsigned char aux[4], runlength = 0;
|
||||
unsigned int skip = 0, flag = 0;
|
||||
size_t step = (sizeof(unsigned char) + sizeof(signed short)) * 6;
|
||||
|
||||
// mode equal the number of components for each pixel
|
||||
mode = info->pixelDepth / 8;
|
||||
// total is the number of unsigned chars we'll have to read
|
||||
total = info->height * info->width;
|
||||
|
||||
for( i = 0; i < total; i++ )
|
||||
{
|
||||
// if we have a run length pending, run it
|
||||
if ( runlength != 0 )
|
||||
{
|
||||
// we do, update the run length count
|
||||
runlength--;
|
||||
skip = (flag != 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// otherwise, read in the run length token
|
||||
CC_BREAK_IF((step + sizeof(unsigned char)) > bufSize);
|
||||
memcpy(&runlength, buffer + step, sizeof(unsigned char));
|
||||
step += sizeof(unsigned char);
|
||||
|
||||
// see if it's a RLE encoded sequence
|
||||
flag = runlength & 0x80;
|
||||
if ( flag )
|
||||
{
|
||||
runlength -= 128;
|
||||
}
|
||||
skip = 0;
|
||||
}
|
||||
|
||||
// do we need to skip reading this pixel?
|
||||
if ( !skip )
|
||||
{
|
||||
// no, read in the pixel data
|
||||
CC_BREAK_IF((step + sizeof(unsigned char) * mode) > bufSize);
|
||||
|
||||
memcpy(aux, buffer + step, sizeof(unsigned char) * mode);
|
||||
step += sizeof(unsigned char) * mode;
|
||||
|
||||
// mode=3 or 4 implies that the image is RGB(A). However TGA
|
||||
// stores it as BGR(A) so we'll have to swap R and B.
|
||||
if ( mode >= 3 )
|
||||
{
|
||||
unsigned char tmp;
|
||||
|
||||
tmp = aux[0];
|
||||
aux[0] = aux[2];
|
||||
aux[2] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
// add the pixel to our image
|
||||
memcpy(&info->imageData[index], aux, mode);
|
||||
index += mode;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void tgaFlipImage( tImageTGA *info )
|
||||
{
|
||||
// mode equal the number of components for each pixel
|
||||
int mode = info->pixelDepth / 8;
|
||||
int rowbytes = info->width*mode;
|
||||
unsigned char *row = (unsigned char *)malloc(rowbytes);
|
||||
int y;
|
||||
|
||||
if (row == nullptr) return;
|
||||
|
||||
for( y = 0; y < (info->height/2); y++ )
|
||||
{
|
||||
memcpy(row, &info->imageData[y*rowbytes],rowbytes);
|
||||
memcpy(&info->imageData[y*rowbytes], &info->imageData[(info->height-(y+1))*rowbytes], rowbytes);
|
||||
memcpy(&info->imageData[(info->height-(y+1))*rowbytes], row, rowbytes);
|
||||
}
|
||||
|
||||
free(row);
|
||||
info->flipped = 0;
|
||||
}
|
||||
|
||||
tImageTGA* tgaLoadBuffer(unsigned char* buffer, long size)
|
||||
{
|
||||
int mode,total;
|
||||
tImageTGA *info = nullptr;
|
||||
|
||||
do
|
||||
{
|
||||
CC_BREAK_IF(! buffer);
|
||||
info = (tImageTGA *)malloc(sizeof(tImageTGA));
|
||||
|
||||
// get the file header info
|
||||
if (! tgaLoadHeader(buffer, size, info))
|
||||
{
|
||||
info->status = TGA_ERROR_MEMORY;
|
||||
break;
|
||||
}
|
||||
|
||||
// check if the image is color indexed
|
||||
if (info->type == 1)
|
||||
{
|
||||
info->status = TGA_ERROR_INDEXED_COLOR;
|
||||
break;
|
||||
}
|
||||
|
||||
// check for other types (compressed images)
|
||||
if ((info->type != 2) && (info->type !=3) && (info->type !=10) )
|
||||
{
|
||||
info->status = TGA_ERROR_COMPRESSED_FILE;
|
||||
break;
|
||||
}
|
||||
|
||||
// mode equals the number of image components
|
||||
mode = info->pixelDepth / 8;
|
||||
// total is the number of unsigned chars to read
|
||||
total = info->height * info->width * mode;
|
||||
// allocate memory for image pixels
|
||||
info->imageData = (unsigned char *)malloc(sizeof(unsigned char) * total);
|
||||
|
||||
// check to make sure we have the memory required
|
||||
if (info->imageData == nullptr)
|
||||
{
|
||||
info->status = TGA_ERROR_MEMORY;
|
||||
break;
|
||||
}
|
||||
|
||||
bool bLoadImage = false;
|
||||
// finally load the image pixels
|
||||
if ( info->type == 10 )
|
||||
{
|
||||
bLoadImage = tgaLoadRLEImageData(buffer, size, info);
|
||||
}
|
||||
else
|
||||
{
|
||||
bLoadImage = tgaLoadImageData(buffer, size, info);
|
||||
}
|
||||
|
||||
// check for errors when reading the pixels
|
||||
if (! bLoadImage)
|
||||
{
|
||||
info->status = TGA_ERROR_READING_FILE;
|
||||
break;
|
||||
}
|
||||
info->status = TGA_OK;
|
||||
|
||||
if ( info->flipped )
|
||||
{
|
||||
tgaFlipImage( info );
|
||||
if ( info->flipped )
|
||||
{
|
||||
info->status = TGA_ERROR_MEMORY;
|
||||
}
|
||||
}
|
||||
} while(0);
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
// this is the function to call when we want to load an image
|
||||
tImageTGA * tgaLoad(const char *filename)
|
||||
{
|
||||
Data data = FileUtils::getInstance()->getDataFromFile(filename);
|
||||
|
||||
if (!data.isNull())
|
||||
{
|
||||
return tgaLoadBuffer(data.getBytes(), data.getSize());
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// converts RGB to grayscale
|
||||
void tgaRGBtogreyscale(tImageTGA *info) {
|
||||
|
||||
int mode,i,j;
|
||||
|
||||
unsigned char *newImageData;
|
||||
|
||||
// if the image is already grayscale do nothing
|
||||
if (info->pixelDepth == 8)
|
||||
return;
|
||||
|
||||
// compute the number of actual components
|
||||
mode = info->pixelDepth / 8;
|
||||
|
||||
// allocate an array for the new image data
|
||||
newImageData = (unsigned char *)malloc(sizeof(unsigned char) *
|
||||
info->height * info->width);
|
||||
if (newImageData == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
// convert pixels: grayscale = o.30 * R + 0.59 * G + 0.11 * B
|
||||
for (i = 0,j = 0; j < info->width * info->height; i +=mode, j++)
|
||||
newImageData[j] =
|
||||
(unsigned char)(0.30 * info->imageData[i] +
|
||||
0.59 * info->imageData[i+1] +
|
||||
0.11 * info->imageData[i+2]);
|
||||
|
||||
|
||||
//free old image data
|
||||
free(info->imageData);
|
||||
|
||||
// reassign pixelDepth and type according to the new image type
|
||||
info->pixelDepth = 8;
|
||||
info->type = 3;
|
||||
// reassigning imageData to the new array.
|
||||
info->imageData = newImageData;
|
||||
}
|
||||
|
||||
// releases the memory used for the image
|
||||
void tgaDestroy(tImageTGA *info) {
|
||||
|
||||
if (info != nullptr) {
|
||||
if (info->imageData != nullptr)
|
||||
{
|
||||
free(info->imageData);
|
||||
}
|
||||
|
||||
free(info);
|
||||
}
|
||||
}
|
||||
NS_CC_END
|
||||
80
cocos2d-x/cocos/base/TGAlib.h
Normal file
80
cocos2d-x/cocos/base/TGAlib.h
Normal file
@@ -0,0 +1,80 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2010-2012 cocos2d-x.org
|
||||
Copyright (c) 2013-2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
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 __SUPPORT_DATA_SUPPORT_TGALIB_H__
|
||||
#define __SUPPORT_DATA_SUPPORT_TGALIB_H__
|
||||
/// @cond DO_NOT_SHOW
|
||||
|
||||
namespace cocos2d {
|
||||
|
||||
enum {
|
||||
TGA_OK,
|
||||
TGA_ERROR_FILE_OPEN,
|
||||
TGA_ERROR_READING_FILE,
|
||||
TGA_ERROR_INDEXED_COLOR,
|
||||
TGA_ERROR_MEMORY,
|
||||
TGA_ERROR_COMPRESSED_FILE,
|
||||
};
|
||||
|
||||
/** TGA format */
|
||||
typedef struct sImageTGA {
|
||||
int status;
|
||||
unsigned char type, pixelDepth;
|
||||
|
||||
/** map width */
|
||||
signed short width;
|
||||
|
||||
/** map height */
|
||||
signed short height;
|
||||
|
||||
/** raw data */
|
||||
unsigned char *imageData;
|
||||
int flipped;
|
||||
} tImageTGA;
|
||||
|
||||
/// load the image header fields. We only keep those that matter!
|
||||
bool tgaLoadHeader(unsigned char *buffer, unsigned long bufSize, tImageTGA *info);
|
||||
|
||||
/// loads the image pixels. You shouldn't call this function directly
|
||||
bool tgaLoadImageData(unsigned char *buffer, unsigned long bufSize, tImageTGA *info);
|
||||
|
||||
/// this is the function to call when we want to load an image buffer.
|
||||
tImageTGA* tgaLoadBuffer(unsigned char* buffer, long size);
|
||||
|
||||
/// this is the function to call when we want to load an image
|
||||
tImageTGA * tgaLoad(const char *filename);
|
||||
|
||||
// /converts RGB to grayscale
|
||||
void tgaRGBtogreyscale(tImageTGA *info);
|
||||
|
||||
/// releases the memory used for the image
|
||||
void tgaDestroy(tImageTGA *info);
|
||||
|
||||
}//namespace cocos2d
|
||||
|
||||
/// @endcond
|
||||
#endif // __SUPPORT_DATA_SUPPORT_TGALIB_H__
|
||||
|
||||
733
cocos2d-x/cocos/base/ZipUtils.cpp
Normal file
733
cocos2d-x/cocos/base/ZipUtils.cpp
Normal file
@@ -0,0 +1,733 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2010-2012 cocos2d-x.org
|
||||
Copyright (c) 2013-2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
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.
|
||||
****************************************************************************/
|
||||
|
||||
// IDEA: hack, must be included before ziputils
|
||||
#include "base/ZipUtils.h"
|
||||
#ifdef MINIZIP_FROM_SYSTEM
|
||||
#include <minizip/unzip.h>
|
||||
#else // from our embedded sources
|
||||
#include "unzip/unzip.h"
|
||||
#endif
|
||||
|
||||
#include <zlib.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "base/CCData.h"
|
||||
#include "base/ccMacros.h"
|
||||
#include "platform/CCFileUtils.h"
|
||||
#include <map>
|
||||
|
||||
// IDEA: Other platforms should use upstream minizip like mingw-w64
|
||||
#ifdef MINIZIP_FROM_SYSTEM
|
||||
#define unzGoToFirstFile64(A,B,C,D) unzGoToFirstFile2(A,B,C,D, NULL, 0, NULL, 0)
|
||||
#define unzGoToNextFile64(A,B,C,D) unzGoToNextFile2(A,B,C,D, NULL, 0, NULL, 0)
|
||||
#endif
|
||||
|
||||
NS_CC_BEGIN
|
||||
|
||||
unsigned int ZipUtils::s_uEncryptedPvrKeyParts[4] = {0,0,0,0};
|
||||
unsigned int ZipUtils::s_uEncryptionKey[1024];
|
||||
bool ZipUtils::s_bEncryptionKeyIsValid = false;
|
||||
|
||||
// --------------------- ZipUtils ---------------------
|
||||
|
||||
inline void ZipUtils::decodeEncodedPvr(unsigned int *data, ssize_t len)
|
||||
{
|
||||
const int enclen = 1024;
|
||||
const int securelen = 512;
|
||||
const int distance = 64;
|
||||
|
||||
// check if key was set
|
||||
// make sure to call caw_setkey_part() for all 4 key parts
|
||||
CCASSERT(s_uEncryptedPvrKeyParts[0] != 0, "ZipUtils: CCZ file is encrypted but key part 0 is not set. Did you call ZipUtils::setPvrEncryptionKeyPart(...)?");
|
||||
CCASSERT(s_uEncryptedPvrKeyParts[1] != 0, "ZipUtils: CCZ file is encrypted but key part 1 is not set. Did you call ZipUtils::setPvrEncryptionKeyPart(...)?");
|
||||
CCASSERT(s_uEncryptedPvrKeyParts[2] != 0, "ZipUtils: CCZ file is encrypted but key part 2 is not set. Did you call ZipUtils::setPvrEncryptionKeyPart(...)?");
|
||||
CCASSERT(s_uEncryptedPvrKeyParts[3] != 0, "ZipUtils: CCZ file is encrypted but key part 3 is not set. Did you call ZipUtils::setPvrEncryptionKeyPart(...)?");
|
||||
|
||||
// create long key
|
||||
if(!s_bEncryptionKeyIsValid)
|
||||
{
|
||||
unsigned int y, p, e;
|
||||
unsigned int rounds = 6;
|
||||
unsigned int sum = 0;
|
||||
unsigned int z = s_uEncryptionKey[enclen-1];
|
||||
|
||||
do
|
||||
{
|
||||
#define DELTA 0x9e3779b9
|
||||
#define MX (((z>>5^y<<2) + (y>>3^z<<4)) ^ ((sum^y) + (s_uEncryptedPvrKeyParts[(p&3)^e] ^ z)))
|
||||
|
||||
sum += DELTA;
|
||||
e = (sum >> 2) & 3;
|
||||
|
||||
for (p = 0; p < enclen - 1; p++)
|
||||
{
|
||||
y = s_uEncryptionKey[p + 1];
|
||||
z = s_uEncryptionKey[p] += MX;
|
||||
}
|
||||
|
||||
y = s_uEncryptionKey[0];
|
||||
z = s_uEncryptionKey[enclen - 1] += MX;
|
||||
|
||||
} while (--rounds);
|
||||
|
||||
s_bEncryptionKeyIsValid = true;
|
||||
}
|
||||
|
||||
int b = 0;
|
||||
int i = 0;
|
||||
|
||||
// encrypt first part completely
|
||||
for(; i < len && i < securelen; i++)
|
||||
{
|
||||
data[i] ^= s_uEncryptionKey[b++];
|
||||
|
||||
if(b >= enclen)
|
||||
{
|
||||
b = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// encrypt second section partially
|
||||
for(; i < len; i += distance)
|
||||
{
|
||||
data[i] ^= s_uEncryptionKey[b++];
|
||||
|
||||
if(b >= enclen)
|
||||
{
|
||||
b = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline unsigned int ZipUtils::checksumPvr(const unsigned int *data, ssize_t len)
|
||||
{
|
||||
unsigned int cs = 0;
|
||||
const int cslen = 128;
|
||||
|
||||
len = (len < cslen) ? len : cslen;
|
||||
|
||||
for(int i = 0; i < len; i++)
|
||||
{
|
||||
cs = cs ^ data[i];
|
||||
}
|
||||
|
||||
return cs;
|
||||
}
|
||||
|
||||
// memory in iPhone is precious
|
||||
// Should buffer factor be 1.5 instead of 2 ?
|
||||
#define BUFFER_INC_FACTOR (2)
|
||||
|
||||
int ZipUtils::inflateMemoryWithHint(unsigned char *in, ssize_t inLength, unsigned char **out, ssize_t *outLength, ssize_t outLengthHint)
|
||||
{
|
||||
/* ret value */
|
||||
int err = Z_OK;
|
||||
|
||||
ssize_t bufferSize = outLengthHint;
|
||||
*out = (unsigned char*)malloc(bufferSize);
|
||||
|
||||
z_stream d_stream; /* decompression stream */
|
||||
d_stream.zalloc = (alloc_func)0;
|
||||
d_stream.zfree = (free_func)0;
|
||||
d_stream.opaque = (voidpf)0;
|
||||
|
||||
d_stream.next_in = in;
|
||||
d_stream.avail_in = static_cast<unsigned int>(inLength);
|
||||
d_stream.next_out = *out;
|
||||
d_stream.avail_out = static_cast<unsigned int>(bufferSize);
|
||||
|
||||
/* window size to hold 256k */
|
||||
if( (err = inflateInit2(&d_stream, 15 + 32)) != Z_OK )
|
||||
return err;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
err = inflate(&d_stream, Z_NO_FLUSH);
|
||||
|
||||
if (err == Z_STREAM_END)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
switch (err)
|
||||
{
|
||||
case Z_NEED_DICT:
|
||||
err = Z_DATA_ERROR;
|
||||
case Z_DATA_ERROR:
|
||||
case Z_MEM_ERROR:
|
||||
inflateEnd(&d_stream);
|
||||
return err;
|
||||
}
|
||||
|
||||
// not enough memory ?
|
||||
if (err != Z_STREAM_END)
|
||||
{
|
||||
*out = (unsigned char*)realloc(*out, bufferSize * BUFFER_INC_FACTOR);
|
||||
|
||||
/* not enough memory, ouch */
|
||||
if (! *out )
|
||||
{
|
||||
CCLOG("ZipUtils: realloc failed");
|
||||
inflateEnd(&d_stream);
|
||||
return Z_MEM_ERROR;
|
||||
}
|
||||
|
||||
d_stream.next_out = *out + bufferSize;
|
||||
d_stream.avail_out = static_cast<unsigned int>(bufferSize);
|
||||
bufferSize *= BUFFER_INC_FACTOR;
|
||||
}
|
||||
}
|
||||
|
||||
*outLength = bufferSize - d_stream.avail_out;
|
||||
err = inflateEnd(&d_stream);
|
||||
return err;
|
||||
}
|
||||
|
||||
ssize_t ZipUtils::inflateMemoryWithHint(unsigned char *in, ssize_t inLength, unsigned char **out, ssize_t outLengthHint)
|
||||
{
|
||||
ssize_t outLength = 0;
|
||||
int err = inflateMemoryWithHint(in, inLength, out, &outLength, outLengthHint);
|
||||
|
||||
if (err != Z_OK || *out == nullptr) {
|
||||
if (err == Z_MEM_ERROR)
|
||||
{
|
||||
CCLOG("ZipUtils: Out of memory while decompressing map data!");
|
||||
} else
|
||||
if (err == Z_VERSION_ERROR)
|
||||
{
|
||||
CCLOG("ZipUtils: Incompatible zlib version!");
|
||||
} else
|
||||
if (err == Z_DATA_ERROR)
|
||||
{
|
||||
CCLOG("ZipUtils: Incorrect zlib compressed data!");
|
||||
}
|
||||
else
|
||||
{
|
||||
CCLOG("ZipUtils: Unknown error while decompressing map data!");
|
||||
}
|
||||
|
||||
if(*out) {
|
||||
free(*out);
|
||||
*out = nullptr;
|
||||
}
|
||||
outLength = 0;
|
||||
}
|
||||
|
||||
return outLength;
|
||||
}
|
||||
|
||||
ssize_t ZipUtils::inflateMemory(unsigned char *in, ssize_t inLength, unsigned char **out)
|
||||
{
|
||||
// 256k for hint
|
||||
return inflateMemoryWithHint(in, inLength, out, 256 * 1024);
|
||||
}
|
||||
|
||||
int ZipUtils::inflateGZipFile(const char *path, unsigned char **out)
|
||||
{
|
||||
int len;
|
||||
unsigned int offset = 0;
|
||||
|
||||
CCASSERT(out, "out can't be nullptr.");
|
||||
CCASSERT(&*out, "&*out can't be nullptr.");
|
||||
|
||||
gzFile inFile = gzopen(FileUtils::getInstance()->getSuitableFOpen(path).c_str(), "rb");
|
||||
if( inFile == nullptr ) {
|
||||
CCLOG("ZipUtils: error open gzip file: %s", path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* 512k initial decompress buffer */
|
||||
unsigned int bufferSize = 512 * 1024;
|
||||
unsigned int totalBufferSize = bufferSize;
|
||||
|
||||
*out = (unsigned char*)malloc( bufferSize );
|
||||
if( ! out )
|
||||
{
|
||||
CCLOG("ZipUtils: out of memory");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
len = gzread(inFile, *out + offset, bufferSize);
|
||||
if (len < 0)
|
||||
{
|
||||
CCLOG("ZipUtils: error in gzread");
|
||||
free( *out );
|
||||
*out = nullptr;
|
||||
return -1;
|
||||
}
|
||||
if (len == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
offset += len;
|
||||
|
||||
// finish reading the file
|
||||
if( (unsigned int)len < bufferSize )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
bufferSize *= BUFFER_INC_FACTOR;
|
||||
totalBufferSize += bufferSize;
|
||||
unsigned char *tmp = (unsigned char*)realloc(*out, totalBufferSize );
|
||||
|
||||
if( ! tmp )
|
||||
{
|
||||
CCLOG("ZipUtils: out of memory");
|
||||
free( *out );
|
||||
*out = nullptr;
|
||||
return -1;
|
||||
}
|
||||
|
||||
*out = tmp;
|
||||
}
|
||||
|
||||
if (gzclose(inFile) != Z_OK)
|
||||
{
|
||||
CCLOG("ZipUtils: gzclose failed");
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
bool ZipUtils::isCCZFile(const char *path)
|
||||
{
|
||||
// load file into memory
|
||||
Data compressedData = FileUtils::getInstance()->getDataFromFile(path);
|
||||
|
||||
if (compressedData.isNull())
|
||||
{
|
||||
CCLOG("ZipUtils: loading file failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
return isCCZBuffer(compressedData.getBytes(), compressedData.getSize());
|
||||
}
|
||||
|
||||
bool ZipUtils::isCCZBuffer(const unsigned char *buffer, ssize_t len)
|
||||
{
|
||||
if (static_cast<size_t>(len) < sizeof(struct CCZHeader))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
struct CCZHeader *header = (struct CCZHeader*) buffer;
|
||||
return header->sig[0] == 'C' && header->sig[1] == 'C' && header->sig[2] == 'Z' && (header->sig[3] == '!' || header->sig[3] == 'p');
|
||||
}
|
||||
|
||||
|
||||
bool ZipUtils::isGZipFile(const char *path)
|
||||
{
|
||||
// load file into memory
|
||||
Data compressedData = FileUtils::getInstance()->getDataFromFile(path);
|
||||
|
||||
if (compressedData.isNull())
|
||||
{
|
||||
CCLOG("ZipUtils: loading file failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
return isGZipBuffer(compressedData.getBytes(), compressedData.getSize());
|
||||
}
|
||||
|
||||
bool ZipUtils::isGZipBuffer(const unsigned char *buffer, ssize_t len)
|
||||
{
|
||||
if (len < 2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return buffer[0] == 0x1F && buffer[1] == 0x8B;
|
||||
}
|
||||
|
||||
|
||||
int ZipUtils::inflateCCZBuffer(const unsigned char *buffer, ssize_t bufferLen, unsigned char **out)
|
||||
{
|
||||
struct CCZHeader *header = (struct CCZHeader*) buffer;
|
||||
|
||||
// verify header
|
||||
if( header->sig[0] == 'C' && header->sig[1] == 'C' && header->sig[2] == 'Z' && header->sig[3] == '!' )
|
||||
{
|
||||
// verify header version
|
||||
unsigned int version = CC_SWAP_INT16_BIG_TO_HOST( header->version );
|
||||
if( version > 2 )
|
||||
{
|
||||
CCLOG("Unsupported CCZ header format");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// verify compression format
|
||||
if( CC_SWAP_INT16_BIG_TO_HOST(header->compression_type) != CCZ_COMPRESSION_ZLIB )
|
||||
{
|
||||
CCLOG("CCZ Unsupported compression method");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else if( header->sig[0] == 'C' && header->sig[1] == 'C' && header->sig[2] == 'Z' && header->sig[3] == 'p' )
|
||||
{
|
||||
// encrypted ccz file
|
||||
header = (struct CCZHeader*) buffer;
|
||||
|
||||
// verify header version
|
||||
unsigned int version = CC_SWAP_INT16_BIG_TO_HOST( header->version );
|
||||
if( version > 0 )
|
||||
{
|
||||
CCLOG("Unsupported CCZ header format");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// verify compression format
|
||||
if( CC_SWAP_INT16_BIG_TO_HOST(header->compression_type) != CCZ_COMPRESSION_ZLIB )
|
||||
{
|
||||
CCLOG("CCZ Unsupported compression method");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// decrypt
|
||||
unsigned int* ints = (unsigned int*)(buffer+12);
|
||||
ssize_t enclen = (bufferLen-12)/4;
|
||||
|
||||
decodeEncodedPvr(ints, enclen);
|
||||
|
||||
#if COCOS2D_DEBUG > 0
|
||||
// verify checksum in debug mode
|
||||
unsigned int calculated = checksumPvr(ints, enclen);
|
||||
unsigned int required = CC_SWAP_INT32_BIG_TO_HOST( header->reserved );
|
||||
|
||||
if(calculated != required)
|
||||
{
|
||||
CCLOG("Can't decrypt image file. Is the decryption key valid?");
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
CCLOG("Invalid CCZ file");
|
||||
return -1;
|
||||
}
|
||||
|
||||
unsigned int len = CC_SWAP_INT32_BIG_TO_HOST( header->len );
|
||||
|
||||
*out = (unsigned char*)malloc( len );
|
||||
if(! *out )
|
||||
{
|
||||
CCLOG("CCZ: Failed to allocate memory for texture");
|
||||
return -1;
|
||||
}
|
||||
|
||||
unsigned long destlen = len;
|
||||
size_t source = (size_t) buffer + sizeof(*header);
|
||||
int ret = uncompress(*out, &destlen, (Bytef*)source, bufferLen - sizeof(*header) );
|
||||
|
||||
if( ret != Z_OK )
|
||||
{
|
||||
CCLOG("CCZ: Failed to uncompress data");
|
||||
free( *out );
|
||||
*out = nullptr;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
int ZipUtils::inflateCCZFile(const char *path, unsigned char **out)
|
||||
{
|
||||
CCASSERT(out, "Invalid pointer for buffer!");
|
||||
|
||||
// load file into memory
|
||||
Data compressedData = FileUtils::getInstance()->getDataFromFile(path);
|
||||
|
||||
if (compressedData.isNull())
|
||||
{
|
||||
CCLOG("Error loading CCZ compressed file");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return inflateCCZBuffer(compressedData.getBytes(), compressedData.getSize(), out);
|
||||
}
|
||||
|
||||
void ZipUtils::setPvrEncryptionKeyPart(int index, unsigned int value)
|
||||
{
|
||||
CCASSERT(index >= 0, "key part index cannot be less than 0");
|
||||
CCASSERT(index <= 3, "key part index cannot be greater than 3");
|
||||
|
||||
if(s_uEncryptedPvrKeyParts[index] != value)
|
||||
{
|
||||
s_uEncryptedPvrKeyParts[index] = value;
|
||||
s_bEncryptionKeyIsValid = false;
|
||||
}
|
||||
}
|
||||
|
||||
void ZipUtils::setPvrEncryptionKey(unsigned int keyPart1, unsigned int keyPart2, unsigned int keyPart3, unsigned int keyPart4)
|
||||
{
|
||||
setPvrEncryptionKeyPart(0, keyPart1);
|
||||
setPvrEncryptionKeyPart(1, keyPart2);
|
||||
setPvrEncryptionKeyPart(2, keyPart3);
|
||||
setPvrEncryptionKeyPart(3, keyPart4);
|
||||
}
|
||||
|
||||
// --------------------- ZipFile ---------------------
|
||||
// from unzip.cpp
|
||||
#define UNZ_MAXFILENAMEINZIP 256
|
||||
|
||||
static const std::string emptyFilename("");
|
||||
|
||||
struct ZipEntryInfo
|
||||
{
|
||||
unz_file_pos pos;
|
||||
uLong uncompressed_size;
|
||||
};
|
||||
|
||||
class ZipFilePrivate
|
||||
{
|
||||
public:
|
||||
unzFile zipFile;
|
||||
|
||||
// std::unordered_map is faster if available on the platform
|
||||
typedef std::unordered_map<std::string, struct ZipEntryInfo> FileListContainer;
|
||||
FileListContainer fileList;
|
||||
};
|
||||
|
||||
ZipFile *ZipFile::createWithBuffer(const void* buffer, uLong size)
|
||||
{
|
||||
ZipFile *zip = new (std::nothrow) ZipFile();
|
||||
if (zip && zip->initWithBuffer(buffer, size)) {
|
||||
return zip;
|
||||
} else {
|
||||
if (zip) delete zip;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
ZipFile::ZipFile()
|
||||
: _data(new ZipFilePrivate)
|
||||
{
|
||||
_data->zipFile = nullptr;
|
||||
}
|
||||
|
||||
ZipFile::ZipFile(const std::string &zipFile, const std::string &filter)
|
||||
: _data(new ZipFilePrivate)
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_readMutex);
|
||||
_data->zipFile = unzOpen(FileUtils::getInstance()->getSuitableFOpen(zipFile).c_str());
|
||||
}
|
||||
setFilter(filter);
|
||||
}
|
||||
|
||||
ZipFile::~ZipFile()
|
||||
{
|
||||
if (_data && _data->zipFile)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_readMutex);
|
||||
unzClose(_data->zipFile);
|
||||
}
|
||||
|
||||
CC_SAFE_DELETE(_data);
|
||||
}
|
||||
|
||||
bool ZipFile::setFilter(const std::string &filter)
|
||||
{
|
||||
bool ret = false;
|
||||
do
|
||||
{
|
||||
CC_BREAK_IF(!_data);
|
||||
CC_BREAK_IF(!_data->zipFile);
|
||||
std::lock_guard<std::mutex> lock(_readMutex);
|
||||
// clear existing file list
|
||||
_data->fileList.clear();
|
||||
|
||||
// UNZ_MAXFILENAMEINZIP + 1 - it is done so in unzLocateFile
|
||||
char szCurrentFileName[UNZ_MAXFILENAMEINZIP + 1];
|
||||
unz_file_info64 fileInfo;
|
||||
|
||||
// go through all files and store position information about the required files
|
||||
int err = unzGoToFirstFile64(_data->zipFile, &fileInfo,
|
||||
szCurrentFileName, sizeof(szCurrentFileName) - 1);
|
||||
while (err == UNZ_OK)
|
||||
{
|
||||
unz_file_pos posInfo;
|
||||
int posErr = unzGetFilePos(_data->zipFile, &posInfo);
|
||||
if (posErr == UNZ_OK)
|
||||
{
|
||||
std::string currentFileName = szCurrentFileName;
|
||||
// cache info about filtered files only (like 'assets/')
|
||||
if (filter.empty()
|
||||
|| currentFileName.substr(0, filter.length()) == filter)
|
||||
{
|
||||
ZipEntryInfo entry;
|
||||
entry.pos = posInfo;
|
||||
entry.uncompressed_size = (uLong)fileInfo.uncompressed_size;
|
||||
_data->fileList[currentFileName] = entry;
|
||||
}
|
||||
}
|
||||
// next file - also get the information about it
|
||||
err = unzGoToNextFile64(_data->zipFile, &fileInfo,
|
||||
szCurrentFileName, sizeof(szCurrentFileName) - 1);
|
||||
}
|
||||
ret = true;
|
||||
|
||||
} while(false);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool ZipFile::fileExists(const std::string &fileName) const
|
||||
{
|
||||
bool ret = false;
|
||||
do
|
||||
{
|
||||
CC_BREAK_IF(!_data);
|
||||
|
||||
ret = _data->fileList.find(fileName) != _data->fileList.end();
|
||||
} while(false);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
unsigned char *ZipFile::getFileData(const std::string &fileName, ssize_t *size)
|
||||
{
|
||||
unsigned char * buffer = nullptr;
|
||||
if (size)
|
||||
*size = 0;
|
||||
|
||||
do
|
||||
{
|
||||
CC_BREAK_IF(!_data->zipFile);
|
||||
CC_BREAK_IF(fileName.empty());
|
||||
|
||||
std::lock_guard<std::mutex> lock(_readMutex);
|
||||
ZipFilePrivate::FileListContainer::const_iterator it = _data->fileList.find(fileName);
|
||||
CC_BREAK_IF(it == _data->fileList.end());
|
||||
|
||||
ZipEntryInfo fileInfo = it->second;
|
||||
|
||||
int nRet = unzGoToFilePos(_data->zipFile, &fileInfo.pos);
|
||||
CC_BREAK_IF(UNZ_OK != nRet);
|
||||
|
||||
nRet = unzOpenCurrentFile(_data->zipFile);
|
||||
CC_BREAK_IF(UNZ_OK != nRet);
|
||||
|
||||
buffer = (unsigned char*)malloc(fileInfo.uncompressed_size);
|
||||
int CC_UNUSED nSize = unzReadCurrentFile(_data->zipFile, buffer, static_cast<unsigned int>(fileInfo.uncompressed_size));
|
||||
CCASSERT(nSize == 0 || nSize == (int)fileInfo.uncompressed_size, "the file size is wrong");
|
||||
|
||||
if (size)
|
||||
{
|
||||
*size = fileInfo.uncompressed_size;
|
||||
}
|
||||
unzCloseCurrentFile(_data->zipFile);
|
||||
} while (0);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
bool ZipFile::getFileData(const std::string &fileName, ResizableBuffer* buffer)
|
||||
{
|
||||
bool res = false;
|
||||
do
|
||||
{
|
||||
CC_BREAK_IF(!_data->zipFile);
|
||||
CC_BREAK_IF(fileName.empty());
|
||||
|
||||
std::lock_guard<std::mutex> lock(_readMutex);
|
||||
ZipFilePrivate::FileListContainer::const_iterator it = _data->fileList.find(fileName);
|
||||
CC_BREAK_IF(it == _data->fileList.end());
|
||||
|
||||
ZipEntryInfo fileInfo = it->second;
|
||||
|
||||
int nRet = unzGoToFilePos(_data->zipFile, &fileInfo.pos);
|
||||
CC_BREAK_IF(UNZ_OK != nRet);
|
||||
|
||||
nRet = unzOpenCurrentFile(_data->zipFile);
|
||||
CC_BREAK_IF(UNZ_OK != nRet);
|
||||
|
||||
buffer->resize(fileInfo.uncompressed_size);
|
||||
int CC_UNUSED nSize = unzReadCurrentFile(_data->zipFile, buffer->buffer(), static_cast<unsigned int>(fileInfo.uncompressed_size));
|
||||
CCASSERT(nSize == 0 || nSize == (int)fileInfo.uncompressed_size, "the file size is wrong");
|
||||
unzCloseCurrentFile(_data->zipFile);
|
||||
res = true;
|
||||
} while (0);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
std::string ZipFile::getFirstFilename()
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_readMutex);
|
||||
if (unzGoToFirstFile(_data->zipFile) != UNZ_OK) return emptyFilename;
|
||||
}
|
||||
std::string path;
|
||||
unz_file_info info;
|
||||
getCurrentFileInfo(&path, &info);
|
||||
return path;
|
||||
}
|
||||
|
||||
std::string ZipFile::getNextFilename()
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_readMutex);
|
||||
if (unzGoToNextFile(_data->zipFile) != UNZ_OK) return emptyFilename;
|
||||
}
|
||||
std::string path;
|
||||
unz_file_info info;
|
||||
getCurrentFileInfo(&path, &info);
|
||||
return path;
|
||||
}
|
||||
|
||||
int ZipFile::getCurrentFileInfo(std::string *filename, unz_file_info *info)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_readMutex);
|
||||
char path[FILENAME_MAX + 1];
|
||||
int ret = unzGetCurrentFileInfo(_data->zipFile, info, path, sizeof(path), nullptr, 0, nullptr, 0);
|
||||
if (ret != UNZ_OK) {
|
||||
*filename = emptyFilename;
|
||||
} else {
|
||||
filename->assign(path);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool ZipFile::initWithBuffer(const void *buffer, uLong size)
|
||||
{
|
||||
if (!buffer || size == 0) return false;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_readMutex);
|
||||
_data->zipFile = unzOpenBuffer(buffer, size);
|
||||
}
|
||||
if (!_data->zipFile) return false;
|
||||
|
||||
setFilter(emptyFilename);
|
||||
return true;
|
||||
}
|
||||
|
||||
NS_CC_END
|
||||
299
cocos2d-x/cocos/base/ZipUtils.h
Normal file
299
cocos2d-x/cocos/base/ZipUtils.h
Normal file
@@ -0,0 +1,299 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2010-2012 cocos2d-x.org
|
||||
Copyright (c) 2013-2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
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 __SUPPORT_ZIPUTILS_H__
|
||||
#define __SUPPORT_ZIPUTILS_H__
|
||||
/// @cond DO_NOT_SHOW
|
||||
|
||||
#include "base/ccMacros.h"
|
||||
#include "platform/CCFileUtils.h"
|
||||
#include <string>
|
||||
#include <mutex>
|
||||
|
||||
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
|
||||
#include "platform/android/CCFileUtils-android.h"
|
||||
#elif(CC_TARGET_PLATFORM == CC_PLATFORM_WIN32) || (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT)
|
||||
// for import ssize_t on win32 platform
|
||||
#include "platform/CCStdC.h"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @addtogroup base
|
||||
* @{
|
||||
*/
|
||||
|
||||
namespace cocos2d
|
||||
{
|
||||
#ifndef _unz64_H
|
||||
typedef struct unz_file_info_s unz_file_info;
|
||||
#endif
|
||||
/** XXX: pragma pack ???
|
||||
* @struct CCZHeader
|
||||
*/
|
||||
struct CCZHeader {
|
||||
unsigned char sig[4]; /** Signature. Should be 'CCZ!' 4 bytes. */
|
||||
unsigned short compression_type; /** Should be 0. */
|
||||
unsigned short version; /** Should be 2 (although version type==1 is also supported). */
|
||||
unsigned int reserved; /** Reserved for users. */
|
||||
unsigned int len; /** Size of the uncompressed file. */
|
||||
};
|
||||
|
||||
enum {
|
||||
CCZ_COMPRESSION_ZLIB, /** zlib format. */
|
||||
CCZ_COMPRESSION_BZIP2, /** bzip2 format (not supported yet). */
|
||||
CCZ_COMPRESSION_GZIP, /** gzip format (not supported yet). */
|
||||
CCZ_COMPRESSION_NONE, /** plain (not supported yet). */
|
||||
};
|
||||
|
||||
class CC_DLL ZipUtils
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Inflates either zlib or gzip deflated memory. The inflated memory is expected to be freed by the caller.
|
||||
*
|
||||
* It will allocate 256k for the destination buffer. If it is not enough it will multiply the previous buffer size per 2, until there is enough memory.
|
||||
*
|
||||
* @return The length of the deflated buffer.
|
||||
* @since v0.8.1
|
||||
*/
|
||||
static ssize_t inflateMemory(unsigned char *in, ssize_t inLength, unsigned char **out);
|
||||
|
||||
/**
|
||||
* Inflates either zlib or gzip deflated memory. The inflated memory is expected to be freed by the caller.
|
||||
*
|
||||
* @param outLenghtHint It is assumed to be the needed room to allocate the inflated buffer.
|
||||
*
|
||||
* @return The length of the deflated buffer.
|
||||
* @since v1.0.0
|
||||
*/
|
||||
static ssize_t inflateMemoryWithHint(unsigned char *in, ssize_t inLength, unsigned char **out, ssize_t outLengthHint);
|
||||
|
||||
/**
|
||||
* Inflates a GZip file into memory.
|
||||
*
|
||||
* @return The length of the deflated buffer.
|
||||
* @since v0.99.5
|
||||
*/
|
||||
static int inflateGZipFile(const char *filename, unsigned char **out);
|
||||
|
||||
/**
|
||||
* Test a file is a GZip format file or not.
|
||||
*
|
||||
* @return True is a GZip format file. false is not.
|
||||
* @since v3.0
|
||||
*/
|
||||
static bool isGZipFile(const char *filename);
|
||||
|
||||
/**
|
||||
* Test the buffer is GZip format or not.
|
||||
*
|
||||
* @return True is GZip format. false is not.
|
||||
* @since v3.0
|
||||
*/
|
||||
static bool isGZipBuffer(const unsigned char *buffer, ssize_t len);
|
||||
|
||||
/**
|
||||
* Inflates a CCZ file into memory.
|
||||
*
|
||||
* @return The length of the deflated buffer.
|
||||
* @since v0.99.5
|
||||
*/
|
||||
static int inflateCCZFile(const char *filename, unsigned char **out);
|
||||
|
||||
/**
|
||||
* Inflates a buffer with CCZ format into memory.
|
||||
*
|
||||
* @return The length of the deflated buffer.
|
||||
* @since v3.0
|
||||
*/
|
||||
static int inflateCCZBuffer(const unsigned char *buffer, ssize_t len, unsigned char **out);
|
||||
|
||||
/**
|
||||
* Test a file is a CCZ format file or not.
|
||||
*
|
||||
* @return True is a CCZ format file. false is not.
|
||||
* @since v3.0
|
||||
*/
|
||||
static bool isCCZFile(const char *filename);
|
||||
|
||||
/**
|
||||
* Test the buffer is CCZ format or not.
|
||||
*
|
||||
* @return True is CCZ format. false is not.
|
||||
* @since v3.0
|
||||
*/
|
||||
static bool isCCZBuffer(const unsigned char *buffer, ssize_t len);
|
||||
|
||||
/**
|
||||
* Sets the pvr.ccz encryption key parts separately for added security.
|
||||
*
|
||||
* Example: If the key used to encrypt the pvr.ccz file is
|
||||
* 0xaaaaaaaabbbbbbbbccccccccdddddddd you will call this function 4
|
||||
* different times, preferably from 4 different source files, as follows
|
||||
*
|
||||
* ZipUtils::setPvrEncryptionKeyPart(0, 0xaaaaaaaa);
|
||||
* ZipUtils::setPvrEncryptionKeyPart(1, 0xbbbbbbbb);
|
||||
* ZipUtils::setPvrEncryptionKeyPart(2, 0xcccccccc);
|
||||
* ZipUtils::setPvrEncryptionKeyPart(3, 0xdddddddd);
|
||||
*
|
||||
* Splitting the key into 4 parts and calling the function from 4 different source
|
||||
* files increases the difficulty to reverse engineer the encryption key.
|
||||
* Be aware that encryption is *never* 100% secure and the key code
|
||||
* can be cracked by knowledgable persons.
|
||||
*
|
||||
* IMPORTANT: Be sure to call setPvrEncryptionKey or
|
||||
* setPvrEncryptionKeyPart with all of the key parts *before* loading
|
||||
* the sprite sheet or decryption will fail and the sprite sheet
|
||||
* will fail to load.
|
||||
*
|
||||
* @param index Part of the key [0..3].
|
||||
* @param value Value of the key part.
|
||||
*/
|
||||
static void setPvrEncryptionKeyPart(int index, unsigned int value);
|
||||
|
||||
/**
|
||||
* Sets the pvr.ccz encryption key.
|
||||
*
|
||||
* Example: If the key used to encrypt the pvr.ccz file is
|
||||
* 0xaaaaaaaabbbbbbbbccccccccdddddddd you will call this function with
|
||||
* the key split into 4 parts as follows
|
||||
*
|
||||
* ZipUtils::setPvrEncryptionKey(0xaaaaaaaa, 0xbbbbbbbb, 0xcccccccc, 0xdddddddd);
|
||||
*
|
||||
* Note that using this function makes it easier to reverse engineer and discover
|
||||
* the complete key because the key parts are present in one function call.
|
||||
*
|
||||
* IMPORTANT: Be sure to call setPvrEncryptionKey or setPvrEncryptionKeyPart
|
||||
* with all of the key parts *before* loading the spritesheet or decryption
|
||||
* will fail and the sprite sheet will fail to load.
|
||||
*
|
||||
* @param keyPart1 The key value part 1.
|
||||
* @param keyPart2 The key value part 2.
|
||||
* @param keyPart3 The key value part 3.
|
||||
* @param keyPart4 The key value part 4.
|
||||
*/
|
||||
static void setPvrEncryptionKey(unsigned int keyPart1, unsigned int keyPart2, unsigned int keyPart3, unsigned int keyPart4);
|
||||
|
||||
private:
|
||||
static int inflateMemoryWithHint(unsigned char *in, ssize_t inLength, unsigned char **out, ssize_t *outLength, ssize_t outLengthHint);
|
||||
static inline void decodeEncodedPvr (unsigned int *data, ssize_t len);
|
||||
static inline unsigned int checksumPvr(const unsigned int *data, ssize_t len);
|
||||
|
||||
static unsigned int s_uEncryptedPvrKeyParts[4];
|
||||
static unsigned int s_uEncryptionKey[1024];
|
||||
static bool s_bEncryptionKeyIsValid;
|
||||
};
|
||||
|
||||
// forward declaration
|
||||
class ZipFilePrivate;
|
||||
struct unz_file_info_s;
|
||||
|
||||
/**
|
||||
* Zip file - reader helper class.
|
||||
*
|
||||
* It will cache the file list of a particular zip file with positions inside an archive,
|
||||
* so it would be much faster to read some particular files or to check their existence.
|
||||
*
|
||||
* @since v2.0.5
|
||||
*/
|
||||
class CC_DLL ZipFile
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor, open zip file and store file list.
|
||||
*
|
||||
* @param zipFile Zip file name
|
||||
* @param filter The first part of file names, which should be accessible.
|
||||
* For example, "@assets/". Other files will be missed.
|
||||
*
|
||||
* @since v2.0.5
|
||||
*/
|
||||
ZipFile(const std::string &zipFile, const std::string &filter = std::string());
|
||||
virtual ~ZipFile();
|
||||
|
||||
/**
|
||||
* Regenerate accessible file list based on a new filter string.
|
||||
*
|
||||
* @param filter New filter string (first part of files names)
|
||||
* @return true whenever zip file is open successfully and it is possible to locate
|
||||
* at least the first file, false otherwise
|
||||
*
|
||||
* @since v2.0.5
|
||||
*/
|
||||
bool setFilter(const std::string &filter);
|
||||
|
||||
/**
|
||||
* Check does a file exists or not in zip file
|
||||
*
|
||||
* @param fileName File to be checked on existence
|
||||
* @return true whenever file exists, false otherwise
|
||||
*
|
||||
* @since v2.0.5
|
||||
*/
|
||||
bool fileExists(const std::string &fileName) const;
|
||||
|
||||
/**
|
||||
* Get resource file data from a zip file.
|
||||
* @param fileName File name
|
||||
* @param[out] pSize If the file read operation succeeds, it will be the data size, otherwise 0.
|
||||
* @return Upon success, a pointer to the data is returned, otherwise nullptr.
|
||||
* @warning Recall: you are responsible for calling free() on any Non-nullptr pointer returned.
|
||||
*
|
||||
* @since v2.0.5
|
||||
*/
|
||||
unsigned char *getFileData(const std::string &fileName, ssize_t *size);
|
||||
|
||||
/**
|
||||
* Get resource file data from a zip file.
|
||||
* @param fileName File name
|
||||
* @param[out] buffer If the file read operation succeeds, if will contain the file data.
|
||||
* @return True if successful.
|
||||
*/
|
||||
bool getFileData(const std::string &fileName, ResizableBuffer* buffer);
|
||||
|
||||
std::string getFirstFilename();
|
||||
std::string getNextFilename();
|
||||
|
||||
static ZipFile *createWithBuffer(const void* buffer, unsigned long size);
|
||||
|
||||
private:
|
||||
/* Only used internal for createWithBuffer() */
|
||||
ZipFile();
|
||||
|
||||
bool initWithBuffer(const void *buffer, unsigned long size);
|
||||
int getCurrentFileInfo(std::string *filename, unz_file_info *info);
|
||||
|
||||
/** Internal data like zip file pointer / file list array and so on */
|
||||
ZipFilePrivate *_data;
|
||||
std::mutex _readMutex;
|
||||
};
|
||||
} // end of namespace cocos2d
|
||||
|
||||
// end group
|
||||
/// @}
|
||||
|
||||
/// @endcond
|
||||
#endif // __SUPPORT_ZIPUTILS_H__
|
||||
172
cocos2d-x/cocos/base/base64.cpp
Normal file
172
cocos2d-x/cocos/base/base64.cpp
Normal file
@@ -0,0 +1,172 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2010-2012 cocos2d-x.org
|
||||
Copyright (c) 2013-2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "base/base64.h"
|
||||
|
||||
namespace cocos2d {
|
||||
|
||||
unsigned char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
int _base64Decode(const unsigned char *input, unsigned int input_len, unsigned char *output, unsigned int *output_len )
|
||||
{
|
||||
static char inalphabet[256], decoder[256];
|
||||
int i, bits, c = 0, char_count, errors = 0;
|
||||
unsigned int input_idx = 0;
|
||||
unsigned int output_idx = 0;
|
||||
|
||||
for (i = (sizeof alphabet) - 1; i >= 0 ; i--) {
|
||||
inalphabet[alphabet[i]] = 1;
|
||||
decoder[alphabet[i]] = i;
|
||||
}
|
||||
|
||||
char_count = 0;
|
||||
bits = 0;
|
||||
for( input_idx=0; input_idx < input_len ; input_idx++ ) {
|
||||
c = input[ input_idx ];
|
||||
if (c == '=')
|
||||
break;
|
||||
if (c > 255 || ! inalphabet[c])
|
||||
continue;
|
||||
bits += decoder[c];
|
||||
char_count++;
|
||||
if (char_count == 4) {
|
||||
output[ output_idx++ ] = (bits >> 16);
|
||||
output[ output_idx++ ] = ((bits >> 8) & 0xff);
|
||||
output[ output_idx++ ] = ( bits & 0xff);
|
||||
bits = 0;
|
||||
char_count = 0;
|
||||
} else {
|
||||
bits <<= 6;
|
||||
}
|
||||
}
|
||||
|
||||
if( c == '=' ) {
|
||||
switch (char_count) {
|
||||
case 1:
|
||||
#if (CC_TARGET_PLATFORM != CC_PLATFORM_BADA)
|
||||
fprintf(stderr, "base64Decode: encoding incomplete: at least 2 bits missing");
|
||||
#endif
|
||||
errors++;
|
||||
break;
|
||||
case 2:
|
||||
output[ output_idx++ ] = ( bits >> 10 );
|
||||
break;
|
||||
case 3:
|
||||
output[ output_idx++ ] = ( bits >> 16 );
|
||||
output[ output_idx++ ] = (( bits >> 8 ) & 0xff);
|
||||
break;
|
||||
}
|
||||
} else if ( input_idx < input_len ) {
|
||||
if (char_count) {
|
||||
#if (CC_TARGET_PLATFORM != CC_PLATFORM_BADA)
|
||||
fprintf(stderr, "base64 encoding incomplete: at least %d bits truncated",
|
||||
((4 - char_count) * 6));
|
||||
#endif
|
||||
errors++;
|
||||
}
|
||||
}
|
||||
|
||||
*output_len = output_idx;
|
||||
return errors;
|
||||
}
|
||||
|
||||
void _base64Encode( const unsigned char *input, unsigned int input_len, char *output )
|
||||
{
|
||||
unsigned int char_count;
|
||||
unsigned int bits;
|
||||
unsigned int input_idx = 0;
|
||||
unsigned int output_idx = 0;
|
||||
|
||||
char_count = 0;
|
||||
bits = 0;
|
||||
for( input_idx=0; input_idx < input_len ; input_idx++ ) {
|
||||
bits |= input[ input_idx ];
|
||||
|
||||
char_count++;
|
||||
if (char_count == 3) {
|
||||
output[ output_idx++ ] = alphabet[(bits >> 18) & 0x3f];
|
||||
output[ output_idx++ ] = alphabet[(bits >> 12) & 0x3f];
|
||||
output[ output_idx++ ] = alphabet[(bits >> 6) & 0x3f];
|
||||
output[ output_idx++ ] = alphabet[bits & 0x3f];
|
||||
bits = 0;
|
||||
char_count = 0;
|
||||
} else {
|
||||
bits <<= 8;
|
||||
}
|
||||
}
|
||||
|
||||
if (char_count) {
|
||||
if (char_count == 1) {
|
||||
bits <<= 8;
|
||||
}
|
||||
|
||||
output[ output_idx++ ] = alphabet[(bits >> 18) & 0x3f];
|
||||
output[ output_idx++ ] = alphabet[(bits >> 12) & 0x3f];
|
||||
if (char_count > 1) {
|
||||
output[ output_idx++ ] = alphabet[(bits >> 6) & 0x3f];
|
||||
} else {
|
||||
output[ output_idx++ ] = '=';
|
||||
}
|
||||
output[ output_idx++ ] = '=';
|
||||
}
|
||||
|
||||
output[ output_idx++ ] = 0;
|
||||
}
|
||||
|
||||
int base64Decode(const unsigned char *in, unsigned int inLength, unsigned char **out)
|
||||
{
|
||||
unsigned int outLength = 0;
|
||||
|
||||
//should be enough to store 6-bit buffers in 8-bit buffers
|
||||
*out = (unsigned char*)malloc(inLength / 4 * 3 + 1);
|
||||
if( *out ) {
|
||||
int ret = _base64Decode(in, inLength, *out, &outLength);
|
||||
|
||||
if (ret > 0)
|
||||
{
|
||||
free(*out);
|
||||
*out = nullptr;
|
||||
outLength = 0;
|
||||
}
|
||||
}
|
||||
return outLength;
|
||||
}
|
||||
|
||||
int base64Encode(const unsigned char *in, unsigned int inLength, char **out) {
|
||||
unsigned int outLength = (inLength + 2) / 3 * 4;
|
||||
|
||||
//should be enough to store 8-bit buffers in 6-bit buffers
|
||||
*out = (char*)malloc(outLength+1);
|
||||
if( *out ) {
|
||||
_base64Encode(in, inLength, *out);
|
||||
}
|
||||
return outLength;
|
||||
}
|
||||
|
||||
}//namespace cocos2d
|
||||
|
||||
71
cocos2d-x/cocos/base/base64.h
Normal file
71
cocos2d-x/cocos/base/base64.h
Normal file
@@ -0,0 +1,71 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2010-2012 cocos2d-x.org
|
||||
Copyright (c) 2013-2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
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 __SUPPORT_BASE64_H__
|
||||
#define __SUPPORT_BASE64_H__
|
||||
/// @cond DO_NOT_SHOW
|
||||
|
||||
#include "base/ccMacros.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
namespace cocos2d {
|
||||
|
||||
/** @file
|
||||
base64 helper functions
|
||||
*/
|
||||
|
||||
/**
|
||||
* Decodes a 64base encoded memory. The decoded memory is
|
||||
* expected to be freed by the caller by calling `free()`
|
||||
*
|
||||
* @returns the length of the out buffer
|
||||
*
|
||||
@since v0.8.1
|
||||
*/
|
||||
int CC_DLL base64Decode(const unsigned char *in, unsigned int inLength, unsigned char **out);
|
||||
|
||||
/**
|
||||
* Encodes bytes into a 64base encoded memory with terminating '\0' character.
|
||||
* The encoded memory is expected to be freed by the caller by calling `free()`
|
||||
*
|
||||
* @returns the length of the out buffer
|
||||
*
|
||||
@since v2.1.4
|
||||
*/
|
||||
int CC_DLL base64Encode(const unsigned char *in, unsigned int inLength, char **out);
|
||||
|
||||
}//namespace cocos2d
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/// @endcond
|
||||
#endif // __SUPPORT_BASE64_H__
|
||||
|
||||
475
cocos2d-x/cocos/base/ccCArray.cpp
Normal file
475
cocos2d-x/cocos/base/ccCArray.cpp
Normal file
@@ -0,0 +1,475 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2007 Scott Lembcke
|
||||
Copyright (c) 2010-2012 cocos2d-x.org
|
||||
CopyRight (c) 2013-2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
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 "base/ccCArray.h"
|
||||
#include "base/ccTypes.h"
|
||||
|
||||
NS_CC_BEGIN
|
||||
|
||||
/** Allocates and initializes a new array with specified capacity */
|
||||
ccArray* ccArrayNew(ssize_t capacity)
|
||||
{
|
||||
if (capacity == 0)
|
||||
capacity = 7;
|
||||
|
||||
ccArray *arr = (ccArray*)malloc( sizeof(ccArray) );
|
||||
arr->num = 0;
|
||||
arr->arr = (Ref**)calloc(capacity, sizeof(Ref*));
|
||||
arr->max = capacity;
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
/** Frees array after removing all remaining objects. Silently ignores nullptr arr. */
|
||||
void ccArrayFree(ccArray*& arr)
|
||||
{
|
||||
if( arr == nullptr )
|
||||
{
|
||||
return;
|
||||
}
|
||||
ccArrayRemoveAllObjects(arr);
|
||||
|
||||
free(arr->arr);
|
||||
free(arr);
|
||||
|
||||
arr = nullptr;
|
||||
}
|
||||
|
||||
void ccArrayDoubleCapacity(ccArray *arr)
|
||||
{
|
||||
arr->max *= 2;
|
||||
Ref** newArr = (Ref**)realloc( arr->arr, arr->max * sizeof(Ref*) );
|
||||
// will fail when there's not enough memory
|
||||
CCASSERT(newArr != 0, "ccArrayDoubleCapacity failed. Not enough memory");
|
||||
arr->arr = newArr;
|
||||
}
|
||||
|
||||
void ccArrayEnsureExtraCapacity(ccArray *arr, ssize_t extra)
|
||||
{
|
||||
while (arr->max < arr->num + extra)
|
||||
{
|
||||
CCLOGINFO("ccCArray: resizing ccArray capacity from [%d] to [%d].",
|
||||
static_cast<int>(arr->max),
|
||||
static_cast<int>(arr->max*2));
|
||||
|
||||
ccArrayDoubleCapacity(arr);
|
||||
}
|
||||
}
|
||||
|
||||
void ccArrayShrink(ccArray *arr)
|
||||
{
|
||||
ssize_t newSize = 0;
|
||||
|
||||
//only resize when necessary
|
||||
if (arr->max > arr->num && !(arr->num==0 && arr->max==1))
|
||||
{
|
||||
if (arr->num!=0)
|
||||
{
|
||||
newSize=arr->num;
|
||||
arr->max=arr->num;
|
||||
}
|
||||
else
|
||||
{//minimum capacity of 1, with 0 elements the array would be free'd by realloc
|
||||
newSize=1;
|
||||
arr->max=1;
|
||||
}
|
||||
|
||||
arr->arr = (Ref**)realloc(arr->arr,newSize * sizeof(Ref*) );
|
||||
CCASSERT(arr->arr!=nullptr,"could not reallocate the memory");
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns index of first occurrence of object, CC_INVALID_INDEX if object not found. */
|
||||
ssize_t ccArrayGetIndexOfObject(ccArray *arr, Ref* object)
|
||||
{
|
||||
const auto arrNum = arr->num;
|
||||
Ref** ptr = arr->arr;
|
||||
for (ssize_t i = 0; i < arrNum; ++i, ++ptr)
|
||||
{
|
||||
if (*ptr == object)
|
||||
return i;
|
||||
}
|
||||
|
||||
return CC_INVALID_INDEX;
|
||||
}
|
||||
|
||||
/** Returns a Boolean value that indicates whether object is present in array. */
|
||||
bool ccArrayContainsObject(ccArray *arr, Ref* object)
|
||||
{
|
||||
return ccArrayGetIndexOfObject(arr, object) != CC_INVALID_INDEX;
|
||||
}
|
||||
|
||||
/** Appends an object. Behavior undefined if array doesn't have enough capacity. */
|
||||
void ccArrayAppendObject(ccArray *arr, Ref* object)
|
||||
{
|
||||
CCASSERT(object != nullptr, "Invalid parameter!");
|
||||
object->retain();
|
||||
arr->arr[arr->num] = object;
|
||||
arr->num++;
|
||||
}
|
||||
|
||||
/** Appends an object. Capacity of arr is increased if needed. */
|
||||
void ccArrayAppendObjectWithResize(ccArray *arr, Ref* object)
|
||||
{
|
||||
ccArrayEnsureExtraCapacity(arr, 1);
|
||||
ccArrayAppendObject(arr, object);
|
||||
}
|
||||
|
||||
/** Appends objects from plusArr to arr. Behavior undefined if arr doesn't have
|
||||
enough capacity. */
|
||||
void ccArrayAppendArray(ccArray *arr, ccArray *plusArr)
|
||||
{
|
||||
for (ssize_t i = 0; i < plusArr->num; i++)
|
||||
{
|
||||
ccArrayAppendObject(arr, plusArr->arr[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/** Appends objects from plusArr to arr. Capacity of arr is increased if needed. */
|
||||
void ccArrayAppendArrayWithResize(ccArray *arr, ccArray *plusArr)
|
||||
{
|
||||
ccArrayEnsureExtraCapacity(arr, plusArr->num);
|
||||
ccArrayAppendArray(arr, plusArr);
|
||||
}
|
||||
|
||||
/** Inserts an object at index */
|
||||
void ccArrayInsertObjectAtIndex(ccArray *arr, Ref* object, ssize_t index)
|
||||
{
|
||||
CCASSERT(index<=arr->num, "Invalid index. Out of bounds");
|
||||
CCASSERT(object != nullptr, "Invalid parameter!");
|
||||
|
||||
ccArrayEnsureExtraCapacity(arr, 1);
|
||||
|
||||
ssize_t remaining = arr->num - index;
|
||||
if (remaining > 0)
|
||||
{
|
||||
memmove((void *)&arr->arr[index+1], (void *)&arr->arr[index], sizeof(Ref*) * remaining );
|
||||
}
|
||||
|
||||
object->retain();
|
||||
arr->arr[index] = object;
|
||||
arr->num++;
|
||||
}
|
||||
|
||||
/** Swaps two objects */
|
||||
void ccArraySwapObjectsAtIndexes(ccArray *arr, ssize_t index1, ssize_t index2)
|
||||
{
|
||||
CCASSERT(index1>=0 && index1 < arr->num, "(1) Invalid index. Out of bounds");
|
||||
CCASSERT(index2>=0 && index2 < arr->num, "(2) Invalid index. Out of bounds");
|
||||
|
||||
Ref* object1 = arr->arr[index1];
|
||||
|
||||
arr->arr[index1] = arr->arr[index2];
|
||||
arr->arr[index2] = object1;
|
||||
}
|
||||
|
||||
/** Removes all objects from arr */
|
||||
void ccArrayRemoveAllObjects(ccArray *arr)
|
||||
{
|
||||
while (arr->num > 0)
|
||||
{
|
||||
(arr->arr[--arr->num])->release();
|
||||
}
|
||||
}
|
||||
|
||||
/** Removes object at specified index and pushes back all subsequent objects.
|
||||
Behavior undefined if index outside [0, num-1]. */
|
||||
void ccArrayRemoveObjectAtIndex(ccArray *arr, ssize_t index, bool releaseObj/* = true*/)
|
||||
{
|
||||
CCASSERT(arr && arr->num > 0 && index>=0 && index < arr->num, "Invalid index. Out of bounds");
|
||||
if (releaseObj)
|
||||
{
|
||||
CC_SAFE_RELEASE(arr->arr[index]);
|
||||
}
|
||||
|
||||
arr->num--;
|
||||
|
||||
ssize_t remaining = arr->num - index;
|
||||
if(remaining>0)
|
||||
{
|
||||
memmove((void *)&arr->arr[index], (void *)&arr->arr[index+1], remaining * sizeof(Ref*));
|
||||
}
|
||||
}
|
||||
|
||||
/** Removes object at specified index and fills the gap with the last object,
|
||||
thereby avoiding the need to push back subsequent objects.
|
||||
Behavior undefined if index outside [0, num-1]. */
|
||||
void ccArrayFastRemoveObjectAtIndex(ccArray *arr, ssize_t index)
|
||||
{
|
||||
CC_SAFE_RELEASE(arr->arr[index]);
|
||||
auto last = --arr->num;
|
||||
arr->arr[index] = arr->arr[last];
|
||||
}
|
||||
|
||||
void ccArrayFastRemoveObject(ccArray *arr, Ref* object)
|
||||
{
|
||||
auto index = ccArrayGetIndexOfObject(arr, object);
|
||||
if (index != CC_INVALID_INDEX)
|
||||
{
|
||||
ccArrayFastRemoveObjectAtIndex(arr, index);
|
||||
}
|
||||
}
|
||||
|
||||
/** Searches for the first occurrence of object and removes it. If object is not
|
||||
found the function has no effect. */
|
||||
void ccArrayRemoveObject(ccArray *arr, Ref* object, bool releaseObj/* = true*/)
|
||||
{
|
||||
auto index = ccArrayGetIndexOfObject(arr, object);
|
||||
if (index != CC_INVALID_INDEX)
|
||||
{
|
||||
ccArrayRemoveObjectAtIndex(arr, index, releaseObj);
|
||||
}
|
||||
}
|
||||
|
||||
/** Removes from arr all objects in minusArr. For each object in minusArr, the
|
||||
first matching instance in arr will be removed. */
|
||||
void ccArrayRemoveArray(ccArray *arr, ccArray *minusArr)
|
||||
{
|
||||
for (ssize_t i = 0; i < minusArr->num; i++)
|
||||
{
|
||||
ccArrayRemoveObject(arr, minusArr->arr[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/** Removes from arr all objects in minusArr. For each object in minusArr, all
|
||||
matching instances in arr will be removed. */
|
||||
void ccArrayFullRemoveArray(ccArray *arr, ccArray *minusArr)
|
||||
{
|
||||
ssize_t back = 0;
|
||||
|
||||
for (ssize_t i = 0; i < arr->num; i++)
|
||||
{
|
||||
if (ccArrayContainsObject(minusArr, arr->arr[i]))
|
||||
{
|
||||
CC_SAFE_RELEASE(arr->arr[i]);
|
||||
back++;
|
||||
}
|
||||
else
|
||||
{
|
||||
arr->arr[i - back] = arr->arr[i];
|
||||
}
|
||||
}
|
||||
|
||||
arr->num -= back;
|
||||
}
|
||||
|
||||
//
|
||||
// // ccCArray for Values (c structures)
|
||||
|
||||
/** Allocates and initializes a new C array with specified capacity */
|
||||
ccCArray* ccCArrayNew(ssize_t capacity)
|
||||
{
|
||||
if (capacity == 0)
|
||||
{
|
||||
capacity = 7;
|
||||
}
|
||||
|
||||
ccCArray *arr = (ccCArray*)malloc(sizeof(ccCArray));
|
||||
arr->num = 0;
|
||||
arr->arr = (void**)malloc(capacity * sizeof(void*));
|
||||
arr->max = capacity;
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
/** Frees C array after removing all remaining values. Silently ignores nullptr arr. */
|
||||
void ccCArrayFree(ccCArray *arr)
|
||||
{
|
||||
if (arr == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
ccCArrayRemoveAllValues(arr);
|
||||
|
||||
free(arr->arr);
|
||||
free(arr);
|
||||
}
|
||||
|
||||
/** Doubles C array capacity */
|
||||
void ccCArrayDoubleCapacity(ccCArray *arr)
|
||||
{
|
||||
ccArrayDoubleCapacity((ccArray*)arr);
|
||||
}
|
||||
|
||||
/** Increases array capacity such that max >= num + extra. */
|
||||
void ccCArrayEnsureExtraCapacity(ccCArray *arr, ssize_t extra)
|
||||
{
|
||||
ccArrayEnsureExtraCapacity((ccArray*)arr,extra);
|
||||
}
|
||||
|
||||
/** Returns index of first occurrence of value, CC_INVALID_INDEX if value not found. */
|
||||
ssize_t ccCArrayGetIndexOfValue(ccCArray *arr, void* value)
|
||||
{
|
||||
for(ssize_t i = 0; i < arr->num; i++)
|
||||
{
|
||||
if( arr->arr[i] == value )
|
||||
return i;
|
||||
}
|
||||
return CC_INVALID_INDEX;
|
||||
}
|
||||
|
||||
/** Returns a Boolean value that indicates whether value is present in the C array. */
|
||||
bool ccCArrayContainsValue(ccCArray *arr, void* value)
|
||||
{
|
||||
return ccCArrayGetIndexOfValue(arr, value) != CC_INVALID_INDEX;
|
||||
}
|
||||
|
||||
/** Inserts a value at a certain position. Behavior undefined if array doesn't have enough capacity */
|
||||
void ccCArrayInsertValueAtIndex( ccCArray *arr, void* value, ssize_t index)
|
||||
{
|
||||
CCASSERT( index < arr->max, "ccCArrayInsertValueAtIndex: invalid index");
|
||||
|
||||
auto remaining = arr->num - index;
|
||||
// make sure it has enough capacity
|
||||
if (arr->num + 1 == arr->max)
|
||||
{
|
||||
ccCArrayDoubleCapacity(arr);
|
||||
}
|
||||
// last Value doesn't need to be moved
|
||||
if( remaining > 0) {
|
||||
// tex coordinates
|
||||
memmove((void *)&arr->arr[index+1], (void *)&arr->arr[index], sizeof(void*) * remaining );
|
||||
}
|
||||
|
||||
arr->num++;
|
||||
arr->arr[index] = value;
|
||||
}
|
||||
|
||||
/** Appends an value. Behavior undefined if array doesn't have enough capacity. */
|
||||
void ccCArrayAppendValue(ccCArray *arr, void* value)
|
||||
{
|
||||
arr->arr[arr->num] = value;
|
||||
arr->num++;
|
||||
// double the capacity for the next append action
|
||||
// if the num >= max
|
||||
if (arr->num >= arr->max)
|
||||
{
|
||||
ccCArrayDoubleCapacity(arr);
|
||||
}
|
||||
}
|
||||
|
||||
/** Appends an value. Capacity of arr is increased if needed. */
|
||||
void ccCArrayAppendValueWithResize(ccCArray *arr, void* value)
|
||||
{
|
||||
ccCArrayEnsureExtraCapacity(arr, 1);
|
||||
ccCArrayAppendValue(arr, value);
|
||||
}
|
||||
|
||||
|
||||
/** Appends values from plusArr to arr. Behavior undefined if arr doesn't have
|
||||
enough capacity. */
|
||||
void ccCArrayAppendArray(ccCArray *arr, ccCArray *plusArr)
|
||||
{
|
||||
for( ssize_t i = 0; i < plusArr->num; i++)
|
||||
{
|
||||
ccCArrayAppendValue(arr, plusArr->arr[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/** Appends values from plusArr to arr. Capacity of arr is increased if needed. */
|
||||
void ccCArrayAppendArrayWithResize(ccCArray *arr, ccCArray *plusArr)
|
||||
{
|
||||
ccCArrayEnsureExtraCapacity(arr, plusArr->num);
|
||||
ccCArrayAppendArray(arr, plusArr);
|
||||
}
|
||||
|
||||
/** Removes all values from arr */
|
||||
void ccCArrayRemoveAllValues(ccCArray *arr)
|
||||
{
|
||||
arr->num = 0;
|
||||
}
|
||||
|
||||
/** Removes value at specified index and pushes back all subsequent values.
|
||||
Behavior undefined if index outside [0, num-1].
|
||||
@since v0.99.4
|
||||
*/
|
||||
void ccCArrayRemoveValueAtIndex(ccCArray *arr, ssize_t index)
|
||||
{
|
||||
for( ssize_t last = --arr->num; index < last; index++)
|
||||
{
|
||||
arr->arr[index] = arr->arr[index + 1];
|
||||
}
|
||||
}
|
||||
|
||||
/** Removes value at specified index and fills the gap with the last value,
|
||||
thereby avoiding the need to push back subsequent values.
|
||||
Behavior undefined if index outside [0, num-1].
|
||||
@since v0.99.4
|
||||
*/
|
||||
void ccCArrayFastRemoveValueAtIndex(ccCArray *arr, ssize_t index)
|
||||
{
|
||||
ssize_t last = --arr->num;
|
||||
arr->arr[index] = arr->arr[last];
|
||||
}
|
||||
|
||||
/** Searches for the first occurrence of value and removes it. If value is not found the function has no effect.
|
||||
@since v0.99.4
|
||||
*/
|
||||
void ccCArrayRemoveValue(ccCArray *arr, void* value)
|
||||
{
|
||||
auto index = ccCArrayGetIndexOfValue(arr, value);
|
||||
if (index != CC_INVALID_INDEX)
|
||||
{
|
||||
ccCArrayRemoveValueAtIndex(arr, index);
|
||||
}
|
||||
}
|
||||
|
||||
/** Removes from arr all values in minusArr. For each Value in minusArr, the first matching instance in arr will be removed.
|
||||
@since v0.99.4
|
||||
*/
|
||||
void ccCArrayRemoveArray(ccCArray *arr, ccCArray *minusArr)
|
||||
{
|
||||
for(ssize_t i = 0; i < minusArr->num; i++)
|
||||
{
|
||||
ccCArrayRemoveValue(arr, minusArr->arr[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/** Removes from arr all values in minusArr. For each value in minusArr, all matching instances in arr will be removed.
|
||||
@since v0.99.4
|
||||
*/
|
||||
void ccCArrayFullRemoveArray(ccCArray *arr, ccCArray *minusArr)
|
||||
{
|
||||
ssize_t back = 0;
|
||||
|
||||
for(ssize_t i = 0; i < arr->num; i++)
|
||||
{
|
||||
if( ccCArrayContainsValue(minusArr, arr->arr[i]) )
|
||||
{
|
||||
back++;
|
||||
}
|
||||
else
|
||||
{
|
||||
arr->arr[i - back] = arr->arr[i];
|
||||
}
|
||||
}
|
||||
|
||||
arr->num -= back;
|
||||
}
|
||||
|
||||
NS_CC_END
|
||||
|
||||
210
cocos2d-x/cocos/base/ccCArray.h
Normal file
210
cocos2d-x/cocos/base/ccCArray.h
Normal file
@@ -0,0 +1,210 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2007 Scott Lembcke
|
||||
Copyright (c) 2010-2012 cocos2d-x.org
|
||||
Copyright (c) 2013-2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
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.
|
||||
****************************************************************************/
|
||||
|
||||
/**
|
||||
@file
|
||||
based on Chipmunk cpArray.
|
||||
ccArray is a faster alternative to NSMutableArray, it does pretty much the
|
||||
same thing (stores NSObjects and retains/releases them appropriately). It's
|
||||
faster because:
|
||||
- it uses a plain C interface so it doesn't incur Objective-c messaging overhead
|
||||
- it assumes you know what you're doing, so it doesn't spend time on safety checks
|
||||
(index out of bounds, required capacity etc.)
|
||||
- comparisons are done using pointer equality instead of isEqual
|
||||
|
||||
There are 2 kind of functions:
|
||||
- ccArray functions that manipulates objective-c objects (retain and release are performed)
|
||||
- ccCArray functions that manipulates values like if they were standard C structures (no retain/release is performed)
|
||||
*/
|
||||
|
||||
#ifndef CC_ARRAY_H
|
||||
#define CC_ARRAY_H
|
||||
/// @cond DO_NOT_SHOW
|
||||
|
||||
#include "base/ccMacros.h"
|
||||
#include "base/CCRef.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
|
||||
NS_CC_BEGIN
|
||||
|
||||
// Easy integration
|
||||
#define CCARRAYDATA_FOREACH(__array__, __object__) \
|
||||
__object__=__array__->arr[0]; for(ssize_t i=0, num=__array__->num; i<num; i++, __object__=__array__->arr[i]) \
|
||||
|
||||
|
||||
typedef struct _ccArray {
|
||||
ssize_t num, max;
|
||||
Ref** arr;
|
||||
} ccArray;
|
||||
|
||||
/** Allocates and initializes a new array with specified capacity */
|
||||
ccArray* ccArrayNew(ssize_t capacity);
|
||||
|
||||
/** Frees array after removing all remaining objects. Silently ignores nil arr. */
|
||||
void ccArrayFree(ccArray*& arr);
|
||||
|
||||
/** Doubles array capacity */
|
||||
void ccArrayDoubleCapacity(ccArray *arr);
|
||||
|
||||
/** Increases array capacity such that max >= num + extra. */
|
||||
void ccArrayEnsureExtraCapacity(ccArray *arr, ssize_t extra);
|
||||
|
||||
/** shrinks the array so the memory footprint corresponds with the number of items */
|
||||
void ccArrayShrink(ccArray *arr);
|
||||
|
||||
/** Returns index of first occurrence of object, NSNotFound if object not found. */
|
||||
ssize_t ccArrayGetIndexOfObject(ccArray *arr, Ref* object);
|
||||
|
||||
/** Returns a Boolean value that indicates whether object is present in array. */
|
||||
bool ccArrayContainsObject(ccArray *arr, Ref* object);
|
||||
|
||||
/** Appends an object. Behavior undefined if array doesn't have enough capacity. */
|
||||
void ccArrayAppendObject(ccArray *arr, Ref* object);
|
||||
|
||||
/** Appends an object. Capacity of arr is increased if needed. */
|
||||
void ccArrayAppendObjectWithResize(ccArray *arr, Ref* object);
|
||||
|
||||
/** Appends objects from plusArr to arr.
|
||||
Behavior undefined if arr doesn't have enough capacity. */
|
||||
void ccArrayAppendArray(ccArray *arr, ccArray *plusArr);
|
||||
|
||||
/** Appends objects from plusArr to arr. Capacity of arr is increased if needed. */
|
||||
void ccArrayAppendArrayWithResize(ccArray *arr, ccArray *plusArr);
|
||||
|
||||
/** Inserts an object at index */
|
||||
void ccArrayInsertObjectAtIndex(ccArray *arr, Ref* object, ssize_t index);
|
||||
|
||||
/** Swaps two objects */
|
||||
void ccArraySwapObjectsAtIndexes(ccArray *arr, ssize_t index1, ssize_t index2);
|
||||
|
||||
/** Removes all objects from arr */
|
||||
void ccArrayRemoveAllObjects(ccArray *arr);
|
||||
|
||||
/** Removes object at specified index and pushes back all subsequent objects.
|
||||
Behavior undefined if index outside [0, num-1]. */
|
||||
void ccArrayRemoveObjectAtIndex(ccArray *arr, ssize_t index, bool releaseObj = true);
|
||||
|
||||
/** Removes object at specified index and fills the gap with the last object,
|
||||
thereby avoiding the need to push back subsequent objects.
|
||||
Behavior undefined if index outside [0, num-1]. */
|
||||
void ccArrayFastRemoveObjectAtIndex(ccArray *arr, ssize_t index);
|
||||
|
||||
void ccArrayFastRemoveObject(ccArray *arr, Ref* object);
|
||||
|
||||
/** Searches for the first occurrence of object and removes it. If object is not
|
||||
found the function has no effect. */
|
||||
void ccArrayRemoveObject(ccArray *arr, Ref* object, bool releaseObj = true);
|
||||
|
||||
/** Removes from arr all objects in minusArr. For each object in minusArr, the
|
||||
first matching instance in arr will be removed. */
|
||||
void ccArrayRemoveArray(ccArray *arr, ccArray *minusArr);
|
||||
|
||||
/** Removes from arr all objects in minusArr. For each object in minusArr, all
|
||||
matching instances in arr will be removed. */
|
||||
void ccArrayFullRemoveArray(ccArray *arr, ccArray *minusArr);
|
||||
|
||||
//
|
||||
// // ccCArray for Values (c structures)
|
||||
|
||||
typedef struct _ccCArray {
|
||||
ssize_t num, max;
|
||||
void** arr;
|
||||
} ccCArray;
|
||||
|
||||
/** Allocates and initializes a new C array with specified capacity */
|
||||
ccCArray* ccCArrayNew(ssize_t capacity);
|
||||
|
||||
/** Frees C array after removing all remaining values. Silently ignores nil arr. */
|
||||
void ccCArrayFree(ccCArray *arr);
|
||||
|
||||
/** Doubles C array capacity */
|
||||
void ccCArrayDoubleCapacity(ccCArray *arr);
|
||||
|
||||
/** Increases array capacity such that max >= num + extra. */
|
||||
void ccCArrayEnsureExtraCapacity(ccCArray *arr, ssize_t extra);
|
||||
|
||||
/** Returns index of first occurrence of value, NSNotFound if value not found. */
|
||||
ssize_t ccCArrayGetIndexOfValue(ccCArray *arr, void* value);
|
||||
|
||||
/** Returns a Boolean value that indicates whether value is present in the C array. */
|
||||
bool ccCArrayContainsValue(ccCArray *arr, void* value);
|
||||
|
||||
/** Inserts a value at a certain position. Behavior undefined if array doesn't have enough capacity */
|
||||
void ccCArrayInsertValueAtIndex( ccCArray *arr, void* value, ssize_t index);
|
||||
|
||||
/** Appends an value. Behavior undefined if array doesn't have enough capacity. */
|
||||
void ccCArrayAppendValue(ccCArray *arr, void* value);
|
||||
|
||||
/** Appends an value. Capacity of arr is increased if needed. */
|
||||
void ccCArrayAppendValueWithResize(ccCArray *arr, void* value);
|
||||
|
||||
/** Appends values from plusArr to arr. Behavior undefined if arr doesn't have
|
||||
enough capacity. */
|
||||
void ccCArrayAppendArray(ccCArray *arr, ccCArray *plusArr);
|
||||
|
||||
/** Appends values from plusArr to arr. Capacity of arr is increased if needed. */
|
||||
void ccCArrayAppendArrayWithResize(ccCArray *arr, ccCArray *plusArr);
|
||||
|
||||
/** Removes all values from arr */
|
||||
void ccCArrayRemoveAllValues(ccCArray *arr);
|
||||
|
||||
/** Removes value at specified index and pushes back all subsequent values.
|
||||
Behavior undefined if index outside [0, num-1].
|
||||
@since v0.99.4
|
||||
*/
|
||||
void ccCArrayRemoveValueAtIndex(ccCArray *arr, ssize_t index);
|
||||
|
||||
/** Removes value at specified index and fills the gap with the last value,
|
||||
thereby avoiding the need to push back subsequent values.
|
||||
Behavior undefined if index outside [0, num-1].
|
||||
@since v0.99.4
|
||||
*/
|
||||
void ccCArrayFastRemoveValueAtIndex(ccCArray *arr, ssize_t index);
|
||||
|
||||
/** Searches for the first occurrence of value and removes it. If value is not found the function has no effect.
|
||||
@since v0.99.4
|
||||
*/
|
||||
void ccCArrayRemoveValue(ccCArray *arr, void* value);
|
||||
|
||||
/** Removes from arr all values in minusArr. For each Value in minusArr, the first matching instance in arr will be removed.
|
||||
@since v0.99.4
|
||||
*/
|
||||
void ccCArrayRemoveArray(ccCArray *arr, ccCArray *minusArr);
|
||||
|
||||
/** Removes from arr all values in minusArr. For each value in minusArr, all matching instances in arr will be removed.
|
||||
@since v0.99.4
|
||||
*/
|
||||
void ccCArrayFullRemoveArray(ccCArray *arr, ccCArray *minusArr);
|
||||
|
||||
NS_CC_END
|
||||
|
||||
/// @endcond
|
||||
#endif // CC_ARRAY_H
|
||||
|
||||
431
cocos2d-x/cocos/base/ccConfig.h
Normal file
431
cocos2d-x/cocos/base/ccConfig.h
Normal file
@@ -0,0 +1,431 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2008-2010 Ricardo Quesada
|
||||
Copyright (c) 2010-2012 cocos2d-x.org
|
||||
Copyright (c) 2011 Zynga Inc.
|
||||
Copyright (c) 2013-2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
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 __CCCONFIG_H__
|
||||
#define __CCCONFIG_H__
|
||||
|
||||
#include "platform/CCPlatformConfig.h"
|
||||
|
||||
/**
|
||||
* @file
|
||||
* cocos2d (cc) configuration file.
|
||||
*/
|
||||
|
||||
// disable module if you didn't need it, this will reduce package size
|
||||
|
||||
#ifndef USE_GFX_RENDERER
|
||||
#define USE_GFX_RENDERER 1
|
||||
#endif
|
||||
|
||||
#ifndef USE_VIDEO
|
||||
#define USE_VIDEO 1
|
||||
#endif
|
||||
|
||||
#ifndef USE_WEB_VIEW
|
||||
#define USE_WEB_VIEW 1
|
||||
#endif
|
||||
|
||||
#ifndef USE_AUDIO
|
||||
#define USE_AUDIO 1
|
||||
#endif
|
||||
|
||||
#ifndef USE_SOCKET
|
||||
#define USE_SOCKET 1
|
||||
#endif
|
||||
|
||||
#ifndef USE_WEBSOCKET_SERVER
|
||||
#define USE_WEBSOCKET_SERVER 0
|
||||
#endif
|
||||
|
||||
#ifndef USE_MIDDLEWARE
|
||||
#define USE_MIDDLEWARE 1
|
||||
#endif
|
||||
|
||||
#if USE_GFX_RENDERER > 0 && USE_MIDDLEWARE > 0
|
||||
|
||||
#ifndef USE_SPINE
|
||||
#define USE_SPINE 1
|
||||
#endif
|
||||
|
||||
#ifndef USE_DRAGONBONES
|
||||
#define USE_DRAGONBONES 1
|
||||
#endif
|
||||
|
||||
#ifndef USE_PARTICLE
|
||||
#define USE_PARTICLE 1
|
||||
#endif
|
||||
|
||||
#endif // endif middleware
|
||||
|
||||
/** @def CC_ENABLE_STACKABLE_ACTIONS
|
||||
* If enabled, actions that alter the position property (eg: MoveBy, JumpBy, BezierBy, etc..) will be stacked.
|
||||
* If you run 2 or more 'position' actions at the same time on a node, then end position will be the sum of all the positions.
|
||||
* If disabled, only the last run action will take effect.
|
||||
* Enabled by default. Disable to be compatible with v2.0 and older versions.
|
||||
* @since v2.1
|
||||
*/
|
||||
#ifndef CC_ENABLE_STACKABLE_ACTIONS
|
||||
#define CC_ENABLE_STACKABLE_ACTIONS 1
|
||||
#endif
|
||||
|
||||
/** @def CC_ENABLE_GL_STATE_CACHE
|
||||
* If enabled, cocos2d will maintain an OpenGL state cache internally to avoid unnecessary switches.
|
||||
* In order to use them, you have to use the following functions, instead of the GL ones:
|
||||
* - ccGLUseProgram() instead of glUseProgram().
|
||||
* - GL::deleteProgram() instead of glDeleteProgram().
|
||||
* - GL::blendFunc() instead of glBlendFunc().
|
||||
|
||||
* If this functionality is disabled, then ccGLUseProgram(), GL::deleteProgram(), GL::blendFunc() will call the GL ones, without using the cache.
|
||||
|
||||
* It is recommended to enable whenever possible to improve speed.
|
||||
* If you are migrating your code from GL ES 1.1, then keep it disabled. Once all your code works as expected, turn it on.
|
||||
|
||||
* Default value: Enabled by default
|
||||
|
||||
* @since v2.0.0
|
||||
*/
|
||||
#ifndef CC_ENABLE_GL_STATE_CACHE
|
||||
#define CC_ENABLE_GL_STATE_CACHE 1
|
||||
#endif
|
||||
|
||||
/** @def CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL
|
||||
* If enabled, the texture coordinates will be calculated by using this formula:
|
||||
* - texCoord.left = (rect.origin.x*2+1) / (texture.wide*2);
|
||||
* - texCoord.right = texCoord.left + (rect.size.width*2-2)/(texture.wide*2);
|
||||
|
||||
* The same for bottom and top.
|
||||
|
||||
* This formula prevents artifacts by using 99% of the texture.
|
||||
* The "correct" way to prevent artifacts is by using the spritesheet-artifact-fixer.py or a similar tool.
|
||||
|
||||
* Affected nodes:
|
||||
* - Sprite / SpriteBatchNode and subclasses: LabelBMFont.
|
||||
* - LabelAtlas.
|
||||
* - QuadParticleSystem.
|
||||
|
||||
* To enabled set it to 1. Disabled by default.
|
||||
|
||||
* @since v0.99.5
|
||||
*/
|
||||
#ifndef CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL
|
||||
#define CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL 0
|
||||
#endif
|
||||
|
||||
/** @def CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL_TMX
|
||||
* If enabled, the texture coordinates will be calculated by using this formula:
|
||||
* - texCoord.left = (rect.origin.x*2+1) / (texture.wide*2);
|
||||
* - texCoord.right = texCoord.left + (rect.size.width*2-2)/(texture.wide*2);
|
||||
|
||||
* The same for bottom and top.
|
||||
|
||||
* This formula prevents artifacts by using 99% of the texture.
|
||||
* The "correct" way to prevent artifacts is by using the spritesheet-artifact-fixer.py or a similar tool.
|
||||
|
||||
* Affected nodes:
|
||||
* - TMXLayer
|
||||
|
||||
* To enabled set it to 1. Enabled by default.
|
||||
|
||||
* @since Cocos Creator v1.7
|
||||
*/
|
||||
#ifndef CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL_TMX
|
||||
#define CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL_TMX 1
|
||||
#endif
|
||||
|
||||
/** @def CC_DIRECTOR_STATS_INTERVAL
|
||||
* Seconds between FPS updates.
|
||||
* 0.5 seconds, means that the FPS number will be updated every 0.5 seconds.
|
||||
* Having a bigger number means a more reliable FPS.
|
||||
|
||||
* Default value: 0.1f
|
||||
*/
|
||||
#ifndef CC_DIRECTOR_STATS_INTERVAL
|
||||
#define CC_DIRECTOR_STATS_INTERVAL (0.1f)
|
||||
#endif
|
||||
|
||||
/** @def CC_DIRECTOR_FPS_POSITION
|
||||
* Position of the FPS.
|
||||
|
||||
* Default: 0,0 (bottom-left corner).
|
||||
*/
|
||||
#ifndef CC_DIRECTOR_FPS_POSITION
|
||||
#define CC_DIRECTOR_FPS_POSITION Vec2(0,0)
|
||||
#endif
|
||||
|
||||
/** @def CC_DIRECTOR_DISPATCH_FAST_EVENTS
|
||||
* If enabled, and only when it is used with FastDirector, the main loop will wait 0.04 seconds to
|
||||
* dispatch all the events, even if there are not events to dispatch.
|
||||
* If your game uses lot's of events (eg: touches) it might be a good idea to enable this feature.
|
||||
* Otherwise, it is safe to leave it disabled.
|
||||
|
||||
* To enable set it to 1. Disabled by default.
|
||||
|
||||
* @warning This feature is experimental.
|
||||
*/
|
||||
#ifndef CC_DIRECTOR_DISPATCH_FAST_EVENTS
|
||||
#define CC_DIRECTOR_DISPATCH_FAST_EVENTS 0
|
||||
#endif
|
||||
|
||||
/** @def CC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREAD
|
||||
* If enabled, cocos2d-mac will run on the Display Link thread. If disabled cocos2d-mac will run in its own thread.
|
||||
* If enabled, the images will be drawn at the "correct" time, but the events might not be very responsive.
|
||||
* If disabled, some frames might be skipped, but the events will be dispatched as they arrived.
|
||||
* To enable set it to a 1, to disable it set to 0. Enabled by default.
|
||||
* Only valid for cocos2d-mac. Not supported on cocos2d-ios.
|
||||
*/
|
||||
#ifndef CC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREAD
|
||||
#define CC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREAD 1
|
||||
#endif
|
||||
|
||||
/** @def CC_NODE_RENDER_SUBPIXEL
|
||||
* If enabled, the Node objects (Sprite, Label,etc) will be able to render in subpixels.
|
||||
* If disabled, integer pixels will be used.
|
||||
* To enable set it to 1. Enabled by default.
|
||||
*/
|
||||
#ifndef CC_NODE_RENDER_SUBPIXEL
|
||||
#define CC_NODE_RENDER_SUBPIXEL 1
|
||||
#endif
|
||||
|
||||
/** @def CC_SPRITEBATCHNODE_RENDER_SUBPIXEL
|
||||
* If enabled, the Sprite objects rendered with SpriteBatchNode will be able to render in subpixels.
|
||||
* If disabled, integer pixels will be used.
|
||||
* To enable set it to 1. Enabled by default.
|
||||
*/
|
||||
#ifndef CC_SPRITEBATCHNODE_RENDER_SUBPIXEL
|
||||
#define CC_SPRITEBATCHNODE_RENDER_SUBPIXEL 1
|
||||
#endif
|
||||
|
||||
/** @def CC_TEXTURE_ATLAS_USE_VAO
|
||||
* By default, TextureAtlas (used by many cocos2d classes) will use VAO (Vertex Array Objects).
|
||||
* Apple recommends its usage but they might consume a lot of memory, specially if you use many of them.
|
||||
* So for certain cases, where you might need hundreds of VAO objects, it might be a good idea to disable it.
|
||||
* To disable it set it to 0. Enabled by default.
|
||||
*/
|
||||
#ifndef CC_TEXTURE_ATLAS_USE_VAO
|
||||
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS) || (CC_TARGET_PLATFORM == CC_PLATFORM_MAC)
|
||||
#define CC_TEXTURE_ATLAS_USE_VAO 1
|
||||
#else
|
||||
/* Some Windows display adapter driver cannot support VAO.
|
||||
* Some android devices cannot support VAO very well, so we disable it by default for android platform.
|
||||
* Blackberry also doesn't support this feature.
|
||||
*/
|
||||
#define CC_TEXTURE_ATLAS_USE_VAO 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
/** @def CC_USE_LA88_LABELS
|
||||
* If enabled, it will use LA88 (Luminance Alpha 16-bit textures) for LabelTTF objects.
|
||||
* If it is disabled, it will use A8 (Alpha 8-bit textures).
|
||||
* LA88 textures are 6% faster than A8 textures, but they will consume 2x memory.
|
||||
* This feature is enabled by default.
|
||||
* @since v0.99.5
|
||||
*/
|
||||
#ifndef CC_USE_LA88_LABELS
|
||||
#define CC_USE_LA88_LABELS 1
|
||||
#endif
|
||||
|
||||
/** @def CC_SPRITE_DEBUG_DRAW
|
||||
* If enabled, all subclasses of Sprite will draw a bounding box.
|
||||
* Useful for debugging purposes only. It is recommended to leave it disabled.
|
||||
* To enable set it to a value different than 0. Disabled by default:
|
||||
* 0 -- disabled
|
||||
* 1 -- draw bounding box
|
||||
* 2 -- draw texture box
|
||||
*/
|
||||
#ifndef CC_SPRITE_DEBUG_DRAW
|
||||
#define CC_SPRITE_DEBUG_DRAW 0
|
||||
#endif
|
||||
|
||||
/** @def CC_LABEL_DEBUG_DRAW
|
||||
* If enabled, all subclasses of Label will draw a bounding box.
|
||||
* Useful for debugging purposes only. It is recommended to leave it disabled.
|
||||
* To enable set it to a value different than 0. Disabled by default:
|
||||
* 0 -- disabled
|
||||
* 1 -- draw bounding box
|
||||
*/
|
||||
#ifndef CC_LABEL_DEBUG_DRAW
|
||||
#define CC_LABEL_DEBUG_DRAW 0
|
||||
#endif
|
||||
|
||||
/** @def CC_SPRITEBATCHNODE_DEBUG_DRAW
|
||||
* If enabled, all subclasses of Sprite that are rendered using an SpriteBatchNode draw a bounding box.
|
||||
* Useful for debugging purposes only. It is recommended to leave it disabled.
|
||||
* To enable set it to a value different than 0. Disabled by default.
|
||||
*/
|
||||
#ifndef CC_SPRITEBATCHNODE_DEBUG_DRAW
|
||||
#define CC_SPRITEBATCHNODE_DEBUG_DRAW 0
|
||||
#endif
|
||||
|
||||
/** @def CC_LABELBMFONT_DEBUG_DRAW
|
||||
* If enabled, all subclasses of LabelBMFont will draw a bounding box.
|
||||
* Useful for debugging purposes only. It is recommended to leave it disabled.
|
||||
* To enable set it to a value different than 0. Disabled by default.
|
||||
*/
|
||||
#ifndef CC_LABELBMFONT_DEBUG_DRAW
|
||||
#define CC_LABELBMFONT_DEBUG_DRAW 0
|
||||
#endif
|
||||
|
||||
/** @def CC_LABELATLAS_DEBUG_DRAW
|
||||
* If enabled, all subclasses of LabeltAtlas will draw a bounding box
|
||||
* Useful for debugging purposes only. It is recommended to leave it disabled.
|
||||
* To enable set it to a value different than 0. Disabled by default.
|
||||
*/
|
||||
#ifndef CC_LABELATLAS_DEBUG_DRAW
|
||||
#define CC_LABELATLAS_DEBUG_DRAW 0
|
||||
#endif
|
||||
|
||||
/** @def CC_NODE_DEBUG_VERIFY_EVENT_LISTENERS
|
||||
* If enabled (in conjunction with assertion macros) will verify on Node destruction that the node being destroyed has no event
|
||||
* listeners still associated with it in the event dispatcher. This can be used to track down problems where the event dispatch
|
||||
* system has dangling pointers to destroyed nodes.
|
||||
* Note: event listener verification will always be disabled in builds where assertions are disabled regardless of this setting.
|
||||
*/
|
||||
#ifndef CC_NODE_DEBUG_VERIFY_EVENT_LISTENERS
|
||||
#define CC_NODE_DEBUG_VERIFY_EVENT_LISTENERS 0
|
||||
#endif
|
||||
|
||||
/** @def CC_ENABLE_PROFILERS
|
||||
* If enabled, will activate various profilers within cocos2d. This statistical data will be output to the console
|
||||
* once per second showing average time (in milliseconds) required to execute the specific routine(s).
|
||||
* Useful for debugging purposes only. It is recommended to leave it disabled.
|
||||
* To enable set it to a value different than 0. Disabled by default.
|
||||
*/
|
||||
#ifndef CC_ENABLE_PROFILERS
|
||||
#define CC_ENABLE_PROFILERS 0
|
||||
#endif
|
||||
|
||||
/** Enable Lua engine debug log. */
|
||||
#ifndef CC_LUA_ENGINE_DEBUG
|
||||
#define CC_LUA_ENGINE_DEBUG 0
|
||||
#endif
|
||||
|
||||
/** Support PNG or not. If your application don't use png format picture, you can undefine this macro to save package size.
|
||||
*/
|
||||
#ifndef CC_USE_PNG
|
||||
#define CC_USE_PNG 1
|
||||
#endif // CC_USE_PNG
|
||||
|
||||
/** Support JPEG or not. If your application don't use jpeg format picture, you can undefine this macro to save package size.
|
||||
*/
|
||||
#ifndef CC_USE_JPEG
|
||||
#define CC_USE_JPEG 1
|
||||
#endif // CC_USE_JPEG
|
||||
|
||||
/** Support TIFF or not. If your application don't use TIFF format picture, you can undefine this macro to save package size.
|
||||
*/
|
||||
#ifndef CC_USE_TIFF
|
||||
#define CC_USE_TIFF 1
|
||||
#endif // CC_USE_TIFF
|
||||
|
||||
/** Support webp or not. If your application don't use webp format picture, you can undefine this macro to save package size.
|
||||
*/
|
||||
#ifndef CC_USE_WEBP
|
||||
#if (CC_TARGET_PLATFORM != CC_PLATFORM_WINRT)
|
||||
#define CC_USE_WEBP 1
|
||||
#endif
|
||||
#endif // CC_USE_WEBP
|
||||
|
||||
/** Support webp or not. If your application don't use webp format picture, you can undefine this macro to save package size.
|
||||
*/
|
||||
//#ifndef CC_USE_WEBP
|
||||
//#if (CC_TARGET_PLATFORM != CC_PLATFORM_WINRT)
|
||||
//#define CC_USE_WEBP 1
|
||||
//#endif
|
||||
//#endif // CC_USE_WEBP
|
||||
|
||||
/** Support WIC (Windows Image Component) or not. Replaces PNG, TIFF and JPEG
|
||||
*/
|
||||
#ifndef CC_USE_WIC
|
||||
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT)
|
||||
#define CC_USE_WIC 1
|
||||
#undef CC_USE_TIFF
|
||||
#undef CC_USE_JPEG
|
||||
#undef CC_USE_PNG
|
||||
#endif
|
||||
#endif // CC_USE_WIC
|
||||
|
||||
/** Enable Script binding. */
|
||||
#ifndef CC_ENABLE_SCRIPT_BINDING
|
||||
#define CC_ENABLE_SCRIPT_BINDING 1
|
||||
#endif
|
||||
|
||||
/** When CC_ENABLE_SCRIPT_BINDING and CC_ENABLE_GC_FOR_NATIVE_OBJECTS are both 1
|
||||
then the Garbage collector will release the native objects, only when the JS/Lua objets
|
||||
are collected.
|
||||
The benefit is that users don't need to retain/release the JS/Lua objects manually.
|
||||
|
||||
By default this behavior is disabled by default
|
||||
*/
|
||||
#ifdef CC_ENABLE_SCRIPT_BINDING
|
||||
#ifndef CC_ENABLE_GC_FOR_NATIVE_OBJECTS
|
||||
#define CC_ENABLE_GC_FOR_NATIVE_OBJECTS 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/** @def CC_CONSTRUCTOR_ACCESS
|
||||
* Indicate the init functions access modifier. If value equals to protected, then these functions are protected.
|
||||
* If value equals to public, these functions are public,
|
||||
* protected by default.
|
||||
*/
|
||||
#ifndef CC_CONSTRUCTOR_ACCESS
|
||||
#ifdef CC_ENABLE_SCRIPT_BINDING
|
||||
#define CC_CONSTRUCTOR_ACCESS public
|
||||
#else
|
||||
#define CC_CONSTRUCTOR_ACCESS protected
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef CC_FILEUTILS_APPLE_ENABLE_OBJC
|
||||
#define CC_FILEUTILS_APPLE_ENABLE_OBJC 1
|
||||
#endif
|
||||
|
||||
/** @def CC_ENABLE_PREMULTIPLIED_ALPHA
|
||||
* If enabled, all textures will be preprocessed to multiply its rgb components
|
||||
* by its alpha component.
|
||||
*/
|
||||
#ifndef CC_ENABLE_PREMULTIPLIED_ALPHA
|
||||
# define CC_ENABLE_PREMULTIPLIED_ALPHA 1
|
||||
#endif
|
||||
|
||||
#ifndef CC_ENABLE_TTF_LABEL_RENDERER
|
||||
# define CC_ENABLE_TTF_LABEL_RENDERER 1
|
||||
#endif
|
||||
|
||||
#ifndef CC_ENABLE_CACHE_TTF_FONT_TEXTURE
|
||||
# define CC_ENABLE_CACHE_TTF_FONT_TEXTURE 1
|
||||
#endif
|
||||
|
||||
/** @def CC_IOS_FORCE_DISABLE_JIT
|
||||
* If enabled, --jitless flag will be add to V8
|
||||
*/
|
||||
#ifndef CC_IOS_FORCE_DISABLE_JIT
|
||||
# define CC_IOS_FORCE_DISABLE_JIT 0
|
||||
#endif
|
||||
|
||||
#endif // __CCCONFIG_H__
|
||||
300
cocos2d-x/cocos/base/ccMacros.h
Normal file
300
cocos2d-x/cocos/base/ccMacros.h
Normal file
@@ -0,0 +1,300 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2008-2010 Ricardo Quesada
|
||||
Copyright (c) 2010-2012 cocos2d-x.org
|
||||
Copyright (c) 2011 Zynga Inc.
|
||||
Copyright (c) 2013-2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
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.
|
||||
****************************************************************************/
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
#ifndef _USE_MATH_DEFINES
|
||||
#define _USE_MATH_DEFINES
|
||||
#endif
|
||||
*/
|
||||
|
||||
#include "base/CCLog.h"
|
||||
#include "base/ccConfig.h"
|
||||
|
||||
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
|
||||
#include <BaseTsd.h>
|
||||
#ifndef __SSIZE_T
|
||||
#define __SSIZE_T
|
||||
typedef SSIZE_T ssize_t;
|
||||
#endif // __SSIZE_T
|
||||
#endif
|
||||
|
||||
#ifndef CCASSERT
|
||||
#if COCOS2D_DEBUG > 0
|
||||
// todo: minggo
|
||||
// #if CC_ENABLE_SCRIPT_BINDING
|
||||
// extern bool CC_DLL cc_assert_script_compatible(const char *msg);
|
||||
// #define CCASSERT(cond, msg) do { \
|
||||
// if (!(cond)) { \
|
||||
// if (!cc_assert_script_compatible(msg) && strlen(msg)) \
|
||||
// cocos2d::log("Assert failed: %s", msg); \
|
||||
// CC_ASSERT(cond); \
|
||||
// } \
|
||||
// } while (0)
|
||||
// #else
|
||||
#define CCASSERT(cond, msg) CC_ASSERT(cond)
|
||||
// #endif
|
||||
#else
|
||||
#define CCASSERT(cond, msg)
|
||||
#endif
|
||||
|
||||
#define GP_ASSERT(cond) CCASSERT(cond, "")
|
||||
#endif // CCASSERT
|
||||
|
||||
/** @def CC_DEGREES_TO_RADIANS
|
||||
converts degrees to radians
|
||||
*/
|
||||
#define CC_DEGREES_TO_RADIANS(__ANGLE__) ((__ANGLE__) * 0.01745329252f) // PI / 180
|
||||
|
||||
/** @def CC_RADIANS_TO_DEGREES
|
||||
converts radians to degrees
|
||||
*/
|
||||
#define CC_RADIANS_TO_DEGREES(__ANGLE__) ((__ANGLE__) * 57.29577951f) // PI * 180
|
||||
|
||||
/** @def CC_SIGN
|
||||
gets sign of value
|
||||
*/
|
||||
#define CC_SIGN(__VALUE__) ((__VALUE__) > 0 ? 1 : ((__VALUE__) < 0 ? -1 : 0))
|
||||
|
||||
#ifndef FLT_EPSILON
|
||||
#define FLT_EPSILON 1.192092896e-07F
|
||||
#endif // FLT_EPSILON
|
||||
|
||||
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
|
||||
TypeName(const TypeName&);\
|
||||
void operator=(const TypeName&)
|
||||
|
||||
|
||||
/** @def CC_SWAP
|
||||
simple macro that swaps 2 variables
|
||||
@deprecated use std::swap() instead
|
||||
*/
|
||||
#define CC_SWAP(x, y, type) \
|
||||
{ type temp = (x); \
|
||||
x = y; y = temp; \
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Helper macros which converts 4-byte little/big endian
|
||||
integral number to the machine native number representation
|
||||
|
||||
It should work same as apples CFSwapInt32LittleToHost(..)
|
||||
*/
|
||||
|
||||
/// when define returns true it means that our architecture uses big endian
|
||||
#define CC_HOST_IS_BIG_ENDIAN (bool)(*(unsigned short *)"\0\xff" < 0x100)
|
||||
#define CC_SWAP32(i) ((i & 0x000000ff) << 24 | (i & 0x0000ff00) << 8 | (i & 0x00ff0000) >> 8 | (i & 0xff000000) >> 24)
|
||||
#define CC_SWAP16(i) ((i & 0x00ff) << 8 | (i &0xff00) >> 8)
|
||||
#define CC_SWAP_INT32_LITTLE_TO_HOST(i) ((CC_HOST_IS_BIG_ENDIAN == true)? CC_SWAP32(i) : (i) )
|
||||
#define CC_SWAP_INT16_LITTLE_TO_HOST(i) ((CC_HOST_IS_BIG_ENDIAN == true)? CC_SWAP16(i) : (i) )
|
||||
#define CC_SWAP_INT32_BIG_TO_HOST(i) ((CC_HOST_IS_BIG_ENDIAN == true)? (i) : CC_SWAP32(i) )
|
||||
#define CC_SWAP_INT16_BIG_TO_HOST(i) ((CC_HOST_IS_BIG_ENDIAN == true)? (i): CC_SWAP16(i) )
|
||||
|
||||
#if !defined(COCOS2D_DEBUG) || COCOS2D_DEBUG == 0
|
||||
#define CHECK_GL_ERROR_DEBUG()
|
||||
#else
|
||||
#define CHECK_GL_ERROR_DEBUG() \
|
||||
do { \
|
||||
GLenum __error = glGetError(); \
|
||||
if(__error) { \
|
||||
cocos2d::log("OpenGL error 0x%04X in %s %s %d\n", __error, __FILE__, __FUNCTION__, __LINE__); \
|
||||
} \
|
||||
} while (false)
|
||||
#endif // !defined(COCOS2D_DEBUG) || COCOS2D_DEBUG == 0
|
||||
|
||||
/**
|
||||
* GL assertion that can be used for any OpenGL function call.
|
||||
*
|
||||
* This macro will assert if an error is detected when executing
|
||||
* the specified GL code. This macro will do nothing in release
|
||||
* mode and is therefore safe to use for realtime/per-frame GL
|
||||
* function calls.
|
||||
*/
|
||||
#if defined(NDEBUG) || (defined(__APPLE__) && !defined(DEBUG))
|
||||
#define CC_GL_ASSERT( gl_code ) gl_code
|
||||
#else
|
||||
#define CC_GL_ASSERT( gl_code ) do \
|
||||
{ \
|
||||
gl_code; \
|
||||
__gl_error_code = glGetError(); \
|
||||
CC_ASSERT(__gl_error_code == GL_NO_ERROR, "Error"); \
|
||||
} while(0)
|
||||
#endif // defined(NDEBUG) || (defined(__APPLE__) && !defined(DEBUG))
|
||||
|
||||
/*********************************/
|
||||
/** 64bits Program Sense Macros **/
|
||||
/*********************************/
|
||||
#if defined(_M_X64) || defined(_WIN64) || defined(__LP64__) || defined(_LP64) || defined(__x86_64)
|
||||
#define CC_64BITS 1
|
||||
#else
|
||||
#define CC_64BITS 0
|
||||
#endif
|
||||
|
||||
// new callbacks based on C++11
|
||||
#define CC_CALLBACK_0(__selector__,__target__, ...) std::bind(&__selector__,__target__, ##__VA_ARGS__)
|
||||
#define CC_CALLBACK_1(__selector__,__target__, ...) std::bind(&__selector__,__target__, std::placeholders::_1, ##__VA_ARGS__)
|
||||
#define CC_CALLBACK_2(__selector__,__target__, ...) std::bind(&__selector__,__target__, std::placeholders::_1, std::placeholders::_2, ##__VA_ARGS__)
|
||||
#define CC_CALLBACK_3(__selector__,__target__, ...) std::bind(&__selector__,__target__, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, ##__VA_ARGS__)
|
||||
|
||||
// Generic macros
|
||||
|
||||
/// @name namespace cocos2d
|
||||
/// @{
|
||||
#ifdef __cplusplus
|
||||
#define NS_CC_BEGIN namespace cocos2d {
|
||||
#define NS_CC_END }
|
||||
#define USING_NS_CC using namespace cocos2d
|
||||
#define NS_CC ::cocos2d
|
||||
#else
|
||||
#define NS_CC_BEGIN
|
||||
#define NS_CC_END
|
||||
#define USING_NS_CC
|
||||
#define NS_CC
|
||||
#endif
|
||||
// end of namespace group
|
||||
/// @}
|
||||
|
||||
#define CC_SAFE_DELETE(p) do { delete (p); (p) = nullptr; } while(0)
|
||||
#define CC_SAFE_DELETE_ARRAY(p) do { if(p) { delete[] (p); (p) = nullptr; } } while(0)
|
||||
#define CC_SAFE_FREE(p) do { if(p) { free(p); (p) = nullptr; } } while(0)
|
||||
#define CC_SAFE_RELEASE(p) do { if(p) { (p)->release(); } } while(0)
|
||||
#define CC_SAFE_RELEASE_NULL(p) do { if(p) { (p)->release(); (p) = nullptr; } } while(0)
|
||||
#define CC_SAFE_RETAIN(p) do { if(p) { (p)->retain(); } } while(0)
|
||||
#define CC_BREAK_IF(cond) if(cond) break
|
||||
|
||||
#define __CCLOGWITHFUNCTION(s, ...) \
|
||||
cocos2d::log("%s : %s",__FUNCTION__, cocos2d::StringUtils::format(s, ##__VA_ARGS__).c_str())
|
||||
|
||||
/// @name Cocos2d debug
|
||||
/// @{
|
||||
#if !defined(COCOS2D_DEBUG) || COCOS2D_DEBUG == 0
|
||||
#define CCLOG(...) do {} while (0)
|
||||
#define CCLOGINFO(...) do {} while (0)
|
||||
#define CCLOGERROR(...) do {} while (0)
|
||||
#define CCLOGWARN(...) do {} while (0)
|
||||
|
||||
#elif COCOS2D_DEBUG == 1
|
||||
|
||||
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
|
||||
#define COCOS_LOG_TAG "cocos2d-x"
|
||||
#define CCLOG(...) __android_log_print(ANDROID_LOG_DEBUG, COCOS_LOG_TAG, __VA_ARGS__)
|
||||
#define CCLOGINFO(format,...) do {} while (0)
|
||||
#define CCLOGWARN(...) __android_log_print(ANDROID_LOG_WARN, COCOS_LOG_TAG, __VA_ARGS__)
|
||||
#define CCLOGERROR(...) __android_log_print(ANDROID_LOG_ERROR, COCOS_LOG_TAG, __VA_ARGS__)
|
||||
#else
|
||||
#define CCLOG(format, ...) cocos2d::log(format, ##__VA_ARGS__)
|
||||
#define CCLOGERROR(format,...) cocos2d::log(format, ##__VA_ARGS__)
|
||||
#define CCLOGINFO(format,...) do {} while (0)
|
||||
#define CCLOGWARN(...) __CCLOGWITHFUNCTION(__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
|
||||
#elif COCOS2D_DEBUG > 1
|
||||
#define CCLOG(format, ...) cocos2d::log(format, ##__VA_ARGS__)
|
||||
#define CCLOGERROR(format,...) cocos2d::log(format, ##__VA_ARGS__)
|
||||
#define CCLOGINFO(format,...) cocos2d::log(format, ##__VA_ARGS__)
|
||||
#define CCLOGWARN(...) __CCLOGWITHFUNCTION(__VA_ARGS__)
|
||||
#endif // COCOS2D_DEBUG
|
||||
|
||||
// end of debug group
|
||||
/// @}
|
||||
|
||||
/** @def CC_DISALLOW_COPY_AND_ASSIGN(TypeName)
|
||||
* A macro to disallow the copy constructor and operator= functions.
|
||||
* This should be used in the private: declarations for a class
|
||||
*/
|
||||
#if defined(__GNUC__) && ((__GNUC__ >= 5) || ((__GNUG__ == 4) && (__GNUC_MINOR__ >= 4))) \
|
||||
|| (defined(__clang__) && (__clang_major__ >= 3)) || (_MSC_VER >= 1800)
|
||||
#define CC_DISALLOW_COPY_AND_ASSIGN(TypeName) \
|
||||
TypeName(const TypeName &) = delete; \
|
||||
TypeName &operator =(const TypeName &) = delete;
|
||||
#else
|
||||
#define CC_DISALLOW_COPY_AND_ASSIGN(TypeName) \
|
||||
TypeName(const TypeName &); \
|
||||
TypeName &operator =(const TypeName &);
|
||||
#endif
|
||||
|
||||
/** @def CC_DEPRECATED_ATTRIBUTE
|
||||
* Only certain compilers support __attribute__((deprecated)).
|
||||
*/
|
||||
#if defined(__GNUC__) && ((__GNUC__ >= 4) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 1)))
|
||||
#define CC_DEPRECATED_ATTRIBUTE __attribute__((deprecated))
|
||||
#elif _MSC_VER >= 1400 //vs 2005 or higher
|
||||
#define CC_DEPRECATED_ATTRIBUTE __declspec(deprecated)
|
||||
#else
|
||||
#define CC_DEPRECATED_ATTRIBUTE
|
||||
#endif
|
||||
|
||||
/** @def CC_DEPRECATED(...)
|
||||
* Macro to mark things deprecated as of a particular version
|
||||
* can be used with arbitrary parameters which are thrown away.
|
||||
* e.g. CC_DEPRECATED(4.0) or CC_DEPRECATED(4.0, "not going to need this anymore") etc.
|
||||
*/
|
||||
#define CC_DEPRECATED(...) CC_DEPRECATED_ATTRIBUTE
|
||||
|
||||
/** @def CC_FORMAT_PRINTF(formatPos, argPos)
|
||||
* Only certain compiler support __attribute__((format))
|
||||
*
|
||||
* @param formatPos 1-based position of format string argument.
|
||||
* @param argPos 1-based position of first format-dependent argument.
|
||||
*/
|
||||
#if defined(__GNUC__) && (__GNUC__ >= 4)
|
||||
#define CC_FORMAT_PRINTF(formatPos, argPos) __attribute__((__format__(printf, formatPos, argPos)))
|
||||
#elif defined(__has_attribute)
|
||||
#if __has_attribute(format)
|
||||
#define CC_FORMAT_PRINTF(formatPos, argPos) __attribute__((__format__(printf, formatPos, argPos)))
|
||||
#endif // __has_attribute(format)
|
||||
#else
|
||||
#define CC_FORMAT_PRINTF(formatPos, argPos)
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#define CC_FORMAT_PRINTF_SIZE_T "%08lX"
|
||||
#else
|
||||
#define CC_FORMAT_PRINTF_SIZE_T "%08zX"
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define CC_UNUSED __attribute__ ((unused))
|
||||
#else
|
||||
#define CC_UNUSED
|
||||
#endif
|
||||
|
||||
/** @def CC_REQUIRES_NULL_TERMINATION
|
||||
*
|
||||
*/
|
||||
#if !defined(CC_REQUIRES_NULL_TERMINATION)
|
||||
#if defined(__APPLE_CC__) && (__APPLE_CC__ >= 5549)
|
||||
#define CC_REQUIRES_NULL_TERMINATION __attribute__((sentinel(0,1)))
|
||||
#elif defined(__GNUC__)
|
||||
#define CC_REQUIRES_NULL_TERMINATION __attribute__((sentinel))
|
||||
#else
|
||||
#define CC_REQUIRES_NULL_TERMINATION
|
||||
#endif
|
||||
#endif
|
||||
27
cocos2d-x/cocos/base/ccRandom.cpp
Normal file
27
cocos2d-x/cocos/base/ccRandom.cpp
Normal file
@@ -0,0 +1,27 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2010 cocos2d-x.org
|
||||
Copyright (c) 2013-2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
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 "base/ccRandom.h"
|
||||
132
cocos2d-x/cocos/base/ccRandom.h
Normal file
132
cocos2d-x/cocos/base/ccRandom.h
Normal file
@@ -0,0 +1,132 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2010-2012 cocos2d-x.org
|
||||
Copyright (c) 2013-2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
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 __ccRandom_H_
|
||||
#define __ccRandom_H_
|
||||
|
||||
#include <random>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "base/ccMacros.h"
|
||||
|
||||
/**
|
||||
* @addtogroup base
|
||||
* @{
|
||||
*/
|
||||
NS_CC_BEGIN
|
||||
|
||||
|
||||
/**
|
||||
* @class RandomHelper
|
||||
* @brief A helper class for creating random number.
|
||||
*/
|
||||
class CC_DLL RandomHelper {
|
||||
public:
|
||||
template<typename T>
|
||||
static inline T random_real(T min, T max) {
|
||||
std::uniform_real_distribution<T> dist(min, max);
|
||||
auto &mt = RandomHelper::getEngine();
|
||||
return dist(mt);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static inline T random_int(T min, T max) {
|
||||
std::uniform_int_distribution<T> dist(min, max);
|
||||
auto &mt = RandomHelper::getEngine();
|
||||
return dist(mt);
|
||||
}
|
||||
private:
|
||||
static inline std::mt19937 &getEngine() {
|
||||
static std::random_device seed_gen;
|
||||
static std::mt19937 engine(seed_gen());
|
||||
return engine;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a random value between `min` and `max`.
|
||||
*/
|
||||
template<typename T>
|
||||
inline T random(T min, T max) {
|
||||
return RandomHelper::random_int<T>(min, max);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline float random(float min, float max) {
|
||||
return RandomHelper::random_real(min, max);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline long double random(long double min, long double max) {
|
||||
return RandomHelper::random_real(min, max);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline double random(double min, double max) {
|
||||
return RandomHelper::random_real(min, max);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a random int between 0 and RAND_MAX.
|
||||
*/
|
||||
inline int random() {
|
||||
return cocos2d::random(0, RAND_MAX);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a random float between -1 and 1.
|
||||
* It can be seeded using std::srand(seed);
|
||||
*/
|
||||
inline float rand_minus1_1() {
|
||||
// IDEA: using the new c++11 random engine generator
|
||||
// without a proper way to set a seed is not useful.
|
||||
// Resorting to the old random method since it can
|
||||
// be seeded using std::srand()
|
||||
return ((std::rand() / (float)RAND_MAX) * 2) -1;
|
||||
|
||||
// return cocos2d::random(-1.f, 1.f);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a random float between 0 and 1.
|
||||
* It can be seeded using std::srand(seed);
|
||||
*/
|
||||
inline float rand_0_1() {
|
||||
// IDEA: using the new c++11 random engine generator
|
||||
// without a proper way to set a seed is not useful.
|
||||
// Resorting to the old random method since it can
|
||||
// be seeded using std::srand()
|
||||
return std::rand() / (float)RAND_MAX;
|
||||
|
||||
// return cocos2d::random(0.f, 1.f);
|
||||
};
|
||||
|
||||
|
||||
NS_CC_END
|
||||
// end group
|
||||
/// @}
|
||||
#endif //__ccRandom_H_
|
||||
|
||||
274
cocos2d-x/cocos/base/ccTypes.cpp
Normal file
274
cocos2d-x/cocos/base/ccTypes.cpp
Normal file
@@ -0,0 +1,274 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2008-2010 Ricardo Quesada
|
||||
Copyright (c) 2010-2012 cocos2d-x.org
|
||||
Copyright (c) 2011 Zynga Inc.
|
||||
Copyright (c) 2013-2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
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 "base/ccTypes.h"
|
||||
|
||||
NS_CC_BEGIN
|
||||
|
||||
const std::string STD_STRING_EMPTY("");
|
||||
const ssize_t CC_INVALID_INDEX = -1;
|
||||
|
||||
/**
|
||||
* Color3B
|
||||
*/
|
||||
|
||||
Color3B::Color3B()
|
||||
: r(0)
|
||||
, g(0)
|
||||
, b(0)
|
||||
{}
|
||||
|
||||
Color3B::Color3B(GLubyte _r, GLubyte _g, GLubyte _b)
|
||||
: r(_r)
|
||||
, g(_g)
|
||||
, b(_b)
|
||||
{}
|
||||
|
||||
Color3B::Color3B(const Color4B& color)
|
||||
: r(color.r)
|
||||
, g(color.g)
|
||||
, b(color.b)
|
||||
{}
|
||||
|
||||
Color3B::Color3B(const Color4F& color)
|
||||
: r(color.r * 255.0f)
|
||||
, g(color.g * 255.0f)
|
||||
, b(color.b * 255.0f)
|
||||
{}
|
||||
|
||||
bool Color3B::operator==(const Color3B& right) const
|
||||
{
|
||||
return (r == right.r && g == right.g && b == right.b);
|
||||
}
|
||||
|
||||
bool Color3B::operator==(const Color4B& right) const
|
||||
{
|
||||
return (r == right.r && g == right.g && b == right.b && 255 == right.a);
|
||||
}
|
||||
|
||||
bool Color3B::operator==(const Color4F& right) const
|
||||
{
|
||||
return (right.a == 1.0f && Color4F(*this) == right);
|
||||
}
|
||||
|
||||
bool Color3B::operator!=(const Color3B& right) const
|
||||
{
|
||||
return !(*this == right);
|
||||
}
|
||||
|
||||
bool Color3B::operator!=(const Color4B& right) const
|
||||
{
|
||||
return !(*this == right);
|
||||
}
|
||||
|
||||
bool Color3B::operator!=(const Color4F& right) const
|
||||
{
|
||||
return !(*this == right);
|
||||
}
|
||||
|
||||
/**
|
||||
* Color4B
|
||||
*/
|
||||
|
||||
Color4B::Color4B()
|
||||
: r(0)
|
||||
, g(0)
|
||||
, b(0)
|
||||
, a(0)
|
||||
{}
|
||||
|
||||
Color4B::Color4B(GLubyte _r, GLubyte _g, GLubyte _b, GLubyte _a)
|
||||
: r(_r)
|
||||
, g(_g)
|
||||
, b(_b)
|
||||
, a(_a)
|
||||
{}
|
||||
|
||||
Color4B::Color4B(const Color3B& color, GLubyte _a)
|
||||
: r(color.r)
|
||||
, g(color.g)
|
||||
, b(color.b)
|
||||
, a(_a)
|
||||
{}
|
||||
|
||||
Color4B::Color4B(const Color4F& color)
|
||||
: r(color.r * 255)
|
||||
, g(color.g * 255)
|
||||
, b(color.b * 255)
|
||||
, a(color.a * 255)
|
||||
{}
|
||||
|
||||
bool Color4B::operator==(const Color4B& right) const
|
||||
{
|
||||
return (r == right.r && g == right.g && b == right.b && a == right.a);
|
||||
}
|
||||
|
||||
bool Color4B::operator==(const Color3B& right) const
|
||||
{
|
||||
return (r == right.r && g == right.g && b == right.b && a == 255);
|
||||
}
|
||||
|
||||
bool Color4B::operator==(const Color4F& right) const
|
||||
{
|
||||
return (*this == Color4B(right));
|
||||
}
|
||||
|
||||
bool Color4B::operator!=(const Color4B& right) const
|
||||
{
|
||||
return !(*this == right);
|
||||
}
|
||||
|
||||
bool Color4B::operator!=(const Color3B& right) const
|
||||
{
|
||||
return !(*this == right);
|
||||
}
|
||||
|
||||
bool Color4B::operator!=(const Color4F& right) const
|
||||
{
|
||||
return !(*this == right);
|
||||
}
|
||||
|
||||
/**
|
||||
* Color4F
|
||||
*/
|
||||
|
||||
Color4F::Color4F()
|
||||
: r(0.0f)
|
||||
, g(0.0f)
|
||||
, b(0.0f)
|
||||
, a(0.0f)
|
||||
{}
|
||||
|
||||
Color4F::Color4F(float _r, float _g, float _b, float _a)
|
||||
: r(_r)
|
||||
, g(_g)
|
||||
, b(_b)
|
||||
, a(_a)
|
||||
{}
|
||||
|
||||
Color4F::Color4F(const Color3B& color, float _a)
|
||||
: r(color.r / 255.0f)
|
||||
, g(color.g / 255.0f)
|
||||
, b(color.b / 255.0f)
|
||||
, a(_a)
|
||||
{}
|
||||
|
||||
Color4F::Color4F(const Color4B& color)
|
||||
: r(color.r / 255.0f)
|
||||
, g(color.g / 255.0f)
|
||||
, b(color.b / 255.0f)
|
||||
, a(color.a / 255.0f)
|
||||
{}
|
||||
|
||||
bool Color4F::operator==(const Color4F& right) const
|
||||
{
|
||||
return (r == right.r && g == right.g && b == right.b && a == right.a);
|
||||
}
|
||||
|
||||
bool Color4F::operator==(const Color3B& right) const
|
||||
{
|
||||
return (a == 1.0f && Color3B(*this) == right);
|
||||
}
|
||||
|
||||
bool Color4F::operator==(const Color4B& right) const
|
||||
{
|
||||
return (*this == Color4F(right));
|
||||
}
|
||||
|
||||
bool Color4F::operator!=(const Color4F& right) const
|
||||
{
|
||||
return !(*this == right);
|
||||
}
|
||||
|
||||
bool Color4F::operator!=(const Color3B& right) const
|
||||
{
|
||||
return !(*this == right);
|
||||
}
|
||||
|
||||
bool Color4F::operator!=(const Color4B& right) const
|
||||
{
|
||||
return !(*this == right);
|
||||
}
|
||||
|
||||
// Color3F
|
||||
|
||||
Color3F::Color3F()
|
||||
: r(0.0f)
|
||||
, g(0.0f)
|
||||
, b(0.0f)
|
||||
{}
|
||||
|
||||
Color3F::Color3F(float _r, float _g, float _b)
|
||||
: r(_r)
|
||||
, g(_g)
|
||||
, b(_b)
|
||||
{}
|
||||
|
||||
const Color3F Color3F::BLACK = {0, 0, 0};
|
||||
|
||||
/**
|
||||
* Color constants
|
||||
*/
|
||||
|
||||
const Color3B Color3B::WHITE (255, 255, 255);
|
||||
const Color3B Color3B::YELLOW (255, 255, 0);
|
||||
const Color3B Color3B::GREEN ( 0, 255, 0);
|
||||
const Color3B Color3B::BLUE ( 0, 0, 255);
|
||||
const Color3B Color3B::RED (255, 0, 0);
|
||||
const Color3B Color3B::MAGENTA(255, 0, 255);
|
||||
const Color3B Color3B::BLACK ( 0, 0, 0);
|
||||
const Color3B Color3B::ORANGE (255, 127, 0);
|
||||
const Color3B Color3B::GRAY (166, 166, 166);
|
||||
|
||||
const Color4B Color4B::WHITE (255, 255, 255, 255);
|
||||
const Color4B Color4B::YELLOW (255, 255, 0, 255);
|
||||
const Color4B Color4B::GREEN ( 0, 255, 0, 255);
|
||||
const Color4B Color4B::BLUE ( 0, 0, 255, 255);
|
||||
const Color4B Color4B::RED (255, 0, 0, 255);
|
||||
const Color4B Color4B::MAGENTA(255, 0, 255, 255);
|
||||
const Color4B Color4B::BLACK ( 0, 0, 0, 255);
|
||||
const Color4B Color4B::ORANGE (255, 127, 0, 255);
|
||||
const Color4B Color4B::GRAY (166, 166, 166, 255);
|
||||
|
||||
const Color4F Color4F::WHITE ( 1, 1, 1, 1);
|
||||
const Color4F Color4F::YELLOW ( 1, 1, 0, 1);
|
||||
const Color4F Color4F::GREEN ( 0, 1, 0, 1);
|
||||
const Color4F Color4F::BLUE ( 0, 0, 1, 1);
|
||||
const Color4F Color4F::RED ( 1, 0, 0, 1);
|
||||
const Color4F Color4F::MAGENTA( 1, 0, 1, 1);
|
||||
const Color4F Color4F::BLACK ( 0, 0, 0, 1);
|
||||
const Color4F Color4F::ORANGE ( 1, 0.5f, 0, 1);
|
||||
const Color4F Color4F::GRAY (0.65f, 0.65f, 0.65f, 1);
|
||||
|
||||
const BlendFunc BlendFunc::DISABLE = {GL_ONE, GL_ZERO};
|
||||
const BlendFunc BlendFunc::ALPHA_PREMULTIPLIED = {GL_ONE, GL_ONE_MINUS_SRC_ALPHA};
|
||||
const BlendFunc BlendFunc::ALPHA_NON_PREMULTIPLIED = {GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA};
|
||||
const BlendFunc BlendFunc::ADDITIVE = {GL_SRC_ALPHA, GL_ONE};
|
||||
|
||||
NS_CC_END
|
||||
|
||||
238
cocos2d-x/cocos/base/ccTypes.h
Normal file
238
cocos2d-x/cocos/base/ccTypes.h
Normal file
@@ -0,0 +1,238 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2008-2010 Ricardo Quesada
|
||||
Copyright (c) 2010-2012 cocos2d-x.org
|
||||
Copyright (c) 2011 Zynga Inc.
|
||||
Copyright (c) 2013-2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
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 __BASE_CCTYPES_H__
|
||||
#define __BASE_CCTYPES_H__
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "math/CCGeometry.h"
|
||||
#include "math/CCMath.h"
|
||||
#include "base/CCRef.h"
|
||||
#include "platform/CCGL.h"
|
||||
|
||||
/**
|
||||
* @addtogroup base
|
||||
* @{
|
||||
*/
|
||||
|
||||
NS_CC_BEGIN
|
||||
|
||||
struct Color4B;
|
||||
struct Color4F;
|
||||
|
||||
/**
|
||||
* RGB color composed of bytes 3 bytes.
|
||||
* @since v3.0
|
||||
*/
|
||||
struct CC_DLL Color3B
|
||||
{
|
||||
Color3B();
|
||||
Color3B(GLubyte _r, GLubyte _g, GLubyte _b);
|
||||
explicit Color3B(const Color4B& color);
|
||||
explicit Color3B(const Color4F& color);
|
||||
|
||||
bool operator==(const Color3B& right) const;
|
||||
bool operator==(const Color4B& right) const;
|
||||
bool operator==(const Color4F& right) const;
|
||||
bool operator!=(const Color3B& right) const;
|
||||
bool operator!=(const Color4B& right) const;
|
||||
bool operator!=(const Color4F& right) const;
|
||||
|
||||
bool equals(const Color3B& other) const
|
||||
{
|
||||
return (*this == other);
|
||||
}
|
||||
|
||||
GLubyte r;
|
||||
GLubyte g;
|
||||
GLubyte b;
|
||||
|
||||
static const Color3B WHITE;
|
||||
static const Color3B YELLOW;
|
||||
static const Color3B BLUE;
|
||||
static const Color3B GREEN;
|
||||
static const Color3B RED;
|
||||
static const Color3B MAGENTA;
|
||||
static const Color3B BLACK;
|
||||
static const Color3B ORANGE;
|
||||
static const Color3B GRAY;
|
||||
};
|
||||
|
||||
/**
|
||||
* RGBA color composed of 4 bytes.
|
||||
* @since v3.0
|
||||
*/
|
||||
struct CC_DLL Color4B
|
||||
{
|
||||
Color4B();
|
||||
Color4B(GLubyte _r, GLubyte _g, GLubyte _b, GLubyte _a);
|
||||
explicit Color4B(const Color3B& color, GLubyte _a = 255);
|
||||
explicit Color4B(const Color4F& color);
|
||||
|
||||
inline void set(GLubyte _r, GLubyte _g, GLubyte _b, GLubyte _a)
|
||||
{
|
||||
r = _r;
|
||||
g = _g;
|
||||
b = _b;
|
||||
a = _a;
|
||||
}
|
||||
|
||||
bool operator==(const Color4B& right) const;
|
||||
bool operator==(const Color3B& right) const;
|
||||
bool operator==(const Color4F& right) const;
|
||||
bool operator!=(const Color4B& right) const;
|
||||
bool operator!=(const Color3B& right) const;
|
||||
bool operator!=(const Color4F& right) const;
|
||||
|
||||
GLubyte r;
|
||||
GLubyte g;
|
||||
GLubyte b;
|
||||
GLubyte a;
|
||||
|
||||
static const Color4B WHITE;
|
||||
static const Color4B YELLOW;
|
||||
static const Color4B BLUE;
|
||||
static const Color4B GREEN;
|
||||
static const Color4B RED;
|
||||
static const Color4B MAGENTA;
|
||||
static const Color4B BLACK;
|
||||
static const Color4B ORANGE;
|
||||
static const Color4B GRAY;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* RGBA color composed of 4 floats.
|
||||
* @since v3.0
|
||||
*/
|
||||
struct CC_DLL Color4F
|
||||
{
|
||||
Color4F();
|
||||
Color4F(float _r, float _g, float _b, float _a);
|
||||
explicit Color4F(const Color3B& color, float _a = 1.0f);
|
||||
explicit Color4F(const Color4B& color);
|
||||
|
||||
bool operator==(const Color4F& right) const;
|
||||
bool operator==(const Color3B& right) const;
|
||||
bool operator==(const Color4B& right) const;
|
||||
bool operator!=(const Color4F& right) const;
|
||||
bool operator!=(const Color3B& right) const;
|
||||
bool operator!=(const Color4B& right) const;
|
||||
|
||||
bool equals(const Color4F &other) const
|
||||
{
|
||||
return (*this == other);
|
||||
}
|
||||
|
||||
void set(float _r, float _g, float _b, float _a)
|
||||
{
|
||||
r = _r;
|
||||
g = _g;
|
||||
b = _b;
|
||||
a = _a;
|
||||
}
|
||||
|
||||
GLfloat r;
|
||||
GLfloat g;
|
||||
GLfloat b;
|
||||
GLfloat a;
|
||||
|
||||
static const Color4F WHITE;
|
||||
static const Color4F YELLOW;
|
||||
static const Color4F BLUE;
|
||||
static const Color4F GREEN;
|
||||
static const Color4F RED;
|
||||
static const Color4F MAGENTA;
|
||||
static const Color4F BLACK;
|
||||
static const Color4F ORANGE;
|
||||
static const Color4F GRAY;
|
||||
};
|
||||
|
||||
struct CC_DLL Color3F
|
||||
{
|
||||
Color3F();
|
||||
Color3F(float _r, float _g, float _b);
|
||||
|
||||
void set(float _r, float _g, float _b)
|
||||
{
|
||||
r = _r;
|
||||
g = _g;
|
||||
b = _b;
|
||||
}
|
||||
|
||||
GLfloat r;
|
||||
GLfloat g;
|
||||
GLfloat b;
|
||||
|
||||
static const Color3F BLACK;
|
||||
};
|
||||
|
||||
/** @struct BlendFunc
|
||||
* Blend Function used for textures.
|
||||
*/
|
||||
struct CC_DLL BlendFunc
|
||||
{
|
||||
/** source blend function */
|
||||
GLenum src;
|
||||
/** destination blend function */
|
||||
GLenum dst;
|
||||
|
||||
/** Blending disabled. Uses {GL_ONE, GL_ZERO} */
|
||||
static const BlendFunc DISABLE;
|
||||
/** Blending enabled for textures with Alpha premultiplied. Uses {GL_ONE, GL_ONE_MINUS_SRC_ALPHA} */
|
||||
static const BlendFunc ALPHA_PREMULTIPLIED;
|
||||
/** Blending enabled for textures with Alpha NON premultiplied. Uses {GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA} */
|
||||
static const BlendFunc ALPHA_NON_PREMULTIPLIED;
|
||||
/** Enables Additive blending. Uses {GL_SRC_ALPHA, GL_ONE} */
|
||||
static const BlendFunc ADDITIVE;
|
||||
|
||||
bool operator==(const BlendFunc &a) const
|
||||
{
|
||||
return src == a.src && dst == a.dst;
|
||||
}
|
||||
|
||||
bool operator!=(const BlendFunc &a) const
|
||||
{
|
||||
return src != a.src || dst != a.dst;
|
||||
}
|
||||
|
||||
bool operator<(const BlendFunc &a) const
|
||||
{
|
||||
return src < a.src || (src == a.src && dst < a.dst);
|
||||
}
|
||||
};
|
||||
|
||||
extern const std::string CC_DLL STD_STRING_EMPTY;
|
||||
extern const ssize_t CC_DLL CC_INVALID_INDEX;
|
||||
|
||||
NS_CC_END
|
||||
// end group
|
||||
/// @}
|
||||
#endif //__BASE_CCTYPES_H__
|
||||
|
||||
399
cocos2d-x/cocos/base/ccUTF8.cpp
Normal file
399
cocos2d-x/cocos/base/ccUTF8.cpp
Normal file
@@ -0,0 +1,399 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2014 cocos2d-x.org
|
||||
Copyright (c) 2014-2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
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 "base/ccUTF8.h"
|
||||
#include "ConvertUTF/ConvertUTF.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
NS_CC_BEGIN
|
||||
|
||||
namespace StringUtils {
|
||||
|
||||
std::string format(const char* format, ...)
|
||||
{
|
||||
#define CC_MAX_STRING_LENGTH (1024*100)
|
||||
|
||||
std::string ret;
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
|
||||
char* buf = (char*)malloc(CC_MAX_STRING_LENGTH);
|
||||
if (buf != nullptr)
|
||||
{
|
||||
vsnprintf(buf, CC_MAX_STRING_LENGTH, format, ap);
|
||||
ret = buf;
|
||||
free(buf);
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* @str: the string to search through.
|
||||
* @c: the character to not look for.
|
||||
*
|
||||
* Return value: the index of the last character that is not c.
|
||||
* */
|
||||
unsigned int getIndexOfLastNotChar16(const std::vector<char16_t>& str, char16_t c)
|
||||
{
|
||||
int len = static_cast<int>(str.size());
|
||||
|
||||
int i = len - 1;
|
||||
for (; i >= 0; --i)
|
||||
if (str[i] != c) return i;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
/*
|
||||
* @str: the string to trim
|
||||
* @index: the index to start trimming from.
|
||||
*
|
||||
* Trims str st str=[0, index) after the operation.
|
||||
*
|
||||
* Return value: the trimmed string.
|
||||
* */
|
||||
static void trimUTF16VectorFromIndex(std::vector<char16_t>& str, int index)
|
||||
{
|
||||
int size = static_cast<int>(str.size());
|
||||
if (index >= size || index < 0)
|
||||
return;
|
||||
|
||||
str.erase(str.begin() + index, str.begin() + size);
|
||||
}
|
||||
|
||||
/*
|
||||
* @ch is the unicode character whitespace?
|
||||
*
|
||||
* Reference: http://en.wikipedia.org/wiki/Whitespace_character#Unicode
|
||||
*
|
||||
* Return value: weather the character is a whitespace character.
|
||||
* */
|
||||
bool isUnicodeSpace(char16_t ch)
|
||||
{
|
||||
return (ch >= 0x0009 && ch <= 0x000D) || ch == 0x0020 || ch == 0x0085 || ch == 0x00A0 || ch == 0x1680
|
||||
|| (ch >= 0x2000 && ch <= 0x200A) || ch == 0x2028 || ch == 0x2029 || ch == 0x202F
|
||||
|| ch == 0x205F || ch == 0x3000;
|
||||
}
|
||||
|
||||
bool isCJKUnicode(char16_t ch)
|
||||
{
|
||||
return (ch >= 0x4E00 && ch <= 0x9FBF) // CJK Unified Ideographs
|
||||
|| (ch >= 0x2E80 && ch <= 0x2FDF) // CJK Radicals Supplement & Kangxi Radicals
|
||||
|| (ch >= 0x2FF0 && ch <= 0x30FF) // Ideographic Description Characters, CJK Symbols and Punctuation & Japanese
|
||||
|| (ch >= 0x3100 && ch <= 0x31BF) // Korean
|
||||
|| (ch >= 0xAC00 && ch <= 0xD7AF) // Hangul Syllables
|
||||
|| (ch >= 0xF900 && ch <= 0xFAFF) // CJK Compatibility Ideographs
|
||||
|| (ch >= 0xFE30 && ch <= 0xFE4F) // CJK Compatibility Forms
|
||||
|| (ch >= 0x31C0 && ch <= 0x4DFF); // Other extensions
|
||||
}
|
||||
|
||||
void trimUTF16Vector(std::vector<char16_t>& str)
|
||||
{
|
||||
int len = static_cast<int>(str.size());
|
||||
|
||||
if ( len <= 0 )
|
||||
return;
|
||||
|
||||
int last_index = len - 1;
|
||||
|
||||
// Only start trimming if the last character is whitespace..
|
||||
if (isUnicodeSpace(str[last_index]))
|
||||
{
|
||||
for (int i = last_index - 1; i >= 0; --i)
|
||||
{
|
||||
if (isUnicodeSpace(str[i]))
|
||||
last_index = i;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
trimUTF16VectorFromIndex(str, last_index);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
struct ConvertTrait {
|
||||
typedef T ArgType;
|
||||
};
|
||||
template <>
|
||||
struct ConvertTrait<char> {
|
||||
typedef UTF8 ArgType;
|
||||
};
|
||||
template <>
|
||||
struct ConvertTrait<char16_t> {
|
||||
typedef UTF16 ArgType;
|
||||
};
|
||||
template <>
|
||||
struct ConvertTrait<char32_t> {
|
||||
typedef UTF32 ArgType;
|
||||
};
|
||||
|
||||
template <typename From, typename To, typename FromTrait = ConvertTrait<From>, typename ToTrait = ConvertTrait<To>>
|
||||
bool utfConvert(
|
||||
const std::basic_string<From>& from, std::basic_string<To>& to,
|
||||
ConversionResult(*cvtfunc)(const typename FromTrait::ArgType**, const typename FromTrait::ArgType*,
|
||||
typename ToTrait::ArgType**, typename ToTrait::ArgType*,
|
||||
ConversionFlags)
|
||||
)
|
||||
{
|
||||
static_assert(sizeof(From) == sizeof(typename FromTrait::ArgType), "Error size mismatched");
|
||||
static_assert(sizeof(To) == sizeof(typename ToTrait::ArgType), "Error size mismatched");
|
||||
|
||||
if (from.empty())
|
||||
{
|
||||
to.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
// See: http://unicode.org/faq/utf_bom.html#gen6
|
||||
static const int most_bytes_per_character = 4;
|
||||
|
||||
const size_t maxNumberOfChars = from.length(); // all UTFs at most one element represents one character.
|
||||
const size_t numberOfOut = maxNumberOfChars * most_bytes_per_character / sizeof(To);
|
||||
|
||||
std::basic_string<To> working(numberOfOut, 0);
|
||||
|
||||
auto inbeg = reinterpret_cast<const typename FromTrait::ArgType*>(&from[0]);
|
||||
auto inend = inbeg + from.length();
|
||||
|
||||
|
||||
auto outbeg = reinterpret_cast<typename ToTrait::ArgType*>(&working[0]);
|
||||
auto outend = outbeg + working.length();
|
||||
auto r = cvtfunc(&inbeg, inend, &outbeg, outend, strictConversion);
|
||||
if (r != conversionOK)
|
||||
return false;
|
||||
|
||||
working.resize(reinterpret_cast<To*>(outbeg) - &working[0]);
|
||||
to = std::move(working);
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
CC_DLL void UTF8LooseFix(const std::string &in, std::string &out)
|
||||
{
|
||||
const UTF8 *p = (const UTF8*) in.c_str();
|
||||
const UTF8 *end = (const UTF8*) (in.c_str() + in.size());
|
||||
unsigned ucharLen = 0;
|
||||
while(p < end) {
|
||||
ucharLen = getNumBytesForUTF8(*p);
|
||||
if(isLegalUTF8Sequence(p, p + ucharLen)) {
|
||||
if( p + ucharLen < end) {
|
||||
out.append(p, p+ucharLen);
|
||||
}
|
||||
p += ucharLen;
|
||||
} else {
|
||||
p += 1; //skip bad char
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool UTF8ToUTF16(const std::string& utf8, std::u16string& outUtf16)
|
||||
{
|
||||
return utfConvert(utf8, outUtf16, ConvertUTF8toUTF16);
|
||||
}
|
||||
|
||||
bool UTF8ToUTF32(const std::string& utf8, std::u32string& outUtf32)
|
||||
{
|
||||
return utfConvert(utf8, outUtf32, ConvertUTF8toUTF32);
|
||||
}
|
||||
|
||||
bool UTF16ToUTF8(const std::u16string& utf16, std::string& outUtf8)
|
||||
{
|
||||
return utfConvert(utf16, outUtf8, ConvertUTF16toUTF8);
|
||||
}
|
||||
|
||||
bool UTF16ToUTF32(const std::u16string& utf16, std::u32string& outUtf32)
|
||||
{
|
||||
return utfConvert(utf16, outUtf32, ConvertUTF16toUTF32);
|
||||
}
|
||||
|
||||
bool UTF32ToUTF8(const std::u32string& utf32, std::string& outUtf8)
|
||||
{
|
||||
return utfConvert(utf32, outUtf8, ConvertUTF32toUTF8);
|
||||
}
|
||||
|
||||
bool UTF32ToUTF16(const std::u32string& utf32, std::u16string& outUtf16)
|
||||
{
|
||||
return utfConvert(utf32, outUtf16, ConvertUTF32toUTF16);
|
||||
}
|
||||
|
||||
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
|
||||
std::string getStringUTFCharsJNI(JNIEnv* env, jstring srcjStr, bool* ret)
|
||||
{
|
||||
std::string utf8Str;
|
||||
const unsigned short * unicodeChar = ( const unsigned short *)env->GetStringChars(srcjStr, nullptr);
|
||||
size_t unicodeCharLength = env->GetStringLength(srcjStr);
|
||||
const std::u16string unicodeStr((const char16_t *)unicodeChar, unicodeCharLength);
|
||||
bool flag = UTF16ToUTF8(unicodeStr, utf8Str);
|
||||
|
||||
if (ret)
|
||||
{
|
||||
*ret = flag;
|
||||
}
|
||||
|
||||
if (!flag)
|
||||
{
|
||||
utf8Str = "";
|
||||
}
|
||||
env->ReleaseStringChars(srcjStr, unicodeChar);
|
||||
return utf8Str;
|
||||
}
|
||||
|
||||
jstring newStringUTFJNI(JNIEnv* env, const std::string& utf8Str, bool* ret)
|
||||
{
|
||||
std::u16string utf16Str;
|
||||
bool flag = cocos2d::StringUtils::UTF8ToUTF16(utf8Str, utf16Str);
|
||||
|
||||
if (ret)
|
||||
{
|
||||
*ret = flag;
|
||||
}
|
||||
|
||||
if(!flag)
|
||||
{
|
||||
utf16Str.clear();
|
||||
}
|
||||
jstring stringText = env->NewString((const jchar*)utf16Str.data(), utf16Str.length());
|
||||
return stringText;
|
||||
}
|
||||
#endif
|
||||
|
||||
std::vector<char16_t> getChar16VectorFromUTF16String(const std::u16string& utf16)
|
||||
{
|
||||
return std::vector<char16_t>(utf16.begin(), utf16.end());
|
||||
}
|
||||
|
||||
long getCharacterCountInUTF8String(const std::string& utf8)
|
||||
{
|
||||
return getUTF8StringLength((const UTF8*)utf8.c_str());
|
||||
}
|
||||
|
||||
|
||||
StringUTF8::StringUTF8()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
StringUTF8::StringUTF8(const std::string& newStr)
|
||||
{
|
||||
replace(newStr);
|
||||
}
|
||||
|
||||
StringUTF8::~StringUTF8()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
std::size_t StringUTF8::length() const
|
||||
{
|
||||
return _str.size();
|
||||
}
|
||||
|
||||
void StringUTF8::replace(const std::string& newStr)
|
||||
{
|
||||
_str.clear();
|
||||
if (!newStr.empty())
|
||||
{
|
||||
UTF8* sequenceUtf8 = (UTF8*)newStr.c_str();
|
||||
|
||||
int lengthString = getUTF8StringLength(sequenceUtf8);
|
||||
|
||||
if (lengthString == 0)
|
||||
{
|
||||
CCLOG("Bad utf-8 set string: %s", newStr.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
while (*sequenceUtf8)
|
||||
{
|
||||
std::size_t lengthChar = getNumBytesForUTF8(*sequenceUtf8);
|
||||
|
||||
CharUTF8 charUTF8;
|
||||
charUTF8._char.append((char*)sequenceUtf8, lengthChar);
|
||||
sequenceUtf8 += lengthChar;
|
||||
|
||||
_str.push_back(charUTF8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string StringUTF8::getAsCharSequence() const
|
||||
{
|
||||
std::string charSequence;
|
||||
|
||||
for (auto& charUtf8 : _str)
|
||||
{
|
||||
charSequence.append(charUtf8._char);
|
||||
}
|
||||
|
||||
return charSequence;
|
||||
}
|
||||
|
||||
bool StringUTF8::deleteChar(std::size_t pos)
|
||||
{
|
||||
if (pos < _str.size())
|
||||
{
|
||||
_str.erase(_str.begin() + pos);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool StringUTF8::insert(std::size_t pos, const std::string& insertStr)
|
||||
{
|
||||
StringUTF8 utf8(insertStr);
|
||||
|
||||
return insert(pos, utf8);
|
||||
}
|
||||
|
||||
bool StringUTF8::insert(std::size_t pos, const StringUTF8& insertStr)
|
||||
{
|
||||
if (pos <= _str.size())
|
||||
{
|
||||
_str.insert(_str.begin() + pos, insertStr._str.begin(), insertStr._str.end());
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
} //namespace StringUtils {
|
||||
|
||||
|
||||
NS_CC_END
|
||||
226
cocos2d-x/cocos/base/ccUTF8.h
Normal file
226
cocos2d-x/cocos/base/ccUTF8.h
Normal file
@@ -0,0 +1,226 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2014 cocos2d-x.org
|
||||
Copyright (c) 2014-2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
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 __cocos2dx__ccUTF8__
|
||||
#define __cocos2dx__ccUTF8__
|
||||
|
||||
#include "base/ccMacros.h"
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
|
||||
#include "platform/android/jni/JniHelper.h"
|
||||
#endif
|
||||
|
||||
NS_CC_BEGIN
|
||||
|
||||
namespace StringUtils {
|
||||
|
||||
template<typename T>
|
||||
std::string toString(T arg)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << arg;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string CC_DLL format(const char* format, ...) CC_FORMAT_PRINTF(1, 2);
|
||||
|
||||
/**
|
||||
* @brief Converts from UTF8 string to UTF16 string.
|
||||
*
|
||||
* This function resizes \p outUtf16 to required size and
|
||||
* fill its contents with result UTF16 string if conversion success.
|
||||
* If conversion fails it guarantees not to change \p outUtf16.
|
||||
*
|
||||
* @param inUtf8 The source UTF8 string to be converted from.
|
||||
* @param outUtf16 The output string to hold the result UTF16s.
|
||||
* @return True if succeed, otherwise false.
|
||||
* @note Please check the return value before using \p outUtf16
|
||||
* e.g.
|
||||
* @code
|
||||
* std::u16string utf16;
|
||||
* bool ret = StringUtils::UTF8ToUTF16("你好hello", utf16);
|
||||
* if (ret) {
|
||||
* do_some_thing_with_utf16(utf16);
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
CC_DLL bool UTF8ToUTF16(const std::string& inUtf8, std::u16string& outUtf16);
|
||||
|
||||
/**
|
||||
* @brief Same as \a UTF8ToUTF16 but converts form UTF8 to UTF32.
|
||||
*
|
||||
* @see UTF8ToUTF16
|
||||
*/
|
||||
CC_DLL bool UTF8ToUTF32(const std::string& inUtf8, std::u32string& outUtf32);
|
||||
|
||||
/**
|
||||
* @brief Same as \a UTF8ToUTF16 but converts form UTF16 to UTF8.
|
||||
*
|
||||
* @see UTF8ToUTF16
|
||||
*/
|
||||
CC_DLL bool UTF16ToUTF8(const std::u16string& inUtf16, std::string& outUtf8);
|
||||
|
||||
/**
|
||||
* @brief Same as \a UTF8ToUTF16 but converts form UTF16 to UTF32.
|
||||
*
|
||||
* @see UTF8ToUTF16
|
||||
*/
|
||||
CC_DLL bool UTF16ToUTF32(const std::u16string& inUtf16, std::u32string& outUtf32);
|
||||
|
||||
/**
|
||||
* @brief Same as \a UTF8ToUTF16 but converts form UTF32 to UTF8.
|
||||
*
|
||||
* @see UTF8ToUTF16
|
||||
*/
|
||||
CC_DLL bool UTF32ToUTF8(const std::u32string& inUtf32, std::string& outUtf8);
|
||||
|
||||
/**
|
||||
* @brief Same as \a UTF8ToUTF16 but converts form UTF32 to UTF16.
|
||||
*
|
||||
* @see UTF8ToUTF16
|
||||
*/
|
||||
CC_DLL bool UTF32ToUTF16(const std::u32string& inUtf32, std::u16string& outUtf16);
|
||||
|
||||
/**
|
||||
* @brief Skip some bad char code.
|
||||
*/
|
||||
CC_DLL void UTF8LooseFix(const std::string &in, std::string &out);
|
||||
|
||||
|
||||
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
|
||||
|
||||
|
||||
/**
|
||||
* @brief convert jstring to utf8 std::string, same function with env->getStringUTFChars.
|
||||
* because getStringUTFChars can not pass special emoticon
|
||||
* @param env The JNI Env
|
||||
* @param srcjStr The jstring which want to convert
|
||||
* @param ret True if the conversion succeeds and the ret pointer isn't null
|
||||
* @returns the result of utf8 string
|
||||
*/
|
||||
CC_DLL std::string getStringUTFCharsJNI(JNIEnv* env, jstring srcjStr, bool* ret = nullptr);
|
||||
|
||||
/**
|
||||
* @brief create a jstring with utf8 std::string, same function with env->newStringUTF
|
||||
* because newStringUTF can not convert special emoticon
|
||||
* @param env The JNI Env
|
||||
* @param srcjStr The std::string which want to convert
|
||||
* @param ret True if the conversion succeeds and the ret pointer isn't null
|
||||
* @returns the result of jstring,the jstring need to DeleteLocalRef(jstring);
|
||||
*/
|
||||
CC_DLL jstring newStringUTFJNI(JNIEnv* env, const std::string& utf8Str, bool* ret = nullptr);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Trims the unicode spaces at the end of char16_t vector.
|
||||
*/
|
||||
CC_DLL void trimUTF16Vector(std::vector<char16_t>& str);
|
||||
|
||||
/**
|
||||
* @brief Whether the character is a whitespace character.
|
||||
* @param ch The unicode character.
|
||||
* @returns Whether the character is a white space character.
|
||||
*
|
||||
* @see http://en.wikipedia.org/wiki/Whitespace_character#Unicode
|
||||
*
|
||||
*/
|
||||
CC_DLL bool isUnicodeSpace(char16_t ch);
|
||||
|
||||
/**
|
||||
* @brief Whether the character is a Chinese/Japanese/Korean character.
|
||||
* @param ch The unicode character.
|
||||
* @returns Whether the character is a Chinese character.
|
||||
*
|
||||
* @see http://www.searchtb.com/2012/04/chinese_encode.html
|
||||
* @see http://tieba.baidu.com/p/748765987
|
||||
*
|
||||
*/
|
||||
CC_DLL bool isCJKUnicode(char16_t ch);
|
||||
|
||||
/**
|
||||
* @brief Returns the length of the string in characters.
|
||||
* @param utf8 An UTF-8 encoded string.
|
||||
* @returns The length of the string in characters.
|
||||
*/
|
||||
CC_DLL long getCharacterCountInUTF8String(const std::string& utf8);
|
||||
|
||||
/**
|
||||
* @brief Gets the index of the last character that is not equal to the character given.
|
||||
* @param str The string to be searched.
|
||||
* @param c The character to be searched for.
|
||||
* @returns The index of the last character that is not \p c.
|
||||
*/
|
||||
CC_DLL unsigned int getIndexOfLastNotChar16(const std::vector<char16_t>& str, char16_t c);
|
||||
|
||||
/**
|
||||
* @brief Gets char16_t vector from a given utf16 string.
|
||||
*/
|
||||
CC_DLL std::vector<char16_t> getChar16VectorFromUTF16String(const std::u16string& utf16);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Utf8 sequence
|
||||
* Store all utf8 chars as std::string
|
||||
* Build from std::string
|
||||
*/
|
||||
class CC_DLL StringUTF8
|
||||
{
|
||||
public:
|
||||
struct CharUTF8
|
||||
{
|
||||
std::string _char;
|
||||
bool isAnsi() { return _char.size() == 1; }
|
||||
};
|
||||
typedef std::vector<CharUTF8> CharUTF8Store;
|
||||
|
||||
StringUTF8();
|
||||
StringUTF8(const std::string& newStr);
|
||||
~StringUTF8();
|
||||
|
||||
std::size_t length() const;
|
||||
void replace(const std::string& newStr);
|
||||
|
||||
std::string getAsCharSequence() const;
|
||||
|
||||
bool deleteChar(std::size_t pos);
|
||||
bool insert(std::size_t pos, const std::string& insertStr);
|
||||
bool insert(std::size_t pos, const StringUTF8& insertStr);
|
||||
|
||||
CharUTF8Store& getString() { return _str; }
|
||||
|
||||
private:
|
||||
CharUTF8Store _str;
|
||||
};
|
||||
|
||||
} // namespace StringUtils {
|
||||
|
||||
NS_CC_END
|
||||
|
||||
#endif /** defined(__cocos2dx__ccUTF8__) */
|
||||
107
cocos2d-x/cocos/base/ccUtils.cpp
Normal file
107
cocos2d-x/cocos/base/ccUtils.cpp
Normal file
@@ -0,0 +1,107 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2010 cocos2d-x.org
|
||||
Copyright (c) 2013-2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
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 "base/ccUtils.h"
|
||||
#include "base/base64.h"
|
||||
#include "platform/CCFileUtils.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <stdlib.h>
|
||||
|
||||
NS_CC_BEGIN
|
||||
|
||||
namespace
|
||||
{
|
||||
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
|
||||
#include "platform/CCStdC.h"
|
||||
int gettimeofday(struct timeval * val, void *)
|
||||
{
|
||||
if (val)
|
||||
{
|
||||
LARGE_INTEGER liTime, liFreq;
|
||||
QueryPerformanceFrequency(&liFreq);
|
||||
QueryPerformanceCounter(&liTime);
|
||||
val->tv_sec = (long)(liTime.QuadPart / liFreq.QuadPart);
|
||||
val->tv_usec = (long)(liTime.QuadPart * 1000000.0 / liFreq.QuadPart - val->tv_sec * 1000000.0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
namespace utils
|
||||
{
|
||||
|
||||
#define MAX_ITOA_BUFFER_SIZE 256
|
||||
double atof(const char* str)
|
||||
{
|
||||
if (str == nullptr)
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
char buf[MAX_ITOA_BUFFER_SIZE];
|
||||
strncpy(buf, str, MAX_ITOA_BUFFER_SIZE);
|
||||
|
||||
// strip string, only remain 7 numbers after '.'
|
||||
char* dot = strchr(buf, '.');
|
||||
if (dot != nullptr && dot - buf + 8 < MAX_ITOA_BUFFER_SIZE)
|
||||
{
|
||||
dot[8] = '\0';
|
||||
}
|
||||
|
||||
return ::atof(buf);
|
||||
}
|
||||
|
||||
double gettime()
|
||||
{
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, nullptr);
|
||||
|
||||
return (double)tv.tv_sec + (double)tv.tv_usec/1000000;
|
||||
}
|
||||
|
||||
long long getTimeInMilliseconds()
|
||||
{
|
||||
struct timeval tv;
|
||||
gettimeofday (&tv, nullptr);
|
||||
return (long long)tv.tv_sec * 1000 + tv.tv_usec / 1000;
|
||||
}
|
||||
|
||||
int nextPOT(int x)
|
||||
{
|
||||
x = x - 1;
|
||||
x = x | (x >> 1);
|
||||
x = x | (x >> 2);
|
||||
x = x | (x >> 4);
|
||||
x = x | (x >> 8);
|
||||
x = x | (x >> 16);
|
||||
return x + 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
NS_CC_END
|
||||
85
cocos2d-x/cocos/base/ccUtils.h
Normal file
85
cocos2d-x/cocos/base/ccUtils.h
Normal file
@@ -0,0 +1,85 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2010 cocos2d-x.org
|
||||
Copyright (c) 2013-2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
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.
|
||||
****************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include "base/ccMacros.h"
|
||||
|
||||
/** @file ccUtils.h
|
||||
Misc free functions
|
||||
*/
|
||||
|
||||
NS_CC_BEGIN
|
||||
|
||||
namespace
|
||||
{
|
||||
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
|
||||
#include <winsock.h>
|
||||
extern "C" int gettimeofday(struct timeval * val, void *);
|
||||
#endif
|
||||
}
|
||||
/*
|
||||
utils::nextPOT function is licensed under the same license that is used in Texture2D.m.
|
||||
*/
|
||||
|
||||
/** Returns the Next Power of Two value.
|
||||
|
||||
Examples:
|
||||
- If "value" is 15, it will return 16.
|
||||
- If "value" is 16, it will return 16.
|
||||
- If "value" is 17, it will return 32.
|
||||
@param value The value to get next power of two.
|
||||
@return Returns the next power of two value.
|
||||
@since v0.99.5
|
||||
*/
|
||||
|
||||
namespace utils
|
||||
{
|
||||
CC_DLL int nextPOT(int x);
|
||||
/** Same to ::atof, but strip the string, remain 7 numbers after '.' before call atof.
|
||||
* Why we need this? Because in android c++_static, atof ( and std::atof ) is unsupported for numbers have long decimal part and contain
|
||||
* several numbers can approximate to 1 ( like 90.099998474121094 ), it will return inf. This function is used to fix this bug.
|
||||
* @param str The string be to converted to double.
|
||||
* @return Returns converted value of a string.
|
||||
*/
|
||||
CC_DLL double atof(const char* str);
|
||||
|
||||
/** Get current exact time, accurate to nanoseconds.
|
||||
* @return Returns the time in seconds since the Epoch.
|
||||
*/
|
||||
CC_DLL double gettime();
|
||||
|
||||
/**
|
||||
* Get current time in milliseconds, accurate to nanoseconds
|
||||
*
|
||||
* @return Returns the time in milliseconds since the Epoch.
|
||||
*/
|
||||
CC_DLL long long getTimeInMilliseconds();
|
||||
}
|
||||
|
||||
NS_CC_END
|
||||
|
||||
290
cocos2d-x/cocos/base/csscolorparser.cpp
Normal file
290
cocos2d-x/cocos/base/csscolorparser.cpp
Normal file
@@ -0,0 +1,290 @@
|
||||
// (c) Dean McNamee <dean@gmail.com>, 2012.
|
||||
// C++ port by Mapbox, Konstantin Käfer <mail@kkaefer.com>, 2014-2017.
|
||||
//
|
||||
// https://github.com/deanm/css-color-parser-js
|
||||
// https://github.com/kkaefer/css-color-parser-cpp
|
||||
//
|
||||
// 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 "csscolorparser.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
namespace CSSColorParser {
|
||||
|
||||
// http://www.w3.org/TR/css3-color/
|
||||
struct NamedColor { const char *const name; const Color color; };
|
||||
|
||||
const std::vector<NamedColor> namedColors = {
|
||||
{ "transparent", { 0, 0, 0, 0 } }, { "aliceblue", { 240, 248, 255, 1 } },
|
||||
{ "antiquewhite", { 250, 235, 215, 1 } }, { "aqua", { 0, 255, 255, 1 } },
|
||||
{ "aquamarine", { 127, 255, 212, 1 } }, { "azure", { 240, 255, 255, 1 } },
|
||||
{ "beige", { 245, 245, 220, 1 } }, { "bisque", { 255, 228, 196, 1 } },
|
||||
{ "black", { 0, 0, 0, 1 } }, { "blanchedalmond", { 255, 235, 205, 1 } },
|
||||
{ "blue", { 0, 0, 255, 1 } }, { "blueviolet", { 138, 43, 226, 1 } },
|
||||
{ "brown", { 165, 42, 42, 1 } }, { "burlywood", { 222, 184, 135, 1 } },
|
||||
{ "cadetblue", { 95, 158, 160, 1 } }, { "chartreuse", { 127, 255, 0, 1 } },
|
||||
{ "chocolate", { 210, 105, 30, 1 } }, { "coral", { 255, 127, 80, 1 } },
|
||||
{ "cornflowerblue", { 100, 149, 237, 1 } }, { "cornsilk", { 255, 248, 220, 1 } },
|
||||
{ "crimson", { 220, 20, 60, 1 } }, { "cyan", { 0, 255, 255, 1 } },
|
||||
{ "darkblue", { 0, 0, 139, 1 } }, { "darkcyan", { 0, 139, 139, 1 } },
|
||||
{ "darkgoldenrod", { 184, 134, 11, 1 } }, { "darkgray", { 169, 169, 169, 1 } },
|
||||
{ "darkgreen", { 0, 100, 0, 1 } }, { "darkgrey", { 169, 169, 169, 1 } },
|
||||
{ "darkkhaki", { 189, 183, 107, 1 } }, { "darkmagenta", { 139, 0, 139, 1 } },
|
||||
{ "darkolivegreen", { 85, 107, 47, 1 } }, { "darkorange", { 255, 140, 0, 1 } },
|
||||
{ "darkorchid", { 153, 50, 204, 1 } }, { "darkred", { 139, 0, 0, 1 } },
|
||||
{ "darksalmon", { 233, 150, 122, 1 } }, { "darkseagreen", { 143, 188, 143, 1 } },
|
||||
{ "darkslateblue", { 72, 61, 139, 1 } }, { "darkslategray", { 47, 79, 79, 1 } },
|
||||
{ "darkslategrey", { 47, 79, 79, 1 } }, { "darkturquoise", { 0, 206, 209, 1 } },
|
||||
{ "darkviolet", { 148, 0, 211, 1 } }, { "deeppink", { 255, 20, 147, 1 } },
|
||||
{ "deepskyblue", { 0, 191, 255, 1 } }, { "dimgray", { 105, 105, 105, 1 } },
|
||||
{ "dimgrey", { 105, 105, 105, 1 } }, { "dodgerblue", { 30, 144, 255, 1 } },
|
||||
{ "firebrick", { 178, 34, 34, 1 } }, { "floralwhite", { 255, 250, 240, 1 } },
|
||||
{ "forestgreen", { 34, 139, 34, 1 } }, { "fuchsia", { 255, 0, 255, 1 } },
|
||||
{ "gainsboro", { 220, 220, 220, 1 } }, { "ghostwhite", { 248, 248, 255, 1 } },
|
||||
{ "gold", { 255, 215, 0, 1 } }, { "goldenrod", { 218, 165, 32, 1 } },
|
||||
{ "gray", { 128, 128, 128, 1 } }, { "green", { 0, 128, 0, 1 } },
|
||||
{ "greenyellow", { 173, 255, 47, 1 } }, { "grey", { 128, 128, 128, 1 } },
|
||||
{ "honeydew", { 240, 255, 240, 1 } }, { "hotpink", { 255, 105, 180, 1 } },
|
||||
{ "indianred", { 205, 92, 92, 1 } }, { "indigo", { 75, 0, 130, 1 } },
|
||||
{ "ivory", { 255, 255, 240, 1 } }, { "khaki", { 240, 230, 140, 1 } },
|
||||
{ "lavender", { 230, 230, 250, 1 } }, { "lavenderblush", { 255, 240, 245, 1 } },
|
||||
{ "lawngreen", { 124, 252, 0, 1 } }, { "lemonchiffon", { 255, 250, 205, 1 } },
|
||||
{ "lightblue", { 173, 216, 230, 1 } }, { "lightcoral", { 240, 128, 128, 1 } },
|
||||
{ "lightcyan", { 224, 255, 255, 1 } }, { "lightgoldenrodyellow", { 250, 250, 210, 1 } },
|
||||
{ "lightgray", { 211, 211, 211, 1 } }, { "lightgreen", { 144, 238, 144, 1 } },
|
||||
{ "lightgrey", { 211, 211, 211, 1 } }, { "lightpink", { 255, 182, 193, 1 } },
|
||||
{ "lightsalmon", { 255, 160, 122, 1 } }, { "lightseagreen", { 32, 178, 170, 1 } },
|
||||
{ "lightskyblue", { 135, 206, 250, 1 } }, { "lightslategray", { 119, 136, 153, 1 } },
|
||||
{ "lightslategrey", { 119, 136, 153, 1 } }, { "lightsteelblue", { 176, 196, 222, 1 } },
|
||||
{ "lightyellow", { 255, 255, 224, 1 } }, { "lime", { 0, 255, 0, 1 } },
|
||||
{ "limegreen", { 50, 205, 50, 1 } }, { "linen", { 250, 240, 230, 1 } },
|
||||
{ "magenta", { 255, 0, 255, 1 } }, { "maroon", { 128, 0, 0, 1 } },
|
||||
{ "mediumaquamarine", { 102, 205, 170, 1 } }, { "mediumblue", { 0, 0, 205, 1 } },
|
||||
{ "mediumorchid", { 186, 85, 211, 1 } }, { "mediumpurple", { 147, 112, 219, 1 } },
|
||||
{ "mediumseagreen", { 60, 179, 113, 1 } }, { "mediumslateblue", { 123, 104, 238, 1 } },
|
||||
{ "mediumspringgreen", { 0, 250, 154, 1 } }, { "mediumturquoise", { 72, 209, 204, 1 } },
|
||||
{ "mediumvioletred", { 199, 21, 133, 1 } }, { "midnightblue", { 25, 25, 112, 1 } },
|
||||
{ "mintcream", { 245, 255, 250, 1 } }, { "mistyrose", { 255, 228, 225, 1 } },
|
||||
{ "moccasin", { 255, 228, 181, 1 } }, { "navajowhite", { 255, 222, 173, 1 } },
|
||||
{ "navy", { 0, 0, 128, 1 } }, { "oldlace", { 253, 245, 230, 1 } },
|
||||
{ "olive", { 128, 128, 0, 1 } }, { "olivedrab", { 107, 142, 35, 1 } },
|
||||
{ "orange", { 255, 165, 0, 1 } }, { "orangered", { 255, 69, 0, 1 } },
|
||||
{ "orchid", { 218, 112, 214, 1 } }, { "palegoldenrod", { 238, 232, 170, 1 } },
|
||||
{ "palegreen", { 152, 251, 152, 1 } }, { "paleturquoise", { 175, 238, 238, 1 } },
|
||||
{ "palevioletred", { 219, 112, 147, 1 } }, { "papayawhip", { 255, 239, 213, 1 } },
|
||||
{ "peachpuff", { 255, 218, 185, 1 } }, { "peru", { 205, 133, 63, 1 } },
|
||||
{ "pink", { 255, 192, 203, 1 } }, { "plum", { 221, 160, 221, 1 } },
|
||||
{ "powderblue", { 176, 224, 230, 1 } }, { "purple", { 128, 0, 128, 1 } },
|
||||
{ "red", { 255, 0, 0, 1 } }, { "rosybrown", { 188, 143, 143, 1 } },
|
||||
{ "royalblue", { 65, 105, 225, 1 } }, { "saddlebrown", { 139, 69, 19, 1 } },
|
||||
{ "salmon", { 250, 128, 114, 1 } }, { "sandybrown", { 244, 164, 96, 1 } },
|
||||
{ "seagreen", { 46, 139, 87, 1 } }, { "seashell", { 255, 245, 238, 1 } },
|
||||
{ "sienna", { 160, 82, 45, 1 } }, { "silver", { 192, 192, 192, 1 } },
|
||||
{ "skyblue", { 135, 206, 235, 1 } }, { "slateblue", { 106, 90, 205, 1 } },
|
||||
{ "slategray", { 112, 128, 144, 1 } }, { "slategrey", { 112, 128, 144, 1 } },
|
||||
{ "snow", { 255, 250, 250, 1 } }, { "springgreen", { 0, 255, 127, 1 } },
|
||||
{ "steelblue", { 70, 130, 180, 1 } }, { "tan", { 210, 180, 140, 1 } },
|
||||
{ "teal", { 0, 128, 128, 1 } }, { "thistle", { 216, 191, 216, 1 } },
|
||||
{ "tomato", { 255, 99, 71, 1 } }, { "turquoise", { 64, 224, 208, 1 } },
|
||||
{ "violet", { 238, 130, 238, 1 } }, { "wheat", { 245, 222, 179, 1 } },
|
||||
{ "white", { 255, 255, 255, 1 } }, { "whitesmoke", { 245, 245, 245, 1 } },
|
||||
{ "yellow", { 255, 255, 0, 1 } }, { "yellowgreen", { 154, 205, 50, 1 } }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
uint8_t clamp_css_byte(T i) { // Clamp to integer 0 .. 255.
|
||||
i = ::round(i); // Seems to be what Chrome does (vs truncation).
|
||||
return i < 0 ? 0 : i > 255 ? 255 : uint8_t(i);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
float clamp_css_float(T f) { // Clamp to float 0.0 .. 1.0.
|
||||
return f < 0 ? 0 : f > 1 ? 1 : float(f);
|
||||
}
|
||||
|
||||
float parseFloat(const std::string& str) {
|
||||
return strtof(str.c_str(), nullptr);
|
||||
}
|
||||
|
||||
int64_t parseInt(const std::string& str, uint8_t base = 10) {
|
||||
return strtoll(str.c_str(), nullptr, base);
|
||||
}
|
||||
|
||||
uint8_t parse_css_int(const std::string& str) { // int or percentage.
|
||||
if (str.length() && str.back() == '%') {
|
||||
return clamp_css_byte(parseFloat(str) / 100.0f * 255.0f);
|
||||
} else {
|
||||
return clamp_css_byte(parseInt(str));
|
||||
}
|
||||
}
|
||||
|
||||
float parse_css_float(const std::string& str) { // float or percentage.
|
||||
if (str.length() && str.back() == '%') {
|
||||
return clamp_css_float(parseFloat(str) / 100.0f);
|
||||
} else {
|
||||
return clamp_css_float(parseFloat(str));
|
||||
}
|
||||
}
|
||||
|
||||
float css_hue_to_rgb(float m1, float m2, float h) {
|
||||
if (h < 0.0f) {
|
||||
h += 1.0f;
|
||||
} else if (h > 1.0f) {
|
||||
h -= 1.0f;
|
||||
}
|
||||
|
||||
if (h * 6.0f < 1.0f) {
|
||||
return m1 + (m2 - m1) * h * 6.0f;
|
||||
}
|
||||
if (h * 2.0f < 1.0f) {
|
||||
return m2;
|
||||
}
|
||||
if (h * 3.0f < 2.0f) {
|
||||
return m1 + (m2 - m1) * (2.0f / 3.0f - h) * 6.0f;
|
||||
}
|
||||
return m1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::vector<std::string> split(const std::string& s, char delim) {
|
||||
std::vector<std::string> elems;
|
||||
std::stringstream ss(s);
|
||||
std::string item;
|
||||
while (std::getline(ss, item, delim)) {
|
||||
elems.push_back(item);
|
||||
}
|
||||
return elems;
|
||||
}
|
||||
|
||||
Color parse(const std::string& css_str) {
|
||||
std::string str = css_str;
|
||||
|
||||
// Remove all whitespace, not compliant, but should just be more accepting.
|
||||
str.erase(std::remove(str.begin(), str.end(), ' '), str.end());
|
||||
|
||||
// Convert to lowercase.
|
||||
std::transform(str.begin(), str.end(), str.begin(), ::tolower);
|
||||
|
||||
|
||||
for (size_t i = 0; i < namedColors.size(); i++) {
|
||||
if (str == namedColors[i].name) {
|
||||
return { namedColors[i].color };
|
||||
}
|
||||
}
|
||||
|
||||
// #abc and #abc123 syntax.
|
||||
if (str.length() && str.front() == '#') {
|
||||
if (str.length() == 4) {
|
||||
int64_t iv = parseInt(str.substr(1), 16); // REFINE(deanm): Stricter parsing.
|
||||
if (!(iv >= 0 && iv <= 0xfff)) {
|
||||
return {};
|
||||
} else {
|
||||
return Color(
|
||||
static_cast<uint8_t>(((iv & 0xf00) >> 4) | ((iv & 0xf00) >> 8)),
|
||||
static_cast<uint8_t>((iv & 0xf0) | ((iv & 0xf0) >> 4)),
|
||||
static_cast<uint8_t>((iv & 0xf) | ((iv & 0xf) << 4)),
|
||||
1
|
||||
);
|
||||
}
|
||||
} else if (str.length() == 7) {
|
||||
int64_t iv = parseInt(str.substr(1), 16); // REFINE(deanm): Stricter parsing.
|
||||
if (!(iv >= 0 && iv <= 0xffffff)) {
|
||||
return {}; // Covers NaN.
|
||||
} else {
|
||||
return Color(
|
||||
static_cast<uint8_t>((iv & 0xff0000) >> 16),
|
||||
static_cast<uint8_t>((iv & 0xff00) >> 8),
|
||||
static_cast<uint8_t>(iv & 0xff),
|
||||
1
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return Color();
|
||||
}
|
||||
|
||||
size_t op = str.find_first_of('('), ep = str.find_first_of(')');
|
||||
if (op != std::string::npos && ep + 1 == str.length()) {
|
||||
const std::string fname = str.substr(0, op);
|
||||
const std::vector<std::string> params = split(str.substr(op + 1, ep - (op + 1)), ',');
|
||||
|
||||
float alpha = 1.0f;
|
||||
|
||||
if (fname == "rgba" || fname == "rgb") {
|
||||
if (fname == "rgba") {
|
||||
if (params.size() != 4) {
|
||||
return {};
|
||||
}
|
||||
alpha = parse_css_float(params.back());
|
||||
} else {
|
||||
if (params.size() != 3) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
return Color(
|
||||
parse_css_int(params[0]),
|
||||
parse_css_int(params[1]),
|
||||
parse_css_int(params[2]),
|
||||
alpha
|
||||
);
|
||||
|
||||
} else if (fname == "hsla" || fname == "hsl") {
|
||||
if (fname == "hsla") {
|
||||
if (params.size() != 4) {
|
||||
return {};
|
||||
}
|
||||
alpha = parse_css_float(params.back());
|
||||
} else {
|
||||
if (params.size() != 3) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
float h = parseFloat(params[0]) / 360.0f;
|
||||
while (h < 0.0f) h++;
|
||||
while (h > 1.0f) h--;
|
||||
|
||||
// NOTE(deanm): According to the CSS spec s/l should only be
|
||||
// percentages, but we don't bother and let float or percentage.
|
||||
float s = parse_css_float(params[1]);
|
||||
float l = parse_css_float(params[2]);
|
||||
|
||||
float m2 = l <= 0.5f ? l * (s + 1.0f) : l + s - l * s;
|
||||
float m1 = l * 2.0f - m2;
|
||||
|
||||
return Color(
|
||||
clamp_css_byte(css_hue_to_rgb(m1, m2, h + 1.0f / 3.0f) * 255.0f),
|
||||
clamp_css_byte(css_hue_to_rgb(m1, m2, h) * 255.0f),
|
||||
clamp_css_byte(css_hue_to_rgb(m1, m2, h - 1.0f / 3.0f) * 255.0f),
|
||||
alpha
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace CSSColorParser
|
||||
55
cocos2d-x/cocos/base/csscolorparser.hpp
Normal file
55
cocos2d-x/cocos/base/csscolorparser.hpp
Normal file
@@ -0,0 +1,55 @@
|
||||
// (c) Dean McNamee <dean@gmail.com>, 2012.
|
||||
// C++ port by Mapbox, Konstantin Käfer <mail@kkaefer.com>, 2014-2017.
|
||||
//
|
||||
// https://github.com/deanm/css-color-parser-js
|
||||
// https://github.com/kkaefer/css-color-parser-cpp
|
||||
//
|
||||
// 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 CSS_COLOR_PARSER_CPP
|
||||
#define CSS_COLOR_PARSER_CPP
|
||||
|
||||
#include <string>
|
||||
#include <math.h>
|
||||
|
||||
namespace CSSColorParser {
|
||||
|
||||
struct Color {
|
||||
inline Color() {
|
||||
}
|
||||
inline Color(unsigned char r_, unsigned char g_, unsigned char b_, float a_)
|
||||
: r(r_), g(g_), b(b_), a(a_ > 1 ? 1 : a_ < 0 ? 0 : a_) {
|
||||
}
|
||||
unsigned char r = 0, g = 0, b = 0;
|
||||
float a = 1.0f;
|
||||
};
|
||||
|
||||
inline bool operator==(const Color& lhs, const Color& rhs) {
|
||||
return lhs.r == rhs.r && lhs.g == rhs.g && lhs.b == rhs.b && ::fabs(lhs.a - rhs.a) < 0.0001f;
|
||||
}
|
||||
|
||||
inline bool operator!=(const Color& lhs, const Color& rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
Color parse(const std::string& css_str);
|
||||
|
||||
} // namespace CSSColorParser
|
||||
|
||||
#endif
|
||||
671
cocos2d-x/cocos/base/etc1.cpp
Normal file
671
cocos2d-x/cocos/base/etc1.cpp
Normal file
@@ -0,0 +1,671 @@
|
||||
// Copyright 2009 Google Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "base/etc1.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/* From http://www.khronos.org/registry/gles/extensions/OES/OES_compressed_ETC1_RGB8_texture.txt
|
||||
|
||||
The number of bits that represent a 4x4 texel block is 64 bits if
|
||||
<internalformat> is given by ETC1_RGB8_OES.
|
||||
|
||||
The data for a block is a number of bytes,
|
||||
|
||||
{q0, q1, q2, q3, q4, q5, q6, q7}
|
||||
|
||||
where byte q0 is located at the lowest memory address and q7 at
|
||||
the highest. The 64 bits specifying the block is then represented
|
||||
by the following 64 bit integer:
|
||||
|
||||
int64bit = 256*(256*(256*(256*(256*(256*(256*q0+q1)+q2)+q3)+q4)+q5)+q6)+q7;
|
||||
|
||||
ETC1_RGB8_OES:
|
||||
|
||||
a) bit layout in bits 63 through 32 if diffbit = 0
|
||||
|
||||
63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48
|
||||
-----------------------------------------------
|
||||
| base col1 | base col2 | base col1 | base col2 |
|
||||
| R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)|
|
||||
-----------------------------------------------
|
||||
|
||||
47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32
|
||||
---------------------------------------------------
|
||||
| base col1 | base col2 | table | table |diff|flip|
|
||||
| B1 (4bits)| B2 (4bits)| cw 1 | cw 2 |bit |bit |
|
||||
---------------------------------------------------
|
||||
|
||||
|
||||
b) bit layout in bits 63 through 32 if diffbit = 1
|
||||
|
||||
63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48
|
||||
-----------------------------------------------
|
||||
| base col1 | dcol 2 | base col1 | dcol 2 |
|
||||
| R1' (5 bits) | dR2 | G1' (5 bits) | dG2 |
|
||||
-----------------------------------------------
|
||||
|
||||
47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32
|
||||
---------------------------------------------------
|
||||
| base col 1 | dcol 2 | table | table |diff|flip|
|
||||
| B1' (5 bits) | dB2 | cw 1 | cw 2 |bit |bit |
|
||||
---------------------------------------------------
|
||||
|
||||
|
||||
c) bit layout in bits 31 through 0 (in both cases)
|
||||
|
||||
31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16
|
||||
-----------------------------------------------
|
||||
| most significant pixel index bits |
|
||||
| p| o| n| m| l| k| j| i| h| g| f| e| d| c| b| a|
|
||||
-----------------------------------------------
|
||||
|
||||
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
|
||||
--------------------------------------------------
|
||||
| least significant pixel index bits |
|
||||
| p| o| n| m| l| k| j| i| h| g| f| e| d| c | b | a |
|
||||
--------------------------------------------------
|
||||
|
||||
|
||||
Add table 3.17.2: Intensity modifier sets for ETC1 compressed textures:
|
||||
|
||||
table codeword modifier table
|
||||
------------------ ----------------------
|
||||
0 -8 -2 2 8
|
||||
1 -17 -5 5 17
|
||||
2 -29 -9 9 29
|
||||
3 -42 -13 13 42
|
||||
4 -60 -18 18 60
|
||||
5 -80 -24 24 80
|
||||
6 -106 -33 33 106
|
||||
7 -183 -47 47 183
|
||||
|
||||
|
||||
Add table 3.17.3 Mapping from pixel index values to modifier values for
|
||||
ETC1 compressed textures:
|
||||
|
||||
pixel index value
|
||||
---------------
|
||||
msb lsb resulting modifier value
|
||||
----- ----- -------------------------
|
||||
1 1 -b (large negative value)
|
||||
1 0 -a (small negative value)
|
||||
0 0 a (small positive value)
|
||||
0 1 b (large positive value)
|
||||
|
||||
|
||||
*/
|
||||
|
||||
static const int kModifierTable[] = {
|
||||
/* 0 */2, 8, -2, -8,
|
||||
/* 1 */5, 17, -5, -17,
|
||||
/* 2 */9, 29, -9, -29,
|
||||
/* 3 */13, 42, -13, -42,
|
||||
/* 4 */18, 60, -18, -60,
|
||||
/* 5 */24, 80, -24, -80,
|
||||
/* 6 */33, 106, -33, -106,
|
||||
/* 7 */47, 183, -47, -183 };
|
||||
|
||||
static const int kLookup[8] = { 0, 1, 2, 3, -4, -3, -2, -1 };
|
||||
|
||||
static inline etc1_byte clamp(int x) {
|
||||
return (etc1_byte) (x >= 0 ? (x < 255 ? x : 255) : 0);
|
||||
}
|
||||
|
||||
static
|
||||
inline int convert4To8(int b) {
|
||||
int c = b & 0xf;
|
||||
return (c << 4) | c;
|
||||
}
|
||||
|
||||
static
|
||||
inline int convert5To8(int b) {
|
||||
int c = b & 0x1f;
|
||||
return (c << 3) | (c >> 2);
|
||||
}
|
||||
|
||||
static
|
||||
inline int convert6To8(int b) {
|
||||
int c = b & 0x3f;
|
||||
return (c << 2) | (c >> 4);
|
||||
}
|
||||
|
||||
static
|
||||
inline int divideBy255(int d) {
|
||||
return (d + 128 + (d >> 8)) >> 8;
|
||||
}
|
||||
|
||||
static
|
||||
inline int convert8To4(int b) {
|
||||
int c = b & 0xff;
|
||||
return divideBy255(c * 15);
|
||||
}
|
||||
|
||||
static
|
||||
inline int convert8To5(int b) {
|
||||
int c = b & 0xff;
|
||||
return divideBy255(c * 31);
|
||||
}
|
||||
|
||||
static
|
||||
inline int convertDiff(int base, int diff) {
|
||||
return convert5To8((0x1f & base) + kLookup[0x7 & diff]);
|
||||
}
|
||||
|
||||
static
|
||||
void decode_subblock(etc1_byte* pOut, int r, int g, int b, const int* table,
|
||||
etc1_uint32 low, bool second, bool flipped) {
|
||||
int baseX = 0;
|
||||
int baseY = 0;
|
||||
if (second) {
|
||||
if (flipped) {
|
||||
baseY = 2;
|
||||
} else {
|
||||
baseX = 2;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < 8; i++) {
|
||||
int x, y;
|
||||
if (flipped) {
|
||||
x = baseX + (i >> 1);
|
||||
y = baseY + (i & 1);
|
||||
} else {
|
||||
x = baseX + (i >> 2);
|
||||
y = baseY + (i & 3);
|
||||
}
|
||||
int k = y + (x * 4);
|
||||
int offset = ((low >> k) & 1) | ((low >> (k + 15)) & 2);
|
||||
int delta = table[offset];
|
||||
etc1_byte* q = pOut + 3 * (x + 4 * y);
|
||||
*q++ = clamp(r + delta);
|
||||
*q++ = clamp(g + delta);
|
||||
*q++ = clamp(b + delta);
|
||||
}
|
||||
}
|
||||
|
||||
// Input is an ETC1 compressed version of the data.
|
||||
// Output is a 4 x 4 square of 3-byte pixels in form R, G, B
|
||||
|
||||
void etc1_decode_block(const etc1_byte* pIn, etc1_byte* pOut) {
|
||||
etc1_uint32 high = (pIn[0] << 24) | (pIn[1] << 16) | (pIn[2] << 8) | pIn[3];
|
||||
etc1_uint32 low = (pIn[4] << 24) | (pIn[5] << 16) | (pIn[6] << 8) | pIn[7];
|
||||
int r1, r2, g1, g2, b1, b2;
|
||||
if (high & 2) {
|
||||
// differential
|
||||
int rBase = high >> 27;
|
||||
int gBase = high >> 19;
|
||||
int bBase = high >> 11;
|
||||
r1 = convert5To8(rBase);
|
||||
r2 = convertDiff(rBase, high >> 24);
|
||||
g1 = convert5To8(gBase);
|
||||
g2 = convertDiff(gBase, high >> 16);
|
||||
b1 = convert5To8(bBase);
|
||||
b2 = convertDiff(bBase, high >> 8);
|
||||
} else {
|
||||
// not differential
|
||||
r1 = convert4To8(high >> 28);
|
||||
r2 = convert4To8(high >> 24);
|
||||
g1 = convert4To8(high >> 20);
|
||||
g2 = convert4To8(high >> 16);
|
||||
b1 = convert4To8(high >> 12);
|
||||
b2 = convert4To8(high >> 8);
|
||||
}
|
||||
int tableIndexA = 7 & (high >> 5);
|
||||
int tableIndexB = 7 & (high >> 2);
|
||||
const int* tableA = kModifierTable + tableIndexA * 4;
|
||||
const int* tableB = kModifierTable + tableIndexB * 4;
|
||||
bool flipped = (high & 1) != 0;
|
||||
decode_subblock(pOut, r1, g1, b1, tableA, low, false, flipped);
|
||||
decode_subblock(pOut, r2, g2, b2, tableB, low, true, flipped);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
etc1_uint32 high;
|
||||
etc1_uint32 low;
|
||||
etc1_uint32 score; // Lower is more accurate
|
||||
} etc_compressed;
|
||||
|
||||
static
|
||||
inline void take_best(etc_compressed* a, const etc_compressed* b) {
|
||||
if (a->score > b->score) {
|
||||
*a = *b;
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void etc_average_colors_subblock(const etc1_byte* pIn, etc1_uint32 inMask,
|
||||
etc1_byte* pColors, bool flipped, bool second) {
|
||||
int r = 0;
|
||||
int g = 0;
|
||||
int b = 0;
|
||||
|
||||
if (flipped) {
|
||||
int by = 0;
|
||||
if (second) {
|
||||
by = 2;
|
||||
}
|
||||
for (int y = 0; y < 2; y++) {
|
||||
int yy = by + y;
|
||||
for (int x = 0; x < 4; x++) {
|
||||
int i = x + 4 * yy;
|
||||
if (inMask & (1 << i)) {
|
||||
const etc1_byte* p = pIn + i * 3;
|
||||
r += *(p++);
|
||||
g += *(p++);
|
||||
b += *(p++);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int bx = 0;
|
||||
if (second) {
|
||||
bx = 2;
|
||||
}
|
||||
for (int y = 0; y < 4; y++) {
|
||||
for (int x = 0; x < 2; x++) {
|
||||
int xx = bx + x;
|
||||
int i = xx + 4 * y;
|
||||
if (inMask & (1 << i)) {
|
||||
const etc1_byte* p = pIn + i * 3;
|
||||
r += *(p++);
|
||||
g += *(p++);
|
||||
b += *(p++);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pColors[0] = (etc1_byte)((r + 4) >> 3);
|
||||
pColors[1] = (etc1_byte)((g + 4) >> 3);
|
||||
pColors[2] = (etc1_byte)((b + 4) >> 3);
|
||||
}
|
||||
|
||||
static
|
||||
inline int square(int x) {
|
||||
return x * x;
|
||||
}
|
||||
|
||||
static etc1_uint32 chooseModifier(const etc1_byte* pBaseColors,
|
||||
const etc1_byte* pIn, etc1_uint32 *pLow, int bitIndex,
|
||||
const int* pModifierTable) {
|
||||
etc1_uint32 bestScore = ~0;
|
||||
int bestIndex = 0;
|
||||
int pixelR = pIn[0];
|
||||
int pixelG = pIn[1];
|
||||
int pixelB = pIn[2];
|
||||
int r = pBaseColors[0];
|
||||
int g = pBaseColors[1];
|
||||
int b = pBaseColors[2];
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int modifier = pModifierTable[i];
|
||||
int decodedG = clamp(g + modifier);
|
||||
etc1_uint32 score = (etc1_uint32) (6 * square(decodedG - pixelG));
|
||||
if (score >= bestScore) {
|
||||
continue;
|
||||
}
|
||||
int decodedR = clamp(r + modifier);
|
||||
score += (etc1_uint32) (3 * square(decodedR - pixelR));
|
||||
if (score >= bestScore) {
|
||||
continue;
|
||||
}
|
||||
int decodedB = clamp(b + modifier);
|
||||
score += (etc1_uint32) square(decodedB - pixelB);
|
||||
if (score < bestScore) {
|
||||
bestScore = score;
|
||||
bestIndex = i;
|
||||
}
|
||||
}
|
||||
etc1_uint32 lowMask = (((bestIndex >> 1) << 16) | (bestIndex & 1))
|
||||
<< bitIndex;
|
||||
*pLow |= lowMask;
|
||||
return bestScore;
|
||||
}
|
||||
|
||||
static
|
||||
void etc_encode_subblock_helper(const etc1_byte* pIn, etc1_uint32 inMask,
|
||||
etc_compressed* pCompressed, bool flipped, bool second,
|
||||
const etc1_byte* pBaseColors, const int* pModifierTable) {
|
||||
int score = pCompressed->score;
|
||||
if (flipped) {
|
||||
int by = 0;
|
||||
if (second) {
|
||||
by = 2;
|
||||
}
|
||||
for (int y = 0; y < 2; y++) {
|
||||
int yy = by + y;
|
||||
for (int x = 0; x < 4; x++) {
|
||||
int i = x + 4 * yy;
|
||||
if (inMask & (1 << i)) {
|
||||
score += chooseModifier(pBaseColors, pIn + i * 3,
|
||||
&pCompressed->low, yy + x * 4, pModifierTable);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int bx = 0;
|
||||
if (second) {
|
||||
bx = 2;
|
||||
}
|
||||
for (int y = 0; y < 4; y++) {
|
||||
for (int x = 0; x < 2; x++) {
|
||||
int xx = bx + x;
|
||||
int i = xx + 4 * y;
|
||||
if (inMask & (1 << i)) {
|
||||
score += chooseModifier(pBaseColors, pIn + i * 3,
|
||||
&pCompressed->low, y + xx * 4, pModifierTable);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pCompressed->score = score;
|
||||
}
|
||||
|
||||
static bool inRange4bitSigned(int color) {
|
||||
return color >= -4 && color <= 3;
|
||||
}
|
||||
|
||||
static void etc_encodeBaseColors(etc1_byte* pBaseColors,
|
||||
const etc1_byte* pColors, etc_compressed* pCompressed) {
|
||||
int r1, g1, b1, r2, g2, b2; // 8 bit base colors for sub-blocks
|
||||
bool differential;
|
||||
{
|
||||
int r51 = convert8To5(pColors[0]);
|
||||
int g51 = convert8To5(pColors[1]);
|
||||
int b51 = convert8To5(pColors[2]);
|
||||
int r52 = convert8To5(pColors[3]);
|
||||
int g52 = convert8To5(pColors[4]);
|
||||
int b52 = convert8To5(pColors[5]);
|
||||
|
||||
r1 = convert5To8(r51);
|
||||
g1 = convert5To8(g51);
|
||||
b1 = convert5To8(b51);
|
||||
|
||||
int dr = r52 - r51;
|
||||
int dg = g52 - g51;
|
||||
int db = b52 - b51;
|
||||
|
||||
differential = inRange4bitSigned(dr) && inRange4bitSigned(dg)
|
||||
&& inRange4bitSigned(db);
|
||||
if (differential) {
|
||||
r2 = convert5To8(r51 + dr);
|
||||
g2 = convert5To8(g51 + dg);
|
||||
b2 = convert5To8(b51 + db);
|
||||
pCompressed->high |= (r51 << 27) | ((7 & dr) << 24) | (g51 << 19)
|
||||
| ((7 & dg) << 16) | (b51 << 11) | ((7 & db) << 8) | 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (!differential) {
|
||||
int r41 = convert8To4(pColors[0]);
|
||||
int g41 = convert8To4(pColors[1]);
|
||||
int b41 = convert8To4(pColors[2]);
|
||||
int r42 = convert8To4(pColors[3]);
|
||||
int g42 = convert8To4(pColors[4]);
|
||||
int b42 = convert8To4(pColors[5]);
|
||||
r1 = convert4To8(r41);
|
||||
g1 = convert4To8(g41);
|
||||
b1 = convert4To8(b41);
|
||||
r2 = convert4To8(r42);
|
||||
g2 = convert4To8(g42);
|
||||
b2 = convert4To8(b42);
|
||||
pCompressed->high |= (r41 << 28) | (r42 << 24) | (g41 << 20) | (g42
|
||||
<< 16) | (b41 << 12) | (b42 << 8);
|
||||
}
|
||||
pBaseColors[0] = r1;
|
||||
pBaseColors[1] = g1;
|
||||
pBaseColors[2] = b1;
|
||||
pBaseColors[3] = r2;
|
||||
pBaseColors[4] = g2;
|
||||
pBaseColors[5] = b2;
|
||||
}
|
||||
|
||||
static
|
||||
void etc_encode_block_helper(const etc1_byte* pIn, etc1_uint32 inMask,
|
||||
const etc1_byte* pColors, etc_compressed* pCompressed, bool flipped) {
|
||||
pCompressed->score = ~0;
|
||||
pCompressed->high = (flipped ? 1 : 0);
|
||||
pCompressed->low = 0;
|
||||
|
||||
etc1_byte pBaseColors[6];
|
||||
|
||||
etc_encodeBaseColors(pBaseColors, pColors, pCompressed);
|
||||
|
||||
int originalHigh = pCompressed->high;
|
||||
|
||||
const int* pModifierTable = kModifierTable;
|
||||
for (int i = 0; i < 8; i++, pModifierTable += 4) {
|
||||
etc_compressed temp;
|
||||
temp.score = 0;
|
||||
temp.high = originalHigh | (i << 5);
|
||||
temp.low = 0;
|
||||
etc_encode_subblock_helper(pIn, inMask, &temp, flipped, false,
|
||||
pBaseColors, pModifierTable);
|
||||
take_best(pCompressed, &temp);
|
||||
}
|
||||
pModifierTable = kModifierTable;
|
||||
etc_compressed firstHalf = *pCompressed;
|
||||
for (int i = 0; i < 8; i++, pModifierTable += 4) {
|
||||
etc_compressed temp;
|
||||
temp.score = firstHalf.score;
|
||||
temp.high = firstHalf.high | (i << 2);
|
||||
temp.low = firstHalf.low;
|
||||
etc_encode_subblock_helper(pIn, inMask, &temp, flipped, true,
|
||||
pBaseColors + 3, pModifierTable);
|
||||
if (i == 0) {
|
||||
*pCompressed = temp;
|
||||
} else {
|
||||
take_best(pCompressed, &temp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void writeBigEndian(etc1_byte* pOut, etc1_uint32 d) {
|
||||
pOut[0] = (etc1_byte)(d >> 24);
|
||||
pOut[1] = (etc1_byte)(d >> 16);
|
||||
pOut[2] = (etc1_byte)(d >> 8);
|
||||
pOut[3] = (etc1_byte) d;
|
||||
}
|
||||
|
||||
// Input is a 4 x 4 square of 3-byte pixels in form R, G, B
|
||||
// inmask is a 16-bit mask where bit (1 << (x + y * 4)) tells whether the corresponding (x,y)
|
||||
// pixel is valid or not. Invalid pixel color values are ignored when compressing.
|
||||
// Output is an ETC1 compressed version of the data.
|
||||
|
||||
void etc1_encode_block(const etc1_byte* pIn, etc1_uint32 inMask,
|
||||
etc1_byte* pOut) {
|
||||
etc1_byte colors[6];
|
||||
etc1_byte flippedColors[6];
|
||||
etc_average_colors_subblock(pIn, inMask, colors, false, false);
|
||||
etc_average_colors_subblock(pIn, inMask, colors + 3, false, true);
|
||||
etc_average_colors_subblock(pIn, inMask, flippedColors, true, false);
|
||||
etc_average_colors_subblock(pIn, inMask, flippedColors + 3, true, true);
|
||||
|
||||
etc_compressed a, b;
|
||||
etc_encode_block_helper(pIn, inMask, colors, &a, false);
|
||||
etc_encode_block_helper(pIn, inMask, flippedColors, &b, true);
|
||||
take_best(&a, &b);
|
||||
writeBigEndian(pOut, a.high);
|
||||
writeBigEndian(pOut + 4, a.low);
|
||||
}
|
||||
|
||||
// Return the size of the encoded image data (does not include size of PKM header).
|
||||
|
||||
etc1_uint32 etc1_get_encoded_data_size(etc1_uint32 width, etc1_uint32 height) {
|
||||
return (((width + 3) & ~3) * ((height + 3) & ~3)) >> 1;
|
||||
}
|
||||
|
||||
// Encode an entire image.
|
||||
// pIn - pointer to the image data. Formatted such that the Red component of
|
||||
// pixel (x,y) is at pIn + pixelSize * x + stride * y + redOffset;
|
||||
// pOut - pointer to encoded data. Must be large enough to store entire encoded image.
|
||||
|
||||
int etc1_encode_image(const etc1_byte* pIn, etc1_uint32 width, etc1_uint32 height,
|
||||
etc1_uint32 pixelSize, etc1_uint32 stride, etc1_byte* pOut) {
|
||||
if (pixelSize < 2 || pixelSize > 3) {
|
||||
return -1;
|
||||
}
|
||||
static const unsigned short kYMask[] = { 0x0, 0xf, 0xff, 0xfff, 0xffff };
|
||||
static const unsigned short kXMask[] = { 0x0, 0x1111, 0x3333, 0x7777,
|
||||
0xffff };
|
||||
etc1_byte block[ETC1_DECODED_BLOCK_SIZE];
|
||||
etc1_byte encoded[ETC1_ENCODED_BLOCK_SIZE];
|
||||
|
||||
etc1_uint32 encodedWidth = (width + 3) & ~3;
|
||||
etc1_uint32 encodedHeight = (height + 3) & ~3;
|
||||
|
||||
for (etc1_uint32 y = 0; y < encodedHeight; y += 4) {
|
||||
etc1_uint32 yEnd = height - y;
|
||||
if (yEnd > 4) {
|
||||
yEnd = 4;
|
||||
}
|
||||
int ymask = kYMask[yEnd];
|
||||
for (etc1_uint32 x = 0; x < encodedWidth; x += 4) {
|
||||
etc1_uint32 xEnd = width - x;
|
||||
if (xEnd > 4) {
|
||||
xEnd = 4;
|
||||
}
|
||||
int mask = ymask & kXMask[xEnd];
|
||||
for (etc1_uint32 cy = 0; cy < yEnd; cy++) {
|
||||
etc1_byte* q = block + (cy * 4) * 3;
|
||||
const etc1_byte* p = pIn + pixelSize * x + stride * (y + cy);
|
||||
if (pixelSize == 3) {
|
||||
memcpy(q, p, xEnd * 3);
|
||||
} else {
|
||||
for (etc1_uint32 cx = 0; cx < xEnd; cx++) {
|
||||
int pixel = (p[1] << 8) | p[0];
|
||||
*q++ = convert5To8(pixel >> 11);
|
||||
*q++ = convert6To8(pixel >> 5);
|
||||
*q++ = convert5To8(pixel);
|
||||
p += pixelSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
etc1_encode_block(block, mask, encoded);
|
||||
memcpy(pOut, encoded, sizeof(encoded));
|
||||
pOut += sizeof(encoded);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Decode an entire image.
|
||||
// pIn - pointer to encoded data.
|
||||
// pOut - pointer to the image data. Will be written such that the Red component of
|
||||
// pixel (x,y) is at pIn + pixelSize * x + stride * y + redOffset. Must be
|
||||
// large enough to store entire image.
|
||||
|
||||
|
||||
int etc1_decode_image(const etc1_byte* pIn, etc1_byte* pOut,
|
||||
etc1_uint32 width, etc1_uint32 height,
|
||||
etc1_uint32 pixelSize, etc1_uint32 stride) {
|
||||
if (pixelSize < 2 || pixelSize > 3) {
|
||||
return -1;
|
||||
}
|
||||
etc1_byte block[ETC1_DECODED_BLOCK_SIZE];
|
||||
|
||||
etc1_uint32 encodedWidth = (width + 3) & ~3;
|
||||
etc1_uint32 encodedHeight = (height + 3) & ~3;
|
||||
|
||||
for (etc1_uint32 y = 0; y < encodedHeight; y += 4) {
|
||||
etc1_uint32 yEnd = height - y;
|
||||
if (yEnd > 4) {
|
||||
yEnd = 4;
|
||||
}
|
||||
for (etc1_uint32 x = 0; x < encodedWidth; x += 4) {
|
||||
etc1_uint32 xEnd = width - x;
|
||||
if (xEnd > 4) {
|
||||
xEnd = 4;
|
||||
}
|
||||
etc1_decode_block(pIn, block);
|
||||
pIn += ETC1_ENCODED_BLOCK_SIZE;
|
||||
for (etc1_uint32 cy = 0; cy < yEnd; cy++) {
|
||||
const etc1_byte* q = block + (cy * 4) * 3;
|
||||
etc1_byte* p = pOut + pixelSize * x + stride * (y + cy);
|
||||
if (pixelSize == 3) {
|
||||
memcpy(p, q, xEnd * 3);
|
||||
} else {
|
||||
for (etc1_uint32 cx = 0; cx < xEnd; cx++) {
|
||||
etc1_byte r = *q++;
|
||||
etc1_byte g = *q++;
|
||||
etc1_byte b = *q++;
|
||||
etc1_uint32 pixel = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
|
||||
*p++ = (etc1_byte) pixel;
|
||||
*p++ = (etc1_byte) (pixel >> 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char kMagic[] = { 'P', 'K', 'M', ' ', '1', '0' };
|
||||
|
||||
static const etc1_uint32 ETC1_PKM_FORMAT_OFFSET = 6;
|
||||
static const etc1_uint32 ETC1_PKM_ENCODED_WIDTH_OFFSET = 8;
|
||||
static const etc1_uint32 ETC1_PKM_ENCODED_HEIGHT_OFFSET = 10;
|
||||
static const etc1_uint32 ETC1_PKM_WIDTH_OFFSET = 12;
|
||||
static const etc1_uint32 ETC1_PKM_HEIGHT_OFFSET = 14;
|
||||
|
||||
static const etc1_uint32 ETC1_RGB_NO_MIPMAPS = 0;
|
||||
|
||||
static void writeBEUint16(etc1_byte* pOut, etc1_uint32 data) {
|
||||
pOut[0] = (etc1_byte) (data >> 8);
|
||||
pOut[1] = (etc1_byte) data;
|
||||
}
|
||||
|
||||
static etc1_uint32 readBEUint16(const etc1_byte* pIn) {
|
||||
return (pIn[0] << 8) | pIn[1];
|
||||
}
|
||||
|
||||
// Format a PKM header
|
||||
|
||||
void etc1_pkm_format_header(etc1_byte* pHeader, etc1_uint32 width, etc1_uint32 height) {
|
||||
memcpy(pHeader, kMagic, sizeof(kMagic));
|
||||
etc1_uint32 encodedWidth = (width + 3) & ~3;
|
||||
etc1_uint32 encodedHeight = (height + 3) & ~3;
|
||||
writeBEUint16(pHeader + ETC1_PKM_FORMAT_OFFSET, ETC1_RGB_NO_MIPMAPS);
|
||||
writeBEUint16(pHeader + ETC1_PKM_ENCODED_WIDTH_OFFSET, encodedWidth);
|
||||
writeBEUint16(pHeader + ETC1_PKM_ENCODED_HEIGHT_OFFSET, encodedHeight);
|
||||
writeBEUint16(pHeader + ETC1_PKM_WIDTH_OFFSET, width);
|
||||
writeBEUint16(pHeader + ETC1_PKM_HEIGHT_OFFSET, height);
|
||||
}
|
||||
|
||||
// Check if a PKM header is correctly formatted.
|
||||
|
||||
etc1_bool etc1_pkm_is_valid(const etc1_byte* pHeader) {
|
||||
if (memcmp(pHeader, kMagic, sizeof(kMagic))) {
|
||||
return false;
|
||||
}
|
||||
etc1_uint32 format = readBEUint16(pHeader + ETC1_PKM_FORMAT_OFFSET);
|
||||
etc1_uint32 encodedWidth = readBEUint16(pHeader + ETC1_PKM_ENCODED_WIDTH_OFFSET);
|
||||
etc1_uint32 encodedHeight = readBEUint16(pHeader + ETC1_PKM_ENCODED_HEIGHT_OFFSET);
|
||||
etc1_uint32 width = readBEUint16(pHeader + ETC1_PKM_WIDTH_OFFSET);
|
||||
etc1_uint32 height = readBEUint16(pHeader + ETC1_PKM_HEIGHT_OFFSET);
|
||||
return format == ETC1_RGB_NO_MIPMAPS &&
|
||||
encodedWidth >= width && encodedWidth - width < 4 &&
|
||||
encodedHeight >= height && encodedHeight - height < 4;
|
||||
}
|
||||
|
||||
// Read the image width from a PKM header
|
||||
|
||||
etc1_uint32 etc1_pkm_get_width(const etc1_byte* pHeader) {
|
||||
return readBEUint16(pHeader + ETC1_PKM_WIDTH_OFFSET);
|
||||
}
|
||||
|
||||
// Read the image height from a PKM header
|
||||
|
||||
etc1_uint32 etc1_pkm_get_height(const etc1_byte* pHeader){
|
||||
return readBEUint16(pHeader + ETC1_PKM_HEIGHT_OFFSET);
|
||||
}
|
||||
|
||||
109
cocos2d-x/cocos/base/etc1.h
Normal file
109
cocos2d-x/cocos/base/etc1.h
Normal file
@@ -0,0 +1,109 @@
|
||||
// Copyright 2009 Google Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef __etc1_h__
|
||||
#define __etc1_h__
|
||||
/// @cond DO_NOT_SHOW
|
||||
|
||||
#define ETC1_ENCODED_BLOCK_SIZE 8
|
||||
#define ETC1_DECODED_BLOCK_SIZE 48
|
||||
|
||||
#ifndef ETC1_RGB8_OES
|
||||
#define ETC1_RGB8_OES 0x8D64
|
||||
#endif
|
||||
|
||||
typedef unsigned char etc1_byte;
|
||||
typedef int etc1_bool;
|
||||
typedef unsigned int etc1_uint32;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Encode a block of pixels.
|
||||
//
|
||||
// pIn is a pointer to a ETC_DECODED_BLOCK_SIZE array of bytes that represent a
|
||||
// 4 x 4 square of 3-byte pixels in form R, G, B. Byte (3 * (x + 4 * y) is the R
|
||||
// value of pixel (x, y).
|
||||
//
|
||||
// validPixelMask is a 16-bit mask where bit (1 << (x + y * 4)) indicates whether
|
||||
// the corresponding (x,y) pixel is valid. Invalid pixel color values are ignored when compressing.
|
||||
//
|
||||
// pOut is an ETC1 compressed version of the data.
|
||||
|
||||
void etc1_encode_block(const etc1_byte* pIn, etc1_uint32 validPixelMask, etc1_byte* pOut);
|
||||
|
||||
// Decode a block of pixels.
|
||||
//
|
||||
// pIn is an ETC1 compressed version of the data.
|
||||
//
|
||||
// pOut is a pointer to a ETC_DECODED_BLOCK_SIZE array of bytes that represent a
|
||||
// 4 x 4 square of 3-byte pixels in form R, G, B. Byte (3 * (x + 4 * y) is the R
|
||||
// value of pixel (x, y).
|
||||
|
||||
void etc1_decode_block(const etc1_byte* pIn, etc1_byte* pOut);
|
||||
|
||||
// Return the size of the encoded image data (does not include size of PKM header).
|
||||
|
||||
etc1_uint32 etc1_get_encoded_data_size(etc1_uint32 width, etc1_uint32 height);
|
||||
|
||||
// Encode an entire image.
|
||||
// pIn - pointer to the image data. Formatted such that
|
||||
// pixel (x,y) is at pIn + pixelSize * x + stride * y;
|
||||
// pOut - pointer to encoded data. Must be large enough to store entire encoded image.
|
||||
// pixelSize can be 2 or 3. 2 is an GL_UNSIGNED_SHORT_5_6_5 image, 3 is a GL_BYTE RGB image.
|
||||
// returns non-zero if there is an error.
|
||||
|
||||
int etc1_encode_image(const etc1_byte* pIn, etc1_uint32 width, etc1_uint32 height,
|
||||
etc1_uint32 pixelSize, etc1_uint32 stride, etc1_byte* pOut);
|
||||
|
||||
// Decode an entire image.
|
||||
// pIn - pointer to encoded data.
|
||||
// pOut - pointer to the image data. Will be written such that
|
||||
// pixel (x,y) is at pIn + pixelSize * x + stride * y. Must be
|
||||
// large enough to store entire image.
|
||||
// pixelSize can be 2 or 3. 2 is an GL_UNSIGNED_SHORT_5_6_5 image, 3 is a GL_BYTE RGB image.
|
||||
// returns non-zero if there is an error.
|
||||
|
||||
int etc1_decode_image(const etc1_byte* pIn, etc1_byte* pOut,
|
||||
etc1_uint32 width, etc1_uint32 height,
|
||||
etc1_uint32 pixelSize, etc1_uint32 stride);
|
||||
|
||||
// Size of a PKM header, in bytes.
|
||||
|
||||
#define ETC_PKM_HEADER_SIZE 16
|
||||
|
||||
// Format a PKM header
|
||||
|
||||
void etc1_pkm_format_header(etc1_byte* pHeader, etc1_uint32 width, etc1_uint32 height);
|
||||
|
||||
// Check if a PKM header is correctly formatted.
|
||||
|
||||
etc1_bool etc1_pkm_is_valid(const etc1_byte* pHeader);
|
||||
|
||||
// Read the image width from a PKM header
|
||||
|
||||
etc1_uint32 etc1_pkm_get_width(const etc1_byte* pHeader);
|
||||
|
||||
// Read the image height from a PKM header
|
||||
|
||||
etc1_uint32 etc1_pkm_get_height(const etc1_byte* pHeader);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/// @endcond
|
||||
#endif
|
||||
|
||||
79
cocos2d-x/cocos/base/etc2.cpp
Normal file
79
cocos2d-x/cocos/base/etc2.cpp
Normal file
@@ -0,0 +1,79 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2010-2012 cocos2d-x.org
|
||||
Copyright (c) 2013-2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
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 "base/etc2.h"
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
static const char kMagic[] = { 'P', 'K', 'M', ' ', '2', '0' };
|
||||
|
||||
static const etc2_uint32 ETC2_PKM_FORMAT_OFFSET = 6;
|
||||
static const etc2_uint32 ETC2_PKM_ENCODED_WIDTH_OFFSET = 8;
|
||||
static const etc2_uint32 ETC2_PKM_ENCODED_HEIGHT_OFFSET = 10;
|
||||
static const etc2_uint32 ETC2_PKM_WIDTH_OFFSET = 12;
|
||||
static const etc2_uint32 ETC2_PKM_HEIGHT_OFFSET = 14;
|
||||
|
||||
static void writeBEUint16(etc2_byte* pOut, etc2_uint32 data) {
|
||||
pOut[0] = (etc2_byte) (data >> 8);
|
||||
pOut[1] = (etc2_byte) data;
|
||||
}
|
||||
|
||||
static etc2_uint32 readBEUint16(const etc2_byte* pIn) {
|
||||
return (pIn[0] << 8) | pIn[1];
|
||||
}
|
||||
|
||||
// Check if a PKM header is correctly formatted.
|
||||
|
||||
etc2_bool etc2_pkm_is_valid(const etc2_byte* pHeader) {
|
||||
if (memcmp(pHeader, kMagic, sizeof(kMagic))) {
|
||||
return false;
|
||||
}
|
||||
etc2_uint32 format = readBEUint16(pHeader + ETC2_PKM_FORMAT_OFFSET);
|
||||
etc2_uint32 encodedWidth = readBEUint16(pHeader + ETC2_PKM_ENCODED_WIDTH_OFFSET);
|
||||
etc2_uint32 encodedHeight = readBEUint16(pHeader + ETC2_PKM_ENCODED_HEIGHT_OFFSET);
|
||||
etc2_uint32 width = readBEUint16(pHeader + ETC2_PKM_WIDTH_OFFSET);
|
||||
etc2_uint32 height = readBEUint16(pHeader + ETC2_PKM_HEIGHT_OFFSET);
|
||||
return (format == ETC2_RGB_NO_MIPMAPS || format == ETC2_RGBA_NO_MIPMAPS) &&
|
||||
encodedWidth >= width && encodedWidth - width < 4 &&
|
||||
encodedHeight >= height && encodedHeight - height < 4;
|
||||
}
|
||||
|
||||
// Read the image width from a PKM header
|
||||
|
||||
etc2_uint32 etc2_pkm_get_width(const etc2_byte* pHeader) {
|
||||
return readBEUint16(pHeader + ETC2_PKM_WIDTH_OFFSET);
|
||||
}
|
||||
|
||||
// Read the image height from a PKM header
|
||||
|
||||
etc2_uint32 etc2_pkm_get_height(const etc2_byte* pHeader){
|
||||
return readBEUint16(pHeader + ETC2_PKM_HEIGHT_OFFSET);
|
||||
}
|
||||
|
||||
etc2_uint32 etc2_pkm_get_format(const uint8_t* pHeader) {
|
||||
return readBEUint16(pHeader + ETC2_PKM_FORMAT_OFFSET);
|
||||
}
|
||||
|
||||
76
cocos2d-x/cocos/base/etc2.h
Normal file
76
cocos2d-x/cocos/base/etc2.h
Normal file
@@ -0,0 +1,76 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2010-2012 cocos2d-x.org
|
||||
Copyright (c) 2013-2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
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 __etc2_h__
|
||||
#define __etc2_h__
|
||||
/// @cond DO_NOT_SHOW
|
||||
|
||||
typedef unsigned char etc2_byte;
|
||||
typedef int etc2_bool;
|
||||
typedef unsigned int etc2_uint32;
|
||||
|
||||
#ifndef GL_COMPRESSED_RGB8_ETC2
|
||||
#define GL_COMPRESSED_RGB8_ETC2 0x9274
|
||||
#endif
|
||||
|
||||
#ifndef GL_COMPRESSED_RGBA8_ETC2_EAC
|
||||
#define GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Size of a PKM header, in bytes.
|
||||
|
||||
#define ETC2_PKM_HEADER_SIZE 16
|
||||
|
||||
#define ETC2_RGB_NO_MIPMAPS 1
|
||||
#define ETC2_RGBA_NO_MIPMAPS 3
|
||||
|
||||
// Check if a PKM header is correctly formatted.
|
||||
|
||||
etc2_bool etc2_pkm_is_valid(const etc2_byte* pHeader);
|
||||
|
||||
// Read the image width from a PKM header
|
||||
|
||||
etc2_uint32 etc2_pkm_get_width(const etc2_byte* pHeader);
|
||||
|
||||
// Read the image height from a PKM header
|
||||
|
||||
etc2_uint32 etc2_pkm_get_height(const etc2_byte* pHeader);
|
||||
|
||||
// Read the image format from a PKM header
|
||||
|
||||
etc2_uint32 etc2_pkm_get_format(const etc2_byte* pHeader);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/// @endcond
|
||||
#endif
|
||||
|
||||
714
cocos2d-x/cocos/base/pvr.cpp
Normal file
714
cocos2d-x/cocos/base/pvr.cpp
Normal file
@@ -0,0 +1,714 @@
|
||||
/******************************************************************************
|
||||
|
||||
@File PVRTDecompress.cpp
|
||||
|
||||
@Title
|
||||
|
||||
@Copyright Copyright (C) 2000 - 2008 by Imagination Technologies Limited.
|
||||
|
||||
@Platform ANSI compatible
|
||||
|
||||
@Description PVRTC Texture Decompression.
|
||||
|
||||
******************************************************************************/
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <cstdint>
|
||||
#include "base/pvr.h"
|
||||
|
||||
#define PVRT_MIN(a,b) (((a) < (b)) ? (a) : (b))
|
||||
#define PVRT_MAX(a,b) (((a) > (b)) ? (a) : (b))
|
||||
#define PVRT_CLAMP(x, l, h) (PVRT_MIN((h), PVRT_MAX((x), (l))))
|
||||
|
||||
/*****************************************************************************
|
||||
* defines and consts
|
||||
*****************************************************************************/
|
||||
#define PT_INDEX (2) // The Punch-through index
|
||||
|
||||
#define BLK_Y_SIZE (4) // always 4 for all 2D block types
|
||||
|
||||
#define BLK_X_MAX (8) // Max X dimension for blocks
|
||||
|
||||
#define BLK_X_2BPP (8) // dimensions for the two formats
|
||||
#define BLK_X_4BPP (4)
|
||||
|
||||
#define WRAP_COORD(Val, Size) ((Val) & ((Size)-1))
|
||||
|
||||
#define POWER_OF_2(X) util_number_is_power_2(X)
|
||||
|
||||
/*
|
||||
Define an expression to either wrap or clamp large or small vals to the
|
||||
legal coordinate range
|
||||
*/
|
||||
#define LIMIT_COORD(Val, Size, AssumeImageTiles) \
|
||||
((AssumeImageTiles)? WRAP_COORD((Val), (Size)): PVRT_CLAMP((Val), 0, (Size)-1))
|
||||
|
||||
/*****************************************************************************
|
||||
* Useful typedefs
|
||||
*****************************************************************************/
|
||||
|
||||
typedef uint32_t U32;
|
||||
typedef uint8_t U8;
|
||||
|
||||
/***********************************************************
|
||||
DECOMPRESSION ROUTINES
|
||||
************************************************************/
|
||||
|
||||
/*!***********************************************************************
|
||||
@Struct AMTC_BLOCK_STRUCT
|
||||
@Brief
|
||||
*************************************************************************/
|
||||
typedef struct
|
||||
{
|
||||
// Uses 64 bits pre block
|
||||
U32 PackedData[2];
|
||||
}AMTC_BLOCK_STRUCT;
|
||||
|
||||
|
||||
static void PVRDecompress(AMTC_BLOCK_STRUCT *pCompressedData,
|
||||
const bool Do2bitMode,
|
||||
const int XDim,
|
||||
const int YDim,
|
||||
const int AssumeImageTiles,
|
||||
unsigned char* pResultImage);
|
||||
|
||||
/*!***********************************************************************
|
||||
@Function PVRTDecompressPVRTC
|
||||
@Input pCompressedData The PVRTC texture data to decompress
|
||||
@Input Do2bitMode Signifies whether the data is PVRTC2 or PVRTC4
|
||||
@Input XDim X dimension of the texture
|
||||
@Input YDim Y dimension of the texture
|
||||
@Modified pResultImage The decompressed texture data
|
||||
@Description Decompresses PVRTC to RGBA 8888
|
||||
*************************************************************************/
|
||||
int PVRTDecompressPVRTC(const void * const pCompressedData,const int XDim,const int YDim, void *pDestData,const bool Do2bitMode)
|
||||
{
|
||||
PVRDecompress((AMTC_BLOCK_STRUCT*)pCompressedData,Do2bitMode,XDim,YDim,1,(unsigned char*)pDestData);
|
||||
|
||||
return XDim*YDim/2;
|
||||
}
|
||||
|
||||
/*!***********************************************************************
|
||||
@Function util_number_is_power_2
|
||||
@Input input A number
|
||||
@Returns TRUE if the number is an integer power of two, else FALSE.
|
||||
@Description Check that a number is an integer power of two, i.e.
|
||||
1, 2, 4, 8, ... etc.
|
||||
Returns FALSE for zero.
|
||||
*************************************************************************/
|
||||
int util_number_is_power_2( unsigned input )
|
||||
{
|
||||
unsigned minus1;
|
||||
|
||||
if( !input ) return 0;
|
||||
|
||||
minus1 = input - 1;
|
||||
return ( (input | minus1) == (input ^ minus1) ) ? 1 : 0;
|
||||
}
|
||||
|
||||
|
||||
/*!***********************************************************************
|
||||
@Function Unpack5554Colour
|
||||
@Input pBlock
|
||||
@Input ABColours
|
||||
@Description Given a block, extract the colour information and convert
|
||||
to 5554 formats
|
||||
*************************************************************************/
|
||||
static void Unpack5554Colour(const AMTC_BLOCK_STRUCT *pBlock,
|
||||
int ABColours[2][4])
|
||||
{
|
||||
U32 RawBits[2];
|
||||
|
||||
int i;
|
||||
|
||||
// Extract A and B
|
||||
RawBits[0] = pBlock->PackedData[1] & (0xFFFE); // 15 bits (shifted up by one)
|
||||
RawBits[1] = pBlock->PackedData[1] >> 16; // 16 bits
|
||||
|
||||
// step through both colours
|
||||
for(i = 0; i < 2; i++)
|
||||
{
|
||||
// If completely opaque
|
||||
if(RawBits[i] & (1<<15))
|
||||
{
|
||||
// Extract R and G (both 5 bit)
|
||||
ABColours[i][0] = (RawBits[i] >> 10) & 0x1F;
|
||||
ABColours[i][1] = (RawBits[i] >> 5) & 0x1F;
|
||||
|
||||
/*
|
||||
The precision of Blue depends on A or B. If A then we need to
|
||||
replicate the top bit to get 5 bits in total
|
||||
*/
|
||||
ABColours[i][2] = RawBits[i] & 0x1F;
|
||||
if(i==0)
|
||||
{
|
||||
ABColours[0][2] |= ABColours[0][2] >> 4;
|
||||
}
|
||||
|
||||
// set 4bit alpha fully on...
|
||||
ABColours[i][3] = 0xF;
|
||||
}
|
||||
else // Else if colour has variable translucency
|
||||
{
|
||||
/*
|
||||
Extract R and G (both 4 bit).
|
||||
(Leave a space on the end for the replication of bits
|
||||
*/
|
||||
ABColours[i][0] = (RawBits[i] >> (8-1)) & 0x1E;
|
||||
ABColours[i][1] = (RawBits[i] >> (4-1)) & 0x1E;
|
||||
|
||||
// replicate bits to truly expand to 5 bits
|
||||
ABColours[i][0] |= ABColours[i][0] >> 4;
|
||||
ABColours[i][1] |= ABColours[i][1] >> 4;
|
||||
|
||||
// grab the 3(+padding) or 4 bits of blue and add an extra padding bit
|
||||
ABColours[i][2] = (RawBits[i] & 0xF) << 1;
|
||||
|
||||
/*
|
||||
expand from 3 to 5 bits if this is from colour A, or 4 to 5 bits if from
|
||||
colour B
|
||||
*/
|
||||
if(i==0)
|
||||
{
|
||||
ABColours[0][2] |= ABColours[0][2] >> 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
ABColours[0][2] |= ABColours[0][2] >> 4;
|
||||
}
|
||||
|
||||
// Set the alpha bits to be 3 + a zero on the end
|
||||
ABColours[i][3] = (RawBits[i] >> 11) & 0xE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!***********************************************************************
|
||||
@Function UnpackModulations
|
||||
@Input pBlock
|
||||
@Input Do2bitMode
|
||||
@Input ModulationVals
|
||||
@Input ModulationModes
|
||||
@Input StartX
|
||||
@Input StartY
|
||||
@Description Given the block and the texture type and it's relative
|
||||
position in the 2x2 group of blocks, extract the bit
|
||||
patterns for the fully defined pixels.
|
||||
*************************************************************************/
|
||||
static void UnpackModulations(const AMTC_BLOCK_STRUCT *pBlock,
|
||||
const int Do2bitMode,
|
||||
int ModulationVals[8][16],
|
||||
int ModulationModes[8][16],
|
||||
int StartX,
|
||||
int StartY)
|
||||
{
|
||||
int BlockModMode;
|
||||
U32 ModulationBits;
|
||||
|
||||
int x, y;
|
||||
|
||||
BlockModMode= pBlock->PackedData[1] & 1;
|
||||
ModulationBits = pBlock->PackedData[0];
|
||||
|
||||
// if it's in an interpolated mode
|
||||
if(Do2bitMode && BlockModMode)
|
||||
{
|
||||
/*
|
||||
run through all the pixels in the block. Note we can now treat all the
|
||||
"stored" values as if they have 2bits (even when they didn't!)
|
||||
*/
|
||||
for(y = 0; y < BLK_Y_SIZE; y++)
|
||||
{
|
||||
for(x = 0; x < BLK_X_2BPP; x++)
|
||||
{
|
||||
ModulationModes[y+StartY][x+StartX] = BlockModMode;
|
||||
|
||||
// if this is a stored value...
|
||||
if(((x^y)&1) == 0)
|
||||
{
|
||||
ModulationVals[y+StartY][x+StartX] = ModulationBits & 3;
|
||||
ModulationBits >>= 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(Do2bitMode) // else if direct encoded 2bit mode - i.e. 1 mode bit per pixel
|
||||
{
|
||||
for(y = 0; y < BLK_Y_SIZE; y++)
|
||||
{
|
||||
for(x = 0; x < BLK_X_2BPP; x++)
|
||||
{
|
||||
ModulationModes[y+StartY][x+StartX] = BlockModMode;
|
||||
|
||||
// double the bits so 0=> 00, and 1=>11
|
||||
if(ModulationBits & 1)
|
||||
{
|
||||
ModulationVals[y+StartY][x+StartX] = 0x3;
|
||||
}
|
||||
else
|
||||
{
|
||||
ModulationVals[y+StartY][x+StartX] = 0x0;
|
||||
}
|
||||
ModulationBits >>= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else // else its the 4bpp mode so each value has 2 bits
|
||||
{
|
||||
for(y = 0; y < BLK_Y_SIZE; y++)
|
||||
{
|
||||
for(x = 0; x < BLK_X_4BPP; x++)
|
||||
{
|
||||
ModulationModes[y+StartY][x+StartX] = BlockModMode;
|
||||
|
||||
ModulationVals[y+StartY][x+StartX] = ModulationBits & 3;
|
||||
ModulationBits >>= 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// make sure nothing is left over
|
||||
assert(ModulationBits==0);
|
||||
}
|
||||
|
||||
/*!***********************************************************************
|
||||
@Function InterpolateColours
|
||||
@Input ColourP
|
||||
@Input ColourQ
|
||||
@Input ColourR
|
||||
@Input ColourS
|
||||
@Input Do2bitMode
|
||||
@Input x
|
||||
@Input y
|
||||
@Modified Result
|
||||
@Description This performs a HW bit accurate interpolation of either the
|
||||
A or B colours for a particular pixel.
|
||||
|
||||
NOTE: It is assumed that the source colours are in ARGB 5554
|
||||
format - This means that some "preparation" of the values will
|
||||
be necessary.
|
||||
*************************************************************************/
|
||||
static void InterpolateColours(const int ColourP[4],
|
||||
const int ColourQ[4],
|
||||
const int ColourR[4],
|
||||
const int ColourS[4],
|
||||
const int Do2bitMode,
|
||||
const int x,
|
||||
const int y,
|
||||
int Result[4])
|
||||
{
|
||||
int u, v, uscale;
|
||||
int k;
|
||||
|
||||
int tmp1, tmp2;
|
||||
|
||||
int P[4], Q[4], R[4], S[4];
|
||||
|
||||
// Copy the colours
|
||||
for(k = 0; k < 4; k++)
|
||||
{
|
||||
P[k] = ColourP[k];
|
||||
Q[k] = ColourQ[k];
|
||||
R[k] = ColourR[k];
|
||||
S[k] = ColourS[k];
|
||||
}
|
||||
|
||||
// put the x and y values into the right range
|
||||
v = (y & 0x3) | ((~y & 0x2) << 1);
|
||||
|
||||
if(Do2bitMode)
|
||||
u = (x & 0x7) | ((~x & 0x4) << 1);
|
||||
else
|
||||
u = (x & 0x3) | ((~x & 0x2) << 1);
|
||||
|
||||
// get the u and v scale amounts
|
||||
v = v - BLK_Y_SIZE/2;
|
||||
|
||||
if(Do2bitMode)
|
||||
{
|
||||
u = u - BLK_X_2BPP/2;
|
||||
uscale = 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
u = u - BLK_X_4BPP/2;
|
||||
uscale = 4;
|
||||
}
|
||||
|
||||
for(k = 0; k < 4; k++)
|
||||
{
|
||||
tmp1 = P[k] * uscale + u * (Q[k] - P[k]);
|
||||
tmp2 = R[k] * uscale + u * (S[k] - R[k]);
|
||||
|
||||
tmp1 = tmp1 * 4 + v * (tmp2 - tmp1);
|
||||
|
||||
Result[k] = tmp1;
|
||||
}
|
||||
|
||||
// Lop off the appropriate number of bits to get us to 8 bit precision
|
||||
if(Do2bitMode)
|
||||
{
|
||||
// do RGB
|
||||
for(k = 0; k < 3; k++)
|
||||
{
|
||||
Result[k] >>= 2;
|
||||
}
|
||||
|
||||
Result[3] >>= 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// do RGB (A is ok)
|
||||
for(k = 0; k < 3; k++)
|
||||
{
|
||||
Result[k] >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
// sanity check
|
||||
for(k = 0; k < 4; k++)
|
||||
{
|
||||
assert(Result[k] < 256);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Convert from 5554 to 8888
|
||||
|
||||
do RGB 5.3 => 8
|
||||
*/
|
||||
for(k = 0; k < 3; k++)
|
||||
{
|
||||
Result[k] += Result[k] >> 5;
|
||||
}
|
||||
|
||||
Result[3] += Result[3] >> 4;
|
||||
|
||||
// 2nd sanity check
|
||||
for(k = 0; k < 4; k++)
|
||||
{
|
||||
assert(Result[k] < 256);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*!***********************************************************************
|
||||
@Function GetModulationValue
|
||||
@Input x
|
||||
@Input y
|
||||
@Input Do2bitMode
|
||||
@Input ModulationVals
|
||||
@Input ModulationModes
|
||||
@Input Mod
|
||||
@Input DoPT
|
||||
@Description Get the modulation value as a numerator of a fraction of 8ths
|
||||
*************************************************************************/
|
||||
static void GetModulationValue(int x,
|
||||
int y,
|
||||
const int Do2bitMode,
|
||||
const int ModulationVals[8][16],
|
||||
const int ModulationModes[8][16],
|
||||
int *Mod,
|
||||
int *DoPT)
|
||||
{
|
||||
static const int RepVals0[4] = {0, 3, 5, 8};
|
||||
static const int RepVals1[4] = {0, 4, 4, 8};
|
||||
|
||||
int ModVal;
|
||||
|
||||
// Map X and Y into the local 2x2 block
|
||||
y = (y & 0x3) | ((~y & 0x2) << 1);
|
||||
|
||||
if(Do2bitMode)
|
||||
x = (x & 0x7) | ((~x & 0x4) << 1);
|
||||
else
|
||||
x = (x & 0x3) | ((~x & 0x2) << 1);
|
||||
|
||||
// assume no PT for now
|
||||
*DoPT = 0;
|
||||
|
||||
// extract the modulation value. If a simple encoding
|
||||
if(ModulationModes[y][x]==0)
|
||||
{
|
||||
ModVal = RepVals0[ModulationVals[y][x]];
|
||||
}
|
||||
else if(Do2bitMode)
|
||||
{
|
||||
// if this is a stored value
|
||||
if(((x^y)&1)==0)
|
||||
ModVal = RepVals0[ModulationVals[y][x]];
|
||||
else if(ModulationModes[y][x] == 1) // else average from the neighbours if H&V interpolation..
|
||||
{
|
||||
ModVal = (RepVals0[ModulationVals[y-1][x]] +
|
||||
RepVals0[ModulationVals[y+1][x]] +
|
||||
RepVals0[ModulationVals[y][x-1]] +
|
||||
RepVals0[ModulationVals[y][x+1]] + 2) / 4;
|
||||
}
|
||||
else if(ModulationModes[y][x] == 2) // else if H-Only
|
||||
{
|
||||
ModVal = (RepVals0[ModulationVals[y][x-1]] +
|
||||
RepVals0[ModulationVals[y][x+1]] + 1) / 2;
|
||||
}
|
||||
else // else it's V-Only
|
||||
{
|
||||
ModVal = (RepVals0[ModulationVals[y-1][x]] +
|
||||
RepVals0[ModulationVals[y+1][x]] + 1) / 2;
|
||||
}
|
||||
}
|
||||
else // else it's 4BPP and PT encoding
|
||||
{
|
||||
ModVal = RepVals1[ModulationVals[y][x]];
|
||||
|
||||
*DoPT = ModulationVals[y][x] == PT_INDEX;
|
||||
}
|
||||
|
||||
*Mod =ModVal;
|
||||
}
|
||||
|
||||
/*!***********************************************************************
|
||||
@Function TwiddleUV
|
||||
@Input YSize Y dimension of the texture in pixels
|
||||
@Input XSize X dimension of the texture in pixels
|
||||
@Input YPos Pixel Y position
|
||||
@Input XPos Pixel X position
|
||||
@Returns The twiddled offset of the pixel
|
||||
@Description Given the Block (or pixel) coordinates and the dimension of
|
||||
the texture in blocks (or pixels) this returns the twiddled
|
||||
offset of the block (or pixel) from the start of the map.
|
||||
|
||||
NOTE the dimensions of the texture must be a power of 2
|
||||
*************************************************************************/
|
||||
static int DisableTwiddlingRoutine = 0;
|
||||
|
||||
static U32 TwiddleUV(U32 YSize, U32 XSize, U32 YPos, U32 XPos)
|
||||
{
|
||||
U32 Twiddled;
|
||||
|
||||
U32 MinDimension;
|
||||
U32 MaxValue;
|
||||
|
||||
U32 SrcBitPos;
|
||||
U32 DstBitPos;
|
||||
|
||||
int ShiftCount;
|
||||
|
||||
assert(YPos < YSize);
|
||||
assert(XPos < XSize);
|
||||
|
||||
assert(POWER_OF_2(YSize));
|
||||
assert(POWER_OF_2(XSize));
|
||||
|
||||
if(YSize < XSize)
|
||||
{
|
||||
MinDimension = YSize;
|
||||
MaxValue = XPos;
|
||||
}
|
||||
else
|
||||
{
|
||||
MinDimension = XSize;
|
||||
MaxValue = YPos;
|
||||
}
|
||||
|
||||
// Nasty hack to disable twiddling
|
||||
if(DisableTwiddlingRoutine)
|
||||
return (YPos* XSize + XPos);
|
||||
|
||||
// Step through all the bits in the "minimum" dimension
|
||||
SrcBitPos = 1;
|
||||
DstBitPos = 1;
|
||||
Twiddled = 0;
|
||||
ShiftCount = 0;
|
||||
|
||||
while(SrcBitPos < MinDimension)
|
||||
{
|
||||
if(YPos & SrcBitPos)
|
||||
{
|
||||
Twiddled |= DstBitPos;
|
||||
}
|
||||
|
||||
if(XPos & SrcBitPos)
|
||||
{
|
||||
Twiddled |= (DstBitPos << 1);
|
||||
}
|
||||
|
||||
|
||||
SrcBitPos <<= 1;
|
||||
DstBitPos <<= 2;
|
||||
ShiftCount += 1;
|
||||
|
||||
}
|
||||
|
||||
// prepend any unused bits
|
||||
MaxValue >>= ShiftCount;
|
||||
|
||||
Twiddled |= (MaxValue << (2*ShiftCount));
|
||||
|
||||
return Twiddled;
|
||||
}
|
||||
|
||||
/*!***********************************************************************
|
||||
@Function Decompress
|
||||
@Input pCompressedData The PVRTC texture data to decompress
|
||||
@Input Do2BitMode Signifies whether the data is PVRTC2 or PVRTC4
|
||||
@Input XDim X dimension of the texture
|
||||
@Input YDim Y dimension of the texture
|
||||
@Input AssumeImageTiles Assume the texture data tiles
|
||||
@Modified pResultImage The decompressed texture data
|
||||
@Description Decompresses PVRTC to RGBA 8888
|
||||
*************************************************************************/
|
||||
static void PVRDecompress(AMTC_BLOCK_STRUCT *pCompressedData,
|
||||
const bool Do2bitMode,
|
||||
const int XDim,
|
||||
const int YDim,
|
||||
const int AssumeImageTiles,
|
||||
unsigned char* pResultImage)
|
||||
{
|
||||
int x, y;
|
||||
int i, j;
|
||||
|
||||
int BlkX, BlkY;
|
||||
int BlkXp1, BlkYp1;
|
||||
int XBlockSize;
|
||||
int BlkXDim, BlkYDim;
|
||||
|
||||
int StartX, StartY;
|
||||
|
||||
int ModulationVals[8][16];
|
||||
int ModulationModes[8][16];
|
||||
|
||||
int Mod, DoPT;
|
||||
|
||||
unsigned int uPosition;
|
||||
|
||||
// local neighbourhood of blocks
|
||||
AMTC_BLOCK_STRUCT *pBlocks[2][2];
|
||||
|
||||
AMTC_BLOCK_STRUCT *pPrevious[2][2] = {{NULL, NULL}, {NULL, NULL}};
|
||||
|
||||
// Low precision colours extracted from the blocks
|
||||
struct
|
||||
{
|
||||
int Reps[2][4];
|
||||
}Colours5554[2][2];
|
||||
|
||||
// Interpolated A and B colours for the pixel
|
||||
int ASig[4], BSig[4];
|
||||
|
||||
int Result[4];
|
||||
|
||||
if(Do2bitMode)
|
||||
XBlockSize = BLK_X_2BPP;
|
||||
else
|
||||
XBlockSize = BLK_X_4BPP;
|
||||
|
||||
// For MBX don't allow the sizes to get too small
|
||||
BlkXDim = PVRT_MAX(2, XDim / XBlockSize);
|
||||
BlkYDim = PVRT_MAX(2, YDim / BLK_Y_SIZE);
|
||||
|
||||
/*
|
||||
Step through the pixels of the image decompressing each one in turn
|
||||
|
||||
Note that this is a hideously inefficient way to do this!
|
||||
*/
|
||||
for(y = 0; y < YDim; y++)
|
||||
{
|
||||
for(x = 0; x < XDim; x++)
|
||||
{
|
||||
// map this pixel to the top left neighbourhood of blocks
|
||||
BlkX = (x - XBlockSize/2);
|
||||
BlkY = (y - BLK_Y_SIZE/2);
|
||||
|
||||
BlkX = LIMIT_COORD(BlkX, XDim, AssumeImageTiles);
|
||||
BlkY = LIMIT_COORD(BlkY, YDim, AssumeImageTiles);
|
||||
|
||||
|
||||
BlkX /= XBlockSize;
|
||||
BlkY /= BLK_Y_SIZE;
|
||||
|
||||
// compute the positions of the other 3 blocks
|
||||
BlkXp1 = LIMIT_COORD(BlkX+1, BlkXDim, AssumeImageTiles);
|
||||
BlkYp1 = LIMIT_COORD(BlkY+1, BlkYDim, AssumeImageTiles);
|
||||
|
||||
// Map to block memory locations
|
||||
pBlocks[0][0] = pCompressedData +TwiddleUV(BlkYDim, BlkXDim, BlkY, BlkX);
|
||||
pBlocks[0][1] = pCompressedData +TwiddleUV(BlkYDim, BlkXDim, BlkY, BlkXp1);
|
||||
pBlocks[1][0] = pCompressedData +TwiddleUV(BlkYDim, BlkXDim, BlkYp1, BlkX);
|
||||
pBlocks[1][1] = pCompressedData +TwiddleUV(BlkYDim, BlkXDim, BlkYp1, BlkXp1);
|
||||
|
||||
|
||||
/*
|
||||
extract the colours and the modulation information IF the previous values
|
||||
have changed.
|
||||
*/
|
||||
if(memcmp(pPrevious, pBlocks, 4*sizeof(void*)) != 0)
|
||||
{
|
||||
StartY = 0;
|
||||
for(i = 0; i < 2; i++)
|
||||
{
|
||||
StartX = 0;
|
||||
for(j = 0; j < 2; j++)
|
||||
{
|
||||
Unpack5554Colour(pBlocks[i][j], Colours5554[i][j].Reps);
|
||||
|
||||
UnpackModulations(pBlocks[i][j],
|
||||
Do2bitMode,
|
||||
ModulationVals,
|
||||
ModulationModes,
|
||||
StartX, StartY);
|
||||
|
||||
StartX += XBlockSize;
|
||||
}
|
||||
|
||||
StartY += BLK_Y_SIZE;
|
||||
}
|
||||
|
||||
// make a copy of the new pointers
|
||||
memcpy(pPrevious, pBlocks, 4*sizeof(void*));
|
||||
}
|
||||
|
||||
// decompress the pixel. First compute the interpolated A and B signals
|
||||
InterpolateColours(Colours5554[0][0].Reps[0],
|
||||
Colours5554[0][1].Reps[0],
|
||||
Colours5554[1][0].Reps[0],
|
||||
Colours5554[1][1].Reps[0],
|
||||
Do2bitMode, x, y,
|
||||
ASig);
|
||||
|
||||
InterpolateColours(Colours5554[0][0].Reps[1],
|
||||
Colours5554[0][1].Reps[1],
|
||||
Colours5554[1][0].Reps[1],
|
||||
Colours5554[1][1].Reps[1],
|
||||
Do2bitMode, x, y,
|
||||
BSig);
|
||||
|
||||
GetModulationValue(x,y, Do2bitMode, (const int (*)[16])ModulationVals, (const int (*)[16])ModulationModes,
|
||||
&Mod, &DoPT);
|
||||
|
||||
// compute the modulated colour
|
||||
for(i = 0; i < 4; i++)
|
||||
{
|
||||
Result[i] = ASig[i] * 8 + Mod * (BSig[i] - ASig[i]);
|
||||
Result[i] >>= 3;
|
||||
}
|
||||
|
||||
if(DoPT)
|
||||
Result[3] = 0;
|
||||
|
||||
// Store the result in the output image
|
||||
uPosition = (x+y*XDim)<<2;
|
||||
pResultImage[uPosition+0] = (U8)Result[0];
|
||||
pResultImage[uPosition+1] = (U8)Result[1];
|
||||
pResultImage[uPosition+2] = (U8)Result[2];
|
||||
pResultImage[uPosition+3] = (U8)Result[3];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
End of file (pvr.cpp)
|
||||
*****************************************************************************/
|
||||
|
||||
23
cocos2d-x/cocos/base/pvr.h
Normal file
23
cocos2d-x/cocos/base/pvr.h
Normal file
@@ -0,0 +1,23 @@
|
||||
/******************************************************************************
|
||||
|
||||
@File PVRTDecompress.h
|
||||
|
||||
@Title
|
||||
|
||||
@Copyright Copyright (C) 2000 - 2008 by Imagination Technologies Limited.
|
||||
|
||||
@Platform ANSI compatible
|
||||
|
||||
@Description PVRTC Texture Decompression.
|
||||
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef __PVR_H__
|
||||
#define __PVR_H__
|
||||
|
||||
|
||||
int PVRTDecompressPVRTC(const void * const pCompressedData,const int XDim,const int YDim,void *pDestData,const bool Do2bitMode);
|
||||
|
||||
|
||||
#endif //__PVR_H__
|
||||
|
||||
943
cocos2d-x/cocos/base/uthash.h
Normal file
943
cocos2d-x/cocos/base/uthash.h
Normal file
@@ -0,0 +1,943 @@
|
||||
/*
|
||||
Copyright (c) 2003-2013, Troy D. Hanson http://troydhanson.github.com/uthash/
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef UTHASH_H
|
||||
#define UTHASH_H
|
||||
/// @cond DO_NOT_SHOW
|
||||
|
||||
#include <string.h> /* memcmp,strlen */
|
||||
#include <stddef.h> /* ptrdiff_t */
|
||||
#include <stdlib.h> /* exit() */
|
||||
|
||||
/* These macros use decltype or the earlier __typeof GNU extension.
|
||||
As decltype is only available in newer compilers (VS2010 or gcc 4.3+
|
||||
when compiling c++ source) this code uses whatever method is needed
|
||||
or, for VS2008 where neither is available, uses casting workarounds. */
|
||||
#ifdef _MSC_VER /* MS compiler */
|
||||
#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */
|
||||
#define DECLTYPE(x) (decltype(x))
|
||||
#else /* VS2008 or older (or VS2010 in C mode) */
|
||||
#define NO_DECLTYPE
|
||||
#define DECLTYPE(x)
|
||||
#endif
|
||||
#else /* GNU, Sun and other compilers */
|
||||
#define DECLTYPE(x) (__typeof(x))
|
||||
#endif
|
||||
|
||||
#ifdef NO_DECLTYPE
|
||||
#define DECLTYPE_ASSIGN(dst,src) \
|
||||
do { \
|
||||
char **_da_dst = (char**)(&(dst)); \
|
||||
*_da_dst = (char*)(src); \
|
||||
} while(0)
|
||||
#else
|
||||
#define DECLTYPE_ASSIGN(dst,src) \
|
||||
do { \
|
||||
(dst) = DECLTYPE(dst)(src); \
|
||||
} while(0)
|
||||
#endif
|
||||
|
||||
/* a number of the hash function use uint32_t which isn't defined on win32 */
|
||||
#ifdef _MSC_VER
|
||||
typedef unsigned int uint32_t;
|
||||
typedef unsigned char uint8_t;
|
||||
#else
|
||||
#include <inttypes.h> /* uint32_t */
|
||||
#endif
|
||||
|
||||
#define UTHASH_VERSION 1.9.8
|
||||
|
||||
#ifndef uthash_fatal
|
||||
#define uthash_fatal(msg) exit(-1) /* fatal error (out of memory,etc) */
|
||||
#endif
|
||||
#ifndef uthash_malloc
|
||||
#define uthash_malloc(sz) malloc(sz) /* malloc fcn */
|
||||
#endif
|
||||
#ifndef uthash_free
|
||||
#define uthash_free(ptr,sz) free(ptr) /* free fcn */
|
||||
#endif
|
||||
|
||||
#ifndef uthash_noexpand_fyi
|
||||
#define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */
|
||||
#endif
|
||||
#ifndef uthash_expand_fyi
|
||||
#define uthash_expand_fyi(tbl) /* can be defined to log expands */
|
||||
#endif
|
||||
|
||||
/* initial number of buckets */
|
||||
#define HASH_INITIAL_NUM_BUCKETS 32 /* initial number of buckets */
|
||||
#define HASH_INITIAL_NUM_BUCKETS_LOG2 5 /* lg2 of initial number of buckets */
|
||||
#define HASH_BKT_CAPACITY_THRESH 10 /* expand when bucket count reaches */
|
||||
|
||||
/* calculate the element whose hash handle address is hhe */
|
||||
#define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho)))
|
||||
|
||||
#define HASH_FIND(hh,head,keyptr,keylen,out) \
|
||||
do { \
|
||||
unsigned _hf_bkt,_hf_hashv; \
|
||||
out=NULL; \
|
||||
if (head) { \
|
||||
HASH_FCN(keyptr,keylen, (head)->hh.tbl->num_buckets, _hf_hashv, _hf_bkt); \
|
||||
if (HASH_BLOOM_TEST((head)->hh.tbl, _hf_hashv)) { \
|
||||
HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], \
|
||||
keyptr,keylen,out); \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#ifdef HASH_BLOOM
|
||||
#define HASH_BLOOM_BITLEN (1ULL << HASH_BLOOM)
|
||||
#define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8) + ((HASH_BLOOM_BITLEN%8) ? 1:0)
|
||||
#define HASH_BLOOM_MAKE(tbl) \
|
||||
do { \
|
||||
(tbl)->bloom_nbits = HASH_BLOOM; \
|
||||
(tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN); \
|
||||
if (!((tbl)->bloom_bv)) { uthash_fatal( "out of memory"); } \
|
||||
memset((tbl)->bloom_bv, 0, HASH_BLOOM_BYTELEN); \
|
||||
(tbl)->bloom_sig = HASH_BLOOM_SIGNATURE; \
|
||||
} while (0)
|
||||
|
||||
#define HASH_BLOOM_FREE(tbl) \
|
||||
do { \
|
||||
uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \
|
||||
} while (0)
|
||||
|
||||
#define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8] |= (1U << ((idx)%8)))
|
||||
#define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8] & (1U << ((idx)%8)))
|
||||
|
||||
#define HASH_BLOOM_ADD(tbl,hashv) \
|
||||
HASH_BLOOM_BITSET((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1)))
|
||||
|
||||
#define HASH_BLOOM_TEST(tbl,hashv) \
|
||||
HASH_BLOOM_BITTEST((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1)))
|
||||
|
||||
#else
|
||||
#define HASH_BLOOM_MAKE(tbl)
|
||||
#define HASH_BLOOM_FREE(tbl)
|
||||
#define HASH_BLOOM_ADD(tbl,hashv)
|
||||
#define HASH_BLOOM_TEST(tbl,hashv) (1)
|
||||
#define HASH_BLOOM_BYTELEN 0
|
||||
#endif
|
||||
|
||||
#define HASH_MAKE_TABLE(hh,head) \
|
||||
do { \
|
||||
(head)->hh.tbl = (UT_hash_table*)uthash_malloc( \
|
||||
sizeof(UT_hash_table)); \
|
||||
if (!((head)->hh.tbl)) { uthash_fatal( "out of memory"); } \
|
||||
memset((head)->hh.tbl, 0, sizeof(UT_hash_table)); \
|
||||
(head)->hh.tbl->tail = &((head)->hh); \
|
||||
(head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \
|
||||
(head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \
|
||||
(head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head); \
|
||||
(head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc( \
|
||||
HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \
|
||||
if (! (head)->hh.tbl->buckets) { uthash_fatal( "out of memory"); } \
|
||||
memset((head)->hh.tbl->buckets, 0, \
|
||||
HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \
|
||||
HASH_BLOOM_MAKE((head)->hh.tbl); \
|
||||
(head)->hh.tbl->signature = HASH_SIGNATURE; \
|
||||
} while(0)
|
||||
|
||||
#define HASH_ADD(hh,head,fieldname,keylen_in,add) \
|
||||
HASH_ADD_KEYPTR(hh,head,&((add)->fieldname),keylen_in,add)
|
||||
|
||||
#define HASH_REPLACE(hh,head,fieldname,keylen_in,add,replaced) \
|
||||
do { \
|
||||
replaced=NULL; \
|
||||
HASH_FIND(hh,head,&((add)->fieldname),keylen_in,replaced); \
|
||||
if (replaced!=NULL) { \
|
||||
HASH_DELETE(hh,head,replaced); \
|
||||
}; \
|
||||
HASH_ADD(hh,head,fieldname,keylen_in,add); \
|
||||
} while(0)
|
||||
|
||||
#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \
|
||||
do { \
|
||||
unsigned _ha_bkt; \
|
||||
(add)->hh.next = NULL; \
|
||||
(add)->hh.key = (char*)keyptr; \
|
||||
(add)->hh.keylen = (unsigned)keylen_in; \
|
||||
if (!(head)) { \
|
||||
head = (add); \
|
||||
(head)->hh.prev = NULL; \
|
||||
HASH_MAKE_TABLE(hh,head); \
|
||||
} else { \
|
||||
(head)->hh.tbl->tail->next = (add); \
|
||||
(add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \
|
||||
(head)->hh.tbl->tail = &((add)->hh); \
|
||||
} \
|
||||
(head)->hh.tbl->num_items++; \
|
||||
(add)->hh.tbl = (head)->hh.tbl; \
|
||||
HASH_FCN(keyptr,keylen_in, (head)->hh.tbl->num_buckets, \
|
||||
(add)->hh.hashv, _ha_bkt); \
|
||||
HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt],&(add)->hh); \
|
||||
HASH_BLOOM_ADD((head)->hh.tbl,(add)->hh.hashv); \
|
||||
HASH_EMIT_KEY(hh,head,keyptr,keylen_in); \
|
||||
HASH_FSCK(hh,head); \
|
||||
} while(0)
|
||||
|
||||
#define HASH_TO_BKT( hashv, num_bkts, bkt ) \
|
||||
do { \
|
||||
bkt = ((hashv) & ((num_bkts) - 1)); \
|
||||
} while(0)
|
||||
|
||||
/* delete "delptr" from the hash table.
|
||||
* "the usual" patch-up process for the app-order doubly-linked-list.
|
||||
* The use of _hd_hh_del below deserves special explanation.
|
||||
* These used to be expressed using (delptr) but that led to a bug
|
||||
* if someone used the same symbol for the head and deletee, like
|
||||
* HASH_DELETE(hh,users,users);
|
||||
* We want that to work, but by changing the head (users) below
|
||||
* we were forfeiting our ability to further refer to the deletee (users)
|
||||
* in the patch-up process. Solution: use scratch space to
|
||||
* copy the deletee pointer, then the latter references are via that
|
||||
* scratch pointer rather than through the repointed (users) symbol.
|
||||
*/
|
||||
#define HASH_DELETE(hh,head,delptr) \
|
||||
do { \
|
||||
unsigned _hd_bkt; \
|
||||
struct UT_hash_handle *_hd_hh_del; \
|
||||
if ( ((delptr)->hh.prev == NULL) && ((delptr)->hh.next == NULL) ) { \
|
||||
uthash_free((head)->hh.tbl->buckets, \
|
||||
(head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \
|
||||
HASH_BLOOM_FREE((head)->hh.tbl); \
|
||||
uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \
|
||||
head = NULL; \
|
||||
} else { \
|
||||
_hd_hh_del = &((delptr)->hh); \
|
||||
if ((delptr) == ELMT_FROM_HH((head)->hh.tbl,(head)->hh.tbl->tail)) { \
|
||||
(head)->hh.tbl->tail = \
|
||||
(UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) + \
|
||||
(head)->hh.tbl->hho); \
|
||||
} \
|
||||
if ((delptr)->hh.prev) { \
|
||||
((UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) + \
|
||||
(head)->hh.tbl->hho))->next = (delptr)->hh.next; \
|
||||
} else { \
|
||||
DECLTYPE_ASSIGN(head,(delptr)->hh.next); \
|
||||
} \
|
||||
if (_hd_hh_del->next) { \
|
||||
((UT_hash_handle*)((ptrdiff_t)_hd_hh_del->next + \
|
||||
(head)->hh.tbl->hho))->prev = \
|
||||
_hd_hh_del->prev; \
|
||||
} \
|
||||
HASH_TO_BKT( _hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \
|
||||
HASH_DEL_IN_BKT(hh,(head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \
|
||||
(head)->hh.tbl->num_items--; \
|
||||
} \
|
||||
HASH_FSCK(hh,head); \
|
||||
} while (0)
|
||||
|
||||
|
||||
/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */
|
||||
#define HASH_FIND_STR(head,findstr,out) \
|
||||
HASH_FIND(hh,head,findstr,strlen(findstr),out)
|
||||
#define HASH_ADD_STR(head,strfield,add) \
|
||||
HASH_ADD(hh,head,strfield,strlen(add->strfield),add)
|
||||
#define HASH_REPLACE_STR(head,strfield,add,replaced) \
|
||||
HASH_REPLACE(hh,head,strfield,strlen(add->strfield),add,replaced)
|
||||
#define HASH_FIND_INT(head,findint,out) \
|
||||
HASH_FIND(hh,head,findint,sizeof(int),out)
|
||||
#define HASH_ADD_INT(head,intfield,add) \
|
||||
HASH_ADD(hh,head,intfield,sizeof(int),add)
|
||||
#define HASH_REPLACE_INT(head,intfield,add,replaced) \
|
||||
HASH_REPLACE(hh,head,intfield,sizeof(int),add,replaced)
|
||||
#define HASH_FIND_PTR(head,findptr,out) \
|
||||
HASH_FIND(hh,head,findptr,sizeof(void *),out)
|
||||
#define HASH_ADD_PTR(head,ptrfield,add) \
|
||||
HASH_ADD(hh,head,ptrfield,sizeof(void *),add)
|
||||
#define HASH_REPLACE_PTR(head,ptrfield,add) \
|
||||
HASH_REPLACE(hh,head,ptrfield,sizeof(void *),add,replaced)
|
||||
#define HASH_DEL(head,delptr) \
|
||||
HASH_DELETE(hh,head,delptr)
|
||||
|
||||
/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined.
|
||||
* This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined.
|
||||
*/
|
||||
#ifdef HASH_DEBUG
|
||||
#define HASH_OOPS(...) do { fprintf(stderr,__VA_ARGS__); exit(-1); } while (0)
|
||||
#define HASH_FSCK(hh,head) \
|
||||
do { \
|
||||
unsigned _bkt_i; \
|
||||
unsigned _count, _bkt_count; \
|
||||
char *_prev; \
|
||||
struct UT_hash_handle *_thh; \
|
||||
if (head) { \
|
||||
_count = 0; \
|
||||
for( _bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; _bkt_i++) { \
|
||||
_bkt_count = 0; \
|
||||
_thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \
|
||||
_prev = NULL; \
|
||||
while (_thh) { \
|
||||
if (_prev != (char*)(_thh->hh_prev)) { \
|
||||
HASH_OOPS("invalid hh_prev %p, actual %p\n", \
|
||||
_thh->hh_prev, _prev ); \
|
||||
} \
|
||||
_bkt_count++; \
|
||||
_prev = (char*)(_thh); \
|
||||
_thh = _thh->hh_next; \
|
||||
} \
|
||||
_count += _bkt_count; \
|
||||
if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) { \
|
||||
HASH_OOPS("invalid bucket count %d, actual %d\n", \
|
||||
(head)->hh.tbl->buckets[_bkt_i].count, _bkt_count); \
|
||||
} \
|
||||
} \
|
||||
if (_count != (head)->hh.tbl->num_items) { \
|
||||
HASH_OOPS("invalid hh item count %d, actual %d\n", \
|
||||
(head)->hh.tbl->num_items, _count ); \
|
||||
} \
|
||||
/* traverse hh in app order; check next/prev integrity, count */ \
|
||||
_count = 0; \
|
||||
_prev = NULL; \
|
||||
_thh = &(head)->hh; \
|
||||
while (_thh) { \
|
||||
_count++; \
|
||||
if (_prev !=(char*)(_thh->prev)) { \
|
||||
HASH_OOPS("invalid prev %p, actual %p\n", \
|
||||
_thh->prev, _prev ); \
|
||||
} \
|
||||
_prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh); \
|
||||
_thh = ( _thh->next ? (UT_hash_handle*)((char*)(_thh->next) + \
|
||||
(head)->hh.tbl->hho) : NULL ); \
|
||||
} \
|
||||
if (_count != (head)->hh.tbl->num_items) { \
|
||||
HASH_OOPS("invalid app item count %d, actual %d\n", \
|
||||
(head)->hh.tbl->num_items, _count ); \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
#else
|
||||
#define HASH_FSCK(hh,head)
|
||||
#endif
|
||||
|
||||
/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to
|
||||
* the descriptor to which this macro is defined for tuning the hash function.
|
||||
* The app can #include <unistd.h> to get the prototype for write(2). */
|
||||
#ifdef HASH_EMIT_KEYS
|
||||
#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) \
|
||||
do { \
|
||||
unsigned _klen = fieldlen; \
|
||||
write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \
|
||||
write(HASH_EMIT_KEYS, keyptr, fieldlen); \
|
||||
} while (0)
|
||||
#else
|
||||
#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen)
|
||||
#endif
|
||||
|
||||
/* default to Jenkin's hash unless overridden e.g. DHASH_FUNCTION=HASH_SAX */
|
||||
#ifdef HASH_FUNCTION
|
||||
#define HASH_FCN HASH_FUNCTION
|
||||
#else
|
||||
#define HASH_FCN HASH_JEN
|
||||
#endif
|
||||
|
||||
/* The Bernstein hash function, used in Perl prior to v5.6 */
|
||||
#define HASH_BER(key,keylen,num_bkts,hashv,bkt) \
|
||||
do { \
|
||||
unsigned _hb_keylen=keylen; \
|
||||
char *_hb_key=(char*)(key); \
|
||||
(hashv) = 0; \
|
||||
while (_hb_keylen--) { (hashv) = ((hashv) * 33) + *_hb_key++; } \
|
||||
bkt = (hashv) & (num_bkts-1); \
|
||||
} while (0)
|
||||
|
||||
|
||||
/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at
|
||||
* http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx */
|
||||
#define HASH_SAX(key,keylen,num_bkts,hashv,bkt) \
|
||||
do { \
|
||||
unsigned _sx_i; \
|
||||
char *_hs_key=(char*)(key); \
|
||||
hashv = 0; \
|
||||
for(_sx_i=0; _sx_i < keylen; _sx_i++) \
|
||||
hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i]; \
|
||||
bkt = hashv & (num_bkts-1); \
|
||||
} while (0)
|
||||
|
||||
#define HASH_FNV(key,keylen,num_bkts,hashv,bkt) \
|
||||
do { \
|
||||
unsigned _fn_i; \
|
||||
char *_hf_key=(char*)(key); \
|
||||
hashv = 2166136261UL; \
|
||||
for(_fn_i=0; _fn_i < keylen; _fn_i++) \
|
||||
hashv = (hashv * 16777619) ^ _hf_key[_fn_i]; \
|
||||
bkt = hashv & (num_bkts-1); \
|
||||
} while(0)
|
||||
|
||||
#define HASH_OAT(key,keylen,num_bkts,hashv,bkt) \
|
||||
do { \
|
||||
unsigned _ho_i; \
|
||||
char *_ho_key=(char*)(key); \
|
||||
hashv = 0; \
|
||||
for(_ho_i=0; _ho_i < keylen; _ho_i++) { \
|
||||
hashv += _ho_key[_ho_i]; \
|
||||
hashv += (hashv << 10); \
|
||||
hashv ^= (hashv >> 6); \
|
||||
} \
|
||||
hashv += (hashv << 3); \
|
||||
hashv ^= (hashv >> 11); \
|
||||
hashv += (hashv << 15); \
|
||||
bkt = hashv & (num_bkts-1); \
|
||||
} while(0)
|
||||
|
||||
#define HASH_JEN_MIX(a,b,c) \
|
||||
do { \
|
||||
a -= b; a -= c; a ^= ( c >> 13 ); \
|
||||
b -= c; b -= a; b ^= ( a << 8 ); \
|
||||
c -= a; c -= b; c ^= ( b >> 13 ); \
|
||||
a -= b; a -= c; a ^= ( c >> 12 ); \
|
||||
b -= c; b -= a; b ^= ( a << 16 ); \
|
||||
c -= a; c -= b; c ^= ( b >> 5 ); \
|
||||
a -= b; a -= c; a ^= ( c >> 3 ); \
|
||||
b -= c; b -= a; b ^= ( a << 10 ); \
|
||||
c -= a; c -= b; c ^= ( b >> 15 ); \
|
||||
} while (0)
|
||||
|
||||
#define HASH_JEN(key,keylen,num_bkts,hashv,bkt) \
|
||||
do { \
|
||||
unsigned _hj_i,_hj_j,_hj_k; \
|
||||
unsigned char *_hj_key=(unsigned char*)(key); \
|
||||
hashv = 0xfeedbeef; \
|
||||
_hj_i = _hj_j = 0x9e3779b9; \
|
||||
_hj_k = (unsigned)keylen; \
|
||||
while (_hj_k >= 12) { \
|
||||
_hj_i += (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 ) \
|
||||
+ ( (unsigned)_hj_key[2] << 16 ) \
|
||||
+ ( (unsigned)_hj_key[3] << 24 ) ); \
|
||||
_hj_j += (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 ) \
|
||||
+ ( (unsigned)_hj_key[6] << 16 ) \
|
||||
+ ( (unsigned)_hj_key[7] << 24 ) ); \
|
||||
hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 ) \
|
||||
+ ( (unsigned)_hj_key[10] << 16 ) \
|
||||
+ ( (unsigned)_hj_key[11] << 24 ) ); \
|
||||
\
|
||||
HASH_JEN_MIX(_hj_i, _hj_j, hashv); \
|
||||
\
|
||||
_hj_key += 12; \
|
||||
_hj_k -= 12; \
|
||||
} \
|
||||
hashv += keylen; \
|
||||
switch ( _hj_k ) { \
|
||||
case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); \
|
||||
case 10: hashv += ( (unsigned)_hj_key[9] << 16 ); \
|
||||
case 9: hashv += ( (unsigned)_hj_key[8] << 8 ); \
|
||||
case 8: _hj_j += ( (unsigned)_hj_key[7] << 24 ); \
|
||||
case 7: _hj_j += ( (unsigned)_hj_key[6] << 16 ); \
|
||||
case 6: _hj_j += ( (unsigned)_hj_key[5] << 8 ); \
|
||||
case 5: _hj_j += _hj_key[4]; \
|
||||
case 4: _hj_i += ( (unsigned)_hj_key[3] << 24 ); \
|
||||
case 3: _hj_i += ( (unsigned)_hj_key[2] << 16 ); \
|
||||
case 2: _hj_i += ( (unsigned)_hj_key[1] << 8 ); \
|
||||
case 1: _hj_i += _hj_key[0]; \
|
||||
} \
|
||||
HASH_JEN_MIX(_hj_i, _hj_j, hashv); \
|
||||
bkt = hashv & (num_bkts-1); \
|
||||
} while(0)
|
||||
|
||||
/* The Paul Hsieh hash function */
|
||||
#undef get16bits
|
||||
#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \
|
||||
|| defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__)
|
||||
#define get16bits(d) (*((const uint16_t *) (d)))
|
||||
#endif
|
||||
|
||||
#if !defined (get16bits)
|
||||
#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \
|
||||
+(uint32_t)(((const uint8_t *)(d))[0]) )
|
||||
#endif
|
||||
#define HASH_SFH(key,keylen,num_bkts,hashv,bkt) \
|
||||
do { \
|
||||
unsigned char *_sfh_key=(unsigned char*)(key); \
|
||||
uint32_t _sfh_tmp, _sfh_len = keylen; \
|
||||
\
|
||||
int _sfh_rem = _sfh_len & 3; \
|
||||
_sfh_len >>= 2; \
|
||||
hashv = 0xcafebabe; \
|
||||
\
|
||||
/* Main loop */ \
|
||||
for (;_sfh_len > 0; _sfh_len--) { \
|
||||
hashv += get16bits (_sfh_key); \
|
||||
_sfh_tmp = (uint32_t)(get16bits (_sfh_key+2)) << 11 ^ hashv; \
|
||||
hashv = (hashv << 16) ^ _sfh_tmp; \
|
||||
_sfh_key += 2*sizeof (uint16_t); \
|
||||
hashv += hashv >> 11; \
|
||||
} \
|
||||
\
|
||||
/* Handle end cases */ \
|
||||
switch (_sfh_rem) { \
|
||||
case 3: hashv += get16bits (_sfh_key); \
|
||||
hashv ^= hashv << 16; \
|
||||
hashv ^= (uint32_t)(_sfh_key[sizeof (uint16_t)] << 18); \
|
||||
hashv += hashv >> 11; \
|
||||
break; \
|
||||
case 2: hashv += get16bits (_sfh_key); \
|
||||
hashv ^= hashv << 11; \
|
||||
hashv += hashv >> 17; \
|
||||
break; \
|
||||
case 1: hashv += *_sfh_key; \
|
||||
hashv ^= hashv << 10; \
|
||||
hashv += hashv >> 1; \
|
||||
} \
|
||||
\
|
||||
/* Force "avalanching" of final 127 bits */ \
|
||||
hashv ^= hashv << 3; \
|
||||
hashv += hashv >> 5; \
|
||||
hashv ^= hashv << 4; \
|
||||
hashv += hashv >> 17; \
|
||||
hashv ^= hashv << 25; \
|
||||
hashv += hashv >> 6; \
|
||||
bkt = hashv & (num_bkts-1); \
|
||||
} while(0)
|
||||
|
||||
#ifdef HASH_USING_NO_STRICT_ALIASING
|
||||
/* The MurmurHash exploits some CPU's (x86,x86_64) tolerance for unaligned reads.
|
||||
* For other types of CPU's (e.g. Sparc) an unaligned read causes a bus error.
|
||||
* MurmurHash uses the faster approach only on CPU's where we know it's safe.
|
||||
*
|
||||
* Note the preprocessor built-in defines can be emitted using:
|
||||
*
|
||||
* gcc -m64 -dM -E - < /dev/null (on gcc)
|
||||
* cc -## a.c (where a.c is a simple test file) (Sun Studio)
|
||||
*/
|
||||
#if (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86))
|
||||
#define MUR_GETBLOCK(p,i) p[i]
|
||||
#else /* non intel */
|
||||
#define MUR_PLUS0_ALIGNED(p) (((unsigned long)p & 0x3) == 0)
|
||||
#define MUR_PLUS1_ALIGNED(p) (((unsigned long)p & 0x3) == 1)
|
||||
#define MUR_PLUS2_ALIGNED(p) (((unsigned long)p & 0x3) == 2)
|
||||
#define MUR_PLUS3_ALIGNED(p) (((unsigned long)p & 0x3) == 3)
|
||||
#define WP(p) ((uint32_t*)((unsigned long)(p) & ~3UL))
|
||||
#if (defined(__BIG_ENDIAN__) || defined(SPARC) || defined(__ppc__) || defined(__ppc64__))
|
||||
#define MUR_THREE_ONE(p) ((((*WP(p))&0x00ffffff) << 8) | (((*(WP(p)+1))&0xff000000) >> 24))
|
||||
#define MUR_TWO_TWO(p) ((((*WP(p))&0x0000ffff) <<16) | (((*(WP(p)+1))&0xffff0000) >> 16))
|
||||
#define MUR_ONE_THREE(p) ((((*WP(p))&0x000000ff) <<24) | (((*(WP(p)+1))&0xffffff00) >> 8))
|
||||
#else /* assume little endian non-intel */
|
||||
#define MUR_THREE_ONE(p) ((((*WP(p))&0xffffff00) >> 8) | (((*(WP(p)+1))&0x000000ff) << 24))
|
||||
#define MUR_TWO_TWO(p) ((((*WP(p))&0xffff0000) >>16) | (((*(WP(p)+1))&0x0000ffff) << 16))
|
||||
#define MUR_ONE_THREE(p) ((((*WP(p))&0xff000000) >>24) | (((*(WP(p)+1))&0x00ffffff) << 8))
|
||||
#endif
|
||||
#define MUR_GETBLOCK(p,i) (MUR_PLUS0_ALIGNED(p) ? ((p)[i]) : \
|
||||
(MUR_PLUS1_ALIGNED(p) ? MUR_THREE_ONE(p) : \
|
||||
(MUR_PLUS2_ALIGNED(p) ? MUR_TWO_TWO(p) : \
|
||||
MUR_ONE_THREE(p))))
|
||||
#endif
|
||||
#define MUR_ROTL32(x,r) (((x) << (r)) | ((x) >> (32 - (r))))
|
||||
#define MUR_FMIX(_h) \
|
||||
do { \
|
||||
_h ^= _h >> 16; \
|
||||
_h *= 0x85ebca6b; \
|
||||
_h ^= _h >> 13; \
|
||||
_h *= 0xc2b2ae35l; \
|
||||
_h ^= _h >> 16; \
|
||||
} while(0)
|
||||
|
||||
#define HASH_MUR(key,keylen,num_bkts,hashv,bkt) \
|
||||
do { \
|
||||
const uint8_t *_mur_data = (const uint8_t*)(key); \
|
||||
const int _mur_nblocks = (keylen) / 4; \
|
||||
uint32_t _mur_h1 = 0xf88D5353; \
|
||||
uint32_t _mur_c1 = 0xcc9e2d51; \
|
||||
uint32_t _mur_c2 = 0x1b873593; \
|
||||
uint32_t _mur_k1 = 0; \
|
||||
const uint8_t *_mur_tail; \
|
||||
const uint32_t *_mur_blocks = (const uint32_t*)(_mur_data+_mur_nblocks*4); \
|
||||
int _mur_i; \
|
||||
for(_mur_i = -_mur_nblocks; _mur_i; _mur_i++) { \
|
||||
_mur_k1 = MUR_GETBLOCK(_mur_blocks,_mur_i); \
|
||||
_mur_k1 *= _mur_c1; \
|
||||
_mur_k1 = MUR_ROTL32(_mur_k1,15); \
|
||||
_mur_k1 *= _mur_c2; \
|
||||
\
|
||||
_mur_h1 ^= _mur_k1; \
|
||||
_mur_h1 = MUR_ROTL32(_mur_h1,13); \
|
||||
_mur_h1 = _mur_h1*5+0xe6546b64; \
|
||||
} \
|
||||
_mur_tail = (const uint8_t*)(_mur_data + _mur_nblocks*4); \
|
||||
_mur_k1=0; \
|
||||
switch((keylen) & 3) { \
|
||||
case 3: _mur_k1 ^= _mur_tail[2] << 16; \
|
||||
case 2: _mur_k1 ^= _mur_tail[1] << 8; \
|
||||
case 1: _mur_k1 ^= _mur_tail[0]; \
|
||||
_mur_k1 *= _mur_c1; \
|
||||
_mur_k1 = MUR_ROTL32(_mur_k1,15); \
|
||||
_mur_k1 *= _mur_c2; \
|
||||
_mur_h1 ^= _mur_k1; \
|
||||
} \
|
||||
_mur_h1 ^= (keylen); \
|
||||
MUR_FMIX(_mur_h1); \
|
||||
hashv = _mur_h1; \
|
||||
bkt = hashv & (num_bkts-1); \
|
||||
} while(0)
|
||||
#endif /* HASH_USING_NO_STRICT_ALIASING */
|
||||
|
||||
/* key comparison function; return 0 if keys equal */
|
||||
#define HASH_KEYCMP(a,b,len) memcmp(a,b,len)
|
||||
|
||||
/* iterate over items in a known bucket to find desired item */
|
||||
#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,out) \
|
||||
do { \
|
||||
if (head.hh_head) DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,head.hh_head)); \
|
||||
else out=NULL; \
|
||||
while (out) { \
|
||||
if ((out)->hh.keylen == keylen_in) { \
|
||||
if ((HASH_KEYCMP((out)->hh.key,keyptr,keylen_in)) == 0) break; \
|
||||
} \
|
||||
if ((out)->hh.hh_next) DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,(out)->hh.hh_next)); \
|
||||
else out = NULL; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
/* add an item to a bucket */
|
||||
#define HASH_ADD_TO_BKT(head,addhh) \
|
||||
do { \
|
||||
head.count++; \
|
||||
(addhh)->hh_next = head.hh_head; \
|
||||
(addhh)->hh_prev = NULL; \
|
||||
if (head.hh_head) { (head).hh_head->hh_prev = (addhh); } \
|
||||
(head).hh_head=addhh; \
|
||||
if (head.count >= ((head.expand_mult+1) * HASH_BKT_CAPACITY_THRESH) \
|
||||
&& (addhh)->tbl->noexpand != 1) { \
|
||||
HASH_EXPAND_BUCKETS((addhh)->tbl); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
/* remove an item from a given bucket */
|
||||
#define HASH_DEL_IN_BKT(hh,head,hh_del) \
|
||||
(head).count--; \
|
||||
if ((head).hh_head == hh_del) { \
|
||||
(head).hh_head = hh_del->hh_next; \
|
||||
} \
|
||||
if (hh_del->hh_prev) { \
|
||||
hh_del->hh_prev->hh_next = hh_del->hh_next; \
|
||||
} \
|
||||
if (hh_del->hh_next) { \
|
||||
hh_del->hh_next->hh_prev = hh_del->hh_prev; \
|
||||
}
|
||||
|
||||
/* Bucket expansion has the effect of doubling the number of buckets
|
||||
* and redistributing the items into the new buckets. Ideally the
|
||||
* items will distribute more or less evenly into the new buckets
|
||||
* (the extent to which this is true is a measure of the quality of
|
||||
* the hash function as it applies to the key domain).
|
||||
*
|
||||
* With the items distributed into more buckets, the chain length
|
||||
* (item count) in each bucket is reduced. Thus by expanding buckets
|
||||
* the hash keeps a bound on the chain length. This bounded chain
|
||||
* length is the essence of how a hash provides constant time lookup.
|
||||
*
|
||||
* The calculation of tbl->ideal_chain_maxlen below deserves some
|
||||
* explanation. First, keep in mind that we're calculating the ideal
|
||||
* maximum chain length based on the *new* (doubled) bucket count.
|
||||
* In fractions this is just n/b (n=number of items,b=new num buckets).
|
||||
* Since the ideal chain length is an integer, we want to calculate
|
||||
* ceil(n/b). We don't depend on floating point arithmetic in this
|
||||
* hash, so to calculate ceil(n/b) with integers we could write
|
||||
*
|
||||
* ceil(n/b) = (n/b) + ((n%b)?1:0)
|
||||
*
|
||||
* and in fact a previous version of this hash did just that.
|
||||
* But now we have improved things a bit by recognizing that b is
|
||||
* always a power of two. We keep its base 2 log handy (call it lb),
|
||||
* so now we can write this with a bit shift and logical AND:
|
||||
*
|
||||
* ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0)
|
||||
*
|
||||
*/
|
||||
#define HASH_EXPAND_BUCKETS(tbl) \
|
||||
do { \
|
||||
unsigned _he_bkt; \
|
||||
unsigned _he_bkt_i; \
|
||||
struct UT_hash_handle *_he_thh, *_he_hh_nxt; \
|
||||
UT_hash_bucket *_he_new_buckets, *_he_newbkt; \
|
||||
_he_new_buckets = (UT_hash_bucket*)uthash_malloc( \
|
||||
2 * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \
|
||||
if (!_he_new_buckets) { uthash_fatal( "out of memory"); } \
|
||||
memset(_he_new_buckets, 0, \
|
||||
2 * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \
|
||||
tbl->ideal_chain_maxlen = \
|
||||
(tbl->num_items >> (tbl->log2_num_buckets+1)) + \
|
||||
((tbl->num_items & ((tbl->num_buckets*2)-1)) ? 1 : 0); \
|
||||
tbl->nonideal_items = 0; \
|
||||
for(_he_bkt_i = 0; _he_bkt_i < tbl->num_buckets; _he_bkt_i++) \
|
||||
{ \
|
||||
_he_thh = tbl->buckets[ _he_bkt_i ].hh_head; \
|
||||
while (_he_thh) { \
|
||||
_he_hh_nxt = _he_thh->hh_next; \
|
||||
HASH_TO_BKT( _he_thh->hashv, tbl->num_buckets*2, _he_bkt); \
|
||||
_he_newbkt = &(_he_new_buckets[ _he_bkt ]); \
|
||||
if (++(_he_newbkt->count) > tbl->ideal_chain_maxlen) { \
|
||||
tbl->nonideal_items++; \
|
||||
_he_newbkt->expand_mult = _he_newbkt->count / \
|
||||
tbl->ideal_chain_maxlen; \
|
||||
} \
|
||||
_he_thh->hh_prev = NULL; \
|
||||
_he_thh->hh_next = _he_newbkt->hh_head; \
|
||||
if (_he_newbkt->hh_head) _he_newbkt->hh_head->hh_prev = \
|
||||
_he_thh; \
|
||||
_he_newbkt->hh_head = _he_thh; \
|
||||
_he_thh = _he_hh_nxt; \
|
||||
} \
|
||||
} \
|
||||
uthash_free( tbl->buckets, tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \
|
||||
tbl->num_buckets *= 2; \
|
||||
tbl->log2_num_buckets++; \
|
||||
tbl->buckets = _he_new_buckets; \
|
||||
tbl->ineff_expands = (tbl->nonideal_items > (tbl->num_items >> 1)) ? \
|
||||
(tbl->ineff_expands+1) : 0; \
|
||||
if (tbl->ineff_expands > 1) { \
|
||||
tbl->noexpand=1; \
|
||||
uthash_noexpand_fyi(tbl); \
|
||||
} \
|
||||
uthash_expand_fyi(tbl); \
|
||||
} while(0)
|
||||
|
||||
|
||||
/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */
|
||||
/* Note that HASH_SORT assumes the hash handle name to be hh.
|
||||
* HASH_SRT was added to allow the hash handle name to be passed in. */
|
||||
#define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn)
|
||||
#define HASH_SRT(hh,head,cmpfcn) \
|
||||
do { \
|
||||
unsigned _hs_i; \
|
||||
unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize; \
|
||||
struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \
|
||||
if (head) { \
|
||||
_hs_insize = 1; \
|
||||
_hs_looping = 1; \
|
||||
_hs_list = &((head)->hh); \
|
||||
while (_hs_looping) { \
|
||||
_hs_p = _hs_list; \
|
||||
_hs_list = NULL; \
|
||||
_hs_tail = NULL; \
|
||||
_hs_nmerges = 0; \
|
||||
while (_hs_p) { \
|
||||
_hs_nmerges++; \
|
||||
_hs_q = _hs_p; \
|
||||
_hs_psize = 0; \
|
||||
for ( _hs_i = 0; _hs_i < _hs_insize; _hs_i++ ) { \
|
||||
_hs_psize++; \
|
||||
_hs_q = (UT_hash_handle*)((_hs_q->next) ? \
|
||||
((void*)((char*)(_hs_q->next) + \
|
||||
(head)->hh.tbl->hho)) : NULL); \
|
||||
if (! (_hs_q) ) break; \
|
||||
} \
|
||||
_hs_qsize = _hs_insize; \
|
||||
while ((_hs_psize > 0) || ((_hs_qsize > 0) && _hs_q )) { \
|
||||
if (_hs_psize == 0) { \
|
||||
_hs_e = _hs_q; \
|
||||
_hs_q = (UT_hash_handle*)((_hs_q->next) ? \
|
||||
((void*)((char*)(_hs_q->next) + \
|
||||
(head)->hh.tbl->hho)) : NULL); \
|
||||
_hs_qsize--; \
|
||||
} else if ( (_hs_qsize == 0) || !(_hs_q) ) { \
|
||||
_hs_e = _hs_p; \
|
||||
_hs_p = (UT_hash_handle*)((_hs_p->next) ? \
|
||||
((void*)((char*)(_hs_p->next) + \
|
||||
(head)->hh.tbl->hho)) : NULL); \
|
||||
_hs_psize--; \
|
||||
} else if (( \
|
||||
cmpfcn(DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_p)), \
|
||||
DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_q))) \
|
||||
) <= 0) { \
|
||||
_hs_e = _hs_p; \
|
||||
_hs_p = (UT_hash_handle*)((_hs_p->next) ? \
|
||||
((void*)((char*)(_hs_p->next) + \
|
||||
(head)->hh.tbl->hho)) : NULL); \
|
||||
_hs_psize--; \
|
||||
} else { \
|
||||
_hs_e = _hs_q; \
|
||||
_hs_q = (UT_hash_handle*)((_hs_q->next) ? \
|
||||
((void*)((char*)(_hs_q->next) + \
|
||||
(head)->hh.tbl->hho)) : NULL); \
|
||||
_hs_qsize--; \
|
||||
} \
|
||||
if ( _hs_tail ) { \
|
||||
_hs_tail->next = ((_hs_e) ? \
|
||||
ELMT_FROM_HH((head)->hh.tbl,_hs_e) : NULL); \
|
||||
} else { \
|
||||
_hs_list = _hs_e; \
|
||||
} \
|
||||
_hs_e->prev = ((_hs_tail) ? \
|
||||
ELMT_FROM_HH((head)->hh.tbl,_hs_tail) : NULL); \
|
||||
_hs_tail = _hs_e; \
|
||||
} \
|
||||
_hs_p = _hs_q; \
|
||||
} \
|
||||
_hs_tail->next = NULL; \
|
||||
if ( _hs_nmerges <= 1 ) { \
|
||||
_hs_looping=0; \
|
||||
(head)->hh.tbl->tail = _hs_tail; \
|
||||
DECLTYPE_ASSIGN(head,ELMT_FROM_HH((head)->hh.tbl, _hs_list)); \
|
||||
} \
|
||||
_hs_insize *= 2; \
|
||||
} \
|
||||
HASH_FSCK(hh,head); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/* This function selects items from one hash into another hash.
|
||||
* The end result is that the selected items have dual presence
|
||||
* in both hashes. There is no copy of the items made; rather
|
||||
* they are added into the new hash through a secondary hash
|
||||
* hash handle that must be present in the structure. */
|
||||
#define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \
|
||||
do { \
|
||||
unsigned _src_bkt, _dst_bkt; \
|
||||
void *_last_elt=NULL, *_elt; \
|
||||
UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL; \
|
||||
ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst)); \
|
||||
if (src) { \
|
||||
for(_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) { \
|
||||
for(_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \
|
||||
_src_hh; \
|
||||
_src_hh = _src_hh->hh_next) { \
|
||||
_elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \
|
||||
if (cond(_elt)) { \
|
||||
_dst_hh = (UT_hash_handle*)(((char*)_elt) + _dst_hho); \
|
||||
_dst_hh->key = _src_hh->key; \
|
||||
_dst_hh->keylen = _src_hh->keylen; \
|
||||
_dst_hh->hashv = _src_hh->hashv; \
|
||||
_dst_hh->prev = _last_elt; \
|
||||
_dst_hh->next = NULL; \
|
||||
if (_last_elt_hh) { _last_elt_hh->next = _elt; } \
|
||||
if (!dst) { \
|
||||
DECLTYPE_ASSIGN(dst,_elt); \
|
||||
HASH_MAKE_TABLE(hh_dst,dst); \
|
||||
} else { \
|
||||
_dst_hh->tbl = (dst)->hh_dst.tbl; \
|
||||
} \
|
||||
HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt); \
|
||||
HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt],_dst_hh); \
|
||||
(dst)->hh_dst.tbl->num_items++; \
|
||||
_last_elt = _elt; \
|
||||
_last_elt_hh = _dst_hh; \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
HASH_FSCK(hh_dst,dst); \
|
||||
} while (0)
|
||||
|
||||
#define HASH_CLEAR(hh,head) \
|
||||
do { \
|
||||
if (head) { \
|
||||
uthash_free((head)->hh.tbl->buckets, \
|
||||
(head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket)); \
|
||||
HASH_BLOOM_FREE((head)->hh.tbl); \
|
||||
uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \
|
||||
(head)=NULL; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define HASH_OVERHEAD(hh,head) \
|
||||
(size_t)((((head)->hh.tbl->num_items * sizeof(UT_hash_handle)) + \
|
||||
((head)->hh.tbl->num_buckets * sizeof(UT_hash_bucket)) + \
|
||||
(sizeof(UT_hash_table)) + \
|
||||
(HASH_BLOOM_BYTELEN)))
|
||||
|
||||
#ifdef NO_DECLTYPE
|
||||
#define HASH_ITER(hh,head,el,tmp) \
|
||||
for((el)=(head), (*(char**)(&(tmp)))=(char*)((head)?(head)->hh.next:NULL); \
|
||||
el; (el)=(tmp),(*(char**)(&(tmp)))=(char*)((tmp)?(tmp)->hh.next:NULL))
|
||||
#else
|
||||
#define HASH_ITER(hh,head,el,tmp) \
|
||||
for((el)=(head),(tmp)=DECLTYPE(el)((head)?(head)->hh.next:NULL); \
|
||||
el; (el)=(tmp),(tmp)=DECLTYPE(el)((tmp)?(tmp)->hh.next:NULL))
|
||||
#endif
|
||||
|
||||
/* obtain a count of items in the hash */
|
||||
#define HASH_COUNT(head) HASH_CNT(hh,head)
|
||||
#define HASH_CNT(hh,head) ((head)?((head)->hh.tbl->num_items):0)
|
||||
|
||||
typedef struct UT_hash_bucket {
|
||||
struct UT_hash_handle *hh_head;
|
||||
unsigned count;
|
||||
|
||||
/* expand_mult is normally set to 0. In this situation, the max chain length
|
||||
* threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If
|
||||
* the bucket's chain exceeds this length, bucket expansion is triggered).
|
||||
* However, setting expand_mult to a non-zero value delays bucket expansion
|
||||
* (that would be triggered by additions to this particular bucket)
|
||||
* until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH.
|
||||
* (The multiplier is simply expand_mult+1). The whole idea of this
|
||||
* multiplier is to reduce bucket expansions, since they are expensive, in
|
||||
* situations where we know that a particular bucket tends to be overused.
|
||||
* It is better to let its chain length grow to a longer yet-still-bounded
|
||||
* value, than to do an O(n) bucket expansion too often.
|
||||
*/
|
||||
unsigned expand_mult;
|
||||
|
||||
} UT_hash_bucket;
|
||||
|
||||
/* random signature used only to find hash tables in external analysis */
|
||||
#define HASH_SIGNATURE 0xa0111fe1
|
||||
#define HASH_BLOOM_SIGNATURE 0xb12220f2
|
||||
|
||||
typedef struct UT_hash_table {
|
||||
UT_hash_bucket *buckets;
|
||||
unsigned num_buckets, log2_num_buckets;
|
||||
unsigned num_items;
|
||||
struct UT_hash_handle *tail; /* tail hh in app order, for fast append */
|
||||
ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */
|
||||
|
||||
/* in an ideal situation (all buckets used equally), no bucket would have
|
||||
* more than ceil(#items/#buckets) items. that's the ideal chain length. */
|
||||
unsigned ideal_chain_maxlen;
|
||||
|
||||
/* nonideal_items is the number of items in the hash whose chain position
|
||||
* exceeds the ideal chain maxlen. these items pay the penalty for an uneven
|
||||
* hash distribution; reaching them in a chain traversal takes >ideal steps */
|
||||
unsigned nonideal_items;
|
||||
|
||||
/* ineffective expands occur when a bucket doubling was performed, but
|
||||
* afterward, more than half the items in the hash had nonideal chain
|
||||
* positions. If this happens on two consecutive expansions we inhibit any
|
||||
* further expansion, as it's not helping; this happens when the hash
|
||||
* function isn't a good fit for the key domain. When expansion is inhibited
|
||||
* the hash will still work, albeit no longer in constant time. */
|
||||
unsigned ineff_expands, noexpand;
|
||||
|
||||
uint32_t signature; /* used only to find hash tables in external analysis */
|
||||
#ifdef HASH_BLOOM
|
||||
uint32_t bloom_sig; /* used only to test bloom exists in external analysis */
|
||||
uint8_t *bloom_bv;
|
||||
char bloom_nbits;
|
||||
#endif
|
||||
|
||||
} UT_hash_table;
|
||||
|
||||
typedef struct UT_hash_handle {
|
||||
struct UT_hash_table *tbl;
|
||||
void *prev; /* prev element in app order */
|
||||
void *next; /* next element in app order */
|
||||
struct UT_hash_handle *hh_prev; /* previous hh in bucket order */
|
||||
struct UT_hash_handle *hh_next; /* next hh in bucket order */
|
||||
void *key; /* ptr to enclosing struct's key */
|
||||
unsigned keylen; /* enclosing struct's key len */
|
||||
unsigned hashv; /* result of hash-fcn(key) */
|
||||
} UT_hash_handle;
|
||||
|
||||
/// @endcond
|
||||
#endif /* UTHASH_H */
|
||||
|
||||
730
cocos2d-x/cocos/base/utlist.h
Normal file
730
cocos2d-x/cocos/base/utlist.h
Normal file
@@ -0,0 +1,730 @@
|
||||
/*
|
||||
Copyright (c) 2007-2013, Troy D. Hanson http://troydhanson.github.com/uthash/
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef UTLIST_H
|
||||
#define UTLIST_H
|
||||
/// @cond DO_NOT_SHOW
|
||||
|
||||
#define UTLIST_VERSION 1.9.8
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
/*
|
||||
* This file contains macros to manipulate singly and doubly-linked lists.
|
||||
*
|
||||
* 1. LL_ macros: singly-linked lists.
|
||||
* 2. DL_ macros: doubly-linked lists.
|
||||
* 3. CDL_ macros: circular doubly-linked lists.
|
||||
*
|
||||
* To use singly-linked lists, your structure must have a "next" pointer.
|
||||
* To use doubly-linked lists, your structure must "prev" and "next" pointers.
|
||||
* Either way, the pointer to the head of the list must be initialized to NULL.
|
||||
*
|
||||
* ----------------.EXAMPLE -------------------------
|
||||
* struct item {
|
||||
* int id;
|
||||
* struct item *prev, *next;
|
||||
* }
|
||||
*
|
||||
* struct item *list = NULL:
|
||||
*
|
||||
* int main() {
|
||||
* struct item *item;
|
||||
* ... allocate and populate item ...
|
||||
* DL_APPEND(list, item);
|
||||
* }
|
||||
* --------------------------------------------------
|
||||
*
|
||||
* For doubly-linked lists, the append and delete macros are O(1)
|
||||
* For singly-linked lists, append and delete are O(n) but prepend is O(1)
|
||||
* The sort macro is O(n log(n)) for all types of single/double/circular lists.
|
||||
*/
|
||||
|
||||
/* These macros use decltype or the earlier __typeof GNU extension.
|
||||
As decltype is only available in newer compilers (VS2010 or gcc 4.3+
|
||||
when compiling c++ code), this code uses whatever method is needed
|
||||
or, for VS2008 where neither is available, uses casting workarounds. */
|
||||
#ifdef _MSC_VER /* MS compiler */
|
||||
#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */
|
||||
#define LDECLTYPE(x) decltype(x)
|
||||
#else /* VS2008 or older (or VS2010 in C mode) */
|
||||
#define NO_DECLTYPE
|
||||
#define LDECLTYPE(x) char*
|
||||
#endif
|
||||
#else /* GNU, Sun and other compilers */
|
||||
#define LDECLTYPE(x) __typeof(x)
|
||||
#endif
|
||||
|
||||
/* for VS2008 we use some workarounds to get around the lack of decltype,
|
||||
* namely, we always reassign our tmp variable to the list head if we need
|
||||
* to dereference its prev/next pointers, and save/restore the real head.*/
|
||||
#ifdef NO_DECLTYPE
|
||||
#define _SV(elt,list) _tmp = (char*)(list); {char **_alias = (char**)&(list); *_alias = (elt); }
|
||||
#define _NEXT(elt,list,next) ((char*)((list)->next))
|
||||
#define _NEXTASGN(elt,list,to,next) { char **_alias = (char**)&((list)->next); *_alias=(char*)(to); }
|
||||
/* #define _PREV(elt,list,prev) ((char*)((list)->prev)) */
|
||||
#define _PREVASGN(elt,list,to,prev) { char **_alias = (char**)&((list)->prev); *_alias=(char*)(to); }
|
||||
#define _RS(list) { char **_alias = (char**)&(list); *_alias=_tmp; }
|
||||
#define _CASTASGN(a,b) { char **_alias = (char**)&(a); *_alias=(char*)(b); }
|
||||
#else
|
||||
#define _SV(elt,list)
|
||||
#define _NEXT(elt,list,next) ((elt)->next)
|
||||
#define _NEXTASGN(elt,list,to,next) ((elt)->next)=(to)
|
||||
/* #define _PREV(elt,list,prev) ((elt)->prev) */
|
||||
#define _PREVASGN(elt,list,to,prev) ((elt)->prev)=(to)
|
||||
#define _RS(list)
|
||||
#define _CASTASGN(a,b) (a)=(b)
|
||||
#endif
|
||||
|
||||
/******************************************************************************
|
||||
* The sort macro is an adaptation of Simon Tatham's O(n log(n)) mergesort *
|
||||
* Unwieldy variable names used here to avoid shadowing passed-in variables. *
|
||||
*****************************************************************************/
|
||||
#define LL_SORT(list, cmp) \
|
||||
LL_SORT2(list, cmp, next)
|
||||
|
||||
#define LL_SORT2(list, cmp, next) \
|
||||
do { \
|
||||
LDECLTYPE(list) _ls_p; \
|
||||
LDECLTYPE(list) _ls_q; \
|
||||
LDECLTYPE(list) _ls_e; \
|
||||
LDECLTYPE(list) _ls_tail; \
|
||||
int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \
|
||||
if (list) { \
|
||||
_ls_insize = 1; \
|
||||
_ls_looping = 1; \
|
||||
while (_ls_looping) { \
|
||||
_CASTASGN(_ls_p,list); \
|
||||
list = NULL; \
|
||||
_ls_tail = NULL; \
|
||||
_ls_nmerges = 0; \
|
||||
while (_ls_p) { \
|
||||
_ls_nmerges++; \
|
||||
_ls_q = _ls_p; \
|
||||
_ls_psize = 0; \
|
||||
for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \
|
||||
_ls_psize++; \
|
||||
_SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list,next); _RS(list); \
|
||||
if (!_ls_q) break; \
|
||||
} \
|
||||
_ls_qsize = _ls_insize; \
|
||||
while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \
|
||||
if (_ls_psize == 0) { \
|
||||
_ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \
|
||||
_NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \
|
||||
} else if (_ls_qsize == 0 || !_ls_q) { \
|
||||
_ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \
|
||||
_NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \
|
||||
} else if (cmp(_ls_p,_ls_q) <= 0) { \
|
||||
_ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \
|
||||
_NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \
|
||||
} else { \
|
||||
_ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \
|
||||
_NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \
|
||||
} \
|
||||
if (_ls_tail) { \
|
||||
_SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e,next); _RS(list); \
|
||||
} else { \
|
||||
_CASTASGN(list,_ls_e); \
|
||||
} \
|
||||
_ls_tail = _ls_e; \
|
||||
} \
|
||||
_ls_p = _ls_q; \
|
||||
} \
|
||||
if (_ls_tail) { \
|
||||
_SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,NULL,next); _RS(list); \
|
||||
} \
|
||||
if (_ls_nmerges <= 1) { \
|
||||
_ls_looping=0; \
|
||||
} \
|
||||
_ls_insize *= 2; \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
||||
#define DL_SORT(list, cmp) \
|
||||
DL_SORT2(list, cmp, prev, next)
|
||||
|
||||
#define DL_SORT2(list, cmp, prev, next) \
|
||||
do { \
|
||||
LDECLTYPE(list) _ls_p; \
|
||||
LDECLTYPE(list) _ls_q; \
|
||||
LDECLTYPE(list) _ls_e; \
|
||||
LDECLTYPE(list) _ls_tail; \
|
||||
int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \
|
||||
if (list) { \
|
||||
_ls_insize = 1; \
|
||||
_ls_looping = 1; \
|
||||
while (_ls_looping) { \
|
||||
_CASTASGN(_ls_p,list); \
|
||||
list = NULL; \
|
||||
_ls_tail = NULL; \
|
||||
_ls_nmerges = 0; \
|
||||
while (_ls_p) { \
|
||||
_ls_nmerges++; \
|
||||
_ls_q = _ls_p; \
|
||||
_ls_psize = 0; \
|
||||
for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \
|
||||
_ls_psize++; \
|
||||
_SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list,next); _RS(list); \
|
||||
if (!_ls_q) break; \
|
||||
} \
|
||||
_ls_qsize = _ls_insize; \
|
||||
while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \
|
||||
if (_ls_psize == 0) { \
|
||||
_ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \
|
||||
_NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \
|
||||
} else if (_ls_qsize == 0 || !_ls_q) { \
|
||||
_ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \
|
||||
_NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \
|
||||
} else if (cmp(_ls_p,_ls_q) <= 0) { \
|
||||
_ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \
|
||||
_NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \
|
||||
} else { \
|
||||
_ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \
|
||||
_NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \
|
||||
} \
|
||||
if (_ls_tail) { \
|
||||
_SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e,next); _RS(list); \
|
||||
} else { \
|
||||
_CASTASGN(list,_ls_e); \
|
||||
} \
|
||||
_SV(_ls_e,list); _PREVASGN(_ls_e,list,_ls_tail,prev); _RS(list); \
|
||||
_ls_tail = _ls_e; \
|
||||
} \
|
||||
_ls_p = _ls_q; \
|
||||
} \
|
||||
_CASTASGN(list->prev, _ls_tail); \
|
||||
_SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,NULL,next); _RS(list); \
|
||||
if (_ls_nmerges <= 1) { \
|
||||
_ls_looping=0; \
|
||||
} \
|
||||
_ls_insize *= 2; \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define CDL_SORT(list, cmp) \
|
||||
CDL_SORT2(list, cmp, prev, next)
|
||||
|
||||
#define CDL_SORT2(list, cmp, prev, next) \
|
||||
do { \
|
||||
LDECLTYPE(list) _ls_p; \
|
||||
LDECLTYPE(list) _ls_q; \
|
||||
LDECLTYPE(list) _ls_e; \
|
||||
LDECLTYPE(list) _ls_tail; \
|
||||
LDECLTYPE(list) _ls_oldhead; \
|
||||
LDECLTYPE(list) _tmp; \
|
||||
int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \
|
||||
if (list) { \
|
||||
_ls_insize = 1; \
|
||||
_ls_looping = 1; \
|
||||
while (_ls_looping) { \
|
||||
_CASTASGN(_ls_p,list); \
|
||||
_CASTASGN(_ls_oldhead,list); \
|
||||
list = NULL; \
|
||||
_ls_tail = NULL; \
|
||||
_ls_nmerges = 0; \
|
||||
while (_ls_p) { \
|
||||
_ls_nmerges++; \
|
||||
_ls_q = _ls_p; \
|
||||
_ls_psize = 0; \
|
||||
for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \
|
||||
_ls_psize++; \
|
||||
_SV(_ls_q,list); \
|
||||
if (_NEXT(_ls_q,list,next) == _ls_oldhead) { \
|
||||
_ls_q = NULL; \
|
||||
} else { \
|
||||
_ls_q = _NEXT(_ls_q,list,next); \
|
||||
} \
|
||||
_RS(list); \
|
||||
if (!_ls_q) break; \
|
||||
} \
|
||||
_ls_qsize = _ls_insize; \
|
||||
while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \
|
||||
if (_ls_psize == 0) { \
|
||||
_ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \
|
||||
_NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \
|
||||
if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \
|
||||
} else if (_ls_qsize == 0 || !_ls_q) { \
|
||||
_ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \
|
||||
_NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \
|
||||
if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \
|
||||
} else if (cmp(_ls_p,_ls_q) <= 0) { \
|
||||
_ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \
|
||||
_NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \
|
||||
if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \
|
||||
} else { \
|
||||
_ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \
|
||||
_NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \
|
||||
if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \
|
||||
} \
|
||||
if (_ls_tail) { \
|
||||
_SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e,next); _RS(list); \
|
||||
} else { \
|
||||
_CASTASGN(list,_ls_e); \
|
||||
} \
|
||||
_SV(_ls_e,list); _PREVASGN(_ls_e,list,_ls_tail,prev); _RS(list); \
|
||||
_ls_tail = _ls_e; \
|
||||
} \
|
||||
_ls_p = _ls_q; \
|
||||
} \
|
||||
_CASTASGN(list->prev,_ls_tail); \
|
||||
_CASTASGN(_tmp,list); \
|
||||
_SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_tmp,next); _RS(list); \
|
||||
if (_ls_nmerges <= 1) { \
|
||||
_ls_looping=0; \
|
||||
} \
|
||||
_ls_insize *= 2; \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/******************************************************************************
|
||||
* singly linked list macros (non-circular) *
|
||||
*****************************************************************************/
|
||||
#define LL_PREPEND(head,add) \
|
||||
LL_PREPEND2(head,add,next)
|
||||
|
||||
#define LL_PREPEND2(head,add,next) \
|
||||
do { \
|
||||
(add)->next = head; \
|
||||
head = add; \
|
||||
} while (0)
|
||||
|
||||
#define LL_CONCAT(head1,head2) \
|
||||
LL_CONCAT2(head1,head2,next)
|
||||
|
||||
#define LL_CONCAT2(head1,head2,next) \
|
||||
do { \
|
||||
LDECLTYPE(head1) _tmp; \
|
||||
if (head1) { \
|
||||
_tmp = head1; \
|
||||
while (_tmp->next) { _tmp = _tmp->next; } \
|
||||
_tmp->next=(head2); \
|
||||
} else { \
|
||||
(head1)=(head2); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define LL_APPEND(head,add) \
|
||||
LL_APPEND2(head,add,next)
|
||||
|
||||
#define LL_APPEND2(head,add,next) \
|
||||
do { \
|
||||
LDECLTYPE(head) _tmp; \
|
||||
(add)->next=NULL; \
|
||||
if (head) { \
|
||||
_tmp = head; \
|
||||
while (_tmp->next) { _tmp = _tmp->next; } \
|
||||
_tmp->next=(add); \
|
||||
} else { \
|
||||
(head)=(add); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define LL_DELETE(head,del) \
|
||||
LL_DELETE2(head,del,next)
|
||||
|
||||
#define LL_DELETE2(head,del,next) \
|
||||
do { \
|
||||
LDECLTYPE(head) _tmp; \
|
||||
if ((head) == (del)) { \
|
||||
(head)=(head)->next; \
|
||||
} else { \
|
||||
_tmp = head; \
|
||||
while (_tmp->next && (_tmp->next != (del))) { \
|
||||
_tmp = _tmp->next; \
|
||||
} \
|
||||
if (_tmp->next) { \
|
||||
_tmp->next = ((del)->next); \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/* Here are VS2008 replacements for LL_APPEND and LL_DELETE */
|
||||
#define LL_APPEND_VS2008(head,add) \
|
||||
LL_APPEND2_VS2008(head,add,next)
|
||||
|
||||
#define LL_APPEND2_VS2008(head,add,next) \
|
||||
do { \
|
||||
if (head) { \
|
||||
(add)->next = head; /* use add->next as a temp variable */ \
|
||||
while ((add)->next->next) { (add)->next = (add)->next->next; } \
|
||||
(add)->next->next=(add); \
|
||||
} else { \
|
||||
(head)=(add); \
|
||||
} \
|
||||
(add)->next=NULL; \
|
||||
} while (0)
|
||||
|
||||
#define LL_DELETE_VS2008(head,del) \
|
||||
LL_DELETE2_VS2008(head,del,next)
|
||||
|
||||
#define LL_DELETE2_VS2008(head,del,next) \
|
||||
do { \
|
||||
if ((head) == (del)) { \
|
||||
(head)=(head)->next; \
|
||||
} else { \
|
||||
char *_tmp = (char*)(head); \
|
||||
while ((head)->next && ((head)->next != (del))) { \
|
||||
head = (head)->next; \
|
||||
} \
|
||||
if ((head)->next) { \
|
||||
(head)->next = ((del)->next); \
|
||||
} \
|
||||
{ \
|
||||
char **_head_alias = (char**)&(head); \
|
||||
*_head_alias = _tmp; \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
#ifdef NO_DECLTYPE
|
||||
#undef LL_APPEND
|
||||
#define LL_APPEND LL_APPEND_VS2008
|
||||
#undef LL_DELETE
|
||||
#define LL_DELETE LL_DELETE_VS2008
|
||||
#undef LL_DELETE2
|
||||
#define LL_DELETE2_VS2008
|
||||
#undef LL_APPEND2
|
||||
#define LL_APPEND2 LL_APPEND2_VS2008
|
||||
#undef LL_CONCAT /* no LL_CONCAT_VS2008 */
|
||||
#undef DL_CONCAT /* no DL_CONCAT_VS2008 */
|
||||
#endif
|
||||
/* end VS2008 replacements */
|
||||
|
||||
#define LL_FOREACH(head,el) \
|
||||
LL_FOREACH2(head,el,next)
|
||||
|
||||
#define LL_FOREACH2(head,el,next) \
|
||||
for(el=head;el;el=(el)->next)
|
||||
|
||||
#define LL_FOREACH_SAFE(head,el,tmp) \
|
||||
LL_FOREACH_SAFE2(head,el,tmp,next)
|
||||
|
||||
#define LL_FOREACH_SAFE2(head,el,tmp,next) \
|
||||
for((el)=(head);(el) && (tmp = (el)->next, 1); (el) = tmp)
|
||||
|
||||
#define LL_SEARCH_SCALAR(head,out,field,val) \
|
||||
LL_SEARCH_SCALAR2(head,out,field,val,next)
|
||||
|
||||
#define LL_SEARCH_SCALAR2(head,out,field,val,next) \
|
||||
do { \
|
||||
LL_FOREACH2(head,out,next) { \
|
||||
if ((out)->field == (val)) break; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define LL_SEARCH(head,out,elt,cmp) \
|
||||
LL_SEARCH2(head,out,elt,cmp,next)
|
||||
|
||||
#define LL_SEARCH2(head,out,elt,cmp,next) \
|
||||
do { \
|
||||
LL_FOREACH2(head,out,next) { \
|
||||
if ((cmp(out,elt))==0) break; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define LL_REPLACE_ELEM(head, el, add) \
|
||||
do { \
|
||||
LDECLTYPE(head) _tmp; \
|
||||
assert(head != NULL); \
|
||||
assert(el != NULL); \
|
||||
assert(add != NULL); \
|
||||
(add)->next = (el)->next; \
|
||||
if ((head) == (el)) { \
|
||||
(head) = (add); \
|
||||
} else { \
|
||||
_tmp = head; \
|
||||
while (_tmp->next && (_tmp->next != (el))) { \
|
||||
_tmp = _tmp->next; \
|
||||
} \
|
||||
if (_tmp->next) { \
|
||||
_tmp->next = (add); \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define LL_PREPEND_ELEM(head, el, add) \
|
||||
do { \
|
||||
LDECLTYPE(head) _tmp; \
|
||||
assert(head != NULL); \
|
||||
assert(el != NULL); \
|
||||
assert(add != NULL); \
|
||||
(add)->next = (el); \
|
||||
if ((head) == (el)) { \
|
||||
(head) = (add); \
|
||||
} else { \
|
||||
_tmp = head; \
|
||||
while (_tmp->next && (_tmp->next != (el))) { \
|
||||
_tmp = _tmp->next; \
|
||||
} \
|
||||
if (_tmp->next) { \
|
||||
_tmp->next = (add); \
|
||||
} \
|
||||
} \
|
||||
} while (0) \
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* doubly linked list macros (non-circular) *
|
||||
*****************************************************************************/
|
||||
#define DL_PREPEND(head,add) \
|
||||
DL_PREPEND2(head,add,prev,next)
|
||||
|
||||
#define DL_PREPEND2(head,add,prev,next) \
|
||||
do { \
|
||||
(add)->next = head; \
|
||||
if (head) { \
|
||||
(add)->prev = (head)->prev; \
|
||||
(head)->prev = (add); \
|
||||
} else { \
|
||||
(add)->prev = (add); \
|
||||
} \
|
||||
(head) = (add); \
|
||||
} while (0)
|
||||
|
||||
#define DL_APPEND(head,add) \
|
||||
DL_APPEND2(head,add,prev,next)
|
||||
|
||||
#define DL_APPEND2(head,add,prev,next) \
|
||||
do { \
|
||||
if (head) { \
|
||||
(add)->prev = (head)->prev; \
|
||||
(head)->prev->next = (add); \
|
||||
(head)->prev = (add); \
|
||||
(add)->next = NULL; \
|
||||
} else { \
|
||||
(head)=(add); \
|
||||
(head)->prev = (head); \
|
||||
(head)->next = NULL; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define DL_CONCAT(head1,head2) \
|
||||
DL_CONCAT2(head1,head2,prev,next)
|
||||
|
||||
#define DL_CONCAT2(head1,head2,prev,next) \
|
||||
do { \
|
||||
LDECLTYPE(head1) _tmp; \
|
||||
if (head2) { \
|
||||
if (head1) { \
|
||||
_tmp = (head2)->prev; \
|
||||
(head2)->prev = (head1)->prev; \
|
||||
(head1)->prev->next = (head2); \
|
||||
(head1)->prev = _tmp; \
|
||||
} else { \
|
||||
(head1)=(head2); \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define DL_DELETE(head,del) \
|
||||
DL_DELETE2(head,del,prev,next)
|
||||
|
||||
#define DL_DELETE2(head,del,prev,next) \
|
||||
do { \
|
||||
assert((del)->prev != NULL); \
|
||||
if ((del)->prev == (del)) { \
|
||||
(head)=NULL; \
|
||||
} else if ((del)==(head)) { \
|
||||
(del)->next->prev = (del)->prev; \
|
||||
(head) = (del)->next; \
|
||||
} else { \
|
||||
(del)->prev->next = (del)->next; \
|
||||
if ((del)->next) { \
|
||||
(del)->next->prev = (del)->prev; \
|
||||
} else { \
|
||||
(head)->prev = (del)->prev; \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
||||
#define DL_FOREACH(head,el) \
|
||||
DL_FOREACH2(head,el,next)
|
||||
|
||||
#define DL_FOREACH2(head,el,next) \
|
||||
for(el=head;el;el=(el)->next)
|
||||
|
||||
/* this version is safe for deleting the elements during iteration */
|
||||
#define DL_FOREACH_SAFE(head,el,tmp) \
|
||||
DL_FOREACH_SAFE2(head,el,tmp,next)
|
||||
|
||||
#define DL_FOREACH_SAFE2(head,el,tmp,next) \
|
||||
for((el)=(head);(el) && (tmp = (el)->next, 1); (el) = tmp)
|
||||
|
||||
/* these are identical to their singly-linked list counterparts */
|
||||
#define DL_SEARCH_SCALAR LL_SEARCH_SCALAR
|
||||
#define DL_SEARCH LL_SEARCH
|
||||
#define DL_SEARCH_SCALAR2 LL_SEARCH_SCALAR2
|
||||
#define DL_SEARCH2 LL_SEARCH2
|
||||
|
||||
#define DL_REPLACE_ELEM(head, el, add) \
|
||||
do { \
|
||||
assert(head != NULL); \
|
||||
assert(el != NULL); \
|
||||
assert(add != NULL); \
|
||||
if ((head) == (el)) { \
|
||||
(head) = (add); \
|
||||
(add)->next = (el)->next; \
|
||||
if ((el)->next == NULL) { \
|
||||
(add)->prev = (add); \
|
||||
} else { \
|
||||
(add)->prev = (el)->prev; \
|
||||
(add)->next->prev = (add); \
|
||||
} \
|
||||
} else { \
|
||||
(add)->next = (el)->next; \
|
||||
(add)->prev = (el)->prev; \
|
||||
(add)->prev->next = (add); \
|
||||
if ((el)->next == NULL) { \
|
||||
(head)->prev = (add); \
|
||||
} else { \
|
||||
(add)->next->prev = (add); \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define DL_PREPEND_ELEM(head, el, add) \
|
||||
do { \
|
||||
assert(head != NULL); \
|
||||
assert(el != NULL); \
|
||||
assert(add != NULL); \
|
||||
(add)->next = (el); \
|
||||
(add)->prev = (el)->prev; \
|
||||
(el)->prev = (add); \
|
||||
if ((head) == (el)) { \
|
||||
(head) = (add); \
|
||||
} else { \
|
||||
(add)->prev->next = (add); \
|
||||
} \
|
||||
} while (0) \
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* circular doubly linked list macros *
|
||||
*****************************************************************************/
|
||||
#define CDL_PREPEND(head,add) \
|
||||
CDL_PREPEND2(head,add,prev,next)
|
||||
|
||||
#define CDL_PREPEND2(head,add,prev,next) \
|
||||
do { \
|
||||
if (head) { \
|
||||
(add)->prev = (head)->prev; \
|
||||
(add)->next = (head); \
|
||||
(head)->prev = (add); \
|
||||
(add)->prev->next = (add); \
|
||||
} else { \
|
||||
(add)->prev = (add); \
|
||||
(add)->next = (add); \
|
||||
} \
|
||||
(head)=(add); \
|
||||
} while (0)
|
||||
|
||||
#define CDL_DELETE(head,del) \
|
||||
CDL_DELETE2(head,del,prev,next)
|
||||
|
||||
#define CDL_DELETE2(head,del,prev,next) \
|
||||
do { \
|
||||
if ( ((head)==(del)) && ((head)->next == (head))) { \
|
||||
(head) = 0L; \
|
||||
} else { \
|
||||
(del)->next->prev = (del)->prev; \
|
||||
(del)->prev->next = (del)->next; \
|
||||
if ((del) == (head)) (head)=(del)->next; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define CDL_FOREACH(head,el) \
|
||||
CDL_FOREACH2(head,el,next)
|
||||
|
||||
#define CDL_FOREACH2(head,el,next) \
|
||||
for(el=head;el;el=((el)->next==head ? 0L : (el)->next))
|
||||
|
||||
#define CDL_FOREACH_SAFE(head,el,tmp1,tmp2) \
|
||||
CDL_FOREACH_SAFE2(head,el,tmp1,tmp2,prev,next)
|
||||
|
||||
#define CDL_FOREACH_SAFE2(head,el,tmp1,tmp2,prev,next) \
|
||||
for((el)=(head), ((tmp1)=(head)?((head)->prev):NULL); \
|
||||
(el) && ((tmp2)=(el)->next, 1); \
|
||||
((el) = (((el)==(tmp1)) ? 0L : (tmp2))))
|
||||
|
||||
#define CDL_SEARCH_SCALAR(head,out,field,val) \
|
||||
CDL_SEARCH_SCALAR2(head,out,field,val,next)
|
||||
|
||||
#define CDL_SEARCH_SCALAR2(head,out,field,val,next) \
|
||||
do { \
|
||||
CDL_FOREACH2(head,out,next) { \
|
||||
if ((out)->field == (val)) break; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define CDL_SEARCH(head,out,elt,cmp) \
|
||||
CDL_SEARCH2(head,out,elt,cmp,next)
|
||||
|
||||
#define CDL_SEARCH2(head,out,elt,cmp,next) \
|
||||
do { \
|
||||
CDL_FOREACH2(head,out,next) { \
|
||||
if ((cmp(out,elt))==0) break; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define CDL_REPLACE_ELEM(head, el, add) \
|
||||
do { \
|
||||
assert(head != NULL); \
|
||||
assert(el != NULL); \
|
||||
assert(add != NULL); \
|
||||
if ((el)->next == (el)) { \
|
||||
(add)->next = (add); \
|
||||
(add)->prev = (add); \
|
||||
(head) = (add); \
|
||||
} else { \
|
||||
(add)->next = (el)->next; \
|
||||
(add)->prev = (el)->prev; \
|
||||
(add)->next->prev = (add); \
|
||||
(add)->prev->next = (add); \
|
||||
if ((head) == (el)) { \
|
||||
(head) = (add); \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define CDL_PREPEND_ELEM(head, el, add) \
|
||||
do { \
|
||||
assert(head != NULL); \
|
||||
assert(el != NULL); \
|
||||
assert(add != NULL); \
|
||||
(add)->next = (el); \
|
||||
(add)->prev = (el)->prev; \
|
||||
(el)->prev = (add); \
|
||||
(add)->prev->next = (add); \
|
||||
if ((head) == (el)) { \
|
||||
(head) = (add); \
|
||||
} \
|
||||
} while (0) \
|
||||
|
||||
/// @endcond
|
||||
#endif /* UTLIST_H */
|
||||
|
||||
Reference in New Issue
Block a user