mirror of https://github.com/axmolengine/axmol.git
Merge pull request #5326 from Dhilan007/develop_label
issue #3821: Moving platform-dependent codes of LabelTTF from cocos2d::Image to cocos2d::Device.
This commit is contained in:
commit
fb4699359d
|
@ -1 +1 @@
|
|||
490db6577af8dba2213a78e7eeef17d1dc60db3a
|
||||
635f17db87974af37b9774d3c038ce25808e3d27
|
|
@ -115,6 +115,7 @@ platform/CCGLViewProtocol.cpp \
|
|||
platform/CCFileUtils.cpp \
|
||||
platform/CCSAXParser.cpp \
|
||||
platform/CCThread.cpp \
|
||||
platform/CCImage.cpp \
|
||||
renderer/CCCustomCommand.cpp \
|
||||
renderer/CCFrustum.cpp \
|
||||
renderer/CCGroupCommand.cpp \
|
||||
|
|
|
@ -43,6 +43,7 @@ THE SOFTWARE.
|
|||
#include "CCGLProgram.h"
|
||||
#include "ccGLStateCache.h"
|
||||
#include "CCShaderCache.h"
|
||||
#include "platform/CCDevice.h"
|
||||
|
||||
#if CC_ENABLE_CACHE_TEXTURE_DATA
|
||||
#include "CCTextureCache.h"
|
||||
|
@ -1039,28 +1040,30 @@ bool Texture2D::initWithString(const char *text, const char *fontName, float fon
|
|||
|
||||
bool Texture2D::initWithString(const char *text, const FontDefinition& textDefinition)
|
||||
{
|
||||
if(!text || 0 == strlen(text))
|
||||
return false;
|
||||
#if CC_ENABLE_CACHE_TEXTURE_DATA
|
||||
// cache the texture data
|
||||
VolatileTextureMgr::addStringTexture(this, text, textDefinition);
|
||||
#endif
|
||||
|
||||
bool ret = false;
|
||||
Image::TextAlign align;
|
||||
Device::TextAlign align;
|
||||
|
||||
if (TextVAlignment::TOP == textDefinition._vertAlignment)
|
||||
{
|
||||
align = (TextHAlignment::CENTER == textDefinition._alignment) ? Image::TextAlign::TOP
|
||||
: (TextHAlignment::LEFT == textDefinition._alignment) ? Image::TextAlign::TOP_LEFT : Image::TextAlign::TOP_RIGHT;
|
||||
align = (TextHAlignment::CENTER == textDefinition._alignment) ? Device::TextAlign::TOP
|
||||
: (TextHAlignment::LEFT == textDefinition._alignment) ? Device::TextAlign::TOP_LEFT : Device::TextAlign::TOP_RIGHT;
|
||||
}
|
||||
else if (TextVAlignment::CENTER == textDefinition._vertAlignment)
|
||||
{
|
||||
align = (TextHAlignment::CENTER == textDefinition._alignment) ? Image::TextAlign::CENTER
|
||||
: (TextHAlignment::LEFT == textDefinition._alignment) ? Image::TextAlign::LEFT : Image::TextAlign::RIGHT;
|
||||
align = (TextHAlignment::CENTER == textDefinition._alignment) ? Device::TextAlign::CENTER
|
||||
: (TextHAlignment::LEFT == textDefinition._alignment) ? Device::TextAlign::LEFT : Device::TextAlign::RIGHT;
|
||||
}
|
||||
else if (TextVAlignment::BOTTOM == textDefinition._vertAlignment)
|
||||
{
|
||||
align = (TextHAlignment::CENTER == textDefinition._alignment) ? Image::TextAlign::BOTTOM
|
||||
: (TextHAlignment::LEFT == textDefinition._alignment) ? Image::TextAlign::BOTTOM_LEFT : Image::TextAlign::BOTTOM_RIGHT;
|
||||
align = (TextHAlignment::CENTER == textDefinition._alignment) ? Device::TextAlign::BOTTOM
|
||||
: (TextHAlignment::LEFT == textDefinition._alignment) ? Device::TextAlign::BOTTOM_LEFT : Device::TextAlign::BOTTOM_RIGHT;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1068,93 +1071,37 @@ bool Texture2D::initWithString(const char *text, const FontDefinition& textDefin
|
|||
return false;
|
||||
}
|
||||
|
||||
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) || (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
|
||||
|
||||
// handle shadow parameters
|
||||
bool shadowEnabled = false;
|
||||
float shadowDX = 0.0f;
|
||||
float shadowDY = 0.0f;
|
||||
float shadowBlur = 0.0f;
|
||||
float shadowOpacity = 0.0f;
|
||||
|
||||
if ( textDefinition._shadow._shadowEnabled )
|
||||
{
|
||||
shadowEnabled = true;
|
||||
shadowDX = textDefinition._shadow._shadowOffset.width;
|
||||
shadowDY = textDefinition._shadow._shadowOffset.height;
|
||||
shadowBlur = textDefinition._shadow._shadowBlur;
|
||||
shadowOpacity = textDefinition._shadow._shadowOpacity;
|
||||
}
|
||||
|
||||
// handle stroke parameters
|
||||
bool strokeEnabled = false;
|
||||
float strokeColorR = 0.0f;
|
||||
float strokeColorG = 0.0f;
|
||||
float strokeColorB = 0.0f;
|
||||
float strokeSize = 0.0f;
|
||||
|
||||
if ( textDefinition._stroke._strokeEnabled )
|
||||
{
|
||||
strokeEnabled = true;
|
||||
strokeColorR = textDefinition._stroke._strokeColor.r / 255.0f;
|
||||
strokeColorG = textDefinition._stroke._strokeColor.g / 255.0f;
|
||||
strokeColorB = textDefinition._stroke._strokeColor.b / 255.0f;
|
||||
strokeSize = textDefinition._stroke._strokeSize;
|
||||
}
|
||||
|
||||
Image* image = new Image();
|
||||
do
|
||||
{
|
||||
CC_BREAK_IF(nullptr == image);
|
||||
|
||||
ret = image->initWithStringShadowStroke(text,
|
||||
(int)textDefinition._dimensions.width,
|
||||
(int)textDefinition._dimensions.height,
|
||||
align,
|
||||
textDefinition._fontName.c_str(),
|
||||
textDefinition._fontSize,
|
||||
textDefinition._fontFillColor.r / 255.0f,
|
||||
textDefinition._fontFillColor.g / 255.0f,
|
||||
textDefinition._fontFillColor.b / 255.0f,
|
||||
shadowEnabled,
|
||||
shadowDX,
|
||||
shadowDY,
|
||||
shadowOpacity,
|
||||
shadowBlur,
|
||||
strokeEnabled,
|
||||
strokeColorR,
|
||||
strokeColorG,
|
||||
strokeColorB,
|
||||
strokeSize);
|
||||
|
||||
|
||||
CC_BREAK_IF(!ret);
|
||||
ret = initWithImage(image);
|
||||
|
||||
} while (0);
|
||||
|
||||
CC_SAFE_RELEASE(image);
|
||||
|
||||
return ret;
|
||||
|
||||
#else
|
||||
#if (CC_TARGET_PLATFORM != CC_PLATFORM_ANDROID) && (CC_TARGET_PLATFORM != CC_PLATFORM_IOS)
|
||||
bool requestUnsupported = textDefinition._shadow._shadowEnabled || textDefinition._stroke._strokeEnabled;
|
||||
|
||||
CCASSERT(requestUnsupported == false, "Currently shadow and stroke only supported on iOS and Android!");
|
||||
|
||||
Image* image = new Image();
|
||||
do
|
||||
{
|
||||
CC_BREAK_IF(nullptr == image);
|
||||
ret = image->initWithString(text, (int)textDefinition._dimensions.width, (int)textDefinition._dimensions.height, align, textDefinition._fontName.c_str(), (int)textDefinition._fontSize);
|
||||
CC_BREAK_IF(!ret);
|
||||
ret = initWithImage(image);
|
||||
} while (0);
|
||||
|
||||
CC_SAFE_RELEASE(image);
|
||||
|
||||
return ret;
|
||||
#endif
|
||||
|
||||
PixelFormat pixelFormat = g_defaultAlphaPixelFormat;
|
||||
unsigned char* outTempData = nullptr;
|
||||
ssize_t outTempDataLen = 0;
|
||||
|
||||
int imageWidth;
|
||||
int imageHeight;
|
||||
Data outData = Device::getTextureDataForText(text,textDefinition,align,imageWidth,imageHeight);
|
||||
if(outData.isNull())
|
||||
return false;
|
||||
|
||||
Size imageSize = Size((float)imageWidth, (float)imageHeight);
|
||||
pixelFormat = convertDataToFormat(outData.getBytes(), imageWidth*imageHeight*4, PixelFormat::RGBA8888, pixelFormat, &outTempData, &outTempDataLen);
|
||||
|
||||
ret = initWithData(outTempData, outTempDataLen, pixelFormat, imageWidth, imageHeight, imageSize);
|
||||
|
||||
if (outTempData != nullptr && outTempData != outData.getBytes())
|
||||
{
|
||||
delete [] outTempData;
|
||||
}
|
||||
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) || (CC_TARGET_PLATFORM == CC_PLATFORM_LINUX)
|
||||
_hasPremultipliedAlpha = true;
|
||||
#else
|
||||
_hasPremultipliedAlpha = false;
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -8,7 +8,6 @@ set(PLATFORM_SRC
|
|||
platform/win32/CCCommon.cpp
|
||||
platform/win32/CCApplication.cpp
|
||||
platform/win32/CCGLView.cpp
|
||||
platform/win32/CCImage.cpp
|
||||
platform/win32/CCDevice.cpp
|
||||
)
|
||||
|
||||
|
@ -22,7 +21,6 @@ set(PLATFORM_SRC
|
|||
platform/linux/CCCommon.cpp
|
||||
platform/linux/CCApplication.cpp
|
||||
platform/desktop/CCGLView.cpp
|
||||
platform/linux/CCImage.cpp
|
||||
platform/linux/CCDevice.cpp
|
||||
)
|
||||
|
||||
|
@ -136,6 +134,7 @@ set(COCOS2D_SRC
|
|||
platform/CCThread.cpp
|
||||
platform/CCGLViewProtocol.cpp
|
||||
platform/CCFileUtils.cpp
|
||||
platform/CCImage.cpp
|
||||
../../external/edtaa3func/edtaa3func.cpp
|
||||
renderer/CCCustomCommand.cpp
|
||||
renderer/CCFrustum.cpp
|
||||
|
|
|
@ -306,6 +306,7 @@ xcopy /Y /Q "$(ProjectDir)..\..\external\win32-specific\gles\prebuilt\*.*" "$(Ou
|
|||
<ClCompile Include="cocos2d.cpp" />
|
||||
<ClCompile Include="platform\CCGLViewProtocol.cpp" />
|
||||
<ClCompile Include="platform\CCFileUtils.cpp" />
|
||||
<ClCompile Include="platform\CCImage.cpp" />
|
||||
<ClCompile Include="platform\CCSAXParser.cpp" />
|
||||
<ClCompile Include="platform\CCThread.cpp" />
|
||||
<ClCompile Include="platform\desktop\CCGLView.cpp" />
|
||||
|
@ -313,7 +314,6 @@ xcopy /Y /Q "$(ProjectDir)..\..\external\win32-specific\gles\prebuilt\*.*" "$(Ou
|
|||
<ClCompile Include="platform\win32\CCCommon.cpp" />
|
||||
<ClCompile Include="platform\win32\CCDevice.cpp" />
|
||||
<ClCompile Include="platform\win32\CCFileUtilsWin32.cpp" />
|
||||
<ClCompile Include="platform\win32\CCImage.cpp" />
|
||||
<ClCompile Include="platform\win32\CCStdC.cpp" />
|
||||
<ClCompile Include="renderer\CCBatchCommand.cpp" />
|
||||
<ClCompile Include="renderer\CCCustomCommand.cpp" />
|
||||
|
@ -508,7 +508,6 @@ xcopy /Y /Q "$(ProjectDir)..\..\external\win32-specific\gles\prebuilt\*.*" "$(Ou
|
|||
<ClInclude Include="platform\CCGLViewProtocol.h" />
|
||||
<ClInclude Include="platform\CCFileUtils.h" />
|
||||
<ClInclude Include="platform\CCImage.h" />
|
||||
<ClInclude Include="platform\CCImageCommon_cpp.h" />
|
||||
<ClInclude Include="platform\CCSAXParser.h" />
|
||||
<ClInclude Include="platform\CCThread.h" />
|
||||
<ClInclude Include="platform\desktop\CCGLView.h" />
|
||||
|
|
|
@ -357,9 +357,6 @@
|
|||
<ClCompile Include="platform\win32\CCFileUtilsWin32.cpp">
|
||||
<Filter>platform\win32</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="platform\win32\CCImage.cpp">
|
||||
<Filter>platform\win32</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="platform\win32\CCStdC.cpp">
|
||||
<Filter>platform\win32</Filter>
|
||||
</ClCompile>
|
||||
|
@ -598,6 +595,9 @@
|
|||
<ClCompile Include="platform\desktop\CCGLView.cpp">
|
||||
<Filter>platform\desktop</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="platform\CCImage.cpp">
|
||||
<Filter>platform</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\physics\CCPhysicsBody.h">
|
||||
|
@ -885,9 +885,6 @@
|
|||
<ClInclude Include="platform\CCImage.h">
|
||||
<Filter>platform</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="platform\CCImageCommon_cpp.h">
|
||||
<Filter>platform</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="platform\CCSAXParser.h">
|
||||
<Filter>platform</Filter>
|
||||
</ClInclude>
|
||||
|
|
|
@ -28,12 +28,27 @@ THE SOFTWARE.
|
|||
|
||||
#include "CCPlatformMacros.h"
|
||||
#include "ccMacros.h"
|
||||
#include "CCData.h"
|
||||
|
||||
NS_CC_BEGIN
|
||||
|
||||
struct FontDefinition;
|
||||
|
||||
class CC_DLL Device
|
||||
{
|
||||
public:
|
||||
enum class TextAlign
|
||||
{
|
||||
CENTER = 0x33, ///< Horizontal center and vertical center.
|
||||
TOP = 0x13, ///< Horizontal center and vertical top.
|
||||
TOP_RIGHT = 0x12, ///< Horizontal right and vertical top.
|
||||
RIGHT = 0x32, ///< Horizontal right and vertical center.
|
||||
BOTTOM_RIGHT = 0x22, ///< Horizontal right and vertical bottom.
|
||||
BOTTOM = 0x23, ///< Horizontal center and vertical bottom.
|
||||
BOTTOM_LEFT = 0x21, ///< Horizontal left and vertical bottom.
|
||||
LEFT = 0x31, ///< Horizontal left and vertical center.
|
||||
TOP_LEFT = 0x11, ///< Horizontal left and vertical top.
|
||||
};
|
||||
/**
|
||||
* Gets the DPI of device
|
||||
* @return The DPI of device.
|
||||
|
@ -49,6 +64,8 @@ public:
|
|||
*/
|
||||
static void setAccelerometerInterval(float interval);
|
||||
|
||||
static Data getTextureDataForText(const char * text,const FontDefinition& textDefinition,TextAlign align,int &widht,int &height);
|
||||
|
||||
private:
|
||||
CC_DISALLOW_IMPLICIT_CONSTRUCTORS(Device);
|
||||
};
|
||||
|
|
|
@ -94,19 +94,6 @@ public:
|
|||
UNKOWN
|
||||
};
|
||||
|
||||
enum class TextAlign
|
||||
{
|
||||
CENTER = 0x33, ///< Horizontal center and vertical center.
|
||||
TOP = 0x13, ///< Horizontal center and vertical top.
|
||||
TOP_RIGHT = 0x12, ///< Horizontal right and vertical top.
|
||||
RIGHT = 0x32, ///< Horizontal right and vertical center.
|
||||
BOTTOM_RIGHT = 0x22, ///< Horizontal right and vertical bottom.
|
||||
BOTTOM = 0x23, ///< Horizontal center and vertical bottom.
|
||||
BOTTOM_LEFT = 0x21, ///< Horizontal left and vertical bottom.
|
||||
LEFT = 0x31, ///< Horizontal left and vertical center.
|
||||
TOP_LEFT = 0x11, ///< Horizontal left and vertical top.
|
||||
};
|
||||
|
||||
/**
|
||||
@brief Load the image from the specified path.
|
||||
@param path the absolute file path.
|
||||
|
@ -127,53 +114,6 @@ public:
|
|||
// @warning kFmtRawData only support RGBA8888
|
||||
bool initWithRawData(const unsigned char * data, ssize_t dataLen, int width, int height, int bitsPerComponent, bool preMulti = false);
|
||||
|
||||
/**
|
||||
@brief Create image with specified string.
|
||||
@param text the text the image will show (cannot be nil).
|
||||
@param width the image width, if 0, the width will match the text's width.
|
||||
@param height the image height, if 0, the height will match the text's height.
|
||||
@param alignMask the test Alignment
|
||||
@param fontName the name of the font used to draw the text. If nil, use the default system font.
|
||||
@param size the font size, if 0, use the system default size.
|
||||
* @js NA
|
||||
* @lua NA
|
||||
*/
|
||||
bool initWithString(
|
||||
const char * text,
|
||||
int width = 0,
|
||||
int height = 0,
|
||||
TextAlign alignMask = TextAlign::CENTER,
|
||||
const char * fontName = 0,
|
||||
int size = 0);
|
||||
|
||||
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) || (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
|
||||
|
||||
bool initWithStringShadowStroke(
|
||||
const char * pText,
|
||||
int nWidth = 0,
|
||||
int nHeight = 0,
|
||||
TextAlign eAlignMask = TextAlign::CENTER,
|
||||
const char * pFontName = 0,
|
||||
int nSize = 0,
|
||||
float textTintR = 1,
|
||||
float textTintG = 1,
|
||||
float textTintB = 1,
|
||||
bool shadow = false,
|
||||
float shadowOffsetX = 0.0,
|
||||
float shadowOffsetY = 0.0,
|
||||
float shadowOpacity = 0.0,
|
||||
float shadowBlur = 0.0,
|
||||
bool stroke = false,
|
||||
float strokeR = 1,
|
||||
float strokeG = 1,
|
||||
float strokeB = 1,
|
||||
float strokeSize = 1
|
||||
|
||||
);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
// Getters
|
||||
inline unsigned char * getData() { return _data; }
|
||||
inline ssize_t getDataLen() { return _dataLen; }
|
||||
|
|
|
@ -12,7 +12,6 @@ CCCommon.cpp \
|
|||
CCDevice.cpp \
|
||||
CCGLView.cpp \
|
||||
CCFileUtilsAndroid.cpp \
|
||||
CCImage.cpp \
|
||||
nativeactivity.cpp \
|
||||
jni/DPIJni.cpp \
|
||||
jni/IMEJni.cpp \
|
||||
|
|
|
@ -27,8 +27,14 @@ THE SOFTWARE.
|
|||
#if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID
|
||||
|
||||
#include "platform/CCDevice.h"
|
||||
#include <string.h>
|
||||
#include <android/log.h>
|
||||
#include <jni.h>
|
||||
#include "ccTypes.h"
|
||||
#include "jni/DPIJni.h"
|
||||
#include "jni/JniHelper.h"
|
||||
#include "nativeactivity.h"
|
||||
#include "platform/CCFileUtils.h"
|
||||
|
||||
NS_CC_BEGIN
|
||||
|
||||
|
@ -59,7 +65,164 @@ void Device::setAccelerometerInterval(float interval)
|
|||
setAccelerometerIntervalJni(interval);
|
||||
}
|
||||
|
||||
class BitmapDC
|
||||
{
|
||||
public:
|
||||
|
||||
BitmapDC()
|
||||
: _data(NULL)
|
||||
, _width(0)
|
||||
, _height(0)
|
||||
{
|
||||
}
|
||||
|
||||
~BitmapDC(void)
|
||||
{
|
||||
if (_data)
|
||||
{
|
||||
delete [] _data;
|
||||
}
|
||||
}
|
||||
|
||||
bool getBitmapFromJavaShadowStroke( const char *text,
|
||||
int nWidth,
|
||||
int nHeight,
|
||||
Device::TextAlign eAlignMask,
|
||||
const char * pFontName,
|
||||
float fontSize,
|
||||
float textTintR = 1.0,
|
||||
float textTintG = 1.0,
|
||||
float textTintB = 1.0,
|
||||
bool shadow = false,
|
||||
float shadowDeltaX = 0.0,
|
||||
float shadowDeltaY = 0.0,
|
||||
float shadowBlur = 0.0,
|
||||
float shadowOpacity = 0.0,
|
||||
bool stroke = false,
|
||||
float strokeColorR = 0.0,
|
||||
float strokeColorG = 0.0,
|
||||
float strokeColorB = 0.0,
|
||||
float strokeSize = 0.0 )
|
||||
{
|
||||
JniMethodInfo methodInfo;
|
||||
if (! JniHelper::getStaticMethodInfo(methodInfo, "org/cocos2dx/lib/Cocos2dxBitmap", "createTextBitmapShadowStroke",
|
||||
"(Ljava/lang/String;Ljava/lang/String;IFFFIIIZFFFFZFFFF)Z"))
|
||||
{
|
||||
CCLOG("%s %d: error to get methodInfo", __FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Do a full lookup for the font path using FileUtils in case the given font name is a relative path to a font file asset,
|
||||
// or the path has been mapped to a different location in the app package:
|
||||
std::string fullPathOrFontName = FileUtils::getInstance()->fullPathForFilename(pFontName);
|
||||
|
||||
// If the path name returned includes the 'assets' dir then that needs to be removed, because the android.content.Context
|
||||
// requires this portion of the path to be omitted for assets inside the app package.
|
||||
if (fullPathOrFontName.find("assets/") == 0)
|
||||
{
|
||||
fullPathOrFontName = fullPathOrFontName.substr(strlen("assets/")); // Chop out the 'assets/' portion of the path.
|
||||
}
|
||||
|
||||
/**create bitmap
|
||||
* this method call Cococs2dx.createBitmap()(java code) to create the bitmap, the java code
|
||||
* will call Java_org_cocos2dx_lib_Cocos2dxBitmap_nativeInitBitmapDC() to init the width, height
|
||||
* and data.
|
||||
* use this approach to decrease the jni call number
|
||||
*/
|
||||
jstring jstrText = methodInfo.env->NewStringUTF(text);
|
||||
jstring jstrFont = methodInfo.env->NewStringUTF(fullPathOrFontName.c_str());
|
||||
|
||||
if(!shadow)
|
||||
{
|
||||
shadowDeltaX = 0.0f;
|
||||
shadowDeltaY = 0.0f;
|
||||
shadowBlur = 0.0f;
|
||||
shadowOpacity = 0.0f;
|
||||
}
|
||||
if (!stroke)
|
||||
{
|
||||
strokeColorR = 0.0f;
|
||||
strokeColorG = 0.0f;
|
||||
strokeColorB = 0.0f;
|
||||
strokeSize = 0.0f;
|
||||
}
|
||||
if(!methodInfo.env->CallStaticBooleanMethod(methodInfo.classID, methodInfo.methodID, jstrText,
|
||||
jstrFont, (int)fontSize, textTintR, textTintG, textTintB, eAlignMask, nWidth, nHeight, shadow, shadowDeltaX, -shadowDeltaY, shadowBlur, shadowOpacity, stroke, strokeColorR, strokeColorG, strokeColorB, strokeSize))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
methodInfo.env->DeleteLocalRef(jstrText);
|
||||
methodInfo.env->DeleteLocalRef(jstrFont);
|
||||
methodInfo.env->DeleteLocalRef(methodInfo.classID);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public:
|
||||
int _width;
|
||||
int _height;
|
||||
unsigned char *_data;
|
||||
JNIEnv *env;
|
||||
};
|
||||
|
||||
static BitmapDC& sharedBitmapDC()
|
||||
{
|
||||
static BitmapDC s_BmpDC;
|
||||
return s_BmpDC;
|
||||
}
|
||||
|
||||
Data Device::getTextureDataForText(const char * text,const FontDefinition& textDefinition,TextAlign align,int &width,int &height)
|
||||
{
|
||||
Data ret;
|
||||
do
|
||||
{
|
||||
BitmapDC &dc = sharedBitmapDC();
|
||||
|
||||
if(! dc.getBitmapFromJavaShadowStroke(text,
|
||||
(int)textDefinition._dimensions.width,
|
||||
(int)textDefinition._dimensions.height,
|
||||
align, textDefinition._fontName.c_str(),
|
||||
textDefinition._fontSize,
|
||||
textDefinition._fontFillColor.r / 255.0f,
|
||||
textDefinition._fontFillColor.g / 255.0f,
|
||||
textDefinition._fontFillColor.b / 255.0f,
|
||||
textDefinition._shadow._shadowEnabled,
|
||||
textDefinition._shadow._shadowOffset.width,
|
||||
textDefinition._shadow._shadowOffset.height,
|
||||
textDefinition._shadow._shadowBlur,
|
||||
textDefinition._shadow._shadowOpacity,
|
||||
textDefinition._stroke._strokeEnabled,
|
||||
textDefinition._stroke._strokeColor.r / 255.0f,
|
||||
textDefinition._stroke._strokeColor.g / 255.0f,
|
||||
textDefinition._stroke._strokeColor.b / 255.0f,
|
||||
textDefinition._stroke._strokeSize )) { break;};
|
||||
|
||||
width = dc._width;
|
||||
height = dc._height;
|
||||
ret.fastSet(dc._data,width * height * 4);
|
||||
} while (0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
NS_CC_END
|
||||
|
||||
#endif // CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID
|
||||
|
||||
// this method is called by Cocos2dxBitmap
|
||||
extern "C"
|
||||
{
|
||||
/**
|
||||
* this method is called by java code to init width, height and pixels data
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_org_cocos2dx_lib_Cocos2dxBitmap_nativeInitBitmapDC(JNIEnv* env, jobject thiz, int width, int height, jbyteArray pixels)
|
||||
{
|
||||
int size = width * height * 4;
|
||||
cocos2d::BitmapDC& bitmapDC = cocos2d::sharedBitmapDC();
|
||||
bitmapDC._width = width;
|
||||
bitmapDC._height = height;
|
||||
bitmapDC._data = new unsigned char[size];
|
||||
env->GetByteArrayRegion(pixels, 0, size, (jbyte*)bitmapDC._data);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,251 +0,0 @@
|
|||
/****************************************************************************
|
||||
Copyright (c) 2010-2012 cocos2d-x.org
|
||||
Copyright (c) 2013-2014 Chukong Technologies Inc.
|
||||
|
||||
http://www.cocos2d-x.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#include "CCPlatformConfig.h"
|
||||
#if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID
|
||||
|
||||
#define __CC_PLATFORM_IMAGE_CPP__
|
||||
#include "platform/CCImageCommon_cpp.h"
|
||||
#include "CCPlatformMacros.h"
|
||||
#include "platform/CCImage.h"
|
||||
#include "platform/CCFileUtils.h"
|
||||
#include "jni/JniHelper.h"
|
||||
|
||||
#include <android/log.h>
|
||||
#include <string.h>
|
||||
#include <jni.h>
|
||||
|
||||
NS_CC_BEGIN
|
||||
|
||||
class BitmapDC
|
||||
{
|
||||
public:
|
||||
|
||||
BitmapDC()
|
||||
: _data(NULL)
|
||||
, _width(0)
|
||||
, _height(0)
|
||||
{
|
||||
}
|
||||
|
||||
~BitmapDC(void)
|
||||
{
|
||||
if (_data)
|
||||
{
|
||||
delete [] _data;
|
||||
}
|
||||
}
|
||||
|
||||
bool getBitmapFromJavaShadowStroke( const char *text,
|
||||
int nWidth,
|
||||
int nHeight,
|
||||
Image::TextAlign eAlignMask,
|
||||
const char * pFontName,
|
||||
float fontSize,
|
||||
float textTintR = 1.0,
|
||||
float textTintG = 1.0,
|
||||
float textTintB = 1.0,
|
||||
bool shadow = false,
|
||||
float shadowDeltaX = 0.0,
|
||||
float shadowDeltaY = 0.0,
|
||||
float shadowBlur = 0.0,
|
||||
float shadowOpacity = 0.0,
|
||||
bool stroke = false,
|
||||
float strokeColorR = 0.0,
|
||||
float strokeColorG = 0.0,
|
||||
float strokeColorB = 0.0,
|
||||
float strokeSize = 0.0 )
|
||||
{
|
||||
JniMethodInfo methodInfo;
|
||||
if (! JniHelper::getStaticMethodInfo(methodInfo, "org/cocos2dx/lib/Cocos2dxBitmap", "createTextBitmapShadowStroke",
|
||||
"(Ljava/lang/String;Ljava/lang/String;IFFFIIIZFFFFZFFFF)Z"))
|
||||
{
|
||||
CCLOG("%s %d: error to get methodInfo", __FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Do a full lookup for the font path using FileUtils in case the given font name is a relative path to a font file asset,
|
||||
// or the path has been mapped to a different location in the app package:
|
||||
std::string fullPathOrFontName = FileUtils::getInstance()->fullPathForFilename(pFontName);
|
||||
|
||||
// If the path name returned includes the 'assets' dir then that needs to be removed, because the android.content.Context
|
||||
// requires this portion of the path to be omitted for assets inside the app package.
|
||||
if (fullPathOrFontName.find("assets/") == 0)
|
||||
{
|
||||
fullPathOrFontName = fullPathOrFontName.substr(strlen("assets/")); // Chop out the 'assets/' portion of the path.
|
||||
}
|
||||
|
||||
/**create bitmap
|
||||
* this method call Cococs2dx.createBitmap()(java code) to create the bitmap, the java code
|
||||
* will call Java_org_cocos2dx_lib_Cocos2dxBitmap_nativeInitBitmapDC() to init the width, height
|
||||
* and data.
|
||||
* use this approach to decrease the jni call number
|
||||
*/
|
||||
jstring jstrText = methodInfo.env->NewStringUTF(text);
|
||||
jstring jstrFont = methodInfo.env->NewStringUTF(fullPathOrFontName.c_str());
|
||||
|
||||
if(!methodInfo.env->CallStaticBooleanMethod(methodInfo.classID, methodInfo.methodID, jstrText,
|
||||
jstrFont, (int)fontSize, textTintR, textTintG, textTintB, eAlignMask, nWidth, nHeight, shadow, shadowDeltaX, -shadowDeltaY, shadowBlur, shadowOpacity, stroke, strokeColorR, strokeColorG, strokeColorB, strokeSize))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
methodInfo.env->DeleteLocalRef(jstrText);
|
||||
methodInfo.env->DeleteLocalRef(jstrFont);
|
||||
methodInfo.env->DeleteLocalRef(methodInfo.classID);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool getBitmapFromJava(const char *text, int nWidth, int nHeight, Image::TextAlign eAlignMask, const char * pFontName, float fontSize)
|
||||
{
|
||||
return getBitmapFromJavaShadowStroke( text, nWidth, nHeight, eAlignMask, pFontName, fontSize );
|
||||
}
|
||||
|
||||
public:
|
||||
int _width;
|
||||
int _height;
|
||||
unsigned char *_data;
|
||||
JNIEnv *env;
|
||||
};
|
||||
|
||||
static BitmapDC& sharedBitmapDC()
|
||||
{
|
||||
static BitmapDC s_BmpDC;
|
||||
return s_BmpDC;
|
||||
}
|
||||
|
||||
bool Image::initWithString(
|
||||
const char * pText,
|
||||
int nWidth/* = 0*/,
|
||||
int nHeight/* = 0*/,
|
||||
TextAlign eAlignMask/* = kAlignCenter*/,
|
||||
const char * pFontName/* = nil*/,
|
||||
int nSize/* = 0*/)
|
||||
{
|
||||
bool bRet = false;
|
||||
|
||||
do
|
||||
{
|
||||
CC_BREAK_IF(! pText);
|
||||
|
||||
BitmapDC &dc = sharedBitmapDC();
|
||||
|
||||
CC_BREAK_IF(! dc.getBitmapFromJava(pText, nWidth, nHeight, eAlignMask, pFontName, nSize));
|
||||
|
||||
// assign the dc._data to _data in order to save time
|
||||
_data = dc._data;
|
||||
CC_BREAK_IF(! _data);
|
||||
|
||||
_width = (short)dc._width;
|
||||
_height = (short)dc._height;
|
||||
_preMulti = true;
|
||||
_renderFormat = Texture2D::PixelFormat::RGBA8888;
|
||||
_dataLen = _width * _height * 4;
|
||||
|
||||
bRet = true;
|
||||
} while (0);
|
||||
|
||||
return bRet;
|
||||
}
|
||||
|
||||
bool Image::initWithStringShadowStroke(
|
||||
const char * pText,
|
||||
int nWidth ,
|
||||
int nHeight ,
|
||||
TextAlign eAlignMask ,
|
||||
const char * pFontName ,
|
||||
int nSize ,
|
||||
float textTintR,
|
||||
float textTintG,
|
||||
float textTintB,
|
||||
bool shadow,
|
||||
float shadowOffsetX,
|
||||
float shadowOffsetY,
|
||||
float shadowOpacity,
|
||||
float shadowBlur,
|
||||
bool stroke,
|
||||
float strokeR,
|
||||
float strokeG,
|
||||
float strokeB,
|
||||
float strokeSize)
|
||||
{
|
||||
bool bRet = false;
|
||||
do
|
||||
{
|
||||
CC_BREAK_IF(! pText);
|
||||
|
||||
BitmapDC &dc = sharedBitmapDC();
|
||||
|
||||
|
||||
CC_BREAK_IF(! dc.getBitmapFromJavaShadowStroke(pText, nWidth, nHeight, eAlignMask, pFontName,
|
||||
nSize, textTintR, textTintG, textTintB, shadow,
|
||||
shadowOffsetX, shadowOffsetY, shadowBlur, shadowOpacity,
|
||||
stroke, strokeR, strokeG, strokeB, strokeSize ));
|
||||
|
||||
|
||||
// assign the dc._data to _data in order to save time
|
||||
_data = dc._data;
|
||||
|
||||
CC_BREAK_IF(! _data);
|
||||
|
||||
_width = (short)dc._width;
|
||||
_height = (short)dc._height;
|
||||
_preMulti = true;
|
||||
_renderFormat = Texture2D::PixelFormat::RGBA8888;
|
||||
_dataLen = _width * _height * 4;
|
||||
|
||||
// ok
|
||||
bRet = true;
|
||||
|
||||
} while (0);
|
||||
|
||||
return bRet;
|
||||
}
|
||||
|
||||
NS_CC_END
|
||||
|
||||
// this method is called by Cocos2dxBitmap
|
||||
extern "C"
|
||||
{
|
||||
/**
|
||||
* this method is called by java code to init width, height and pixels data
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_org_cocos2dx_lib_Cocos2dxBitmap_nativeInitBitmapDC(JNIEnv* env, jobject thiz, int width, int height, jbyteArray pixels)
|
||||
{
|
||||
int size = width * height * 4;
|
||||
cocos2d::BitmapDC& bitmapDC = cocos2d::sharedBitmapDC();
|
||||
bitmapDC._width = width;
|
||||
bitmapDC._height = height;
|
||||
bitmapDC._data = new unsigned char[size];
|
||||
env->GetByteArrayRegion(pixels, 0, size, (jbyte*)bitmapDC._data);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID
|
|
@ -177,7 +177,319 @@ void Device::setAccelerometerInterval(float interval)
|
|||
[[CCAccelerometerDispatcher sharedAccelerometerDispather] setAccelerometerInterval:interval];
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned int height;
|
||||
unsigned int width;
|
||||
bool isPremultipliedAlpha;
|
||||
bool hasShadow;
|
||||
CGSize shadowOffset;
|
||||
float shadowBlur;
|
||||
float shadowOpacity;
|
||||
bool hasStroke;
|
||||
float strokeColorR;
|
||||
float strokeColorG;
|
||||
float strokeColorB;
|
||||
float strokeSize;
|
||||
float tintColorR;
|
||||
float tintColorG;
|
||||
float tintColorB;
|
||||
|
||||
unsigned char* data;
|
||||
|
||||
} tImageInfo;
|
||||
|
||||
static CGSize _calculateStringSize(NSString *str, id font, CGSize *constrainSize)
|
||||
{
|
||||
NSArray *listItems = [str componentsSeparatedByString: @"\n"];
|
||||
CGSize dim = CGSizeZero;
|
||||
CGSize textRect = CGSizeZero;
|
||||
textRect.width = constrainSize->width > 0 ? constrainSize->width
|
||||
: 0x7fffffff;
|
||||
textRect.height = constrainSize->height > 0 ? constrainSize->height
|
||||
: 0x7fffffff;
|
||||
|
||||
|
||||
for (NSString *s in listItems)
|
||||
{
|
||||
CGSize tmp = [s sizeWithFont:font constrainedToSize:textRect];
|
||||
|
||||
if (tmp.width > dim.width)
|
||||
{
|
||||
dim.width = tmp.width;
|
||||
}
|
||||
|
||||
dim.height += tmp.height;
|
||||
}
|
||||
|
||||
return dim;
|
||||
}
|
||||
|
||||
// refer Image::ETextAlign
|
||||
#define ALIGN_TOP 1
|
||||
#define ALIGN_CENTER 3
|
||||
#define ALIGN_BOTTOM 2
|
||||
|
||||
static bool _initWithString(const char * text, cocos2d::Device::TextAlign align, const char * fontName, int size, tImageInfo* info)
|
||||
{
|
||||
bool bRet = false;
|
||||
do
|
||||
{
|
||||
CC_BREAK_IF(! text || ! info);
|
||||
|
||||
NSString * str = [NSString stringWithUTF8String:text];
|
||||
NSString * fntName = [NSString stringWithUTF8String:fontName];
|
||||
|
||||
CGSize dim, constrainSize;
|
||||
|
||||
constrainSize.width = info->width;
|
||||
constrainSize.height = info->height;
|
||||
|
||||
// On iOS custom fonts must be listed beforehand in the App info.plist (in order to be usable) and referenced only the by the font family name itself when
|
||||
// calling [UIFont fontWithName]. Therefore even if the developer adds 'SomeFont.ttf' or 'fonts/SomeFont.ttf' to the App .plist, the font must
|
||||
// be referenced as 'SomeFont' when calling [UIFont fontWithName]. Hence we strip out the folder path components and the extension here in order to get just
|
||||
// the font family name itself. This stripping step is required especially for references to user fonts stored in CCB files; CCB files appear to store
|
||||
// the '.ttf' extensions when referring to custom fonts.
|
||||
fntName = [[fntName lastPathComponent] stringByDeletingPathExtension];
|
||||
|
||||
// create the font
|
||||
id font = [UIFont fontWithName:fntName size:size];
|
||||
|
||||
if (font)
|
||||
{
|
||||
dim = _calculateStringSize(str, font, &constrainSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!font)
|
||||
{
|
||||
font = [UIFont systemFontOfSize:size];
|
||||
}
|
||||
|
||||
if (font)
|
||||
{
|
||||
dim = _calculateStringSize(str, font, &constrainSize);
|
||||
}
|
||||
}
|
||||
|
||||
CC_BREAK_IF(! font);
|
||||
|
||||
// compute start point
|
||||
int startH = 0;
|
||||
if (constrainSize.height > dim.height)
|
||||
{
|
||||
// vertical alignment
|
||||
unsigned int vAlignment = ((int)align >> 4) & 0x0F;
|
||||
if (vAlignment == ALIGN_TOP)
|
||||
{
|
||||
startH = 0;
|
||||
}
|
||||
else if (vAlignment == ALIGN_CENTER)
|
||||
{
|
||||
startH = (constrainSize.height - dim.height) / 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
startH = constrainSize.height - dim.height;
|
||||
}
|
||||
}
|
||||
|
||||
// adjust text rect
|
||||
if (constrainSize.width > 0 && constrainSize.width > dim.width)
|
||||
{
|
||||
dim.width = constrainSize.width;
|
||||
}
|
||||
if (constrainSize.height > 0 && constrainSize.height > dim.height)
|
||||
{
|
||||
dim.height = constrainSize.height;
|
||||
}
|
||||
|
||||
|
||||
// compute the padding needed by shadow and stroke
|
||||
float shadowStrokePaddingX = 0.0f;
|
||||
float shadowStrokePaddingY = 0.0f;
|
||||
|
||||
if ( info->hasStroke )
|
||||
{
|
||||
shadowStrokePaddingX = ceilf(info->strokeSize);
|
||||
shadowStrokePaddingY = ceilf(info->strokeSize);
|
||||
}
|
||||
|
||||
if ( info->hasShadow )
|
||||
{
|
||||
shadowStrokePaddingX = std::max(shadowStrokePaddingX, (float)fabs(info->shadowOffset.width));
|
||||
shadowStrokePaddingY = std::max(shadowStrokePaddingY, (float)fabs(info->shadowOffset.height));
|
||||
}
|
||||
|
||||
// add the padding (this could be 0 if no shadow and no stroke)
|
||||
dim.width += shadowStrokePaddingX;
|
||||
dim.height += shadowStrokePaddingY;
|
||||
|
||||
|
||||
unsigned char* data = new unsigned char[(int)(dim.width * dim.height * 4)];
|
||||
memset(data, 0, (int)(dim.width * dim.height * 4));
|
||||
|
||||
// draw text
|
||||
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
||||
CGContextRef context = CGBitmapContextCreate(data,
|
||||
dim.width,
|
||||
dim.height,
|
||||
8,
|
||||
(int)(dim.width) * 4,
|
||||
colorSpace,
|
||||
kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
|
||||
if (!context)
|
||||
{
|
||||
CGColorSpaceRelease(colorSpace);
|
||||
delete[] data;
|
||||
break;
|
||||
}
|
||||
|
||||
// text color
|
||||
CGContextSetRGBFillColor(context, info->tintColorR, info->tintColorG, info->tintColorB, 1);
|
||||
// move Y rendering to the top of the image
|
||||
CGContextTranslateCTM(context, 0.0f, (dim.height - shadowStrokePaddingY) );
|
||||
CGContextScaleCTM(context, 1.0f, -1.0f); //NOTE: NSString draws in UIKit referential i.e. renders upside-down compared to CGBitmapContext referential
|
||||
|
||||
// store the current context
|
||||
UIGraphicsPushContext(context);
|
||||
|
||||
// measure text size with specified font and determine the rectangle to draw text in
|
||||
unsigned uHoriFlag = (int)align & 0x0f;
|
||||
UITextAlignment testAlign = (UITextAlignment)((2 == uHoriFlag) ? UITextAlignmentRight
|
||||
: (3 == uHoriFlag) ? UITextAlignmentCenter
|
||||
: UITextAlignmentLeft);
|
||||
|
||||
|
||||
// take care of stroke if needed
|
||||
if ( info->hasStroke )
|
||||
{
|
||||
CGContextSetTextDrawingMode(context, kCGTextFillStroke);
|
||||
CGContextSetRGBStrokeColor(context, info->strokeColorR, info->strokeColorG, info->strokeColorB, 1);
|
||||
CGContextSetLineWidth(context, info->strokeSize);
|
||||
}
|
||||
|
||||
// take care of shadow if needed
|
||||
if ( info->hasShadow )
|
||||
{
|
||||
CGSize offset;
|
||||
offset.height = info->shadowOffset.height;
|
||||
offset.width = info->shadowOffset.width;
|
||||
CGFloat shadowColorValues[] = {0, 0, 0, info->shadowOpacity};
|
||||
CGColorRef shadowColor = CGColorCreate (colorSpace, shadowColorValues);
|
||||
|
||||
CGContextSetShadowWithColor(context, offset, info->shadowBlur, shadowColor);
|
||||
|
||||
CGColorRelease (shadowColor);
|
||||
}
|
||||
|
||||
CGColorSpaceRelease(colorSpace);
|
||||
|
||||
|
||||
// normal fonts
|
||||
//if( [font isKindOfClass:[UIFont class] ] )
|
||||
//{
|
||||
// [str drawInRect:CGRectMake(0, startH, dim.width, dim.height) withFont:font lineBreakMode:(UILineBreakMode)UILineBreakModeWordWrap alignment:align];
|
||||
//}
|
||||
//else // ZFont class
|
||||
//{
|
||||
// [FontLabelStringDrawingHelper drawInRect:str rect:CGRectMake(0, startH, dim.width, dim.height) withZFont:font lineBreakMode:(UILineBreakMode)UILineBreakModeWordWrap
|
||||
////alignment:align];
|
||||
//}
|
||||
|
||||
|
||||
|
||||
// compute the rect used for rendering the text
|
||||
// based on wether shadows or stroke are enabled
|
||||
|
||||
float textOriginX = 0.0;
|
||||
float textOrigingY = 0.0;
|
||||
|
||||
float textWidth = dim.width - shadowStrokePaddingX;
|
||||
float textHeight = dim.height - shadowStrokePaddingY;
|
||||
|
||||
|
||||
if ( info->shadowOffset.width < 0 )
|
||||
{
|
||||
textOriginX = shadowStrokePaddingX;
|
||||
}
|
||||
else
|
||||
{
|
||||
textOriginX = 0.0;
|
||||
}
|
||||
|
||||
if (info->shadowOffset.height > 0)
|
||||
{
|
||||
textOrigingY = startH;
|
||||
}
|
||||
else
|
||||
{
|
||||
textOrigingY = startH - shadowStrokePaddingY;
|
||||
}
|
||||
|
||||
CGRect rect = CGRectMake(textOriginX, textOrigingY, textWidth, textHeight);
|
||||
|
||||
CGContextBeginTransparencyLayerWithRect(context, rect, nullptr);
|
||||
// actually draw the text in the context
|
||||
// XXX: ios7 casting
|
||||
[str drawInRect: rect withFont:font lineBreakMode:NSLineBreakByWordWrapping alignment:(NSTextAlignment)testAlign];
|
||||
|
||||
CGContextEndTransparencyLayer(context);
|
||||
|
||||
// pop the context
|
||||
UIGraphicsPopContext();
|
||||
|
||||
// release the context
|
||||
CGContextRelease(context);
|
||||
|
||||
// output params
|
||||
info->data = data;
|
||||
info->isPremultipliedAlpha = true;
|
||||
info->width = dim.width;
|
||||
info->height = dim.height;
|
||||
bRet = true;
|
||||
|
||||
} while (0);
|
||||
|
||||
return bRet;
|
||||
}
|
||||
|
||||
|
||||
Data Device::getTextureDataForText(const char * text,const FontDefinition& textDefinition,TextAlign align,int &width,int &height)
|
||||
{
|
||||
Data ret;
|
||||
|
||||
do {
|
||||
tImageInfo info = {0};
|
||||
info.width = textDefinition._dimensions.width;
|
||||
info.height = textDefinition._dimensions.height;
|
||||
info.hasShadow = textDefinition._shadow._shadowEnabled;
|
||||
info.shadowOffset.width = textDefinition._shadow._shadowOffset.width;
|
||||
info.shadowOffset.height = textDefinition._shadow._shadowOffset.height;
|
||||
info.shadowBlur = textDefinition._shadow._shadowBlur;
|
||||
info.shadowOpacity = textDefinition._shadow._shadowOpacity;
|
||||
info.hasStroke = textDefinition._stroke._strokeEnabled;
|
||||
info.strokeColorR = textDefinition._stroke._strokeColor.r;
|
||||
info.strokeColorG = textDefinition._stroke._strokeColor.g;
|
||||
info.strokeColorB = textDefinition._stroke._strokeColor.b;
|
||||
info.strokeSize = textDefinition._stroke._strokeSize;
|
||||
info.tintColorR = textDefinition._fontFillColor.r;
|
||||
info.tintColorG = textDefinition._fontFillColor.g;
|
||||
info.tintColorB = textDefinition._fontFillColor.b;
|
||||
|
||||
if (! _initWithString(text, align, textDefinition._fontName.c_str(), textDefinition._fontSize, &info))
|
||||
{
|
||||
break;
|
||||
}
|
||||
height = (short)info.height;
|
||||
widht = (short)info.width;
|
||||
ret.fastSet(info.data,width * height * 4);
|
||||
} while (0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
NS_CC_END
|
||||
|
||||
#endif // CC_PLATFORM_IOS
|
||||
|
||||
|
|
|
@ -26,10 +26,7 @@ THE SOFTWARE.
|
|||
#include "CCPlatformConfig.h"
|
||||
#if CC_TARGET_PLATFORM == CC_PLATFORM_IOS
|
||||
|
||||
#include "CCImageCommon_cpp.h"
|
||||
|
||||
#import "CCImage.h"
|
||||
#import "CCFileUtils.h"
|
||||
#import "platform/CCCommon.h"
|
||||
#import <string>
|
||||
|
||||
|
@ -38,354 +35,9 @@ THE SOFTWARE.
|
|||
|
||||
#include<math.h>
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned int height;
|
||||
unsigned int width;
|
||||
bool isPremultipliedAlpha;
|
||||
bool hasShadow;
|
||||
CGSize shadowOffset;
|
||||
float shadowBlur;
|
||||
float shadowOpacity;
|
||||
bool hasStroke;
|
||||
float strokeColorR;
|
||||
float strokeColorG;
|
||||
float strokeColorB;
|
||||
float strokeSize;
|
||||
float tintColorR;
|
||||
float tintColorG;
|
||||
float tintColorB;
|
||||
|
||||
unsigned char* data;
|
||||
|
||||
} tImageInfo;
|
||||
|
||||
static CGSize _calculateStringSize(NSString *str, id font, CGSize *constrainSize)
|
||||
{
|
||||
NSArray *listItems = [str componentsSeparatedByString: @"\n"];
|
||||
CGSize dim = CGSizeZero;
|
||||
CGSize textRect = CGSizeZero;
|
||||
textRect.width = constrainSize->width > 0 ? constrainSize->width
|
||||
: 0x7fffffff;
|
||||
textRect.height = constrainSize->height > 0 ? constrainSize->height
|
||||
: 0x7fffffff;
|
||||
|
||||
|
||||
for (NSString *s in listItems)
|
||||
{
|
||||
CGSize tmp = [s sizeWithFont:font constrainedToSize:textRect];
|
||||
|
||||
if (tmp.width > dim.width)
|
||||
{
|
||||
dim.width = tmp.width;
|
||||
}
|
||||
|
||||
dim.height += tmp.height;
|
||||
}
|
||||
|
||||
return dim;
|
||||
}
|
||||
|
||||
// refer Image::ETextAlign
|
||||
#define ALIGN_TOP 1
|
||||
#define ALIGN_CENTER 3
|
||||
#define ALIGN_BOTTOM 2
|
||||
|
||||
static bool _initWithString(const char * text, cocos2d::Image::TextAlign align, const char * fontName, int size, tImageInfo* info)
|
||||
{
|
||||
bool bRet = false;
|
||||
do
|
||||
{
|
||||
CC_BREAK_IF(! text || ! info);
|
||||
|
||||
NSString * str = [NSString stringWithUTF8String:text];
|
||||
NSString * fntName = [NSString stringWithUTF8String:fontName];
|
||||
|
||||
CGSize dim, constrainSize;
|
||||
|
||||
constrainSize.width = info->width;
|
||||
constrainSize.height = info->height;
|
||||
|
||||
// On iOS custom fonts must be listed beforehand in the App info.plist (in order to be usable) and referenced only the by the font family name itself when
|
||||
// calling [UIFont fontWithName]. Therefore even if the developer adds 'SomeFont.ttf' or 'fonts/SomeFont.ttf' to the App .plist, the font must
|
||||
// be referenced as 'SomeFont' when calling [UIFont fontWithName]. Hence we strip out the folder path components and the extension here in order to get just
|
||||
// the font family name itself. This stripping step is required especially for references to user fonts stored in CCB files; CCB files appear to store
|
||||
// the '.ttf' extensions when referring to custom fonts.
|
||||
fntName = [[fntName lastPathComponent] stringByDeletingPathExtension];
|
||||
|
||||
// create the font
|
||||
id font = [UIFont fontWithName:fntName size:size];
|
||||
|
||||
if (font)
|
||||
{
|
||||
dim = _calculateStringSize(str, font, &constrainSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!font)
|
||||
{
|
||||
font = [UIFont systemFontOfSize:size];
|
||||
}
|
||||
|
||||
if (font)
|
||||
{
|
||||
dim = _calculateStringSize(str, font, &constrainSize);
|
||||
}
|
||||
}
|
||||
|
||||
CC_BREAK_IF(! font);
|
||||
|
||||
// compute start point
|
||||
int startH = 0;
|
||||
if (constrainSize.height > dim.height)
|
||||
{
|
||||
// vertical alignment
|
||||
unsigned int vAlignment = ((int)align >> 4) & 0x0F;
|
||||
if (vAlignment == ALIGN_TOP)
|
||||
{
|
||||
startH = 0;
|
||||
}
|
||||
else if (vAlignment == ALIGN_CENTER)
|
||||
{
|
||||
startH = (constrainSize.height - dim.height) / 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
startH = constrainSize.height - dim.height;
|
||||
}
|
||||
}
|
||||
|
||||
// adjust text rect
|
||||
if (constrainSize.width > 0 && constrainSize.width > dim.width)
|
||||
{
|
||||
dim.width = constrainSize.width;
|
||||
}
|
||||
if (constrainSize.height > 0 && constrainSize.height > dim.height)
|
||||
{
|
||||
dim.height = constrainSize.height;
|
||||
}
|
||||
|
||||
|
||||
// compute the padding needed by shadow and stroke
|
||||
float shadowStrokePaddingX = 0.0f;
|
||||
float shadowStrokePaddingY = 0.0f;
|
||||
|
||||
if ( info->hasStroke )
|
||||
{
|
||||
shadowStrokePaddingX = ceilf(info->strokeSize);
|
||||
shadowStrokePaddingY = ceilf(info->strokeSize);
|
||||
}
|
||||
|
||||
if ( info->hasShadow )
|
||||
{
|
||||
shadowStrokePaddingX = std::max(shadowStrokePaddingX, (float)fabs(info->shadowOffset.width));
|
||||
shadowStrokePaddingY = std::max(shadowStrokePaddingY, (float)fabs(info->shadowOffset.height));
|
||||
}
|
||||
|
||||
// add the padding (this could be 0 if no shadow and no stroke)
|
||||
dim.width += shadowStrokePaddingX;
|
||||
dim.height += shadowStrokePaddingY;
|
||||
|
||||
|
||||
unsigned char* data = new unsigned char[(int)(dim.width * dim.height * 4)];
|
||||
memset(data, 0, (int)(dim.width * dim.height * 4));
|
||||
|
||||
// draw text
|
||||
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
||||
CGContextRef context = CGBitmapContextCreate(data,
|
||||
dim.width,
|
||||
dim.height,
|
||||
8,
|
||||
(int)(dim.width) * 4,
|
||||
colorSpace,
|
||||
kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
|
||||
if (!context)
|
||||
{
|
||||
CGColorSpaceRelease(colorSpace);
|
||||
delete[] data;
|
||||
break;
|
||||
}
|
||||
|
||||
// text color
|
||||
CGContextSetRGBFillColor(context, info->tintColorR, info->tintColorG, info->tintColorB, 1);
|
||||
// move Y rendering to the top of the image
|
||||
CGContextTranslateCTM(context, 0.0f, (dim.height - shadowStrokePaddingY) );
|
||||
CGContextScaleCTM(context, 1.0f, -1.0f); //NOTE: NSString draws in UIKit referential i.e. renders upside-down compared to CGBitmapContext referential
|
||||
|
||||
// store the current context
|
||||
UIGraphicsPushContext(context);
|
||||
|
||||
// measure text size with specified font and determine the rectangle to draw text in
|
||||
unsigned uHoriFlag = (int)align & 0x0f;
|
||||
UITextAlignment testAlign = (UITextAlignment)((2 == uHoriFlag) ? UITextAlignmentRight
|
||||
: (3 == uHoriFlag) ? UITextAlignmentCenter
|
||||
: UITextAlignmentLeft);
|
||||
|
||||
|
||||
// take care of stroke if needed
|
||||
if ( info->hasStroke )
|
||||
{
|
||||
CGContextSetTextDrawingMode(context, kCGTextFillStroke);
|
||||
CGContextSetRGBStrokeColor(context, info->strokeColorR, info->strokeColorG, info->strokeColorB, 1);
|
||||
CGContextSetLineWidth(context, info->strokeSize);
|
||||
}
|
||||
|
||||
// take care of shadow if needed
|
||||
if ( info->hasShadow )
|
||||
{
|
||||
CGSize offset;
|
||||
offset.height = info->shadowOffset.height;
|
||||
offset.width = info->shadowOffset.width;
|
||||
CGFloat shadowColorValues[] = {0, 0, 0, info->shadowOpacity};
|
||||
CGColorRef shadowColor = CGColorCreate (colorSpace, shadowColorValues);
|
||||
|
||||
CGContextSetShadowWithColor(context, offset, info->shadowBlur, shadowColor);
|
||||
|
||||
CGColorRelease (shadowColor);
|
||||
}
|
||||
|
||||
CGColorSpaceRelease(colorSpace);
|
||||
|
||||
|
||||
// normal fonts
|
||||
//if( [font isKindOfClass:[UIFont class] ] )
|
||||
//{
|
||||
// [str drawInRect:CGRectMake(0, startH, dim.width, dim.height) withFont:font lineBreakMode:(UILineBreakMode)UILineBreakModeWordWrap alignment:align];
|
||||
//}
|
||||
//else // ZFont class
|
||||
//{
|
||||
// [FontLabelStringDrawingHelper drawInRect:str rect:CGRectMake(0, startH, dim.width, dim.height) withZFont:font lineBreakMode:(UILineBreakMode)UILineBreakModeWordWrap
|
||||
////alignment:align];
|
||||
//}
|
||||
|
||||
|
||||
|
||||
// compute the rect used for rendering the text
|
||||
// based on wether shadows or stroke are enabled
|
||||
|
||||
float textOriginX = 0.0;
|
||||
float textOrigingY = 0.0;
|
||||
|
||||
float textWidth = dim.width - shadowStrokePaddingX;
|
||||
float textHeight = dim.height - shadowStrokePaddingY;
|
||||
|
||||
|
||||
if ( info->shadowOffset.width < 0 )
|
||||
{
|
||||
textOriginX = shadowStrokePaddingX;
|
||||
}
|
||||
else
|
||||
{
|
||||
textOriginX = 0.0;
|
||||
}
|
||||
|
||||
if (info->shadowOffset.height > 0)
|
||||
{
|
||||
textOrigingY = startH;
|
||||
}
|
||||
else
|
||||
{
|
||||
textOrigingY = startH - shadowStrokePaddingY;
|
||||
}
|
||||
|
||||
CGRect rect = CGRectMake(textOriginX, textOrigingY, textWidth, textHeight);
|
||||
|
||||
CGContextBeginTransparencyLayerWithRect(context, rect, nullptr);
|
||||
// actually draw the text in the context
|
||||
// XXX: ios7 casting
|
||||
[str drawInRect: rect withFont:font lineBreakMode:NSLineBreakByWordWrapping alignment:(NSTextAlignment)testAlign];
|
||||
|
||||
CGContextEndTransparencyLayer(context);
|
||||
|
||||
// pop the context
|
||||
UIGraphicsPopContext();
|
||||
|
||||
// release the context
|
||||
CGContextRelease(context);
|
||||
|
||||
// output params
|
||||
info->data = data;
|
||||
info->isPremultipliedAlpha = true;
|
||||
info->width = dim.width;
|
||||
info->height = dim.height;
|
||||
bRet = true;
|
||||
|
||||
} while (0);
|
||||
|
||||
return bRet;
|
||||
}
|
||||
|
||||
NS_CC_BEGIN
|
||||
|
||||
bool Image::initWithString(
|
||||
const char * pText,
|
||||
int nWidth /* = 0 */,
|
||||
int nHeight /* = 0 */,
|
||||
TextAlign eAlignMask /* = kAlignCenter */,
|
||||
const char * pFontName /* = nil */,
|
||||
int nSize /* = 0 */)
|
||||
{
|
||||
return initWithStringShadowStroke(pText, nWidth, nHeight, eAlignMask , pFontName, nSize);
|
||||
}
|
||||
|
||||
bool Image::initWithStringShadowStroke(
|
||||
const char * text,
|
||||
int width ,
|
||||
int height ,
|
||||
TextAlign alignMask ,
|
||||
const char * fontName ,
|
||||
int size ,
|
||||
float textTintR,
|
||||
float textTintG,
|
||||
float textTintB,
|
||||
bool shadow,
|
||||
float shadowOffsetX,
|
||||
float shadowOffsetY,
|
||||
float shadowOpacity,
|
||||
float shadowBlur,
|
||||
bool stroke,
|
||||
float strokeR,
|
||||
float strokeG,
|
||||
float strokeB,
|
||||
float strokeSize)
|
||||
{
|
||||
|
||||
|
||||
|
||||
tImageInfo info = {0};
|
||||
info.width = width;
|
||||
info.height = height;
|
||||
info.hasShadow = shadow;
|
||||
info.shadowOffset.width = shadowOffsetX;
|
||||
info.shadowOffset.height = shadowOffsetY;
|
||||
info.shadowBlur = shadowBlur;
|
||||
info.shadowOpacity = shadowOpacity;
|
||||
info.hasStroke = stroke;
|
||||
info.strokeColorR = strokeR;
|
||||
info.strokeColorG = strokeG;
|
||||
info.strokeColorB = strokeB;
|
||||
info.strokeSize = strokeSize;
|
||||
info.tintColorR = textTintR;
|
||||
info.tintColorG = textTintG;
|
||||
info.tintColorB = textTintB;
|
||||
|
||||
|
||||
if (! _initWithString(text, alignMask, fontName, size, &info))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
_height = (short)info.height;
|
||||
_width = (short)info.width;
|
||||
_renderFormat = Texture2D::PixelFormat::RGBA8888;
|
||||
_preMulti = info.isPremultipliedAlpha;
|
||||
_data = info.data;
|
||||
_dataLen = _width * _height * 4;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Image::saveToFile(const std::string& filename, bool isToRGB)
|
||||
bool cocos2d::Image::saveToFile(const std::string& filename, bool isToRGB)
|
||||
{
|
||||
bool saveToPNG = false;
|
||||
bool needToCopyPixels = false;
|
||||
|
|
|
@ -30,6 +30,51 @@ THE SOFTWARE.
|
|||
#include <X11/Xlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <fontconfig/fontconfig.h>
|
||||
#include "platform/CCFileUtils.h"
|
||||
|
||||
#include "ft2build.h"
|
||||
#include FT_FREETYPE_H
|
||||
|
||||
using namespace std;
|
||||
|
||||
// as FcFontMatch is quite an expensive call, cache the results of getFontFile
|
||||
static std::map<std::string, std::string> fontCache;
|
||||
|
||||
struct LineBreakGlyph {
|
||||
FT_UInt glyphIndex;
|
||||
int paintPosition;
|
||||
int glyphWidth;
|
||||
|
||||
int bearingX;
|
||||
int kerning;
|
||||
int horizAdvance;
|
||||
};
|
||||
|
||||
struct LineBreakLine {
|
||||
LineBreakLine() : lineWidth(0) {}
|
||||
|
||||
std::vector<LineBreakGlyph> glyphs;
|
||||
int lineWidth;
|
||||
|
||||
void reset() {
|
||||
glyphs.clear();
|
||||
lineWidth = 0;
|
||||
}
|
||||
|
||||
void calculateWidth() {
|
||||
lineWidth = 0;
|
||||
if ( glyphs.empty() == false ) {
|
||||
lineWidth = glyphs.at(glyphs.size() - 1).paintPosition + glyphs.at(glyphs.size() - 1).glyphWidth;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
NS_CC_BEGIN
|
||||
|
||||
int Device::getDPI()
|
||||
|
@ -67,6 +112,392 @@ void Device::setAccelerometerInterval(float interval)
|
|||
|
||||
}
|
||||
|
||||
class BitmapDC
|
||||
{
|
||||
public:
|
||||
BitmapDC() {
|
||||
libError = FT_Init_FreeType( &library );
|
||||
FcInit();
|
||||
_data = NULL;
|
||||
reset();
|
||||
}
|
||||
|
||||
~BitmapDC() {
|
||||
FT_Done_FreeType(library);
|
||||
FcFini();
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
void reset() {
|
||||
iMaxLineWidth = 0;
|
||||
iMaxLineHeight = 0;
|
||||
textLines.clear();
|
||||
}
|
||||
|
||||
int utf8(char **p)
|
||||
{
|
||||
if ((**p & 0x80) == 0x00)
|
||||
{
|
||||
int a = *((*p)++);
|
||||
|
||||
return a;
|
||||
}
|
||||
if ((**p & 0xE0) == 0xC0)
|
||||
{
|
||||
int a = *((*p)++) & 0x1F;
|
||||
int b = *((*p)++) & 0x3F;
|
||||
|
||||
return (a << 6) | b;
|
||||
}
|
||||
if ((**p & 0xF0) == 0xE0)
|
||||
{
|
||||
int a = *((*p)++) & 0x0F;
|
||||
int b = *((*p)++) & 0x3F;
|
||||
int c = *((*p)++) & 0x3F;
|
||||
|
||||
return (a << 12) | (b << 6) | c;
|
||||
}
|
||||
if ((**p & 0xF8) == 0xF0)
|
||||
{
|
||||
int a = *((*p)++) & 0x07;
|
||||
int b = *((*p)++) & 0x3F;
|
||||
int c = *((*p)++) & 0x3F;
|
||||
int d = *((*p)++) & 0x3F;
|
||||
|
||||
return (a << 18) | (b << 12) | (c << 8) | d;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool isBreakPoint(FT_UInt currentCharacter, FT_UInt previousCharacter) {
|
||||
if ( previousCharacter == '-' || previousCharacter == '/' || previousCharacter == '\\' ) {
|
||||
// we can insert a line break after one of these characters
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool divideString(FT_Face face, const char* sText, int iMaxWidth, int iMaxHeight) {
|
||||
const char* pText = sText;
|
||||
textLines.clear();
|
||||
iMaxLineWidth = 0;
|
||||
|
||||
FT_UInt unicode;
|
||||
FT_UInt prevCharacter = 0;
|
||||
FT_UInt glyphIndex = 0;
|
||||
FT_UInt prevGlyphIndex = 0;
|
||||
FT_Vector delta;
|
||||
LineBreakLine currentLine;
|
||||
|
||||
int currentPaintPosition = 0;
|
||||
int lastBreakIndex = -1;
|
||||
bool hasKerning = FT_HAS_KERNING( face );
|
||||
while ((unicode=utf8((char**)&pText))) {
|
||||
if (unicode == '\n') {
|
||||
currentLine.calculateWidth();
|
||||
iMaxLineWidth = max(iMaxLineWidth, currentLine.lineWidth);
|
||||
textLines.push_back(currentLine);
|
||||
currentLine.reset();
|
||||
prevGlyphIndex = 0;
|
||||
prevCharacter = 0;
|
||||
lastBreakIndex = -1;
|
||||
currentPaintPosition = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( isBreakPoint(unicode, prevCharacter) ) {
|
||||
lastBreakIndex = currentLine.glyphs.size() - 1;
|
||||
}
|
||||
|
||||
glyphIndex = FT_Get_Char_Index(face, unicode);
|
||||
if (FT_Load_Glyph(face, glyphIndex, FT_LOAD_DEFAULT)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isspace(unicode)) {
|
||||
currentPaintPosition += face->glyph->metrics.horiAdvance >> 6;
|
||||
prevGlyphIndex = glyphIndex;
|
||||
prevCharacter = unicode;
|
||||
lastBreakIndex = currentLine.glyphs.size();
|
||||
continue;
|
||||
}
|
||||
|
||||
LineBreakGlyph glyph;
|
||||
glyph.glyphIndex = glyphIndex;
|
||||
glyph.glyphWidth = face->glyph->metrics.width >> 6;
|
||||
glyph.bearingX = face->glyph->metrics.horiBearingX >> 6;
|
||||
glyph.horizAdvance = face->glyph->metrics.horiAdvance >> 6;
|
||||
glyph.kerning = 0;
|
||||
|
||||
if (prevGlyphIndex != 0 && hasKerning) {
|
||||
FT_Get_Kerning(face, prevGlyphIndex, glyphIndex, FT_KERNING_DEFAULT, &delta);
|
||||
glyph.kerning = delta.x >> 6;
|
||||
}
|
||||
|
||||
if (iMaxWidth > 0 && currentPaintPosition + glyph.bearingX + glyph.kerning + glyph.glyphWidth > iMaxWidth) {
|
||||
|
||||
int glyphCount = currentLine.glyphs.size();
|
||||
if ( lastBreakIndex >= 0 && lastBreakIndex < glyphCount && currentPaintPosition + glyph.bearingX + glyph.kerning + glyph.glyphWidth - currentLine.glyphs.at(lastBreakIndex).paintPosition < iMaxWidth ) {
|
||||
// we insert a line break at our last break opportunity
|
||||
std::vector<LineBreakGlyph> tempGlyphs;
|
||||
std::vector<LineBreakGlyph>::iterator it = currentLine.glyphs.begin();
|
||||
std::advance(it, lastBreakIndex);
|
||||
tempGlyphs.insert(tempGlyphs.begin(), it, currentLine.glyphs.end());
|
||||
currentLine.glyphs.erase(it, currentLine.glyphs.end());
|
||||
currentLine.calculateWidth();
|
||||
iMaxLineWidth = max(iMaxLineWidth, currentLine.lineWidth);
|
||||
textLines.push_back(currentLine);
|
||||
currentLine.reset();
|
||||
currentPaintPosition = 0;
|
||||
for ( it = tempGlyphs.begin(); it != tempGlyphs.end(); it++ ) {
|
||||
if ( currentLine.glyphs.empty() ) {
|
||||
currentPaintPosition = -(*it).bearingX;
|
||||
(*it).kerning = 0;
|
||||
}
|
||||
(*it).paintPosition = currentPaintPosition + (*it).bearingX + (*it).kerning;
|
||||
currentLine.glyphs.push_back((*it));
|
||||
currentPaintPosition += (*it).kerning + (*it).horizAdvance;
|
||||
}
|
||||
} else {
|
||||
// the current word is too big to fit into one line, insert line break right here
|
||||
currentPaintPosition = 0;
|
||||
glyph.kerning = 0;
|
||||
currentLine.calculateWidth();
|
||||
iMaxLineWidth = max(iMaxLineWidth, currentLine.lineWidth);
|
||||
textLines.push_back(currentLine);
|
||||
currentLine.reset();
|
||||
}
|
||||
|
||||
prevGlyphIndex = 0;
|
||||
prevCharacter = 0;
|
||||
lastBreakIndex = -1;
|
||||
} else {
|
||||
prevGlyphIndex = glyphIndex;
|
||||
prevCharacter = unicode;
|
||||
}
|
||||
|
||||
if ( currentLine.glyphs.empty() ) {
|
||||
currentPaintPosition = -glyph.bearingX;
|
||||
}
|
||||
glyph.paintPosition = currentPaintPosition + glyph.bearingX + glyph.kerning;
|
||||
currentLine.glyphs.push_back(glyph);
|
||||
currentPaintPosition += glyph.kerning + glyph.horizAdvance;
|
||||
}
|
||||
|
||||
if ( currentLine.glyphs.empty() == false ) {
|
||||
currentLine.calculateWidth();
|
||||
iMaxLineWidth = max(iMaxLineWidth, currentLine.lineWidth);
|
||||
textLines.push_back(currentLine);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* compute the start pos of every line
|
||||
*/
|
||||
int computeLineStart(FT_Face face, Device::TextAlign eAlignMask, int line) {
|
||||
int lineWidth = textLines.at(line).lineWidth;
|
||||
if (eAlignMask == Device::TextAlign::CENTER || eAlignMask == Device::TextAlign::TOP || eAlignMask == Device::TextAlign::BOTTOM) {
|
||||
return (iMaxLineWidth - lineWidth) / 2;
|
||||
} else if (eAlignMask == Device::TextAlign::RIGHT || eAlignMask == Device::TextAlign::TOP_RIGHT || eAlignMask == Device::TextAlign::BOTTOM_RIGHT) {
|
||||
return (iMaxLineWidth - lineWidth);
|
||||
}
|
||||
|
||||
// left or other situation
|
||||
return 0;
|
||||
}
|
||||
|
||||
int computeLineStartY( FT_Face face, Device::TextAlign eAlignMask, int txtHeight, int borderHeight ){
|
||||
int baseLinePos = ceilf(FT_MulFix( face->bbox.yMax, face->size->metrics.y_scale )/64.0f);
|
||||
if (eAlignMask == Device::TextAlign::CENTER || eAlignMask == Device::TextAlign::LEFT || eAlignMask == Device::TextAlign::RIGHT) {
|
||||
//vertical center
|
||||
return (borderHeight - txtHeight) / 2 + baseLinePos;
|
||||
} else if (eAlignMask == Device::TextAlign::BOTTOM_RIGHT || eAlignMask == Device::TextAlign::BOTTOM || eAlignMask == Device::TextAlign::BOTTOM_LEFT) {
|
||||
//vertical bottom
|
||||
return borderHeight - txtHeight + baseLinePos;
|
||||
}
|
||||
|
||||
// top alignment
|
||||
return baseLinePos;
|
||||
}
|
||||
|
||||
std::string getFontFile(const char* family_name) {
|
||||
std::string fontPath = family_name;
|
||||
|
||||
std::map<std::string, std::string>::iterator it = fontCache.find(family_name);
|
||||
if ( it != fontCache.end() ) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
// check if the parameter is a font file shipped with the application
|
||||
std::string lowerCasePath = fontPath;
|
||||
std::transform(lowerCasePath.begin(), lowerCasePath.end(), lowerCasePath.begin(), ::tolower);
|
||||
if ( lowerCasePath.find(".ttf") != std::string::npos ) {
|
||||
fontPath = cocos2d::FileUtils::getInstance()->fullPathForFilename(fontPath.c_str());
|
||||
|
||||
FILE *f = fopen(fontPath.c_str(), "r");
|
||||
if ( f ) {
|
||||
fclose(f);
|
||||
fontCache.insert(std::pair<std::string, std::string>(family_name, fontPath));
|
||||
return fontPath;
|
||||
}
|
||||
}
|
||||
|
||||
// use fontconfig to match the parameter against the fonts installed on the system
|
||||
FcPattern *pattern = FcPatternBuild (0, FC_FAMILY, FcTypeString, family_name, (char *) 0);
|
||||
FcConfigSubstitute(0, pattern, FcMatchPattern);
|
||||
FcDefaultSubstitute(pattern);
|
||||
|
||||
FcResult result;
|
||||
FcPattern *font = FcFontMatch(0, pattern, &result);
|
||||
if ( font ) {
|
||||
FcChar8 *s = NULL;
|
||||
if ( FcPatternGetString(font, FC_FILE, 0, &s) == FcResultMatch ) {
|
||||
fontPath = (const char*)s;
|
||||
|
||||
FcPatternDestroy(font);
|
||||
FcPatternDestroy(pattern);
|
||||
|
||||
fontCache.insert(std::pair<std::string, std::string>(family_name, fontPath));
|
||||
return fontPath;
|
||||
}
|
||||
FcPatternDestroy(font);
|
||||
}
|
||||
FcPatternDestroy(pattern);
|
||||
|
||||
return family_name;
|
||||
}
|
||||
|
||||
bool getBitmap(const char *text, int nWidth, int nHeight, Device::TextAlign eAlignMask, const char * pFontName, float fontSize) {
|
||||
if (libError) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FT_Face face;
|
||||
std::string fontfile = getFontFile(pFontName);
|
||||
if ( FT_New_Face(library, fontfile.c_str(), 0, &face) ) {
|
||||
//no valid font found use default
|
||||
if ( FT_New_Face(library, "/usr/share/fonts/truetype/freefont/FreeSerif.ttf", 0, &face) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//select utf8 charmap
|
||||
if ( FT_Select_Charmap(face, FT_ENCODING_UNICODE) ) {
|
||||
FT_Done_Face(face);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( FT_Set_Pixel_Sizes(face, fontSize, fontSize) ) {
|
||||
FT_Done_Face(face);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( divideString(face, text, nWidth, nHeight) == false ) {
|
||||
FT_Done_Face(face);
|
||||
return false;
|
||||
}
|
||||
|
||||
//compute the final line width
|
||||
iMaxLineWidth = MAX(iMaxLineWidth, nWidth);
|
||||
|
||||
//compute the final line height
|
||||
iMaxLineHeight = ceilf(FT_MulFix( face->bbox.yMax - face->bbox.yMin, face->size->metrics.y_scale )/64.0f);
|
||||
int lineHeight = face->size->metrics.height>>6;
|
||||
if ( textLines.size() > 0 ) {
|
||||
iMaxLineHeight += (lineHeight * (textLines.size() -1));
|
||||
}
|
||||
int txtHeight = iMaxLineHeight;
|
||||
iMaxLineHeight = MAX(iMaxLineHeight, nHeight);
|
||||
|
||||
_data = new unsigned char[iMaxLineWidth * iMaxLineHeight * 4];
|
||||
memset(_data,0, iMaxLineWidth * iMaxLineHeight*4);
|
||||
|
||||
int iCurYCursor = computeLineStartY(face, eAlignMask, txtHeight, iMaxLineHeight);
|
||||
|
||||
int lineCount = textLines.size();
|
||||
for (int line = 0; line < lineCount; line++) {
|
||||
int iCurXCursor = computeLineStart(face, eAlignMask, line);
|
||||
|
||||
int glyphCount = textLines.at(line).glyphs.size();
|
||||
for (int i = 0; i < glyphCount; i++) {
|
||||
LineBreakGlyph glyph = textLines.at(line).glyphs.at(i);
|
||||
|
||||
if (FT_Load_Glyph(face, glyph.glyphIndex, FT_LOAD_RENDER)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
FT_Bitmap& bitmap = face->glyph->bitmap;
|
||||
int yoffset = iCurYCursor - (face->glyph->metrics.horiBearingY >> 6);
|
||||
int xoffset = iCurXCursor + glyph.paintPosition;
|
||||
|
||||
for (int y = 0; y < bitmap.rows; ++y) {
|
||||
int iY = yoffset + y;
|
||||
if (iY>=iMaxLineHeight) {
|
||||
//exceed the height truncate
|
||||
break;
|
||||
}
|
||||
iY *= iMaxLineWidth;
|
||||
|
||||
int bitmap_y = y * bitmap.width;
|
||||
|
||||
for (int x = 0; x < bitmap.width; ++x) {
|
||||
unsigned char cTemp = bitmap.buffer[bitmap_y + x];
|
||||
if (cTemp == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int iX = xoffset + x;
|
||||
|
||||
int iTemp = cTemp << 24 | cTemp << 16 | cTemp << 8 | cTemp;
|
||||
*(int*) &_data[(iY + iX) * 4 + 0] = iTemp;
|
||||
}
|
||||
}
|
||||
}
|
||||
// step to next line
|
||||
iCurYCursor += lineHeight;
|
||||
}
|
||||
|
||||
// free face
|
||||
FT_Done_Face(face);
|
||||
return true;
|
||||
}
|
||||
|
||||
public:
|
||||
FT_Library library;
|
||||
|
||||
unsigned char *_data;
|
||||
int libError;
|
||||
std::vector<LineBreakLine> textLines;
|
||||
int iMaxLineWidth;
|
||||
int iMaxLineHeight;
|
||||
};
|
||||
|
||||
static BitmapDC& sharedBitmapDC()
|
||||
{
|
||||
static BitmapDC s_BmpDC;
|
||||
return s_BmpDC;
|
||||
}
|
||||
|
||||
Data Device::getTextureDataForText(const char * text,const FontDefinition& textDefinition,TextAlign align,int &width,int &height)
|
||||
{
|
||||
Data ret;
|
||||
do
|
||||
{
|
||||
BitmapDC &dc = sharedBitmapDC();
|
||||
|
||||
CC_BREAK_IF(! dc.getBitmap(text, textDefinition._dimensions.width, textDefinition._dimensions.height, align, textDefinition._fontName.c_str(), textDefinition._fontSize));
|
||||
CC_BREAK_IF(! dc._data);
|
||||
width = dc.iMaxLineWidth;
|
||||
height = dc.iMaxLineHeight;
|
||||
dc.reset();
|
||||
ret.fastSet(dc._data,width * height * 4);
|
||||
} while (0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
NS_CC_END
|
||||
|
||||
#endif // CC_TARGET_PLATFORM == CC_PLATFORM_LINUX
|
||||
|
|
|
@ -1,494 +0,0 @@
|
|||
/****************************************************************************
|
||||
Copyright (c) 2011 Laschweinski
|
||||
Copyright (c) 2013-2014 Chukong Technologies Inc.
|
||||
|
||||
http://www.cocos2d-x.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#include "CCPlatformConfig.h"
|
||||
#if CC_TARGET_PLATFORM == CC_PLATFORM_LINUX
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <fontconfig/fontconfig.h>
|
||||
|
||||
#include "platform/CCFileUtils.h"
|
||||
#include "CCPlatformMacros.h"
|
||||
#define __CC_PLATFORM_IMAGE_CPP__
|
||||
#include "platform/CCImageCommon_cpp.h"
|
||||
#include "platform/CCImage.h"
|
||||
#include "platform/linux/CCApplication.h"
|
||||
|
||||
#include "ft2build.h"
|
||||
#include "CCStdC.h"
|
||||
|
||||
#include FT_FREETYPE_H
|
||||
|
||||
using namespace std;
|
||||
|
||||
// as FcFontMatch is quite an expensive call, cache the results of getFontFile
|
||||
static std::map<std::string, std::string> fontCache;
|
||||
|
||||
struct LineBreakGlyph {
|
||||
FT_UInt glyphIndex;
|
||||
int paintPosition;
|
||||
int glyphWidth;
|
||||
|
||||
int bearingX;
|
||||
int kerning;
|
||||
int horizAdvance;
|
||||
};
|
||||
|
||||
struct LineBreakLine {
|
||||
LineBreakLine() : lineWidth(0) {}
|
||||
|
||||
std::vector<LineBreakGlyph> glyphs;
|
||||
int lineWidth;
|
||||
|
||||
void reset() {
|
||||
glyphs.clear();
|
||||
lineWidth = 0;
|
||||
}
|
||||
|
||||
void calculateWidth() {
|
||||
lineWidth = 0;
|
||||
if ( glyphs.empty() == false ) {
|
||||
lineWidth = glyphs.at(glyphs.size() - 1).paintPosition + glyphs.at(glyphs.size() - 1).glyphWidth;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
NS_CC_BEGIN
|
||||
|
||||
class BitmapDC
|
||||
{
|
||||
public:
|
||||
BitmapDC() {
|
||||
libError = FT_Init_FreeType( &library );
|
||||
FcInit();
|
||||
_data = NULL;
|
||||
reset();
|
||||
}
|
||||
|
||||
~BitmapDC() {
|
||||
FT_Done_FreeType(library);
|
||||
FcFini();
|
||||
//data will be deleted by Image
|
||||
// if (_data) {
|
||||
// delete _data;
|
||||
// }
|
||||
reset();
|
||||
}
|
||||
|
||||
void reset() {
|
||||
iMaxLineWidth = 0;
|
||||
iMaxLineHeight = 0;
|
||||
textLines.clear();
|
||||
}
|
||||
|
||||
int utf8(char **p)
|
||||
{
|
||||
if ((**p & 0x80) == 0x00)
|
||||
{
|
||||
int a = *((*p)++);
|
||||
|
||||
return a;
|
||||
}
|
||||
if ((**p & 0xE0) == 0xC0)
|
||||
{
|
||||
int a = *((*p)++) & 0x1F;
|
||||
int b = *((*p)++) & 0x3F;
|
||||
|
||||
return (a << 6) | b;
|
||||
}
|
||||
if ((**p & 0xF0) == 0xE0)
|
||||
{
|
||||
int a = *((*p)++) & 0x0F;
|
||||
int b = *((*p)++) & 0x3F;
|
||||
int c = *((*p)++) & 0x3F;
|
||||
|
||||
return (a << 12) | (b << 6) | c;
|
||||
}
|
||||
if ((**p & 0xF8) == 0xF0)
|
||||
{
|
||||
int a = *((*p)++) & 0x07;
|
||||
int b = *((*p)++) & 0x3F;
|
||||
int c = *((*p)++) & 0x3F;
|
||||
int d = *((*p)++) & 0x3F;
|
||||
|
||||
return (a << 18) | (b << 12) | (c << 8) | d;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool isBreakPoint(FT_UInt currentCharacter, FT_UInt previousCharacter) {
|
||||
if ( previousCharacter == '-' || previousCharacter == '/' || previousCharacter == '\\' ) {
|
||||
// we can insert a line break after one of these characters
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool divideString(FT_Face face, const char* sText, int iMaxWidth, int iMaxHeight) {
|
||||
const char* pText = sText;
|
||||
textLines.clear();
|
||||
iMaxLineWidth = 0;
|
||||
|
||||
FT_UInt unicode;
|
||||
FT_UInt prevCharacter = 0;
|
||||
FT_UInt glyphIndex = 0;
|
||||
FT_UInt prevGlyphIndex = 0;
|
||||
FT_Vector delta;
|
||||
LineBreakLine currentLine;
|
||||
|
||||
int currentPaintPosition = 0;
|
||||
int lastBreakIndex = -1;
|
||||
bool hasKerning = FT_HAS_KERNING( face );
|
||||
while ((unicode=utf8((char**)&pText))) {
|
||||
if (unicode == '\n') {
|
||||
currentLine.calculateWidth();
|
||||
iMaxLineWidth = max(iMaxLineWidth, currentLine.lineWidth);
|
||||
textLines.push_back(currentLine);
|
||||
currentLine.reset();
|
||||
prevGlyphIndex = 0;
|
||||
prevCharacter = 0;
|
||||
lastBreakIndex = -1;
|
||||
currentPaintPosition = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( isBreakPoint(unicode, prevCharacter) ) {
|
||||
lastBreakIndex = currentLine.glyphs.size() - 1;
|
||||
}
|
||||
|
||||
glyphIndex = FT_Get_Char_Index(face, unicode);
|
||||
if (FT_Load_Glyph(face, glyphIndex, FT_LOAD_DEFAULT)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isspace(unicode)) {
|
||||
currentPaintPosition += face->glyph->metrics.horiAdvance >> 6;
|
||||
prevGlyphIndex = glyphIndex;
|
||||
prevCharacter = unicode;
|
||||
lastBreakIndex = currentLine.glyphs.size();
|
||||
continue;
|
||||
}
|
||||
|
||||
LineBreakGlyph glyph;
|
||||
glyph.glyphIndex = glyphIndex;
|
||||
glyph.glyphWidth = face->glyph->metrics.width >> 6;
|
||||
glyph.bearingX = face->glyph->metrics.horiBearingX >> 6;
|
||||
glyph.horizAdvance = face->glyph->metrics.horiAdvance >> 6;
|
||||
glyph.kerning = 0;
|
||||
|
||||
if (prevGlyphIndex != 0 && hasKerning) {
|
||||
FT_Get_Kerning(face, prevGlyphIndex, glyphIndex, FT_KERNING_DEFAULT, &delta);
|
||||
glyph.kerning = delta.x >> 6;
|
||||
}
|
||||
|
||||
if (iMaxWidth > 0 && currentPaintPosition + glyph.bearingX + glyph.kerning + glyph.glyphWidth > iMaxWidth) {
|
||||
|
||||
int glyphCount = currentLine.glyphs.size();
|
||||
if ( lastBreakIndex >= 0 && lastBreakIndex < glyphCount && currentPaintPosition + glyph.bearingX + glyph.kerning + glyph.glyphWidth - currentLine.glyphs.at(lastBreakIndex).paintPosition < iMaxWidth ) {
|
||||
// we insert a line break at our last break opportunity
|
||||
std::vector<LineBreakGlyph> tempGlyphs;
|
||||
std::vector<LineBreakGlyph>::iterator it = currentLine.glyphs.begin();
|
||||
std::advance(it, lastBreakIndex);
|
||||
tempGlyphs.insert(tempGlyphs.begin(), it, currentLine.glyphs.end());
|
||||
currentLine.glyphs.erase(it, currentLine.glyphs.end());
|
||||
currentLine.calculateWidth();
|
||||
iMaxLineWidth = max(iMaxLineWidth, currentLine.lineWidth);
|
||||
textLines.push_back(currentLine);
|
||||
currentLine.reset();
|
||||
currentPaintPosition = 0;
|
||||
for ( it = tempGlyphs.begin(); it != tempGlyphs.end(); it++ ) {
|
||||
if ( currentLine.glyphs.empty() ) {
|
||||
currentPaintPosition = -(*it).bearingX;
|
||||
(*it).kerning = 0;
|
||||
}
|
||||
(*it).paintPosition = currentPaintPosition + (*it).bearingX + (*it).kerning;
|
||||
currentLine.glyphs.push_back((*it));
|
||||
currentPaintPosition += (*it).kerning + (*it).horizAdvance;
|
||||
}
|
||||
} else {
|
||||
// the current word is too big to fit into one line, insert line break right here
|
||||
currentPaintPosition = 0;
|
||||
glyph.kerning = 0;
|
||||
currentLine.calculateWidth();
|
||||
iMaxLineWidth = max(iMaxLineWidth, currentLine.lineWidth);
|
||||
textLines.push_back(currentLine);
|
||||
currentLine.reset();
|
||||
}
|
||||
|
||||
prevGlyphIndex = 0;
|
||||
prevCharacter = 0;
|
||||
lastBreakIndex = -1;
|
||||
} else {
|
||||
prevGlyphIndex = glyphIndex;
|
||||
prevCharacter = unicode;
|
||||
}
|
||||
|
||||
if ( currentLine.glyphs.empty() ) {
|
||||
currentPaintPosition = -glyph.bearingX;
|
||||
}
|
||||
glyph.paintPosition = currentPaintPosition + glyph.bearingX + glyph.kerning;
|
||||
currentLine.glyphs.push_back(glyph);
|
||||
currentPaintPosition += glyph.kerning + glyph.horizAdvance;
|
||||
}
|
||||
|
||||
if ( currentLine.glyphs.empty() == false ) {
|
||||
currentLine.calculateWidth();
|
||||
iMaxLineWidth = max(iMaxLineWidth, currentLine.lineWidth);
|
||||
textLines.push_back(currentLine);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* compute the start pos of every line
|
||||
*/
|
||||
int computeLineStart(FT_Face face, Image::TextAlign eAlignMask, int line) {
|
||||
int lineWidth = textLines.at(line).lineWidth;
|
||||
if (eAlignMask == Image::TextAlign::CENTER || eAlignMask == Image::TextAlign::TOP || eAlignMask == Image::TextAlign::BOTTOM) {
|
||||
return (iMaxLineWidth - lineWidth) / 2;
|
||||
} else if (eAlignMask == Image::TextAlign::RIGHT || eAlignMask == Image::TextAlign::TOP_RIGHT || eAlignMask == Image::TextAlign::BOTTOM_RIGHT) {
|
||||
return (iMaxLineWidth - lineWidth);
|
||||
}
|
||||
|
||||
// left or other situation
|
||||
return 0;
|
||||
}
|
||||
|
||||
int computeLineStartY( FT_Face face, Image::TextAlign eAlignMask, int txtHeight, int borderHeight ){
|
||||
int baseLinePos = ceilf(FT_MulFix( face->bbox.yMax, face->size->metrics.y_scale )/64.0f);
|
||||
if (eAlignMask == Image::TextAlign::CENTER || eAlignMask == Image::TextAlign::LEFT || eAlignMask == Image::TextAlign::RIGHT) {
|
||||
//vertical center
|
||||
return (borderHeight - txtHeight) / 2 + baseLinePos;
|
||||
} else if (eAlignMask == Image::TextAlign::BOTTOM_RIGHT || eAlignMask == Image::TextAlign::BOTTOM || eAlignMask == Image::TextAlign::BOTTOM_LEFT) {
|
||||
//vertical bottom
|
||||
return borderHeight - txtHeight + baseLinePos;
|
||||
}
|
||||
|
||||
// top alignment
|
||||
return baseLinePos;
|
||||
}
|
||||
|
||||
std::string getFontFile(const char* family_name) {
|
||||
std::string fontPath = family_name;
|
||||
|
||||
std::map<std::string, std::string>::iterator it = fontCache.find(family_name);
|
||||
if ( it != fontCache.end() ) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
// check if the parameter is a font file shipped with the application
|
||||
std::string lowerCasePath = fontPath;
|
||||
std::transform(lowerCasePath.begin(), lowerCasePath.end(), lowerCasePath.begin(), ::tolower);
|
||||
if ( lowerCasePath.find(".ttf") != std::string::npos ) {
|
||||
fontPath = cocos2d::FileUtils::getInstance()->fullPathForFilename(fontPath.c_str());
|
||||
|
||||
FILE *f = fopen(fontPath.c_str(), "r");
|
||||
if ( f ) {
|
||||
fclose(f);
|
||||
fontCache.insert(std::pair<std::string, std::string>(family_name, fontPath));
|
||||
return fontPath;
|
||||
}
|
||||
}
|
||||
|
||||
// use fontconfig to match the parameter against the fonts installed on the system
|
||||
FcPattern *pattern = FcPatternBuild (0, FC_FAMILY, FcTypeString, family_name, (char *) 0);
|
||||
FcConfigSubstitute(0, pattern, FcMatchPattern);
|
||||
FcDefaultSubstitute(pattern);
|
||||
|
||||
FcResult result;
|
||||
FcPattern *font = FcFontMatch(0, pattern, &result);
|
||||
if ( font ) {
|
||||
FcChar8 *s = NULL;
|
||||
if ( FcPatternGetString(font, FC_FILE, 0, &s) == FcResultMatch ) {
|
||||
fontPath = (const char*)s;
|
||||
|
||||
FcPatternDestroy(font);
|
||||
FcPatternDestroy(pattern);
|
||||
|
||||
fontCache.insert(std::pair<std::string, std::string>(family_name, fontPath));
|
||||
return fontPath;
|
||||
}
|
||||
FcPatternDestroy(font);
|
||||
}
|
||||
FcPatternDestroy(pattern);
|
||||
|
||||
return family_name;
|
||||
}
|
||||
|
||||
bool getBitmap(const char *text, int nWidth, int nHeight, Image::TextAlign eAlignMask, const char * pFontName, float fontSize) {
|
||||
if (libError) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FT_Face face;
|
||||
std::string fontfile = getFontFile(pFontName);
|
||||
if ( FT_New_Face(library, fontfile.c_str(), 0, &face) ) {
|
||||
//no valid font found use default
|
||||
if ( FT_New_Face(library, "/usr/share/fonts/truetype/freefont/FreeSerif.ttf", 0, &face) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//select utf8 charmap
|
||||
if ( FT_Select_Charmap(face, FT_ENCODING_UNICODE) ) {
|
||||
FT_Done_Face(face);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( FT_Set_Pixel_Sizes(face, fontSize, fontSize) ) {
|
||||
FT_Done_Face(face);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( divideString(face, text, nWidth, nHeight) == false ) {
|
||||
FT_Done_Face(face);
|
||||
return false;
|
||||
}
|
||||
|
||||
//compute the final line width
|
||||
iMaxLineWidth = MAX(iMaxLineWidth, nWidth);
|
||||
|
||||
//compute the final line height
|
||||
iMaxLineHeight = ceilf(FT_MulFix( face->bbox.yMax - face->bbox.yMin, face->size->metrics.y_scale )/64.0f);
|
||||
int lineHeight = face->size->metrics.height>>6;
|
||||
if ( textLines.size() > 0 ) {
|
||||
iMaxLineHeight += (lineHeight * (textLines.size() -1));
|
||||
}
|
||||
int txtHeight = iMaxLineHeight;
|
||||
iMaxLineHeight = MAX(iMaxLineHeight, nHeight);
|
||||
|
||||
_data = new unsigned char[iMaxLineWidth * iMaxLineHeight * 4];
|
||||
memset(_data,0, iMaxLineWidth * iMaxLineHeight*4);
|
||||
|
||||
int iCurYCursor = computeLineStartY(face, eAlignMask, txtHeight, iMaxLineHeight);
|
||||
|
||||
int lineCount = textLines.size();
|
||||
for (int line = 0; line < lineCount; line++) {
|
||||
int iCurXCursor = computeLineStart(face, eAlignMask, line);
|
||||
|
||||
int glyphCount = textLines.at(line).glyphs.size();
|
||||
for (int i = 0; i < glyphCount; i++) {
|
||||
LineBreakGlyph glyph = textLines.at(line).glyphs.at(i);
|
||||
|
||||
if (FT_Load_Glyph(face, glyph.glyphIndex, FT_LOAD_RENDER)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
FT_Bitmap& bitmap = face->glyph->bitmap;
|
||||
int yoffset = iCurYCursor - (face->glyph->metrics.horiBearingY >> 6);
|
||||
int xoffset = iCurXCursor + glyph.paintPosition;
|
||||
|
||||
for (int y = 0; y < bitmap.rows; ++y) {
|
||||
int iY = yoffset + y;
|
||||
if (iY>=iMaxLineHeight) {
|
||||
//exceed the height truncate
|
||||
break;
|
||||
}
|
||||
iY *= iMaxLineWidth;
|
||||
|
||||
int bitmap_y = y * bitmap.width;
|
||||
|
||||
for (int x = 0; x < bitmap.width; ++x) {
|
||||
unsigned char cTemp = bitmap.buffer[bitmap_y + x];
|
||||
if (cTemp == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int iX = xoffset + x;
|
||||
|
||||
int iTemp = cTemp << 24 | cTemp << 16 | cTemp << 8 | cTemp;
|
||||
*(int*) &_data[(iY + iX) * 4 + 0] = iTemp;
|
||||
}
|
||||
}
|
||||
}
|
||||
// step to next line
|
||||
iCurYCursor += lineHeight;
|
||||
}
|
||||
|
||||
// free face
|
||||
FT_Done_Face(face);
|
||||
return true;
|
||||
}
|
||||
|
||||
public:
|
||||
FT_Library library;
|
||||
|
||||
unsigned char *_data;
|
||||
int libError;
|
||||
std::vector<LineBreakLine> textLines;
|
||||
int iMaxLineWidth;
|
||||
int iMaxLineHeight;
|
||||
};
|
||||
|
||||
static BitmapDC& sharedBitmapDC()
|
||||
{
|
||||
static BitmapDC s_BmpDC;
|
||||
return s_BmpDC;
|
||||
}
|
||||
|
||||
bool Image::initWithString(
|
||||
const char * text,
|
||||
int width/* = 0*/,
|
||||
int height/* = 0*/,
|
||||
TextAlign alignMask/* = kAlignCenter*/,
|
||||
const char * fontName/* = nil*/,
|
||||
int size/* = 0*/)
|
||||
{
|
||||
bool ret = false;
|
||||
do
|
||||
{
|
||||
CC_BREAK_IF(!text || 0 == strlen(text));
|
||||
|
||||
BitmapDC &dc = sharedBitmapDC();
|
||||
|
||||
CC_BREAK_IF(! dc.getBitmap(text, width, height, alignMask, fontName, size));
|
||||
|
||||
// assign the dc._data to _data in order to save time
|
||||
_data = dc._data;
|
||||
CC_BREAK_IF(! _data);
|
||||
|
||||
_width = (short)dc.iMaxLineWidth;
|
||||
_height = (short)dc.iMaxLineHeight;
|
||||
_renderFormat = Texture2D::PixelFormat::RGBA8888;
|
||||
_preMulti = true;
|
||||
_dataLen = _width * _height * 4;
|
||||
|
||||
ret = true;
|
||||
|
||||
dc.reset();
|
||||
}while (0);
|
||||
|
||||
//do nothing
|
||||
return ret;
|
||||
}
|
||||
|
||||
NS_CC_END
|
||||
|
||||
#endif // CC_TARGET_PLATFORM == CC_PLATFORM_LINUX
|
|
@ -1,51 +0,0 @@
|
|||
/****************************************************************************
|
||||
Copyright (c) 2010-2012 cocos2d-x.org
|
||||
Copyright (c) 2013-2014 Chukong Technologies Inc.
|
||||
|
||||
http://www.cocos2d-x.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#include "CCPlatformConfig.h"
|
||||
#if CC_TARGET_PLATFORM == CC_PLATFORM_MAC
|
||||
|
||||
#include "platform/CCDevice.h"
|
||||
|
||||
NS_CC_BEGIN
|
||||
|
||||
int Device::getDPI()
|
||||
{
|
||||
//TODO:
|
||||
return 160;
|
||||
}
|
||||
|
||||
void Device::setAccelerometerEnabled(bool isEnabled)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Device::setAccelerometerInterval(float interval)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
NS_CC_END
|
||||
|
||||
#endif // CC_TARGET_PLATFORM == CC_PLATFORM_MAC
|
|
@ -26,21 +26,30 @@ THE SOFTWARE.
|
|||
#include "CCPlatformConfig.h"
|
||||
#if CC_TARGET_PLATFORM == CC_PLATFORM_MAC
|
||||
|
||||
#include "CCImageCommon_cpp.h"
|
||||
#include "platform/CCDevice.h"
|
||||
#include <Foundation/Foundation.h>
|
||||
#include <Cocoa/Cocoa.h>
|
||||
#include "CCDirector.h"
|
||||
#include "ccMacros.h"
|
||||
#include "CCImage.h"
|
||||
#include "CCFileUtils.h"
|
||||
#include "CCTexture2D.h"
|
||||
#include <string>
|
||||
#include <sys/stat.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include "ccTypes.h"
|
||||
|
||||
NS_CC_BEGIN
|
||||
|
||||
int Device::getDPI()
|
||||
{
|
||||
//TODO:
|
||||
return 160;
|
||||
}
|
||||
|
||||
void Device::setAccelerometerEnabled(bool isEnabled)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Device::setAccelerometerInterval(float interval)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int height;
|
||||
|
@ -50,10 +59,10 @@ typedef struct
|
|||
unsigned char* data;
|
||||
} tImageInfo;
|
||||
|
||||
static bool _initWithString(const char * text, cocos2d::Image::TextAlign align, const char * fontName, int size, tImageInfo* info, cocos2d::Color3B* strokeColor)
|
||||
static bool _initWithString(const char * text, Device::TextAlign align, const char * fontName, int size, tImageInfo* info, Color3B* strokeColor)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
|
||||
CCASSERT(text, "Invalid pText");
|
||||
CCASSERT(info, "Invalid pInfo");
|
||||
|
||||
|
@ -62,10 +71,10 @@ static bool _initWithString(const char * text, cocos2d::Image::TextAlign align,
|
|||
|
||||
// font
|
||||
NSFont *font = [[NSFontManager sharedFontManager]
|
||||
fontWithFamily:[NSString stringWithUTF8String:fontName]
|
||||
fontWithFamily:[NSString stringWithUTF8String:fontName]
|
||||
traits:NSUnboldFontMask | NSUnitalicFontMask
|
||||
weight:0
|
||||
size:size];
|
||||
weight:0
|
||||
size:size];
|
||||
|
||||
if (font == nil) {
|
||||
font = [[NSFontManager sharedFontManager]
|
||||
|
@ -89,20 +98,20 @@ static bool _initWithString(const char * text, cocos2d::Image::TextAlign align,
|
|||
unsigned horiFlag = (int)align & 0x0f;
|
||||
unsigned vertFlag = ((int)align >> 4) & 0x0f;
|
||||
NSTextAlignment textAlign = (2 == horiFlag) ? NSRightTextAlignment
|
||||
: (3 == horiFlag) ? NSCenterTextAlignment
|
||||
: NSLeftTextAlignment;
|
||||
: (3 == horiFlag) ? NSCenterTextAlignment
|
||||
: NSLeftTextAlignment;
|
||||
|
||||
NSMutableParagraphStyle *paragraphStyle = [[[NSMutableParagraphStyle alloc] init] autorelease];
|
||||
[paragraphStyle setParagraphStyle:[NSParagraphStyle defaultParagraphStyle]];
|
||||
[paragraphStyle setLineBreakMode:NSLineBreakByCharWrapping];
|
||||
[paragraphStyle setAlignment:textAlign];
|
||||
|
||||
|
||||
// attribute
|
||||
NSDictionary* tokenAttributesDict = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
foregroundColor,NSForegroundColorAttributeName,
|
||||
font, NSFontAttributeName,
|
||||
paragraphStyle, NSParagraphStyleAttributeName, nil];
|
||||
|
||||
|
||||
// linebreak
|
||||
if (info->width > 0) {
|
||||
if ([string sizeWithAttributes:tokenAttributesDict].width > info->width) {
|
||||
|
@ -124,29 +133,29 @@ static bool _initWithString(const char * text, cocos2d::Image::TextAlign align,
|
|||
string = lineBreak;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
NSAttributedString *stringWithAttributes =[[[NSAttributedString alloc] initWithString:string
|
||||
attributes:tokenAttributesDict] autorelease];
|
||||
|
||||
attributes:tokenAttributesDict] autorelease];
|
||||
|
||||
NSSize realDimensions = [stringWithAttributes size];
|
||||
// Mac crashes if the width or height is 0
|
||||
CC_BREAK_IF(realDimensions.width <= 0 || realDimensions.height <= 0);
|
||||
|
||||
|
||||
CGSize dimensions = CGSizeMake(info->width, info->height);
|
||||
|
||||
|
||||
|
||||
if(dimensions.width <= 0 && dimensions.height <= 0) {
|
||||
dimensions.width = realDimensions.width;
|
||||
dimensions.height = realDimensions.height;
|
||||
} else if (dimensions.height <= 0) {
|
||||
dimensions.height = realDimensions.height;
|
||||
}
|
||||
|
||||
|
||||
NSInteger POTWide = dimensions.width;
|
||||
NSInteger POTHigh = MAX(dimensions.height, realDimensions.height);
|
||||
unsigned char* data;
|
||||
//Alignment
|
||||
|
||||
|
||||
CGFloat xPadding = 0;
|
||||
switch (textAlign) {
|
||||
case NSLeftTextAlignment: xPadding = 0; break;
|
||||
|
@ -154,7 +163,7 @@ static bool _initWithString(const char * text, cocos2d::Image::TextAlign align,
|
|||
case NSRightTextAlignment: xPadding = dimensions.width-realDimensions.width; break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
|
||||
// 1: TOP
|
||||
// 2: BOTTOM
|
||||
// 3: CENTER
|
||||
|
@ -166,7 +175,7 @@ static bool _initWithString(const char * text, cocos2d::Image::TextAlign align,
|
|||
NSRect textRect = NSMakeRect(xPadding, POTHigh - dimensions.height + yPadding, realDimensions.width, realDimensions.height);
|
||||
//Disable antialias
|
||||
|
||||
[[NSGraphicsContext currentContext] setShouldAntialias:NO];
|
||||
[[NSGraphicsContext currentContext] setShouldAntialias:NO];
|
||||
|
||||
NSImage *image = [[NSImage alloc] initWithSize:NSMakeSize(POTWide, POTHigh)];
|
||||
|
||||
|
@ -175,7 +184,7 @@ static bool _initWithString(const char * text, cocos2d::Image::TextAlign align,
|
|||
// patch for mac retina display and lableTTF
|
||||
[[NSAffineTransform transform] set];
|
||||
|
||||
//[stringWithAttributes drawAtPoint:NSMakePoint(xPadding, offsetY)]; // draw at offset position
|
||||
//[stringWithAttributes drawAtPoint:NSMakePoint(xPadding, offsetY)]; // draw at offset position
|
||||
[stringWithAttributes drawInRect:textRect];
|
||||
//[stringWithAttributes drawInRect:textRect withAttributes:tokenAttributesDict];
|
||||
NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithFocusedViewRect:NSMakeRect (0.0f, 0.0f, POTWide, POTHigh)];
|
||||
|
@ -202,36 +211,25 @@ static bool _initWithString(const char * text, cocos2d::Image::TextAlign align,
|
|||
return ret;
|
||||
}
|
||||
|
||||
bool Image::initWithString(
|
||||
const char * text,
|
||||
int width,
|
||||
int height,
|
||||
TextAlign alignMask,
|
||||
const char * fontName,
|
||||
int size)
|
||||
Data Device::getTextureDataForText(const char * text,const FontDefinition& textDefinition,TextAlign align,int &width,int &height)
|
||||
{
|
||||
tImageInfo info = {0};
|
||||
info.width = width;
|
||||
info.height = height;
|
||||
|
||||
if (! _initWithString(text, alignMask, fontName, size, &info, nullptr)) //pStrokeColor))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
_height = (short)info.height;
|
||||
_width = (short)info.width;
|
||||
_renderFormat = Texture2D::PixelFormat::RGBA8888;
|
||||
_preMulti = info.isPremultipliedAlpha;
|
||||
if (_data) {
|
||||
CC_SAFE_DELETE_ARRAY(_data);
|
||||
}
|
||||
_data = info.data;
|
||||
_dataLen = _width * _height * 4;
|
||||
|
||||
return true;
|
||||
Data ret;
|
||||
do {
|
||||
tImageInfo info = {0};
|
||||
info.width = textDefinition._dimensions.width;
|
||||
info.height = textDefinition._dimensions.height;
|
||||
|
||||
if (! _initWithString(text, align, textDefinition._fontName.c_str(), textDefinition._fontSize, &info, nullptr)) //pStrokeColor))
|
||||
{
|
||||
break;
|
||||
}
|
||||
height = (short)info.height;
|
||||
widht = (short)info.width;
|
||||
ret.fastSet(info.data,width * height * 4);
|
||||
} while (0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
NS_CC_END
|
||||
|
||||
#endif // CC_TARGET_PLATFORM == CC_PLATFORM_MAC
|
|
@ -27,6 +27,7 @@ THE SOFTWARE.
|
|||
#if CC_TARGET_PLATFORM == CC_PLATFORM_WIN32
|
||||
|
||||
#include "platform/CCDevice.h"
|
||||
#include "platform/CCFileUtils.h"
|
||||
#include "CCStdC.h"
|
||||
|
||||
NS_CC_BEGIN
|
||||
|
@ -51,6 +52,408 @@ void Device::setAccelerometerEnabled(bool isEnabled)
|
|||
void Device::setAccelerometerInterval(float interval)
|
||||
{}
|
||||
|
||||
class BitmapDC
|
||||
{
|
||||
public:
|
||||
BitmapDC(HWND hWnd = NULL)
|
||||
: _DC(NULL)
|
||||
, _bmp(NULL)
|
||||
, _font((HFONT)GetStockObject(DEFAULT_GUI_FONT))
|
||||
, _wnd(NULL)
|
||||
{
|
||||
_wnd = hWnd;
|
||||
HDC hdc = GetDC(hWnd);
|
||||
_DC = CreateCompatibleDC(hdc);
|
||||
ReleaseDC(hWnd, hdc);
|
||||
}
|
||||
|
||||
~BitmapDC()
|
||||
{
|
||||
prepareBitmap(0, 0);
|
||||
if (_DC)
|
||||
{
|
||||
DeleteDC(_DC);
|
||||
}
|
||||
/*HFONT hDefFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
|
||||
if (hDefFont != _font)
|
||||
{
|
||||
DeleteObject(_font);
|
||||
_font = 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 = NULL;
|
||||
}
|
||||
}*/
|
||||
removeCustomFont();
|
||||
}
|
||||
|
||||
wchar_t * utf8ToUtf16(std::string nString)
|
||||
{
|
||||
wchar_t * pwszBuffer = NULL;
|
||||
do
|
||||
{
|
||||
if (nString.size() < 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
// utf-8 to utf-16
|
||||
int nLen = nString.size();
|
||||
int nBufLen = nLen + 1;
|
||||
pwszBuffer = new wchar_t[nBufLen];
|
||||
CC_BREAK_IF(! pwszBuffer);
|
||||
memset(pwszBuffer,0,nBufLen);
|
||||
nLen = MultiByteToWideChar(CP_UTF8, 0, nString.c_str(), nLen, pwszBuffer, nBufLen);
|
||||
pwszBuffer[nLen] = '\0';
|
||||
} while (0);
|
||||
return pwszBuffer;
|
||||
|
||||
}
|
||||
|
||||
bool setFont(const char * pFontName = NULL, int nSize = 0)
|
||||
{
|
||||
bool bRet = false;
|
||||
do
|
||||
{
|
||||
std::string fontName = pFontName;
|
||||
std::string fontPath;
|
||||
HFONT hDefFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
|
||||
LOGFONTA tNewFont = {0};
|
||||
LOGFONTA tOldFont = {0};
|
||||
GetObjectA(hDefFont, sizeof(tNewFont), &tNewFont);
|
||||
if (fontName.c_str())
|
||||
{
|
||||
// create font from ttf file
|
||||
int nFindttf = fontName.find(".ttf");
|
||||
int nFindTTF = fontName.find(".TTF");
|
||||
if (nFindttf >= 0 || nFindTTF >= 0)
|
||||
{
|
||||
fontPath = FileUtils::getInstance()->fullPathForFilename(fontName.c_str());
|
||||
int nFindPos = fontName.rfind("/");
|
||||
fontName = &fontName[nFindPos+1];
|
||||
nFindPos = fontName.rfind(".");
|
||||
fontName = fontName.substr(0,nFindPos);
|
||||
}
|
||||
tNewFont.lfCharSet = DEFAULT_CHARSET;
|
||||
strcpy_s(tNewFont.lfFaceName, LF_FACESIZE, fontName.c_str());
|
||||
}
|
||||
if (nSize)
|
||||
{
|
||||
tNewFont.lfHeight = -nSize;
|
||||
}
|
||||
GetObjectA(_font, sizeof(tOldFont), &tOldFont);
|
||||
|
||||
if (tOldFont.lfHeight == tNewFont.lfHeight
|
||||
&& 0 == strcmp(tOldFont.lfFaceName, tNewFont.lfFaceName))
|
||||
{
|
||||
// already has the font
|
||||
bRet = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// 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 = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
_font = nullptr;
|
||||
|
||||
// disable Cleartype
|
||||
tNewFont.lfQuality = ANTIALIASED_QUALITY;
|
||||
|
||||
// create new font
|
||||
_font = CreateFontIndirectA(&tNewFont);
|
||||
if (! _font)
|
||||
{
|
||||
// create failed, use default font
|
||||
_font = hDefFont;
|
||||
break;
|
||||
}
|
||||
|
||||
bRet = true;
|
||||
} while (0);
|
||||
return bRet;
|
||||
}
|
||||
|
||||
SIZE sizeWithText(const wchar_t * pszText, int nLen, DWORD dwFmt, LONG nWidthLimit)
|
||||
{
|
||||
SIZE tRet = {0};
|
||||
do
|
||||
{
|
||||
CC_BREAK_IF(! pszText || nLen <= 0);
|
||||
|
||||
RECT rc = {0, 0, 0, 0};
|
||||
DWORD dwCalcFmt = DT_CALCRECT;
|
||||
|
||||
if (nWidthLimit > 0)
|
||||
{
|
||||
rc.right = nWidthLimit;
|
||||
dwCalcFmt |= DT_WORDBREAK
|
||||
| (dwFmt & DT_CENTER)
|
||||
| (dwFmt & DT_RIGHT);
|
||||
}
|
||||
// use current font to measure text extent
|
||||
HGDIOBJ hOld = SelectObject(_DC, _font);
|
||||
|
||||
// measure text size
|
||||
DrawTextW(_DC, pszText, nLen, &rc, dwCalcFmt);
|
||||
SelectObject(_DC, hOld);
|
||||
|
||||
tRet.cx = rc.right;
|
||||
tRet.cy = rc.bottom;
|
||||
} while (0);
|
||||
|
||||
return tRet;
|
||||
}
|
||||
|
||||
bool prepareBitmap(int nWidth, int nHeight)
|
||||
{
|
||||
// release bitmap
|
||||
if (_bmp)
|
||||
{
|
||||
DeleteObject(_bmp);
|
||||
_bmp = NULL;
|
||||
}
|
||||
if (nWidth > 0 && nHeight > 0)
|
||||
{
|
||||
_bmp = CreateBitmap(nWidth, nHeight, 1, 32, NULL);
|
||||
if (! _bmp)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int drawText(const char * pszText, SIZE& tSize, Device::TextAlign eAlign)
|
||||
{
|
||||
int nRet = 0;
|
||||
wchar_t * pwszBuffer = 0;
|
||||
do
|
||||
{
|
||||
CC_BREAK_IF(! pszText);
|
||||
|
||||
DWORD dwFmt = DT_WORDBREAK;
|
||||
DWORD dwHoriFlag = (int)eAlign & 0x0f;
|
||||
DWORD dwVertFlag = ((int)eAlign & 0xf0) >> 4;
|
||||
|
||||
switch (dwHoriFlag)
|
||||
{
|
||||
case 1: // left
|
||||
dwFmt |= DT_LEFT;
|
||||
break;
|
||||
case 2: // right
|
||||
dwFmt |= DT_RIGHT;
|
||||
break;
|
||||
case 3: // center
|
||||
dwFmt |= DT_CENTER;
|
||||
break;
|
||||
}
|
||||
|
||||
int nLen = strlen(pszText);
|
||||
// utf-8 to utf-16
|
||||
int nBufLen = nLen + 1;
|
||||
pwszBuffer = new wchar_t[nBufLen];
|
||||
CC_BREAK_IF(! pwszBuffer);
|
||||
|
||||
memset(pwszBuffer, 0, sizeof(wchar_t)*nBufLen);
|
||||
nLen = MultiByteToWideChar(CP_UTF8, 0, pszText, nLen, pwszBuffer, nBufLen);
|
||||
|
||||
SIZE newSize = sizeWithText(pwszBuffer, nLen, dwFmt, tSize.cx);
|
||||
|
||||
RECT rcText = {0};
|
||||
// if content width is 0, use text size as content size
|
||||
if (tSize.cx <= 0)
|
||||
{
|
||||
tSize = newSize;
|
||||
rcText.right = newSize.cx;
|
||||
rcText.bottom = newSize.cy;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
LONG offsetX = 0;
|
||||
LONG offsetY = 0;
|
||||
rcText.right = newSize.cx; // store the text width to rectangle
|
||||
|
||||
// calculate text horizontal offset
|
||||
if (1 != dwHoriFlag // and text isn't align to left
|
||||
&& newSize.cx < tSize.cx) // and text's width less then content width,
|
||||
{ // then need adjust offset of X.
|
||||
offsetX = (2 == dwHoriFlag) ? tSize.cx - newSize.cx // align to right
|
||||
: (tSize.cx - newSize.cx) / 2; // align to center
|
||||
}
|
||||
|
||||
// if content height is 0, use text height as content height
|
||||
// else if content height less than text height, use content height to draw text
|
||||
if (tSize.cy <= 0)
|
||||
{
|
||||
tSize.cy = newSize.cy;
|
||||
dwFmt |= DT_NOCLIP;
|
||||
rcText.bottom = newSize.cy; // store the text height to rectangle
|
||||
}
|
||||
else if (tSize.cy < newSize.cy)
|
||||
{
|
||||
// content height larger than text height need, clip text to rect
|
||||
rcText.bottom = tSize.cy;
|
||||
}
|
||||
else
|
||||
{
|
||||
rcText.bottom = newSize.cy; // store the text height to rectangle
|
||||
|
||||
// content larger than text, need adjust vertical position
|
||||
dwFmt |= DT_NOCLIP;
|
||||
|
||||
// calculate text vertical offset
|
||||
offsetY = (2 == dwVertFlag) ? tSize.cy - newSize.cy // align to bottom
|
||||
: (3 == dwVertFlag) ? (tSize.cy - newSize.cy) / 2 // align to middle
|
||||
: 0; // align to top
|
||||
}
|
||||
|
||||
if (offsetX || offsetY)
|
||||
{
|
||||
OffsetRect(&rcText, offsetX, offsetY);
|
||||
}
|
||||
}
|
||||
|
||||
CC_BREAK_IF(! prepareBitmap(tSize.cx, tSize.cy));
|
||||
|
||||
// draw text
|
||||
HGDIOBJ hOldFont = SelectObject(_DC, _font);
|
||||
HGDIOBJ hOldBmp = SelectObject(_DC, _bmp);
|
||||
|
||||
SetBkMode(_DC, TRANSPARENT);
|
||||
SetTextColor(_DC, RGB(255, 255, 255)); // white color
|
||||
|
||||
// draw text
|
||||
nRet = DrawTextW(_DC, pwszBuffer, nLen, &rcText, dwFmt);
|
||||
//DrawTextA(_DC, pszText, nLen, &rcText, dwFmt);
|
||||
|
||||
SelectObject(_DC, hOldBmp);
|
||||
SelectObject(_DC, hOldFont);
|
||||
} while (0);
|
||||
CC_SAFE_DELETE_ARRAY(pwszBuffer);
|
||||
return nRet;
|
||||
}
|
||||
|
||||
CC_SYNTHESIZE_READONLY(HDC, _DC, DC);
|
||||
CC_SYNTHESIZE_READONLY(HBITMAP, _bmp, Bitmap);
|
||||
private:
|
||||
|
||||
friend class Image;
|
||||
HFONT _font;
|
||||
HWND _wnd;
|
||||
std::string _curFontPath;
|
||||
|
||||
void removeCustomFont()
|
||||
{
|
||||
HFONT hDefFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
|
||||
if (hDefFont != _font)
|
||||
{
|
||||
DeleteObject(_font);
|
||||
_font = 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 = NULL;
|
||||
}
|
||||
_curFontPath.clear();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static BitmapDC& sharedBitmapDC()
|
||||
{
|
||||
static BitmapDC s_BmpDC;
|
||||
return s_BmpDC;
|
||||
}
|
||||
|
||||
Data Device::getTextureDataForText(const char * text,const FontDefinition& textDefinition,TextAlign align,int &width,int &height)
|
||||
{
|
||||
Data ret;
|
||||
do
|
||||
{
|
||||
BitmapDC& dc = sharedBitmapDC();
|
||||
|
||||
if (! dc.setFont(textDefinition._fontName.c_str(), textDefinition._fontSize))
|
||||
{
|
||||
log("Can't found font(%s), use system default", textDefinition._fontName.c_str());
|
||||
}
|
||||
|
||||
// draw text
|
||||
SIZE size = {textDefinition._dimensions.width, textDefinition._dimensions.height};
|
||||
CC_BREAK_IF(! dc.drawText(text, size, align));
|
||||
|
||||
int dataLen = size.cx * size.cy * 4;
|
||||
unsigned char* dataBuf = new unsigned char[dataLen];
|
||||
CC_BREAK_IF(! dataBuf);
|
||||
|
||||
struct
|
||||
{
|
||||
BITMAPINFOHEADER bmiHeader;
|
||||
int mask[4];
|
||||
} bi = {0};
|
||||
bi.bmiHeader.biSize = sizeof(bi.bmiHeader);
|
||||
CC_BREAK_IF(! GetDIBits(dc.getDC(), dc.getBitmap(), 0, 0,
|
||||
NULL, (LPBITMAPINFO)&bi, DIB_RGB_COLORS));
|
||||
|
||||
width = (short)size.cx;
|
||||
height = (short)size.cy;
|
||||
|
||||
// copy pixed data
|
||||
bi.bmiHeader.biHeight = (bi.bmiHeader.biHeight > 0)
|
||||
? - bi.bmiHeader.biHeight : bi.bmiHeader.biHeight;
|
||||
GetDIBits(dc.getDC(), dc.getBitmap(), 0, height, dataBuf,
|
||||
(LPBITMAPINFO)&bi, DIB_RGB_COLORS);
|
||||
|
||||
// change pixel's alpha value to 255, when it's RGB != 0
|
||||
COLORREF * pPixel = NULL;
|
||||
for (int y = 0; y < height; ++y)
|
||||
{
|
||||
pPixel = (COLORREF *)dataBuf + y * width;
|
||||
for (int x = 0; x < width; ++x)
|
||||
{
|
||||
COLORREF& clr = *pPixel;
|
||||
if (GetRValue(clr) || GetGValue(clr) || GetBValue(clr))
|
||||
{
|
||||
clr |= 0xff000000;
|
||||
}
|
||||
++pPixel;
|
||||
}
|
||||
}
|
||||
|
||||
ret.fastSet(dataBuf,dataLen);
|
||||
} while (0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
NS_CC_END
|
||||
|
||||
#endif // CC_TARGET_PLATFORM == CC_PLATFORM_WIN32
|
||||
|
|
|
@ -1,448 +0,0 @@
|
|||
/****************************************************************************
|
||||
Copyright (c) 2010-2012 cocos2d-x.org
|
||||
Copyright (c) 2013-2014 Chukong Technologies Inc.
|
||||
|
||||
http://www.cocos2d-x.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
#define __CC_PLATFORM_IMAGE_CPP__
|
||||
|
||||
#include "CCPlatformConfig.h"
|
||||
#if CC_TARGET_PLATFORM == CC_PLATFORM_WIN32
|
||||
|
||||
#if _MSC_VER
|
||||
#include <cctype>
|
||||
#endif
|
||||
#include "platform/CCImageCommon_cpp.h"
|
||||
|
||||
NS_CC_BEGIN
|
||||
|
||||
/**
|
||||
@brief A memory DC which uses to draw text on bitmap.
|
||||
*/
|
||||
class BitmapDC
|
||||
{
|
||||
public:
|
||||
BitmapDC(HWND hWnd = NULL)
|
||||
: _DC(NULL)
|
||||
, _bmp(NULL)
|
||||
, _font((HFONT)GetStockObject(DEFAULT_GUI_FONT))
|
||||
, _wnd(NULL)
|
||||
{
|
||||
_wnd = hWnd;
|
||||
HDC hdc = GetDC(hWnd);
|
||||
_DC = CreateCompatibleDC(hdc);
|
||||
ReleaseDC(hWnd, hdc);
|
||||
}
|
||||
|
||||
~BitmapDC()
|
||||
{
|
||||
prepareBitmap(0, 0);
|
||||
if (_DC)
|
||||
{
|
||||
DeleteDC(_DC);
|
||||
}
|
||||
HFONT hDefFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
|
||||
if (hDefFont != _font)
|
||||
{
|
||||
DeleteObject(_font);
|
||||
_font = 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 = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wchar_t * utf8ToUtf16(std::string nString)
|
||||
{
|
||||
wchar_t * pwszBuffer = NULL;
|
||||
do
|
||||
{
|
||||
if (nString.size() < 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
// utf-8 to utf-16
|
||||
int nLen = nString.size();
|
||||
int nBufLen = nLen + 1;
|
||||
pwszBuffer = new wchar_t[nBufLen];
|
||||
CC_BREAK_IF(! pwszBuffer);
|
||||
memset(pwszBuffer,0,nBufLen);
|
||||
nLen = MultiByteToWideChar(CP_UTF8, 0, nString.c_str(), nLen, pwszBuffer, nBufLen);
|
||||
pwszBuffer[nLen] = '\0';
|
||||
} while (0);
|
||||
return pwszBuffer;
|
||||
|
||||
}
|
||||
|
||||
bool setFont(const char * pFontName = NULL, int nSize = 0)
|
||||
{
|
||||
bool bRet = false;
|
||||
do
|
||||
{
|
||||
std::string fontName = pFontName;
|
||||
std::string fontPath;
|
||||
HFONT hDefFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
|
||||
LOGFONTA tNewFont = {0};
|
||||
LOGFONTA tOldFont = {0};
|
||||
GetObjectA(hDefFont, sizeof(tNewFont), &tNewFont);
|
||||
if (fontName.c_str())
|
||||
{
|
||||
// create font from ttf file
|
||||
int nFindttf = fontName.find(".ttf");
|
||||
int nFindTTF = fontName.find(".TTF");
|
||||
if (nFindttf >= 0 || nFindTTF >= 0)
|
||||
{
|
||||
fontPath = FileUtils::getInstance()->fullPathForFilename(fontName.c_str());
|
||||
int nFindPos = fontName.rfind("/");
|
||||
fontName = &fontName[nFindPos+1];
|
||||
nFindPos = fontName.rfind(".");
|
||||
fontName = fontName.substr(0,nFindPos);
|
||||
}
|
||||
tNewFont.lfCharSet = DEFAULT_CHARSET;
|
||||
strcpy_s(tNewFont.lfFaceName, LF_FACESIZE, fontName.c_str());
|
||||
}
|
||||
if (nSize)
|
||||
{
|
||||
tNewFont.lfHeight = -nSize;
|
||||
}
|
||||
GetObjectA(_font, sizeof(tOldFont), &tOldFont);
|
||||
|
||||
if (tOldFont.lfHeight == tNewFont.lfHeight
|
||||
&& 0 == strcmp(tOldFont.lfFaceName, tNewFont.lfFaceName))
|
||||
{
|
||||
// already has the font
|
||||
bRet = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// delete old font
|
||||
if (_font != hDefFont)
|
||||
{
|
||||
DeleteObject(_font);
|
||||
// release old font register
|
||||
if (_curFontPath.size() > 0)
|
||||
{
|
||||
wchar_t * pwszBuffer = utf8ToUtf16(_curFontPath);
|
||||
if (pwszBuffer)
|
||||
{
|
||||
if(RemoveFontResource(pwszBuffer))
|
||||
{
|
||||
SendMessage( _wnd, WM_FONTCHANGE, 0, 0);
|
||||
}
|
||||
delete [] pwszBuffer;
|
||||
pwszBuffer = NULL;
|
||||
}
|
||||
}
|
||||
if (fontPath.size() > 0)
|
||||
_curFontPath = fontPath;
|
||||
else
|
||||
_curFontPath.clear();
|
||||
// register temp font
|
||||
if (_curFontPath.size() > 0)
|
||||
{
|
||||
wchar_t * pwszBuffer = utf8ToUtf16(_curFontPath);
|
||||
if (pwszBuffer)
|
||||
{
|
||||
if(AddFontResource(pwszBuffer))
|
||||
{
|
||||
SendMessage( _wnd, WM_FONTCHANGE, 0, 0);
|
||||
}
|
||||
delete [] pwszBuffer;
|
||||
pwszBuffer = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
_font = NULL;
|
||||
|
||||
// disable Cleartype
|
||||
tNewFont.lfQuality = ANTIALIASED_QUALITY;
|
||||
|
||||
// create new font
|
||||
_font = CreateFontIndirectA(&tNewFont);
|
||||
if (! _font)
|
||||
{
|
||||
// create failed, use default font
|
||||
_font = hDefFont;
|
||||
break;
|
||||
}
|
||||
|
||||
bRet = true;
|
||||
} while (0);
|
||||
return bRet;
|
||||
}
|
||||
|
||||
SIZE sizeWithText(const wchar_t * pszText, int nLen, DWORD dwFmt, LONG nWidthLimit)
|
||||
{
|
||||
SIZE tRet = {0};
|
||||
do
|
||||
{
|
||||
CC_BREAK_IF(! pszText || nLen <= 0);
|
||||
|
||||
RECT rc = {0, 0, 0, 0};
|
||||
DWORD dwCalcFmt = DT_CALCRECT;
|
||||
|
||||
if (nWidthLimit > 0)
|
||||
{
|
||||
rc.right = nWidthLimit;
|
||||
dwCalcFmt |= DT_WORDBREAK
|
||||
| (dwFmt & DT_CENTER)
|
||||
| (dwFmt & DT_RIGHT);
|
||||
}
|
||||
// use current font to measure text extent
|
||||
HGDIOBJ hOld = SelectObject(_DC, _font);
|
||||
|
||||
// measure text size
|
||||
DrawTextW(_DC, pszText, nLen, &rc, dwCalcFmt);
|
||||
SelectObject(_DC, hOld);
|
||||
|
||||
tRet.cx = rc.right;
|
||||
tRet.cy = rc.bottom;
|
||||
} while (0);
|
||||
|
||||
return tRet;
|
||||
}
|
||||
|
||||
bool prepareBitmap(int nWidth, int nHeight)
|
||||
{
|
||||
// release bitmap
|
||||
if (_bmp)
|
||||
{
|
||||
DeleteObject(_bmp);
|
||||
_bmp = NULL;
|
||||
}
|
||||
if (nWidth > 0 && nHeight > 0)
|
||||
{
|
||||
_bmp = CreateBitmap(nWidth, nHeight, 1, 32, NULL);
|
||||
if (! _bmp)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int drawText(const char * pszText, SIZE& tSize, Image::TextAlign eAlign)
|
||||
{
|
||||
int nRet = 0;
|
||||
wchar_t * pwszBuffer = 0;
|
||||
do
|
||||
{
|
||||
CC_BREAK_IF(! pszText);
|
||||
|
||||
DWORD dwFmt = DT_WORDBREAK;
|
||||
DWORD dwHoriFlag = (int)eAlign & 0x0f;
|
||||
DWORD dwVertFlag = ((int)eAlign & 0xf0) >> 4;
|
||||
|
||||
switch (dwHoriFlag)
|
||||
{
|
||||
case 1: // left
|
||||
dwFmt |= DT_LEFT;
|
||||
break;
|
||||
case 2: // right
|
||||
dwFmt |= DT_RIGHT;
|
||||
break;
|
||||
case 3: // center
|
||||
dwFmt |= DT_CENTER;
|
||||
break;
|
||||
}
|
||||
|
||||
int nLen = strlen(pszText);
|
||||
// utf-8 to utf-16
|
||||
int nBufLen = nLen + 1;
|
||||
pwszBuffer = new wchar_t[nBufLen];
|
||||
CC_BREAK_IF(! pwszBuffer);
|
||||
|
||||
memset(pwszBuffer, 0, sizeof(wchar_t)*nBufLen);
|
||||
nLen = MultiByteToWideChar(CP_UTF8, 0, pszText, nLen, pwszBuffer, nBufLen);
|
||||
|
||||
SIZE newSize = sizeWithText(pwszBuffer, nLen, dwFmt, tSize.cx);
|
||||
|
||||
RECT rcText = {0};
|
||||
// if content width is 0, use text size as content size
|
||||
if (tSize.cx <= 0)
|
||||
{
|
||||
tSize = newSize;
|
||||
rcText.right = newSize.cx;
|
||||
rcText.bottom = newSize.cy;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
LONG offsetX = 0;
|
||||
LONG offsetY = 0;
|
||||
rcText.right = newSize.cx; // store the text width to rectangle
|
||||
|
||||
// calculate text horizontal offset
|
||||
if (1 != dwHoriFlag // and text isn't align to left
|
||||
&& newSize.cx < tSize.cx) // and text's width less then content width,
|
||||
{ // then need adjust offset of X.
|
||||
offsetX = (2 == dwHoriFlag) ? tSize.cx - newSize.cx // align to right
|
||||
: (tSize.cx - newSize.cx) / 2; // align to center
|
||||
}
|
||||
|
||||
// if content height is 0, use text height as content height
|
||||
// else if content height less than text height, use content height to draw text
|
||||
if (tSize.cy <= 0)
|
||||
{
|
||||
tSize.cy = newSize.cy;
|
||||
dwFmt |= DT_NOCLIP;
|
||||
rcText.bottom = newSize.cy; // store the text height to rectangle
|
||||
}
|
||||
else if (tSize.cy < newSize.cy)
|
||||
{
|
||||
// content height larger than text height need, clip text to rect
|
||||
rcText.bottom = tSize.cy;
|
||||
}
|
||||
else
|
||||
{
|
||||
rcText.bottom = newSize.cy; // store the text height to rectangle
|
||||
|
||||
// content larger than text, need adjust vertical position
|
||||
dwFmt |= DT_NOCLIP;
|
||||
|
||||
// calculate text vertical offset
|
||||
offsetY = (2 == dwVertFlag) ? tSize.cy - newSize.cy // align to bottom
|
||||
: (3 == dwVertFlag) ? (tSize.cy - newSize.cy) / 2 // align to middle
|
||||
: 0; // align to top
|
||||
}
|
||||
|
||||
if (offsetX || offsetY)
|
||||
{
|
||||
OffsetRect(&rcText, offsetX, offsetY);
|
||||
}
|
||||
}
|
||||
|
||||
CC_BREAK_IF(! prepareBitmap(tSize.cx, tSize.cy));
|
||||
|
||||
// draw text
|
||||
HGDIOBJ hOldFont = SelectObject(_DC, _font);
|
||||
HGDIOBJ hOldBmp = SelectObject(_DC, _bmp);
|
||||
|
||||
SetBkMode(_DC, TRANSPARENT);
|
||||
SetTextColor(_DC, RGB(255, 255, 255)); // white color
|
||||
|
||||
// draw text
|
||||
nRet = DrawTextW(_DC, pwszBuffer, nLen, &rcText, dwFmt);
|
||||
//DrawTextA(_DC, pszText, nLen, &rcText, dwFmt);
|
||||
|
||||
SelectObject(_DC, hOldBmp);
|
||||
SelectObject(_DC, hOldFont);
|
||||
} while (0);
|
||||
CC_SAFE_DELETE_ARRAY(pwszBuffer);
|
||||
return nRet;
|
||||
}
|
||||
|
||||
CC_SYNTHESIZE_READONLY(HDC, _DC, DC);
|
||||
CC_SYNTHESIZE_READONLY(HBITMAP, _bmp, Bitmap);
|
||||
private:
|
||||
friend class Image;
|
||||
HFONT _font;
|
||||
HWND _wnd;
|
||||
std::string _curFontPath;
|
||||
};
|
||||
|
||||
static BitmapDC& sharedBitmapDC()
|
||||
{
|
||||
static BitmapDC s_BmpDC;
|
||||
return s_BmpDC;
|
||||
}
|
||||
|
||||
bool Image::initWithString(
|
||||
const char * pText,
|
||||
int nWidth/* = 0*/,
|
||||
int nHeight/* = 0*/,
|
||||
TextAlign eAlignMask/* = kAlignCenter*/,
|
||||
const char * pFontName/* = nil*/,
|
||||
int nSize/* = 0*/)
|
||||
{
|
||||
bool bRet = false;
|
||||
do
|
||||
{
|
||||
CC_BREAK_IF(! pText);
|
||||
|
||||
BitmapDC& dc = sharedBitmapDC();
|
||||
|
||||
if (! dc.setFont(pFontName, nSize))
|
||||
{
|
||||
log("Can't found font(%s), use system default", pFontName);
|
||||
}
|
||||
|
||||
// draw text
|
||||
SIZE size = {nWidth, nHeight};
|
||||
CC_BREAK_IF(! dc.drawText(pText, size, eAlignMask));
|
||||
|
||||
_dataLen = size.cx * size.cy * 4;
|
||||
_data = new unsigned char[_dataLen];
|
||||
CC_BREAK_IF(! _data);
|
||||
|
||||
struct
|
||||
{
|
||||
BITMAPINFOHEADER bmiHeader;
|
||||
int mask[4];
|
||||
} bi = {0};
|
||||
bi.bmiHeader.biSize = sizeof(bi.bmiHeader);
|
||||
CC_BREAK_IF(! GetDIBits(dc.getDC(), dc.getBitmap(), 0, 0,
|
||||
NULL, (LPBITMAPINFO)&bi, DIB_RGB_COLORS));
|
||||
|
||||
_width = (short)size.cx;
|
||||
_height = (short)size.cy;
|
||||
_preMulti = false;
|
||||
_renderFormat = Texture2D::PixelFormat::RGBA8888;
|
||||
// copy pixed data
|
||||
bi.bmiHeader.biHeight = (bi.bmiHeader.biHeight > 0)
|
||||
? - bi.bmiHeader.biHeight : bi.bmiHeader.biHeight;
|
||||
GetDIBits(dc.getDC(), dc.getBitmap(), 0, _height, _data,
|
||||
(LPBITMAPINFO)&bi, DIB_RGB_COLORS);
|
||||
|
||||
// change pixel's alpha value to 255, when it's RGB != 0
|
||||
COLORREF * pPixel = NULL;
|
||||
for (int y = 0; y < _height; ++y)
|
||||
{
|
||||
pPixel = (COLORREF *)_data + y * _width;
|
||||
for (int x = 0; x < _width; ++x)
|
||||
{
|
||||
COLORREF& clr = *pPixel;
|
||||
if (GetRValue(clr) || GetGValue(clr) || GetBValue(clr))
|
||||
{
|
||||
clr |= 0xff000000;
|
||||
}
|
||||
++pPixel;
|
||||
}
|
||||
}
|
||||
|
||||
bRet = true;
|
||||
} while (0);
|
||||
|
||||
return bRet;
|
||||
}
|
||||
|
||||
NS_CC_END
|
||||
|
||||
#endif // CC_TARGET_PLATFORM == CC_PLATFORM_WIN32
|
Loading…
Reference in New Issue