Files
wagic/projects/mtg/src/WFont.cpp
wrenczes@gmail.com 76cba56a1c Resuming on my threading support work with the card caching mechanism. This change unfortunately touches quite a few files, but I needed to get it out of the way before things got out of hand: one significant hurdle is the assumed lifetime of a JQuad pointer. In a single threaded model, the life time of the pointer is clear: you fetch it into the cache, the cache makes room, you use the pointer immediately. In a multithreaded context however, it's unsafe, as the drawing thread can request a few JQuads, and the cache operating on a separate thread can potentially bounce a JQuad out of the cache before the draw routine is done using it, which ends up in an access violation when you attempt to draw using an invalidated quad pointer. To prevent this, the bulk of this change swaps out the use of naked JQuad* pointers in the code with a JQuadPtr, which is basically a typedef to a boost shared_ptr<JQuad>.
This btw points out another circular dependancy between the texture and the JQuad - a texture owns a bunch of JQuads, yet the renderer uses JQuads and always assumes that the texture is valid.  We're going to need to add more defensiveness to JGE to protect against this.

Other changes in this check-in:  WResourceManager doesn't derive from JResourceManager anymore.  It actually didn't require anything from the base, so I killed the dependency.  Also cleaned up the notion of a WTrackedQuad in the WCachedResource - it didn't need a separate class, just a better container.

I've build this & tested against PSP, win, linux, QT (linux).  I haven't tried against iOS and QT Win, or Maemo.  If these other platforms are broken, I apologize in advance! - I'm hoping it should be fairly simple to put them back into play.
2011-02-01 10:37:21 +00:00

