#include "platform/CCCanvasRenderingContext2D.h" #include "base/ccTypes.h" #include "base/csscolorparser.hpp" #include "cocos/scripting/js-bindings/jswrapper/SeApi.h" #include "cocos/scripting/js-bindings/manual/jsb_platform.h" #include "platform/CCFileUtils.h" #include using namespace cocos2d; enum class CanvasTextAlign { LEFT, CENTER, RIGHT }; enum class CanvasTextBaseline { TOP, MIDDLE, BOTTOM }; namespace { void fillRectWithColor(uint8_t* buf, uint32_t totalWidth, uint32_t totalHeight, uint32_t x, uint32_t y, uint32_t width, uint32_t height, uint8_t r, uint8_t g, uint8_t b, uint8_t a) { assert(x + width <= totalWidth); assert(y + height <= totalHeight); uint32_t y0 = y; uint32_t y1 = y + height; uint8_t* p; for (uint32_t offsetY = y0; offsetY < y1; ++offsetY) { for (uint32_t offsetX = x; offsetX < (x + width); ++offsetX) { p = buf + (totalWidth * offsetY + offsetX) * 4; *p++ = r; *p++ = g; *p++ = b; *p++ = a; } } } } class CanvasRenderingContext2DImpl { public: CanvasRenderingContext2DImpl() : _DC(nullptr) , _bmp(nullptr) , _font((HFONT)GetStockObject(DEFAULT_GUI_FONT)) , _wnd(nullptr) , _savedDC(0) { _wnd = nullptr; HDC hdc = GetDC(_wnd); _DC = CreateCompatibleDC(hdc); ReleaseDC(_wnd, hdc); } ~CanvasRenderingContext2DImpl() { _deleteBitmap(); _removeCustomFont(); if (_DC) DeleteDC(_DC); } void recreateBuffer(float w, float h) { _bufferWidth = w; _bufferHeight = h; if (_bufferWidth < 1.0f || _bufferHeight < 1.0f) { _deleteBitmap(); return; } int textureSize = _bufferWidth * _bufferHeight * 4; uint8_t* data = (uint8_t*)malloc(sizeof(uint8_t) * textureSize); memset(data, 0x00, textureSize); _imageData.fastSet(data, textureSize); _prepareBitmap(_bufferWidth, _bufferHeight); } void beginPath() { // called: set_lineWidth() -> beginPath() -> moveTo() -> lineTo() -> stroke(), when draw line _hpen = CreatePen(PS_SOLID, _lineWidth, RGB(255, 255, 255)); // the return value of SelectObject is a handle to the object being replaced, so we should delete them to avoid memory leak HGDIOBJ hOldPen = SelectObject(_DC, _hpen); HGDIOBJ hOldBmp = SelectObject(_DC, _bmp); DeleteObject(hOldPen); DeleteObject(hOldBmp); SetBkMode(_DC, TRANSPARENT); } void closePath() { } void moveTo(float x, float y) { MoveToEx(_DC, x, -(y - _bufferHeight - _fontSize), nullptr); } void lineTo(float x, float y) { LineTo(_DC, x, -(y - _bufferHeight - _fontSize)); } void stroke() { DeleteObject(_hpen); if (_bufferWidth < 1.0f || _bufferHeight < 1.0f) return; _fillTextureData(); } void saveContext() { _savedDC = SaveDC(_DC); } void restoreContext() { BOOL ret = RestoreDC(_DC, _savedDC); if (0 == ret) { SE_LOGD("CanvasRenderingContext2DImpl restore context failed.\n"); } } void clearRect(float x, float y, float w, float h) { if (_bufferWidth < 1.0f || _bufferHeight < 1.0f) return; if (_imageData.isNull()) return; recreateBuffer(w, h); } void fillRect(float x, float y, float w, float h) { if (_bufferWidth < 1.0f || _bufferHeight < 1.0f) return; //not filled all Bits in buffer? the buffer length is _bufferWidth * _bufferHeight * 4, but it filled _bufferWidth * _bufferHeight * 3? uint8_t* buffer = _imageData.getBytes(); if (buffer) { uint8_t r = _fillStyle.r * 255.0f; uint8_t g = _fillStyle.g * 255.0f; uint8_t b = _fillStyle.b * 255.0f; uint8_t a = _fillStyle.a * 255.0f; fillRectWithColor(buffer, (uint32_t)_bufferWidth, (uint32_t)_bufferHeight, (uint32_t)x, (uint32_t)y, (uint32_t)w, (uint32_t)h, r, g, b, a); } } void fillText(const std::string& text, float x, float y, float maxWidth) { if (text.empty() || _bufferWidth < 1.0f || _bufferHeight < 1.0f) return; SIZE textSize = { 0, 0 }; Point offsetPoint = _convertDrawPoint(Point(x, y), text); _drawText(text, (int)offsetPoint.x, (int)offsetPoint.y); _fillTextureData(); } void strokeText(const std::string& text, float x, float y, float maxWidth) { if (text.empty() || _bufferWidth < 1.0f || _bufferHeight < 1.0f) return; // REFINE } cocos2d::Size measureText(const std::string& text) { if (text.empty()) return Size(0.0f, 0.0f); int bufferLen = 0; wchar_t * pwszBuffer = _utf8ToUtf16(text, &bufferLen); SIZE size = _sizeWithText(pwszBuffer, bufferLen); //SE_LOGD("CanvasRenderingContext2DImpl::measureText: %s, %d, %d\n", text.c_str(), size.cx, size.cy); CC_SAFE_DELETE_ARRAY(pwszBuffer); return Size(size.cx, size.cy); } void updateFont(const std::string& fontName, float fontSize, bool bold = false) { do { _fontName = fontName; _fontSize = fontSize; std::string fontPath; LOGFONTA tFont = { 0 }; if (!_fontName.empty()) { // firstly, try to create font from ttf file const auto& fontInfoMap = getFontFamilyNameMap(); auto iter = fontInfoMap.find(_fontName); if (iter != fontInfoMap.end()) { fontPath = iter->second; std::string tmpFontPath = fontPath; int nFindPos = tmpFontPath.rfind("/"); tmpFontPath = &tmpFontPath[nFindPos + 1]; nFindPos = tmpFontPath.rfind("."); // IDEA: draw ttf failed if font file name not equal font face name // for example: "DejaVuSansMono-Oblique" not equal "DejaVu Sans Mono" when using DejaVuSansMono-Oblique.ttf _fontName = tmpFontPath.substr(0, nFindPos); } else { auto nFindPos = fontName.rfind("/"); if (nFindPos != fontName.npos) { if (fontName.length() == nFindPos + 1) { _fontName = ""; } else { _fontName = &_fontName[nFindPos + 1]; } } } tFont.lfCharSet = DEFAULT_CHARSET; strcpy_s(tFont.lfFaceName, LF_FACESIZE, _fontName.c_str()); } if (_fontSize) tFont.lfHeight = -_fontSize; if (bold) tFont.lfWeight = FW_BOLD; else tFont.lfWeight = FW_NORMAL; // disable Cleartype tFont.lfQuality = ANTIALIASED_QUALITY; // delete old font _removeCustomFont(); if (fontPath.size() > 0) { _curFontPath = fontPath; wchar_t * pwszBuffer = _utf8ToUtf16(_curFontPath); if (pwszBuffer) { if (AddFontResource(pwszBuffer)) { SendMessage(_wnd, WM_FONTCHANGE, 0, 0); } delete[] pwszBuffer; pwszBuffer = nullptr; } } // create new font _font = CreateFontIndirectA(&tFont); if (!_font) { // create failed, use default font SE_LOGE("Failed to create custom font(font name: %s, font size: %f), use default font.\n", _fontName.c_str(), fontSize); break; } else { SelectObject(_DC, _font); SendMessage(_wnd, WM_FONTCHANGE, 0, 0); } } while (0); } void setTextAlign(CanvasTextAlign align) { _textAlign = align; } void setTextBaseline(CanvasTextBaseline baseline) { _textBaseLine = baseline; } void setFillStyle(float r, float g, float b, float a) { _fillStyle.r = r; _fillStyle.g = g; _fillStyle.b = b; _fillStyle.a = a; } void setStrokeStyle(float r, float g, float b, float a) { _strokeStyle.r = r; _strokeStyle.g = g; _strokeStyle.b = b; _strokeStyle.a = a; } void setLineWidth(float lineWidth) { _lineWidth = lineWidth; } void setPremultiply(bool multiply) { _premultiply = multiply; } const Data& getDataRef() const { return _imageData; } HDC _DC; HBITMAP _bmp; private: Data _imageData; HFONT _font; HWND _wnd; HPEN _hpen; PAINTSTRUCT _paintStruct; std::string _curFontPath; int _savedDC; float _lineWidth = 0.0f; float _bufferWidth = 0.0f; float _bufferHeight = 0.0f; bool _premultiply = true; std::string _fontName; int _fontSize; SIZE _textSize; CanvasTextAlign _textAlign; CanvasTextBaseline _textBaseLine; cocos2d::Color4F _fillStyle; cocos2d::Color4F _strokeStyle; TEXTMETRIC _tm; // change utf-8 string to utf-16, pRetLen is the string length after changing wchar_t * _utf8ToUtf16(const std::string& str, int * pRetLen = nullptr) { wchar_t * pwszBuffer = nullptr; do { if (str.empty()) { break; } int nLen = str.size(); int nBufLen = nLen + 1; pwszBuffer = new wchar_t[nBufLen]; CC_BREAK_IF(!pwszBuffer); memset(pwszBuffer, 0, sizeof(wchar_t) * nBufLen); // str.size() not equal actuallyLen for Chinese char int actuallyLen = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), nLen, pwszBuffer, nBufLen); // SE_LOGE("_utf8ToUtf16, str:%s, strLen:%d, retLen:%d\n", str.c_str(), str.size(), actuallyLen); if (pRetLen != nullptr) { *pRetLen = actuallyLen; } } while (0); return pwszBuffer; } void _removeCustomFont() { HFONT hDefFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT); if (hDefFont != _font) { DeleteObject(SelectObject(_DC, hDefFont)); } // release temp font resource if (_curFontPath.size() > 0) { wchar_t * pwszBuffer = _utf8ToUtf16(_curFontPath); if (pwszBuffer) { RemoveFontResource(pwszBuffer); SendMessage(_wnd, WM_FONTCHANGE, 0, 0); delete[] pwszBuffer; pwszBuffer = nullptr; } _curFontPath.clear(); } } // x, y offset value int _drawText(const std::string& text, int x, int y) { int nRet = 0; wchar_t * pwszBuffer = nullptr; do { CC_BREAK_IF(text.empty()); DWORD dwFmt = DT_SINGLELINE | DT_NOPREFIX; int bufferLen = 0; pwszBuffer = _utf8ToUtf16(text, &bufferLen); SIZE newSize = _sizeWithText(pwszBuffer, bufferLen); _textSize = newSize; RECT rcText = { 0 }; rcText.right = newSize.cx; rcText.bottom = newSize.cy; LONG offsetX = x; LONG offsetY = y; if (offsetX || offsetY) { OffsetRect(&rcText, offsetX, offsetY); } // SE_LOGE("_drawText text,%s size: (%d, %d) offset after convert: (%d, %d) \n", text.c_str(), newSize.cx, newSize.cy, offsetX, offsetY); SetBkMode(_DC, TRANSPARENT); SetTextColor(_DC, RGB(255, 255, 255)); // white color // draw text nRet = DrawTextW(_DC, pwszBuffer, bufferLen, &rcText, dwFmt); } while (0); CC_SAFE_DELETE_ARRAY(pwszBuffer); return nRet; } SIZE _sizeWithText(const wchar_t * pszText, int nLen) { SIZE tRet = { 0 }; do { CC_BREAK_IF(!pszText || nLen <= 0); RECT rc = { 0, 0, 0, 0 }; DWORD dwCalcFmt = DT_CALCRECT | DT_NOPREFIX; // measure text size DrawTextW(_DC, pszText, nLen, &rc, dwCalcFmt); tRet.cx = rc.right; tRet.cy = rc.bottom; } while (0); return tRet; } void _prepareBitmap(int nWidth, int nHeight) { // release bitmap _deleteBitmap(); if (nWidth > 0 && nHeight > 0) { _bmp = CreateBitmap(nWidth, nHeight, 1, 32, nullptr); SelectObject(_DC, _bmp); } } void _deleteBitmap() { if (_bmp) { DeleteObject(_bmp); _bmp = nullptr; } } void _fillTextureData() { do { int dataLen = _bufferWidth * _bufferHeight * 4; unsigned char* dataBuf = (unsigned char*)malloc(sizeof(unsigned char) * dataLen); CC_BREAK_IF(!dataBuf); unsigned char* imageBuf = _imageData.getBytes(); CC_BREAK_IF(!imageBuf); struct { BITMAPINFOHEADER bmiHeader; int mask[4]; } bi = { 0 }; bi.bmiHeader.biSize = sizeof(bi.bmiHeader); CC_BREAK_IF(!GetDIBits(_DC, _bmp, 0, 0, nullptr, (LPBITMAPINFO)&bi, DIB_RGB_COLORS)); // copy pixel data bi.bmiHeader.biHeight = (bi.bmiHeader.biHeight > 0) ? -bi.bmiHeader.biHeight : bi.bmiHeader.biHeight; GetDIBits(_DC, _bmp, 0, _bufferHeight, dataBuf, (LPBITMAPINFO)&bi, DIB_RGB_COLORS); uint8_t r = _fillStyle.r * 255; uint8_t g = _fillStyle.g * 255; uint8_t b = _fillStyle.b * 255; uint8_t a = _fillStyle.a; COLORREF textColor = (b << 16 | g << 8 | r) & 0x00ffffff; COLORREF * pPixel = nullptr; COLORREF * pImage = nullptr; if (_premultiply) { uint8_t dirtyValue = 0; for (int y = 0; y < _bufferHeight; ++y) { pPixel = (COLORREF *)dataBuf + y * (int)_bufferWidth; pImage = (COLORREF *)imageBuf + y * (int)_bufferWidth; for (int x = 0; x < _bufferWidth; ++x) { COLORREF& clr = *pPixel; COLORREF& val = *pImage; dirtyValue = GetRValue(clr); // "dirtyValue > 0" means pixel was covered when drawing text if (dirtyValue > 0) { // r = _fillStyle.r * 255 * (dirtyValue / 255) * alpha; r = _fillStyle.r * dirtyValue * a; g = _fillStyle.g * dirtyValue * a; b = _fillStyle.b * dirtyValue * a; textColor = (b << 16 | g << 8 | r) & 0x00ffffff; val = ((BYTE)(dirtyValue * a) << 24) | textColor; } ++pPixel; ++pImage; } } } else { for (int y = 0; y < _bufferHeight; ++y) { pPixel = (COLORREF *)dataBuf + y * (int)_bufferWidth; pImage = (COLORREF *)imageBuf + y * (int)_bufferWidth; for (int x = 0; x < _bufferWidth; ++x) { COLORREF& clr = *pPixel; COLORREF& val = *pImage; // Because text is drawn in white color, and background color is black, // so the red value is equal to alpha value. And we should keep this value // as it includes anti-atlas information. uint8_t alpha = GetRValue(clr); if (alpha > 0) { val = (alpha << 24) | textColor; } ++pPixel; ++pImage; } } } free(dataBuf); } while (0); } Point _convertDrawPoint(Point point, std::string text) { Size textSize = 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 GetTextMetrics(_DC, &_tm); point.y -= _tm.tmAscent; return point; } }; 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 CanvasRenderingContext2D::CanvasRenderingContext2D(float width, float height) : __width(width) , __height(height) { //SE_LOGD("CanvasRenderingContext2D constructor: %p, width: %f, height: %f\n", this, width, height); _impl = new CanvasRenderingContext2DImpl(); recreateBufferIfNeeded(); } CanvasRenderingContext2D::~CanvasRenderingContext2D() { //SE_LOGD("CanvasRenderingContext2D destructor: %p\n", this); delete _impl; } void CanvasRenderingContext2D::recreateBufferIfNeeded() { if (_isBufferSizeDirty) { _isBufferSizeDirty = false; //SE_LOGD("Recreate buffer %p, w: %f, h:%f\n", this, __width, __height); _impl->recreateBuffer(__width, __height); if (_canvasBufferUpdatedCB != nullptr) _canvasBufferUpdatedCB(_impl->getDataRef()); } } void CanvasRenderingContext2D::clearRect(float x, float y, float width, float height) { //SE_LOGD("CanvasRenderingContext2D::clearRect: %p, %f, %f, %f, %f\n", this, x, y, width, height); recreateBufferIfNeeded(); _impl->clearRect(x, y, width, height); } void CanvasRenderingContext2D::fillRect(float x, float y, float width, float height) { recreateBufferIfNeeded(); _impl->fillRect(x, y, width, height); if (_canvasBufferUpdatedCB != nullptr) _canvasBufferUpdatedCB(_impl->getDataRef()); } void CanvasRenderingContext2D::fillText(const std::string& text, float x, float y, float maxWidth) { //SE_LOGD("CanvasRenderingContext2D::fillText: %s, offset: (%f, %f), %f\n", text.c_str(), x, y, maxWidth); if (text.empty()) return; recreateBufferIfNeeded(); _impl->fillText(text, x, y, maxWidth); if (_canvasBufferUpdatedCB != nullptr) _canvasBufferUpdatedCB(_impl->getDataRef()); } void CanvasRenderingContext2D::strokeText(const std::string& text, float x, float y, float maxWidth) { //SE_LOGD("CanvasRenderingContext2D::strokeText: %s, %f, %f, %f\n", text.c_str(), x, y, maxWidth); if (text.empty()) return; recreateBufferIfNeeded(); _impl->strokeText(text, x, y, maxWidth); if (_canvasBufferUpdatedCB != nullptr) _canvasBufferUpdatedCB(_impl->getDataRef()); } cocos2d::Size CanvasRenderingContext2D::measureText(const std::string& text) { //SE_LOGD("CanvasRenderingContext2D::measureText: %s\n", text.c_str()); return _impl->measureText(text); } CanvasGradient* CanvasRenderingContext2D::createLinearGradient(float x0, float y0, float x1, float y1) { return nullptr; } void CanvasRenderingContext2D::save() { //SE_LOGD("CanvasRenderingContext2D::save\n"); _impl->saveContext(); } void CanvasRenderingContext2D::beginPath() { //SE_LOGD("\n-----------begin------------------\nCanvasRenderingContext2D::beginPath\n"); _impl->beginPath(); } void CanvasRenderingContext2D::closePath() { //SE_LOGD("CanvasRenderingContext2D::closePath\n"); _impl->closePath(); } void CanvasRenderingContext2D::moveTo(float x, float y) { //SE_LOGD("CanvasRenderingContext2D::moveTo\n"); _impl->moveTo(x, y); } void CanvasRenderingContext2D::lineTo(float x, float y) { //SE_LOGD("CanvasRenderingContext2D::lineTo\n"); _impl->lineTo(x, y); } void CanvasRenderingContext2D::stroke() { //SE_LOGD("CanvasRenderingContext2D::stroke\n"); _impl->stroke(); if (_canvasBufferUpdatedCB != nullptr) _canvasBufferUpdatedCB(_impl->getDataRef()); } void CanvasRenderingContext2D::restore() { //SE_LOGD("CanvasRenderingContext2D::restore\n"); _impl->restoreContext(); } void CanvasRenderingContext2D::setCanvasBufferUpdatedCallback(const CanvasBufferUpdatedCallback& cb) { _canvasBufferUpdatedCB = cb; } void CanvasRenderingContext2D::setPremultiply(bool multiply) { _impl->setPremultiply(_premultiply); } 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) { //SE_LOGD("CanvasRenderingContext2D::set_lineWidth %d\n", lineWidth); _lineWidth = lineWidth; _impl->setLineWidth(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::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::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 results; if (std::regex_search(_font.cbegin(), _font.cend(), results, re)) { boldStr = results[1].str(); fontSizeStr = results[2].str(); fontName = results[5].str(); } float fontSize = atof(fontSizeStr.c_str()); //SE_LOGD("CanvasRenderingContext2D::set_font: %s, Size: %f, isBold: %b\n", fontName.c_str(), fontSize, !boldStr.empty()); _impl->updateFont(fontName, fontSize, !boldStr.empty()); } } void CanvasRenderingContext2D::set_textAlign(const std::string& textAlign) { //SE_LOGD("CanvasRenderingContext2D::set_textAlign: %s\n", textAlign.c_str()); if (textAlign == "left") { _impl->setTextAlign(CanvasTextAlign::LEFT); } else if (textAlign == "center" || textAlign == "middle") { _impl->setTextAlign(CanvasTextAlign::CENTER); } else if (textAlign == "right") { _impl->setTextAlign(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->setTextBaseline(CanvasTextBaseline::TOP); } else if (textBaseline == "middle") { _impl->setTextBaseline(CanvasTextBaseline::MIDDLE); } else if (textBaseline == "bottom" || textBaseline == "alphabetic") //REFINE:, how to deal with alphabetic, currently we handle it as bottom mode. { _impl->setTextBaseline(CanvasTextBaseline::BOTTOM); } else { assert(false); } } void CanvasRenderingContext2D::set_fillStyle(const std::string& fillStyle) { CSSColorParser::Color color = CSSColorParser::parse(fillStyle); _impl->setFillStyle(color.r/255.0f, color.g/255.0f, color.b/255.0f, 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->setStrokeStyle(color.r/255.0f, color.g/255.0f, color.b/255.0f, 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 //REFINE: 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