no message

This commit is contained in:
gem
2025-02-18 15:21:31 +08:00
commit 2d133e56d7
1980 changed files with 465595 additions and 0 deletions

View File

@@ -0,0 +1,68 @@
/****************************************************************************
Copyright (c) 2010-2012 cocos2d-x.org
Copyright (c) 2011 Zynga Inc.
Copyright (c) 2013-2016 Chukong Technologies Inc.
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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 <memory>
#include "base/std/container/string.h"
#include "base/Macros.h"
#include "platform/FileUtils.h"
namespace cc {
//! @brief Helper class to handle file operations
class CC_DLL FileUtilsApple : public FileUtils {
public:
FileUtilsApple();
~FileUtilsApple() override = default;
/* override functions */
ccstd::string getWritablePath() const override;
ccstd::string getFullPathForDirectoryAndFilename(const ccstd::string &directory, const ccstd::string &filename) const override;
ValueMap getValueMapFromFile(const ccstd::string &filename) override;
ValueMap getValueMapFromData(const char *filedata, int filesize) override;
bool writeToFile(const ValueMap &dict, const ccstd::string &fullPath) override;
ValueVector getValueVectorFromFile(const ccstd::string &filename) override;
#if CC_FILEUTILS_APPLE_ENABLE_OBJC
void setBundle(NSBundle *bundle);
#endif
bool createDirectory(const ccstd::string &path) override;
private:
bool isFileExistInternal(const ccstd::string &filePath) const override;
bool removeDirectory(const ccstd::string &dirPath) override;
void valueMapCompact(ValueMap &valueMap) override;
void valueVectorCompact(ValueVector &valueVector) override;
struct IMPL;
std::unique_ptr<IMPL> pimpl_;
};
} // namespace cc

View File

