初始化

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,850 @@
#include "platform/CCCanvasRenderingContext2D.h"
#include "base/ccTypes.h"
#include "base/csscolorparser.hpp"
#include "base/ccUTF8.h"
#include "cocos/scripting/js-bindings/jswrapper/SeApi.h"
#include "cocos/scripting/js-bindings/manual/jsb_platform.h"
#import <Foundation/Foundation.h>
#if CC_TARGET_PLATFORM == CC_PLATFORM_MAC
#import <Cocoa/Cocoa.h>
#else
#import <CoreText/CoreText.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>
enum class CanvasTextAlign {
LEFT,
CENTER,
RIGHT
};
enum class CanvasTextBaseline {
TOP,
MIDDLE,
BOTTOM
};
@interface CanvasRenderingContext2DImpl : NSObject {
NSFont* _font;
NSMutableDictionary* _tokenAttributesDict;
NSString* _fontName;
CGFloat _fontSize;
CGFloat _width;
CGFloat _height;
CGContextRef _context;
#if CC_TARGET_PLATFORM == CC_PLATFORM_MAC
NSGraphicsContext* _currentGraphicsContext;
NSGraphicsContext* _oldGraphicsContext;
#else
CGContextRef _oldContext;
#endif
CGColorSpaceRef _colorSpace;
cocos2d::Data _imageData;
NSBezierPath* _path;
CanvasTextAlign _textAlign;
CanvasTextBaseline _textBaseLine;
cocos2d::Color4F _fillStyle;
cocos2d::Color4F _strokeStyle;
float _lineWidth;
bool _bold;
}
@property (nonatomic, strong) NSFont* font;
@property (nonatomic, strong) NSMutableDictionary* tokenAttributesDict;
@property (nonatomic, strong) NSString* fontName;
@property (nonatomic, assign) CanvasTextAlign textAlign;
@property (nonatomic, assign) CanvasTextBaseline textBaseLine;
@property (nonatomic, assign) float lineWidth;
@end
@implementation CanvasRenderingContext2DImpl
@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 = CanvasTextAlign::LEFT;
_textBaseLine = CanvasTextBaseline::BOTTOM;
_width = _height = 0;
_context = nil;
_colorSpace = nil;
#if CC_TARGET_PLATFORM == CC_PLATFORM_MAC
_currentGraphicsContext = nil;
_oldGraphicsContext = nil;
#endif
_path = [NSBezierPath bezierPath];
[_path retain];
[self updateFontWithName:@"Arial" fontSize:30 bold:false];
}
return self;
}
-(void) dealloc {
self.font = nil;
self.tokenAttributesDict = nil;
self.fontName = nil;
CGColorSpaceRelease(_colorSpace);
// release the context
CGContextRelease(_context);
[_path release];
#if CC_TARGET_PLATFORM == CC_PLATFORM_MAC
[_currentGraphicsContext release];
#endif
[super dealloc];
}
#if CC_TARGET_PLATFORM == CC_PLATFORM_MAC
-(NSFont*) _createSystemFont {
NSFontTraitMask mask = NSUnitalicFontMask;
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{
_fontSize = fontSize;
_bold = bold;
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
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, textureSize);
if (_context != nil)
{
CGContextRelease(_context);
_context = nil;
}
#if CC_TARGET_PLATFORM == CC_PLATFORM_MAC
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_TARGET_PLATFORM == CC_PLATFORM_MAC
_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;
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 == CanvasTextAlign::CENTER)
{
point.x -= textSize.width / 2.0f;
}
else if (_textAlign == CanvasTextAlign::RIGHT)
{
point.x -= textSize.width;
}
if (_textBaseLine == CanvasTextBaseline::TOP)
{
point.y += _fontSize;
}
else if (_textBaseLine == CanvasTextBaseline::MIDDLE)
{
point.y += _fontSize / 2.0f;
}
// Since the web platform cannot get the baseline of the font, an additive offset is performed for all platforms.
// That's why we should add baseline back again on other platforms
#if CC_TARGET_PLATFORM == CC_PLATFORM_MAC
point.y -= _font.descender;
// 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;
#else
point.y -= _font.ascender;
#endif
return point;
}
-(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.r green:_fillStyle.g blue:_fillStyle.b alpha:_fillStyle.a]
forKey:NSForegroundColorAttributeName];
[self saveContext];
// text color
CGContextSetRGBFillColor(_context, _fillStyle.r, _fillStyle.g, _fillStyle.b, _fillStyle.a);
CGContextSetShouldSubpixelQuantizeFonts(_context, false);
CGContextBeginTransparencyLayerWithRect(_context, CGRectMake(0, 0, _width, _height), nullptr);
CGContextSetTextDrawingMode(_context, kCGTextFill);
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.r
green:_strokeStyle.g
blue:_strokeStyle.b
alpha:_strokeStyle.a] forKey:NSStrokeColorAttributeName];
[self saveContext];
// text color
CGContextSetRGBStrokeColor(_context, _strokeStyle.r, _strokeStyle.g, _strokeStyle.b, _strokeStyle.a);
CGContextSetRGBFillColor(_context, _fillStyle.r, _fillStyle.g, _fillStyle.b, _fillStyle.a);
CGContextSetLineWidth(_context, _lineWidth);
CGContextSetLineJoin(_context, kCGLineJoinRound);
CGContextSetShouldSubpixelQuantizeFonts(_context, false);
CGContextBeginTransparencyLayerWithRect(_context, CGRectMake(0, 0, _width, _height), nullptr);
CGContextSetTextDrawingMode(_context, kCGTextStroke);
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.r = r;
_fillStyle.g = g;
_fillStyle.b = b;
_fillStyle.a = a;
}
-(void) setStrokeStyleWithRed:(CGFloat) r green:(CGFloat) g blue:(CGFloat) b alpha:(CGFloat) a {
_strokeStyle.r = r;
_strokeStyle.g = g;
_strokeStyle.b = b;
_strokeStyle.a = a;
}
-(const cocos2d::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:
// 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.r green:_fillStyle.g blue:_fillStyle.b alpha:_fillStyle.a];
[color setFill];
#if CC_TARGET_PLATFORM == CC_PLATFORM_MAC
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_TARGET_PLATFORM == CC_PLATFORM_MAC
// 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_TARGET_PLATFORM == CC_PLATFORM_MAC
// 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.r green:_strokeStyle.g blue:_strokeStyle.b alpha:_strokeStyle.a];
[color setStroke];
[_path setLineWidth: _lineWidth];
[_path stroke];
}
-(void) moveToX: (float) x y:(float) y {
#if CC_TARGET_PLATFORM == CC_PLATFORM_MAC
[_path moveToPoint: NSMakePoint(x, _height - y)];
#else
[_path moveToPoint: NSMakePoint(x, y)];
#endif
}
-(void) lineToX: (float) x y:(float) y {
#if CC_TARGET_PLATFORM == CC_PLATFORM_MAC
[_path lineToPoint: NSMakePoint(x, _height - y)];
#else
[_path addLineToPoint: NSMakePoint(x, y)];
#endif
}
@end
NS_CC_BEGIN
CanvasGradient::CanvasGradient()
{
SE_LOGD("CanvasGradient constructor: %p\n", this);
}
CanvasGradient::~CanvasGradient()
{
SE_LOGD("CanvasGradient destructor: %p\n", this);
}
void CanvasGradient::addColorStop(float offset, const std::string& color)
{
SE_LOGD("CanvasGradient::addColorStop: %p\n", this);
}
// CanvasRenderingContext2D
namespace
{
#define CLAMP(V, HI) std::min( (V), (HI) )
void unMultiplyAlpha(unsigned char* ptr, ssize_t size)
{
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);
}
}
}
}
#define SEND_DATA_TO_JS(CB, IMPL, PREMULTIPLY) \
if (CB) \
{ \
Data data([IMPL getDataRef]); \
if (!PREMULTIPLY) \
{ \
unMultiplyAlpha(data.getBytes(), data.getSize() ); \
} \
CB(data); \
}
CanvasRenderingContext2D::CanvasRenderingContext2D(float width, float height)
: __width(width)
, __height(height)
{
// SE_LOGD("CanvasRenderingContext2D constructor: %p, width: %f, height: %f\n", this, width, height);
_impl = [[CanvasRenderingContext2DImpl alloc] init];
[_impl recreateBufferWithWidth:width height:height];
}
CanvasRenderingContext2D::~CanvasRenderingContext2D()
{
// SE_LOGD("CanvasRenderingContext2D destructor: %p\n", this);
[_impl release];
}
void CanvasRenderingContext2D::recreateBufferIfNeeded()
{
if (_isBufferSizeDirty)
{
_isBufferSizeDirty = false;
// SE_LOGD("CanvasRenderingContext2D::recreateBufferIfNeeded %p, w: %f, h:%f\n", this, __width, __height);
[_impl recreateBufferWithWidth: __width height:__height];
SEND_DATA_TO_JS(_canvasBufferUpdatedCB, _impl, _premultiply);
}
}
void CanvasRenderingContext2D::clearRect(float x, float y, float width, float height)
{
// SE_LOGD("CanvasGradient::clearRect: %p, %f, %f, %f, %f\n", this, x, y, width, height);
recreateBufferIfNeeded();
[_impl clearRect:CGRectMake(x, y, width, height)];
}
void CanvasRenderingContext2D::fillRect(float x, float y, float width, float height)
{
recreateBufferIfNeeded();
[_impl fillRect:CGRectMake(x, y, width, height)];
SEND_DATA_TO_JS(_canvasBufferUpdatedCB, _impl, _premultiply);
}
void CanvasRenderingContext2D::fillText(const std::string& text, float x, float y, float maxWidth)
{
// SE_LOGD("CanvasRenderingContext2D(%p)::fillText: %s, %f, %f, %f\n", this, text.c_str(), x, y, maxWidth);
if (text.empty())
return;
recreateBufferIfNeeded();
auto textUtf8 = [NSString stringWithUTF8String:text.c_str()];
if(textUtf8 == nullptr) {
SE_LOGE("CanvasRenderingContext2D::fillText failed to convert text to UTF8\n text:\"%s\"", text.c_str());
return;
}
[_impl fillText:textUtf8 x:x y:y maxWidth:maxWidth];
SEND_DATA_TO_JS(_canvasBufferUpdatedCB, _impl, _premultiply);
}
void CanvasRenderingContext2D::strokeText(const std::string& text, float x, float y, float maxWidth)
{
// SE_LOGD("CanvasRenderingContext2D(%p)::strokeText: %s, %f, %f, %f\n", this, text.c_str(), x, y, maxWidth);
if (text.empty())
return;
recreateBufferIfNeeded();
auto textUtf8 = [NSString stringWithUTF8String:text.c_str()];
if(textUtf8 == nullptr) {
SE_LOGE("CanvasRenderingContext2D::strokeText failed to convert text to UTF8\n text:\"%s\"", text.c_str());
return;
}
[_impl strokeText:textUtf8 x:x y:y maxWidth:maxWidth];
SEND_DATA_TO_JS(_canvasBufferUpdatedCB, _impl, _premultiply);
}
cocos2d::Size CanvasRenderingContext2D::measureText(const std::string& text)
{
NSString *str =[NSString stringWithUTF8String:text.c_str()];
if(str == nil) {
std::string textNew;
cocos2d::StringUtils::UTF8LooseFix(text, textNew);
str = [NSString stringWithUTF8String:textNew.c_str()];
}
CGSize size = [_impl measureText: str];
return cocos2d::Size(size.width, size.height);
}
CanvasGradient* CanvasRenderingContext2D::createLinearGradient(float x0, float y0, float x1, float y1)
{
return nullptr;
}
void CanvasRenderingContext2D::save()
{
[_impl saveContext];
}
void CanvasRenderingContext2D::beginPath()
{
[_impl beginPath];
}
void CanvasRenderingContext2D::closePath()
{
//SE_LOGE("%s isn't implemented!\n", __FUNCTION__);
}
void CanvasRenderingContext2D::moveTo(float x, float y)
{
[_impl moveToX:x y:y];
}
void CanvasRenderingContext2D::lineTo(float x, float y)
{
[_impl lineToX:x y:y];
}
void CanvasRenderingContext2D::stroke()
{
[_impl stroke];
SEND_DATA_TO_JS(_canvasBufferUpdatedCB, _impl, _premultiply);
}
void CanvasRenderingContext2D::fill()
{
//SE_LOGE("%s isn't implemented!\n", __FUNCTION__);
}
void CanvasRenderingContext2D::rect(float x, float y, float w, float h)
{
//SE_LOGE("%s isn't implemented!\n", __FUNCTION__);
}
void CanvasRenderingContext2D::restore()
{
[_impl restoreContext];
}
void CanvasRenderingContext2D::setCanvasBufferUpdatedCallback(const CanvasBufferUpdatedCallback& cb)
{
_canvasBufferUpdatedCB = cb;
}
void CanvasRenderingContext2D::setPremultiply(bool multiply)
{
_premultiply = multiply;
}
void CanvasRenderingContext2D::set__width(float width)
{
// SE_LOGD("CanvasRenderingContext2D::set__width: %f\n", width);
__width = width;
_isBufferSizeDirty = true;
recreateBufferIfNeeded();
}
void CanvasRenderingContext2D::set__height(float height)
{
// SE_LOGD("CanvasRenderingContext2D::set__height: %f\n", height);
__height = height;
_isBufferSizeDirty = true;
recreateBufferIfNeeded();
}
void CanvasRenderingContext2D::set_lineWidth(float lineWidth)
{
_lineWidth = lineWidth;
_impl.lineWidth = _lineWidth;
}
void CanvasRenderingContext2D::set_lineCap(const std::string& lineCap)
{
//SE_LOGE("%s isn't implemented!\n", __FUNCTION__);
}
void CanvasRenderingContext2D::set_lineJoin(const std::string& lineJoin)
{
//SE_LOGE("%s isn't implemented!\n", __FUNCTION__);
}
void CanvasRenderingContext2D::set_font(const std::string& font)
{
if (_font != font)
{
_font = font;
std::string boldStr;
std::string fontName = "Arial";
std::string fontSizeStr = "30";
// support get font name from `60px American` or `60px "American abc-abc_abc"`
std::regex re("(bold)?\\s*((\\d+)([\\.]\\d+)?)px\\s+([\\w-]+|\"[\\w -]+\"$)");
std::match_results<std::string::const_iterator> results;
if (std::regex_search(_font.cbegin(), _font.cend(), results, re))
{
boldStr = results[1].str();
fontSizeStr = results[2].str();
fontName = results[5].str();
}
CGFloat fontSize = atof(fontSizeStr.c_str());
[_impl updateFontWithName:[NSString stringWithUTF8String:fontName.c_str()] fontSize:fontSize bold:!boldStr.empty()];
}
}
void CanvasRenderingContext2D::set_textAlign(const std::string& textAlign)
{
// SE_LOGD("CanvasRenderingContext2D::set_textAlign: %s\n", textAlign.c_str());
if (textAlign == "left")
{
_impl.textAlign = CanvasTextAlign::LEFT;
}
else if (textAlign == "center" || textAlign == "middle")
{
_impl.textAlign = CanvasTextAlign::CENTER;
}
else if (textAlign == "right")
{
_impl.textAlign = CanvasTextAlign::RIGHT;
}
else
{
assert(false);
}
}
void CanvasRenderingContext2D::set_textBaseline(const std::string& textBaseline)
{
// SE_LOGD("CanvasRenderingContext2D::set_textBaseline: %s\n", textBaseline.c_str());
if (textBaseline == "top")
{
_impl.textBaseLine = CanvasTextBaseline::TOP;
}
else if (textBaseline == "middle")
{
_impl.textBaseLine = CanvasTextBaseline::MIDDLE;
}
else if (textBaseline == "bottom" || textBaseline == "alphabetic") //REFINE:, how to deal with alphabetic, currently we handle it as bottom mode.
{
_impl.textBaseLine = CanvasTextBaseline::BOTTOM;
}
else
{
assert(false);
}
}
void CanvasRenderingContext2D::set_fillStyle(const std::string& fillStyle)
{
CSSColorParser::Color color = CSSColorParser::parse(fillStyle);
[_impl setFillStyleWithRed:color.r/255.0f green:color.g/255.0f blue:color.b/255.0f alpha:color.a];
// SE_LOGD("CanvasRenderingContext2D::set_fillStyle: %s, (%d, %d, %d, %f)\n", fillStyle.c_str(), color.r, color.g, color.b, color.a);
}
void CanvasRenderingContext2D::set_strokeStyle(const std::string& strokeStyle)
{
CSSColorParser::Color color = CSSColorParser::parse(strokeStyle);
[_impl setStrokeStyleWithRed:color.r/255.0f green:color.g/255.0f blue:color.b/255.0f alpha:color.a];
}
void CanvasRenderingContext2D::set_globalCompositeOperation(const std::string& globalCompositeOperation)
{
//SE_LOGE("%s isn't implemented!\n", __FUNCTION__);
}
void CanvasRenderingContext2D::_fillImageData(const Data& imageData, float imageWidth, float imageHeight, float offsetX, float offsetY)
{
//SE_LOGE("%s isn't implemented!\n", __FUNCTION__);
}
// transform
void CanvasRenderingContext2D::translate(float x, float y)
{
//SE_LOGE("%s isn't implemented!\n", __FUNCTION__);
}
void CanvasRenderingContext2D::scale(float x, float y)
{
//SE_LOGE("%s isn't implemented!\n", __FUNCTION__);
}
void CanvasRenderingContext2D::rotate(float angle)
{
//SE_LOGE("%s isn't implemented!\n", __FUNCTION__);
}
void CanvasRenderingContext2D::transform(float a, float b, float c, float d, float e, float f)
{
//SE_LOGE("%s isn't implemented!\n", __FUNCTION__);
}
void CanvasRenderingContext2D::setTransform(float a, float b, float c, float d, float e, float f)
{
//SE_LOGE("%s isn't implemented!\n", __FUNCTION__);
}
NS_CC_END