1233 lines
37 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "PrecompiledHeader.h"
#include "WFont.h"
#include "WResourceManager.h"
#include "JFileSystem.h"
#define ISGBK(c) ((c) > 0x80 || (c) < 0x30 || (c) == '-' || (c) == '/')
static PIXEL_TYPE gencolor(int id, PIXEL_TYPE color)
{
unsigned int a, r, g, b, r0, g0, b0;
#if defined (WIN32) || defined (LINUX)
a = (color >> 24) & 0xFF;
r = (color >> 16) & 0xFF;
g = (color >> 8) & 0xFF;
b = (color >> 0) & 0xFF;
#else // PSP
# if defined (ABGR8888)
a = (color >> 24) & 0xFF;
b = (color >> 16) & 0xFF;
g = (color >> 8) & 0xFF;
r = (color >> 0) & 0xFF;
# elif defined (ABGR5551)
a = ((color >> 15) & 0x01) << 7;
b = ((color >> 10) & 0x1F) << 3;
g = ((color >> 5) & 0x1F) << 3;
r = ((color >> 0) & 0x1F) << 3;
# elif defined (ABGR4444)
a = ((color >> 12) & 0x0F) << 4;
b = ((color >> 8) & 0x0F) << 4;
g = ((color >> 4) & 0x0F) << 4;
r = ((color >> 0) & 0x0F) << 4;
# endif
#endif
r0 = g0 = b0 = 255;
switch (id)
{
case Fonts::MAIN_FONT: // simon 245, 228, 156
r0 = 245;
g0 = 228;
b0 = 156;
break;
case Fonts::MENU_FONT: // f3 255, 252, 175
r0 = 255;
g0 = 252;
b0 = 175;
break;
case Fonts::MAGIC_FONT: // magic 219, 255, 151
r0 = 219;
g0 = 255;
b0 = 151;
break;
case Fonts::SMALLFACE_FONT: // smallface 255, 255, 255
r0 = 255;
g0 = 255;
b0 = 255;
break;
default:
;
}
r = r * r0 / 255;
g = g * g0 / 255;
b = b * b0 / 255;
return ARGB(a,r,g,b);
}
static inline bool doubleWidthChar(const u8* const src)
{
// Determination of this is done with < 0xC0, which is not
// right but okay for now (in particular it will return 2
// for european accented characters)
return *src >= 0xC0;
}
static inline bool GBKDoubleWidthChar(const u8* const src)
{
// Determination of this is done with < 0xC0, which is not
// right but okay for now (in particular it will return 2
// for european accented characters)
return *src >= 0x80;
}
// This returns the size in bytes of a character.
// The 4 first bits of the leading byte indicate the length.
// The zeroes in the middle match the 10xx forms, which are
// only found in trailing bytes.
static inline int charWidth(const u8 s)
{
// The following is the "correct" one, but if called on incorrect strings
// the presence of 0's may create an infinite loop, so replace them with
// 1's. This may happen when changing from chinese to japanese because the
// encoding for chinese is not regular.
// static const int sizes[] = {1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 2, 2, 3, 4};
static const int sizes[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 3, 4 };
#if 0
// Reactivate to support being called on trailing bytes. This is not needed at the moment
// as this function will always be passed leading bytes.
while (0 == sizes[s >> 4]) --s;
#endif
return sizes[s >> 4];
}
//
JRenderer * WFBFont::mRenderer = NULL;
WLBFont::WLBFont(int inFontID, const char *fontname, int lineheight, bool useVideoRAM) :
WFont(inFontID)
{
string path(fontname);
if (path.size() > 4) path = path.substr(0, path.size() - 4); //some stupid manipulation because of the way Font works in JGE
it = NEW JLBFont(path.c_str(), lineheight, useVideoRAM);
}
void WLBFont::FormatText(string &s, vector<string>& output)
{
// The way of CardPrimitive::formattedText() in r2081.
std::string::size_type len = 30;
while (s.length() > 0)
{
std::string::size_type cut = s.find_first_of("., \t)", 0);
if (cut >= len || cut == string::npos)
{
output.push_back(s.substr(0,len));
if (s.length() > len)
s = s.substr(len, s.length() - len);
else
s = "";
}
else
{
std::string::size_type newcut = cut;
while (newcut < len && newcut != string::npos)
{
cut = newcut;
newcut = s.find_first_of("., \t)", newcut + 1);
}
output.push_back(s.substr(0,cut+1));
if (s.length() > cut+1)
s = s.substr(cut+1,s.length() - cut - 1);
else
s = "";
}
}
}
WFBFont::WFBFont(int inFontID, const char *fontname, int lineheight, bool useVideoRAM) :
WFont(inFontID)
{
mRenderer = JRenderer::GetInstance();
mCharBuffer = NULL;
mSprites = NULL;
mGBCode = NULL;
mCurr = 0;
char tmpFileName[32], engFileName[32];
strcpy(tmpFileName, fontname);
char * ep = strrchr(tmpFileName, '.');
*ep = '\0';
sprintf(engFileName, "%s.asc", tmpFileName);
JFileSystem *fileSys = JFileSystem::GetInstance();
int size = 0;
struct
{
unsigned short chars;
unsigned char width;
unsigned char height;
} sizeStr = { 0, 0, 0 };
if (!fileSys->OpenFile(engFileName)) return;
size = fileSys->GetFileSize();
mStdFont = NEW u8[size];
fileSys->ReadFile(mStdFont, size);
fileSys->CloseFile();
if (!fileSys->OpenFile(fontname)) return;
fileSys->ReadFile(&sizeStr, 4); // Works only for little-endian machines (PSP and PC are)
size = sizeStr.chars * sizeStr.width * sizeStr.height / 2;
mExtraFont = NEW u8[size]; // 4 bits for a pixel
mIndex = NEW u16[65536];
fileSys->ReadFile(mIndex, 65536 * sizeof(u16));
fileSys->ReadFile(mExtraFont, size);
fileSys->CloseFile();
mColor0 = ARGB(255, 255, 255, 255);
mColor = mColor0;
mFontSize = lineheight;
mScale = 1.0f;
// using 4-bit(half-byte) to store 1 pixel
mBytesPerRow = static_cast<unsigned int> (mFontSize / 2);
mBytesPerChar = static_cast<unsigned int> (mBytesPerRow * mFontSize);
mCacheImageWidth = 256;
mCacheImageHeight = 256;
mCol = mCacheImageWidth / mFontSize;
mRow = mCacheImageHeight / mFontSize;
mCacheSize = mCol * mRow;
mSprites = NEW JQuad*[mCacheSize];
mGBCode = NEW int[mCacheSize];
#if defined (WIN32) || defined (LINUX)
mCharBuffer = NEW u32[mFontSize*mFontSize];
#endif
mTexture = mRenderer->CreateTexture(mCacheImageWidth, mCacheImageHeight, true);
int index = 0;
for (int y = 0; y < mRow; y++)
{
for (int x = 0; x < mCol; x++)
{
mGBCode[index] = -1;
mSprites[index] = NEW JQuad(mTexture, static_cast<float> (x * mFontSize), static_cast<float> (y * mFontSize),
static_cast<float> (mFontSize), static_cast<float> (mFontSize));
mSprites[index]->SetHotSpot(static_cast<float> (mFontSize / 2), static_cast<float> (mFontSize / 2));
index++;
}
}
}
WFBFont::~WFBFont()
{
SAFE_DELETE_ARRAY(mStdFont);
SAFE_DELETE_ARRAY(mExtraFont);
SAFE_DELETE(mTexture);
if (mSprites)
{
for (int i = 0; i < mCacheSize; i++)
{
if (mSprites[i]) delete mSprites[i];
}
delete[] mSprites;
}
if (NULL != mIndex) delete[] mIndex;
if (mGBCode) delete[] mGBCode;
if (mCharBuffer) delete[] mCharBuffer;
}
#if defined (WIN32) || defined (LINUX)
#else
static void SwizzlePlot(u8* out, PIXEL_TYPE color, int i, int j, unsigned int width)
{
unsigned int rowblocks = (width >> 4);
unsigned int blockx = (i >> 4);
unsigned int blocky = (j >> 3);
unsigned int x = (i - (blockx << 4));
unsigned int y = (j - (blocky << 3));
unsigned int block_index = blockx + ((blocky) * rowblocks);
unsigned int block_address = block_index << 7;
u8* p = out + (block_address + x + (y << 4));
PIXEL_TYPE* dest = (PIXEL_TYPE *) p;
*dest = color;
}
#endif
int WFBFont::PreCacheChar(const u8 *ch)
{
int code;
int charLength = 1;
u8 * src;
unsigned int size, offset;
u8 gray;
code = this->GetCode(ch, &charLength);
if (doubleWidthChar(ch) && mIndex) code = mIndex[code]; // mGBCode[] stores the final code.
if (mGBCode[mCurr] != -1) for (int i = 0; i < mCacheSize; i++)
if (mGBCode[i] == code) return i;
int index = mCurr++;
if (mCurr >= mCacheSize) mCurr = 0;
#if defined (WIN32) || defined (LINUX) || defined (IOS)
int x = 0;
int y = 0;
memset(mCharBuffer, 0, sizeof(u32) * mFontSize * mFontSize);
#else
u8* pTexture = (u8*) mTexture->mBits;
int x;
int y = (int) mSprites[index]->mY;
#endif
if (doubleWidthChar(ch))
{
//if (mIndex) code = mIndex[code];
size = mFontSize;
src = mExtraFont + code * mBytesPerChar;
offset = 0;
}
else
{
size = mFontSize / 2;
src = mStdFont + code * (mFontSize * size / 2);
offset = 0;
}
// set up the font texture buffer
for (unsigned int i = 0; i < mFontSize; i++)
{
#if defined (WIN32) || defined (LINUX)
x = 0;
#else
x = (int) mSprites[index]->mX;
#endif
unsigned int j = 0;
#if 1
for (; j < offset; j++)
{
#if defined (WIN32) || defined (LINUX) || defined (IOS)
mCharBuffer[y * mFontSize + x] = ARGB(0, 0, 0, 0);
#else
SwizzlePlot(pTexture, ARGB(0, 0, 0, 0), x * PIXEL_SIZE, y, mTexture->mTexWidth * PIXEL_SIZE);
#endif
x++;
}
#endif
for (; j < offset + size; j++)
{
// as 4-bit(half-byte) stores 1 pixel
// get out the proper data according to the even or odd quality of the counter
gray = src[(i * size + j - offset) / 2];
gray = ((j - offset) & 1) ? (gray & 0xF0) : ((gray & 0x0F) << 4);
if (gray) gray |= 0x0F;
#if defined (WIN32) || defined (LINUX) || defined (IOS)
mCharBuffer[y * mFontSize + x] = ARGB(gray, 255, 255, 255);
#else
SwizzlePlot(pTexture, ARGB(gray, 255, 255, 255), x * PIXEL_SIZE, y, mTexture->mTexWidth * PIXEL_SIZE);
#endif
x++;
}
for (; j < mFontSize; j++)
{
#if defined (WIN32) || defined (LINUX) || defined (IOS)
mCharBuffer[y * mFontSize + x] = ARGB(0, 0, 0, 0);
#else
SwizzlePlot(pTexture, ARGB(0, 0, 0, 0), x * PIXEL_SIZE, y, mTexture->mTexWidth * PIXEL_SIZE);
#endif
x++;
}
y++;
}
mGBCode[index] = code;
#if defined (WIN32) || defined (LINUX) || defined (IOS)
x = (int)mSprites[index]->mX;
y = (int)mSprites[index]->mY;
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, mFontSize, mFontSize, GL_RGBA, GL_UNSIGNED_BYTE, mCharBuffer);
#else
sceKernelDcacheWritebackAll();
#endif
return index;
}
void WFBFont::DrawString(const char *s, float x, float y, int align, float leftOffset, float width)
{
u8 * str = (u8 *) s;
if (*str < 0x80)
{
// tricky: the single byte font is always mFontID + kSingleByteFontOffset!
// See WResourceManager::InitFonts()
WFont * mFont = WResourceManager::Instance()->GetWFont(mFontID + Fonts::kSingleByteFontOffset);
mFont->SetScale(GetScale());
mFont->SetColor(GetColor());
mFont->DrawString(s, x, y, align, leftOffset, width);
return;
}
// (0, 0) refers to the center of the word, so fix it to the upper-left corner
x += (mFontSize * mScale) / 2;
y += (mFontSize * mScale) / 2;
// Warning : non-left alignment is not supported for multiline strings
// (this is because GetStringWidth is not aware of line breaks).
switch (align)
{
case JGETEXT_RIGHT:
if (width) {
x -= width;
leftOffset += GetStringWidth(s) - width;
}
else
x -= GetStringWidth(s);
break;
case JGETEXT_CENTER:
if (width) {
x -= width/2;
leftOffset += GetStringWidth(s)/2 - width/2;
}
else
x -= GetStringWidth(s)/2;
break;
case JGETEXT_LEFT:
default:
break;
}
mRenderer->BindTexture(mTexture);
u8 * src = str;
float xx = x;
float yy = y;
int index = 0;
while (*src != 0)
{
if (yy > SCREEN_HEIGHT_F) // don't render or count outside the buttom of viewport
return;
else if (yy + mFontSize < 0.0f)
{ // don't render when outside the top of viewport, but counted
if (*src < 0x20)
{ // control characters
if (*src == 0x0a)
{ // NEWLINE
xx = x;
yy += (mFontSize * mScale);
}
src += 1;
}
else
{
xx += (mFontSize * mScale);
if (xx >= width)
{
xx = x;
yy += (mFontSize * mScale);
}
}
}
else
{
if (*src < 0x20)
{ // control characters
if (*src == 0x0a)
{ // NEWLINE
xx = x;
yy += (mFontSize * mScale);
}
src += 1;
}
else
{
int mana = this->GetMana(src);
bool doubleW = doubleWidthChar(src);
index = PreCacheChar(src);
src += charWidth(*src);
// fix for leftoffset and width's setting
float xPos, yPos, charW, charHeight;
mSprites[index]->GetTextureRect(&xPos, &yPos, &charW, &charHeight);
float xPos0 = xPos;
float charW0 = charW;
float delta = doubleW ? (charW * mScale) : (charW * mScale / 2);
if (leftOffset)
{
if (leftOffset < 0)
{
xx -= leftOffset;
leftOffset = 0;
}
else if (leftOffset - delta > 0)
{
leftOffset -= delta;
continue;
}
else
{
xPos += leftOffset / mScale;
delta -= leftOffset;
leftOffset = 0;
charW = delta / mScale;
}
}
if (width)
{
if (xx > x + width) return;
if (xx + delta > x + width)
{
delta = x + width - xx;
charW = delta / mScale;
}
}
if (mana >= 0)
{
int mana2 = -1;
if (*src == '/' && (mana2 = this->GetMana(src + 1)) >= 0)
{ // hybrid mana cost
src += 1 + charWidth(*src);
unsigned char t = (JGE::GetInstance()->GetTime() / 3) & 0xFF;
unsigned char v = t + 127;
float scale = 0.05f * cosf(2 * M_PI * ((float) t) / 256.0f);
if (scale < 0)
{
mRenderer->RenderQuad(manaIcons[mana].get(), xx + 3 * sinf(2 * M_PI * ((float) t) / 256.0f), yy + 3 * cosf(2
* M_PI * ((float) (t - 35)) / 256.0f), 0, 0.5f * mScale, 0.5f * mScale);
mRenderer->RenderQuad(manaIcons[mana2].get(), xx + 3 * sinf(2 * M_PI * ((float) v) / 256.0f), yy + 3 * cosf(2
* M_PI * ((float) (v - 35)) / 256.0f), 0, 0.5f * mScale, 0.5f * mScale);
}
else
{
mRenderer->RenderQuad(manaIcons[mana2].get(), xx + 3 * sinf(2 * M_PI * ((float) v) / 256.0f), yy + 3 * cosf(2
* M_PI * ((float) (v - 35)) / 256.0f), 0, 0.5f * mScale, 0.5f * mScale);
mRenderer->RenderQuad(manaIcons[mana].get(), xx + 3 * sinf(2 * M_PI * ((float) t) / 256.0f), yy + 3 * cosf(2
* M_PI * ((float) (t - 35)) / 256.0f), 0, 0.5f * mScale, 0.5f * mScale);
}
mana = Constants::MTG_NB_COLORS + 1; // do not draw colorless cost in hybrid mana cost
}
else
mRenderer->RenderQuad(manaIcons[mana].get(), xx, yy, 0, 0.5f * mScale, 0.5f * mScale);
mRenderer->BindTexture(mTexture); // manaIcons use different texture, so we need to rebind it.
}
if (mana <= 0)
{
mSprites[index]->SetTextureRect(xPos, yPos, charW, charHeight);
mSprites[index]->SetColor(mColor);
mRenderer->RenderQuad(mSprites[index], xx, yy, 0, mScale, mScale);
mSprites[index]->SetTextureRect(xPos0, yPos, charW0, charHeight);
}
xx += delta;
if (xx >= 480)
{
xx = x;
yy += (mFontSize * mScale);
}
}
}
}
}
void WFBFont::DrawString(std::string s, float x, float y, int align, float leftOffset, float width)
{
DrawString(s.c_str(), x, y, align, leftOffset, width);
}
void WFBFont::SetColor(PIXEL_TYPE color)
{
mColor0 = color;
mColor = gencolor(mFontID, color);
}
float WFBFont::GetStringWidth(const char *s) const
{
u8 * src = (u8 *) s;
int width = 0;
if (doubleWidthChar(src))
{
while (*src != 0)
{
// Add the number of single-char widths according to whether
// this is a single or double-width char.
width += (doubleWidthChar(src)) ? 2 : 1;
src += charWidth(*src);
}
return width * mFontSize * mScale / 2;
}
else
{
WFont * mFont = WResourceManager::Instance()->GetWFont(mFontID + Fonts::kSingleByteFontOffset);
mFont->SetScale(GetScale());
return mFont->GetStringWidth(s);
}
}
void WFBFont::SetScale(float scale)
{
mScale = scale;
}
float WFBFont::GetScale() const
{
return mScale;
}
float WFBFont::GetHeight() const
{
return (mFontSize * mScale);
}
// Legacy : GBK encoding
WGBKFont::WGBKFont(int inFontID, const char *fontname, int lineheight, bool useVideoRAM) :
WFBFont(inFontID)
{
mRenderer = JRenderer::GetInstance();
mCharBuffer = NULL;
mSprites = NULL;
mGBCode = NULL;
mCurr = 0;
char tmpFileName[32], engFileName[32];
strcpy(tmpFileName, fontname);
char * ep = strrchr(tmpFileName, '.');
*ep = '\0';
sprintf(engFileName, "%s.asc", tmpFileName);
JFileSystem *fileSys = JFileSystem::GetInstance();
int size = 0;
if (!fileSys->OpenFile(fontname)) return;
size = fileSys->GetFileSize();
mExtraFont = NEW u8[size];
fileSys->ReadFile(mExtraFont, size);
fileSys->CloseFile();
if (!fileSys->OpenFile(engFileName)) return;
size = fileSys->GetFileSize();
mStdFont = NEW u8[size];
fileSys->ReadFile(mStdFont, size);
fileSys->CloseFile();
mIndex = 0;
mColor0 = ARGB(255, 255, 255, 255);
mColor = mColor0;
mFontSize = lineheight;
mScale = 1.0f;
// using 4-bit(half-byte) to store 1 pixel
mBytesPerRow = static_cast<unsigned int> (mFontSize / 2);
mBytesPerChar = static_cast<unsigned int> (mBytesPerRow * mFontSize);
mCacheImageWidth = 256;
mCacheImageHeight = 256;
mCol = mCacheImageWidth / mFontSize;
mRow = mCacheImageHeight / mFontSize;
mCacheSize = mCol * mRow;
mSprites = NEW JQuad*[mCacheSize];
mGBCode = NEW int[mCacheSize];
#if defined (WIN32) || defined (LINUX) || defined (IOS)
mCharBuffer = NEW u32[mFontSize*mFontSize];
#endif
mTexture = mRenderer->CreateTexture(mCacheImageWidth, mCacheImageHeight, true);
int index = 0;
for (int y = 0; y < mRow; y++)
{
for (int x = 0; x < mCol; x++)
{
mGBCode[index] = -1;
mSprites[index] = NEW JQuad(mTexture, static_cast<float> (x * mFontSize), static_cast<float> (y * mFontSize),
static_cast<float> (mFontSize), static_cast<float> (mFontSize));
mSprites[index]->SetHotSpot(static_cast<float> (mFontSize / 2), static_cast<float> (mFontSize / 2));
index++;
}
}
}
int WGBKFont::PreCacheChar(const u8 *ch)
{
int code;
int charLength = 1;
u8 * src;
unsigned int size, offset;
u8 gray;
code = this->GetCode(ch, &charLength);
if (mGBCode[mCurr] != -1) for (int i = 0; i < mCacheSize; i++)
if (mGBCode[i] == code) return i;
int index = mCurr++;
if (mCurr >= mCacheSize) mCurr = 0;
#if defined (WIN32) || defined (LINUX) || defined (IOS)
int x = 0;
int y = 0;
memset(mCharBuffer, 0, sizeof(u32) * mFontSize * mFontSize);
#else
u8* pTexture = (u8*) mTexture->mBits;
int x;
int y = (int) mSprites[index]->mY;
#endif
if (mIndex) code = mIndex[code];
if (GBKDoubleWidthChar(ch))
{
size = mFontSize;
src = mExtraFont + code * mBytesPerChar;
offset = 0;
}
else
{
size = mFontSize / 2;
src = mStdFont + code * (mFontSize * size / 2);
offset = 0;
}
// set up the font texture buffer
for (unsigned int i = 0; i < mFontSize; i++)
{
#if defined (WIN32) || defined (LINUX) || defined (IOS)
x = 0;
#else
x = (int) mSprites[index]->mX;
#endif
unsigned int j = 0;
#if 1
for (; j < offset; j++)
{
#if defined (WIN32) || defined (LINUX) || defined (IOS)
mCharBuffer[y * mFontSize + x] = ARGB(0, 0, 0, 0);
#else
SwizzlePlot(pTexture, ARGB(0, 0, 0, 0), x * PIXEL_SIZE, y, mTexture->mTexWidth * PIXEL_SIZE);
#endif
x++;
}
#endif
for (; j < offset + size; j++)
{
// as 4-bit(half-byte) stores 1 pixel
// get out the proper data according to the even or odd quality of the counter
gray = src[(i * size + j - offset) / 2];
gray = ((j - offset) & 1) ? (gray & 0xF0) : ((gray & 0x0F) << 4);
if (gray) gray |= 0x0F;
#if defined (WIN32) || defined (LINUX) || defined (IOS)
mCharBuffer[y * mFontSize + x] = ARGB(gray, 255, 255, 255);
#else
SwizzlePlot(pTexture, ARGB(gray, 255, 255, 255), x * PIXEL_SIZE, y, mTexture->mTexWidth * PIXEL_SIZE);
#endif
x++;
}
for (; j < mFontSize; j++)
{
#if defined (WIN32) || defined (LINUX) || defined (IOS)
mCharBuffer[y * mFontSize + x] = ARGB(0, 0, 0, 0);
#else
SwizzlePlot(pTexture, ARGB(0, 0, 0, 0), x * PIXEL_SIZE, y, mTexture->mTexWidth * PIXEL_SIZE);
#endif
x++;
}
y++;
}
mGBCode[index] = code;
#if defined (WIN32) || defined (LINUX) || defined (IOS)
x = (int)mSprites[index]->mX;
y = (int)mSprites[index]->mY;
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, mFontSize, mFontSize, GL_RGBA, GL_UNSIGNED_BYTE, mCharBuffer);
#else
sceKernelDcacheWritebackAll();
#endif
return index;
}
void WGBKFont::DrawString(const char *s, float x, float y, int align, float leftOffset, float width)
{
unsigned char c = *(unsigned short *) s & 0xFF;
if (ISGBK(c) || (s[1] == ':' && s[2] == ' '))
{
}
else
{
// tricky: the single byte font is always mFontID + kSingleByteFontOffset!
// See WResourceManager::InitFonts()
WFont * mFont = WResourceManager::Instance()->GetWFont(mFontID + Fonts::kSingleByteFontOffset);
mFont->SetScale(GetScale());
mFont->SetColor(GetColor());
mFont->DrawString(s, x, y, align, leftOffset, width);
return;
}
u8 * str = (u8 *) s;
// (0, 0) refers to the center of the word, so fix it to the upper-left corner
x += (mFontSize * mScale) / 2;
y += (mFontSize * mScale) / 2;
switch (align)
{
case JGETEXT_RIGHT:
if (width) {
x -= width;
leftOffset += GetStringWidth(s) - width;
}
else
x -= GetStringWidth(s);
break;
case JGETEXT_CENTER:
if (width) {
x -= width/2;
leftOffset += GetStringWidth(s)/2 - width/2;
}
else
x -= GetStringWidth(s)/2;
break;
case JGETEXT_LEFT:
default:
break;
}
mRenderer->BindTexture(mTexture);
u8 * src = str;
float xx = x;
float yy = y;
int index = 0;
bool dualByteFont = true;
while (*src != 0)
{
if (yy > SCREEN_HEIGHT_F) // don't render or count outside the buttom of viewport
return;
else if (yy + mFontSize < 0.0f)
{ // don't render when outside the top of viewport, but counted
if (*src < 0x20)
{ // control characters
if (*src == 0x0a)
{ // NEWLINE
xx = x;
yy += (mFontSize * mScale);
}
src += 1;
}
else
{
if (*src > 0x80) // 2-bytes char
src += 2;
else
src += 1;
xx += (mFontSize * mScale);
if (xx >= 480)
{
xx = x;
yy += (mFontSize * mScale);
}
}
}
else
{
if (*src < 0x20)
{ // control characters
if (*src == 0x0a)
{ // NEWLINE
xx = x;
yy += (mFontSize * mScale);
}
src += 1;
}
else
{
int mana = -1;
if (*src > 0x80)
{ // 2-bytes char
mana = this->GetMana(src);
index = PreCacheChar(src);
src += 2;
dualByteFont = true;
}
else
{
index = PreCacheChar(src);
src += 1;
dualByteFont = false;
}
// fix for leftoffset and witdth's setting
float xPos, yPos, charW, charHeight;
mSprites[index]->GetTextureRect(&xPos, &yPos, &charW, &charHeight);
float xPos0 = xPos;
float charW0 = charW;
float delta = (dualByteFont) ? (charW * mScale) : (charW * mScale / 2);
if (leftOffset)
{
if (leftOffset < 0)
{
xx -= leftOffset;
leftOffset = 0;
}
else if (leftOffset - delta > 0)
{
leftOffset -= delta;
continue;
}
else
{
xPos += leftOffset / mScale;
delta -= leftOffset;
leftOffset = 0;
charW = delta / mScale;
}
}
if (width)
{
if (xx > x + width) return;
if (xx + delta > x + width)
{
delta = x + width - xx;
charW = delta / mScale;
}
}
if (mana >= 0)
{
int mana2 = -1;
if (*src == '/' && (mana2 = this->GetMana(src + 1)) >= 0)
{ // hybrid mana cost
src += 3;
unsigned char t = (JGE::GetInstance()->GetTime() / 3) & 0xFF;
unsigned char v = t + 127;
float scale = 0.05f * cosf(2 * M_PI * ((float) t) / 256.0f);
if (scale < 0)
{
mRenderer->RenderQuad(manaIcons[mana].get(), xx + 3 * sinf(2 * M_PI * ((float) t) / 256.0f), yy + 3 * cosf(2
* M_PI * ((float) (t - 35)) / 256.0f), 0, 0.5f * mScale, 0.5f * mScale);
mRenderer->RenderQuad(manaIcons[mana2].get(), xx + 3 * sinf(2 * M_PI * ((float) v) / 256.0f), yy + 3 * cosf(2
* M_PI * ((float) (v - 35)) / 256.0f), 0, 0.5f * mScale, 0.5f * mScale);
}
else
{
mRenderer->RenderQuad(manaIcons[mana2].get(), xx + 3 * sinf(2 * M_PI * ((float) v) / 256.0f), yy + 3 * cosf(2
* M_PI * ((float) (v - 35)) / 256.0f), 0, 0.5f * mScale, 0.5f * mScale);
mRenderer->RenderQuad(manaIcons[mana].get(), xx + 3 * sinf(2 * M_PI * ((float) t) / 256.0f), yy + 3 * cosf(2
* M_PI * ((float) (t - 35)) / 256.0f), 0, 0.5f * mScale, 0.5f * mScale);
}
mana = Constants::MTG_NB_COLORS + 1; // donot draw colorless cost in hybrid mana cost
}
else
mRenderer->RenderQuad(manaIcons[mana].get(), xx, yy, 0, 0.5f * mScale, 0.5f * mScale);
mRenderer->BindTexture(mTexture); // manaIcons use different texture, so we need to rebind it.
}
if (mana <= 0)
{
mSprites[index]->SetTextureRect(xPos, yPos, charW, charHeight);
mSprites[index]->SetColor(mColor);
mRenderer->RenderQuad(mSprites[index], xx, yy, 0, mScale, mScale);
mSprites[index]->SetTextureRect(xPos0, yPos, charW0, charHeight);
}
xx += delta;
if (xx >= 480)
{
xx = x;
yy += (mFontSize * mScale);
}
}
}
}
}
float WGBKFont::GetStringWidth(const char *s) const
{
unsigned char c = *(unsigned short *) s & 0xFF;
if (ISGBK(c))
{
u8 * src = (u8 *) s;
float xx = 0;
bool dualByteFont = true;
while (*src != 0)
{
if (*src > 0x80)
{ // Chinese and Japanese
src += 2;
dualByteFont = true;
}
else
{ // Latin 1
src += 1;
dualByteFont = false;
}
if (dualByteFont)
xx += (mFontSize * mScale);
else
xx += (mFontSize * mScale) / 2;
}
return xx;
}
else
{
WFont * mFont = WResourceManager::Instance()->GetWFont(mFontID + Fonts::kSingleByteFontOffset);
mFont->SetScale(GetScale());
return mFont->GetStringWidth(s);
}
}
int WGBKFont::GetCode(const u8 *ch, int *charLength) const
{
int code = 0;
*charLength = 2;
if (*ch > 0xA0 && *(ch + 1) > 0xA0)
{
// get offset to the proper character bits (GB2312 encoding)
code = (((u32)(*ch - 0xA1)) * 0x5E + ((u32)(*(ch + 1) - 0xA1)));
}
else if (*ch > 0x80)
{
// get offset to the character space's bits (GBK encoding)
code = 0;
}
else
{
code = ((u32) * ch);
*charLength = 1;
}
return code;
}
int WGBKFont::GetMana(const u8 *ch) const
{
int mana = -1;
if (*ch != 0xa3) return mana;
switch (*(ch + 1))
{
case 0xC7:
mana = Constants::MTG_COLOR_GREEN;
break;
case 0xD5:
mana = Constants::MTG_COLOR_BLUE;
break;
case 0xD2:
mana = Constants::MTG_COLOR_RED;
break;
case 0xC2:
mana = Constants::MTG_COLOR_BLACK;
break;
case 0xD7:
mana = Constants::MTG_COLOR_WHITE;
break;
case 0xD4: // T
case 0xD8: // X
case 0xD9: // Y
mana = Constants::MTG_UNCOLORED;
break;
default:
if (*(ch + 1) >= 0xB0 && *(ch + 1) <= 0xB9) mana = Constants::MTG_UNCOLORED;
}
return mana;
}
void WGBKFont::FormatText(string &s, vector<string>& output)
{
while (s.length() > 0)
{
std::string::size_type len = 24;
std::string::size_type cut = s.find_first_of("., \t)", 0);
if (cut >= len || cut == string::npos)
{
// Fix for single byte character in some complex language like Chinese
u8 * src = (u8 *) s.c_str();
//if (neofont)
{
len = 0;
std::string::size_type limit = 24;
while (*src != 0)
{
if (*src > 0x80)
{ // Non-ASCII
if (len + 2 > limit && !(((*src & 0xF0) == 0xA0) && ((*(src + 1) & 0xF0) == 0xA0)))
break;
src += 2;
len += 2;
}
else
{ // ASCII
if (*src == '/' && (*(src + 1) & 0xF0) == 0xA0)
limit += 3;
if (len + 1 > limit && (*src == '+' || *src == '-' || *src == '/'))
break;
src += 1;
len += 1;
}
}
}
output.push_back(s.substr(0, len));
if (s.length() > len)
s = s.substr(len, s.length() - len);
else
s = "";
}
else
{
std::string::size_type newcut = cut;
while (newcut < len && newcut != string::npos)
{
// neofont use space to separate one line
u8 * src = (u8 *) s.c_str();
//if (neofont && *src > 0x80)
if (*src > 0x80)
break;
cut = newcut;
newcut = s.find_first_of("., \t)", newcut + 1);
}
output.push_back(s.substr(0, cut + 1));
if (s.length() > cut + 1)
s = s.substr(cut + 1, s.length() - cut - 1);
else
s = "";
}
}
}
int WUFont::GetCode(const u8 *ch, int *charLength) const
{
int code = 0;
// This assumes the string is valid. We could test for
// validity by ensuring all trailing bytes are & 0xC0 == F0
// and that no first byte is.
// For a description of the binary representation, look at wikipedia://utf-8
if ((*ch & 0xF8) == 0xF0)
{ // Four bytes
*charLength = 4;
code = ((*ch & 0x7) << 18) + ((*(ch + 1) & 0x3F) << 12) + ((*(ch + 2) & 0x3F) << 6) + ((*(ch + 3) * 0x3F));
}
else if ((*ch & 0xF0) == 0xE0)
{ // Three bytes
*charLength = 3;
code = ((*ch & 0xF) << 12) + ((*(ch + 1) & 0x3F) << 6) + ((*(ch + 2) & 0x3F));
}
else if ((*ch & 0xE0) == 0xC0)
{ // Two bytes
*charLength = 2;
code = ((*ch & 0x1F) << 6) + ((*(ch + 1) & 0x3F));
}
else
{
*charLength = 1;
code = *ch;
}
return code;
}
int WUFont::GetMana(const u8 *ch) const
{
/*
* Reactivate the following code to be able to use
* single-width characters too, if needed.
**/
/*
switch (*ch)
{
case 0x67: return Constants::MTG_COLOR_GREEN;
case 0x75: return Constants::MTG_COLOR_BLUE;
case 0x72: return Constants::MTG_COLOR_RED;
case 0x62: return Constants::MTG_COLOR_BLACK;
case 0x77: return Constants::MTG_COLOR_WHITE;
case 0x74:
case 0x78:
case 0x79: return Constants::MTG_UNCOLORED;
default:
if (*ch >= 0x30 && *ch <= 0x39)
return Constants::MTG_UNCOLORED;
}
*/
if (*ch != 0xef || *(ch + 1) != 0xbc) return -1;
ch += 2;
switch (*ch)
{
case 0xa7: // : 0xefbca7
return Constants::MTG_COLOR_GREEN;
case 0xb5: // : 0xefbcb5
return Constants::MTG_COLOR_BLUE;
case 0xb2: // : 0xefbcb2
return Constants::MTG_COLOR_RED;
case 0xa2: // : 0xefbca2
return Constants::MTG_COLOR_BLACK;
case 0xb7: // : 0xefbcb7
return Constants::MTG_COLOR_WHITE;
case 0xb4: // : 0xefbcb4
case 0xb8: // : 0xefbcb8
case 0xb9: // : 0xefbcb9
return Constants::MTG_UNCOLORED;
default:
if (*ch >= 0x90 && *ch <= 0x99) return Constants::MTG_UNCOLORED;
}
return -1;
}
void WUFont::FormatText(string &s, vector<string>& output)
{
std::string::size_type limit = 22; //28
string delim("., \t)");
while (s.length() > 0)
{
u8 * src = (u8 *) s.c_str();
std::string::size_type len = 0;
std::string::size_type ctr = 0;
std::string::size_type lastcut = 0;
u8 ch = 0;
while ((ch = src[len]) != 0)
{
ctr += 2;
if ((ch & 0xF8) == 0xF0) len += 4;
else if ((ch & 0xF0) == 0xE0) len += 3;
else if ((ch & 0xE0) == 0xC0) len += 2;
else // ASCII
{
ctr--; len += 1;
if (delim.find(ch) != string::npos) lastcut = len;
if (ctr > limit && lastcut) {len = lastcut; break;}
//if (ch == ' ') break; // if we use ' ' to break a line.
}
if (ctr > limit) break;
}
output.push_back(s.substr(0, len));
if (s.length() > len)
s = s.substr(len, s.length() - len);
else
s = "";
}
}