@@ -0,0 +1,438 @@
/****************************************************************************
Copyright (c) 2010-2012 cc-x.org
Copyright (c) 2011 Zynga Inc.
Copyright (c) 2013-2016 Chukong Technologies Inc.
Copyright (c) 2017-2022 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
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.
****************************************************************************/
#import <Foundation/Foundation.h>
#include "platform/apple/FileUtils-apple.h"
#include <ftw.h>
#include <stack>
#include "base/Log.h"
#include "base/memory/Memory.h"
#include "base/std/container/string.h"
#include "platform/FileUtils.h"
#include "platform/SAXParser.h"
namespace cc {
struct FileUtilsApple::IMPL {
IMPL(NSBundle *bundle) : bundle_([NSBundle mainBundle]) {}
void setBundle(NSBundle *bundle) {
bundle_ = bundle;
}
NSBundle *getBundle() const {
return bundle_;
}
private:
NSBundle *bundle_;
};
static id convertCCValueToNSObject(const cc::Value &value);
static cc::Value convertNSObjectToCCValue(id object);
static void addNSObjectToCCMap(id nsKey, id nsValue, ValueMap &dict);
static void addCCValueToNSDictionary(const ccstd::string &key, const Value &value, NSMutableDictionary *dict);
static void addNSObjectToCCVector(id item, ValueVector &array);
static void addCCValueToNSArray(const Value &value, NSMutableArray *array);
static id convertCCValueToNSObject(const cc::Value &value) {
switch (value.getType()) {
case Value::Type::NONE:
return [NSNull null];
case Value::Type::STRING:
return [NSString stringWithCString:value.asString().c_str() encoding:NSUTF8StringEncoding];
case Value::Type::BYTE:
return [NSNumber numberWithInt:value.asByte()];
case Value::Type::INTEGER:
return [NSNumber numberWithInt:value.asInt()];
case Value::Type::UNSIGNED:
return [NSNumber numberWithUnsignedInt:value.asUnsignedInt()];
case Value::Type::FLOAT:
return [NSNumber numberWithFloat:value.asFloat()];
case Value::Type::DOUBLE:
return [NSNumber numberWithDouble:value.asDouble()];
case Value::Type::BOOLEAN:
return [NSNumber numberWithBool:value.asBool()];
case Value::Type::VECTOR: {
NSMutableArray *array = [NSMutableArray array];
const ValueVector &vector = value.asValueVector();
for (const auto &e : vector) {
addCCValueToNSArray(e, array);
}
return array;
}
case Value::Type::MAP: {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
const ValueMap &map = value.asValueMap();
for (auto iter = map.begin(); iter != map.end(); ++iter) {
addCCValueToNSDictionary(iter->first, iter->second, dictionary);
}
return dictionary;
}
case Value::Type::INT_KEY_MAP:
break;
}
return [NSNull null];
}
static cc::Value convertNSObjectToCCValue(id item) {
// add string value into array
if ([item isKindOfClass:[NSString class]]) {
return Value([item UTF8String]);
}
// add number value into array(such as int, float, bool and so on)
// the value is a number
if ([item isKindOfClass:[NSNumber class]]) {
NSNumber *num = item;
const char *numType = [num objCType];
if (num == (void *)kCFBooleanFalse || num == (void *)kCFBooleanTrue) {
bool v = [num boolValue];
return Value(v);
} else if (strcmp(numType, @encode(float)) == 0) {
return Value([num floatValue]);
} else if (strcmp(numType, @encode(double)) == 0) {
return Value([num doubleValue]);
} else {
return Value([num intValue]);
}
}
// add dictionary value into array
if ([item isKindOfClass:[NSDictionary class]]) {
ValueMap dict;
for (id subKey in [item allKeys]) {
id subValue = [item objectForKey:subKey];
addNSObjectToCCMap(subKey, subValue, dict);
}
return Value(dict);
}
// add array value into array
if ([item isKindOfClass:[NSArray class]]) {
ValueVector subArray;
for (id subItem in item) {
addNSObjectToCCVector(subItem, subArray);
}
return Value(subArray);
}
return Value::VALUE_NULL;
}
static void addNSObjectToCCVector(id item, ValueVector &array) {
array.push_back(convertNSObjectToCCValue(item));
}
static void addCCValueToNSArray(const Value &value, NSMutableArray *array) {
[array addObject:convertCCValueToNSObject(value)];
}
static void addNSObjectToCCMap(id nsKey, id nsValue, ValueMap &dict) {
// the key must be a string
CC_ASSERT([nsKey isKindOfClass:[NSString class]]);
ccstd::string key = [nsKey UTF8String];
dict[key] = convertNSObjectToCCValue(nsValue);
}
static void addCCValueToNSDictionary(const ccstd::string &key, const Value &value, NSMutableDictionary *dict) {
NSString *NSkey = [NSString stringWithCString:key.c_str() encoding:NSUTF8StringEncoding];
[dict setObject:convertCCValueToNSObject(value) forKey:NSkey];
}
FileUtils *createFileUtils() {
// TODO(qgh):In the simulator, it will be called twice. So the judgment here is to prevent memory leaks.
// But this is equivalent to using a singleton pattern,
// which is not consistent with the current design and will be optimized later.
if (!FileUtils::getInstance()) {
return ccnew FileUtilsApple();
}
return FileUtils::getInstance();
}
FileUtilsApple::FileUtilsApple() : pimpl_(ccnew IMPL([NSBundle mainBundle])) {
init();
}
#if CC_FILEUTILS_APPLE_ENABLE_OBJC
void FileUtilsApple::setBundle(NSBundle *bundle) {
pimpl_->setBundle(bundle);
}
#endif
#pragma mark - FileUtils
static NSFileManager *s_fileManager = [NSFileManager defaultManager];
ccstd::string FileUtilsApple::getWritablePath() const {
if (_writablePath.length()) {
return _writablePath;
}
// save to document folder
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
ccstd::string strRet = [documentsDirectory UTF8String];
strRet.append("/");
return strRet;
}
bool FileUtilsApple::isFileExistInternal(const ccstd::string &filePath) const {
if (filePath.empty()) {
return false;
}
bool ret = false;
if (filePath[0] != '/') {
ccstd::string path;
ccstd::string file;
size_t pos = filePath.find_last_of("/");
if (pos != ccstd::string::npos) {
file = filePath.substr(pos + 1);
path = filePath.substr(0, pos + 1);
} else {
file = filePath;
}
NSString *fullpath = [pimpl_->getBundle() pathForResource:[NSString stringWithUTF8String:file.c_str()]
ofType:nil
inDirectory:[NSString stringWithUTF8String:path.c_str()]];
if (fullpath != nil) {
ret = true;
}
} else {
// Search path is an absolute path.
if ([s_fileManager fileExistsAtPath:[NSString stringWithUTF8String:filePath.c_str()]]) {
ret = true;
}
}
return ret;
}
static int unlink_cb(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf) {
auto ret = remove(fpath);
if (ret) {
CC_LOG_INFO("Fail to remove: %s ", fpath);
}
return ret;
}
bool FileUtilsApple::removeDirectory(const ccstd::string &path) {
if (path.empty()) {
CC_LOG_ERROR("Fail to remove directory, path is empty!");
return false;
}
if (nftw(path.c_str(), unlink_cb, 64, FTW_DEPTH | FTW_PHYS))
return false;
else
return true;
}
ccstd::string FileUtilsApple::getFullPathForDirectoryAndFilename(const ccstd::string &directory, const ccstd::string &filename) const {
if (directory[0] != '/') {
NSString *dirStr = [NSString stringWithUTF8String:directory.c_str()];
// The following logic is used for remove the "../" in the directory
// Because method "pathForResource" will return nil if the directory contains "../".
auto theIdx = directory.find("..");
if (theIdx != ccstd::string::npos && theIdx > 0) {
NSMutableArray<NSString *> *pathComps = [NSMutableArray arrayWithArray:[dirStr pathComponents]];
NSUInteger idx = [pathComps indexOfObject:@".."];
while (idx != NSNotFound && idx > 0) { // if found ".." & it's not at the beginning of the string
[pathComps removeObjectAtIndex:idx]; // remove the item ".."
[pathComps removeObjectAtIndex:idx - 1]; // remove the item before ".."
idx = [pathComps indexOfObject:@".."]; // find ".." again
}
dirStr = [NSString pathWithComponents:pathComps];
}
NSString *fullpath = [pimpl_->getBundle() pathForResource:[NSString stringWithUTF8String:filename.c_str()]
ofType:nil
inDirectory:dirStr];
if (fullpath != nil) {
return [fullpath UTF8String];
}
} else {
ccstd::string fullPath = directory + filename;
// Search path is an absolute path.
if ([s_fileManager fileExistsAtPath:[NSString stringWithUTF8String:fullPath.c_str()]]) {
return fullPath;
}
}
return "";
}
ValueMap FileUtilsApple::getValueMapFromFile(const ccstd::string &filename) {
auto d(FileUtils::getInstance()->getDataFromFile(filename));
return getValueMapFromData(reinterpret_cast<char *>(d.getBytes()), static_cast<int>(d.getSize()));
}
ValueMap FileUtilsApple::getValueMapFromData(const char *filedata, int filesize) {
NSData *file = [NSData dataWithBytes:filedata length:filesize];
NSPropertyListFormat format;
NSError *error;
NSDictionary *dict = [NSPropertyListSerialization propertyListWithData:file options:NSPropertyListImmutable format:&format error:&error];
ValueMap ret;
if (dict != nil) {
for (id key in [dict allKeys]) {
id value = [dict objectForKey:key];
addNSObjectToCCMap(key, value, ret);
}
}
return ret;
}
bool FileUtilsApple::writeToFile(const ValueMap &dict, const ccstd::string &fullPath) {
return writeValueMapToFile(dict, fullPath);
}
bool FileUtils::writeValueMapToFile(const ValueMap &dict, const ccstd::string &fullPath) {
valueMapCompact(const_cast<ValueMap &>(dict));
//CC_LOG_DEBUG("iOS||Mac Dictionary %d write to file %s", dict->_ID, fullPath.c_str());
NSMutableDictionary *nsDict = [NSMutableDictionary dictionary];
for (auto iter = dict.begin(); iter != dict.end(); ++iter) {
addCCValueToNSDictionary(iter->first, iter->second, nsDict);
}
NSString *file = [NSString stringWithUTF8String:fullPath.c_str()];
// do it atomically
return [nsDict writeToFile:file atomically:YES];
}
void FileUtilsApple::valueMapCompact(ValueMap &valueMap) {
auto itr = valueMap.begin();
while (itr != valueMap.end()) {
auto vtype = itr->second.getType();
switch (vtype) {
case Value::Type::NONE: {
itr = valueMap.erase(itr);
continue;
} break;
case Value::Type::MAP: {
valueMapCompact(itr->second.asValueMap());
} break;
case Value::Type::VECTOR: {
valueVectorCompact(itr->second.asValueVector());
} break;
default:
break;
}
itr++;
}
}
void FileUtilsApple::valueVectorCompact(ValueVector &valueVector) {
auto itr = valueVector.begin();
while (itr != valueVector.end()) {
auto vtype = (*itr).getType();
switch (vtype) {
case Value::Type::NONE: {
itr = valueVector.erase(itr);
continue;
} break;
case Value::Type::MAP: {
valueMapCompact((*itr).asValueMap());
} break;
case Value::Type::VECTOR: {
valueVectorCompact((*itr).asValueVector());
} break;
default:
break;
}
itr++;
}
}
bool FileUtils::writeValueVectorToFile(const ValueVector &vecData, const ccstd::string &fullPath) {
NSString *path = [NSString stringWithUTF8String:fullPath.c_str()];
NSMutableArray *array = [NSMutableArray array];
for (const auto &e : vecData) {
addCCValueToNSArray(e, array);
}
[array writeToFile:path atomically:YES];
return true;
}
ValueVector FileUtilsApple::getValueVectorFromFile(const ccstd::string &filename) {
// NSString* pPath = [NSString stringWithUTF8String:pFileName];
// NSString* pathExtension= [pPath pathExtension];
// pPath = [pPath stringByDeletingPathExtension];
// pPath = [[NSBundle mainBundle] pathForResource:pPath ofType:pathExtension];
// fixing cannot read data using Array::createWithContentsOfFile
ccstd::string fullPath = fullPathForFilename(filename);
NSString *path = [NSString stringWithUTF8String:fullPath.c_str()];
NSArray *array = [NSArray arrayWithContentsOfFile:path];
ValueVector ret;
for (id value in array) {
addNSObjectToCCVector(value, ret);
}
return ret;
}
bool FileUtilsApple::createDirectory(const ccstd::string &path) {
CC_ASSERT(!path.empty());
if (isDirectoryExist(path))
return true;
NSError *error;
bool result = [s_fileManager createDirectoryAtPath:[NSString stringWithUTF8String:path.c_str()] withIntermediateDirectories:YES attributes:nil error:&error];
if (!result && error != nil) {
CC_LOG_ERROR("Fail to create directory \"%s\": %s", path.c_str(), [error.localizedDescription UTF8String]);
}
return result;
}
} // namespace cc