View File

@@ -0,0 +1,32 @@
/****************************************************************************
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 CCDevice_apple_hpp
#define CCDevice_apple_hpp
#include "platform/CCDevice.h"
#endif /* CCDevice_apple_hpp */

View File

@@ -0,0 +1,38 @@
/****************************************************************************
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 "platform/apple/CCDevice-apple.h"
#include "platform/CCApplication.h"
NS_CC_BEGIN
int Device::getDevicePixelRatio()
{
return Application::getInstance()->getScreenScale();
}
NS_CC_END

View File

@@ -0,0 +1,80 @@
/****************************************************************************
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 __CC_FILEUTILS_APPLE_H__
#define __CC_FILEUTILS_APPLE_H__
#include <memory>
#include <string>
#include <vector>
#include "platform/CCFileUtils.h"
#include "base/ccMacros.h"
#include "base/ccTypes.h"
NS_CC_BEGIN
/**
* @addtogroup platform
* @{
*/
//! @brief Helper class to handle file operations
class CC_DLL FileUtilsApple : public FileUtils
{
public:
FileUtilsApple();
virtual ~FileUtilsApple();
/* override functions */
virtual std::string getWritablePath() const override;
virtual std::string getFullPathForDirectoryAndFilename(const std::string& directory, const std::string& filename) const override;
virtual ValueMap getValueMapFromFile(const std::string& filename) override;
virtual ValueMap getValueMapFromData(const char* filedata, int filesize)override;
virtual bool writeToFile(const ValueMap& dict, const std::string& fullPath) override;
virtual ValueVector getValueVectorFromFile(const std::string& filename) override;
#if CC_FILEUTILS_APPLE_ENABLE_OBJC
void setBundle(NSBundle* bundle);
#endif
virtual bool createDirectory(const std::string& path) override;
private:
virtual bool isFileExistInternal(const std::string& filePath) const override;
virtual bool removeDirectory(const std::string& dirPath) override;
virtual void valueMapCompact(ValueMap& valueMap) override;
virtual void valueVectorCompact(ValueVector& valueVector) override;
struct IMPL;
std::unique_ptr<IMPL> pimpl_;
};
// end of platform group
/// @}
NS_CC_END
#endif // __CC_FILEUTILS_APPLE_H__

