Font rendering code should be cleaner, maybe a little more efficient and better documented. Rotated text is supported again.

git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@12748 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Stephan Aßmus 2005-05-20 23:26:15 +00:00
parent e980fe005f
commit 3f513bbafe
4 changed files with 128 additions and 96 deletions

View File

@ -74,15 +74,12 @@ class Painter {
void SetPattern(const pattern& p);
void SetPenLocation(const BPoint& location);
void SetFont(const BFont& font);
void SetFont(const ServerFont& font);
// BView API compatibility (for easier testing)
void Sync() {}
inline void MovePenTo(const BPoint& location)
{ SetPenLocation(location); }
inline void SetFont(const BFont* font)
{ if (font) SetFont(*font); }
// painting functions
@ -310,6 +307,8 @@ class Painter {
// by Family and Style
AGGTextRenderer* fTextRenderer;
uint32 fLastFamilyAndStyle;
float fLastRotation;
float fLastShear;
};
// SetHighColor

View File

@ -569,7 +569,7 @@ void ServerWindow::DispatchMessage(int32 code, LinkMsgReader &link)
{
DTRACE(("ServerWindow %s: Message AS_LAYER_SET_STATE: Layer name: %s\n", fName, cl->fName->String()));
// SetLayerState(cl);
SetLayerState(cl,link);
SetLayerState(cl, link);
// TODO: should this be moved into SetLayerState?
// If it _always_ needs to be done afterwards, then yes!
cl->RebuildFullRegion();
@ -579,7 +579,7 @@ void ServerWindow::DispatchMessage(int32 code, LinkMsgReader &link)
{
DTRACE(("ServerWindow %s: Message AS_LAYER_SET_FONT_STATE: Layer name: %s\n", fName, cl->fName->String()));
// SetLayerFontState(cl);
SetLayerFontState(cl,link);
SetLayerFontState(cl, link);
cl->RebuildFullRegion();
break;
}

View File

@ -66,7 +66,9 @@ Painter::Painter()
fDrawingModeFactory(new DrawingModeFactory()),
fPatternHandler(new PatternHandler()),
fTextRenderer(new AGGTextRenderer()),
fLastFamilyAndStyle(0)
fLastFamilyAndStyle(0),
fLastRotation(0.0),
fLastShear(90.0)
{
if (fontserver && fontserver->GetSystemPlain())
fFont = *fontserver->GetSystemPlain();
@ -257,19 +259,6 @@ Painter::SetPenLocation(const BPoint& location)
fPenLocation = location;
}
// SetFont
void
Painter::SetFont(const BFont& font)
{
//fFont.SetFamilyAndStyle(font.GetFamily(), font.GetStyle());
fFont.SetSpacing(font.Spacing());
fFont.SetShear(font.Shear());
fFont.SetRotation(font.Rotation());
fFont.SetSize(font.Size());
_UpdateFont();
}
// SetFont
void
Painter::SetFont(const ServerFont& font)
@ -1082,16 +1071,20 @@ Painter::_UpdateFont()
if (fFont.InitCheck() < B_OK)
return;
if (fLastFamilyAndStyle != fFont.GetFamilyAndStyle()) {
fLastFamilyAndStyle = fFont.GetFamilyAndStyle();
bool success = false;
success = fTextRenderer->SetFont(fFont);
if (!success)
fprintf(stderr, "unable to set font\n");
if (fLastFamilyAndStyle != fFont.GetFamilyAndStyle()
|| fFont.Rotation() != fLastRotation || fFont.Shear() != fLastShear) {
if (fTextRenderer->SetFont(fFont)) {
fLastFamilyAndStyle = fFont.GetFamilyAndStyle();
fLastRotation = fFont.Rotation();
fLastShear = fFont.Shear();
} else {
fprintf(stderr, "unable to set font\n");
}
} else {
// just update the size
fTextRenderer->SetPointSize(fFont.Size());
}
fTextRenderer->SetPointSize(fFont.Size());
}
// _UpdateLineWidth
@ -1468,7 +1461,6 @@ Painter::_StrokePath(VertexSource& path) const
agg::conv_stroke<VertexSource> stroke(path);
stroke.line_join(agg::round_join);
stroke.width(fPenSize);
// stroke.approximation_scale(fPenSize / 2);
fRasterizer->add_path(stroke);
agg::render_scanlines(*fRasterizer, *fScanline, *fRenderer);
@ -1480,7 +1472,7 @@ Painter::_StrokePath(VertexSource& path) const
#endif
BRect touched = _BoundingBox(path);
float penSize = ceilf(fPenSize);
float penSize = ceilf(fPenSize / 2.0);
touched.InsetBy(-penSize, -penSize);
return _Clipped(touched);

View File

@ -23,6 +23,13 @@
#define FLIP_Y false
#define SHOW_GLYPH_BOUNDS 0
#if SHOW_GLYPH_BOUNDS
# include <agg_conv_stroke.h>
# include <agg_path_storage.h>
#endif
// rect_to_int
inline void
rect_to_int(BRect r,
@ -68,20 +75,23 @@ bool
AGGTextRenderer::SetFont(const ServerFont &font)
{
bool success = false;
if (font.Rotation() == 0.0 && font.Shear() == 90.0)
if (font.Rotation() == 0.0 && font.Shear() == 90.0) {
//printf("AGGTextRenderer::SetFont() - native\n");
success = fFontEngine.load_font(font, agg::glyph_ren_native_gray8);
else
fFontEngine.hinting(fHinted);
} else {
//printf("AGGTextRenderer::SetFont() - outline\n");
success = fFontEngine.load_font(font, agg::glyph_ren_outline);
fFontEngine.hinting(false);
}
SetPointSize(font.Size());
if (!success) {
fprintf(stderr, "font could not be loaded\n");
return false;
}
fFontEngine.hinting(fHinted);
SetPointSize(font.Size());
return true;
}
@ -132,6 +142,9 @@ AGGTextRenderer::RenderString(const char* string,
{
//printf("RenderString(\"%s\", length: %ld, dry: %d)\n", string, length, dryRun);
// "bounds" will track the bounding box arround all glyphs that are actually drawn
// it will be calculated in untransformed coordinates within the loop and then
// it is transformed to the real location at the exit of the function.
BRect bounds(0.0, 0.0, -1.0, -1.0);
uint32 glyphCount;
@ -143,7 +156,7 @@ AGGTextRenderer::RenderString(const char* string,
// (only if glyph->data_type == agg::glyph_data_outline)
// in the pipeline for the rasterizer
typedef agg::conv_transform<conv_font_curve_type, agg::trans_affine> conv_font_trans_type;
conv_font_trans_type ftrans(fCurves, transform);
conv_font_trans_type transformedOutline(fCurves, transform);
uint16* p = (uint16*)fUnicodeBuffer;
@ -182,69 +195,98 @@ AGGTextRenderer::RenderString(const char* string,
x += advanceX;
y += advanceY;
// calculate bounds
// "glyphBounds" is the bounds of the glyph transformed
// by the x y location of the glyph along the base line,
// it is therefor yet "untransformed".
const agg::rect& r = glyph->bounds;
BRect glyphBounds(r.x1 + x, r.y1 + y, r.x2 + x, r.y2 + y);
// init the fontmanager and transform glyph bounds
if (glyph->data_type != agg::glyph_data_outline) {
// we cannot use the transformation pipeline
double transformedX = x + transformOffset.x;
double transformedY = y + transformOffset.y;
fFontManager.init_embedded_adaptors(glyph,
transformedX,
transformedY);
glyphBounds.OffsetBy(transformOffset);
} else {
fFontManager.init_embedded_adaptors(glyph, x, y);
glyphBounds = transform.TransformBounds(glyphBounds);
}
// render glyph and update touched area
if (!dryRun && clippingFrame.Intersects(glyphBounds)) {
switch (glyph->data_type) {
case agg::glyph_data_mono:
agg::render_scanlines(fFontManager.mono_adaptor(),
fFontManager.mono_scanline(),
*binRenderer);
break;
case agg::glyph_data_gray8:
agg::render_scanlines(fFontManager.gray8_adaptor(),
fFontManager.gray8_scanline(),
*solidRenderer);
break;
case agg::glyph_data_outline:
fRasterizer.reset();
// NOTE: this function can be easily extended to handle
// conversion to contours, so that's why there is a lot of
// commented out code, I leave here because I think it
// will be needed again.
// if(fabs(0.0) <= 0.01) {
// For the sake of efficiency skip the
// contour converter if the weight is about zero.
//-----------------------
// fRasterizer.add_path(fCurves);
fRasterizer.add_path(ftrans);
/* } else {
// fRasterizer.add_path(fContour);
fRasterizer.add_path(ftrans);
}*/
if (fAntialias) {
agg::render_scanlines(fRasterizer, fScanline, *solidRenderer);
} else {
agg::render_scanlines(fRasterizer, fScanline, *binRenderer);
}
break;
default:
break;
}
}
// track bounding box
if (glyphBounds.IsValid())
bounds = bounds.IsValid() ? bounds | glyphBounds : glyphBounds;
// render the glyph if this is not a dry run
if (!dryRun) {
// init the fontmanager's embedded adaptors
// NOTE: The initialization for the "location" of
// the glyph is different depending on wether we
// deal with non-(rotated/sheared) text, in which
// case we have a native FT bitmap. For rotated or
// sheared text, we use AGG vector outlines and
// a transformation pipeline, which will be applied
// _after_ we retrieve the outline, and that's why
// we simply pass x and y, which are untransformed.
// "glyphBounds" is now transformed into screen coords
// in order to stop drawing when we are already outside
// of the clipping frame
if (glyph->data_type != agg::glyph_data_outline) {
// we cannot use the transformation pipeline
double transformedX = x + transformOffset.x;
double transformedY = y + transformOffset.y;
fFontManager.init_embedded_adaptors(glyph,
transformedX,
transformedY);
glyphBounds.OffsetBy(transformOffset);
} else {
fFontManager.init_embedded_adaptors(glyph, x, y);
glyphBounds = transform.TransformBounds(glyphBounds);
}
if (clippingFrame.Intersects(glyphBounds)) {
switch (glyph->data_type) {
case agg::glyph_data_mono:
agg::render_scanlines(fFontManager.mono_adaptor(),
fFontManager.mono_scanline(),
*binRenderer);
break;
case agg::glyph_data_gray8:
agg::render_scanlines(fFontManager.gray8_adaptor(),
fFontManager.gray8_scanline(),
*solidRenderer);
break;
case agg::glyph_data_outline: {
fRasterizer.reset();
// NOTE: this function can be easily extended to handle
// conversion to contours, so that's why there is a lot of
// commented out code, I leave it here because I think it
// will be needed again.
// if(fabs(0.0) <= 0.01) {
// For the sake of efficiency skip the
// contour converter if the weight is about zero.
//-----------------------
fRasterizer.add_path(transformedOutline);
#if SHOW_GLYPH_BOUNDS
agg::path_storage p;
p.move_to(glyphBounds.left + 0.5, glyphBounds.top + 0.5);
p.line_to(glyphBounds.right + 0.5, glyphBounds.top + 0.5);
p.line_to(glyphBounds.right + 0.5, glyphBounds.bottom + 0.5);
p.line_to(glyphBounds.left + 0.5, glyphBounds.bottom + 0.5);
p.close_polygon();
agg::conv_stroke<agg::path_storage> ps(p);
ps.width(1.0);
fRasterizer.add_path(ps);
#endif
/* } else {
// fRasterizer.add_path(fContour);
fRasterizer.add_path(transformedOutline);
}*/
if (fAntialias) {
agg::render_scanlines(fRasterizer, fScanline, *solidRenderer);
} else {
agg::render_scanlines(fRasterizer, fScanline, *binRenderer);
}
break;
}
default:
break;
}
}
}
// increment pen position
advanceX = glyph->advance_x;
advanceY = glyph->advance_y;
@ -259,8 +301,7 @@ AGGTextRenderer::RenderString(const char* string,
}
}
// return transform.TransformBounds(bounds);
return bounds;
return transform.TransformBounds(bounds);
}
// StringWidth