* 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
This commit is contained in:
Stephan Aßmus 2010-03-15 13:59:14 +00:00
parent f237e4182b
commit 77e5acc0d9
14 changed files with 329 additions and 21 deletions

View File

@ -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);

View File

@ -254,6 +254,7 @@ enum {
AS_DRAW_STRING,
AS_DRAW_STRING_WITH_DELTA,
AS_DRAW_STRING_WITH_OFFSETS,
AS_SYNC,

View File

@ -45,6 +45,7 @@
#include <String.h>
#include <Window.h>
#include <utf8_functions.h>
#include <AppMisc.h>
#include <AppServerLink.h>
#include <binary_compatibility/Interface.h>
@ -59,7 +60,6 @@
#include <TokenSpace.h>
#include <ViewPrivate.h>
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<int32>(length);
fOwner->fLink->Attach<int32>(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)

View File

@ -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;

View File

@ -49,8 +49,10 @@
#include <WindowPrivate.h>
#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<ViewDrawStringInfo>(&info) != B_OK)
if (link.Read<ViewDrawStringInfo>(&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<int32>(&stringLength) != B_OK || stringLength <= 0)
break;
int32 glyphCount;
if (link.Read<int32>(&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:
{

View File

@ -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)

View File

@ -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);

View File

@ -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());
}

View File

@ -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;

View File

@ -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,

View File

@ -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,

View File

@ -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 ;

View File

@ -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 ;
}

View File

@ -0,0 +1,70 @@
#include <stdio.h>
#include <string.h>
#include <Application.h>
#include <Message.h>
#include <Shape.h>
#include <View.h>
#include <Window.h>
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;
}