View File

@@ -0,0 +1,37 @@
/****************************************************************************
Copyright (c) 2018-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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
#import <Foundation/Foundation.h>
typedef void (^ICallback)(NSString*, NSString*);
@interface JsbBridge : NSObject
+ (instancetype)sharedInstance;
- (void)setCallback:(ICallback)cb;
- (bool)callByScript:(NSString*)arg0 arg1:(NSString*)arg1;
- (void)sendToScript:(NSString*)arg0 arg1:(NSString*)arg1;
- (void)sendToScript:(NSString*)arg0;
@end

View File

@@ -0,0 +1,98 @@
/****************************************************************************
Copyright (c) 2018-2022 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
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 "JsbBridge.h"
#import <Foundation/Foundation.h>
#include "base/std/container/string.h"
#include "cocos/bindings/manual/JavaScriptObjCBridge.h"
#include "engine/EngineEvents.h"
bool callPlatformStringMethod(const ccstd::string &arg0, const ccstd::string &arg1) {
NSString *oc_arg0 = [NSString stringWithCString:arg0.c_str() encoding:NSUTF8StringEncoding];
NSString *oc_arg1 = [NSString stringWithCString:arg1.c_str() encoding:NSUTF8StringEncoding];
JsbBridge *m = [JsbBridge sharedInstance];
[m callByScript:oc_arg0 arg1:oc_arg1];
return true;
}
@implementation JsbBridge {
ICallback callback;
cc::events::Close::Listener closeListener;
}
static JsbBridge *instance = nil;
+ (instancetype)sharedInstance {
static dispatch_once_t pred = 0;
dispatch_once(&pred, ^{
instance = [[super allocWithZone:NULL] init];
NSAssert(instance != nil, @"alloc or init failed");
});
return instance;
}
+ (id)allocWithZone:(struct _NSZone *)zone {
return [JsbBridge sharedInstance];
}
- (id)copyWithZone:(struct _NSZone *)zone {
return [JsbBridge sharedInstance];
}
- (id)init {
if (self = [super init]) {
closeListener.bind([&](){
if ([JsbBridge sharedInstance] != nil) {
[[JsbBridge sharedInstance] release];
}
});
}
return self;
}
- (void)setCallback:(ICallback)cb {
callback = cb;
}
- (bool)callByScript:(NSString *)arg0 arg1:(NSString *)arg1 {
if (callback != nil) {
callback(arg0, arg1);
return true;
}
return false;
}
- (void)sendToScript:(NSString *)arg0 arg1:(NSString *)arg1 {
const ccstd::string c_arg0{[arg0 UTF8String]};
const ccstd::string c_arg1{[arg1 UTF8String]};
callScript(c_arg0, c_arg1);
}
- (void)sendToScript:(NSString *)arg0 {
const ccstd::string c_arg0{[arg0 UTF8String]};
callScript(c_arg0, "");
}
@end

View File

@@ -0,0 +1,59 @@
/****************************************************************************
Copyright (c) 2018-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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
#import <Foundation/Foundation.h>
typedef void (^OnScriptEventListener)(NSString*);
@interface JsbBridgeWrapper : NSObject
/**
* Get the instance of JsbBridgetWrapper
*/
+ (instancetype)sharedInstance;
/**
* Add a listener to specified event, if the event does not exist, the wrapper will create one. Concurrent listener will be ignored
*/
- (void)addScriptEventListener:(NSString*)eventName listener:(OnScriptEventListener)listener;
/**
* Remove listener for specified event, concurrent event will be deleted. Return false only if the event does not exist
*/
- (bool)removeScriptEventListener:(NSString*)eventName listener:(OnScriptEventListener)listener;
/**
* Remove all listener for event specified.
*/
- (void)removeAllListenersForEvent:(NSString*)eventName;
/**
* Remove all event registered. Use it carefully!
*/
- (void)removeAllListeners;
/**
* Dispatch the event with argument, the event should be registered in javascript, or other script language in future.
*/
- (void)dispatchEventToScript:(NSString*)eventName arg:(NSString*)arg;
/**
* Dispatch the event which is registered in javascript, or other script language in future.
*/
- (void)dispatchEventToScript:(NSString*)eventName;
@end

View File