View File

@@ -0,0 +1,503 @@
/****************************************************************************
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.
****************************************************************************/
#import <Foundation/Foundation.h>
#include "platform/apple/CCFileUtils-apple.h"
#include <ftw.h>
#include <string>
#include <stack>
#include "platform/CCFileUtils.h"
#include "platform/CCSAXParser.h"
NS_CC_BEGIN
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 cocos2d::Value &value);
static cocos2d::Value convertNSObjectToCCValue(id object);
static void addNSObjectToCCMap(id nsKey, id nsValue, ValueMap& dict);
static void addCCValueToNSDictionary(const std::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 cocos2d::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 cocos2d::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::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
CCASSERT([nsKey isKindOfClass:[NSString class]], "The key should be a string!");
std::string key = [nsKey UTF8String];
dict[key] = convertNSObjectToCCValue(nsValue);
}
static void addCCValueToNSDictionary(const std::string& key, const Value& value, NSMutableDictionary *dict)
{
NSString *NSkey = [NSString stringWithCString:key.c_str() encoding:NSUTF8StringEncoding];
[dict setObject:convertCCValueToNSObject(value) forKey:NSkey];
}
FileUtilsApple::FileUtilsApple() : pimpl_(new IMPL([NSBundle mainBundle])) {
}
FileUtilsApple::~FileUtilsApple() = default;
#if CC_FILEUTILS_APPLE_ENABLE_OBJC
void FileUtilsApple::setBundle(NSBundle* bundle) {
pimpl_->setBundle(bundle);
}
#endif
#pragma mark - FileUtils
static NSFileManager* s_fileManager = [NSFileManager defaultManager];
FileUtils* FileUtils::getInstance()
{
if (s_sharedFileUtils == nullptr)
{
s_sharedFileUtils = new (std::nothrow) FileUtilsApple();
if(!s_sharedFileUtils->init())
{
delete s_sharedFileUtils;
s_sharedFileUtils = nullptr;
CCLOG("ERROR: Could not init CCFileUtilsApple");
}
}
return s_sharedFileUtils;
}
std::string FileUtilsApple::getWritablePath() const
{
if (_writablePath.length())
{
return _writablePath;
}
// save to document folder
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
std::string strRet = [documentsDirectory UTF8String];
strRet.append("/");
return strRet;
}
bool FileUtilsApple::isFileExistInternal(const std::string& filePath) const
{
if (filePath.empty())
{
return false;
}
bool ret = false;
if (filePath[0] != '/')
{
std::string path;
std::string file;
size_t pos = filePath.find_last_of("/");
if (pos != std::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)
{
log("Fail to remove: %s ",fpath);
}
return ret;
}
bool FileUtilsApple::removeDirectory(const std::string& path)
{
if (path.empty())
{
CCLOGERROR("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;
}
std::string FileUtilsApple::getFullPathForDirectoryAndFilename(const std::string& directory, const std::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 != std::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
{
std::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 std::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 std::string &fullPath)
{
return writeValueMapToFile(dict, fullPath);
}
bool FileUtils::writeValueMapToFile(const ValueMap& dict, const std::string& fullPath)
{
valueMapCompact(const_cast<ValueMap&>(dict));
//CCLOG("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 std::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 std::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
std::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 std::string& path)
{
CCASSERT(!path.empty(), "Invalid path");
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)
{
CCLOGERROR("Fail to create directory \"%s\": %s", path.c_str(), [error.localizedDescription UTF8String]);
}
return result;
}
NS_CC_END