From 3f513bbafe2c20df2e51b756e437dcd21ef30380 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20A=C3=9Fmus?= Date: Fri, 20 May 2005 23:26:15 +0000 Subject: [PATCH] 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 --- headers/private/servers/app/Painter.h | 5 +- src/servers/app/ServerWindow.cpp | 4 +- src/servers/app/drawing/Painter/Painter.cpp | 42 ++--- .../Painter/font_support/AGGTextRenderer.cpp | 173 +++++++++++------- 4 files changed, 128 insertions(+), 96 deletions(-) diff --git a/headers/private/servers/app/Painter.h b/headers/private/servers/app/Painter.h index eca4176e31..4c3aa21987 100644 --- a/headers/private/servers/app/Painter.h +++ b/headers/private/servers/app/Painter.h @@ -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 diff --git a/src/servers/app/ServerWindow.cpp b/src/servers/app/ServerWindow.cpp index f9a25ba2c5..0e7daf6202 100644 --- a/src/servers/app/ServerWindow.cpp +++ b/src/servers/app/ServerWindow.cpp @@ -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; } diff --git a/src/servers/app/drawing/Painter/Painter.cpp b/src/servers/app/drawing/Painter/Painter.cpp index 84a334d8b0..2905008e6c 100644 --- a/src/servers/app/drawing/Painter/Painter.cpp +++ b/src/servers/app/drawing/Painter/Painter.cpp @@ -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 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); diff --git a/src/servers/app/drawing/Painter/font_support/AGGTextRenderer.cpp b/src/servers/app/drawing/Painter/font_support/AGGTextRenderer.cpp index d5d3001818..142a9bba51 100644 --- a/src/servers/app/drawing/Painter/font_support/AGGTextRenderer.cpp +++ b/src/servers/app/drawing/Painter/font_support/AGGTextRenderer.cpp @@ -23,6 +23,13 @@ #define FLIP_Y false +#define SHOW_GLYPH_BOUNDS 0 + +#if SHOW_GLYPH_BOUNDS +# include +# include +#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_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 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