* Changed the AGGTextRenderer to use the new UTF8 handling

* Added more char codes to is_white_space(), should be all I think

Sorry if I stepped on your toes Stephan, but I wanted these changes flushed before I leave for holidays :-).

git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@16501 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Michael Lotz
2006-02-23 09:04:26 +00:00
parent 6990418519
commit 20a4e7265c
4 changed files with 179 additions and 218 deletions
+2 -4
View File
@@ -159,10 +159,8 @@ UTF8ToCharCode(const char **bytes)
} }
if ((*bytes)[0] == 0) { if ((*bytes)[0] == 0) {
/* We do not advance beyond the terminating 0. Just pad any further /* We do not advance beyond the terminating 0. */
request with spaces. */ return 0x00;
result += 0x20;
return result;
} }
result += (*bytes)[0]; result += (*bytes)[0];
+23 -14
View File
@@ -81,11 +81,21 @@ CubicToFunc(FT_Vector *control1, FT_Vector *control2, FT_Vector *to, void *user)
inline bool inline bool
is_white_space(uint16 glyph) is_white_space(uint32 charCode)
{ {
// TODO: handle them all! switch (charCode) {
if (glyph == ' ' || glyph == B_TAB) case 0x0009: /* tab */
case 0x000b: /* vertical tab */
case 0x000c: /* form feed */
case 0x0020: /* space */
case 0x00a0: /* non breaking space */
case 0x000a: /* line feed */
case 0x000d: /* carriage return */
case 0x2028: /* line separator */
case 0x2029: /* paragraph separator */
return true; return true;
}
return false; return false;
} }
@@ -428,10 +438,9 @@ ServerFont::GetEscapements(const char charArray[], int32 numChars,
const char *string = charArray; const char *string = charArray;
for (int i = 0; i < numChars; i++) { for (int i = 0; i < numChars; i++) {
// Do this first as UTF8ToCharCode advances string. uint32 charCode = UTF8ToCharCode(&string);
escapementArray[i].x = is_white_space(*string) ? delta.space : delta.nonspace; FT_Load_Char(face, charCode, FT_LOAD_NO_BITMAP);
escapementArray[i].x = is_white_space(charCode) ? delta.space : delta.nonspace;
FT_Load_Char(face, UTF8ToCharCode(&string), FT_LOAD_NO_BITMAP);
escapementArray[i].x += float(face->glyph->advance.x) / 64; escapementArray[i].x += float(face->glyph->advance.x) / 64;
escapementArray[i].y = -float(face->glyph->advance.y) / 64; escapementArray[i].y = -float(face->glyph->advance.y) / 64;
escapementArray[i].x /= fSize; escapementArray[i].x /= fSize;
@@ -466,10 +475,9 @@ ServerFont::GetEscapements(const char charArray[], int32 numChars,
const char *string = charArray; const char *string = charArray;
for (int i = 0; i < numChars; i++) { for (int i = 0; i < numChars; i++) {
// Do this first as UTF8ToCharCode advances string. uint32 charCode = UTF8ToCharCode(&string);
widthArray[i] = is_white_space(*string) ? delta.space : delta.nonspace; FT_Load_Char(face, charCode, FT_LOAD_NO_BITMAP);
widthArray[i] = is_white_space(charCode) ? delta.space : delta.nonspace;
FT_Load_Char(face, UTF8ToCharCode(&string), FT_LOAD_NO_BITMAP);
widthArray[i] += float(face->glyph->metrics.horiAdvance) / 64.0; widthArray[i] += float(face->glyph->metrics.horiAdvance) / 64.0;
widthArray[i] /= fSize; widthArray[i] /= fSize;
} }
@@ -493,14 +501,15 @@ ServerFont::GetBoundingBoxesAsString(const char charArray[], int32 numChars,
const char *string = charArray; const char *string = charArray;
for (int i = 0; i < numChars; i++) { for (int i = 0; i < numChars; i++) {
uint32 charCode = UTF8ToCharCode(&string);
if (stringEscapement) { if (stringEscapement) {
if (i > 0) if (i > 0)
rectArray[i].OffsetBy(is_white_space(*string) ? delta.space / 2.0 : delta.nonspace / 2.0, 0.0); rectArray[i].OffsetBy(is_white_space(charCode) ? delta.space / 2.0 : delta.nonspace / 2.0, 0.0);
rectArray[i].OffsetBy(is_white_space(*string) ? delta.space / 2.0 : delta.nonspace / 2.0, 0.0); rectArray[i].OffsetBy(is_white_space(charCode) ? delta.space / 2.0 : delta.nonspace / 2.0, 0.0);
} }
FT_Load_Char(face, UTF8ToCharCode(&string), FT_LOAD_NO_BITMAP); FT_Load_Char(face, charCode, FT_LOAD_NO_BITMAP);
if (i < numChars - 1) { if (i < numChars - 1) {
rectArray[i + 1].left = rectArray[i + 1].right = rectArray[i].left rectArray[i + 1].left = rectArray[i + 1].right = rectArray[i].left
+ face->glyph->metrics.horiAdvance / 64.0; + face->glyph->metrics.horiAdvance / 64.0;
@@ -20,6 +20,7 @@
#include <agg_trans_affine.h> #include <agg_trans_affine.h>
#include "AGGTextRenderer.h" #include "AGGTextRenderer.h"
#include "moreUTF8.h"
#define FLIP_Y false #define FLIP_Y false
@@ -42,13 +43,22 @@ rect_to_int(BRect r,
} }
// is_white_space
inline bool inline bool
is_white_space(uint16 glyph) is_white_space(uint32 charCode)
{ {
// TODO: handle them all! switch (charCode) {
if (glyph == ' ' || glyph == B_TAB) case 0x0009: /* tab */
case 0x000b: /* vertical tab */
case 0x000c: /* form feed */
case 0x0020: /* space */
case 0x00a0: /* non breaking space */
case 0x000a: /* line feed */
case 0x000d: /* carriage return */
case 0x2028: /* line separator */
case 0x2029: /* paragraph separator */
return true; return true;
}
return false; return false;
} }
@@ -156,7 +166,7 @@ AGGTextRenderer::Unset()
// TODO ? release some kind of reference count on the ServerFont? // TODO ? release some kind of reference count on the ServerFont?
} }
// RenderString
BRect BRect
AGGTextRenderer::RenderString(const char* string, AGGTextRenderer::RenderString(const char* string,
uint32 length, uint32 length,
@@ -178,9 +188,6 @@ AGGTextRenderer::RenderString(const char* string,
Transformable transform(fEmbeddedTransformation); Transformable transform(fEmbeddedTransformation);
transform.TranslateBy(baseLine); transform.TranslateBy(baseLine);
uint32 glyphCount;
if (_PrepareUnicodeBuffer(string, length, &glyphCount) >= B_OK) {
fCurves.approximation_scale(transform.scale()); fCurves.approximation_scale(transform.scale());
// use a transformation behind the curves // use a transformation behind the curves
@@ -190,49 +197,55 @@ AGGTextRenderer::RenderString(const char* string,
conv_font_trans_type; conv_font_trans_type;
conv_font_trans_type transformedOutline(fCurves, transform); conv_font_trans_type transformedOutline(fCurves, transform);
uint16* p = (uint16*)fUnicodeBuffer;
double x = 0.0; double x = 0.0;
double y0 = 0.0; double y0 = 0.0;
double y = y0; double y = y0;
double advanceX = 0.0; double advanceX = 0.0;
double advanceY = 0.0; double advanceY = 0.0;
bool firstLoop = true;
// for when we bypass the transformation pipeline // for when we bypass the transformation pipeline
BPoint transformOffset(0.0, 0.0); BPoint transformOffset(0.0, 0.0);
transform.Transform(&transformOffset); transform.Transform(&transformOffset);
for (uint32 i = 0; i < glyphCount; i++) { uint32 charCode;
/* // line break (not supported by R5) while ((charCode = UTF8ToCharCode(&string)) > 0) {
if (*p == '\n') { // line break (not supported by R5)
/*if (charCode == '\n') {
y0 += LineOffset(); y0 += LineOffset();
x = 0.0; x = 0.0;
y = y0; y = y0;
advanceX = 0.0; advanceX = 0.0;
advanceY = 0.0; advanceY = 0.0;
++p;
continue; continue;
}*/ }*/
const agg::glyph_cache* glyph = fFontCache.glyph(*p); const agg::glyph_cache* glyph = fFontCache.glyph(charCode);
if (glyph) { if (!glyph) {
if (i > 0 && fKerning) if (charCode >= 0x20 && charCode < 0x80)
fprintf(stderr, "failed to load glyph for '%c'\n", (char)charCode);
else
fprintf(stderr, "failed to load glyph for 0x%04x\n", charCode);
continue;
}
if (!firstLoop && fKerning)
fFontCache.add_kerning(&advanceX, &advanceY); fFontCache.add_kerning(&advanceX, &advanceY);
x += advanceX; x += advanceX;
y += advanceY; y += advanceY;
if (delta) if (delta)
x += is_white_space(*p) ? delta->space : delta->nonspace; x += is_white_space(charCode) ? delta->space : delta->nonspace;
// "glyphBounds" is the bounds of the glyph transformed // "glyphBounds" is the bounds of the glyph transformed
// by the x y location of the glyph along the base line, // by the x y location of the glyph along the base line,
// it is therefor yet "untransformed". // it is therefor yet "untransformed".
const agg::rect& r = glyph->bounds; const agg::rect& r = glyph->bounds;
BRect glyphBounds(r.x1 + x, r.y1 + y - 1, r.x2 + x + 1, r.y2 + y + 1); BRect glyphBounds(r.x1 + x, r.y1 + y - 1, r.x2 + x + 1, r.y2 + y + 1);
// NOTE: "-1"/"+ 1" converts the glyph bounding box from pixel // NOTE: "-1"/"+1" converts the glyph bounding box from pixel
// indices to pixel area coordinates // indices to pixel area coordinates
// track bounding box // track bounding box
@@ -259,8 +272,7 @@ AGGTextRenderer::RenderString(const char* string,
double transformedX = x + transformOffset.x; double transformedX = x + transformOffset.x;
double transformedY = y + transformOffset.y; double transformedY = y + transformOffset.y;
fFontCache.init_embedded_adaptors(glyph, fFontCache.init_embedded_adaptors(glyph,
transformedX, transformedX, transformedY);
transformedY);
glyphBounds.OffsetBy(transformOffset); glyphBounds.OffsetBy(transformOffset);
} else { } else {
fFontCache.init_embedded_adaptors(glyph, x, y); fFontCache.init_embedded_adaptors(glyph, x, y);
@@ -271,14 +283,12 @@ AGGTextRenderer::RenderString(const char* string,
switch (glyph->data_type) { switch (glyph->data_type) {
case agg::glyph_data_mono: case agg::glyph_data_mono:
agg::render_scanlines(fFontCache.mono_adaptor(), agg::render_scanlines(fFontCache.mono_adaptor(),
fFontCache.mono_scanline(), fFontCache.mono_scanline(), *binRenderer);
*binRenderer);
break; break;
case agg::glyph_data_gray8: case agg::glyph_data_gray8:
agg::render_scanlines(fFontCache.gray8_adaptor(), agg::render_scanlines(fFontCache.gray8_adaptor(),
fFontCache.gray8_scanline(), fFontCache.gray8_scanline(), *solidRenderer);
*solidRenderer);
break; break;
case agg::glyph_data_outline: { case agg::glyph_data_outline: {
@@ -288,10 +298,10 @@ AGGTextRenderer::RenderString(const char* string,
// commented out code, I leave it here because I think it // commented out code, I leave it here because I think it
// will be needed again. // will be needed again.
// if(fabs(0.0) <= 0.01) { //if(fabs(0.0) <= 0.01) {
// For the sake of efficiency skip the // For the sake of efficiency skip the
// contour converter if the weight is about zero. // contour converter if the weight is about zero.
//-----------------------
fRasterizer.add_path(transformedOutline); fRasterizer.add_path(transformedOutline);
#if SHOW_GLYPH_BOUNDS #if SHOW_GLYPH_BOUNDS
agg::path_storage p; agg::path_storage p;
@@ -304,10 +314,11 @@ AGGTextRenderer::RenderString(const char* string,
ps.width(1.0); ps.width(1.0);
fRasterizer.add_path(ps); fRasterizer.add_path(ps);
#endif #endif
/* } else { /*} else {
// fRasterizer.add_path(fContour); //fRasterizer.add_path(fContour);
fRasterizer.add_path(transformedOutline); fRasterizer.add_path(transformedOutline);
}*/ }*/
agg::render_scanlines(fRasterizer, fScanline, *solidRenderer); agg::render_scanlines(fRasterizer, fScanline, *solidRenderer);
break; break;
} }
@@ -320,18 +331,10 @@ AGGTextRenderer::RenderString(const char* string,
// increment pen position // increment pen position
advanceX = glyph->advance_x; advanceX = glyph->advance_x;
advanceY = glyph->advance_y; advanceY = glyph->advance_y;
} else {
if (*p < 128) { firstLoop = false;
char c[2]; };
c[0] = (uint8)*p;
c[1] = 0;
fprintf(stderr, "failed to load glyph for '%s'\n", c);
} else {
fprintf(stderr, "failed to load glyph for %d\n", *p);
}
}
++p;
}
// put pen location behind rendered text // put pen location behind rendered text
// (at the baseline of the virtual next glyph) // (at the baseline of the virtual next glyph)
if (nextCharPos) { if (nextCharPos) {
@@ -340,15 +343,13 @@ AGGTextRenderer::RenderString(const char* string,
transform.Transform(nextCharPos); transform.Transform(nextCharPos);
} }
}
return transform.TransformBounds(bounds); return transform.TransformBounds(bounds);
} }
// StringWidth
double double
AGGTextRenderer::StringWidth(const char* utf8String, uint32 length) AGGTextRenderer::StringWidth(const char* string, uint32 length)
{ {
// NOTE: The implementation does not take font rotation (or shear) // NOTE: The implementation does not take font rotation (or shear)
// into account. Just like on R5. Should it ever be desirable to // into account. Just like on R5. Should it ever be desirable to
@@ -361,69 +362,25 @@ AGGTextRenderer::StringWidth(const char* utf8String, uint32 length)
// Note that shear will not have any influence on the baseline though. // Note that shear will not have any influence on the baseline though.
double width = 0.0; double width = 0.0;
uint32 glyphCount; uint32 charCode;
if (_PrepareUnicodeBuffer(utf8String, length, &glyphCount) >= B_OK) {
uint16* p = (uint16*)fUnicodeBuffer;
double y = 0.0; double y = 0.0;
const agg::glyph_cache* glyph; const agg::glyph_cache* glyph;
bool firstLoop = true;
for (uint32 i = 0; i < glyphCount; i++) { while ((charCode = UTF8ToCharCode(&string)) > 0) {
if ((glyph = fFontCache.glyph(*p))) { glyph = fFontCache.glyph(charCode);
if (i > 0 && fKerning) if (glyph) {
if (!firstLoop && fKerning)
fFontCache.add_kerning(&width, &y); fFontCache.add_kerning(&width, &y);
width += glyph->advance_x; width += glyph->advance_x;
} }
++p;
} firstLoop = false;
} };
return width; return width;
} }
// _PrepareUnicodeBuffer
status_t
AGGTextRenderer::_PrepareUnicodeBuffer(const char* utf8String,
uint32 length, uint32* glyphCount)
{
if (length == 0) {
*glyphCount = 0;
return B_OK;
}
int32 srcLength = length;
int32 dstLength = srcLength * 4;
// take care of adjusting buffer size
if (dstLength > fUnicodeBufferSize) {
fUnicodeBufferSize = dstLength;
fUnicodeBuffer = (char*)realloc((void*)fUnicodeBuffer, fUnicodeBufferSize);
}
status_t ret;
if (!fUnicodeBuffer) {
ret = B_NO_MEMORY;
} else {
int32 state = 0;
ret = convert_from_utf8(B_UNICODE_CONVERSION,
utf8String, &srcLength,
fUnicodeBuffer, &dstLength,
&state, B_SUBSTITUTE);
}
if (ret >= B_OK) {
*glyphCount = (uint32)(dstLength / 2);
ret = swap_data(B_INT16_TYPE, fUnicodeBuffer, dstLength,
B_SWAP_BENDIAN_TO_HOST);
} else {
*glyphCount = 0;
fprintf(stderr, "AGGTextRenderer::_PrepareUnicodeBuffer() - UTF8 -> Unicode conversion failed: %s\n", strerror(ret));
}
return ret;
}
// _UpdateSizeAndHinting // _UpdateSizeAndHinting
void void
AGGTextRenderer::_UpdateSizeAndHinting(float size, bool hinted, bool force) AGGTextRenderer::_UpdateSizeAndHinting(float size, bool hinted, bool force)
@@ -48,9 +48,6 @@ class AGGTextRenderer {
uint32 length); uint32 length);
private: private:
status_t _PrepareUnicodeBuffer(const char* utf8String,
uint32 length,
uint32* glyphCount);
void _UpdateSizeAndHinting(float size, bool hinted, void _UpdateSizeAndHinting(float size, bool hinted,
bool force = false); bool force = false);