@@ -0,0 +1,134 @@
/****************************************************************************
Copyright (c) 2018-2022 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
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 <iostream>
#include "base/std/container/string.h"
#include "JsbBridge.h"
#include "JsbBridgeWrapper.h"
#include "engine/EngineEvents.h"
@implementation JsbBridgeWrapper {
JsbBridge* jb;
NSMutableDictionary<NSString*, NSMutableArray<OnScriptEventListener>*>* cbDictionnary;
cc::events::Close::Listener closeListener;
}
static JsbBridgeWrapper* instance = nil;
static ICallback cb = ^void(NSString* _event, NSString* _arg) {
[[JsbBridgeWrapper sharedInstance] triggerEvent:_event arg:_arg];
};
+ (instancetype)sharedInstance {
static dispatch_once_t pred = 0;
dispatch_once(&pred, ^{
instance = [[super allocWithZone:NULL] init];
NSAssert(instance != nil, @"alloc or init failed");
});
return instance;
}
+ (id)allocWithZone:(struct _NSZone*)zone {
return [JsbBridgeWrapper sharedInstance];
}
- (id)copyWithZone:(struct _NSZone*)zone {
return [JsbBridgeWrapper sharedInstance];
}
- (void)addScriptEventListener:(NSString*)eventName listener:(OnScriptEventListener)listener {
if (![cbDictionnary objectForKey:eventName]) {
NSMutableArray *newArr = [[NSMutableArray<OnScriptEventListener> alloc] init];
[cbDictionnary setValue:newArr forKey:eventName];
[newArr release];
}
NSMutableArray* arr = [cbDictionnary objectForKey:eventName];
if (![arr containsObject:listener]) {
[arr addObject:listener];
}
[listener release];
}
- (void)triggerEvent:(NSString*)eventName arg:(NSString*)arg {
NSMutableArray<OnScriptEventListener>* arr = [cbDictionnary objectForKey:eventName];
if (!arr) {
return;
}
for (OnScriptEventListener listener : arr) {
listener(arg);
}
}
- (void)removeAllListenersForEvent:(NSString*)eventName {
if (![cbDictionnary objectForKey:eventName]) {
return;
}
//same as release all listeners.
[cbDictionnary removeObjectForKey:eventName];
}
- (bool)removeScriptEventListener:(NSString*)eventName listener:(OnScriptEventListener)listener {
NSMutableArray<OnScriptEventListener>* arr = [cbDictionnary objectForKey:eventName];
if (!arr) {
return false;
}
[arr removeObject:listener];
return true;
}
- (void)removeAllListeners {
[cbDictionnary removeAllObjects];
}
- (void)dispatchEventToScript:(NSString*)eventName arg:(NSString*)arg {
[jb sendToScript:eventName arg1:arg];
}
- (void)dispatchEventToScript:(NSString*)eventName {
[jb sendToScript:eventName];
}
- (id)init {
if (self = [super init]) {
cbDictionnary = [NSMutableDictionary new];
if (cbDictionnary == nil) {
[self release];
return nil;
}
jb = [JsbBridge sharedInstance];
if (jb == nil) {
[self release];
return nil;
}
[jb setCallback:cb];
closeListener.bind([&](){
if ([JsbBridgeWrapper sharedInstance] != nil) {
[[JsbBridgeWrapper sharedInstance] release];
}
});
}
return self;
}
- (void)dealloc {
for (NSMutableArray* arr : cbDictionnary) {
[arr release];
}
[cbDictionnary release];
[super dealloc];
}
@end

View File

@@ -0,0 +1,218 @@
/****************************************************************************
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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 "Reachability.h"
#include <SystemConfiguration/SystemConfiguration.h>
#include <arpa/inet.h>
#include <ifaddrs.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include "base/DeferredReleasePool.h"
#include "base/Macros.h"
#include "base/memory/Memory.h"
namespace {
#define ShouldPrintReachabilityFlags 0
static void PrintReachabilityFlags(SCNetworkReachabilityFlags flags, const char *comment) {
#if ShouldPrintReachabilityFlags
printf("Reachability Flag Status: %c%c %c%c%c%c%c%c%c %s\n",
#if CC_PLATFORM == CC_PLATFORM_IOS
(flags & kSCNetworkReachabilityFlagsIsWWAN) ? 'W' : '-',
#else
'-',
#endif
(flags & kSCNetworkReachabilityFlagsReachable) ? 'R' : '-',
(flags & kSCNetworkReachabilityFlagsTransientConnection) ? 't' : '-',
(flags & kSCNetworkReachabilityFlagsConnectionRequired) ? 'c' : '-',
(flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) ? 'C' : '-',
(flags & kSCNetworkReachabilityFlagsInterventionRequired) ? 'i' : '-',
(flags & kSCNetworkReachabilityFlagsConnectionOnDemand) ? 'D' : '-',
(flags & kSCNetworkReachabilityFlagsIsLocalAddress) ? 'l' : '-',
(flags & kSCNetworkReachabilityFlagsIsDirect) ? 'd' : '-',
comment);
#endif
}
cc::Reachability::NetworkStatus getNetworkStatusForFlags(SCNetworkReachabilityFlags flags) {
PrintReachabilityFlags(flags, "networkStatusForFlags");
if ((flags & kSCNetworkReachabilityFlagsReachable) == 0) {
// The target host is not reachable.
return cc::Reachability::NetworkStatus::NOT_REACHABLE;
}
cc::Reachability::NetworkStatus returnValue = cc::Reachability::NetworkStatus::NOT_REACHABLE;
if ((flags & kSCNetworkReachabilityFlagsConnectionRequired) == 0) {
/*
If the target host is reachable and no connection is required then we'll assume (for now) that you're on Wi-Fi...
*/
returnValue = cc::Reachability::NetworkStatus::REACHABLE_VIA_WIFI;
}
if ((((flags & kSCNetworkReachabilityFlagsConnectionOnDemand) != 0) ||
(flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0)) {
/*
... and the connection is on-demand (or on-traffic) if the calling application is using the CFSocketStream or higher APIs...
*/
if ((flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0) {
/*
... and no [user] intervention is needed...
*/
returnValue = cc::Reachability::NetworkStatus::REACHABLE_VIA_WIFI;
}
}
#if CC_PLATFORM == CC_PLATFORM_IOS
if ((flags & kSCNetworkReachabilityFlagsIsWWAN) == kSCNetworkReachabilityFlagsIsWWAN) {
/*
... but WWAN connections are OK if the calling application is using the CFNetwork APIs.
*/
returnValue = cc::Reachability::NetworkStatus::REACHABLE_VIA_WWAN;
}
#endif
return returnValue;
}
} // namespace
namespace cc {
Reachability *Reachability::createWithHostName(const ccstd::string &hostName) {
Reachability *returnValue = nullptr;
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(nullptr, hostName.c_str());
if (reachability != nullptr) {
returnValue = ccnew Reachability();
returnValue->addRef();
if (returnValue != nullptr) {
cc::DeferredReleasePool::add(returnValue);
returnValue->_reachabilityRef = reachability;
} else {
CFRelease(reachability);
}
}
return returnValue;
}
Reachability *Reachability::createWithAddress(const struct sockaddr *hostAddress) {
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, hostAddress);
Reachability *returnValue = nullptr;
if (reachability != nullptr) {
returnValue = ccnew Reachability();
returnValue->addRef();
if (returnValue != nullptr) {
cc::DeferredReleasePool::add(returnValue);
returnValue->_reachabilityRef = reachability;
} else {
CFRelease(reachability);
}
}
return returnValue;
}
Reachability *Reachability::createForInternetConnection() {
struct sockaddr_in zeroAddress;
bzero(&zeroAddress, sizeof(zeroAddress));
zeroAddress.sin_len = sizeof(zeroAddress);
zeroAddress.sin_family = AF_INET;
return createWithAddress((const struct sockaddr *)&zeroAddress);
}
Reachability::Reachability()
: _callback(nullptr),
_userData(nullptr),
_reachabilityRef(nullptr) {
}
Reachability::~Reachability() {
stopNotifier();
if (_reachabilityRef != nullptr) {
CFRelease(_reachabilityRef);
}
}
void Reachability::onReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void *info) {
CC_ASSERT_NOT_NULL(info);
cc::Reachability *thiz = reinterpret_cast<cc::Reachability *>(info);
if (thiz->_callback != nullptr) {
NetworkStatus status = getNetworkStatusForFlags(flags);
thiz->_callback(thiz, status, thiz->_userData);
}
}
bool Reachability::startNotifier(const ReachabilityCallback &cb, void *userData) {
_callback = cb;
_userData = userData;
bool returnValue = false;
SCNetworkReachabilityContext context = {0, this, nullptr, nullptr, nullptr};
if (SCNetworkReachabilitySetCallback(_reachabilityRef, onReachabilityCallback, &context)) {
if (SCNetworkReachabilityScheduleWithRunLoop(_reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode)) {
returnValue = true;
}
}
return returnValue;
}
void Reachability::stopNotifier() {
if (_reachabilityRef != nullptr) {
SCNetworkReachabilityUnscheduleFromRunLoop(_reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
}
}
bool Reachability::isConnectionRequired() const {
CC_ASSERT_NOT_NULL(_reachabilityRef);
SCNetworkReachabilityFlags flags;
if (SCNetworkReachabilityGetFlags(_reachabilityRef, &flags)) {
return (flags & kSCNetworkReachabilityFlagsConnectionRequired);
}
return false;
}
Reachability::NetworkStatus Reachability::getCurrentReachabilityStatus() const {
CC_ASSERT_NOT_NULL(_reachabilityRef);
NetworkStatus returnValue = NetworkStatus::NOT_REACHABLE;
SCNetworkReachabilityFlags flags;
if (SCNetworkReachabilityGetFlags(_reachabilityRef, &flags)) {
returnValue = getNetworkStatusForFlags(flags);
}
return returnValue;
}
} // namespace cc

View File

