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:
parent
e980fe005f
commit
3f513bbafe
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue