922 lines
25 KiB
C++
922 lines
25 KiB
C++
|
#include "precomp.h"
|
||
|
// ============================================================================================================================================================
|
||
|
// Font abstract class + statics to install TT fonts and Bitmap fonts
|
||
|
// ============================================================================================================================================================
|
||
|
#include "freetypefont.h"
|
||
|
#include <tataki/canvas/ifc_canvas.h>
|
||
|
#include <tataki/bitmap/bitmap.h>
|
||
|
#include <api/config/items/cfgitem.h>
|
||
|
#include <api/memmgr/api_memmgr.h>
|
||
|
|
||
|
#define DO_KERNING // because Keith say so.
|
||
|
|
||
|
// local prototypes
|
||
|
static const wchar_t *find_break(void *f, const wchar_t *str, int width, int antialias);
|
||
|
|
||
|
#define M_PI 3.14159
|
||
|
#define M_2PI (M_PI*2)
|
||
|
|
||
|
#define FAUX_BOLD_RATIO 0.1f
|
||
|
#define FAUX_ITALIC_DEGREES 12
|
||
|
|
||
|
// This was necessary in Freetype 2.1.3 and bellow, but let us rejoice, they've fixed it
|
||
|
//#define NEED_SMALL_KERNING_HACK
|
||
|
|
||
|
static int freetype_width(void *data, const wchar_t *str, int len, int fixed, int antialias);
|
||
|
|
||
|
|
||
|
/**********************************************************************
|
||
|
*
|
||
|
* FreeType Lib
|
||
|
*
|
||
|
**********************************************************************/
|
||
|
|
||
|
int (*FOLDSTRING)(
|
||
|
|
||
|
DWORD dwMapFlags,
|
||
|
LPCWSTR lpSrcStr,
|
||
|
int cchSrc,
|
||
|
LPWSTR lpDestStr,
|
||
|
int cchDest
|
||
|
)=0;
|
||
|
|
||
|
FreeTypeFont::FreeTypeFont()
|
||
|
{
|
||
|
if (!FOLDSTRING)
|
||
|
{
|
||
|
HMODULE lib = LoadLibraryA("kernel32.dll");
|
||
|
if (lib)
|
||
|
{
|
||
|
//*(void **)&FOLDSTRING = GetProcAddress(lib, "FoldStringW");
|
||
|
}
|
||
|
FreeLibrary(lib);
|
||
|
}
|
||
|
fontbuffer = NULL;
|
||
|
font = NULL;
|
||
|
curboldstrength = 2;
|
||
|
}
|
||
|
|
||
|
int FreeTypeFont::addFontResource2(void *data, int datalen, const wchar_t *name)
|
||
|
{
|
||
|
optionsitem = NULL;
|
||
|
if (FT_Init_FreeType(&flib)) {
|
||
|
DebugString("FreeType: Cannot load freetype!\n");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
last_encoding = -1;
|
||
|
|
||
|
facename = name;
|
||
|
|
||
|
fontbuffer = (char *)data;
|
||
|
fontbufferlen = datalen;
|
||
|
|
||
|
if (FT_New_Memory_Face(flib, (FT_Byte *)fontbuffer, fontbufferlen, 0, &font)) {
|
||
|
DebugString("FreeType: Cannot load font!\n");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
updateCharmap();
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
int FreeTypeFont::addFontResource(OSFILETYPE file, const wchar_t *name)
|
||
|
{
|
||
|
optionsitem = NULL;
|
||
|
if (FT_Init_FreeType(&flib))
|
||
|
{
|
||
|
DebugString("FreeType: Cannot load freetype!\n");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
last_encoding = -1;
|
||
|
|
||
|
fontbufferlen = (int)FGETSIZE(file);
|
||
|
fontbuffer = (char *)WASABI_API_MEMMGR->sysMalloc(fontbufferlen);
|
||
|
FREAD(fontbuffer, fontbufferlen, 1, file);
|
||
|
|
||
|
facename = name;
|
||
|
|
||
|
if (FT_New_Memory_Face(flib, (FT_Byte *)fontbuffer, fontbufferlen, 0, &font))
|
||
|
{
|
||
|
DebugString("FreeType: Cannot load font!\n");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
updateCharmap();
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
FreeTypeFont::~FreeTypeFont()
|
||
|
{
|
||
|
if (fontbuffer) {
|
||
|
WASABI_API_MEMMGR->sysFree(fontbuffer);
|
||
|
FT_Done_Face(font);
|
||
|
FT_Done_FreeType(flib);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void FreeTypeFont::updateCharmap()
|
||
|
{
|
||
|
if (optionsitem == NULL)
|
||
|
{
|
||
|
const GUID options_guid =
|
||
|
{ 0x280876cf, 0x48c0, 0x40bc, { 0x8e, 0x86, 0x73, 0xce, 0x6b, 0xb4, 0x62, 0xe5 } };
|
||
|
optionsitem = WASABI_API_CONFIG->config_getCfgItemByGuid(options_guid);
|
||
|
}
|
||
|
int i = 0;
|
||
|
if (optionsitem) i = optionsitem->getDataAsInt( L"Character mapping", 0);
|
||
|
if (i != last_encoding)
|
||
|
{
|
||
|
FT_Done_Face(font);
|
||
|
FT_Done_FreeType(flib);
|
||
|
FT_Init_FreeType(&flib);
|
||
|
FT_New_Memory_Face(flib, (FT_Byte *)fontbuffer, fontbufferlen, 0, &font);
|
||
|
switch (i) {
|
||
|
case -1:
|
||
|
break;
|
||
|
case 0:
|
||
|
FT_Select_Charmap(font, FT_ENCODING_UNICODE);
|
||
|
break;
|
||
|
case 1:
|
||
|
FT_Select_Charmap(font, FT_ENCODING_APPLE_ROMAN);
|
||
|
break;
|
||
|
case 2:
|
||
|
FT_Select_Charmap(font, FT_ENCODING_ADOBE_LATIN_1);
|
||
|
break;
|
||
|
case 3:
|
||
|
FT_Select_Charmap(font, FT_ENCODING_ADOBE_STANDARD);
|
||
|
break;
|
||
|
case 4:
|
||
|
FT_Select_Charmap(font, FT_ENCODING_ADOBE_CUSTOM);
|
||
|
break;
|
||
|
case 5:
|
||
|
FT_Select_Charmap(font, FT_ENCODING_ADOBE_EXPERT);
|
||
|
break;
|
||
|
case 6:
|
||
|
FT_Select_Charmap(font, FT_ENCODING_SJIS);
|
||
|
break;
|
||
|
case 7:
|
||
|
FT_Select_Charmap(font, FT_ENCODING_BIG5);
|
||
|
break;
|
||
|
case 8:
|
||
|
FT_Select_Charmap(font, FT_ENCODING_WANSUNG);
|
||
|
break;
|
||
|
case 9:
|
||
|
FT_Select_Charmap(font, FT_ENCODING_JOHAB);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
last_encoding = i;
|
||
|
}
|
||
|
|
||
|
int FreeTypeFont::tweakSize(const wchar_t *face, int size)
|
||
|
{
|
||
|
if (WCSCASEEQLSAFE(face, L"nonstep")) return size-1;
|
||
|
if (WCSCASEEQLSAFE(face, L"04B_08__")) return size+3;
|
||
|
if (WCSCASEEQLSAFE(face, L"Blocky")) return size+6;
|
||
|
if (WCSCASEEQLSAFE(face, L"04b_03b_")) return size+3;
|
||
|
if (WCSCASEEQLSAFE(face, L"04b_09__")) return size+3;
|
||
|
if (WCSCASEEQLSAFE(face, L"04b_21")) return size+3;
|
||
|
if (WCSCASEEQLSAFE(face, L"Radiosta")) return size+2;
|
||
|
if (WCSCASEEQLSAFE(face, L"ETHNOCENTRIC")) return size+3;
|
||
|
if (WCSCASEEQLSAFE(face, L"ETHNOCEN")) return size+3;
|
||
|
if (WCSCASEEQLSAFE(face, L"pixel")) return size+3;
|
||
|
return size;
|
||
|
}
|
||
|
|
||
|
#define VERTICAL_TPADDING 2
|
||
|
#define VERTICAL_BPADDING 0
|
||
|
#define HORIZONTAL_LPADDING -1
|
||
|
#define HORIZONTAL_RPADDING 1
|
||
|
|
||
|
void FreeTypeFont::prepareCanvas(api_canvas *c, int size, int bold, int opaque, int underline, int italic, COLORREF color, COLORREF bkcolor, const wchar_t *txt, int width, int height)
|
||
|
{
|
||
|
// Our "size" variable is fun to calculate!
|
||
|
//int vRez = GetDeviceCaps(c->getHDC(), LOGPIXELSY); // this needs to be a Canvas method or something.
|
||
|
int fsize = tweakSize(facename, size);
|
||
|
int nHeight = MulDiv(fsize << 6, 72, 96);
|
||
|
FT_Set_Char_Size(font, 0, nHeight, 0, 0);
|
||
|
|
||
|
updateCharmap();
|
||
|
|
||
|
font->style_flags = 0;
|
||
|
if (bold)
|
||
|
{
|
||
|
font->style_flags |= FT_STYLE_FLAG_BOLD;
|
||
|
curboldstrength = bold;//(bold && c->getTextAntialias()) ? 2 : bold;
|
||
|
}
|
||
|
if (italic)
|
||
|
font->style_flags |= FT_STYLE_FLAG_ITALIC;
|
||
|
if (underline)
|
||
|
font->underline_thickness = 1;
|
||
|
else
|
||
|
font->underline_thickness = 0;
|
||
|
|
||
|
if (height == -1 || width == -1) {
|
||
|
getTextExtent(c, txt, &width, &height, size, bold, underline, italic, 0);
|
||
|
}
|
||
|
|
||
|
blt = new SkinBitmap(width+1, height, color & 0xffffff);
|
||
|
blt->setHasAlpha(1);
|
||
|
}
|
||
|
|
||
|
void FreeTypeFont::restoreCanvas(api_canvas *c, int x, int y)
|
||
|
{
|
||
|
unsigned int *bits = (unsigned int *)blt->getBits();
|
||
|
int n = blt->getWidth() * blt->getHeight();
|
||
|
|
||
|
#ifndef NO_MMX
|
||
|
if (Blenders::MMX_AVAILABLE())
|
||
|
for (int i = 0; i < n; i++)
|
||
|
*bits++ = Blenders::BLEND_MUL_MMX(*bits | 0xff000000, *bits >> 24);
|
||
|
else
|
||
|
#endif
|
||
|
for (int i = 0; i < n; i++)
|
||
|
*bits++ = Blenders::BLEND_MUL(*bits | 0xff000000, *bits >> 24);
|
||
|
|
||
|
#ifndef NO_MMX
|
||
|
Blenders::BLEND_MMX_END();
|
||
|
#endif
|
||
|
|
||
|
blt->blit(c, x + HORIZONTAL_LPADDING, y + VERTICAL_TPADDING);
|
||
|
delete blt;
|
||
|
blt = NULL;
|
||
|
}
|
||
|
|
||
|
int FreeTypeFont::getAscent()
|
||
|
{
|
||
|
FT_Glyph glyph;
|
||
|
FT_BBox box;
|
||
|
|
||
|
FT_Load_Glyph(font, FT_Get_Char_Index(font, 'M'), FT_LOAD_DEFAULT);
|
||
|
FT_Get_Glyph(font->glyph, &glyph);
|
||
|
FT_Glyph_Get_CBox(glyph, ft_glyph_bbox_unscaled, &box);
|
||
|
FT_Done_Glyph(glyph);
|
||
|
|
||
|
return box.yMax >> 6;
|
||
|
}
|
||
|
|
||
|
void FreeTypeFont::drawText(int x, int y, const wchar_t *wtxt, int len, COLORREF color, int antialias)
|
||
|
{
|
||
|
POINT pen = { x << 6, y};
|
||
|
|
||
|
// we start left to right
|
||
|
direction = 1;
|
||
|
|
||
|
WCHAR *neutral = NULL;
|
||
|
WCHAR *rtlstr = NULL;
|
||
|
|
||
|
wchar_t *freeme = 0;
|
||
|
|
||
|
/* TODO: change this to be
|
||
|
AutoChar ucs4(txt, 12000, WC_COMPOSITECHECK); // convert from UTF-16 to 'raw' Unicode
|
||
|
However, we have to re-write the LTR part of the code below
|
||
|
*/
|
||
|
if (FOLDSTRING)
|
||
|
{
|
||
|
wchar_t *txt = WMALLOC((len+1));
|
||
|
FOLDSTRING(MAP_PRECOMPOSED, wtxt, -1, txt, len+1);
|
||
|
freeme=txt;
|
||
|
wtxt=txt;
|
||
|
}
|
||
|
|
||
|
lastchar = 0;
|
||
|
for (int i=0 ; *wtxt && i<len;)
|
||
|
{
|
||
|
WORD cur_dir;
|
||
|
GetStringTypeExW(LOCALE_SYSTEM_DEFAULT, CT_CTYPE2, wtxt, 1, &cur_dir);
|
||
|
if (cur_dir == C2_RIGHTTOLEFT)
|
||
|
{
|
||
|
// we're now about to write some right-to-left text
|
||
|
// we need to scan the string to determine the length of this right-to-left block
|
||
|
if (!neutral) { neutral = WMALLOC(len+1); }
|
||
|
if (!rtlstr) { rtlstr = WMALLOC(len+1); }
|
||
|
rtlstr[0] = 0;
|
||
|
neutral[0] = 0;
|
||
|
|
||
|
const wchar_t *p = wtxt;
|
||
|
while (1)
|
||
|
{
|
||
|
WORD char_dir;
|
||
|
if (*p)
|
||
|
{
|
||
|
GetStringTypeExW(LOCALE_SYSTEM_DEFAULT, CT_CTYPE2, p, 1, &char_dir);
|
||
|
if (char_dir != C2_RIGHTTOLEFT && char_dir != C2_LEFTTORIGHT)
|
||
|
{
|
||
|
size_t l = wcslen(neutral);
|
||
|
neutral[l] = *p;
|
||
|
neutral[l+1] = 0;
|
||
|
}
|
||
|
}
|
||
|
if (!*p || char_dir == C2_LEFTTORIGHT)
|
||
|
{
|
||
|
// we now need to write rtlstr as right-to-left
|
||
|
int w = freetype_width(font, rtlstr, (int)wcslen(rtlstr), 1, antialias); // this is in fixed point units
|
||
|
|
||
|
// save current pen position
|
||
|
int oldpenx = pen.x;
|
||
|
|
||
|
// jump to the end of the block
|
||
|
pen.x += w;
|
||
|
|
||
|
p = rtlstr;
|
||
|
|
||
|
direction = -1;
|
||
|
|
||
|
while (p && *p)
|
||
|
{
|
||
|
// move to the left by the width of the char
|
||
|
pen.x -= freetype_width(font, p, 1, 1, antialias);
|
||
|
// render the rtl character
|
||
|
drawChar(pen.x, pen.y, *p, color, antialias);
|
||
|
p++;
|
||
|
}
|
||
|
|
||
|
// now jump our to the end of the rtl block
|
||
|
pen.x = oldpenx + w;
|
||
|
|
||
|
// skip what we just printed from the source string
|
||
|
wtxt += wcslen(rtlstr);
|
||
|
|
||
|
// and continue like nothing happened
|
||
|
direction = 1;
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
else if (char_dir == C2_RIGHTTOLEFT)
|
||
|
{
|
||
|
wcscat(rtlstr, neutral);
|
||
|
*neutral = 0;
|
||
|
size_t l = wcslen(rtlstr);
|
||
|
rtlstr[l] = *p;
|
||
|
rtlstr[l+1] = 0;
|
||
|
}
|
||
|
p++;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pen.x += drawChar(pen.x, pen.y, *wtxt, color, antialias);
|
||
|
wtxt++;
|
||
|
}
|
||
|
}
|
||
|
if (rtlstr) FREE(rtlstr);
|
||
|
if (neutral) FREE(neutral);
|
||
|
if (freeme) FREE(freeme);
|
||
|
}
|
||
|
|
||
|
|
||
|
int FreeTypeFont::drawChar(int x0, int y0, unsigned long c, COLORREF color, int antialias)
|
||
|
{
|
||
|
unsigned int *bits = (unsigned int *)blt->getBits();
|
||
|
int width = blt->getWidth();
|
||
|
int height = blt->getHeight();
|
||
|
|
||
|
x0;
|
||
|
|
||
|
FT_BitmapGlyph ftg;
|
||
|
|
||
|
int glyph_index = FT_Get_Char_Index(font, c);
|
||
|
|
||
|
FT_Vector delta = { 0, 0 };
|
||
|
#ifdef DO_KERNING
|
||
|
if (lastchar && FT_HAS_KERNING(font)) {
|
||
|
FT_Get_Kerning(font, lastchar, glyph_index, FT_KERNING_DEFAULT, &delta);
|
||
|
x0 += delta.x;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
int rc = FT_Load_Glyph(font, glyph_index, FT_LOAD_DEFAULT|FT_LOAD_NO_BITMAP);
|
||
|
if (rc)
|
||
|
return 0;
|
||
|
|
||
|
rc = FT_Get_Glyph(font->glyph, (FT_Glyph*)&ftg);
|
||
|
if (rc)
|
||
|
return 0;
|
||
|
|
||
|
if (font->style_flags & FT_STYLE_FLAG_ITALIC)
|
||
|
{
|
||
|
|
||
|
FT_Matrix mat;
|
||
|
|
||
|
// sets up the matrix, 0 degrees
|
||
|
double sintheta = sin(0.0);
|
||
|
double costheta = cos(0.0);
|
||
|
mat.xx = (FT_Fixed)(costheta * (1<<16));
|
||
|
mat.xy = (FT_Fixed)(sintheta * (1<<16));
|
||
|
mat.yx = -mat.xy;
|
||
|
mat.yy = mat.xx;
|
||
|
|
||
|
// shear the vectors for italic, 10 to 12 deg suggested
|
||
|
FT_Fixed f = (FT_Fixed)(tan(M_2PI/(360/FAUX_ITALIC_DEGREES)) * (1<<16));
|
||
|
mat.xy += FT_MulFix(f, mat.xx);
|
||
|
mat.yy += FT_MulFix(f, mat.yx);
|
||
|
|
||
|
// do the transform
|
||
|
FT_Vector v = {0,0};
|
||
|
FT_Vector_Transform(&v, &mat);
|
||
|
FT_Glyph_Transform((FT_Glyph)ftg, &mat, &v);
|
||
|
|
||
|
}
|
||
|
|
||
|
// get the glyph
|
||
|
rc = FT_Glyph_To_Bitmap((FT_Glyph*)&ftg, antialias?ft_render_mode_normal:ft_render_mode_mono, NULL, 1);
|
||
|
if (rc) {
|
||
|
FT_Done_Glyph((FT_Glyph)ftg);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
FT_Bitmap *bmp = &ftg->bitmap;
|
||
|
|
||
|
int r = 1;
|
||
|
if (font->style_flags & FT_STYLE_FLAG_BOLD)
|
||
|
r = MAX((int)((float)(font->glyph->advance.x >> 6) * FAUX_BOLD_RATIO), 2);
|
||
|
|
||
|
int ys = MAX(0, y0 - ftg->top);
|
||
|
int ye = MIN(height, (int)(y0 + bmp->rows - ftg->top));
|
||
|
int xs = MAX(0, (x0 >> 6) + ftg->left);
|
||
|
int xe = MIN(width, (int)((x0 >> 6) + ftg->left + bmp->width));
|
||
|
|
||
|
unsigned char *_bmpbits = bmp->buffer + (ys + ftg->top - y0) * bmp->pitch + (xs - ftg->left - (x0 >> 6));
|
||
|
unsigned int *_linebits = (unsigned int *)(bits + ys * width + xs);
|
||
|
|
||
|
if (ftg->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY) {
|
||
|
for (int z = 0; z < r; z++) {
|
||
|
unsigned char *bmpbits = _bmpbits;
|
||
|
unsigned int *linebits = _linebits + z;
|
||
|
for (int y = ys; y < ye; y++) {
|
||
|
if (z > 0) {
|
||
|
for (int x = xs; x < xe; x++) {
|
||
|
if (x != width-z) {
|
||
|
*linebits |= overlay((int)(*linebits >> 24), (int)*bmpbits, curboldstrength, r, z) << 24;
|
||
|
linebits++; bmpbits++;
|
||
|
} else {
|
||
|
linebits++; bmpbits++;
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
for (int x = xs; x < xe; x++)
|
||
|
*linebits++ |= *bmpbits++ << 24;
|
||
|
}
|
||
|
bmpbits += bmp->pitch - (xe - xs);
|
||
|
linebits += width - (xe - xs);
|
||
|
}
|
||
|
}
|
||
|
} else if (ftg->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) {
|
||
|
for (int z = 0; z < r; z++) {
|
||
|
unsigned char *bmpbits = _bmpbits;
|
||
|
unsigned int *linebits = _linebits + z;
|
||
|
for (int y = ys; y < ye; y++) {
|
||
|
for (int x = xs; x < xe; x++) {
|
||
|
/* if (y == ys || y == ye-1 || x == xs || x == xe-1)
|
||
|
*linebits++ |= 0xFF << 24;
|
||
|
else
|
||
|
linebits++;*/
|
||
|
int byte = (x-xs)>>3;
|
||
|
*linebits++ |= (*(bmpbits + byte) & (1 << (7-((x-xs)-(byte<<3)))) ? 0xFF : 0) << 24;
|
||
|
}
|
||
|
bmpbits += bmp->pitch;
|
||
|
linebits += width - (xe - xs);
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
// simply draw rectangles
|
||
|
for (int z = 0; z < r; z++) {
|
||
|
unsigned char *bmpbits = _bmpbits;
|
||
|
unsigned int *linebits = _linebits + z;
|
||
|
for (int y = ys; y < ye; y++) {
|
||
|
for (int x = xs; x < xe; x++) {
|
||
|
if (y == ys || y == ye-1 || x == xs || x == xe-1)
|
||
|
*linebits++ |= 0xFF << 24;
|
||
|
else
|
||
|
linebits++;
|
||
|
}
|
||
|
bmpbits += bmp->pitch;
|
||
|
linebits += width - (xe - xs);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
FT_Done_Glyph((FT_Glyph)ftg);
|
||
|
|
||
|
lastchar = glyph_index;
|
||
|
|
||
|
return font->glyph->advance.x + delta.x + ((r - 1) << 6);
|
||
|
}
|
||
|
|
||
|
void FreeTypeFont::textOut(api_canvas *c, int x, int y, const wchar_t *txt, int size, int bold, int opaque, int underline, int italic, COLORREF color, COLORREF bkcolor, int xoffset, int yoffset, int antialias)
|
||
|
{
|
||
|
color = RGBTOBGR(color);
|
||
|
bkcolor = RGBTOBGR(bkcolor);
|
||
|
y++; x++;
|
||
|
prepareCanvas(c, size, bold, opaque, underline, italic, color, bkcolor, txt);
|
||
|
int maxheight = getAscent();
|
||
|
|
||
|
drawText(0, maxheight, txt, (int)wcslen(txt), color, antialias);
|
||
|
|
||
|
/* POINT pen = { 0, maxheight };
|
||
|
|
||
|
lastchar = 0;
|
||
|
for (; *txt; txt++) {
|
||
|
pen.x += drawChar(pen.x, pen.y, *txt, color);
|
||
|
}*/
|
||
|
|
||
|
restoreCanvas(c, x + xoffset , y + yoffset);
|
||
|
}
|
||
|
|
||
|
void FreeTypeFont::textOut2(api_canvas *c, int x, int y, int w, int h, const wchar_t *txt, int size, int bold, int opaque, int underline, int italic, int align, COLORREF color, COLORREF bkcolor, int xoffset, int yoffset, int antialias)
|
||
|
{
|
||
|
color = RGBTOBGR(color);
|
||
|
bkcolor = RGBTOBGR(bkcolor);
|
||
|
y++; x++;
|
||
|
prepareCanvas(c, size, bold, opaque, underline, italic, color, bkcolor, txt, w, h);
|
||
|
int maxheight = getAscent();
|
||
|
int width = getTextWidth(c,txt,size,bold,underline,italic,antialias);
|
||
|
|
||
|
int xstart = 0;
|
||
|
if (align == DT_RIGHT)
|
||
|
{
|
||
|
xstart = w - width;
|
||
|
}
|
||
|
else if (align == DT_CENTER)
|
||
|
{
|
||
|
xstart = (w - width) / 2;
|
||
|
}
|
||
|
|
||
|
|
||
|
drawText(xstart, maxheight, txt, (int)wcslen(txt), color, antialias);
|
||
|
|
||
|
/* POINT pen = { xstart << 6, maxheight };
|
||
|
|
||
|
lastchar = 0;
|
||
|
for (; *txt; txt++) {
|
||
|
pen.x += drawChar(pen.x, pen.y, *txt, color);
|
||
|
}*/
|
||
|
|
||
|
restoreCanvas(c, x + xoffset , y + yoffset);
|
||
|
}
|
||
|
|
||
|
void FreeTypeFont::textOutEllipsed(api_canvas *c, int x, int y, int w, int h, const wchar_t *txt, int size, int bold, int opaque, int underline, int italic, int align, COLORREF color, COLORREF bkcolor, int xoffset, int yoffset, int antialias)
|
||
|
{
|
||
|
color = RGBTOBGR(color);
|
||
|
bkcolor = RGBTOBGR(bkcolor);
|
||
|
y++; x++;
|
||
|
if (txt == NULL)
|
||
|
return;
|
||
|
|
||
|
RECT r;
|
||
|
r.left = x+xoffset;
|
||
|
r.top = y+yoffset;
|
||
|
r.right = r.left + w;
|
||
|
r.bottom = r.top + h;
|
||
|
prepareCanvas(c, size, bold, opaque, underline, italic, color, bkcolor, txt, w, h);
|
||
|
|
||
|
int len = ((int)wcslen(txt) + 3);
|
||
|
wchar_t *tmp = (wchar_t *)MALLOC(sizeof(wchar_t) * len);
|
||
|
wcsncpy(tmp, txt, len);
|
||
|
int width, height;
|
||
|
width = getTextWidth(c, txt, size, bold, underline, italic, (int)antialias);
|
||
|
height = getAscent();
|
||
|
int dddw = getTextWidth(c, L"...", size, bold, underline, italic, antialias);
|
||
|
|
||
|
if (width > r.right - r.left)
|
||
|
{
|
||
|
wchar_t *p = tmp + wcslen(tmp);
|
||
|
width = r.right - r.left - dddw;
|
||
|
while(p > tmp &&
|
||
|
getTextWidth(c,tmp,size,bold,underline,italic,antialias) > width)
|
||
|
{
|
||
|
*p-- = '\0';
|
||
|
}
|
||
|
wcscpy(p, L"...");
|
||
|
}
|
||
|
|
||
|
drawText(0, height, tmp, (int)wcslen(tmp), color, antialias);
|
||
|
|
||
|
/* POINT pen = { 0, height };
|
||
|
|
||
|
lastchar = 0;
|
||
|
for (char *p = tmp; *p; p++) {
|
||
|
pen.x += drawChar(pen.x, pen.y, *p, color);
|
||
|
}*/
|
||
|
|
||
|
FREE(tmp);
|
||
|
|
||
|
restoreCanvas(c, r.left, r.top);
|
||
|
}
|
||
|
|
||
|
static int freetype_width(void *data, const wchar_t *str, int len, int fixed, int antialias)
|
||
|
{
|
||
|
FT_Face font = (FT_Face)data;
|
||
|
|
||
|
int w = 0;
|
||
|
|
||
|
int prev, index;
|
||
|
const wchar_t *p;
|
||
|
int count = 0;
|
||
|
for (p = str; *p && count < len;)
|
||
|
{
|
||
|
FT_Vector delta = { 0, 0 };
|
||
|
|
||
|
FT_BitmapGlyph ftg;
|
||
|
|
||
|
index = FT_Get_Char_Index(font, *p);
|
||
|
|
||
|
#ifdef DO_KERNING
|
||
|
if (w > 0 && FT_HAS_KERNING(font)) {
|
||
|
FT_Get_Kerning(font, prev, index, ft_kerning_default, &delta);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
int rc = FT_Load_Glyph(font, index, FT_LOAD_DEFAULT|FT_LOAD_NO_BITMAP);
|
||
|
if (rc)
|
||
|
{
|
||
|
p++;
|
||
|
count++;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
w += (delta.x + font->glyph->advance.x);
|
||
|
prev = index;
|
||
|
|
||
|
|
||
|
rc = FT_Get_Glyph(font->glyph, (FT_Glyph*)&ftg);
|
||
|
if (rc)
|
||
|
{
|
||
|
p++;
|
||
|
count++;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (font->style_flags & FT_STYLE_FLAG_ITALIC)
|
||
|
{
|
||
|
|
||
|
FT_Matrix mat;
|
||
|
|
||
|
// sets up the matrix, 0 degrees
|
||
|
double sintheta = sin(0.0);
|
||
|
double costheta = cos(0.0);
|
||
|
mat.xx = (FT_Fixed)(costheta * (1<<16));
|
||
|
mat.xy = (FT_Fixed)(sintheta * (1<<16));
|
||
|
mat.yx = -mat.xy;
|
||
|
mat.yy = mat.xx;
|
||
|
|
||
|
// shear the vectors for italic, 10 to 12 deg suggested
|
||
|
FT_Fixed f = (FT_Fixed)(tan(M_2PI/(360/FAUX_ITALIC_DEGREES)) * (1<<16));
|
||
|
mat.xy += FT_MulFix(f, mat.xx);
|
||
|
mat.yy += FT_MulFix(f, mat.yx);
|
||
|
|
||
|
// do the transform
|
||
|
FT_Vector v = {0,0};
|
||
|
FT_Vector_Transform(&v, &mat);
|
||
|
FT_Glyph_Transform((FT_Glyph)ftg, &mat, &v);
|
||
|
|
||
|
}
|
||
|
|
||
|
// get the glyph
|
||
|
rc = FT_Glyph_To_Bitmap((FT_Glyph*)&ftg, antialias?ft_render_mode_normal:ft_render_mode_mono, NULL, 1);
|
||
|
if (rc)
|
||
|
{
|
||
|
FT_Done_Glyph((FT_Glyph)ftg);
|
||
|
p++;
|
||
|
count++;
|
||
|
continue;
|
||
|
}
|
||
|
FT_Bitmap *bmp = &ftg->bitmap;
|
||
|
|
||
|
int ys = MAX(0, ftg->top);
|
||
|
int ye = bmp->rows - ftg->top;
|
||
|
int xs = MAX(0, ftg->left);
|
||
|
int xe = ftg->left + bmp->width;
|
||
|
|
||
|
FT_Done_Glyph((FT_Glyph)ftg);
|
||
|
|
||
|
int r = 1;
|
||
|
if (font->style_flags & FT_STYLE_FLAG_BOLD)
|
||
|
{
|
||
|
r = MAX((int)((float)(font->glyph->advance.x >> 6) * FAUX_BOLD_RATIO), 2);
|
||
|
}
|
||
|
w += ((r - 1) << 6);
|
||
|
p++;
|
||
|
count++;
|
||
|
}
|
||
|
|
||
|
if (fixed) return w;
|
||
|
return (w >> 6);
|
||
|
}
|
||
|
|
||
|
|
||
|
void FreeTypeFont::textOutWrapped(api_canvas *c, int x, int y, int w, int h, const wchar_t *txt, int size, int bold, int opaque, int underline, int italic, int align, COLORREF color, COLORREF bkcolor, int xoffset, int yoffset, int antialias)
|
||
|
{
|
||
|
color = RGBTOBGR(color);
|
||
|
bkcolor = RGBTOBGR(bkcolor);
|
||
|
y++; x++;
|
||
|
prepareCanvas(c, size, bold, opaque, underline, italic, color, bkcolor, txt, w, h);
|
||
|
|
||
|
int ascent = getAscent();
|
||
|
int descent = getTextHeight2(c, size, bold, underline, italic, antialias);
|
||
|
descent -= ascent;
|
||
|
|
||
|
int yoff = ascent;
|
||
|
const wchar_t *cur = txt, *next;
|
||
|
int length = (int)wcslen(txt);
|
||
|
|
||
|
//NO.
|
||
|
//for(int yoff = ascent;
|
||
|
for(yoff = ascent;
|
||
|
yoff < h;
|
||
|
yoff += ascent + descent) {
|
||
|
|
||
|
next = find_break(font, cur, w, antialias);
|
||
|
|
||
|
/* POINT pen = { 0, yoff };
|
||
|
|
||
|
lastchar = 0;
|
||
|
for (; cur < next; cur++) {
|
||
|
pen.x += drawChar(pen.x, pen.y, *cur, color);
|
||
|
}*/
|
||
|
drawText(0, yoff, cur, (int)(next-cur), color, antialias);
|
||
|
|
||
|
cur = next;
|
||
|
while (cur && *cur && *cur == ' ')
|
||
|
cur++;
|
||
|
|
||
|
if (*cur == '\r' || *cur == '\n') cur++;
|
||
|
|
||
|
if (cur >= txt + length)
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
restoreCanvas(c, x + xoffset , y + yoffset);
|
||
|
}
|
||
|
|
||
|
void FreeTypeFont::textOutWrappedPathed(api_canvas *c, int x, int y, int w, const wchar_t *txt, int size, int bold, int opaque, int underline, int italic, int align, COLORREF color, COLORREF bkcolor, int xoffset, int yoffset, int antialias)
|
||
|
{
|
||
|
color = RGBTOBGR(color);
|
||
|
bkcolor = RGBTOBGR(bkcolor);
|
||
|
DebugString("writeme -- FreeTypeFont::textOutWrappedPathed...\n");
|
||
|
}
|
||
|
|
||
|
void FreeTypeFont::textOutCentered(api_canvas *c, RECT *r, const wchar_t *txt, int size, int bold, int opaque, int underline, int italic, int align, COLORREF color, COLORREF bkcolor, int xoffset, int yoffset, int antialias)
|
||
|
{
|
||
|
color = RGBTOBGR(color);
|
||
|
bkcolor = RGBTOBGR(bkcolor);
|
||
|
r->top++;
|
||
|
r->left++;
|
||
|
|
||
|
RECT rr = *r;
|
||
|
rr.left += xoffset;
|
||
|
rr.right += xoffset;
|
||
|
rr.top += yoffset;
|
||
|
rr.bottom += yoffset;
|
||
|
|
||
|
prepareCanvas(c, size, bold, opaque, underline, italic, color, bkcolor, txt, rr.right - rr.left, rr.bottom - rr.top);
|
||
|
|
||
|
int width, height;
|
||
|
height = getAscent();
|
||
|
width = getTextWidth(c, txt, size, bold, underline, italic, (int)antialias);
|
||
|
|
||
|
drawText(((rr.right - rr.left - width) / 2), height, txt, (int)wcslen(txt), color, (int)antialias);
|
||
|
/* POINT pen = { ((rr.right - rr.left - width) / 2) << 6, height };
|
||
|
|
||
|
lastchar = 0;
|
||
|
for (; *txt; txt++) {
|
||
|
pen.x += drawChar(pen.x, pen.y, *txt, color);
|
||
|
}*/
|
||
|
|
||
|
restoreCanvas(c, rr.left, rr.top);
|
||
|
}
|
||
|
|
||
|
int FreeTypeFont::getTextWidth(api_canvas *c, const wchar_t *text, int size, int bold, int underline, int italic, int antialiased)
|
||
|
{
|
||
|
int w;
|
||
|
updateCharmap();
|
||
|
getTextExtent(c, text, &w, NULL, size, bold, underline, italic, antialiased);
|
||
|
return w;
|
||
|
}
|
||
|
|
||
|
int FreeTypeFont::getTextHeight(api_canvas *c, const wchar_t *text, int size, int bold, int underline, int italic, int antialiased) {
|
||
|
int h;
|
||
|
updateCharmap();
|
||
|
getTextExtent(c, text, NULL, &h, size, bold, underline, italic, antialiased);
|
||
|
{
|
||
|
// calcul for multiline text
|
||
|
const wchar_t *p=text;
|
||
|
int n=0;
|
||
|
while(p && *p!=0) if(*p++=='\n') n++;
|
||
|
if(n) h*=(n+1);
|
||
|
}
|
||
|
return h;
|
||
|
}
|
||
|
|
||
|
int FreeTypeFont::getTextHeight2(api_canvas *c, int size, int bold, int underline, int italic, int antialiased)
|
||
|
{
|
||
|
return getTextHeight(c, L"Mg", size, bold, underline, italic, antialiased);
|
||
|
}
|
||
|
|
||
|
void FreeTypeFont::getTextExtent(api_canvas *c, const wchar_t *txt, int *w, int *h, int size, int bold, int underline, int italic, int antialias)
|
||
|
{
|
||
|
updateCharmap();
|
||
|
// Our "size" variable is fun to calculate!
|
||
|
//int vRez = GetDeviceCaps(c->getHDC(), LOGPIXELSY); // this needs to be a Canvas method or something.
|
||
|
int fsize = tweakSize(facename, size);
|
||
|
int nHeight = MulDiv(fsize << 6, 72, 96);
|
||
|
FT_Set_Char_Size(font, 0, nHeight, 0, 0);
|
||
|
|
||
|
font->style_flags = 0;
|
||
|
if (bold)
|
||
|
font->style_flags |= FT_STYLE_FLAG_BOLD;
|
||
|
if (italic)
|
||
|
font->style_flags |= FT_STYLE_FLAG_ITALIC;
|
||
|
if (underline)
|
||
|
font->underline_thickness = 1;
|
||
|
else
|
||
|
font->underline_thickness = 0;
|
||
|
|
||
|
SIZE rsize={0,0};
|
||
|
ASSERT(txt != NULL);
|
||
|
if (*txt == 0)
|
||
|
{
|
||
|
if (w != NULL) *w = 0;
|
||
|
if (h != NULL) *h = 0;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
FT_BBox box;
|
||
|
FT_Glyph glyph;
|
||
|
|
||
|
int minh = 0, maxh = 0;
|
||
|
|
||
|
if (w)
|
||
|
*w = freetype_width(font, txt, (int)wcslen(txt), 0, antialias) + HORIZONTAL_RPADDING + HORIZONTAL_LPADDING;
|
||
|
|
||
|
if (h)
|
||
|
{
|
||
|
FT_Load_Glyph(font, FT_Get_Char_Index(font, 'M'), FT_LOAD_DEFAULT);
|
||
|
FT_Get_Glyph(font->glyph, &glyph);
|
||
|
FT_Glyph_Get_CBox(glyph, ft_glyph_bbox_unscaled, &box);
|
||
|
maxh = box.yMax;
|
||
|
FT_Done_Glyph(glyph);
|
||
|
|
||
|
FT_Load_Glyph(font, FT_Get_Char_Index(font, 'g'), FT_LOAD_DEFAULT);
|
||
|
FT_Get_Glyph(font->glyph, &glyph);
|
||
|
FT_Glyph_Get_CBox(glyph, ft_glyph_bbox_unscaled, &box);
|
||
|
minh = box.yMin;
|
||
|
FT_Done_Glyph(glyph);
|
||
|
|
||
|
*h = ((maxh - minh) >> 6) + VERTICAL_TPADDING + VERTICAL_BPADDING;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
int FreeTypeFont::isBitmap()
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static const wchar_t *find_break(void *f, const wchar_t *str, int width, int antialias)
|
||
|
{
|
||
|
const wchar_t *softret, *lastsoft, *hardret;
|
||
|
|
||
|
if (freetype_width(f, str, (int)wcslen(str), 0, antialias) <= width)
|
||
|
return str + wcslen(str);
|
||
|
|
||
|
for(hardret = str; *hardret; hardret ++)
|
||
|
if (*hardret == '\r' || *hardret == '\n')
|
||
|
break;
|
||
|
|
||
|
if (hardret && freetype_width(f, str, (int)(hardret - str), 0, antialias) <= width) {
|
||
|
return hardret;
|
||
|
}
|
||
|
for(softret = str; *softret && !isspace(*softret); softret++)
|
||
|
;
|
||
|
|
||
|
if (freetype_width(f, str, (int)(softret - str), 0, antialias) <= width)
|
||
|
{
|
||
|
do
|
||
|
{
|
||
|
lastsoft = softret;
|
||
|
|
||
|
for(softret = lastsoft+1; *softret && !isspace(*softret); softret++)
|
||
|
;
|
||
|
|
||
|
} while (lastsoft && *lastsoft && freetype_width(f, str, (int)(softret - str), 0, antialias) <= width);
|
||
|
|
||
|
softret = lastsoft;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
for(softret = str; *softret; softret++)
|
||
|
if (freetype_width(f, str, (int)(softret - str), 0, antialias) > width)
|
||
|
break;
|
||
|
|
||
|
softret--;
|
||
|
}
|
||
|
|
||
|
return softret;
|
||
|
}
|