diff --git a/src/servers/app/drawing/remote/RemoteDrawingEngine.cpp b/src/servers/app/drawing/remote/RemoteDrawingEngine.cpp index 4feabff161..adfa74975f 100644 --- a/src/servers/app/drawing/remote/RemoteDrawingEngine.cpp +++ b/src/servers/app/drawing/remote/RemoteDrawingEngine.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2009, Haiku, Inc. + * Copyright 2009-2010, Haiku, Inc. * Distributed under the terms of the MIT License. * * Authors: @@ -9,6 +9,7 @@ #include "RemoteDrawingEngine.h" #include "RemoteMessage.h" +#include "BitmapDrawingEngine.h" #include "DrawState.h" #include @@ -24,7 +25,8 @@ RemoteDrawingEngine::RemoteDrawingEngine(RemoteHWInterface* interface) fToken((uint32)this), // TODO: need to redo that for 64 bit fExtendWidth(0), fCallbackAdded(false), - fResultNotify(-1) + fResultNotify(-1), + fBitmapDrawingEngine(NULL) { RemoteMessage message(NULL, fHWInterface->SendBuffer()); message.Start(RP_CREATE_STATE); @@ -39,6 +41,8 @@ RemoteDrawingEngine::~RemoteDrawingEngine() message.Add(fToken); message.Flush(); + delete fBitmapDrawingEngine; + if (fCallbackAdded) fHWInterface->RemoveCallback(fToken); if (fResultNotify >= 0) @@ -288,47 +292,67 @@ void RemoteDrawingEngine::DrawBitmap(ServerBitmap* bitmap, const BRect& _bitmapRect, const BRect& _viewRect, uint32 options) { - if (!fClippingRegion.Intersects(_viewRect)) - return; - - BRect viewRect = _viewRect; BRect bitmapRect = _bitmapRect; - if (bitmapRect.IntegerWidth() == viewRect.IntegerWidth() - && bitmapRect.IntegerHeight() == viewRect.IntegerHeight()) { - // unscaled bitmap we can chop off stuff we don't need - BRegion target(viewRect); - target.IntersectWith(&fClippingRegion); - BRect frame = target.Frame(); + BRect viewRect = _viewRect; + double xScale = (bitmapRect.Width() + 1) / (viewRect.Width() + 1); + double yScale = (bitmapRect.Height() + 1) / (viewRect.Height() + 1); - if (frame != viewRect) { - BPoint offset = frame.LeftTop() - viewRect.LeftTop(); - viewRect = frame; - bitmapRect = viewRect.OffsetToCopy(bitmapRect.LeftTop() + offset); - } + // constrain rect to passed bitmap bounds + // and transfer the changes to the viewRect with the right scale + 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; } - UtilityBitmap* other = NULL; - BRect bounds = bitmap->Bounds(); - BRect newBounds; - newBounds.right - = min_c(bounds.IntegerWidth(), bitmapRect.IntegerWidth()); - newBounds.bottom - = min_c(bounds.IntegerHeight(), bitmapRect.IntegerHeight()); + BRegion clippedRegion(viewRect); + clippedRegion.IntersectWith(&fClippingRegion); - if (newBounds.IntegerWidth() < bounds.IntegerWidth() - || newBounds.IntegerHeight() < bounds.IntegerHeight()) { + int32 rectCount = clippedRegion.CountRects(); + if (rectCount == 0) + return; - other = new(std::nothrow) UtilityBitmap(newBounds, bitmap->ColorSpace(), - bitmap->Flags()); - - if (other != NULL && other->ImportBits(bitmap->Bits(), - bitmap->BitsLength(), bitmap->BytesPerRow(), - bitmap->ColorSpace(), bitmapRect.LeftTop(), BPoint(0, 0), - newBounds.IntegerWidth() + 1, - newBounds.IntegerHeight() + 1) == B_OK) { - bitmapRect.OffsetTo(0, 0); - bitmap = other; + 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; } // TODO: we may want to cache/checksum bitmaps @@ -339,9 +363,6 @@ RemoteDrawingEngine::DrawBitmap(ServerBitmap* bitmap, const BRect& _bitmapRect, message.Add(viewRect); message.Add(options); message.AddBitmap(*bitmap); - - if (other != NULL) - delete other; } @@ -972,3 +993,120 @@ RemoteDrawingEngine::_BuildBounds(BPoint* points, int32 pointCount) 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()); + // sourceRect is now the part of the bitmap we want copied + + status_t result = B_OK; + if ((xScale > 1.0 || yScale > 1.0) + && (targetWidth * targetHeight < (int32)(sourceRect.Width() + 1.5) + * (int32)(sourceRect.Height() + 1.5))) { + // the target bitmap is smaller than the source, scale it locally + // and send over the smaller version to avoid sending any extra data + if (fBitmapDrawingEngine == NULL) { + fBitmapDrawingEngine + = new(std::nothrow) BitmapDrawingEngine(B_RGBA32); + if (fBitmapDrawingEngine == NULL) + 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: + { + // we need to clear the background if there may be + // transparency through transparent magic (we use + // B_OP_COPY when we draw alpha enabled bitmaps, so we + // don't need to clear there) + // TODO: this is not actually correct, as we're going to + // loose the transparency with the conversion to the + // original non-alpha colorspace happening in + // ExportToBitmap + 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 { + // source is smaller or equal target, extract the relevant rects + // directly without any scaling and conversion + 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; + + 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]; + } + + if (result != B_OK) { + for (int32 j = 0; j < i; j++) + delete bitmaps[j]; + free(bitmaps); + return result; + } + } + + return B_OK; +} diff --git a/src/servers/app/drawing/remote/RemoteDrawingEngine.h b/src/servers/app/drawing/remote/RemoteDrawingEngine.h index fc2b97e6ab..60f3f210c1 100644 --- a/src/servers/app/drawing/remote/RemoteDrawingEngine.h +++ b/src/servers/app/drawing/remote/RemoteDrawingEngine.h @@ -13,6 +13,13 @@ #include "RemoteHWInterface.h" #include "ServerFont.h" +class BPoint; +class BRect; +class BRegion; + +class BitmapDrawingEngine; +class ServerBitmap; + class RemoteDrawingEngine : public DrawingEngine { public: RemoteDrawingEngine( @@ -99,18 +106,18 @@ public: float yRadius, const BGradient& gradient); virtual void DrawShape(const BRect& bounds, - int32 opCount, const uint32* opList, + int32 opCount, const uint32* opList, int32 pointCount, const BPoint* pointList, bool filled, const BPoint& viewToScreenOffset, float viewScale); virtual void FillShape(const BRect& bounds, - int32 opCount, const uint32* opList, + int32 opCount, const uint32* opList, int32 pointCount, const BPoint* pointList, const BGradient& gradient, const BPoint& viewToScreenOffset, float viewScale); - + virtual void DrawTriangle(BPoint* points, const BRect& bounds, bool filled); virtual void FillTriangle(BPoint* points, @@ -147,6 +154,11 @@ private: RemoteMessage& message); BRect _BuildBounds(BPoint* points, int32 pointCount); + status_t _ExtractBitmapRegions(ServerBitmap& bitmap, + uint32 options, const BRect& bitmapRect, + const BRect& viewRect, double xScale, + double yScale, BRegion& region, + UtilityBitmap**& bitmaps); RemoteHWInterface* fHWInterface; uint32 fToken; @@ -160,6 +172,8 @@ private: BPoint fDrawStringResult; float fStringWidthResult; BBitmap* fReadBitmapResult; + + BitmapDrawingEngine* fBitmapDrawingEngine; }; #endif // REMOTE_DRAWING_ENGINE_H