@@ -0,0 +1,86 @@
/****************************************************************************
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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/RefCounted.h"
#include <SystemConfiguration/SystemConfiguration.h>
#include <functional>
#include "base/std/container/string.h"
struct sockaddr;
namespace cc {
class Reachability final : public RefCounted {
public:
enum class NetworkStatus : uint8_t {
NOT_REACHABLE,
REACHABLE_VIA_WIFI,
REACHABLE_VIA_WWAN
};
/*!
* Use to check the reachability of a given host name.
*/
static Reachability *createWithHostName(const ccstd::string &hostName);
/*!
* Use to check the reachability of a given IP address.
*/
static Reachability *createWithAddress(const struct sockaddr *hostAddress);
/*!
* Checks whether the default route is available. Should be used by applications that do not connect to a particular host.
*/
static Reachability *createForInternetConnection();
using ReachabilityCallback = std::function<void(Reachability *, NetworkStatus, void *)>;
/*!
* Start listening for reachability notifications on the current run loop.
*/
bool startNotifier(const ReachabilityCallback &cb, void *userData);
void stopNotifier();
NetworkStatus getCurrentReachabilityStatus() const;
/*!
* WWAN may be available, but not active until a connection has been established. WiFi may require a connection for VPN on Demand.
*/
bool isConnectionRequired() const;
private:
Reachability();
~Reachability();
static void onReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void *info);
ReachabilityCallback _callback;
void *_userData;
SCNetworkReachabilityRef _reachabilityRef;
};
} // namespace cc

View File

@@ -0,0 +1,89 @@
/****************************************************************************
Copyright (c) 2018-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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/csscolorparser.h"
#include "base/std/container/array.h"
#include "bindings/jswrapper/config.h"
#include "math/Math.h"
#include "platform/interfaces/modules/canvas/ICanvasRenderingContext2D.h"
#ifdef __OBJC__
@class CanvasRenderingContext2DDelegateImpl;
#else
class CanvasRenderingContext2DDelegateImpl;
#endif
namespace cc {
class CanvasRenderingContext2DDelegate : public ICanvasRenderingContext2D::Delegate {
public:
using Size = ccstd::array<float, 2>;
using TextAlign = ICanvasRenderingContext2D::TextAlign;
using TextBaseline = ICanvasRenderingContext2D::TextBaseline;
CanvasRenderingContext2DDelegate();
~CanvasRenderingContext2DDelegate() override;
void recreateBuffer(float w, float h) override;
void beginPath() override;
void closePath() override;
void moveTo(float x, float y) override;
void lineTo(float x, float y) override;
void stroke() override;
void saveContext() override;
void restoreContext() override;
void clearRect(float /*x*/, float /*y*/, float w, float h) override;
void fill() override;
void setLineCap(const ccstd::string &lineCap) override;
void setLineJoin(const ccstd::string &lineJoin) override;
void rect(float x, float y, float w, float h) override;
void fillRect(float x, float y, float w, float h) override;
void fillText(const ccstd::string &text, float x, float y, float /*maxWidth*/) override;
void strokeText(const ccstd::string &text, float /*x*/, float /*y*/, float /*maxWidth*/) override;
Size measureText(const ccstd::string &text) override;
void updateFont(const ccstd::string &fontName, float fontSize, bool bold, bool italic, bool oblique, bool smallCaps) override;
void setTextAlign(TextAlign align) override;
void setTextBaseline(TextBaseline baseline) override;
void setFillStyle(uint8_t r, uint8_t g, uint8_t b, uint8_t a) override;
void setStrokeStyle(uint8_t r, uint8_t g, uint8_t b, uint8_t a) override;
void setLineWidth(float lineWidth) override;
const cc::Data &getDataRef() const override;
void fillImageData(const Data &imageData, float imageWidth, float imageHeight, float offsetX, float offsetY) override;
void updateData() override {}
void setShadowBlur(float blur) override;
void setShadowColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a) override;
void setShadowOffsetX(float offsetX) override;
void setShadowOffsetY(float offsetY) override;
private:
void fillData();
void unMultiplyAlpha(unsigned char *ptr, uint32_t size) const;
public:
private:
CanvasRenderingContext2DDelegateImpl *_impl;
};
} // namespace cc

View File

