初始化

This commit is contained in:
SmallMain
2022-06-25 00:23:03 +08:00
commit ef0589e8e5
2264 changed files with 617829 additions and 0 deletions

View 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

View 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__

View 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

View 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__

View 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

View 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__

View 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

View 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

View 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);
}
}

View 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

View 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
/// @}

View 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

View 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__

View 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__

View 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

View 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

View 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

View 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

View 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 {

View 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 {

View 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

View 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__) */

View 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

View 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

View 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__

View 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

View 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__

View 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

View 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__

View 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

View 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

View 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__

View 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

View 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"

View 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_

View 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

View 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__

View 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

View 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__) */

View 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

View 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

View 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

View 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

View 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
View 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

View 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);
}

View 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

View 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)
*****************************************************************************/

View 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__

View 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 */

View 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 */