* 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:
parent
f237e4182b
commit
77e5acc0d9
@ -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);
|
||||
|
@ -254,6 +254,7 @@ enum {
|
||||
|
||||
AS_DRAW_STRING,
|
||||
AS_DRAW_STRING_WITH_DELTA,
|
||||
AS_DRAW_STRING_WITH_OFFSETS,
|
||||
|
||||
AS_SYNC,
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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:
|
||||
{
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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 ;
|
||||
|
17
src/tests/servers/app/draw_string_offsets/Jamfile
Normal file
17
src/tests/servers/app/draw_string_offsets/Jamfile
Normal 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 ;
|
||||
}
|
70
src/tests/servers/app/draw_string_offsets/main.cpp
Normal file
70
src/tests/servers/app/draw_string_offsets/main.cpp
Normal 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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user