2013-04-09 09:21:53 +08:00
|
|
|
/****************************************************************************
|
|
|
|
Copyright (c) 2010 cocos2d-x.org
|
|
|
|
|
|
|
|
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 "platform/CCImageCommon_cpp.h"
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
#include <vector>
|
|
|
|
#include <string>
|
|
|
|
#include <sstream>
|
|
|
|
#include <algorithm>
|
|
|
|
#include "platform/CCImage.h"
|
|
|
|
#include "platform/CCFileUtils.h"
|
|
|
|
#include "platform/CCCommon.h"
|
|
|
|
#include "CCStdC.h"
|
|
|
|
#include <map>
|
2013-05-15 12:38:56 +08:00
|
|
|
#include <SDL/SDL.h>
|
|
|
|
#include <SDL/SDL_ttf.h>
|
2013-05-16 11:21:30 +08:00
|
|
|
#include <net/arpa/inet.h>
|
2013-04-09 09:21:53 +08:00
|
|
|
|
|
|
|
#define szFont_kenning 2
|
|
|
|
|
|
|
|
#define RSHIFT6(num) ((num)>>6)
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
|
|
|
struct TextLine {
|
|
|
|
std::string sLineStr;
|
|
|
|
int iLineWidth;
|
|
|
|
};
|
|
|
|
|
|
|
|
NS_CC_BEGIN
|
|
|
|
|
|
|
|
class BitmapDC
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
BitmapDC()
|
|
|
|
{
|
|
|
|
iInterval = szFont_kenning;
|
2013-06-15 14:03:30 +08:00
|
|
|
_data = NULL;
|
2013-04-09 09:21:53 +08:00
|
|
|
reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
~BitmapDC(void)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void reset() {
|
|
|
|
iMaxLineWidth = 0;
|
|
|
|
iMaxLineHeight = 0;
|
|
|
|
vLines.clear();
|
|
|
|
}
|
|
|
|
|
2013-05-15 12:38:56 +08:00
|
|
|
void buildLine(std::stringstream& ss, TTF_Font *face)
|
2013-04-09 09:21:53 +08:00
|
|
|
{
|
|
|
|
TextLine oTempLine;
|
|
|
|
ss << '\0';
|
|
|
|
oTempLine.sLineStr = ss.str();
|
|
|
|
|
2013-05-15 12:38:56 +08:00
|
|
|
int w, h;
|
|
|
|
TTF_SizeText(face, oTempLine.sLineStr.c_str(), &w, &h);
|
|
|
|
|
|
|
|
oTempLine.iLineWidth = w;
|
2013-04-09 09:21:53 +08:00
|
|
|
|
|
|
|
iMaxLineWidth = MAX(iMaxLineWidth, oTempLine.iLineWidth);
|
|
|
|
ss.clear();
|
|
|
|
ss.str("");
|
|
|
|
vLines.push_back(oTempLine);
|
|
|
|
}
|
|
|
|
|
2013-05-15 12:38:56 +08:00
|
|
|
bool divideString(TTF_Font *face, const char* sText, int iMaxWidth, int iMaxHeight) {
|
2013-04-09 09:21:53 +08:00
|
|
|
const char* pText = sText;
|
2013-05-15 12:38:56 +08:00
|
|
|
|
2013-04-09 09:21:53 +08:00
|
|
|
//init stringstream
|
|
|
|
std::stringstream ss;
|
2013-05-15 12:38:56 +08:00
|
|
|
int w, h;
|
2013-04-09 09:21:53 +08:00
|
|
|
|
|
|
|
while (*pText != '\0') {
|
|
|
|
if (*pText == '\n') {
|
2013-05-15 12:38:56 +08:00
|
|
|
buildLine(ss, face);
|
2013-04-09 09:21:53 +08:00
|
|
|
|
|
|
|
pText++;
|
|
|
|
continue;
|
|
|
|
}
|
2013-05-15 12:38:56 +08:00
|
|
|
|
2013-04-09 09:21:53 +08:00
|
|
|
//check its width
|
|
|
|
//divide it when exceeding
|
2013-05-15 12:38:56 +08:00
|
|
|
std::string s = ss.str();
|
|
|
|
s.push_back(*pText);
|
|
|
|
TTF_SizeText(face, s.c_str(), &w, &h);
|
2013-04-09 09:21:53 +08:00
|
|
|
|
2013-05-15 12:38:56 +08:00
|
|
|
if (iMaxWidth > 0 && (w > iMaxWidth)) {
|
|
|
|
buildLine(ss, face);
|
2013-04-09 09:21:53 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
ss << *pText;
|
|
|
|
pText++;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2013-05-15 12:38:56 +08:00
|
|
|
buildLine(ss, face);
|
2013-04-09 09:21:53 +08:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* compute the start pos of every line
|
|
|
|
*
|
|
|
|
* return >0 represent the start x pos of the line
|
|
|
|
* while -1 means fail
|
|
|
|
*
|
|
|
|
*/
|
2013-07-29 16:33:34 +08:00
|
|
|
int computeLineStart(TTF_Font *face, Image::TextAlign eAlignMask, char cText,
|
2013-04-09 09:21:53 +08:00
|
|
|
int iLineIndex) {
|
2013-05-15 12:38:56 +08:00
|
|
|
return 0;
|
|
|
|
/*
|
2013-04-09 09:21:53 +08:00
|
|
|
int iRet;
|
|
|
|
int iError = FT_Load_Glyph(face, FT_Get_Char_Index(face, cText),
|
|
|
|
FT_LOAD_DEFAULT);
|
|
|
|
if (iError) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2013-06-20 14:13:12 +08:00
|
|
|
if (eAlignMask == Image::kAlignCenter) {
|
2013-04-09 09:21:53 +08:00
|
|
|
iRet = (iMaxLineWidth - vLines[iLineIndex].iLineWidth) / 2
|
|
|
|
- RSHIFT6(face->glyph->metrics.horiBearingX );
|
|
|
|
|
2013-06-20 14:13:12 +08:00
|
|
|
} else if (eAlignMask == Image::kAlignRight) {
|
2013-04-09 09:21:53 +08:00
|
|
|
iRet = (iMaxLineWidth - vLines[iLineIndex].iLineWidth)
|
|
|
|
- RSHIFT6(face->glyph->metrics.horiBearingX );
|
|
|
|
} else {
|
|
|
|
// left or other situation
|
|
|
|
iRet = -RSHIFT6(face->glyph->metrics.horiBearingX );
|
|
|
|
}
|
|
|
|
return iRet;
|
2013-05-15 12:38:56 +08:00
|
|
|
*/
|
2013-04-09 09:21:53 +08:00
|
|
|
}
|
|
|
|
|
2013-07-29 16:33:34 +08:00
|
|
|
int computeLineStartY( TTF_Font *face, Image::TextAlign eAlignMask, int txtHeight, int borderHeight ){
|
2013-05-15 12:38:56 +08:00
|
|
|
return 0;
|
|
|
|
/*
|
2013-04-09 09:21:53 +08:00
|
|
|
int iRet;
|
2013-06-20 14:13:12 +08:00
|
|
|
if (eAlignMask == Image::kAlignCenter || eAlignMask == Image::kAlignLeft ||
|
|
|
|
eAlignMask == Image::kAlignRight ) {
|
2013-04-09 09:21:53 +08:00
|
|
|
//vertical center
|
|
|
|
iRet = (borderHeight - txtHeight)/2 + RSHIFT6(face->size->metrics.ascender);
|
|
|
|
|
2013-06-20 14:13:12 +08:00
|
|
|
} else if (eAlignMask == Image::kAlignBottomRight ||
|
|
|
|
eAlignMask == Image::kAlignBottom ||
|
|
|
|
eAlignMask == Image::kAlignBottomLeft ) {
|
2013-04-09 09:21:53 +08:00
|
|
|
//vertical bottom
|
|
|
|
iRet = borderHeight - txtHeight + RSHIFT6(face->size->metrics.ascender);
|
|
|
|
} else {
|
|
|
|
// left or other situation
|
|
|
|
iRet = RSHIFT6(face->size->metrics.ascender);
|
|
|
|
}
|
|
|
|
return iRet;
|
2013-05-15 12:38:56 +08:00
|
|
|
*/
|
2013-04-09 09:21:53 +08:00
|
|
|
}
|
|
|
|
|
2013-07-29 16:33:34 +08:00
|
|
|
bool getBitmap(const char *text, int nWidth, int nHeight, Image::TextAlign eAlignMask, const char * pFontName, float fontSize) {
|
2013-04-09 09:21:53 +08:00
|
|
|
const char* pText = text;
|
2013-05-30 12:25:32 +08:00
|
|
|
int pxSize = (int)fontSize;
|
2013-04-09 09:21:53 +08:00
|
|
|
|
2013-05-30 12:25:32 +08:00
|
|
|
TTF_Font *face = TTF_OpenFont(pFontName, pxSize);
|
2013-05-15 12:38:56 +08:00
|
|
|
if(!face)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2013-04-09 09:21:53 +08:00
|
|
|
|
2013-05-15 12:38:56 +08:00
|
|
|
divideString(face, text, nWidth, nHeight);
|
2013-04-09 09:21:53 +08:00
|
|
|
|
2013-05-15 12:38:56 +08:00
|
|
|
//compute the final line width
|
|
|
|
iMaxLineWidth = MAX(iMaxLineWidth, nWidth);
|
2013-04-09 09:21:53 +08:00
|
|
|
|
2013-05-30 12:25:32 +08:00
|
|
|
iMaxLineHeight = pxSize;
|
2013-05-15 12:38:56 +08:00
|
|
|
iMaxLineHeight *= vLines.size();
|
2013-04-09 09:21:53 +08:00
|
|
|
|
2013-05-15 12:38:56 +08:00
|
|
|
//compute the final line height
|
|
|
|
iMaxLineHeight = MAX(iMaxLineHeight, nHeight);
|
2013-04-09 09:21:53 +08:00
|
|
|
|
2013-05-16 11:21:30 +08:00
|
|
|
uint bitmapSize = iMaxLineWidth * iMaxLineHeight * 4;
|
2013-06-15 14:03:30 +08:00
|
|
|
_data = new unsigned char[bitmapSize];
|
|
|
|
memset(_data, 0, bitmapSize);
|
2013-04-09 09:21:53 +08:00
|
|
|
|
2013-05-30 12:25:32 +08:00
|
|
|
if(!strlen(text))
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-05-16 11:21:30 +08:00
|
|
|
// XXX: Can this be optimized by inserting newlines into the string and
|
|
|
|
// making a single TTF_RenderText_Solid call? Could conceivably just
|
|
|
|
// pass back SDL's buffer then, though would need additional logic to
|
|
|
|
// call SDL_FreeSurface appropriately.
|
2013-05-30 12:25:32 +08:00
|
|
|
|
|
|
|
// FIXME: handle alignment, etc.
|
2013-05-15 12:38:56 +08:00
|
|
|
for (size_t l = 0; l < vLines.size(); l++) {
|
|
|
|
pText = vLines[l].sLineStr.c_str();
|
2013-05-30 12:25:32 +08:00
|
|
|
if(!strlen(pText))
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
2013-04-09 09:21:53 +08:00
|
|
|
|
2013-05-15 12:38:56 +08:00
|
|
|
SDL_Color color = { 0xff, 0xff, 0xff, 0xff };
|
|
|
|
SDL_Surface *tSurf = TTF_RenderText_Solid(face, pText, color);
|
2013-05-30 12:25:32 +08:00
|
|
|
if(!tSurf)
|
|
|
|
{
|
|
|
|
TTF_CloseFont(face);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-05-15 12:38:56 +08:00
|
|
|
SDL_LockSurface(tSurf);
|
|
|
|
SDL_UnlockSurface(tSurf);
|
2013-04-09 09:21:53 +08:00
|
|
|
|
2013-05-15 12:38:56 +08:00
|
|
|
// We treat pixels as 32-bit words, since both source and target
|
|
|
|
// are rendered as such.
|
2013-05-30 12:25:32 +08:00
|
|
|
int *pixels = (int*)tSurf->pixels;
|
2013-06-15 14:03:30 +08:00
|
|
|
int *out = (int*)_data;
|
2013-04-09 09:21:53 +08:00
|
|
|
|
2013-05-30 12:25:32 +08:00
|
|
|
// (i, j) should be treated as (x, y) coordinates in the source
|
|
|
|
// bitmap. This loop maps those locations to the target bitmap.
|
|
|
|
// Need to ensure that those values do not exceed the allocated
|
|
|
|
// memory.
|
|
|
|
int minWidth = MIN(tSurf->w, iMaxLineWidth);
|
|
|
|
for(int i = 0; i < tSurf->h && (i + l * pxSize) < iMaxLineHeight; ++i)
|
2013-05-15 12:38:56 +08:00
|
|
|
{
|
2013-05-30 12:25:32 +08:00
|
|
|
for(int j = 0; j < minWidth; ++j)
|
2013-05-15 12:38:56 +08:00
|
|
|
{
|
|
|
|
int sourceOffset = i * tSurf->w + j;
|
2013-05-30 12:25:32 +08:00
|
|
|
int targetOffset = (l * pxSize + i) * iMaxLineWidth + j;
|
2013-05-18 08:10:51 +08:00
|
|
|
|
|
|
|
// HTML5 canvas is non-pre-alpha-multiplied, so alpha-multiply here.
|
|
|
|
unsigned char *p = (unsigned char*) &pixels[sourceOffset];
|
|
|
|
out[targetOffset] = CC_RGB_PREMULTIPLY_ALPHA( p[0], p[1], p[2], p[3] );
|
2013-04-09 09:21:53 +08:00
|
|
|
}
|
|
|
|
}
|
2013-05-15 12:38:56 +08:00
|
|
|
SDL_FreeSurface(tSurf);
|
|
|
|
}
|
2013-04-09 09:21:53 +08:00
|
|
|
|
2013-05-15 12:38:56 +08:00
|
|
|
//clear all lines
|
|
|
|
vLines.clear();
|
2013-04-09 09:21:53 +08:00
|
|
|
|
2013-05-15 12:38:56 +08:00
|
|
|
TTF_CloseFont(face);
|
2013-04-09 09:21:53 +08:00
|
|
|
|
2013-05-15 12:38:56 +08:00
|
|
|
return true;
|
2013-04-09 09:21:53 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
2013-06-15 14:03:30 +08:00
|
|
|
unsigned char *_data;
|
2013-04-09 09:21:53 +08:00
|
|
|
int libError;
|
|
|
|
vector<TextLine> vLines;
|
|
|
|
int iInterval;
|
|
|
|
int iMaxLineWidth;
|
|
|
|
int iMaxLineHeight;
|
|
|
|
};
|
|
|
|
|
|
|
|
static BitmapDC& sharedBitmapDC()
|
|
|
|
{
|
|
|
|
static BitmapDC s_BmpDC;
|
|
|
|
return s_BmpDC;
|
|
|
|
}
|
|
|
|
|
2013-06-20 14:13:12 +08:00
|
|
|
bool Image::initWithString(
|
2013-04-09 09:21:53 +08:00
|
|
|
const char * pText,
|
|
|
|
int nWidth/* = 0*/,
|
|
|
|
int nHeight/* = 0*/,
|
2013-07-29 16:33:34 +08:00
|
|
|
TextAlign eAlignMask/* = kAlignCenter*/,
|
2013-04-09 09:21:53 +08:00
|
|
|
const char * pFontName/* = nil*/,
|
|
|
|
int nSize/* = 0*/)
|
|
|
|
{
|
|
|
|
bool bRet = false;
|
|
|
|
do
|
|
|
|
{
|
|
|
|
CC_BREAK_IF(! pText);
|
|
|
|
BitmapDC &dc = sharedBitmapDC();
|
|
|
|
|
|
|
|
std::string fullFontName = pFontName;
|
|
|
|
std::string lowerCasePath = fullFontName;
|
|
|
|
std::transform(lowerCasePath.begin(), lowerCasePath.end(), lowerCasePath.begin(), ::tolower);
|
|
|
|
|
2013-05-16 11:21:30 +08:00
|
|
|
CC_BREAK_IF(! dc.getBitmap(pText, nWidth, nHeight, eAlignMask, fullFontName.c_str(), nSize));
|
2013-04-09 09:21:53 +08:00
|
|
|
|
2013-06-15 14:03:30 +08:00
|
|
|
// assign the dc._data to _data in order to save time
|
|
|
|
_data = dc._data;
|
|
|
|
CC_BREAK_IF(! _data);
|
2013-04-09 09:21:53 +08:00
|
|
|
|
2013-06-15 14:03:30 +08:00
|
|
|
_width = (short)dc.iMaxLineWidth;
|
|
|
|
_height = (short)dc.iMaxLineHeight;
|
|
|
|
_hasAlpha = true;
|
|
|
|
_preMulti = true;
|
|
|
|
_bitsPerComponent = 8;
|
2013-04-09 09:21:53 +08:00
|
|
|
|
|
|
|
bRet = true;
|
|
|
|
|
|
|
|
dc.reset();
|
|
|
|
|
|
|
|
} while (0);
|
|
|
|
|
|
|
|
return bRet;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_CC_END
|
|
|
|
|