CCCanvasRenderingContext2D-apple.mm 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850
  1. #include "platform/CCCanvasRenderingContext2D.h"
  2. #include "base/ccTypes.h"
  3. #include "base/csscolorparser.hpp"
  4. #include "base/ccUTF8.h"
  5. #include "cocos/scripting/js-bindings/jswrapper/SeApi.h"
  6. #include "cocos/scripting/js-bindings/manual/jsb_platform.h"
  7. #import <Foundation/Foundation.h>
  8. #if CC_TARGET_PLATFORM == CC_PLATFORM_MAC
  9. #import <Cocoa/Cocoa.h>
  10. #else
  11. #import <CoreText/CoreText.h>
  12. #define NSBezierPath UIBezierPath
  13. #define NSFont UIFont
  14. #define NSColor UIColor
  15. #define NSSize CGSize
  16. #define NSZeroSize CGSizeZero
  17. #define NSPoint CGPoint
  18. #define NSMakePoint CGPointMake
  19. #endif
  20. #include <regex>
  21. enum class CanvasTextAlign {
  22. LEFT,
  23. CENTER,
  24. RIGHT
  25. };
  26. enum class CanvasTextBaseline {
  27. TOP,
  28. MIDDLE,
  29. BOTTOM
  30. };
  31. @interface CanvasRenderingContext2DImpl : NSObject {
  32. NSFont* _font;
  33. NSMutableDictionary* _tokenAttributesDict;
  34. NSString* _fontName;
  35. CGFloat _fontSize;
  36. CGFloat _width;
  37. CGFloat _height;
  38. CGContextRef _context;
  39. #if CC_TARGET_PLATFORM == CC_PLATFORM_MAC
  40. NSGraphicsContext* _currentGraphicsContext;
  41. NSGraphicsContext* _oldGraphicsContext;
  42. #else
  43. CGContextRef _oldContext;
  44. #endif
  45. CGColorSpaceRef _colorSpace;
  46. cocos2d::Data _imageData;
  47. NSBezierPath* _path;
  48. CanvasTextAlign _textAlign;
  49. CanvasTextBaseline _textBaseLine;
  50. cocos2d::Color4F _fillStyle;
  51. cocos2d::Color4F _strokeStyle;
  52. float _lineWidth;
  53. bool _bold;
  54. }
  55. @property (nonatomic, strong) NSFont* font;
  56. @property (nonatomic, strong) NSMutableDictionary* tokenAttributesDict;
  57. @property (nonatomic, strong) NSString* fontName;
  58. @property (nonatomic, assign) CanvasTextAlign textAlign;
  59. @property (nonatomic, assign) CanvasTextBaseline textBaseLine;
  60. @property (nonatomic, assign) float lineWidth;
  61. @end
  62. @implementation CanvasRenderingContext2DImpl
  63. @synthesize font = _font;
  64. @synthesize tokenAttributesDict = _tokenAttributesDict;
  65. @synthesize fontName = _fontName;
  66. @synthesize textAlign = _textAlign;
  67. @synthesize textBaseLine = _textBaseLine;
  68. @synthesize lineWidth = _lineWidth;
  69. -(id) init {
  70. if (self = [super init]) {
  71. _lineWidth = 0;
  72. _textAlign = CanvasTextAlign::LEFT;
  73. _textBaseLine = CanvasTextBaseline::BOTTOM;
  74. _width = _height = 0;
  75. _context = nil;
  76. _colorSpace = nil;
  77. #if CC_TARGET_PLATFORM == CC_PLATFORM_MAC
  78. _currentGraphicsContext = nil;
  79. _oldGraphicsContext = nil;
  80. #endif
  81. _path = [NSBezierPath bezierPath];
  82. [_path retain];
  83. [self updateFontWithName:@"Arial" fontSize:30 bold:false];
  84. }
  85. return self;
  86. }
  87. -(void) dealloc {
  88. self.font = nil;
  89. self.tokenAttributesDict = nil;
  90. self.fontName = nil;
  91. CGColorSpaceRelease(_colorSpace);
  92. // release the context
  93. CGContextRelease(_context);
  94. [_path release];
  95. #if CC_TARGET_PLATFORM == CC_PLATFORM_MAC
  96. [_currentGraphicsContext release];
  97. #endif
  98. [super dealloc];
  99. }
  100. #if CC_TARGET_PLATFORM == CC_PLATFORM_MAC
  101. -(NSFont*) _createSystemFont {
  102. NSFontTraitMask mask = NSUnitalicFontMask;
  103. if (_bold) {
  104. mask |= NSBoldFontMask;
  105. }
  106. else {
  107. mask |= NSUnboldFontMask;
  108. }
  109. NSFont* font = [[NSFontManager sharedFontManager]
  110. fontWithFamily:_fontName
  111. traits:mask
  112. weight:0
  113. size:_fontSize];
  114. if (font == nil) {
  115. const auto& familyMap = getFontFamilyNameMap();
  116. auto iter = familyMap.find([_fontName UTF8String]);
  117. if (iter != familyMap.end()) {
  118. font = [[NSFontManager sharedFontManager]
  119. fontWithFamily: [NSString stringWithUTF8String:iter->second.c_str()]
  120. traits: mask
  121. weight: 0
  122. size: _fontSize];
  123. }
  124. }
  125. if (font == nil) {
  126. font = [[NSFontManager sharedFontManager]
  127. fontWithFamily: @"Arial"
  128. traits: mask
  129. weight: 0
  130. size: _fontSize];
  131. }
  132. return font;
  133. }
  134. #else
  135. -(UIFont*) _createSystemFont {
  136. UIFont* font = nil;
  137. if (_bold) {
  138. font = [UIFont fontWithName:[_fontName stringByAppendingString:@"-Bold"] size:_fontSize];
  139. }
  140. else {
  141. font = [UIFont fontWithName:_fontName size:_fontSize];
  142. }
  143. if (font == nil) {
  144. const auto& familyMap = getFontFamilyNameMap();
  145. auto iter = familyMap.find([_fontName UTF8String]);
  146. if (iter != familyMap.end()) {
  147. font = [UIFont fontWithName:[NSString stringWithUTF8String:iter->second.c_str()] size:_fontSize];
  148. }
  149. }
  150. if (font == nil) {
  151. if (_bold) {
  152. font = [UIFont boldSystemFontOfSize:_fontSize];
  153. } else {
  154. font = [UIFont systemFontOfSize:_fontSize];
  155. }
  156. }
  157. return font;
  158. }
  159. #endif
  160. -(void) updateFontWithName: (NSString*)fontName fontSize: (CGFloat)fontSize bold: (bool)bold{
  161. _fontSize = fontSize;
  162. _bold = bold;
  163. self.fontName = fontName;
  164. self.font = [self _createSystemFont];
  165. NSMutableParagraphStyle* paragraphStyle = [[[NSMutableParagraphStyle alloc] init] autorelease];
  166. paragraphStyle.lineBreakMode = NSLineBreakByTruncatingTail;
  167. [paragraphStyle setAlignment:NSTextAlignmentCenter];
  168. // color
  169. NSColor* foregroundColor = [NSColor colorWithRed:1.0f
  170. green:1.0f
  171. blue:1.0f
  172. alpha:1.0f];
  173. // attribute
  174. self.tokenAttributesDict = [NSMutableDictionary dictionaryWithObjectsAndKeys:
  175. foregroundColor, NSForegroundColorAttributeName,
  176. _font, NSFontAttributeName,
  177. paragraphStyle, NSParagraphStyleAttributeName, nil];
  178. }
  179. -(void) recreateBufferWithWidth:(NSInteger) width height:(NSInteger) height {
  180. _width = width = width > 0 ? width : 1;
  181. _height = height = height > 0 ? height : 1;
  182. NSUInteger textureSize = width * height * 4;
  183. unsigned char* data = (unsigned char*)malloc(sizeof(unsigned char) * textureSize);
  184. memset(data, 0, textureSize);
  185. _imageData.fastSet(data, textureSize);
  186. if (_context != nil)
  187. {
  188. CGContextRelease(_context);
  189. _context = nil;
  190. }
  191. #if CC_TARGET_PLATFORM == CC_PLATFORM_MAC
  192. if (_currentGraphicsContext != nil)
  193. {
  194. [_currentGraphicsContext release];
  195. _currentGraphicsContext = nil;
  196. }
  197. #endif
  198. // draw text
  199. _colorSpace = CGColorSpaceCreateDeviceRGB();
  200. _context = CGBitmapContextCreate(data,
  201. width,
  202. height,
  203. 8,
  204. width * 4,
  205. _colorSpace,
  206. kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
  207. if (nil == _context)
  208. {
  209. CGColorSpaceRelease(_colorSpace); //REFINE: HOWTO RELEASE?
  210. _colorSpace = nil;
  211. }
  212. #if CC_TARGET_PLATFORM == CC_PLATFORM_MAC
  213. _currentGraphicsContext = [NSGraphicsContext graphicsContextWithCGContext:_context flipped: NO];
  214. [_currentGraphicsContext retain];
  215. #else
  216. // move Y rendering to the top of the image
  217. CGContextTranslateCTM(_context, 0.0f, _height);
  218. //NOTE: NSString draws in UIKit referential i.e. renders upside-down compared to CGBitmapContext referential
  219. CGContextScaleCTM(_context, 1.0f, -1.0f);
  220. #endif
  221. }
  222. -(NSSize) measureText:(NSString*) text {
  223. NSAttributedString* stringWithAttributes = [[[NSAttributedString alloc] initWithString:text
  224. attributes:_tokenAttributesDict] autorelease];
  225. NSSize textRect = NSZeroSize;
  226. textRect.width = CGFLOAT_MAX;
  227. textRect.height = CGFLOAT_MAX;
  228. NSSize dim = [stringWithAttributes boundingRectWithSize:textRect options:(NSStringDrawingOptions)(NSStringDrawingUsesLineFragmentOrigin) context:nil].size;
  229. return dim;
  230. }
  231. -(NSPoint) convertDrawPoint:(NSPoint) point text:(NSString*) text {
  232. // The parameter 'point' is located at left-bottom position.
  233. // Need to adjust 'point' according 'text align' & 'text base line'.
  234. NSSize textSize = [self measureText:text];
  235. if (_textAlign == CanvasTextAlign::CENTER)
  236. {
  237. point.x -= textSize.width / 2.0f;
  238. }
  239. else if (_textAlign == CanvasTextAlign::RIGHT)
  240. {
  241. point.x -= textSize.width;
  242. }
  243. if (_textBaseLine == CanvasTextBaseline::TOP)
  244. {
  245. point.y += _fontSize;
  246. }
  247. else if (_textBaseLine == CanvasTextBaseline::MIDDLE)
  248. {
  249. point.y += _fontSize / 2.0f;
  250. }
  251. // Since the web platform cannot get the baseline of the font, an additive offset is performed for all platforms.
  252. // That's why we should add baseline back again on other platforms
  253. #if CC_TARGET_PLATFORM == CC_PLATFORM_MAC
  254. point.y -= _font.descender;
  255. // The origin on macOS is bottom-left by default, so we need to convert y from top-left origin to bottom-left origin.
  256. point.y = _height - point.y;
  257. #else
  258. point.y -= _font.ascender;
  259. #endif
  260. return point;
  261. }
  262. -(void) fillText:(NSString*) text x:(CGFloat) x y:(CGFloat) y maxWidth:(CGFloat) maxWidth {
  263. if (text.length == 0)
  264. return;
  265. NSPoint drawPoint = [self convertDrawPoint:NSMakePoint(x, y) text:text];
  266. NSMutableParagraphStyle* paragraphStyle = [[[NSMutableParagraphStyle alloc] init] autorelease];
  267. paragraphStyle.lineBreakMode = NSLineBreakByTruncatingTail;
  268. [_tokenAttributesDict removeObjectForKey:NSStrokeColorAttributeName];
  269. [_tokenAttributesDict setObject:paragraphStyle forKey:NSParagraphStyleAttributeName];
  270. [_tokenAttributesDict setObject:[NSColor colorWithRed:_fillStyle.r green:_fillStyle.g blue:_fillStyle.b alpha:_fillStyle.a]
  271. forKey:NSForegroundColorAttributeName];
  272. [self saveContext];
  273. // text color
  274. CGContextSetRGBFillColor(_context, _fillStyle.r, _fillStyle.g, _fillStyle.b, _fillStyle.a);
  275. CGContextSetShouldSubpixelQuantizeFonts(_context, false);
  276. CGContextBeginTransparencyLayerWithRect(_context, CGRectMake(0, 0, _width, _height), nullptr);
  277. CGContextSetTextDrawingMode(_context, kCGTextFill);
  278. NSAttributedString *stringWithAttributes =[[[NSAttributedString alloc] initWithString:text
  279. attributes:_tokenAttributesDict] autorelease];
  280. [stringWithAttributes drawAtPoint:drawPoint];
  281. CGContextEndTransparencyLayer(_context);
  282. [self restoreContext];
  283. }
  284. -(void) strokeText:(NSString*) text x:(CGFloat) x y:(CGFloat) y maxWidth:(CGFloat) maxWidth {
  285. if (text.length == 0)
  286. return;
  287. NSPoint drawPoint = [self convertDrawPoint:NSMakePoint(x, y) text:text];
  288. NSMutableParagraphStyle* paragraphStyle = [[[NSMutableParagraphStyle alloc] init] autorelease];
  289. paragraphStyle.lineBreakMode = NSLineBreakByTruncatingTail;
  290. [_tokenAttributesDict removeObjectForKey:NSForegroundColorAttributeName];
  291. [_tokenAttributesDict setObject:paragraphStyle forKey:NSParagraphStyleAttributeName];
  292. [_tokenAttributesDict setObject:[NSColor colorWithRed:_strokeStyle.r
  293. green:_strokeStyle.g
  294. blue:_strokeStyle.b
  295. alpha:_strokeStyle.a] forKey:NSStrokeColorAttributeName];
  296. [self saveContext];
  297. // text color
  298. CGContextSetRGBStrokeColor(_context, _strokeStyle.r, _strokeStyle.g, _strokeStyle.b, _strokeStyle.a);
  299. CGContextSetRGBFillColor(_context, _fillStyle.r, _fillStyle.g, _fillStyle.b, _fillStyle.a);
  300. CGContextSetLineWidth(_context, _lineWidth);
  301. CGContextSetLineJoin(_context, kCGLineJoinRound);
  302. CGContextSetShouldSubpixelQuantizeFonts(_context, false);
  303. CGContextBeginTransparencyLayerWithRect(_context, CGRectMake(0, 0, _width, _height), nullptr);
  304. CGContextSetTextDrawingMode(_context, kCGTextStroke);
  305. NSAttributedString *stringWithAttributes =[[[NSAttributedString alloc] initWithString:text
  306. attributes:_tokenAttributesDict] autorelease];
  307. [stringWithAttributes drawAtPoint:drawPoint];
  308. CGContextEndTransparencyLayer(_context);
  309. [self restoreContext];
  310. }
  311. -(void) setFillStyleWithRed:(CGFloat) r green:(CGFloat) g blue:(CGFloat) b alpha:(CGFloat) a {
  312. _fillStyle.r = r;
  313. _fillStyle.g = g;
  314. _fillStyle.b = b;
  315. _fillStyle.a = a;
  316. }
  317. -(void) setStrokeStyleWithRed:(CGFloat) r green:(CGFloat) g blue:(CGFloat) b alpha:(CGFloat) a {
  318. _strokeStyle.r = r;
  319. _strokeStyle.g = g;
  320. _strokeStyle.b = b;
  321. _strokeStyle.a = a;
  322. }
  323. -(const cocos2d::Data&) getDataRef {
  324. return _imageData;
  325. }
  326. -(void) clearRect:(CGRect) rect {
  327. if (_imageData.isNull())
  328. return;
  329. rect.origin.x = floor(rect.origin.x);
  330. rect.origin.y = floor(rect.origin.y);
  331. rect.size.width = floor(rect.size.width);
  332. rect.size.height = floor(rect.size.height);
  333. if (rect.origin.x < 0) rect.origin.x = 0;
  334. if (rect.origin.y < 0) rect.origin.y = 0;
  335. if (rect.size.width < 1 || rect.size.height < 1)
  336. return;
  337. //REFINE:
  338. // assert(rect.origin.x == 0 && rect.origin.y == 0);
  339. memset((void*)_imageData.getBytes(), 0x00, _imageData.getSize());
  340. }
  341. -(void) fillRect:(CGRect) rect {
  342. [self saveContext];
  343. NSColor* color = [NSColor colorWithRed:_fillStyle.r green:_fillStyle.g blue:_fillStyle.b alpha:_fillStyle.a];
  344. [color setFill];
  345. #if CC_TARGET_PLATFORM == CC_PLATFORM_MAC
  346. CGRect tmpRect = CGRectMake(rect.origin.x, _height - rect.origin.y - rect.size.height, rect.size.width, rect.size.height);
  347. [NSBezierPath fillRect:tmpRect];
  348. #else
  349. NSBezierPath* path = [NSBezierPath bezierPathWithRect:rect];
  350. [path fill];
  351. #endif
  352. [self restoreContext];
  353. }
  354. -(void) saveContext {
  355. #if CC_TARGET_PLATFORM == CC_PLATFORM_MAC
  356. // save the old graphics context
  357. _oldGraphicsContext = [NSGraphicsContext currentContext];
  358. // store the current context
  359. [NSGraphicsContext setCurrentContext:_currentGraphicsContext];
  360. // push graphics state to stack
  361. [NSGraphicsContext saveGraphicsState];
  362. [[NSGraphicsContext currentContext] setShouldAntialias:YES];
  363. #else
  364. // save the old graphics context
  365. _oldContext = UIGraphicsGetCurrentContext();
  366. // store the current context
  367. UIGraphicsPushContext(_context);
  368. CGContextSaveGState(_context);
  369. #endif
  370. }
  371. -(void) restoreContext {
  372. #if CC_TARGET_PLATFORM == CC_PLATFORM_MAC
  373. // pop the context
  374. [NSGraphicsContext restoreGraphicsState];
  375. // reset the old graphics context
  376. [NSGraphicsContext setCurrentContext:_oldGraphicsContext];
  377. _oldGraphicsContext = nil;
  378. #else
  379. // pop the context
  380. CGContextRestoreGState(_context);
  381. // reset the old graphics context
  382. UIGraphicsPopContext();
  383. _oldContext = nil;
  384. #endif
  385. }
  386. -(void) beginPath {
  387. }
  388. -(void) stroke {
  389. NSColor* color = [NSColor colorWithRed:_strokeStyle.r green:_strokeStyle.g blue:_strokeStyle.b alpha:_strokeStyle.a];
  390. [color setStroke];
  391. [_path setLineWidth: _lineWidth];
  392. [_path stroke];
  393. }
  394. -(void) moveToX: (float) x y:(float) y {
  395. #if CC_TARGET_PLATFORM == CC_PLATFORM_MAC
  396. [_path moveToPoint: NSMakePoint(x, _height - y)];
  397. #else
  398. [_path moveToPoint: NSMakePoint(x, y)];
  399. #endif
  400. }
  401. -(void) lineToX: (float) x y:(float) y {
  402. #if CC_TARGET_PLATFORM == CC_PLATFORM_MAC
  403. [_path lineToPoint: NSMakePoint(x, _height - y)];
  404. #else
  405. [_path addLineToPoint: NSMakePoint(x, y)];
  406. #endif
  407. }
  408. @end
  409. NS_CC_BEGIN
  410. CanvasGradient::CanvasGradient()
  411. {
  412. SE_LOGD("CanvasGradient constructor: %p\n", this);
  413. }
  414. CanvasGradient::~CanvasGradient()
  415. {
  416. SE_LOGD("CanvasGradient destructor: %p\n", this);
  417. }
  418. void CanvasGradient::addColorStop(float offset, const std::string& color)
  419. {
  420. SE_LOGD("CanvasGradient::addColorStop: %p\n", this);
  421. }
  422. // CanvasRenderingContext2D
  423. namespace
  424. {
  425. #define CLAMP(V, HI) std::min( (V), (HI) )
  426. void unMultiplyAlpha(unsigned char* ptr, ssize_t size)
  427. {
  428. float alpha;
  429. for (int i = 0; i < size; i += 4)
  430. {
  431. alpha = (float)ptr[i + 3];
  432. if (alpha > 0)
  433. {
  434. ptr[i] = CLAMP((int)((float)ptr[i] / alpha * 255), 255);
  435. ptr[i+1] = CLAMP((int)((float)ptr[i+1] / alpha * 255), 255);
  436. ptr[i+2] = CLAMP((int)((float)ptr[i+2] / alpha * 255), 255);
  437. }
  438. }
  439. }
  440. }
  441. #define SEND_DATA_TO_JS(CB, IMPL, PREMULTIPLY) \
  442. if (CB) \
  443. { \
  444. Data data([IMPL getDataRef]); \
  445. if (!PREMULTIPLY) \
  446. { \
  447. unMultiplyAlpha(data.getBytes(), data.getSize() ); \
  448. } \
  449. CB(data); \
  450. }
  451. CanvasRenderingContext2D::CanvasRenderingContext2D(float width, float height)
  452. : __width(width)
  453. , __height(height)
  454. {
  455. // SE_LOGD("CanvasRenderingContext2D constructor: %p, width: %f, height: %f\n", this, width, height);
  456. _impl = [[CanvasRenderingContext2DImpl alloc] init];
  457. [_impl recreateBufferWithWidth:width height:height];
  458. }
  459. CanvasRenderingContext2D::~CanvasRenderingContext2D()
  460. {
  461. // SE_LOGD("CanvasRenderingContext2D destructor: %p\n", this);
  462. [_impl release];
  463. }
  464. void CanvasRenderingContext2D::recreateBufferIfNeeded()
  465. {
  466. if (_isBufferSizeDirty)
  467. {
  468. _isBufferSizeDirty = false;
  469. // SE_LOGD("CanvasRenderingContext2D::recreateBufferIfNeeded %p, w: %f, h:%f\n", this, __width, __height);
  470. [_impl recreateBufferWithWidth: __width height:__height];
  471. SEND_DATA_TO_JS(_canvasBufferUpdatedCB, _impl, _premultiply);
  472. }
  473. }
  474. void CanvasRenderingContext2D::clearRect(float x, float y, float width, float height)
  475. {
  476. // SE_LOGD("CanvasGradient::clearRect: %p, %f, %f, %f, %f\n", this, x, y, width, height);
  477. recreateBufferIfNeeded();
  478. [_impl clearRect:CGRectMake(x, y, width, height)];
  479. }
  480. void CanvasRenderingContext2D::fillRect(float x, float y, float width, float height)
  481. {
  482. recreateBufferIfNeeded();
  483. [_impl fillRect:CGRectMake(x, y, width, height)];
  484. SEND_DATA_TO_JS(_canvasBufferUpdatedCB, _impl, _premultiply);
  485. }
  486. void CanvasRenderingContext2D::fillText(const std::string& text, float x, float y, float maxWidth)
  487. {
  488. // SE_LOGD("CanvasRenderingContext2D(%p)::fillText: %s, %f, %f, %f\n", this, text.c_str(), x, y, maxWidth);
  489. if (text.empty())
  490. return;
  491. recreateBufferIfNeeded();
  492. auto textUtf8 = [NSString stringWithUTF8String:text.c_str()];
  493. if(textUtf8 == nullptr) {
  494. SE_LOGE("CanvasRenderingContext2D::fillText failed to convert text to UTF8\n text:\"%s\"", text.c_str());
  495. return;
  496. }
  497. [_impl fillText:textUtf8 x:x y:y maxWidth:maxWidth];
  498. SEND_DATA_TO_JS(_canvasBufferUpdatedCB, _impl, _premultiply);
  499. }
  500. void CanvasRenderingContext2D::strokeText(const std::string& text, float x, float y, float maxWidth)
  501. {
  502. // SE_LOGD("CanvasRenderingContext2D(%p)::strokeText: %s, %f, %f, %f\n", this, text.c_str(), x, y, maxWidth);
  503. if (text.empty())
  504. return;
  505. recreateBufferIfNeeded();
  506. auto textUtf8 = [NSString stringWithUTF8String:text.c_str()];
  507. if(textUtf8 == nullptr) {
  508. SE_LOGE("CanvasRenderingContext2D::strokeText failed to convert text to UTF8\n text:\"%s\"", text.c_str());
  509. return;
  510. }
  511. [_impl strokeText:textUtf8 x:x y:y maxWidth:maxWidth];
  512. SEND_DATA_TO_JS(_canvasBufferUpdatedCB, _impl, _premultiply);
  513. }
  514. cocos2d::Size CanvasRenderingContext2D::measureText(const std::string& text)
  515. {
  516. NSString *str =[NSString stringWithUTF8String:text.c_str()];
  517. if(str == nil) {
  518. std::string textNew;
  519. cocos2d::StringUtils::UTF8LooseFix(text, textNew);
  520. str = [NSString stringWithUTF8String:textNew.c_str()];
  521. }
  522. CGSize size = [_impl measureText: str];
  523. return cocos2d::Size(size.width, size.height);
  524. }
  525. CanvasGradient* CanvasRenderingContext2D::createLinearGradient(float x0, float y0, float x1, float y1)
  526. {
  527. return nullptr;
  528. }
  529. void CanvasRenderingContext2D::save()
  530. {
  531. [_impl saveContext];
  532. }
  533. void CanvasRenderingContext2D::beginPath()
  534. {
  535. [_impl beginPath];
  536. }
  537. void CanvasRenderingContext2D::closePath()
  538. {
  539. //SE_LOGE("%s isn't implemented!\n", __FUNCTION__);
  540. }
  541. void CanvasRenderingContext2D::moveTo(float x, float y)
  542. {
  543. [_impl moveToX:x y:y];
  544. }
  545. void CanvasRenderingContext2D::lineTo(float x, float y)
  546. {
  547. [_impl lineToX:x y:y];
  548. }
  549. void CanvasRenderingContext2D::stroke()
  550. {
  551. [_impl stroke];
  552. SEND_DATA_TO_JS(_canvasBufferUpdatedCB, _impl, _premultiply);
  553. }
  554. void CanvasRenderingContext2D::fill()
  555. {
  556. //SE_LOGE("%s isn't implemented!\n", __FUNCTION__);
  557. }
  558. void CanvasRenderingContext2D::rect(float x, float y, float w, float h)
  559. {
  560. //SE_LOGE("%s isn't implemented!\n", __FUNCTION__);
  561. }
  562. void CanvasRenderingContext2D::restore()
  563. {
  564. [_impl restoreContext];
  565. }
  566. void CanvasRenderingContext2D::setCanvasBufferUpdatedCallback(const CanvasBufferUpdatedCallback& cb)
  567. {
  568. _canvasBufferUpdatedCB = cb;
  569. }
  570. void CanvasRenderingContext2D::setPremultiply(bool multiply)
  571. {
  572. _premultiply = multiply;
  573. }
  574. void CanvasRenderingContext2D::set__width(float width)
  575. {
  576. // SE_LOGD("CanvasRenderingContext2D::set__width: %f\n", width);
  577. __width = width;
  578. _isBufferSizeDirty = true;
  579. recreateBufferIfNeeded();
  580. }
  581. void CanvasRenderingContext2D::set__height(float height)
  582. {
  583. // SE_LOGD("CanvasRenderingContext2D::set__height: %f\n", height);
  584. __height = height;
  585. _isBufferSizeDirty = true;
  586. recreateBufferIfNeeded();
  587. }
  588. void CanvasRenderingContext2D::set_lineWidth(float lineWidth)
  589. {
  590. _lineWidth = lineWidth;
  591. _impl.lineWidth = _lineWidth;
  592. }
  593. void CanvasRenderingContext2D::set_lineCap(const std::string& lineCap)
  594. {
  595. //SE_LOGE("%s isn't implemented!\n", __FUNCTION__);
  596. }
  597. void CanvasRenderingContext2D::set_lineJoin(const std::string& lineJoin)
  598. {
  599. //SE_LOGE("%s isn't implemented!\n", __FUNCTION__);
  600. }
  601. void CanvasRenderingContext2D::set_font(const std::string& font)
  602. {
  603. if (_font != font)
  604. {
  605. _font = font;
  606. std::string boldStr;
  607. std::string fontName = "Arial";
  608. std::string fontSizeStr = "30";
  609. // support get font name from `60px American` or `60px "American abc-abc_abc"`
  610. std::regex re("(bold)?\\s*((\\d+)([\\.]\\d+)?)px\\s+([\\w-]+|\"[\\w -]+\"$)");
  611. std::match_results<std::string::const_iterator> results;
  612. if (std::regex_search(_font.cbegin(), _font.cend(), results, re))
  613. {
  614. boldStr = results[1].str();
  615. fontSizeStr = results[2].str();
  616. fontName = results[5].str();
  617. }
  618. CGFloat fontSize = atof(fontSizeStr.c_str());
  619. [_impl updateFontWithName:[NSString stringWithUTF8String:fontName.c_str()] fontSize:fontSize bold:!boldStr.empty()];
  620. }
  621. }
  622. void CanvasRenderingContext2D::set_textAlign(const std::string& textAlign)
  623. {
  624. // SE_LOGD("CanvasRenderingContext2D::set_textAlign: %s\n", textAlign.c_str());
  625. if (textAlign == "left")
  626. {
  627. _impl.textAlign = CanvasTextAlign::LEFT;
  628. }
  629. else if (textAlign == "center" || textAlign == "middle")
  630. {
  631. _impl.textAlign = CanvasTextAlign::CENTER;
  632. }
  633. else if (textAlign == "right")
  634. {
  635. _impl.textAlign = CanvasTextAlign::RIGHT;
  636. }
  637. else
  638. {
  639. assert(false);
  640. }
  641. }
  642. void CanvasRenderingContext2D::set_textBaseline(const std::string& textBaseline)
  643. {
  644. // SE_LOGD("CanvasRenderingContext2D::set_textBaseline: %s\n", textBaseline.c_str());
  645. if (textBaseline == "top")
  646. {
  647. _impl.textBaseLine = CanvasTextBaseline::TOP;
  648. }
  649. else if (textBaseline == "middle")
  650. {
  651. _impl.textBaseLine = CanvasTextBaseline::MIDDLE;
  652. }
  653. else if (textBaseline == "bottom" || textBaseline == "alphabetic") //REFINE:, how to deal with alphabetic, currently we handle it as bottom mode.
  654. {
  655. _impl.textBaseLine = CanvasTextBaseline::BOTTOM;
  656. }
  657. else
  658. {
  659. assert(false);
  660. }
  661. }
  662. void CanvasRenderingContext2D::set_fillStyle(const std::string& fillStyle)
  663. {
  664. CSSColorParser::Color color = CSSColorParser::parse(fillStyle);
  665. [_impl setFillStyleWithRed:color.r/255.0f green:color.g/255.0f blue:color.b/255.0f alpha:color.a];
  666. // SE_LOGD("CanvasRenderingContext2D::set_fillStyle: %s, (%d, %d, %d, %f)\n", fillStyle.c_str(), color.r, color.g, color.b, color.a);
  667. }
  668. void CanvasRenderingContext2D::set_strokeStyle(const std::string& strokeStyle)
  669. {
  670. CSSColorParser::Color color = CSSColorParser::parse(strokeStyle);
  671. [_impl setStrokeStyleWithRed:color.r/255.0f green:color.g/255.0f blue:color.b/255.0f alpha:color.a];
  672. }
  673. void CanvasRenderingContext2D::set_globalCompositeOperation(const std::string& globalCompositeOperation)
  674. {
  675. //SE_LOGE("%s isn't implemented!\n", __FUNCTION__);
  676. }
  677. void CanvasRenderingContext2D::_fillImageData(const Data& imageData, float imageWidth, float imageHeight, float offsetX, float offsetY)
  678. {
  679. //SE_LOGE("%s isn't implemented!\n", __FUNCTION__);
  680. }
  681. // transform
  682. void CanvasRenderingContext2D::translate(float x, float y)
  683. {
  684. //SE_LOGE("%s isn't implemented!\n", __FUNCTION__);
  685. }
  686. void CanvasRenderingContext2D::scale(float x, float y)
  687. {
  688. //SE_LOGE("%s isn't implemented!\n", __FUNCTION__);
  689. }
  690. void CanvasRenderingContext2D::rotate(float angle)
  691. {
  692. //SE_LOGE("%s isn't implemented!\n", __FUNCTION__);
  693. }
  694. void CanvasRenderingContext2D::transform(float a, float b, float c, float d, float e, float f)
  695. {
  696. //SE_LOGE("%s isn't implemented!\n", __FUNCTION__);
  697. }
  698. void CanvasRenderingContext2D::setTransform(float a, float b, float c, float d, float e, float f)
  699. {
  700. //SE_LOGE("%s isn't implemented!\n", __FUNCTION__);
  701. }
  702. NS_CC_END