* Copyright 2009-2010, Haiku, Inc.
* Distributed under the terms of the MIT License.
*
* Authors:
* Michael Lotz <mmlr@mlotz.ch>
*/
#include "RemoteDrawingEngine.h"
#include "RemoteMessage.h"
#include "BitmapDrawingEngine.h"
#include "DrawState.h"
#include "ServerTokenSpace.h"
#include <Bitmap.h>
#include <utf8_functions.h>
#include <new>
#define TRACE(x...)
#define TRACE_ALWAYS(x...) debug_printf("RemoteDrawingEngine: " x)
#define TRACE_ERROR(x...) debug_printf("RemoteDrawingEngine: " x)
RemoteDrawingEngine::RemoteDrawingEngine(RemoteHWInterface* interface)
:
DrawingEngine(interface),
fHWInterface(interface),
fToken(gTokenSpace.NewToken(kRemoteDrawingEngineToken, this)),
fExtendWidth(0),
fCallbackAdded(false),
fResultNotify(-1),
fStringWidthResult(0.0f),
fReadBitmapResult(NULL),
fBitmapDrawingEngine(NULL)
{
RemoteMessage message(NULL, fHWInterface->SendBuffer());
message.Start(RP_CREATE_STATE);
message.Add(fToken);
}
RemoteDrawingEngine::~RemoteDrawingEngine()
{
RemoteMessage message(NULL, fHWInterface->SendBuffer());
message.Start(RP_DELETE_STATE);
message.Add(fToken);
message.Flush();
if (fCallbackAdded)
fHWInterface->RemoveCallback(fToken);
if (fResultNotify >= 0)
delete_sem(fResultNotify);
}
void
RemoteDrawingEngine::FrameBufferChanged()
{
}
void
RemoteDrawingEngine::SetCopyToFrontEnabled(bool enabled)
{
DrawingEngine::SetCopyToFrontEnabled(enabled);
RemoteMessage message(NULL, fHWInterface->SendBuffer());
message.Start(enabled ? RP_ENABLE_SYNC_DRAWING : RP_DISABLE_SYNC_DRAWING);
message.Add(fToken);
}
void
RemoteDrawingEngine::ConstrainClippingRegion(const BRegion* region)
{
if (fClippingRegion == *region)
return;
fClippingRegion = *region;
RemoteMessage message(NULL, fHWInterface->SendBuffer());
message.Start(RP_CONSTRAIN_CLIPPING_REGION);
message.Add(fToken);
message.AddRegion(*region);
}
void
RemoteDrawingEngine::SetDrawState(const DrawState* state, int32 xOffset,
int32 yOffset)
{
SetPenSize(state->PenSize());
SetDrawingMode(state->GetDrawingMode());
SetBlendingMode(state->AlphaSrcMode(), state->AlphaFncMode());
SetPattern(state->GetPattern().GetPattern());
SetStrokeMode(state->LineCapMode(), state->LineJoinMode(),
state->MiterLimit());
SetHighColor(state->HighColor());
SetLowColor(state->LowColor());
SetFont(state->Font());
SetTransform(state->CombinedTransform(), xOffset, yOffset);
RemoteMessage message(NULL, fHWInterface->SendBuffer());
message.Start(RP_SET_OFFSETS);
message.Add(fToken);
message.Add(xOffset);
message.Add(yOffset);
}
void
RemoteDrawingEngine::SetHighColor(const rgb_color& color)
{
if (fState.HighColor() == color)
return;
fState.SetHighColor(color);
RemoteMessage message(NULL, fHWInterface->SendBuffer());
message.Start(RP_SET_HIGH_COLOR);
message.Add(fToken);
message.Add(color);
}
void
RemoteDrawingEngine::SetLowColor(const rgb_color& color)
{
if (fState.LowColor() == color)
return;
fState.SetLowColor(color);
RemoteMessage message(NULL, fHWInterface->SendBuffer());
message.Start(RP_SET_LOW_COLOR);
message.Add(fToken);
message.Add(color);
}
void
RemoteDrawingEngine::SetPenSize(float size)
{
if (fState.PenSize() == size)
return;
fState.SetPenSize(size);
fExtendWidth = -(size / 2);
RemoteMessage message(NULL, fHWInterface->SendBuffer());
message.Start(RP_SET_PEN_SIZE);
message.Add(fToken);
message.Add(size);
}
void
RemoteDrawingEngine::SetStrokeMode(cap_mode lineCap, join_mode joinMode,
float miterLimit)
{
if (fState.LineCapMode() == lineCap && fState.LineJoinMode() == joinMode
&& fState.MiterLimit() == miterLimit)
return;
fState.SetLineCapMode(lineCap);
fState.SetLineJoinMode(joinMode);
fState.SetMiterLimit(miterLimit);
RemoteMessage message(NULL, fHWInterface->SendBuffer());
message.Start(RP_SET_STROKE_MODE);
message.Add(fToken);
message.Add(lineCap);
message.Add(joinMode);
message.Add(miterLimit);
}
void
RemoteDrawingEngine::SetBlendingMode(source_alpha sourceAlpha,
alpha_function alphaFunc)
{
if (fState.AlphaSrcMode() == sourceAlpha
&& fState.AlphaFncMode() == alphaFunc)
return;
fState.SetBlendingMode(sourceAlpha, alphaFunc);
RemoteMessage message(NULL, fHWInterface->SendBuffer());
message.Start(RP_SET_BLENDING_MODE);
message.Add(fToken);
message.Add(sourceAlpha);
message.Add(alphaFunc);
}
void
RemoteDrawingEngine::SetPattern(const struct pattern& pattern)
{
if (fState.GetPattern() == pattern)
return;
fState.SetPattern(pattern);
RemoteMessage message(NULL, fHWInterface->SendBuffer());
message.Start(RP_SET_PATTERN);
message.Add(fToken);
message.Add(pattern);
}
void
RemoteDrawingEngine::SetDrawingMode(drawing_mode mode)
{
if (fState.GetDrawingMode() == mode)
return;
fState.SetDrawingMode(mode);
RemoteMessage message(NULL, fHWInterface->SendBuffer());
message.Start(RP_SET_DRAWING_MODE);
message.Add(fToken);
message.Add(mode);
}
void
RemoteDrawingEngine::SetDrawingMode(drawing_mode mode, drawing_mode& oldMode)
{
oldMode = fState.GetDrawingMode();
SetDrawingMode(mode);
}
void
RemoteDrawingEngine::SetFont(const ServerFont& font)
{
if (fState.Font() == font)
return;
fState.SetFont(font);
RemoteMessage message(NULL, fHWInterface->SendBuffer());
message.Start(RP_SET_FONT);
message.Add(fToken);
message.AddFont(font);
}
void
RemoteDrawingEngine::SetFont(const DrawState* state)
{
SetFont(state->Font());
}
void
RemoteDrawingEngine::SetTransform(const BAffineTransform& transform,
int32 xOffset, int32 yOffset)
{
if (fState.Transform() == transform)
return;
fState.SetTransform(transform);
RemoteMessage message(NULL, fHWInterface->SendBuffer());
message.Start(RP_SET_TRANSFORM);
message.Add(fToken);
message.AddTransform(transform);
}
BRect
RemoteDrawingEngine::CopyRect(BRect rect, int32 xOffset, int32 yOffset) const
{
RemoteMessage message(NULL, fHWInterface->SendBuffer());
message.Start(RP_COPY_RECT_NO_CLIPPING);
message.Add(xOffset);
message.Add(yOffset);
message.Add(rect);
return rect.OffsetBySelf(xOffset, yOffset);
}
void
RemoteDrawingEngine::InvertRect(BRect rect)
{
if (!fClippingRegion.Intersects(rect))
return;
RemoteMessage message(NULL, fHWInterface->SendBuffer());
message.Start(RP_INVERT_RECT);
message.Add(fToken);
message.Add(rect);
}
void
RemoteDrawingEngine::DrawBitmap(ServerBitmap* bitmap, const BRect& _bitmapRect,
const BRect& _viewRect, uint32 options)
{
BRect bitmapRect = _bitmapRect;
BRect viewRect = _viewRect;
double xScale = (bitmapRect.Width() + 1) / (viewRect.Width() + 1);
double yScale = (bitmapRect.Height() + 1) / (viewRect.Height() + 1);
BRect actualBitmapRect = bitmap->Bounds();
if (bitmapRect.left < actualBitmapRect.left) {
float diff = actualBitmapRect.left - bitmapRect.left;
viewRect.left += diff / xScale;
bitmapRect.left = actualBitmapRect.left;
}
if (bitmapRect.top < actualBitmapRect.top) {
float diff = actualBitmapRect.top - bitmapRect.top;
viewRect.top += diff / yScale;
bitmapRect.top = actualBitmapRect.top;
}
if (bitmapRect.right > actualBitmapRect.right) {
float diff = bitmapRect.right - actualBitmapRect.right;
viewRect.right -= diff / xScale;
bitmapRect.right = actualBitmapRect.right;
}
if (bitmapRect.bottom > actualBitmapRect.bottom) {
float diff = bitmapRect.bottom - actualBitmapRect.bottom;
viewRect.bottom -= diff / yScale;
bitmapRect.bottom = actualBitmapRect.bottom;
}
BRegion clippedRegion(viewRect);
clippedRegion.IntersectWith(&fClippingRegion);
int32 rectCount = clippedRegion.CountRects();
if (rectCount == 0)
return;
if (rectCount > 1 || (rectCount == 1 && clippedRegion.RectAt(0) != viewRect)
|| viewRect.Width() < bitmapRect.Width()
|| viewRect.Height() < bitmapRect.Height()) {
UtilityBitmap** bitmaps;
if (_ExtractBitmapRegions(*bitmap, options, bitmapRect, viewRect,
xScale, yScale, clippedRegion, bitmaps) != B_OK) {
return;
}
RemoteMessage message(NULL, fHWInterface->SendBuffer());
message.Start(RP_DRAW_BITMAP_RECTS);
message.Add(fToken);
message.Add(options);
message.Add(bitmap->ColorSpace());
message.Add(bitmap->Flags());
message.Add(rectCount);
for (int32 i = 0; i < rectCount; i++) {
message.Add(clippedRegion.RectAt(i));
message.AddBitmap(*bitmaps[i], true);
delete bitmaps[i];
}
free(bitmaps);
return;
}
RemoteMessage message(NULL, fHWInterface->SendBuffer());
message.Start(RP_DRAW_BITMAP);
message.Add(fToken);
message.Add(bitmapRect);
message.Add(viewRect);
message.Add(options);
message.AddBitmap(*bitmap);
}
void
RemoteDrawingEngine::DrawArc(BRect rect, const float& angle, const float& span,
bool filled)
{
BRect bounds = rect;
if (!filled)
bounds.InsetBy(fExtendWidth, fExtendWidth);
if (!fClippingRegion.Intersects(bounds))
return;
RemoteMessage message(NULL, fHWInterface->SendBuffer());
message.Start(filled ? RP_FILL_ARC : RP_STROKE_ARC);
message.Add(fToken);
message.Add(rect);
message.Add(angle);
message.Add(span);
}
void
RemoteDrawingEngine::FillArc(BRect rect, const float& angle, const float& span,
const BGradient& gradient)
{
if (!fClippingRegion.Intersects(rect))
return;
RemoteMessage message(NULL, fHWInterface->SendBuffer());
message.Start(RP_FILL_ARC_GRADIENT);
message.Add(fToken);
message.Add(rect);
message.Add(angle);
message.Add(span);
message.AddGradient(gradient);
}
void
RemoteDrawingEngine::DrawBezier(BPoint* points, bool filled)
{
BRect bounds = _BuildBounds(points, 4);
if (!filled)
bounds.InsetBy(fExtendWidth, fExtendWidth);
if (!fClippingRegion.Intersects(bounds))
return;
RemoteMessage message(NULL, fHWInterface->SendBuffer());
message.Start(filled ? RP_FILL_BEZIER : RP_STROKE_BEZIER);
message.Add(fToken);
message.AddList(points, 4);
}
void
RemoteDrawingEngine::FillBezier(BPoint* points, const BGradient& gradient)
{
BRect bounds = _BuildBounds(points, 4);
if (!fClippingRegion.Intersects(bounds))
return;
RemoteMessage message(NULL, fHWInterface->SendBuffer());
message.Start(RP_FILL_BEZIER_GRADIENT);
message.Add(fToken);
message.AddList(points, 4);
message.AddGradient(gradient);
}
void
RemoteDrawingEngine::DrawEllipse(BRect rect, bool filled)
{
BRect bounds = rect;
if (!filled)
bounds.InsetBy(fExtendWidth, fExtendWidth);
if (!fClippingRegion.Intersects(bounds))
return;
RemoteMessage message(NULL, fHWInterface->SendBuffer());
message.Start(filled ? RP_FILL_ELLIPSE : RP_STROKE_ELLIPSE);
message.Add(fToken);
message.Add(rect);
}
void
RemoteDrawingEngine::FillEllipse(BRect rect, const BGradient& gradient)
{
if (!fClippingRegion.Intersects(rect))
return;
RemoteMessage message(NULL, fHWInterface->SendBuffer());
message.Start(RP_FILL_ELLIPSE_GRADIENT);
message.Add(fToken);
message.Add(rect);
message.AddGradient(gradient);
}
void
RemoteDrawingEngine::DrawPolygon(BPoint* pointList, int32 numPoints,
BRect bounds, bool filled, bool closed)
{
BRect clipBounds = bounds;
if (!filled)
clipBounds.InsetBy(fExtendWidth, fExtendWidth);
if (!fClippingRegion.Intersects(clipBounds))
return;
RemoteMessage message(NULL, fHWInterface->SendBuffer());
message.Start(filled ? RP_FILL_POLYGON : RP_STROKE_POLYGON);
message.Add(fToken);
message.Add(bounds);
message.Add(closed);
message.Add(numPoints);
for (int32 i = 0; i < numPoints; i++)
message.Add(pointList[i]);
}
void
RemoteDrawingEngine::FillPolygon(BPoint* pointList, int32 numPoints,
BRect bounds, const BGradient& gradient, bool closed)
{
if (!fClippingRegion.Intersects(bounds))
return;
RemoteMessage message(NULL, fHWInterface->SendBuffer());
message.Start(RP_FILL_POLYGON_GRADIENT);
message.Add(fToken);
message.Add(bounds);
message.Add(closed);
message.Add(numPoints);
for (int32 i = 0; i < numPoints; i++)
message.Add(pointList[i]);
message.AddGradient(gradient);
}
void
RemoteDrawingEngine::StrokePoint(const BPoint& point, const rgb_color& color)
{
BRect bounds(point, point);
bounds.InsetBy(fExtendWidth, fExtendWidth);
if (!fClippingRegion.Intersects(bounds))
return;
RemoteMessage message(NULL, fHWInterface->SendBuffer());
message.Start(RP_STROKE_POINT_COLOR);
message.Add(fToken);
message.Add(point);
message.Add(color);
}
void
RemoteDrawingEngine::StrokeLine(const BPoint& start, const BPoint& end,
const rgb_color& color)
{
BPoint points[2] = { start, end };
BRect bounds = _BuildBounds(points, 2);
if (!fClippingRegion.Intersects(bounds))
return;
RemoteMessage message(NULL, fHWInterface->SendBuffer());
message.Start(RP_STROKE_LINE_1PX_COLOR);
message.Add(fToken);
message.AddList(points, 2);
message.Add(color);
}
void
RemoteDrawingEngine::StrokeRect(BRect rect, const rgb_color &color)
{
BRect bounds = rect;
bounds.InsetBy(fExtendWidth, fExtendWidth);
if (!fClippingRegion.Intersects(bounds))
return;
RemoteMessage message(NULL, fHWInterface->SendBuffer());
message.Start(RP_STROKE_RECT_1PX_COLOR);
message.Add(fToken);
message.Add(rect);
message.Add(color);
}
void
RemoteDrawingEngine::FillRect(BRect rect, const rgb_color& color)
{
if (!fClippingRegion.Intersects(rect))
return;
RemoteMessage message(NULL, fHWInterface->SendBuffer());
message.Start(RP_FILL_RECT_COLOR);
message.Add(fToken);
message.Add(rect);
message.Add(color);
}
void
RemoteDrawingEngine::FillRegion(BRegion& region, const rgb_color& color)
{
RemoteMessage message(NULL, fHWInterface->SendBuffer());
message.Start(RP_FILL_REGION_COLOR_NO_CLIPPING);
message.AddRegion(region);
message.Add(color);
}
void
RemoteDrawingEngine::StrokeRect(BRect rect)
{
BRect bounds = rect;
bounds.InsetBy(fExtendWidth, fExtendWidth);
if (!fClippingRegion.Intersects(bounds))
return;
RemoteMessage message(NULL, fHWInterface->SendBuffer());
message.Start(RP_STROKE_RECT);
message.Add(fToken);
message.Add(rect);
}
void
RemoteDrawingEngine::FillRect(BRect rect)
{
if (!fClippingRegion.Intersects(rect))
return;
RemoteMessage message(NULL, fHWInterface->SendBuffer());
message.Start(RP_FILL_RECT);
message.Add(fToken);
message.Add(rect);
}
void
RemoteDrawingEngine::FillRect(BRect rect, const BGradient& gradient)
{
if (!fClippingRegion.Intersects(rect))
return;
RemoteMessage message(NULL, fHWInterface->SendBuffer());
message.Start(RP_FILL_RECT_GRADIENT);
message.Add(fToken);
message.Add(rect);
message.AddGradient(gradient);
}
void
RemoteDrawingEngine::FillRegion(BRegion& region)
{
BRegion clippedRegion = region;
clippedRegion.IntersectWith(&fClippingRegion);
if (clippedRegion.CountRects() == 0)
return;
RemoteMessage message(NULL, fHWInterface->SendBuffer());
message.Start(RP_FILL_REGION);
message.Add(fToken);
message.AddRegion(clippedRegion.CountRects() < region.CountRects()
? clippedRegion : region);
}
void
RemoteDrawingEngine::FillRegion(BRegion& region, const BGradient& gradient)
{
BRegion clippedRegion = region;
clippedRegion.IntersectWith(&fClippingRegion);
if (clippedRegion.CountRects() == 0)
return;
RemoteMessage message(NULL, fHWInterface->SendBuffer());
message.Start(RP_FILL_REGION_GRADIENT);
message.Add(fToken);
message.AddRegion(clippedRegion.CountRects() < region.CountRects()
? clippedRegion : region);
message.AddGradient(gradient);
}
void
RemoteDrawingEngine::DrawRoundRect(BRect rect, float xRadius, float yRadius,
bool filled)
{
BRect bounds = rect;
if (!filled)
bounds.InsetBy(fExtendWidth, fExtendWidth);
if (!fClippingRegion.Intersects(bounds))
return;
RemoteMessage message(NULL, fHWInterface->SendBuffer());
message.Start(filled ? RP_FILL_ROUND_RECT : RP_STROKE_ROUND_RECT);
message.Add(fToken);
message.Add(rect);
message.Add(xRadius);
message.Add(yRadius);
}
void
RemoteDrawingEngine::FillRoundRect(BRect rect, float xRadius, float yRadius,
const BGradient& gradient)
{
if (!fClippingRegion.Intersects(rect))
return;
RemoteMessage message(NULL, fHWInterface->SendBuffer());
message.Start(RP_FILL_ROUND_RECT_GRADIENT);
message.Add(fToken);
message.Add(rect);
message.Add(xRadius);
message.Add(yRadius);
message.AddGradient(gradient);
}
void
RemoteDrawingEngine::DrawShape(const BRect& bounds, int32 opCount,
const uint32* opList, int32 pointCount, const BPoint* pointList,
bool filled, const BPoint& viewToScreenOffset, float viewScale)
{
BRect clipBounds = bounds;
if (!filled)
clipBounds.InsetBy(fExtendWidth, fExtendWidth);
if (!fClippingRegion.Intersects(clipBounds))
return;
RemoteMessage message(NULL, fHWInterface->SendBuffer());
message.Start(filled ? RP_FILL_SHAPE : RP_STROKE_SHAPE);
message.Add(fToken);
message.Add(bounds);
message.Add(opCount);
message.AddList(opList, opCount);
message.Add(pointCount);
message.AddList(pointList, pointCount);
message.Add(viewToScreenOffset);
message.Add(viewScale);
}
void
RemoteDrawingEngine::FillShape(const BRect& bounds, int32 opCount,
const uint32* opList, int32 pointCount, const BPoint* pointList,
const BGradient& gradient, const BPoint& viewToScreenOffset,
float viewScale)
{
if (!fClippingRegion.Intersects(bounds))
return;
RemoteMessage message(NULL, fHWInterface->SendBuffer());
message.Start(RP_FILL_SHAPE_GRADIENT);
message.Add(fToken);
message.Add(bounds);
message.Add(opCount);
message.AddList(opList, opCount);
message.Add(pointCount);
message.AddList(pointList, pointCount);
message.Add(viewToScreenOffset);
message.Add(viewScale);
message.AddGradient(gradient);
}
void
RemoteDrawingEngine::DrawTriangle(BPoint* points, const BRect& bounds,
bool filled)
{
BRect clipBounds = bounds;
if (!filled)
clipBounds.InsetBy(fExtendWidth, fExtendWidth);
if (!fClippingRegion.Intersects(clipBounds))
return;
RemoteMessage message(NULL, fHWInterface->SendBuffer());
message.Start(filled ? RP_FILL_TRIANGLE : RP_STROKE_TRIANGLE);
message.Add(fToken);
message.AddList(points, 3);
message.Add(bounds);
}
void
RemoteDrawingEngine::FillTriangle(BPoint* points, const BRect& bounds,
const BGradient& gradient)
{
if (!fClippingRegion.Intersects(bounds))
return;
RemoteMessage message(NULL, fHWInterface->SendBuffer());
message.Start(RP_FILL_TRIANGLE_GRADIENT);
message.Add(fToken);
message.AddList(points, 3);
message.Add(bounds);
message.AddGradient(gradient);
}
void
RemoteDrawingEngine::StrokeLine(const BPoint &start, const BPoint &end)
{
BPoint points[2] = { start, end };
BRect bounds = _BuildBounds(points, 2);
if (!fClippingRegion.Intersects(bounds))
return;
RemoteMessage message(NULL, fHWInterface->SendBuffer());
message.Start(RP_STROKE_LINE);
message.Add(fToken);
message.AddList(points, 2);
}
void
RemoteDrawingEngine::StrokeLineArray(int32 numLines,
const ViewLineArrayInfo *lineData)
{
RemoteMessage message(NULL, fHWInterface->SendBuffer());
message.Start(RP_STROKE_LINE_ARRAY);
message.Add(fToken);
message.Add(numLines);
for (int32 i = 0; i < numLines; i++)
message.AddArrayLine(lineData[i]);
}
BPoint
RemoteDrawingEngine::DrawString(const char* string, int32 length,
const BPoint& point, escapement_delta* delta)
{
RemoteMessage message(NULL, fHWInterface->SendBuffer());
message.Start(RP_DRAW_STRING);
message.Add(fToken);
message.Add(point);
message.AddString(string, length);
message.Add(delta != NULL);
if (delta != NULL)
message.AddList(delta, length);
status_t result = _AddCallback();
if (message.Flush() != B_OK)
return point;
if (result != B_OK)
return point;
do {
result = acquire_sem_etc(fResultNotify, 1, B_RELATIVE_TIMEOUT,
1 * 1000 * 1000);
} while (result == B_INTERRUPTED);
if (result != B_OK)
return point;
return fDrawStringResult;
}
BPoint
RemoteDrawingEngine::DrawString(const char* string, int32 length,
const BPoint* offsets)
{
RemoteMessage message(NULL, fHWInterface->SendBuffer());
message.Start(RP_DRAW_STRING_WITH_OFFSETS);
message.Add(fToken);
message.AddString(string, length);
message.AddList(offsets, UTF8CountChars(string, length));
status_t result = _AddCallback();
if (message.Flush() != B_OK)
return offsets[0];
if (result != B_OK)
return offsets[0];
do {
result = acquire_sem_etc(fResultNotify, 1, B_RELATIVE_TIMEOUT,
1 * 1000 * 1000);
} while (result == B_INTERRUPTED);
if (result != B_OK)
return offsets[0];
return fDrawStringResult;
}
float
RemoteDrawingEngine::StringWidth(const char* string, int32 length,
escapement_delta* delta)
{
while (true) {
if (_AddCallback() != B_OK)
break;
RemoteMessage message(NULL, fHWInterface->SendBuffer());
message.Start(RP_STRING_WIDTH);
message.Add(fToken);
message.AddString(string, length);
if (message.Flush() != B_OK)
break;
status_t result;
do {
result = acquire_sem_etc(fResultNotify, 1, B_RELATIVE_TIMEOUT,
1 * 1000 * 1000);
} while (result == B_INTERRUPTED);
if (result != B_OK)
break;
return fStringWidthResult;
}
return fState.Font().StringWidth(string, length, delta);
}
status_t
RemoteDrawingEngine::ReadBitmap(ServerBitmap* bitmap, bool drawCursor,
BRect bounds)
{
if (_AddCallback() != B_OK)
return B_UNSUPPORTED;
RemoteMessage message(NULL, fHWInterface->SendBuffer());
message.Start(RP_READ_BITMAP);
message.Add(fToken);
message.Add(bounds);
message.Add(drawCursor);
if (message.Flush() != B_OK)
return B_UNSUPPORTED;
status_t result;
do {
result = acquire_sem_etc(fResultNotify, 1, B_RELATIVE_TIMEOUT,
10 * 1000 * 1000);
} while (result == B_INTERRUPTED);
if (result != B_OK)
return result;
BBitmap* read = fReadBitmapResult;
if (read == NULL)
return B_UNSUPPORTED;
result = bitmap->ImportBits(read->Bits(), read->BitsLength(),
read->BytesPerRow(), read->ColorSpace());
delete read;
return result;
}
status_t
RemoteDrawingEngine::_AddCallback()
{
if (fCallbackAdded)
return B_OK;
if (fResultNotify < 0)
fResultNotify = create_sem(0, "drawing engine result");
if (fResultNotify < 0)
return fResultNotify;
status_t result = fHWInterface->AddCallback(fToken, &_DrawingEngineResult,
this);
fCallbackAdded = result == B_OK;
return result;
}
bool
RemoteDrawingEngine::_DrawingEngineResult(void* cookie, RemoteMessage& message)
{
RemoteDrawingEngine* engine = (RemoteDrawingEngine*)cookie;
switch (message.Code()) {
case RP_DRAW_STRING_RESULT:
{
status_t result = message.Read(engine->fDrawStringResult);
if (result != B_OK) {
TRACE_ERROR("failed to read draw string result: %s\n",
strerror(result));
return false;
}
break;
}
case RP_STRING_WIDTH_RESULT:
{
status_t result = message.Read(engine->fStringWidthResult);
if (result != B_OK) {
TRACE_ERROR("failed to read string width result: %s\n",
strerror(result));
return false;
}
break;
}
case RP_READ_BITMAP_RESULT:
{
status_t result = message.ReadBitmap(&engine->fReadBitmapResult);
if (result != B_OK) {
TRACE_ERROR("failed to read bitmap of read bitmap result: %s\n",
strerror(result));
return false;
}
break;
}
default:
return false;
}
release_sem(engine->fResultNotify);
return true;
}
BRect
RemoteDrawingEngine::_BuildBounds(BPoint* points, int32 pointCount)
{
BRect bounds(1000000, 1000000, 0, 0);
for (int32 i = 0; i < pointCount; i++) {
bounds.left = min_c(bounds.left, points[i].x);
bounds.top = min_c(bounds.top, points[i].y);
bounds.right = max_c(bounds.right, points[i].x);
bounds.bottom = max_c(bounds.bottom, points[i].y);
}
return bounds;
}
status_t
RemoteDrawingEngine::_ExtractBitmapRegions(ServerBitmap& bitmap, uint32 options,
const BRect& bitmapRect, const BRect& viewRect, double xScale,
double yScale, BRegion& region, UtilityBitmap**& bitmaps)
{
int32 rectCount = region.CountRects();
bitmaps = (UtilityBitmap**)malloc(rectCount * sizeof(UtilityBitmap*));
if (bitmaps == NULL)
return B_NO_MEMORY;
for (int32 i = 0; i < rectCount; i++) {
BRect sourceRect = region.RectAt(i).OffsetByCopy(-viewRect.LeftTop());
int32 targetWidth = (int32)(sourceRect.Width() + 1.5);
int32 targetHeight = (int32)(sourceRect.Height() + 1.5);
if (xScale != 1.0) {
sourceRect.left = (int32)(sourceRect.left * xScale + 0.5);
sourceRect.right = (int32)(sourceRect.right * xScale + 0.5);
if (xScale < 1.0)
targetWidth = (int32)(sourceRect.Width() + 1.5);
}
if (yScale != 1.0) {
sourceRect.top = (int32)(sourceRect.top * yScale + 0.5);
sourceRect.bottom = (int32)(sourceRect.bottom * yScale + 0.5);
if (yScale < 1.0)
targetHeight = (int32)(sourceRect.Height() + 1.5);
}
sourceRect.OffsetBy(bitmapRect.LeftTop());
status_t result = B_OK;
if ((xScale > 1.0 || yScale > 1.0)
&& (targetWidth * targetHeight < (int32)(sourceRect.Width() + 1.5)
* (int32)(sourceRect.Height() + 1.5))) {
if (!fBitmapDrawingEngine.IsSet()) {
fBitmapDrawingEngine.SetTo(
new(std::nothrow) BitmapDrawingEngine(B_RGBA32));
if (!fBitmapDrawingEngine.IsSet())
result = B_NO_MEMORY;
}
if (result == B_OK) {
result = fBitmapDrawingEngine->SetSize(targetWidth,
targetHeight);
}
if (result == B_OK) {
fBitmapDrawingEngine->SetDrawingMode(B_OP_COPY);
switch (bitmap.ColorSpace()) {
case B_RGBA32:
case B_RGBA32_BIG:
case B_RGBA15:
case B_RGBA15_BIG:
break;
default:
{
rgb_color background = { 0, 0, 0, 0 };
fBitmapDrawingEngine->FillRect(
BRect(0, 0, targetWidth - 1, targetHeight -1),
background);
fBitmapDrawingEngine->SetDrawingMode(B_OP_OVER);
break;
}
}
fBitmapDrawingEngine->DrawBitmap(&bitmap, sourceRect,
BRect(0, 0, targetWidth - 1, targetHeight - 1), options);
bitmaps[i] = fBitmapDrawingEngine->ExportToBitmap(targetWidth,
targetHeight, bitmap.ColorSpace());
if (bitmaps[i] == NULL)
result = B_NO_MEMORY;
}
} else {
targetWidth = (int32)(sourceRect.Width() + 1.5);
targetHeight = (int32)(sourceRect.Height() + 1.5);
bitmaps[i] = new(std::nothrow) UtilityBitmap(
BRect(0, 0, targetWidth - 1, targetHeight - 1),
bitmap.ColorSpace(), 0);
if (bitmaps[i] == NULL) {
result = B_NO_MEMORY;
} else {
result = bitmaps[i]->ImportBits(bitmap.Bits(),
bitmap.BitsLength(), bitmap.BytesPerRow(),
bitmap.ColorSpace(), sourceRect.LeftTop(),
BPoint(0, 0), targetWidth, targetHeight);
if (result != B_OK) {
delete bitmaps[i];
bitmaps[i] = NULL;
}
}
}
if (result != B_OK) {
for (int32 j = 0; j < i; j++)
delete bitmaps[j];
free(bitmaps);
return result;
}
}
return B_OK;
}