From 77e5acc0d9d737051591e663ccf3376a32bfcf84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20A=C3=9Fmus?= Date: Mon, 15 Mar 2010 13:59:14 +0000 Subject: [PATCH] * Extended the BView drawing API by a DrawString() version that takes an array of locations, one for each glyph. * Added a test for the new functionality. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@35865 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- headers/os/interface/View.h | 10 +-- headers/private/app/ServerProtocol.h | 1 + src/kits/interface/View.cpp | 25 ++++++- src/servers/app/GlyphLayoutEngine.h | 36 ++++++---- src/servers/app/ServerWindow.cpp | 63 ++++++++++++++++- src/servers/app/drawing/DrawingEngine.cpp | 31 ++++++++ src/servers/app/drawing/DrawingEngine.h | 2 + .../app/drawing/Painter/AGGTextRenderer.cpp | 38 +++++++++- .../app/drawing/Painter/AGGTextRenderer.h | 6 ++ src/servers/app/drawing/Painter/Painter.cpp | 42 ++++++++++- src/servers/app/drawing/Painter/Painter.h | 8 +++ src/tests/servers/app/Jamfile | 1 + .../servers/app/draw_string_offsets/Jamfile | 17 +++++ .../servers/app/draw_string_offsets/main.cpp | 70 +++++++++++++++++++ 14 files changed, 329 insertions(+), 21 deletions(-) create mode 100644 src/tests/servers/app/draw_string_offsets/Jamfile create mode 100644 src/tests/servers/app/draw_string_offsets/main.cpp diff --git a/headers/os/interface/View.h b/headers/os/interface/View.h index 40ded6664b..19a0e3e4f8 100644 --- a/headers/os/interface/View.h +++ b/headers/os/interface/View.h @@ -423,16 +423,18 @@ public: void DrawChar(char aChar); void DrawChar(char aChar, BPoint location); - void DrawString(const char* aString, + void DrawString(const char* string, escapement_delta* delta = NULL); - void DrawString(const char* aString, + void DrawString(const char* string, BPoint location, escapement_delta* delta = NULL); - void DrawString(const char* aString, int32 length, + void DrawString(const char* string, int32 length, escapement_delta* delta = NULL); - void DrawString(const char* aString, int32 length, + void DrawString(const char* string, int32 length, BPoint location, escapement_delta* delta = 0L); + void DrawString(const char* string, int32 length, + const BPoint* locations); virtual void SetFont(const BFont* font, uint32 mask = B_FONT_ALL); diff --git a/headers/private/app/ServerProtocol.h b/headers/private/app/ServerProtocol.h index 98d462648e..69becc6a49 100644 --- a/headers/private/app/ServerProtocol.h +++ b/headers/private/app/ServerProtocol.h @@ -254,6 +254,7 @@ enum { AS_DRAW_STRING, AS_DRAW_STRING_WITH_DELTA, + AS_DRAW_STRING_WITH_OFFSETS, AS_SYNC, diff --git a/src/kits/interface/View.cpp b/src/kits/interface/View.cpp index fdee2464d8..b051f8704f 100644 --- a/src/kits/interface/View.cpp +++ b/src/kits/interface/View.cpp @@ -45,6 +45,7 @@ #include #include +#include #include #include #include @@ -59,7 +60,6 @@ #include #include - using std::nothrow; //#define DEBUG_BVIEW @@ -2570,6 +2570,29 @@ BView::DrawString(const char* string, int32 length, BPoint location, } +void +BView::DrawString(const char* string, int32 length, const BPoint* locations) +{ + if (fOwner == NULL || string == NULL || length < 1 || locations == NULL) + return; + + _CheckLockAndSwitchCurrent(); + + fOwner->fLink->StartMessage(AS_DRAW_STRING_WITH_OFFSETS); + + int32 glyphCount = UTF8CountChars(string, length); + fOwner->fLink->Attach(length); + fOwner->fLink->Attach(glyphCount); + fOwner->fLink->Attach(string, length); + fOwner->fLink->Attach(locations, glyphCount * sizeof(BPoint)); + + _FlushIfNotInTransaction(); + + // this modifies our pen location, so we invalidate the flag. + fState->valid_flags &= ~B_VIEW_PEN_LOCATION_BIT; +} + + void BView::StrokeEllipse(BPoint center, float xRadius, float yRadius, ::pattern pattern) diff --git a/src/servers/app/GlyphLayoutEngine.h b/src/servers/app/GlyphLayoutEngine.h index 4852e8236d..41cf9b2444 100644 --- a/src/servers/app/GlyphLayoutEngine.h +++ b/src/servers/app/GlyphLayoutEngine.h @@ -64,6 +64,7 @@ class GlyphLayoutEngine { const escapement_delta* delta = NULL, bool kerning = true, uint8 spacing = B_BITMAP_SPACING, + const BPoint* offsets = NULL, FontCacheReference* cacheReference = NULL); static bool IsWhiteSpace(uint32 glyphCode); @@ -101,7 +102,7 @@ GlyphLayoutEngine::LayoutGlyphs(GlyphConsumer& consumer, const ServerFont& font, const char* utf8String, int32 length, const escapement_delta* delta, bool kerning, uint8 spacing, - FontCacheReference* cacheReference) + const BPoint* offsets, FontCacheReference* cacheReference) { // TODO: implement spacing modes @@ -136,6 +137,10 @@ GlyphLayoutEngine::LayoutGlyphs(GlyphConsumer& consumer, double x = 0.0; double y = 0.0; + if (offsets) { + x = offsets[0].x; + y = offsets[0].y; + } double advanceX = 0.0; double advanceY = 0.0; @@ -146,6 +151,24 @@ GlyphLayoutEngine::LayoutGlyphs(GlyphConsumer& consumer, const char* start = utf8String; while ((charCode = UTF8ToCharCode(&utf8String))) { + if (offsets) { + // Use direct glyph locations instead of calculating them + // from the advance values + x = offsets[index].x; + y = offsets[index].y; + } else { +// TODO: Currently disabled, because it works much too slow (doesn't seem +// to be properly cached in FreeType.) +// if (kerning) +// entry->GetKerning(lastCharCode, charCode, &advanceX, &advanceY); + + x += advanceX; + y += advanceY; + + if (delta) + x += IsWhiteSpace(charCode) ? delta->space : delta->nonspace; + } + const GlyphCache* glyph = entry->Glyph(charCode); if (glyph == NULL) { fprintf(stderr, "failed to load glyph for 0x%04lx (%c)\n", charCode, @@ -155,17 +178,6 @@ GlyphLayoutEngine::LayoutGlyphs(GlyphConsumer& consumer, continue; } -// TODO: Currently disabled, because it works much too slow (doesn't seem -// to be properly cached in FreeType.) -// if (kerning) -// entry->GetKerning(lastCharCode, charCode, &advanceX, &advanceY); - - x += advanceX; - y += advanceY; - - if (delta) - x += IsWhiteSpace(charCode) ? delta->space : delta->nonspace; - if (!consumer.ConsumeGlyph(index, charCode, glyph, entry, x, y)) { advanceX = 0; advanceY = 0; diff --git a/src/servers/app/ServerWindow.cpp b/src/servers/app/ServerWindow.cpp index c0451619ed..4085b66823 100644 --- a/src/servers/app/ServerWindow.cpp +++ b/src/servers/app/ServerWindow.cpp @@ -49,8 +49,10 @@ #include #include "clipping.h" +#include "utf8_functions.h" #include "AppServer.h" +#include "AutoDeleter.h" #include "Desktop.h" #include "DirectWindowInfo.h" #include "DrawingEngine.h" @@ -2674,8 +2676,10 @@ ServerWindow::_DispatchViewDrawingMessage(int32 code, case AS_DRAW_STRING_WITH_DELTA: { ViewDrawStringInfo info; - if (link.Read(&info) != B_OK) + if (link.Read(&info) != B_OK + || info.stringLength <= 0) { break; + } const ssize_t kMaxStackStringSize = 4096; char stackString[kMaxStackStringSize]; @@ -2716,6 +2720,63 @@ ServerWindow::_DispatchViewDrawingMessage(int32 code, free(string); break; } + case AS_DRAW_STRING_WITH_OFFSETS: + { + int32 stringLength; + if (link.Read(&stringLength) != B_OK || stringLength <= 0) + break; + + int32 glyphCount; + if (link.Read(&glyphCount) != B_OK || glyphCount <= 0) + break; + + const ssize_t kMaxStackStringSize = 512; + char stackString[kMaxStackStringSize]; + char* string = stackString; + BPoint stackLocations[kMaxStackStringSize]; + BPoint* locations = stackLocations; + MemoryDeleter stringDeleter; + MemoryDeleter locationsDeleter; + if (stringLength >= kMaxStackStringSize) { + // NOTE: Careful, the + 1 is for termination! + string = (char*)malloc((stringLength + 1 + 63) / 64 * 64); + if (string == NULL) + break; + stringDeleter.SetTo(string); + } + if (glyphCount > kMaxStackStringSize) { + locations = (BPoint*)malloc( + ((glyphCount * sizeof(BPoint)) + 63) / 64 * 64); + if (locations == NULL) + break; + locationsDeleter.SetTo(locations); + } + + if (link.Read(string, stringLength) != B_OK) + break; + // Count UTF8 glyphs and make sure we have enough locations + if ((int32)UTF8CountChars(string, stringLength) > glyphCount) + break; + if (link.Read(locations, glyphCount * sizeof(BPoint)) != B_OK) + break; + // Terminate the string, if nothing else, it's important + // for the DTRACE call below... + string[stringLength] = '\0'; + + DTRACE(("ServerWindow %s: Message AS_DRAW_STRING_WITH_OFFSETS, View: %s " + "-> %s\n", Title(), fCurrentView->Name(), string)); + + for (int32 i = 0; i < stringLength; i++) + fCurrentView->ConvertToScreenForDrawing(&locations[i]); + + BPoint penLocation = drawingEngine->DrawString(string, + stringLength, locations); + + fCurrentView->ConvertFromScreenForDrawing(&penLocation); + fCurrentView->CurrentState()->SetPenLocation(penLocation); + + break; + } case AS_VIEW_DRAW_PICTURE: { diff --git a/src/servers/app/drawing/DrawingEngine.cpp b/src/servers/app/drawing/DrawingEngine.cpp index 3627b98ec5..7f4dd6fc40 100644 --- a/src/servers/app/drawing/DrawingEngine.cpp +++ b/src/servers/app/drawing/DrawingEngine.cpp @@ -1343,6 +1343,37 @@ DrawingEngine::DrawString(const char* string, int32 length, } +BPoint +DrawingEngine::DrawString(const char* string, int32 length, + const BPoint* offsets) +{ + ASSERT_PARALLEL_LOCKED(); + + // use a FontCacheRefernece to speed up the second pass of + // drawing the string + FontCacheReference cacheReference; + + BPoint penLocation; + BRect b = fPainter->BoundingBox(string, length, offsets, &penLocation, + &cacheReference); + // stop here if we're supposed to render outside of the clipping + b = fPainter->ClipRect(b); + if (b.IsValid()) { +//printf("bounding box '%s': %lld µs\n", string, system_time() - now); + AutoFloatingOverlaysHider _(fGraphicsCard, b); + +//now = system_time(); + BRect touched = fPainter->DrawString(string, length, offsets, + &cacheReference); +//printf("drawing string: %lld µs\n", system_time() - now); + + _CopyToFront(touched); + } + + return penLocation; +} + + float DrawingEngine::StringWidth(const char* string, int32 length, escapement_delta* delta) diff --git a/src/servers/app/drawing/DrawingEngine.h b/src/servers/app/drawing/DrawingEngine.h index 4dd95cf2bb..d9aee5e7cb 100644 --- a/src/servers/app/drawing/DrawingEngine.h +++ b/src/servers/app/drawing/DrawingEngine.h @@ -166,6 +166,8 @@ public: virtual BPoint DrawString(const char* string, int32 length, const BPoint& pt, escapement_delta* delta = NULL); + virtual BPoint DrawString(const char* string, int32 length, + const BPoint* offsets); float StringWidth(const char* string, int32 length, escapement_delta* delta = NULL); diff --git a/src/servers/app/drawing/Painter/AGGTextRenderer.cpp b/src/servers/app/drawing/Painter/AGGTextRenderer.cpp index 3341c2b0c7..dec6189f2b 100644 --- a/src/servers/app/drawing/Painter/AGGTextRenderer.cpp +++ b/src/servers/app/drawing/Painter/AGGTextRenderer.cpp @@ -304,7 +304,7 @@ private: AGGTextRenderer& fRenderer; }; -// RenderString + BRect AGGTextRenderer::RenderString(const char* string, uint32 length, const BPoint& baseLine, const BRect& clippingFrame, bool dryRun, @@ -335,7 +335,41 @@ AGGTextRenderer::RenderString(const char* string, uint32 length, transform, transformOffset, nextCharPos, *this); GlyphLayoutEngine::LayoutGlyphs(renderer, fFont, string, length, delta, - fKerning, B_BITMAP_SPACING, cacheReference); + fKerning, B_BITMAP_SPACING, NULL, cacheReference); + + return transform.TransformBounds(renderer.Bounds()); +} + + +BRect +AGGTextRenderer::RenderString(const char* string, uint32 length, + const BPoint* offsets, const BRect& clippingFrame, bool dryRun, + BPoint* nextCharPos, FontCacheReference* cacheReference) +{ +//printf("RenderString(\"%s\", length: %ld, dry: %d)\n", string, length, dryRun); + + Transformable transform(fEmbeddedTransformation); + + fCurves.approximation_scale(transform.scale()); + + // use a transformation behind the curves + // (only if glyph->data_type == agg::glyph_data_outline) + // in the pipeline for the rasterizer + FontCacheEntry::TransformedOutline + transformedOutline(fCurves, transform); + FontCacheEntry::TransformedContourOutline + transformedContourOutline(fContour, transform); + + // for when we bypass the transformation pipeline + BPoint transformOffset(0.0, 0.0); + transform.Transform(&transformOffset); + + StringRenderer renderer(clippingFrame, dryRun, + transformedOutline, transformedContourOutline, + transform, transformOffset, nextCharPos, *this); + + GlyphLayoutEngine::LayoutGlyphs(renderer, fFont, string, length, NULL, + fKerning, B_BITMAP_SPACING, offsets, cacheReference); return transform.TransformBounds(renderer.Bounds()); } diff --git a/src/servers/app/drawing/Painter/AGGTextRenderer.h b/src/servers/app/drawing/Painter/AGGTextRenderer.h index 6b53eb02c9..f9f4644f8e 100644 --- a/src/servers/app/drawing/Painter/AGGTextRenderer.h +++ b/src/servers/app/drawing/Painter/AGGTextRenderer.h @@ -54,6 +54,12 @@ public: const escapement_delta* delta, FontCacheReference* cacheReference); + BRect RenderString(const char* utf8String, + uint32 length, const BPoint* offsets, + const BRect& clippingFrame, bool dryRun, + BPoint* nextCharPos, + FontCacheReference* cacheReference); + private: class StringRenderer; diff --git a/src/servers/app/drawing/Painter/Painter.cpp b/src/servers/app/drawing/Painter/Painter.cpp index 1dfd757bab..0231e5498c 100644 --- a/src/servers/app/drawing/Painter/Painter.cpp +++ b/src/servers/app/drawing/Painter/Painter.cpp @@ -1418,7 +1418,7 @@ Painter::DrawString(const char* utf8String, uint32 length, BPoint baseLine, baseLine.y = roundf(baseLine.y); } - BRect bounds(0.0, 0.0, -1.0, -1.0); + BRect bounds; // text is not rendered with patterns, but we need to // make sure that the previous pattern is restored @@ -1435,6 +1435,32 @@ Painter::DrawString(const char* utf8String, uint32 length, BPoint baseLine, } +// DrawString +BRect +Painter::DrawString(const char* utf8String, uint32 length, + const BPoint* offsets, FontCacheReference* cacheReference) +{ + CHECK_CLIPPING + + // TODO: Round offsets to device pixel grid if !fSubpixelPrecise? + + BRect bounds; + + // text is not rendered with patterns, but we need to + // make sure that the previous pattern is restored + pattern oldPattern = *fPatternHandler.GetR5Pattern(); + SetPattern(B_SOLID_HIGH, true); + + bounds = fTextRenderer.RenderString(utf8String, length, + offsets, fClippingRegion->Frame(), false, NULL, + cacheReference); + + SetPattern(oldPattern); + + return _Clipped(bounds); +} + + // BoundingBox BRect Painter::BoundingBox(const char* utf8String, uint32 length, BPoint baseLine, @@ -1452,6 +1478,20 @@ Painter::BoundingBox(const char* utf8String, uint32 length, BPoint baseLine, } +// BoundingBox +BRect +Painter::BoundingBox(const char* utf8String, uint32 length, + const BPoint* offsets, BPoint* penLocation, + FontCacheReference* cacheReference) const +{ + // TODO: Round offsets to device pixel grid if !fSubpixelPrecise? + + static BRect dummy; + return fTextRenderer.RenderString(utf8String, length, + offsets, dummy, true, penLocation, cacheReference); +} + + // StringWidth float Painter::StringWidth(const char* utf8String, uint32 length, diff --git a/src/servers/app/drawing/Painter/Painter.h b/src/servers/app/drawing/Painter/Painter.h index 99bb61e02d..29638e4ffa 100644 --- a/src/servers/app/drawing/Painter/Painter.h +++ b/src/servers/app/drawing/Painter/Painter.h @@ -200,6 +200,9 @@ class Painter { uint32 length, BPoint baseLine, const escapement_delta* delta, FontCacheReference* cacheReference = NULL); + BRect DrawString(const char* utf8String, + uint32 length, const BPoint* offsets, + FontCacheReference* cacheReference = NULL); BRect BoundingBox(const char* utf8String, uint32 length, BPoint baseLine, @@ -207,6 +210,11 @@ class Painter { const escapement_delta* delta, FontCacheReference* cacheReference = NULL) const; + BRect BoundingBox(const char* utf8String, + uint32 length, const BPoint* offsets, + BPoint* penLocation, + FontCacheReference* cacheReference + = NULL) const; float StringWidth(const char* utf8String, uint32 length, diff --git a/src/tests/servers/app/Jamfile b/src/tests/servers/app/Jamfile index ad37f37fd1..0f24d68519 100644 --- a/src/tests/servers/app/Jamfile +++ b/src/tests/servers/app/Jamfile @@ -173,6 +173,7 @@ SubInclude HAIKU_TOP src tests servers app copy_bits ; SubInclude HAIKU_TOP src tests servers app cursor_test ; SubInclude HAIKU_TOP src tests servers app desktop_window ; SubInclude HAIKU_TOP src tests servers app draw_after_children ; +SubInclude HAIKU_TOP src tests servers app draw_string_offsets ; SubInclude HAIKU_TOP src tests servers app drawing_debugger ; SubInclude HAIKU_TOP src tests servers app drawing_modes ; SubInclude HAIKU_TOP src tests servers app event_mask ; diff --git a/src/tests/servers/app/draw_string_offsets/Jamfile b/src/tests/servers/app/draw_string_offsets/Jamfile new file mode 100644 index 0000000000..cab82eaf61 --- /dev/null +++ b/src/tests/servers/app/draw_string_offsets/Jamfile @@ -0,0 +1,17 @@ +SubDir HAIKU_TOP src tests servers app draw_string_offsets ; + +SetSubDirSupportedPlatformsBeOSCompatible ; +AddSubDirSupportedPlatforms libbe_test ; + +UseHeaders [ FDirName os app ] ; +UseHeaders [ FDirName os interface ] ; + +SimpleTest DrawStringOffsets : + main.cpp + : be $(TARGET_LIBSUPC++) + ; + +if ( $(TARGET_PLATFORM) = libbe_test ) { + HaikuInstall install-test-apps : $(HAIKU_APP_TEST_DIR) : DrawStringOffsets + : tests!apps ; +} diff --git a/src/tests/servers/app/draw_string_offsets/main.cpp b/src/tests/servers/app/draw_string_offsets/main.cpp new file mode 100644 index 0000000000..37269a974e --- /dev/null +++ b/src/tests/servers/app/draw_string_offsets/main.cpp @@ -0,0 +1,70 @@ +#include +#include + +#include +#include +#include +#include +#include + + +static const char* kAppSignature = "application/x.vnd-Haiku.DrawStringOffsets"; + + +class TestView : public BView { +public: + TestView(BRect frame, const char* name, + uint32 resizeFlags, uint32 flags); + + virtual void Draw(BRect updateRect); +}; + + +TestView::TestView(BRect frame, const char* name, uint32 resizeFlags, + uint32 flags) + : + BView(frame, name, resizeFlags, flags) +{ +} + + +void +TestView::Draw(BRect updateRect) +{ + BPoint offsets[5]; + offsets[0].x = 10; + offsets[0].y = 10; + offsets[1].x = 15; + offsets[1].y = 10; + offsets[2].x = 20; + offsets[2].y = 18; + offsets[3].x = 23; + offsets[3].y = 12; + offsets[4].x = 30; + offsets[4].y = 10; + + DrawString("Hello", strlen("Hello"), offsets); +} + + +// #pragma mark - + + +int +main(int argc, char** argv) +{ + BApplication app(kAppSignature); + + BWindow* window = new BWindow(BRect(50.0, 50.0, 300.0, 250.0), + "DrawString() with offsets", B_TITLED_WINDOW, + B_ASYNCHRONOUS_CONTROLS | B_QUIT_ON_WINDOW_CLOSE); + + BView* view = new TestView(window->Bounds(), "test", B_FOLLOW_ALL, + B_WILL_DRAW); + window->AddChild(view); + + window->Show(); + + app.Run(); + return 0; +}