* Copyright 2001-2018, Haiku, Inc.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aรmus <superstippi@gmx.de>
* Julian Harnath <julian.harnath@rwth-aachen.de>
*/
#include "DrawingEngine.h"
#include <Bitmap.h>
#include <StackOrHeapArray.h>
#include <stdio.h>
#include <algorithm>
#include <stack>
#include "DrawState.h"
#include "GlyphLayoutEngine.h"
#include "Painter.h"
#include "ServerBitmap.h"
#include "ServerCursor.h"
#include "RenderingBuffer.h"
#include "drawing_support.h"
#if DEBUG
# define ASSERT_PARALLEL_LOCKED() \
{ if (!IsParallelAccessLocked()) debugger("not parallel locked!"); }
# define ASSERT_EXCLUSIVE_LOCKED() \
{ if (!IsExclusiveAccessLocked()) debugger("not exclusive locked!"); }
#else
# define ASSERT_PARALLEL_LOCKED()
# define ASSERT_EXCLUSIVE_LOCKED()
#endif
static inline void
make_rect_valid(BRect& rect)
{
if (rect.left > rect.right) {
float temp = rect.left;
rect.left = rect.right;
rect.right = temp;
}
if (rect.top > rect.bottom) {
float temp = rect.top;
rect.top = rect.bottom;
rect.bottom = temp;
}
}
static inline void
extend_by_stroke_width(BRect& rect, float penSize)
{
float inset = -ceilf(penSize / 2.0 - 0.5);
rect.InsetBy(inset, inset);
}
class AutoFloatingOverlaysHider {
public:
AutoFloatingOverlaysHider(HWInterface* interface, const BRect& area)
:
fInterface(interface),
fHidden(interface->HideFloatingOverlays(area))
{
}
AutoFloatingOverlaysHider(HWInterface* interface)
:
fInterface(interface),
fHidden(fInterface->HideFloatingOverlays())
{
}
~AutoFloatingOverlaysHider()
{
if (fHidden)
fInterface->ShowFloatingOverlays();
}
bool WasHidden() const
{
return fHidden;
}
private:
HWInterface* fInterface;
bool fHidden;
};
class DrawTransaction {
public:
DrawTransaction(DrawingEngine *engine, const BRect &bounds)
:
fEngine(engine),
fOverlaysHidden(false)
{
fDirty.Set(bounds);
fDirty.IntersectWith(fEngine->fPainter->ClippingRegion());
if (fDirty.CountRects() == 0)
return;
fOverlaysHidden
= fEngine->fGraphicsCard->HideFloatingOverlays(fDirty.Frame());
}
DrawTransaction(DrawingEngine *engine)
:
fEngine(engine),
fOverlaysHidden(false)
{
fDirty = *fEngine->fPainter->ClippingRegion();
if (fDirty.CountRects() == 0)
return;
fOverlaysHidden
= fEngine->fGraphicsCard->HideFloatingOverlays(fDirty.Frame());
}
DrawTransaction(DrawingEngine *engine, const BRegion ®ion)
:
fEngine(engine),
fOverlaysHidden(false)
{
fDirty = region;
if (fDirty.CountRects() == 0)
return;
fOverlaysHidden
= fEngine->fGraphicsCard->HideFloatingOverlays(fDirty.Frame());
}
~DrawTransaction()
{
if (fEngine->fCopyToFront)
fEngine->fGraphicsCard->InvalidateRegion(fDirty);
if (fOverlaysHidden)
fEngine->fGraphicsCard->ShowFloatingOverlays();
}
bool IsDirty() const
{
return fDirty.CountRects() > 0;
}
void SetDirty(const BRect &rect)
{
fDirty.Set(rect);
fDirty.IntersectWith(fEngine->fPainter->ClippingRegion());
}
const BRegion &DirtyRegion() const
{
return fDirty;
}
bool WasOverlaysHidden() const
{
return fOverlaysHidden;
}
private:
DrawingEngine *fEngine;
bool fOverlaysHidden;
BRegion fDirty;
};
DrawingEngine::DrawingEngine(HWInterface* interface)
:
fPainter(new Painter()),
fGraphicsCard(NULL),
fCopyToFront(true)
{
SetHWInterface(interface);
}
DrawingEngine::~DrawingEngine()
{
SetHWInterface(NULL);
}
bool
DrawingEngine::LockParallelAccess()
{
return fGraphicsCard->LockParallelAccess();
}
#if DEBUG
bool
DrawingEngine::IsParallelAccessLocked() const
{
return fGraphicsCard->IsParallelAccessLocked();
}
#endif
void
DrawingEngine::UnlockParallelAccess()
{
fGraphicsCard->UnlockParallelAccess();
}
bool
DrawingEngine::LockExclusiveAccess()
{
return fGraphicsCard->LockExclusiveAccess();
}
bool
DrawingEngine::IsExclusiveAccessLocked() const
{
return fGraphicsCard->IsExclusiveAccessLocked();
}
void
DrawingEngine::UnlockExclusiveAccess()
{
fGraphicsCard->UnlockExclusiveAccess();
}
void
DrawingEngine::FrameBufferChanged()
{
if (!fGraphicsCard) {
fPainter->DetachFromBuffer();
return;
}
if (LockExclusiveAccess()) {
fPainter->AttachToBuffer(fGraphicsCard->DrawingBuffer());
UnlockExclusiveAccess();
}
}
void
DrawingEngine::SetHWInterface(HWInterface* interface)
{
if (fGraphicsCard == interface)
return;
if (fGraphicsCard)
fGraphicsCard->RemoveListener(this);
fGraphicsCard = interface;
if (fGraphicsCard)
fGraphicsCard->AddListener(this);
FrameBufferChanged();
}
void
DrawingEngine::SetCopyToFrontEnabled(bool enable)
{
fCopyToFront = enable;
}
void
DrawingEngine::CopyToFront( BRegion& region)
{
fGraphicsCard->InvalidateRegion(region);
}
void
DrawingEngine::ConstrainClippingRegion(const BRegion* region)
{
ASSERT_PARALLEL_LOCKED();
fPainter->ConstrainClipping(region);
}
void
DrawingEngine::SetDrawState(const DrawState* state, int32 xOffset,
int32 yOffset)
{
fPainter->SetDrawState(state, xOffset, yOffset);
}
void
DrawingEngine::SetHighColor(const rgb_color& color)
{
fPainter->SetHighColor(color);
}
void
DrawingEngine::SetLowColor(const rgb_color& color)
{
fPainter->SetLowColor(color);
}
void
DrawingEngine::SetPenSize(float size)
{
fPainter->SetPenSize(size);
}
void
DrawingEngine::SetStrokeMode(cap_mode lineCap, join_mode joinMode,
float miterLimit)
{
fPainter->SetStrokeMode(lineCap, joinMode, miterLimit);
}
void
DrawingEngine::SetFillRule(int32 fillRule)
{
fPainter->SetFillRule(fillRule);
}
void
DrawingEngine::SetBlendingMode(source_alpha srcAlpha, alpha_function alphaFunc)
{
fPainter->SetBlendingMode(srcAlpha, alphaFunc);
}
void
DrawingEngine::SetPattern(const struct pattern& pattern)
{
fPainter->SetPattern(pattern);
}
void
DrawingEngine::SetDrawingMode(drawing_mode mode)
{
fPainter->SetDrawingMode(mode);
}
void
DrawingEngine::SetDrawingMode(drawing_mode mode, drawing_mode& oldMode)
{
oldMode = fPainter->DrawingMode();
fPainter->SetDrawingMode(mode);
}
void
DrawingEngine::SetFont(const ServerFont& font)
{
fPainter->SetFont(font);
}
void
DrawingEngine::SetFont(const DrawState* state)
{
fPainter->SetFont(state);
}
void
DrawingEngine::SetTransform(const BAffineTransform& transform, int32 xOffset,
int32 yOffset)
{
fPainter->SetTransform(transform, xOffset, yOffset);
}
struct node {
node()
{
pointers = NULL;
}
node(const BRect& r, int32 maxPointers)
{
init(r, maxPointers);
}
~node()
{
delete [] pointers;
}
void init(const BRect& r, int32 maxPointers)
{
rect = r;
pointers = new(std::nothrow) node*[maxPointers];
in_degree = 0;
next_pointer = 0;
}
void push(node* node)
{
pointers[next_pointer] = node;
next_pointer++;
}
node* top()
{
return pointers[next_pointer];
}
node* pop()
{
node* ret = top();
next_pointer--;
return ret;
}
BRect rect;
int32 in_degree;
node** pointers;
int32 next_pointer;
};
static bool
is_left_of(const BRect& a, const BRect& b)
{
return (a.right < b.left);
}
static bool
is_above(const BRect& a, const BRect& b)
{
return (a.bottom < b.top);
}
void
DrawingEngine::CopyRegion( BRegion* region, int32 xOffset,
int32 yOffset)
{
ASSERT_PARALLEL_LOCKED();
BRect frame = region->Frame();
frame = frame | frame.OffsetByCopy(xOffset, yOffset);
AutoFloatingOverlaysHider _(fGraphicsCard, frame);
int32 count = region->CountRects();
BStackOrHeapArray<node, 64> nodes(count);
for (int32 i= 0; i < count; i++) {
nodes[i].init(region->RectAt(i), count);
if (nodes[i].pointers == NULL)
return;
}
for (int32 i = 0; i < count; i++) {
BRect a = region->RectAt(i);
for (int32 k = i + 1; k < count; k++) {
BRect b = region->RectAt(k);
int cmp = 0;
if (xOffset > 0) {
if (is_left_of(a, b)) {
cmp -= 1;
} else if (is_left_of(b, a)) {
cmp += 1;
}
} else if (xOffset < 0) {
if (is_left_of(a, b)) {
cmp += 1;
} else if (is_left_of(b, a)) {
cmp -= 1;
}
}
if (yOffset > 0) {
if (is_above(a, b)) {
cmp -= 1;
} else if (is_above(b, a)) {
cmp += 1;
}
} else if (yOffset < 0) {
if (is_above(a, b)) {
cmp += 1;
} else if (is_above(b, a)) {
cmp -= 1;
}
}
if (cmp > 0) {
nodes[i].push(&nodes[k]);
nodes[k].in_degree++;
} else if (cmp < 0) {
nodes[k].push(&nodes[i]);
nodes[i].in_degree++;
}
}
}
std::stack<node*> inDegreeZeroNodes;
for (int32 i = 0; i < count; i++) {
if (nodes[i].in_degree == 0) {
inDegreeZeroNodes.push(&nodes[i]);
}
}
while (!inDegreeZeroNodes.empty()) {
node* n = inDegreeZeroNodes.top();
inDegreeZeroNodes.pop();
BRect touched = CopyRect(n->rect, xOffset, yOffset);
fGraphicsCard->Invalidate(touched);
for (int32 k = 0; k < n->next_pointer; k++) {
n->pointers[k]->in_degree--;
if (n->pointers[k]->in_degree == 0)
inDegreeZeroNodes.push(n->pointers[k]);
}
}
}
void
DrawingEngine::InvertRect(BRect r)
{
ASSERT_PARALLEL_LOCKED();
make_rect_valid(r);
DrawTransaction transaction(this, fPainter->ClipRect(r));
if (!transaction.IsDirty())
return;
fPainter->InvertRect(r);
}
void
DrawingEngine::DrawBitmap(ServerBitmap* bitmap, const BRect& bitmapRect,
const BRect& viewRect, uint32 options)
{
ASSERT_PARALLEL_LOCKED();
DrawTransaction transaction(this, fPainter->TransformAndClipRect(viewRect));
if (transaction.IsDirty())
fPainter->DrawBitmap(bitmap, bitmapRect, viewRect, options);
}
void
DrawingEngine::DrawArc(BRect r, const float& angle, const float& span,
bool filled)
{
ASSERT_PARALLEL_LOCKED();
make_rect_valid(r);
fPainter->AlignEllipseRect(&r, filled);
BRect clipped(r);
if (!filled)
extend_by_stroke_width(clipped, fPainter->PenSize());
DrawTransaction transaction(this, fPainter->TransformAndClipRect(clipped));
if (!transaction.IsDirty())
return;
float xRadius = r.Width() / 2.0;
float yRadius = r.Height() / 2.0;
BPoint center(r.left + xRadius,
r.top + yRadius);
if (filled)
fPainter->FillArc(center, xRadius, yRadius, angle, span);
else
fPainter->StrokeArc(center, xRadius, yRadius, angle, span);
}
void
DrawingEngine::FillArc(BRect r, const float& angle, const float& span,
const BGradient& gradient)
{
ASSERT_PARALLEL_LOCKED();
make_rect_valid(r);
fPainter->AlignEllipseRect(&r, true);
DrawTransaction transaction(this, fPainter->TransformAndClipRect(r));
if (!transaction.IsDirty())
return;
float xRadius = r.Width() / 2.0;
float yRadius = r.Height() / 2.0;
BPoint center(r.left + xRadius,
r.top + yRadius);
fPainter->FillArc(center, xRadius, yRadius, angle, span, gradient);
}
void
DrawingEngine::DrawBezier(BPoint* pts, bool filled)
{
ASSERT_PARALLEL_LOCKED();
DrawTransaction transaction(this);
transaction.SetDirty(fPainter->DrawBezier(pts, filled));
}
void
DrawingEngine::FillBezier(BPoint* pts, const BGradient& gradient)
{
ASSERT_PARALLEL_LOCKED();
DrawTransaction transaction(this);
transaction.SetDirty(fPainter->FillBezier(pts, gradient));
}
void
DrawingEngine::DrawEllipse(BRect r, bool filled)
{
ASSERT_PARALLEL_LOCKED();
make_rect_valid(r);
BRect clipped = r;
fPainter->AlignEllipseRect(&clipped, filled);
if (!filled)
extend_by_stroke_width(clipped, fPainter->PenSize());
clipped.left = floorf(clipped.left);
clipped.top = floorf(clipped.top);
clipped.right = ceilf(clipped.right);
clipped.bottom = ceilf(clipped.bottom);
DrawTransaction transaction(this, fPainter->TransformAndClipRect(clipped));
if (!transaction.IsDirty())
return;
fPainter->DrawEllipse(r, filled);
}
void
DrawingEngine::FillEllipse(BRect r, const BGradient& gradient)
{
ASSERT_PARALLEL_LOCKED();
make_rect_valid(r);
BRect clipped = r;
fPainter->AlignEllipseRect(&clipped, true);
clipped.left = floorf(clipped.left);
clipped.top = floorf(clipped.top);
clipped.right = ceilf(clipped.right);
clipped.bottom = ceilf(clipped.bottom);
DrawTransaction transaction(this, fPainter->TransformAndClipRect(clipped));
if (!transaction.IsDirty())
return;
fPainter->FillEllipse(r, gradient);
}
void
DrawingEngine::DrawPolygon(BPoint* ptlist, int32 numpts, BRect bounds,
bool filled, bool closed)
{
ASSERT_PARALLEL_LOCKED();
make_rect_valid(bounds);
if (!filled)
extend_by_stroke_width(bounds, fPainter->PenSize());
DrawTransaction transaction(this, fPainter->TransformAndClipRect(bounds));
if (!transaction.IsDirty())
return;
fPainter->DrawPolygon(ptlist, numpts, filled, closed);
}
void
DrawingEngine::FillPolygon(BPoint* ptlist, int32 numpts, BRect bounds,
const BGradient& gradient, bool closed)
{
ASSERT_PARALLEL_LOCKED();
make_rect_valid(bounds);
DrawTransaction transaction(this, fPainter->TransformAndClipRect(bounds));
if (!transaction.IsDirty())
return;
fPainter->FillPolygon(ptlist, numpts, gradient, closed);
}
void
DrawingEngine::StrokePoint(const BPoint& pt, const rgb_color& color)
{
StrokeLine(pt, pt, color);
}
it assumes a one pixel wide line
*/
void
DrawingEngine::StrokeLine(const BPoint& start, const BPoint& end,
const rgb_color& color)
{
ASSERT_PARALLEL_LOCKED();
BRect touched(start, end);
make_rect_valid(touched);
touched = fPainter->ClipRect(touched);
DrawTransaction transaction(this, touched);
if (!fPainter->StraightLine(start, end, color)) {
rgb_color previousColor = fPainter->HighColor();
drawing_mode previousMode = fPainter->DrawingMode();
fPainter->SetHighColor(color);
fPainter->SetDrawingMode(B_OP_OVER);
fPainter->StrokeLine(start, end);
fPainter->SetDrawingMode(previousMode);
fPainter->SetHighColor(previousColor);
}
}
void
DrawingEngine::StrokeRect(BRect r, const rgb_color& color)
{
ASSERT_PARALLEL_LOCKED();
make_rect_valid(r);
DrawTransaction transaction(this, fPainter->ClipRect(r));
if (!transaction.IsDirty())
return;
fPainter->StrokeRect(r, color);
}
void
DrawingEngine::FillRect(BRect r, const rgb_color& color)
{
ASSERT_PARALLEL_LOCKED();
make_rect_valid(r);
r = fPainter->ClipRect(r);
DrawTransaction transaction(this, r);
if (!transaction.IsDirty())
return;
fPainter->FillRect(r, color);
}
void
DrawingEngine::FillRegion(BRegion& r, const rgb_color& color)
{
ASSERT_PARALLEL_LOCKED();
BRect frame = r.Frame();
if (!fPainter->Bounds().Contains(frame)) {
return;
}
DrawTransaction transaction(this, r);
int32 count = r.CountRects();
for (int32 i = 0; i < count; i++)
fPainter->FillRectNoClipping(r.RectAtInt(i), color);
}
void
DrawingEngine::StrokeRect(BRect r)
{
ASSERT_PARALLEL_LOCKED();
make_rect_valid(r);
BRect clipped(r);
extend_by_stroke_width(clipped, fPainter->PenSize());
DrawTransaction transaction(this, fPainter->TransformAndClipRect(clipped));
if (!transaction.IsDirty())
return;
fPainter->StrokeRect(r);
}
void
DrawingEngine::FillRect(BRect r)
{
ASSERT_PARALLEL_LOCKED();
make_rect_valid(r);
r = fPainter->AlignRect(r);
DrawTransaction transaction(this, fPainter->TransformAndClipRect(r));
if (!transaction.IsDirty())
return;
fPainter->FillRect(r);
}
void
DrawingEngine::FillRect(BRect r, const BGradient& gradient)
{
ASSERT_PARALLEL_LOCKED();
make_rect_valid(r);
r = fPainter->AlignRect(r);
DrawTransaction transaction(this, fPainter->TransformAndClipRect(r));
if (!transaction.IsDirty())
return;
fPainter->FillRect(r, gradient);
}
void
DrawingEngine::FillRegion(BRegion& r)
{
ASSERT_PARALLEL_LOCKED();
BRect clipped = fPainter->TransformAndClipRect(r.Frame());
DrawTransaction transaction(this, clipped);
if (!transaction.IsDirty())
return;
int32 count = r.CountRects();
for (int32 i = 0; i < count; i++)
fPainter->FillRect(r.RectAt(i));
}
void
DrawingEngine::FillRegion(BRegion& r, const BGradient& gradient)
{
ASSERT_PARALLEL_LOCKED();
BRect clipped = fPainter->TransformAndClipRect(r.Frame());
DrawTransaction transaction(this, clipped);
if (!transaction.IsDirty())
return;
int32 count = r.CountRects();
for (int32 i = 0; i < count; i++)
fPainter->FillRect(r.RectAt(i), gradient);
}
void
DrawingEngine::DrawRoundRect(BRect r, float xrad, float yrad, bool filled)
{
ASSERT_PARALLEL_LOCKED();
make_rect_valid(r);
if (!filled)
extend_by_stroke_width(r, fPainter->PenSize());
BRect clipped = fPainter->TransformAndClipRect(r);
clipped.left = floorf(clipped.left);
clipped.top = floorf(clipped.top);
clipped.right = ceilf(clipped.right);
clipped.bottom = ceilf(clipped.bottom);
DrawTransaction transaction(this, clipped);
if (!transaction.IsDirty())
return;
if (filled)
fPainter->FillRoundRect(r, xrad, yrad);
else
fPainter->StrokeRoundRect(r, xrad, yrad);
}
void
DrawingEngine::FillRoundRect(BRect r, float xrad, float yrad,
const BGradient& gradient)
{
ASSERT_PARALLEL_LOCKED();
make_rect_valid(r);
BRect clipped = fPainter->TransformAndClipRect(r);
clipped.left = floorf(clipped.left);
clipped.top = floorf(clipped.top);
clipped.right = ceilf(clipped.right);
clipped.bottom = ceilf(clipped.bottom);
DrawTransaction transaction(this, clipped);
if (!transaction.IsDirty())
return;
fPainter->FillRoundRect(r, xrad, yrad, gradient);
}
void
DrawingEngine::DrawShape(const BRect& bounds, int32 opCount,
const uint32* opList, int32 ptCount, const BPoint* ptList, bool filled,
const BPoint& viewToScreenOffset, float viewScale)
{
ASSERT_PARALLEL_LOCKED();
DrawTransaction transaction(this);
transaction.SetDirty(fPainter->DrawShape(opCount, opList, ptCount, ptList,
filled, viewToScreenOffset, viewScale));
}
void
DrawingEngine::FillShape(const BRect& bounds, int32 opCount,
const uint32* opList, int32 ptCount, const BPoint* ptList,
const BGradient& gradient, const BPoint& viewToScreenOffset,
float viewScale)
{
ASSERT_PARALLEL_LOCKED();
DrawTransaction transaction(this);
transaction.SetDirty(fPainter->FillShape(opCount, opList, ptCount, ptList,
gradient, viewToScreenOffset, viewScale));
}
void
DrawingEngine::DrawTriangle(BPoint* pts, const BRect& bounds, bool filled)
{
ASSERT_PARALLEL_LOCKED();
BRect clipped(bounds);
if (!filled)
extend_by_stroke_width(clipped, fPainter->PenSize());
DrawTransaction transaction(this, fPainter->TransformAndClipRect(clipped));
if (!transaction.IsDirty())
return;
if (filled)
fPainter->FillTriangle(pts[0], pts[1], pts[2]);
else
fPainter->StrokeTriangle(pts[0], pts[1], pts[2]);
}
void
DrawingEngine::FillTriangle(BPoint* pts, const BRect& bounds,
const BGradient& gradient)
{
ASSERT_PARALLEL_LOCKED();
DrawTransaction transaction(this, fPainter->TransformAndClipRect(bounds));
if (!transaction.IsDirty())
return;
fPainter->FillTriangle(pts[0], pts[1], pts[2], gradient);
}
void
DrawingEngine::StrokeLine(const BPoint& start, const BPoint& end)
{
ASSERT_PARALLEL_LOCKED();
BRect touched(start, end);
make_rect_valid(touched);
extend_by_stroke_width(touched, fPainter->PenSize());
DrawTransaction transaction(this, fPainter->TransformAndClipRect(touched));
if (!transaction.IsDirty())
return;
fPainter->StrokeLine(start, end);
}
void
DrawingEngine::StrokeLineArray(int32 numLines,
const ViewLineArrayInfo *lineData)
{
ASSERT_PARALLEL_LOCKED();
if (!lineData || numLines <= 0)
return;
const ViewLineArrayInfo* data = (const ViewLineArrayInfo*)&lineData[0];
BRect touched(min_c(data->startPoint.x, data->endPoint.x),
min_c(data->startPoint.y, data->endPoint.y),
max_c(data->startPoint.x, data->endPoint.x),
max_c(data->startPoint.y, data->endPoint.y));
for (int32 i = 1; i < numLines; i++) {
data = (const ViewLineArrayInfo*)&lineData[i];
BRect box(min_c(data->startPoint.x, data->endPoint.x),
min_c(data->startPoint.y, data->endPoint.y),
max_c(data->startPoint.x, data->endPoint.x),
max_c(data->startPoint.y, data->endPoint.y));
touched = touched | box;
}
extend_by_stroke_width(touched, fPainter->PenSize());
DrawTransaction transaction(this, fPainter->TransformAndClipRect(touched));
if (!transaction.IsDirty())
return;
data = (const ViewLineArrayInfo*)&(lineData[0]);
rgb_color oldColor = fPainter->HighColor();
struct pattern pattern = fPainter->Pattern();
fPainter->SetHighColor(data->color);
fPainter->SetPattern(B_SOLID_HIGH);
fPainter->StrokeLine(data->startPoint, data->endPoint);
for (int32 i = 1; i < numLines; i++) {
data = (const ViewLineArrayInfo*)&(lineData[i]);
fPainter->SetHighColor(data->color);
fPainter->StrokeLine(data->startPoint, data->endPoint);
}
fPainter->SetHighColor(oldColor);
fPainter->SetPattern(pattern);
}
BPoint
DrawingEngine::DrawString(const char* string, int32 length,
const BPoint& pt, escapement_delta* delta)
{
ASSERT_PARALLEL_LOCKED();
BPoint penLocation = pt;
if (fPainter->ClippingRegion() != NULL
&& fPainter->Font().Rotation() == 0.0f
&& fPainter->IsIdentityTransform()) {
float fontSize = fPainter->Font().Size();
BRect clippingFrame = fPainter->ClippingRegion()->Frame();
if (pt.x - fontSize > clippingFrame.right
|| pt.y + fontSize < clippingFrame.top
|| pt.y - fontSize > clippingFrame.bottom) {
penLocation.x += StringWidth(string, length, delta);
return penLocation;
}
}
FontCacheReference cacheReference;
BRect b = fPainter->BoundingBox(string, length, pt, &penLocation, delta,
&cacheReference);
DrawTransaction transaction(this, fPainter->ClipRect(b));
if (transaction.IsDirty()) {
fPainter->DrawString(string, length, pt, delta, &cacheReference);
}
return penLocation;
}
BPoint
DrawingEngine::DrawString(const char* string, int32 length,
const BPoint* offsets)
{
ASSERT_PARALLEL_LOCKED();
FontCacheReference cacheReference;
BPoint penLocation;
BRect b = fPainter->BoundingBox(string, length, offsets, &penLocation,
&cacheReference);
DrawTransaction transaction(this, fPainter->ClipRect(b));
if (transaction.IsDirty()) {
fPainter->DrawString(string, length, offsets, &cacheReference);
}
return penLocation;
}
float
DrawingEngine::StringWidth(const char* string, int32 length,
escapement_delta* delta)
{
return fPainter->StringWidth(string, length, delta);
}
float
DrawingEngine::StringWidth(const char* string, int32 length,
const ServerFont& font, escapement_delta* delta)
{
return font.StringWidth(string, length, delta);
}
BPoint
DrawingEngine::DrawStringDry(const char* string, int32 length,
const BPoint& pt, escapement_delta* delta)
{
ASSERT_PARALLEL_LOCKED();
BPoint penLocation = pt;
if (fPainter->Font().Rotation() == 0.0f
&& fPainter->IsIdentityTransform()) {
penLocation.x += StringWidth(string, length, delta);
return penLocation;
}
fPainter->BoundingBox(string, length, pt, &penLocation, delta, NULL);
return penLocation;
}
BPoint
DrawingEngine::DrawStringDry(const char* string, int32 length,
const BPoint* offsets)
{
ASSERT_PARALLEL_LOCKED();
BPoint penLocation;
fPainter->BoundingBox(string, length, offsets, &penLocation, NULL);
return penLocation;
}
ServerBitmap*
DrawingEngine::DumpToBitmap()
{
return NULL;
}
status_t
DrawingEngine::ReadBitmap(ServerBitmap* bitmap, bool drawCursor, BRect bounds)
{
ASSERT_EXCLUSIVE_LOCKED();
RenderingBuffer* buffer = fGraphicsCard->FrontBuffer();
if (buffer == NULL)
return B_ERROR;
BRect clip(0, 0, buffer->Width() - 1, buffer->Height() - 1);
bounds = bounds & clip;
AutoFloatingOverlaysHider _(fGraphicsCard, bounds);
status_t result = bitmap->ImportBits(buffer->Bits(), buffer->BitsLength(),
buffer->BytesPerRow(), buffer->ColorSpace(),
bounds.LeftTop(), BPoint(0, 0),
bounds.IntegerWidth() + 1, bounds.IntegerHeight() + 1);
if (drawCursor) {
ServerCursorReference cursorRef = fGraphicsCard->Cursor();
ServerCursor* cursor = cursorRef.Get();
if (!cursor)
return result;
int32 cursorWidth = cursor->Width();
int32 cursorHeight = cursor->Height();
BPoint cursorPosition = fGraphicsCard->CursorPosition();
cursorPosition -= bounds.LeftTop() + cursor->GetHotSpot();
BBitmap cursorArea(BRect(0, 0, cursorWidth - 1, cursorHeight - 1),
B_BITMAP_NO_SERVER_LINK, B_RGBA32);
cursorArea.ImportBits(bitmap->Bits(), bitmap->BitsLength(),
bitmap->BytesPerRow(), bitmap->ColorSpace(),
cursorPosition, BPoint(0, 0),
cursorArea.Bounds().Size());
uint8* bits = (uint8*)cursorArea.Bits();
uint8* cursorBits = (uint8*)cursor->Bits();
for (int32 i = 0; i < cursorHeight; i++) {
for (int32 j = 0; j < cursorWidth; j++) {
uint8 alpha = 255 - cursorBits[3];
bits[0] = ((bits[0] * alpha) >> 8) + cursorBits[0];
bits[1] = ((bits[1] * alpha) >> 8) + cursorBits[1];
bits[2] = ((bits[2] * alpha) >> 8) + cursorBits[2];
cursorBits += 4;
bits += 4;
}
}
bitmap->ImportBits(cursorArea.Bits(), cursorArea.BitsLength(),
cursorArea.BytesPerRow(), cursorArea.ColorSpace(),
BPoint(0, 0), cursorPosition,
cursorWidth, cursorHeight);
}
return result;
}
BRect
DrawingEngine::CopyRect(BRect src, int32 xOffset, int32 yOffset) const
{
BRect dst;
RenderingBuffer* buffer = fGraphicsCard->DrawingBuffer();
if (buffer) {
BRect clip(0, 0, buffer->Width() - 1, buffer->Height() - 1);
dst = src;
dst.OffsetBy(xOffset, yOffset);
if (clip.Intersects(src) && clip.Intersects(dst)) {
uint32 bytesPerRow = buffer->BytesPerRow();
uint8* bits = (uint8*)buffer->Bits();
src = src & clip;
dst = dst & clip;
dst.OffsetBy(-xOffset, -yOffset);
src = src & dst;
bits += (ssize_t)src.left * 4 + (ssize_t)src.top * bytesPerRow;
uint32 width = src.IntegerWidth() + 1;
uint32 height = src.IntegerHeight() + 1;
_CopyRect(bits, width, height, bytesPerRow,
xOffset, yOffset);
dst.OffsetBy(xOffset, yOffset);
}
}
return dst;
}
void
DrawingEngine::SetRendererOffset(int32 offsetX, int32 offsetY)
{
fPainter->SetRendererOffset(offsetX, offsetY);
}
void
DrawingEngine::_CopyRect(uint8* src, uint32 width, uint32 height,
uint32 bytesPerRow, int32 xOffset, int32 yOffset) const
{
int32 yIncrement;
const bool needMemmove = (yOffset == 0 && xOffset > 0 && uint32(xOffset) <= width);
if (yOffset > 0) {
yIncrement = -bytesPerRow;
src += (height - 1) * bytesPerRow;
} else {
yIncrement = bytesPerRow;
}
uint8* dst = src + (ssize_t)yOffset * bytesPerRow + (ssize_t)xOffset * 4;
if (!needMemmove) {
for (uint32 y = 0; y < height; y++) {
memcpy(dst, src, width * 4);
src += yIncrement;
dst += yIncrement;
}
} else {
for (uint32 y = 0; y < height; y++) {
memmove(dst, src, width * 4);
src += yIncrement;
dst += yIncrement;
}
}
}