@@ -0,0 +1,714 @@
/****************************************************************************
Copyright (c) 2021-2022 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
#include "platform/apple/modules/CanvasRenderingContext2DDelegate.h"
#include "base/UTF8.h"
#include "base/csscolorparser.h"
#include "math/Math.h"
#include "cocos/bindings/jswrapper/SeApi.h"
#include "cocos/bindings/manual/jsb_platform.h"
#import <Foundation/Foundation.h>
#if CC_PLATFORM == CC_PLATFORM_MACOS
#import <Cocoa/Cocoa.h>
#else
#import <CoreText/CoreText.h>
#import <UIKit/UIKit.h>
#define NSBezierPath UIBezierPath
#define NSFont UIFont
#define NSColor UIColor
#define NSSize CGSize
#define NSZeroSize CGSizeZero
#define NSPoint CGPoint
#define NSMakePoint CGPointMake
#endif
#include <regex>
@interface CanvasRenderingContext2DDelegateImpl : NSObject {
NSFont *_font;
NSMutableDictionary *_tokenAttributesDict;
NSString *_fontName;
CGFloat _fontSize;
CGFloat _width;
CGFloat _height;
CGContextRef _context;
#if CC_PLATFORM == CC_PLATFORM_MACOS
NSGraphicsContext *_currentGraphicsContext;
NSGraphicsContext *_oldGraphicsContext;
#else
CGContextRef _oldContext;
#endif
CGColorSpaceRef _colorSpace;
cc::Data _imageData;
NSBezierPath *_path;
cc::ICanvasRenderingContext2D::TextAlign _textAlign;
cc::ICanvasRenderingContext2D::TextBaseline _textBaseLine;
ccstd::array<float, 4> _fillStyle;
ccstd::array<float, 4> _strokeStyle;
NSColor *_shadowColor;
float _lineWidth;
bool _bold;
bool _italic;
}
@property (nonatomic, strong) NSFont *font;
@property (nonatomic, strong) NSMutableDictionary *tokenAttributesDict;
@property (nonatomic, strong) NSString *fontName;
@property (nonatomic, assign) cc::ICanvasRenderingContext2D::TextAlign textAlign;
@property (nonatomic, assign) cc::ICanvasRenderingContext2D::TextBaseline textBaseLine;
@property (nonatomic, assign) float lineWidth;
@property (nonatomic, assign) float shadowBlur;
@property (nonatomic, assign) float shadowOffsetX;
@property (nonatomic, assign) float shadowOffsetY;
@end
@implementation CanvasRenderingContext2DDelegateImpl
@synthesize font = _font;
@synthesize tokenAttributesDict = _tokenAttributesDict;
@synthesize fontName = _fontName;
@synthesize textAlign = _textAlign;
@synthesize textBaseLine = _textBaseLine;
@synthesize lineWidth = _lineWidth;
- (id)init {
if (self = [super init]) {
_lineWidth = 0;
_textAlign = cc::ICanvasRenderingContext2D::TextAlign::LEFT;
_textBaseLine = cc::ICanvasRenderingContext2D::TextBaseline::BOTTOM;
_width = _height = 0;
_shadowBlur = _shadowOffsetX = _shadowOffsetY = 0;
_shadowColor = nil;
_context = nil;
_colorSpace = nil;
#if CC_PLATFORM == CC_PLATFORM_MACOS
_currentGraphicsContext = nil;
_oldGraphicsContext = nil;
#endif
_path = [NSBezierPath bezierPath];
[_path retain];
[self updateFontWithName:@"Arial" fontSize:30 bold:false italic:false];
}
return self;
}
- (void)dealloc {
self.font = nil;
self.tokenAttributesDict = nil;
self.fontName = nil;
if (_shadowColor) {
[_shadowColor release];
}
_shadowColor = nil;
CGColorSpaceRelease(_colorSpace);
// release the context
CGContextRelease(_context);
[_path release];
#if CC_PLATFORM == CC_PLATFORM_MACOS
[_currentGraphicsContext release];
#endif
[super dealloc];
}
#if CC_PLATFORM == CC_PLATFORM_MACOS
- (NSFont *)_createSystemFont {
NSFontTraitMask mask = NSUnitalicFontMask;
if (_italic) {
mask = NSItalicFontMask;
}
if (_bold) {
mask |= NSBoldFontMask;
} else {
mask |= NSUnboldFontMask;
}
NSFont *font = [[NSFontManager sharedFontManager]
fontWithFamily:_fontName
traits:mask
weight:0
size:_fontSize];
if (font == nil) {
const auto &familyMap = getFontFamilyNameMap();
auto iter = familyMap.find([_fontName UTF8String]);
if (iter != familyMap.end()) {
font = [[NSFontManager sharedFontManager]
fontWithFamily:[NSString stringWithUTF8String:iter->second.c_str()]
traits:mask
weight:0
size:_fontSize];
}
}
if (font == nil) {
font = [[NSFontManager sharedFontManager]
fontWithFamily:@"Arial"
traits:mask
weight:0
size:_fontSize];
}
return font;
}
#else
- (UIFont *)_createSystemFont {
UIFont *font = nil;
if (_bold) {
font = [UIFont fontWithName:[_fontName stringByAppendingString:@"-Bold"] size:_fontSize];
} else {
font = [UIFont fontWithName:_fontName size:_fontSize];
}
if (font == nil) {
const auto &familyMap = getFontFamilyNameMap();
auto iter = familyMap.find([_fontName UTF8String]);
if (iter != familyMap.end()) {
font = [UIFont fontWithName:[NSString stringWithUTF8String:iter->second.c_str()] size:_fontSize];
}
}
if (font == nil) {
if (_bold) {
font = [UIFont boldSystemFontOfSize:_fontSize];
} else {
font = [UIFont systemFontOfSize:_fontSize];
}
}
return font;
}
#endif
- (void)updateFontWithName:(NSString *)fontName fontSize:(CGFloat)fontSize bold:(bool)bold italic:(bool)italic {
_fontSize = fontSize;
_bold = bold;
_italic = italic;
self.fontName = fontName;
self.font = [self _createSystemFont];
NSMutableParagraphStyle *paragraphStyle = [[[NSMutableParagraphStyle alloc] init] autorelease];
paragraphStyle.lineBreakMode = NSLineBreakByTruncatingTail;
[paragraphStyle setAlignment:NSTextAlignmentCenter];
// color
NSColor *foregroundColor = [NSColor colorWithRed:1.0f
green:1.0f
blue:1.0f
alpha:1.0f];
// attribute
if (_italic) {
self.tokenAttributesDict = [NSMutableDictionary dictionaryWithObjectsAndKeys:
foregroundColor, NSForegroundColorAttributeName,
_font, NSFontAttributeName,
@(0.25f), NSObliquenessAttributeName,
paragraphStyle, NSParagraphStyleAttributeName, nil];
} else {
self.tokenAttributesDict = [NSMutableDictionary dictionaryWithObjectsAndKeys:
foregroundColor, NSForegroundColorAttributeName,
_font, NSFontAttributeName,
paragraphStyle, NSParagraphStyleAttributeName, nil];
}
}
- (void)recreateBufferWithWidth:(NSInteger)width height:(NSInteger)height {
_width = width = width > 0 ? width : 1;
_height = height = height > 0 ? height : 1;
NSUInteger textureSize = width * height * 4;
unsigned char *data = (unsigned char *)malloc(sizeof(unsigned char) * textureSize);
memset(data, 0, textureSize);
_imageData.fastSet(data, static_cast<uint32_t>(textureSize));
if (_context != nil) {
CGContextRelease(_context);
_context = nil;
}
#if CC_PLATFORM == CC_PLATFORM_MACOS
if (_currentGraphicsContext != nil) {
[_currentGraphicsContext release];
_currentGraphicsContext = nil;
}
#endif
// draw text
_colorSpace = CGColorSpaceCreateDeviceRGB();
_context = CGBitmapContextCreate(data,
width,
height,
8,
width * 4,
_colorSpace,
kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
if (nil == _context) {
CGColorSpaceRelease(_colorSpace); //REFINE: HOWTO RELEASE?
_colorSpace = nil;
}
#if CC_PLATFORM == CC_PLATFORM_MACOS
_currentGraphicsContext = [NSGraphicsContext graphicsContextWithCGContext:_context flipped:NO];
[_currentGraphicsContext retain];
#else
// move Y rendering to the top of the image
CGContextTranslateCTM(_context, 0.0f, _height);
//NOTE: NSString draws in UIKit referential i.e. renders upside-down compared to CGBitmapContext referential
CGContextScaleCTM(_context, 1.0f, -1.0f);
#endif
}
- (NSSize)measureText:(NSString *)text {
NSAttributedString *stringWithAttributes = [[[NSAttributedString alloc] initWithString:text
attributes:_tokenAttributesDict] autorelease];
NSSize textRect = NSZeroSize;
textRect.width = CGFLOAT_MAX;
textRect.height = CGFLOAT_MAX;
NSSize dim = [stringWithAttributes boundingRectWithSize:textRect options:(NSStringDrawingOptions)(NSStringDrawingUsesLineFragmentOrigin)context:nil].size;
dim.width = ceilf(dim.width);
dim.height = ceilf(dim.height);
return dim;
}
- (NSPoint)convertDrawPoint:(NSPoint)point text:(NSString *)text {
// The parameter 'point' is located at left-bottom position.
// Need to adjust 'point' according 'text align' & 'text base line'.
NSSize textSize = [self measureText:text];
if (_textAlign == cc::ICanvasRenderingContext2D::TextAlign::CENTER) {
point.x -= textSize.width / 2.0f;
} else if (_textAlign == cc::ICanvasRenderingContext2D::TextAlign::RIGHT) {
point.x -= textSize.width;
}
#if CC_PLATFORM == CC_PLATFORM_MACOS
// The origin on macOS is bottom-left by default,
// so we need to convert y from top-left origin to bottom-left origin.
point.y = _height - point.y;
if (_textBaseLine == cc::ICanvasRenderingContext2D::TextBaseline::TOP) {
point.y += -textSize.height;
} else if (_textBaseLine == cc::ICanvasRenderingContext2D::TextBaseline::MIDDLE) {
point.y += -textSize.height / 2.0f;
} else if (_textBaseLine == cc::ICanvasRenderingContext2D::TextBaseline::BOTTOM) {
// drawAtPoint default
} else if (_textBaseLine == cc::ICanvasRenderingContext2D::TextBaseline::ALPHABETIC) {
point.y += _font.descender;
}
#else
if (_textBaseLine == cc::ICanvasRenderingContext2D::TextBaseline::TOP) {
// drawAtPoint default
} else if (_textBaseLine == cc::ICanvasRenderingContext2D::TextBaseline::MIDDLE) {
point.y += -textSize.height / 2.0f;
} else if (_textBaseLine == cc::ICanvasRenderingContext2D::TextBaseline::BOTTOM) {
point.y += -textSize.height;
} else if (_textBaseLine == cc::ICanvasRenderingContext2D::TextBaseline::ALPHABETIC) {
point.y -= _font.ascender;
}
#endif
return point;
}
- (bool) isShadowEnabled {
if (_shadowColor && (_shadowBlur > 0 || _shadowOffsetX > 0 || _shadowOffsetY > 0)) {
return true;
}
return false;
}
- (void)fillText:(NSString *)text x:(CGFloat)x y:(CGFloat)y maxWidth:(CGFloat)maxWidth {
if (text.length == 0)
return;
NSPoint drawPoint = [self convertDrawPoint:NSMakePoint(x, y) text:text];
NSMutableParagraphStyle *paragraphStyle = [[[NSMutableParagraphStyle alloc] init] autorelease];
paragraphStyle.lineBreakMode = NSLineBreakByTruncatingTail;
[_tokenAttributesDict removeObjectForKey:NSStrokeColorAttributeName];
[_tokenAttributesDict setObject:paragraphStyle forKey:NSParagraphStyleAttributeName];
[_tokenAttributesDict setObject:[NSColor colorWithRed:_fillStyle[0] green:_fillStyle[1] blue:_fillStyle[2] alpha:_fillStyle[3]]
forKey:NSForegroundColorAttributeName];
[self saveContext];
// text color
CGContextSetRGBFillColor(_context, _fillStyle[0], _fillStyle[1], _fillStyle[2], _fillStyle[3]);
CGContextSetShouldSubpixelQuantizeFonts(_context, false);
CGContextBeginTransparencyLayerWithRect(_context, CGRectMake(0, 0, _width, _height), nullptr);
CGContextSetTextDrawingMode(_context, kCGTextFill);
if ([self isShadowEnabled]) {
// https://developer.apple.com/library/archive/documentation/2DDrawing/Conceptual/DrawingPrintingiOS/GraphicsDrawingOverview/GraphicsDrawingOverview.html
/* Core Graphics uses lower-left-origin coordinate system;
but the api CGContextSetShadowWithColor's parameter 'offset' accept base space(relative to view: upper-left-origin coordinate system),
and _shadowOffsetY is edited by lower-left-origin coordinate system, so here we need to multiply the change in y direction by -1
*/
CGContextSetShadowWithColor(_context, CGSizeMake(_shadowOffsetX, -_shadowOffsetY), _shadowBlur, _shadowColor.CGColor);
}
NSAttributedString *stringWithAttributes = [[[NSAttributedString alloc] initWithString:text
attributes:_tokenAttributesDict] autorelease];
[stringWithAttributes drawAtPoint:drawPoint];
CGContextEndTransparencyLayer(_context);
[self restoreContext];
}
- (void)strokeText:(NSString *)text x:(CGFloat)x y:(CGFloat)y maxWidth:(CGFloat)maxWidth {
if (text.length == 0)
return;
NSPoint drawPoint = [self convertDrawPoint:NSMakePoint(x, y) text:text];
NSMutableParagraphStyle *paragraphStyle = [[[NSMutableParagraphStyle alloc] init] autorelease];
paragraphStyle.lineBreakMode = NSLineBreakByTruncatingTail;
[_tokenAttributesDict removeObjectForKey:NSForegroundColorAttributeName];
[_tokenAttributesDict setObject:paragraphStyle forKey:NSParagraphStyleAttributeName];
[_tokenAttributesDict setObject:[NSColor colorWithRed:_strokeStyle[0]
green:_strokeStyle[1]
blue:_strokeStyle[2]
alpha:_strokeStyle[3]]
forKey:NSStrokeColorAttributeName];
[self saveContext];
// text color
CGContextSetRGBStrokeColor(_context, _strokeStyle[0], _strokeStyle[1], _strokeStyle[2], _strokeStyle[3]);
CGContextSetRGBFillColor(_context, _fillStyle[0], _fillStyle[1], _fillStyle[2], _fillStyle[3]);
CGContextSetLineWidth(_context, _lineWidth);
CGContextSetLineJoin(_context, kCGLineJoinRound);
CGContextSetShouldSubpixelQuantizeFonts(_context, false);
CGContextBeginTransparencyLayerWithRect(_context, CGRectMake(0, 0, _width, _height), nullptr);
CGContextSetTextDrawingMode(_context, kCGTextStroke);
if ([self isShadowEnabled]) {
// https://developer.apple.com/library/archive/documentation/2DDrawing/Conceptual/DrawingPrintingiOS/GraphicsDrawingOverview/GraphicsDrawingOverview.html
/* Core Graphics uses lower-left-origin coordinate system;
but the api CGContextSetShadowWithColor's parameter 'offset' accept base space(relative to view: upper-left-origin coordinate system),
and _shadowOffsetY is edited by lower-left-origin coordinate system, so here we need to multiply the change in y direction by -1
*/
CGContextSetShadowWithColor(_context, CGSizeMake(_shadowOffsetX, -_shadowOffsetY), _shadowBlur, _shadowColor.CGColor);
}
NSAttributedString *stringWithAttributes = [[[NSAttributedString alloc] initWithString:text
attributes:_tokenAttributesDict] autorelease];
[stringWithAttributes drawAtPoint:drawPoint];
CGContextEndTransparencyLayer(_context);
[self restoreContext];
}
- (void)setFillStyleWithRed:(CGFloat)r green:(CGFloat)g blue:(CGFloat)b alpha:(CGFloat)a {
_fillStyle[0] = r;
_fillStyle[1] = g;
_fillStyle[2] = b;
_fillStyle[3] = a;
}
- (void)setStrokeStyleWithRed:(CGFloat)r green:(CGFloat)g blue:(CGFloat)b alpha:(CGFloat)a {
_strokeStyle[0] = r;
_strokeStyle[1] = g;
_strokeStyle[2] = b;
_strokeStyle[3] = a;
}
- (void)setShadowColorWithRed:(CGFloat)r green:(CGFloat)g blue:(CGFloat)b alpha:(CGFloat)a {
_shadowColor = [NSColor colorWithRed:r green:g blue:b alpha:a];
[_shadowColor retain];
}
- (const cc::Data &)getDataRef {
return _imageData;
}
- (void)clearRect:(CGRect)rect {
if (_imageData.isNull())
return;
rect.origin.x = floor(rect.origin.x);
rect.origin.y = floor(rect.origin.y);
rect.size.width = floor(rect.size.width);
rect.size.height = floor(rect.size.height);
if (rect.origin.x < 0) rect.origin.x = 0;
if (rect.origin.y < 0) rect.origin.y = 0;
if (rect.size.width < 1 || rect.size.height < 1)
return;
//REFINE:
// CC_ASSERT(rect.origin.x == 0 && rect.origin.y == 0);
memset((void *)_imageData.getBytes(), 0x00, _imageData.getSize());
}
- (void)fillRect:(CGRect)rect {
[self saveContext];
NSColor *color = [NSColor colorWithRed:_fillStyle[0] green:_fillStyle[1] blue:_fillStyle[2] alpha:_fillStyle[3]];
[color setFill];
#if CC_PLATFORM == CC_PLATFORM_MACOS
CGRect tmpRect = CGRectMake(rect.origin.x, _height - rect.origin.y - rect.size.height, rect.size.width, rect.size.height);
[NSBezierPath fillRect:tmpRect];
#else
NSBezierPath *path = [NSBezierPath bezierPathWithRect:rect];
[path fill];
#endif
[self restoreContext];
}
- (void)saveContext {
#if CC_PLATFORM == CC_PLATFORM_MACOS
// save the old graphics context
_oldGraphicsContext = [NSGraphicsContext currentContext];
// store the current context
[NSGraphicsContext setCurrentContext:_currentGraphicsContext];
// push graphics state to stack
[NSGraphicsContext saveGraphicsState];
[[NSGraphicsContext currentContext] setShouldAntialias:YES];
#else
// save the old graphics context
_oldContext = UIGraphicsGetCurrentContext();
// store the current context
UIGraphicsPushContext(_context);
CGContextSaveGState(_context);
#endif
}
- (void)restoreContext {
#if CC_PLATFORM == CC_PLATFORM_MACOS
// pop the context
[NSGraphicsContext restoreGraphicsState];
// reset the old graphics context
[NSGraphicsContext setCurrentContext:_oldGraphicsContext];
_oldGraphicsContext = nil;
#else
// pop the context
CGContextRestoreGState(_context);
// reset the old graphics context
UIGraphicsPopContext();
_oldContext = nil;
#endif
}
- (void)beginPath {
}
- (void)stroke {
NSColor *color = [NSColor colorWithRed:_strokeStyle[0] green:_strokeStyle[1] blue:_strokeStyle[2] alpha:_strokeStyle[3]];
[color setStroke];
[_path setLineWidth:_lineWidth];
[_path stroke];
}
- (void)moveToX:(float)x y:(float)y {
#if CC_PLATFORM == CC_PLATFORM_MACOS
[_path moveToPoint:NSMakePoint(x, _height - y)];
#else
[_path moveToPoint:NSMakePoint(x, y)];
#endif
}
- (void)lineToX:(float)x y:(float)y {
#if CC_PLATFORM == CC_PLATFORM_MACOS
[_path lineToPoint:NSMakePoint(x, _height - y)];
#else
[_path addLineToPoint:NSMakePoint(x, y)];
#endif
}
@end
namespace cc {
CanvasRenderingContext2DDelegate::CanvasRenderingContext2DDelegate() {
_impl = [[CanvasRenderingContext2DDelegateImpl alloc] init];
}
CanvasRenderingContext2DDelegate::~CanvasRenderingContext2DDelegate() {
[_impl release];
}
void CanvasRenderingContext2DDelegate::recreateBuffer(float w, float h) {
[_impl recreateBufferWithWidth:w height:h];
}
const cc::Data &CanvasRenderingContext2DDelegate::getDataRef() const {
static Data data;
data = [_impl getDataRef];
unMultiplyAlpha(data.getBytes(), data.getSize());
return data;
}
void CanvasRenderingContext2DDelegate::beginPath() {
[_impl beginPath];
}
void CanvasRenderingContext2DDelegate::closePath() {
}
void CanvasRenderingContext2DDelegate::moveTo(float x, float y) {
[_impl moveToX:x y:y];
}
void CanvasRenderingContext2DDelegate::lineTo(float x, float y) {
[_impl lineToX:x y:y];
}
void CanvasRenderingContext2DDelegate::stroke() {
[_impl stroke];
}
void CanvasRenderingContext2DDelegate::saveContext() {
[_impl saveContext];
}
void CanvasRenderingContext2DDelegate::restoreContext() {
[_impl restoreContext];
}
void CanvasRenderingContext2DDelegate::clearRect(float x, float y, float w, float h) {
[_impl clearRect:CGRectMake(x, y, w, h)];
}
void CanvasRenderingContext2DDelegate::fill() {
}
void CanvasRenderingContext2DDelegate::setLineCap(const ccstd::string &lineCap) {
}
void CanvasRenderingContext2DDelegate::setLineJoin(const ccstd::string &lineJoin) {
}
void CanvasRenderingContext2DDelegate::rect(float x, float y, float w, float h) {
}
void CanvasRenderingContext2DDelegate::fillRect(float x, float y, float w, float h) {
[_impl fillRect:CGRectMake(x, y, w, h)];
}
void CanvasRenderingContext2DDelegate::fillText(const ccstd::string &text, float x, float y, float maxWidth) {
[_impl fillText:[NSString stringWithUTF8String:text.c_str()] x:x y:y maxWidth:maxWidth];
}
void CanvasRenderingContext2DDelegate::strokeText(const ccstd::string &text, float x, float y, float maxWidth) {
[_impl strokeText:[NSString stringWithUTF8String:text.c_str()] x:x y:y maxWidth:maxWidth];
}
CanvasRenderingContext2DDelegate::Size CanvasRenderingContext2DDelegate::measureText(const ccstd::string &text) {
NSString *str = [NSString stringWithUTF8String:text.c_str()];
if (str == nil) {
ccstd::string textNew;
cc::StringUtils::UTF8LooseFix(text, textNew);
str = [NSString stringWithUTF8String:textNew.c_str()];
}
CGSize size = [_impl measureText:str];
return CanvasRenderingContext2DDelegate::Size{(float)size.width, (float)size.height};
}
void CanvasRenderingContext2DDelegate::updateFont(const ccstd::string &fontName, float fontSize, bool bold, bool italic, bool oblique, bool smallCaps) {
CGFloat gfloatFontSize = fontSize;
[_impl updateFontWithName:[NSString stringWithUTF8String:fontName.c_str()] fontSize:gfloatFontSize bold:bold italic:italic];
}
void CanvasRenderingContext2DDelegate::setTextAlign(TextAlign align) {
_impl.textAlign = align;
}
void CanvasRenderingContext2DDelegate::setTextBaseline(TextBaseline baseline) {
_impl.textBaseLine = baseline;
}
void CanvasRenderingContext2DDelegate::setFillStyle(uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
[_impl setFillStyleWithRed:r / 255.0f green:g / 255.0f blue:b / 255.0f alpha:a / 255.0f];
}
void CanvasRenderingContext2DDelegate::setStrokeStyle(uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
[_impl setStrokeStyleWithRed:r / 255.0f green:g / 255.0f blue:b / 255.0f alpha:a / 255.0f];
}
void CanvasRenderingContext2DDelegate::setLineWidth(float lineWidth) {
_impl.lineWidth = lineWidth;
}
void CanvasRenderingContext2DDelegate::fillImageData(const Data &imageData, float imageWidth, float imageHeight, float offsetX, float offsetY) {
}
void CanvasRenderingContext2DDelegate::fillData() {
}
#define CLAMP(V, HI) std::min((V), (HI))
void CanvasRenderingContext2DDelegate::unMultiplyAlpha(unsigned char *ptr, uint32_t size) const {
float alpha;
for (int i = 0; i < size; i += 4) {
alpha = (float)ptr[i + 3];
if (alpha > 0) {
ptr[i] = CLAMP((int)((float)ptr[i] / alpha * 255), 255);
ptr[i + 1] = CLAMP((int)((float)ptr[i + 1] / alpha * 255), 255);
ptr[i + 2] = CLAMP((int)((float)ptr[i + 2] / alpha * 255), 255);
}
}
}
void CanvasRenderingContext2DDelegate::setShadowBlur(float blur) {
_impl.shadowBlur = blur * 0.5f;
}
void CanvasRenderingContext2DDelegate::setShadowColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
[_impl setShadowColorWithRed:r / 255.0f green:g / 255.0f blue:b / 255.0f alpha:a / 255.0f];
}
void CanvasRenderingContext2DDelegate::setShadowOffsetX(float offsetX) {
_impl.shadowOffsetX = offsetX;
}
void CanvasRenderingContext2DDelegate::setShadowOffsetY(float offsetY) {
_impl.shadowOffsetY = offsetY;
}
} // namespace cc