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
+2 -3
View File
@@ -74,15 +74,12 @@ class Painter {
void SetPattern(const pattern& p); void SetPattern(const pattern& p);
void SetPenLocation(const BPoint& location); void SetPenLocation(const BPoint& location);
void SetFont(const BFont& font);
void SetFont(const ServerFont& font); void SetFont(const ServerFont& font);
// BView API compatibility (for easier testing) // BView API compatibility (for easier testing)
void Sync() {} void Sync() {}
inline void MovePenTo(const BPoint& location) inline void MovePenTo(const BPoint& location)
{ SetPenLocation(location); } { SetPenLocation(location); }
inline void SetFont(const BFont* font)
{ if (font) SetFont(*font); }
// painting functions // painting functions
@@ -310,6 +307,8 @@ class Painter {
// by Family and Style // by Family and Style
AGGTextRenderer* fTextRenderer; AGGTextRenderer* fTextRenderer;
uint32 fLastFamilyAndStyle; uint32 fLastFamilyAndStyle;
float fLastRotation;
float fLastShear;
}; };
// SetHighColor // SetHighColor
+2 -2
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())); DTRACE(("ServerWindow %s: Message AS_LAYER_SET_STATE: Layer name: %s\n", fName, cl->fName->String()));
// SetLayerState(cl); // SetLayerState(cl);
SetLayerState(cl,link); SetLayerState(cl, link);
// TODO: should this be moved into SetLayerState? // TODO: should this be moved into SetLayerState?
// If it _always_ needs to be done afterwards, then yes! // If it _always_ needs to be done afterwards, then yes!
cl->RebuildFullRegion(); 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())); DTRACE(("ServerWindow %s: Message AS_LAYER_SET_FONT_STATE: Layer name: %s\n", fName, cl->fName->String()));
// SetLayerFontState(cl); // SetLayerFontState(cl);
SetLayerFontState(cl,link); SetLayerFontState(cl, link);
cl->RebuildFullRegion(); cl->RebuildFullRegion();
break; break;
} }
+17 -25
View File
@@ -66,7 +66,9 @@ Painter::Painter()
fDrawingModeFactory(new DrawingModeFactory()), fDrawingModeFactory(new DrawingModeFactory()),
fPatternHandler(new PatternHandler()), fPatternHandler(new PatternHandler()),
fTextRenderer(new AGGTextRenderer()), fTextRenderer(new AGGTextRenderer()),
fLastFamilyAndStyle(0) fLastFamilyAndStyle(0),
fLastRotation(0.0),
fLastShear(90.0)
{ {
if (fontserver && fontserver->GetSystemPlain()) if (fontserver && fontserver->GetSystemPlain())
fFont = *fontserver->GetSystemPlain(); fFont = *fontserver->GetSystemPlain();
@@ -257,19 +259,6 @@ Painter::SetPenLocation(const BPoint& location)
fPenLocation = 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 // SetFont
void void
Painter::SetFont(const ServerFont& font) Painter::SetFont(const ServerFont& font)
@@ -1082,16 +1071,20 @@ Painter::_UpdateFont()
if (fFont.InitCheck() < B_OK) if (fFont.InitCheck() < B_OK)
return; return;
if (fLastFamilyAndStyle != fFont.GetFamilyAndStyle()) { if (fLastFamilyAndStyle != fFont.GetFamilyAndStyle()
fLastFamilyAndStyle = fFont.GetFamilyAndStyle(); || fFont.Rotation() != fLastRotation || fFont.Shear() != fLastShear) {
bool success = false; if (fTextRenderer->SetFont(fFont)) {
success = fTextRenderer->SetFont(fFont); fLastFamilyAndStyle = fFont.GetFamilyAndStyle();
if (!success) fLastRotation = fFont.Rotation();
fprintf(stderr, "unable to set font\n"); 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 // _UpdateLineWidth
@@ -1468,7 +1461,6 @@ Painter::_StrokePath(VertexSource& path) const
agg::conv_stroke<VertexSource> stroke(path); agg::conv_stroke<VertexSource> stroke(path);
stroke.line_join(agg::round_join); stroke.line_join(agg::round_join);
stroke.width(fPenSize); stroke.width(fPenSize);
// stroke.approximation_scale(fPenSize / 2);
fRasterizer->add_path(stroke); fRasterizer->add_path(stroke);
agg::render_scanlines(*fRasterizer, *fScanline, *fRenderer); agg::render_scanlines(*fRasterizer, *fScanline, *fRenderer);
@@ -1480,7 +1472,7 @@ Painter::_StrokePath(VertexSource& path) const
#endif #endif
BRect touched = _BoundingBox(path); BRect touched = _BoundingBox(path);
float penSize = ceilf(fPenSize); float penSize = ceilf(fPenSize / 2.0);
touched.InsetBy(-penSize, -penSize); touched.InsetBy(-penSize, -penSize);
return _Clipped(touched); return _Clipped(touched);
@@ -23,6 +23,13 @@
#define FLIP_Y false #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 // rect_to_int
inline void inline void
rect_to_int(BRect r, rect_to_int(BRect r,
@@ -68,20 +75,23 @@ bool
AGGTextRenderer::SetFont(const ServerFont &font) AGGTextRenderer::SetFont(const ServerFont &font)
{ {
bool success = false; 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); 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); success = fFontEngine.load_font(font, agg::glyph_ren_outline);
fFontEngine.hinting(false);
}
SetPointSize(font.Size());
if (!success) { if (!success) {
fprintf(stderr, "font could not be loaded\n"); fprintf(stderr, "font could not be loaded\n");
return false; return false;
} }
fFontEngine.hinting(fHinted);
SetPointSize(font.Size());
return true; return true;
} }
@@ -132,6 +142,9 @@ AGGTextRenderer::RenderString(const char* string,
{ {
//printf("RenderString(\"%s\", length: %ld, dry: %d)\n", string, length, dryRun); //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); BRect bounds(0.0, 0.0, -1.0, -1.0);
uint32 glyphCount; uint32 glyphCount;
@@ -143,7 +156,7 @@ AGGTextRenderer::RenderString(const char* string,
// (only if glyph->data_type == agg::glyph_data_outline) // (only if glyph->data_type == agg::glyph_data_outline)
// in the pipeline for the rasterizer // in the pipeline for the rasterizer
typedef agg::conv_transform<conv_font_curve_type, agg::trans_affine> conv_font_trans_type; 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; uint16* p = (uint16*)fUnicodeBuffer;
@@ -182,69 +195,98 @@ AGGTextRenderer::RenderString(const char* string,
x += advanceX; x += advanceX;
y += advanceY; 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; const agg::rect& r = glyph->bounds;
BRect glyphBounds(r.x1 + x, r.y1 + y, r.x2 + x, r.y2 + y); BRect glyphBounds(r.x1 + x, r.y1 + y, r.x2 + x, r.y2 + y);
// init the fontmanager and transform glyph bounds // track bounding box
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;
}
}
if (glyphBounds.IsValid()) if (glyphBounds.IsValid())
bounds = bounds.IsValid() ? bounds | glyphBounds : glyphBounds; 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 // increment pen position
advanceX = glyph->advance_x; advanceX = glyph->advance_x;
advanceY = glyph->advance_y; advanceY = glyph->advance_y;
@@ -259,8 +301,7 @@ AGGTextRenderer::RenderString(const char* string,
} }
} }
// return transform.TransformBounds(bounds); return transform.TransformBounds(bounds);
return bounds;
} }
// StringWidth // StringWidth