From 55d6f1b792f86ba5529b5ec311e84a1bfd6e15ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20A=C3=9Fmus?= Date: Wed, 25 May 2005 18:20:45 +0000 Subject: [PATCH] Many efficiency improvements to text rendering. Moved stuff from Painter into AGGTextRenderer which didn't belong in Painter. AGGTextRenderer now has an embedded transformation, which expresses the font rotation and (in future) shear settings. Removed direct support for BBitmaps from Painter (supposed to draw ServerBitmaps). Tested drawing of bitmaps other than B_RGB32. (only B_CMAP8 and B_GRAY8 so far, but they work). Right now, these colorspaces are supported by on the fly conversion. So every colorspace supported by BBitmap::ImportBits() should work, which are a lot more than the R5 app_server can display. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@12815 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- headers/private/servers/app/Painter.h | 9 +- src/servers/app/Desktop.cpp | 2 + src/servers/app/ServerWindow.cpp | 8 +- .../app/drawing/DisplayDriverPainter.cpp | 27 +++-- src/servers/app/drawing/Painter/Painter.cpp | 112 +++++++----------- .../app/drawing/Painter/agg_renderer_region.h | 26 ++-- .../Painter/font_support/AGGTextRenderer.cpp | 92 +++++++++----- .../Painter/font_support/AGGTextRenderer.h | 20 +++- 8 files changed, 153 insertions(+), 143 deletions(-) diff --git a/headers/private/servers/app/Painter.h b/headers/private/servers/app/Painter.h index 4c3aa21987..1961833b19 100644 --- a/headers/private/servers/app/Painter.h +++ b/headers/private/servers/app/Painter.h @@ -193,11 +193,7 @@ class Painter { const escapement_delta* delta = NULL); // bitmaps - void DrawBitmap( const BBitmap* bitmap, - BRect bitmapRect, - BRect viewRect) const; - - void DrawBitmap( const ServerBitmap* bitmap, + BRect DrawBitmap( const ServerBitmap* bitmap, BRect bitmapRect, BRect viewRect) const; @@ -306,9 +302,6 @@ class Painter { // font file, it uses the FontManager to locate a file // by Family and Style AGGTextRenderer* fTextRenderer; - uint32 fLastFamilyAndStyle; - float fLastRotation; - float fLastShear; }; // SetHighColor diff --git a/src/servers/app/Desktop.cpp b/src/servers/app/Desktop.cpp index 8290a0874b..174497ff02 100644 --- a/src/servers/app/Desktop.cpp +++ b/src/servers/app/Desktop.cpp @@ -104,6 +104,8 @@ Desktop::AddDriver(DisplayDriver *driver) if (driver->Initialize()) { // TODO: be careful of screen initialization - monitor may not support 640x480 Screen *sc = new Screen(driver, BPoint(640, 480), B_RGB32, fScreenList.CountItems()+1); + +// Screen *sc = new Screen(driver, BPoint(1400, 1050), B_RGB32, fScreenList.CountItems()+1); // Screen *sc = new Screen(driver, BPoint(640, 480), B_CMAP8, fScreenList.CountItems()+1); // Screen *sc = new Screen(driver, BPoint(640, 480), B_GRAY8, fScreenList.CountItems()+1); // Screen *sc = new Screen(driver, BPoint(640, 480), B_RGB15, fScreenList.CountItems()+1); diff --git a/src/servers/app/ServerWindow.cpp b/src/servers/app/ServerWindow.cpp index c76262e76e..068b5fe206 100644 --- a/src/servers/app/ServerWindow.cpp +++ b/src/servers/app/ServerWindow.cpp @@ -2020,7 +2020,6 @@ void ServerWindow::DispatchGraphicsMessage(int32 code, LinkMsgReader &link) case AS_DRAW_STRING: { DTRACE(("ServerWindow %s: Message AS_DRAW_STRING\n",fName)); - char *string; int32 length; BPoint location; @@ -2031,9 +2030,10 @@ void ServerWindow::DispatchGraphicsMessage(int32 code, LinkMsgReader &link) link.Read(&delta); link.ReadString(&string); - if(cl && cl->fLayerData) - desktop->GetDisplayDriver()->DrawString(string,length,cl->ConvertToTop(location), - cl->fLayerData); + if (cl && cl->fLayerData) + desktop->GetDisplayDriver()->DrawString(string, length, + cl->ConvertToTop(location), + cl->fLayerData); free(string); break; diff --git a/src/servers/app/drawing/DisplayDriverPainter.cpp b/src/servers/app/drawing/DisplayDriverPainter.cpp index a7698e7e65..47a1e863dc 100644 --- a/src/servers/app/drawing/DisplayDriverPainter.cpp +++ b/src/servers/app/drawing/DisplayDriverPainter.cpp @@ -387,9 +387,9 @@ DisplayDriverPainter::DrawBitmap(ServerBitmap *bitmap, fGraphicsCard->HideSoftwareCursor(clipped); fPainter->SetDrawData(d); - fPainter->DrawBitmap(bitmap, source, dest); + BRect touched = fPainter->DrawBitmap(bitmap, source, dest); - fGraphicsCard->Invalidate(clipped); + fGraphicsCard->Invalidate(touched); fGraphicsCard->ShowSoftwareCursor(); Unlock(); @@ -969,7 +969,8 @@ DisplayDriverPainter::DrawString(const char *string, const int32 &length, { static DrawData d; d.SetHighColor(color); - + + // TODO: Why is escapement_delta a part of the state stack? if (delta) d.SetEscapementDelta(*delta); @@ -985,21 +986,23 @@ DisplayDriverPainter::DrawString(const char *string, const int32 &length, fPainter->SetDrawData(d); //bigtime_t now = system_time(); // TODO: BoundingBox is quite slow!! Optimizing it will be beneficial. -// Cursiously, the actual DrawString after it is actually faster!?! +// Cursiously, the DrawString after it is actually faster!?! // TODO: make the availability of the hardware cursor part of the // HW acceleration flags and skip all calculations for HideSoftwareCursor -// in case we don't need one. +// in case we don't have one. BRect b = fPainter->BoundingBox(string, length, pt); + // stop here if we're supposed to render outside of the clipping + if (fPainter->ClippingRegion()->Frame().Intersects(b)) { //printf("bounding box '%s': %lld µs\n", string, system_time() - now); - fGraphicsCard->HideSoftwareCursor(b); - + fGraphicsCard->HideSoftwareCursor(b); + //now = system_time(); - BRect touched = fPainter->DrawString(string, length, pt); + BRect touched = fPainter->DrawString(string, length, pt); //printf("drawing string: %lld µs\n", system_time() - now); - - fGraphicsCard->Invalidate(touched); - fGraphicsCard->ShowSoftwareCursor(); - + + fGraphicsCard->Invalidate(touched); + fGraphicsCard->ShowSoftwareCursor(); + } Unlock(); } } diff --git a/src/servers/app/drawing/Painter/Painter.cpp b/src/servers/app/drawing/Painter/Painter.cpp index 36d1d5363e..f0088c9572 100644 --- a/src/servers/app/drawing/Painter/Painter.cpp +++ b/src/servers/app/drawing/Painter/Painter.cpp @@ -80,10 +80,7 @@ Painter::Painter() fPenLocation(0.0, 0.0), fDrawingModeFactory(new DrawingModeFactory()), fPatternHandler(new PatternHandler()), - fTextRenderer(new AGGTextRenderer()), - fLastFamilyAndStyle(0), - fLastRotation(0.0), - fLastShear(90.0) + fTextRenderer(new AGGTextRenderer()) { if (fontserver && fontserver->GetSystemPlain()) fFont = *fontserver->GetSystemPlain(); @@ -883,25 +880,14 @@ Painter::DrawString(const char* utf8String, uint32 length, if (fBuffer) { - Transformable transform; -// TODO: convert BFont::Shear(), which is in degrees 45°...135° to whatever AGG is using -// transform.ShearBy(B_ORIGIN, fFont.Shear(), 0.0); - transform.RotateBy(B_ORIGIN, -fFont.Rotation()); - transform.TranslateBy(baseLine); - - BRect clippingFrame; - if (fClippingRegion) - clippingFrame = fClippingRegion->Frame(); - bounds = fTextRenderer->RenderString(utf8String, length, fFontRendererSolid, fFontRendererBin, - transform, - clippingFrame, + baseLine, + fClippingRegion->Frame(), false, &fPenLocation); - transform.Transform(&fPenLocation); } return _Clipped(bounds); } @@ -926,31 +912,15 @@ Painter::DrawString(const char* utf8String, BPoint baseLine, // #pragma mark - // DrawBitmap -void -Painter::DrawBitmap(const BBitmap* bitmap, - BRect bitmapRect, BRect viewRect) const -{ - if (bitmap && bitmap->IsValid()) { - // the native bitmap coordinate system - // (can have left top corner offset) - BRect actualBitmapRect(bitmap->Bounds()); - - agg::rendering_buffer srcBuffer; - srcBuffer.attach((uint8*)bitmap->Bits(), - (uint32)actualBitmapRect.IntegerWidth() + 1, - (uint32)actualBitmapRect.IntegerHeight() + 1, - bitmap->BytesPerRow()); - - _DrawBitmap(srcBuffer, bitmap->ColorSpace(), actualBitmapRect, bitmapRect, viewRect); - } -} - -// DrawBitmap -void +BRect Painter::DrawBitmap(const ServerBitmap* bitmap, BRect bitmapRect, BRect viewRect) const { - if (bitmap && bitmap->InitCheck()) { + CHECK_CLIPPING + + BRect touched = _Clipped(viewRect); + + if (bitmap && bitmap->InitCheck() && touched.IsValid()) { // the native bitmap coordinate system BRect actualBitmapRect(bitmap->Bounds()); @@ -962,6 +932,7 @@ Painter::DrawBitmap(const ServerBitmap* bitmap, _DrawBitmap(srcBuffer, bitmap->ColorSpace(), actualBitmapRect, bitmapRect, viewRect); } + return touched; } // #pragma mark - @@ -1004,15 +975,12 @@ BRect Painter::BoundingBox(const char* utf8String, uint32 length, const BPoint& baseLine) const { - Transformable transform; - transform.TranslateBy(baseLine); - static BRect dummy; return fTextRenderer->RenderString(utf8String, length, fFontRendererSolid, fFontRendererBin, - transform, dummy, true); + baseLine, dummy, true); } // StringWidth @@ -1106,20 +1074,7 @@ Painter::_UpdateFont() if (fFont.InitCheck() < B_OK) return; - 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->SetFont(fFont); } // _UpdateLineWidth @@ -1176,7 +1131,6 @@ Painter::_UpdateDrawingMode() void Painter::_SetRendererColor(const rgb_color& color) const { - if (fOutlineRenderer) #if ALIASED_DRAWING fOutlineRenderer->line_color(agg::rgba(color.red / 255.0, @@ -1340,14 +1294,13 @@ Painter::_DrawBitmap(const agg::rendering_buffer& srcBuffer, color_space format, _DrawBitmap32(srcBuffer, actualBitmapRect, bitmapRect, viewRect); break; default: -fprintf(stderr, "Painter::_DrawBitmap() - non-native colorspace: %d\n", format); -#ifdef __HAIKU__ +//printf("Painter::_DrawBitmap() - non-native colorspace: %d\n", format); // TODO: this is only a temporary implementation, // to really handle other colorspaces, one would // rather do the conversion with much less overhead, // for example in the nn filter (hm), or in the - // scanline generator - BBitmap temp(actualBitmapRect, 0, B_RGB32); + // scanline generator (better) + BBitmap temp(actualBitmapRect, B_BITMAP_NO_SERVER_LINK, B_RGB32); status_t err = temp.ImportBits(srcBuffer.buf(), srcBuffer.height() * srcBuffer.stride(), srcBuffer.stride(), @@ -1360,9 +1313,8 @@ fprintf(stderr, "Painter::_DrawBitmap() - non-native colorspace: %d\n", format); temp.BytesPerRow()); _DrawBitmap32(convertedBuffer, actualBitmapRect, bitmapRect, viewRect); } else { -fprintf(stderr, "Painter::_DrawBitmap() - colorspace conversion failed: %s\n", strerror(err)); +printf("Painter::_DrawBitmap() - colorspace conversion failed: %s\n", strerror(err)); } -#endif // __HAIKU__ break; } } @@ -1373,10 +1325,17 @@ Painter::_DrawBitmap32(const agg::rendering_buffer& srcBuffer, BRect actualBitmapRect, BRect bitmapRect, BRect viewRect) const { typedef agg::span_allocator span_alloc_type; + +// pipeline for non-scaled bitmaps +//typedef agg::span_generator span_gen_type; +//typedef agg::renderer_scanline_aa image_renderer_type; + +// pipeline for scaled bitmaps typedef agg::span_interpolator_linear<> interpolator_type; typedef agg::span_image_filter_rgba32_nn span_gen_type; -typedef agg::renderer_scanline_aa image_renderer_type; + interpolator_type> scaled_span_gen_type; +typedef agg::renderer_scanline_aa scaled_image_renderer_type; + if (bitmapRect.IsValid() && bitmapRect.Intersects(actualBitmapRect) && viewRect.IsValid()) { @@ -1429,10 +1388,6 @@ typedef agg::renderer_scanline_aa image_renderer_t span_alloc_type sa; interpolator_type interpolator(imgMatrix); - span_gen_type sg(sa, srcBuffer, agg::rgba(0, 0, 0, 0), interpolator); - - image_renderer_type ri(*fBaseRenderer, sg); - agg::rasterizer_scanline_aa<> pf; agg::scanline_u8 sl; @@ -1447,7 +1402,22 @@ typedef agg::renderer_scanline_aa image_renderer_t agg::conv_transform tr(path, srcMatrix); pf.add_path(tr); - agg::render_scanlines(pf, sl, ri); + +// if (xScale != 1.0 || yScale != 1.0) { +//printf("scaled\n"); + scaled_span_gen_type sg(sa, srcBuffer, agg::rgba(0, 0, 0, 0), interpolator); + scaled_image_renderer_type ri(*fBaseRenderer, sg); + + agg::render_scanlines(pf, sl, ri); +/* } else { +// TODO: Does not even compile, find out how to construct a pipeline without +// scaling +printf("non scaled scaled\n"); + span_gen_type sg(sa); + image_renderer_type ri(*fBaseRenderer, sg); + + agg::render_scanlines(pf, sl, ri); + }*/ } } diff --git a/src/servers/app/drawing/Painter/agg_renderer_region.h b/src/servers/app/drawing/Painter/agg_renderer_region.h index 205a013636..e398448de3 100644 --- a/src/servers/app/drawing/Painter/agg_renderer_region.h +++ b/src/servers/app/drawing/Painter/agg_renderer_region.h @@ -74,7 +74,7 @@ namespace agg m_curr_cb = 0; if(m_region && m_region->CountRects() > 0) { - BRect cb = m_region->RectAt(0); + clipping_rect cb = m_region->RectAtInt(0); m_ren.clip_box_naked(cb.left, cb.top, cb.right, cb.bottom); } else @@ -86,7 +86,7 @@ namespace agg { if(m_region && (int)(++m_curr_cb) < m_region->CountRects()) { - BRect cb = m_region->RectAt(m_curr_cb); + clipping_rect cb = m_region->RectAtInt(m_curr_cb); m_ren.clip_box_naked(cb.left, cb.top, cb.right, cb.bottom); return true; } @@ -107,16 +107,18 @@ namespace agg { m_region = region; if (m_region) { - BRect r = m_region->Frame(); - if (r.IsValid()) { - r = r & BRect(0, 0, width() - 1, height() - 1); - // a BRegion actually keeps integer clipping_rects - // so just converting to int without rounding is - // fine - if(r.left < m_bounds.x1) m_bounds.x1 = (int)r.left; - if(r.top < m_bounds.y1) m_bounds.y1 = (int)r.top; - if(r.right > m_bounds.x2) m_bounds.x2 = (int)r.right; - if(r.bottom > m_bounds.y2) m_bounds.y2 = (int)r.bottom; + clipping_rect r = m_region->FrameInt(); + if (r.left <= r.right && r.top <= r.bottom) { + // clip rect to frame buffer bounds + r.left = max_c(0, r.left); + r.top = max_c(0, r.top); + r.right = min_c((int)width() - 1, r.right); + r.bottom = min_c((int)height() - 1, r.bottom); + + if(r.left < m_bounds.x1) m_bounds.x1 = r.left; + if(r.top < m_bounds.y1) m_bounds.y1 = r.top; + if(r.right > m_bounds.x2) m_bounds.x2 = r.right; + if(r.bottom > m_bounds.y2) m_bounds.y2 = r.bottom; } } } diff --git a/src/servers/app/drawing/Painter/font_support/AGGTextRenderer.cpp b/src/servers/app/drawing/Painter/font_support/AGGTextRenderer.cpp index ea933fbc08..12c72703f8 100644 --- a/src/servers/app/drawing/Painter/font_support/AGGTextRenderer.cpp +++ b/src/servers/app/drawing/Painter/font_support/AGGTextRenderer.cpp @@ -56,7 +56,12 @@ AGGTextRenderer::AGGTextRenderer() fHinted(true), fAntialias(true), - fKerning(true) + fKerning(true), + fEmbeddedTransformation(), + + fLastFamilyAndStyle(0), + fLastPointSize(-1.0), + fLastHinted(false) { fCurves.approximation_scale(2.0); fContour.auto_detect_orientation(false); @@ -74,18 +79,30 @@ AGGTextRenderer::~AGGTextRenderer() bool AGGTextRenderer::SetFont(const ServerFont &font) { - bool success = false; - if (font.Rotation() == 0.0 && font.Shear() == 90.0) { + bool success = true; + // construct an embedded transformation (rotate & shear) + Transformable transform; +// TODO: convert BFont::Shear(), which is in degrees 45°...135° to whatever AGG is using +// transform.ShearBy(B_ORIGIN, fFont.Shear(), 0.0); + transform.RotateBy(B_ORIGIN, -font.Rotation()); + + bool load = font.GetFamilyAndStyle() != fLastFamilyAndStyle || + transform != fEmbeddedTransformation; + + if (load) { + if (transform.IsIdentity()) { //printf("AGGTextRenderer::SetFont() - native\n"); - success = fFontEngine.load_font(font, agg::glyph_ren_native_gray8); - fFontEngine.hinting(fHinted); - } else { + success = fFontEngine.load_font(font, agg::glyph_ren_native_gray8); + } else { //printf("AGGTextRenderer::SetFont() - outline\n"); - success = fFontEngine.load_font(font, agg::glyph_ren_outline); - fFontEngine.hinting(false); + success = fFontEngine.load_font(font, agg::glyph_ren_outline); + } } - SetPointSize(font.Size()); + fLastFamilyAndStyle = font.GetFamilyAndStyle(); + fEmbeddedTransformation = transform; + + _UpdateSizeAndHinting(font.Size(), fEmbeddedTransformation.IsIdentity() && fHinted, load); if (!success) { fprintf(stderr, "font could not be loaded\n"); @@ -95,23 +112,13 @@ AGGTextRenderer::SetFont(const ServerFont &font) return true; } -// SetPointSize -void -AGGTextRenderer::SetPointSize(float size) -{ - if (size != fFontEngine.height()) { - fFontEngine.height(size); - fFontEngine.width(size); - } -} - // SetHinting void AGGTextRenderer::SetHinting(bool hinting) { if (fHinted != hinting) { fHinted = hinting; - fFontEngine.hinting(fHinted); + _UpdateSizeAndHinting(fLastPointSize, fEmbeddedTransformation.IsIdentity() && fHinted); } } @@ -135,26 +142,23 @@ AGGTextRenderer::RenderString(const char* string, uint32 length, font_renderer_solid_type* solidRenderer, font_renderer_bin_type* binRenderer, - const Transformable& transform, - BRect clippingFrame, + const BPoint& baseLine, + const BRect& clippingFrame, bool dryRun, BPoint* nextCharPos) { //printf("RenderString(\"%s\", length: %ld, dry: %d)\n", string, length, dryRun); - // ToDo: this is a temporary fix for some drawing problems - // Please remove when the real cause has been found :-) - float size = fFontEngine.height(); - fFontEngine.height(size); - fFontEngine.width(size); - fFontEngine.hinting(fHinted); - // "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); + Transformable transform(fEmbeddedTransformation); + transform.TranslateBy(baseLine); + uint32 glyphCount; + if (_PrepareUnicodeBuffer(string, length, &glyphCount) >= B_OK) { fCurves.approximation_scale(transform.scale()); @@ -194,7 +198,6 @@ AGGTextRenderer::RenderString(const char* string, const agg::glyph_cache* glyph = fFontManager.glyph(*p); if (glyph) { - if (i > 0 && fKerning) { fFontManager.add_kerning(&advanceX, &advanceY); } @@ -240,7 +243,7 @@ AGGTextRenderer::RenderString(const char* string, glyphBounds = transform.TransformBounds(glyphBounds); } - if (clippingFrame.Intersects(glyphBounds)) { + if (true /*clippingFrame.Intersects(glyphBounds)*/) { switch (glyph->data_type) { case agg::glyph_data_mono: agg::render_scanlines(fFontManager.mono_adaptor(), @@ -297,6 +300,15 @@ AGGTextRenderer::RenderString(const char* string, // increment pen position advanceX = glyph->advance_x; advanceY = glyph->advance_y; + } else { + if (*p < 128) { + 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; } @@ -305,6 +317,8 @@ AGGTextRenderer::RenderString(const char* string, if (nextCharPos) { nextCharPos->x = x + advanceX; nextCharPos->y = y + advanceY; + + transform.Transform(nextCharPos); } } @@ -371,3 +385,19 @@ AGGTextRenderer::_PrepareUnicodeBuffer(const char* utf8String, return ret; } + +// _UpdateSizeAndHinting +void +AGGTextRenderer::_UpdateSizeAndHinting(float size, bool hinted, bool force) +{ + if (force || size != fLastPointSize || hinted != fLastHinted) { +//printf("AGGTextRenderer::_UpdateSizeAndHinting(%.1f, %d, %d)\n", size, hinted, force); + fLastPointSize = size; + fLastHinted = hinted; + + fFontEngine.height(size); + fFontEngine.width(size); + fFontEngine.hinting(hinted); + } +} + diff --git a/src/servers/app/drawing/Painter/font_support/AGGTextRenderer.h b/src/servers/app/drawing/Painter/font_support/AGGTextRenderer.h index c11b959908..7357a8720c 100644 --- a/src/servers/app/drawing/Painter/font_support/AGGTextRenderer.h +++ b/src/servers/app/drawing/Painter/font_support/AGGTextRenderer.h @@ -22,8 +22,6 @@ class AGGTextRenderer { bool SetFont(const ServerFont &font); void Unset(); - void SetPointSize(float size); - void SetHinting(bool hinting); bool Hinting() const { return fHinted; } @@ -32,12 +30,16 @@ class AGGTextRenderer { bool Antialiasing() const { return fAntialias; } + void SetKerning(bool kerning); + bool Kerning() const + { return fKerning; } + BRect RenderString(const char* utf8String, uint32 length, font_renderer_solid_type* solidRenderer, font_renderer_bin_type* binRenderer, - const Transformable& transform, - BRect clippingFrame, + const BPoint& baseLine, + const BRect& clippingFrame, bool dryRun = false, BPoint* nextCharPos = NULL); @@ -48,6 +50,8 @@ class AGGTextRenderer { status_t _PrepareUnicodeBuffer(const char* utf8String, uint32 length, uint32* glyphCount); + void _UpdateSizeAndHinting(float size, bool hinted, + bool force = false); typedef agg::font_engine_freetype_int32 font_engine_type; @@ -70,8 +74,14 @@ class AGGTextRenderer { int32 fUnicodeBufferSize; bool fHinted; // is glyph hinting active? - bool fAntialias; // is anti-aliasing active? + bool fAntialias; bool fKerning; + Transformable fEmbeddedTransformation; // rotated or sheared font? + + // caching to avoid loading a font unnecessarily + uint32 fLastFamilyAndStyle; + float fLastPointSize; + bool fLastHinted; }; #endif // AGG_TEXT_RENDERER_H