From dd98ed8dfcfe69c729b06f4d3deb9cbc82015552 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20A=C3=9Fmus?= Date: Wed, 19 Apr 2006 14:12:57 +0000 Subject: [PATCH] * implemented view bitmap options (B_BITMAP_TILE...) in ViewLayer (for example, fixes NetPositive rendering HTML with a background image) * use BRegion pool everywhere in ViewLayer * WindowLayer update sessions distinguish between different reasons for the update: exposed and requested - on expose updates, the view backgrounds are cleared immidiately (as on R5), to keep the time previous stuff keeps showing as short as possible, while on requested updates, the background clearing is delayed until the client draws something, to keep the time until the client fills a view with content as small as possible to reduce flickering (might need more work, could be buggy yet) * HWInterface and DrawingEngine support delayed syncing to the graphics hardware at least for FillRect/Region. The speed up gained by this is minor though. * HWInterface cursor rendering uses a bit of rounding to avoid the slight transparent shadow around the cursor (I don't know if it is fully correct though, at least the shasow disappeared) git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@17172 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/servers/app/ViewLayer.cpp | 137 +++++++++++---- src/servers/app/WindowLayer.cpp | 87 ++++++---- src/servers/app/WindowLayer.h | 13 ++ .../app/drawing/AccelerantHWInterface.cpp | 13 +- .../app/drawing/AccelerantHWInterface.h | 5 +- .../app/drawing/DWindowHWInterface.cpp | 12 +- src/servers/app/drawing/DWindowHWInterface.h | 5 +- src/servers/app/drawing/DrawingEngine.cpp | 69 ++++++-- src/servers/app/{ => drawing}/DrawingEngine.h | 4 + src/servers/app/drawing/HWInterface.cpp | 22 ++- src/servers/app/drawing/HWInterface.h | 7 +- src/servers/app/drawing/Jamfile | 1 + src/servers/app/drawing/Painter/Painter.cpp | 14 +- .../Painter/drawing_modes/DrawingMode.h | 2 +- src/servers/app/drawing/drawing_support.cpp | 13 ++ ...ame_buffer_support.h => drawing_support.h} | 3 + src/tests/servers/app/Jamfile | 2 + src/tests/servers/app/following/Jamfile | 17 ++ src/tests/servers/app/following/main.cpp | 164 ++++++++++++++++++ src/tests/servers/app/following/run | 18 ++ 20 files changed, 502 insertions(+), 106 deletions(-) rename src/servers/app/{ => drawing}/DrawingEngine.h (98%) create mode 100644 src/servers/app/drawing/drawing_support.cpp rename src/servers/app/drawing/{frame_buffer_support.h => drawing_support.h} (98%) create mode 100644 src/tests/servers/app/following/Jamfile create mode 100644 src/tests/servers/app/following/main.cpp create mode 100755 src/tests/servers/app/following/run diff --git a/src/servers/app/ViewLayer.cpp b/src/servers/app/ViewLayer.cpp index 09479298a1..11e96a64f5 100644 --- a/src/servers/app/ViewLayer.cpp +++ b/src/servers/app/ViewLayer.cpp @@ -22,6 +22,8 @@ #include "ServerWindow.h" #include "WindowLayer.h" +#include "drawing_support.h" + #include #include #include @@ -809,14 +811,16 @@ ViewLayer::ScrollBy(int32 x, int32 y, BRegion* dirtyRegion) // do the blit, this will make sure // that other more complex dirty regions // are taken care of - BRegion copyRegion(stillVisibleBounds); - fWindow->CopyContents(©Region, -x, -y); + BRegion* copyRegion = fWindow->GetRegion(); + if (!copyRegion) + return; + copyRegion->Set(stillVisibleBounds); + fWindow->CopyContents(copyRegion, -x, -y); // find the dirty region as far as we are // concerned - BRegion* dirty = fWindow->GetRegion(); - if (!dirty) - return; + BRegion* dirty = copyRegion; + // reuse copyRegion and call it dirty dirty->Set(oldBounds); stillVisibleBounds.OffsetBy(-x, -y); @@ -860,17 +864,20 @@ ViewLayer::CopyBits(BRect src, BRect dst, BRegion& windowContentClipping) // do the blit, this will make sure // that other more complex dirty regions // are taken care of - BRegion copyRegion(visibleSrc); - copyRegion.IntersectWith(&ScreenClipping(&windowContentClipping)); - fWindow->CopyContents(©Region, xOffset, yOffset); + BRegion* copyRegion = fWindow->GetRegion(); + if (!copyRegion) + return; + + copyRegion->Set(visibleSrc); + copyRegion->IntersectWith(&ScreenClipping(&windowContentClipping)); + fWindow->CopyContents(copyRegion, xOffset, yOffset); // find the dirty region as far as we are concerned BRect dirtyDst(dst); ConvertToVisibleInTopView(&dirtyDst); - BRegion* dirty = fWindow->GetRegion(); - if (!dirty) - return; + BRegion* dirty = copyRegion; + // reuse copyRegion and call it "dirty" dirty->Set(dirtyDst); // exclude the part that we could copy @@ -965,9 +972,11 @@ ViewLayer::Draw(DrawingEngine* drawingEngine, BRegion* effectiveClipping, if (fViewBitmap != NULL || !fViewColor.IsTransparentMagic()) { // we can only draw within our own area - BRegion redraw(ScreenClipping(windowContentClipping)); + BRegion* redraw = fWindow->GetRegion(ScreenClipping(windowContentClipping)); + if (!redraw) + return; // add the current clipping - redraw.IntersectWith(effectiveClipping); + redraw->IntersectWith(effectiveClipping); if (fViewBitmap != NULL) { // draw view bitmap @@ -975,20 +984,79 @@ ViewLayer::Draw(DrawingEngine* drawingEngine, BRegion* effectiveClipping, BRect rect = fBitmapDestination; ConvertToScreenForDrawing(&rect); - // lock the drawing engine for as long as we need the clipping - // to be valid - if (drawingEngine->Lock()) { - drawingEngine->ConstrainClippingRegion(&redraw); + align_rect_to_pixels(&rect); - DrawState defaultDrawState; - drawingEngine->DrawBitmap(fViewBitmap, fBitmapSource, - rect, &defaultDrawState); - // NOTE: It is ok not to reset the clipping, that - // would only waste time - drawingEngine->Unlock(); + if (fBitmapOptions & B_TILE_BITMAP_Y) { + // move rect up as much as needed + while (rect.top > redraw->Frame().top) + rect.OffsetBy(0.0, -(rect.Height() + 1)); + } + if (fBitmapOptions & B_TILE_BITMAP_X) { + // move rect left as much as needed + while (rect.left > redraw->Frame().left) + rect.OffsetBy(-(rect.Width() + 1), 0.0); + } + +// XXX: locking removed because the WindowLayer keeps the engine locked +// because it keeps track of syncing right now + + // lock the drawing engine for as long as we need the clipping + // to be valid + if (rect.IsValid()/* && drawingEngine->Lock()*/) { + drawingEngine->ConstrainClippingRegion(redraw); + + DrawState defaultDrawState; + + if (fBitmapOptions & B_TILE_BITMAP) { + // tile across entire view + float start = rect.left; + while (rect.top < redraw->Frame().bottom) { + while (rect.left < redraw->Frame().right) { + drawingEngine->DrawBitmap(fViewBitmap, fBitmapSource, + rect, &defaultDrawState); + rect.OffsetBy(rect.Width() + 1, 0.0); + } + rect.OffsetBy(start - rect.left, rect.Height() + 1); + } + // nothing left to be drawn + redraw->MakeEmpty(); + + } else if (fBitmapOptions & B_TILE_BITMAP_X) { + // tile in x direction + while (rect.left < redraw->Frame().right) { + drawingEngine->DrawBitmap(fViewBitmap, fBitmapSource, + rect, &defaultDrawState); + rect.OffsetBy(rect.Width() + 1, 0.0); + } + // remove horizontal stripe from clipping + rect.left = redraw->Frame().left; + rect.right = redraw->Frame().right; + redraw->Exclude(rect); + + } else if (fBitmapOptions & B_TILE_BITMAP_Y) { + // tile in y direction + while (rect.top < redraw->Frame().bottom) { + drawingEngine->DrawBitmap(fViewBitmap, fBitmapSource, + rect, &defaultDrawState); + rect.OffsetBy(0.0, rect.Height() + 1); + } + // remove vertical stripe from clipping + rect.top = redraw->Frame().top; + rect.bottom = redraw->Frame().bottom; + redraw->Exclude(rect); + + } else { + // no tiling at all + drawingEngine->DrawBitmap(fViewBitmap, fBitmapSource, + rect, &defaultDrawState); + redraw->Exclude(rect); + } + + // NOTE: It is ok not to reset the clipping, that + // would only waste time +// drawingEngine->Unlock(); } - redraw.Exclude(rect); } if (!fViewColor.IsTransparentMagic()) { @@ -996,8 +1064,10 @@ ViewLayer::Draw(DrawingEngine* drawingEngine, BRegion* effectiveClipping, // this version of FillRegion ignores any // clipping, that's why "redraw" needs to // be correct - drawingEngine->FillRegion(redraw, fViewColor); + drawingEngine->FillRegion(*redraw, fViewColor); } + + fWindow->RecycleRegion(redraw); } fBackgroundDirty = false; @@ -1176,9 +1246,12 @@ ViewLayer::RebuildClipping(bool deep) // hand, views for which this feature is actually used will // probably not have any children, so it is not that expensive // after all - BRegion screenUserClipping(*userClipping); - fDrawState->Transform(&screenUserClipping); - fLocalClipping.IntersectWith(&screenUserClipping); + BRegion* screenUserClipping = fWindow->GetRegion(*userClipping); + if (!screenUserClipping) + return; + fDrawState->Transform(screenUserClipping); + fLocalClipping.IntersectWith(screenUserClipping); + fWindow->RecycleRegion(screenUserClipping); } fScreenClippingValid = false; @@ -1198,8 +1271,12 @@ ViewLayer::ScreenClipping(BRegion* windowContentClipping, bool force) const ConvertToVisibleInTopView(&clippedBounds); if (clippedBounds.Width() < fScreenClipping.Frame().Width() || clippedBounds.Height() < fScreenClipping.Frame().Height()) { - BRegion temp(clippedBounds); - fScreenClipping.IntersectWith(&temp); + BRegion* temp = fWindow->GetRegion(); + if (temp) { + temp->Set(clippedBounds); + fScreenClipping.IntersectWith(temp); + fWindow->RecycleRegion(temp); + } } fScreenClipping.IntersectWith(windowContentClipping); diff --git a/src/servers/app/WindowLayer.cpp b/src/servers/app/WindowLayer.cpp index 10b7eb077c..7be1ea018d 100644 --- a/src/servers/app/WindowLayer.cpp +++ b/src/servers/app/WindowLayer.cpp @@ -65,7 +65,6 @@ using std::nothrow; // its previous position though if the exposed parts are not // cleared right away. maybe there ought to be a flag in // the update session, which tells us the cause of the update -#define DELAYED_BACKGROUND_CLEARING 1 WindowLayer::WindowLayer(const BRect& frame, const char *name, @@ -81,6 +80,7 @@ WindowLayer::WindowLayer(const BRect& frame, const char *name, fVisibleContentRegion(), fVisibleContentRegionValid(false), fDirtyRegion(), + fDirtyCause(0), fBorderRegion(), fBorderRegionValid(false), @@ -609,8 +609,8 @@ WindowLayer::ProcessDirtyRegion(BRegion& region) ServerWindow()->RequestRedraw(); } - // this is executed from the desktop thread fDirtyRegion.Include(®ion); + fDirtyCause |= UPDATE_EXPOSE; } @@ -639,6 +639,7 @@ WindowLayer::RedrawDirtyRegion() // get write access, since we're holding // the read lock for the whole time. fDirtyRegion.MakeEmpty(); + fDirtyCause = 0; } @@ -665,6 +666,7 @@ WindowLayer::MarkContentDirty(BRegion& regionOnScreen) return; regionOnScreen.IntersectWith(&VisibleContentRegion()); + fDirtyCause |= UPDATE_REQUEST; _TriggerContentRedraw(regionOnScreen); } @@ -684,7 +686,7 @@ WindowLayer::InvalidateView(ViewLayer* layer, BRegion& layerRegion) //fDrawingEngine->FillRegion(layerRegion, RGBColor(0, 255, 0, 255)); //snooze(10000); - + fDirtyCause |= UPDATE_REQUEST; _TriggerContentRedraw(layerRegion); } } @@ -1621,25 +1623,32 @@ WindowLayer::_TriggerContentRedraw(BRegion& dirtyContentRegion) if (IsVisible() && dirtyContentRegion.CountRects() > 0) { // put this into the pending dirty region // to eventually trigger a client redraw + bool wasExpose = fPendingUpdateSession.IsExpose(); + BRegion* backgroundClearingRegion = &dirtyContentRegion; + _TransferToUpdateSession(&dirtyContentRegion); -#if DELAYED_BACKGROUND_CLEARING -// NOTE: currently not used, might come in handy later though -// if (!fTopLayer->IsBackgroundDirty()) -// fTopLayer->MarkBackgroundDirty(); -#else -// NOTE: turning off DELAYED_BACKGROUND_CLEARING will -// need investigation if it even still works... - if (!fContentRegionValid) - _UpdateContentRegion(); + if (fPendingUpdateSession.IsExpose()) { + if (!fContentRegionValid) + _UpdateContentRegion(); - if (fDrawingEngine->Lock()) { - fDrawingEngine->ConstrainClippingRegion(&dirtyContentRegion); - fTopLayer->Draw(fDrawingEngine, &dirtyContentRegion, - &fContentRegion, true); - fDrawingEngine->Unlock(); + if (!wasExpose) { + // there was suddenly added a dirty region + // caused by exposing content, we need to clear + // the entire background + backgroundClearingRegion = &fPendingUpdateSession.DirtyRegion(); + } + + if (fDrawingEngine->Lock()) { + fDrawingEngine->SuspendAutoSync(); + + fTopLayer->Draw(fDrawingEngine, backgroundClearingRegion, + &fContentRegion, true); + + fDrawingEngine->Sync(); + fDrawingEngine->Unlock(); + } } -#endif } } @@ -1691,6 +1700,8 @@ WindowLayer::_TransferToUpdateSession(BRegion* contentDirtyRegion) // add to pending fPendingUpdateSession.SetUsed(true); +// if (!fPendingUpdateSession.IsExpose()) + fPendingUpdateSession.AddCause(fDirtyCause); fPendingUpdateSession.Include(contentDirtyRegion); // clip pending update session from current @@ -1699,14 +1710,10 @@ WindowLayer::_TransferToUpdateSession(BRegion* contentDirtyRegion) // this could be done smarter (clip layers from pending // that have not yet been redrawn in the current update // session) -#if !DELAYED_BACKGROUND_CLEARING -// NOTE: turning off DELAYED_BACKGROUND_CLEARING will -// need investigation if it even still works... - if (fCurrentUpdateSession.IsUsed()) { + if (fCurrentUpdateSession.IsUsed() && fCurrentUpdateSession.IsExpose()) { fCurrentUpdateSession.Exclude(contentDirtyRegion); fEffectiveDrawingRegionValid = false; } -#endif if (!fUpdateRequested) { // send this to client @@ -1768,8 +1775,6 @@ WindowLayer::BeginUpdate(BPrivate::PortLink& link) dirty->IntersectWith(&VisibleContentRegion()); -//fDrawingEngine->FillRegion(dirty, RGBColor(255, 0, 0, 255)); - link.StartMessage(B_OK); // append the current window geometry to the // message, the client will need it @@ -1785,14 +1790,19 @@ WindowLayer::BeginUpdate(BPrivate::PortLink& link) link.Attach(B_NULL_TOKEN); link.Flush(); -#if DELAYED_BACKGROUND_CLEARING -// NOTE: turning off DELAYED_BACKGROUND_CLEARING will -// need investigation if it even still works... - fTopLayer->Draw(fDrawingEngine, dirty, - &fContentRegion, true); + if (!fCurrentUpdateSession.IsExpose() && fDrawingEngine->Lock()) { +//fDrawingEngine->FillRegion(dirty, RGBColor(255, 0, 0, 255)); + fDrawingEngine->SuspendAutoSync(); + + fTopLayer->Draw(fDrawingEngine, dirty, + &fContentRegion, true); + + fDrawingEngine->Sync(); + fDrawingEngine->Unlock(); + } // else the background was cleared already fRegionPool.Recycle(dirty); -#endif + } else { printf("BeginUpdate() but no update requested!!\n"); link.StartMessage(B_ERROR); @@ -1908,7 +1918,8 @@ WindowLayer::_ObeySizeLimits() // constructor WindowLayer::UpdateSession::UpdateSession() : fDirtyRegion(), - fInUse(false) + fInUse(false), + fCause(0) { } @@ -1943,8 +1954,17 @@ void WindowLayer::UpdateSession::SetUsed(bool used) { fInUse = used; - if (!fInUse) + if (!fInUse) { fDirtyRegion.MakeEmpty(); + fCause = 0; + } +} + + +void +WindowLayer::UpdateSession::AddCause(uint8 cause) +{ + fCause |= cause; } @@ -1953,6 +1973,7 @@ WindowLayer::UpdateSession::operator=(const WindowLayer::UpdateSession& other) { fDirtyRegion = other.fDirtyRegion; fInUse = other.fInUse; + fCause = other.fCause; return *this; } diff --git a/src/servers/app/WindowLayer.h b/src/servers/app/WindowLayer.h index 99eb5936bb..6db8efeca7 100644 --- a/src/servers/app/WindowLayer.h +++ b/src/servers/app/WindowLayer.h @@ -35,6 +35,11 @@ class WindowLayer; // TODO: move this into a proper place #define AS_REDRAW 'rdrw' +enum { + UPDATE_REQUEST = 0x01, + UPDATE_EXPOSE = 0x02, +}; + class WindowLayer { public: WindowLayer(const BRect& frame, @@ -248,6 +253,7 @@ class WindowLayer { // the clipping, since it is local and the desktop // thread is blocked BRegion fDirtyRegion; + uint32 fDirtyCause; // caching local regions BRegion fBorderRegion; @@ -301,12 +307,19 @@ class WindowLayer { void SetUsed(bool used); inline bool IsUsed() const { return fInUse; } + + void AddCause(uint8 cause); + inline bool IsExpose() const + { return fCause & UPDATE_EXPOSE; } + inline bool IsRequest() const + { return fCause & UPDATE_REQUEST; } UpdateSession& operator=(const UpdateSession& other); private: BRegion fDirtyRegion; bool fInUse; + uint8 fCause; }; BRegion fDecoratorRegion; diff --git a/src/servers/app/drawing/AccelerantHWInterface.cpp b/src/servers/app/drawing/AccelerantHWInterface.cpp index 0434e058c6..5db2de2ab1 100644 --- a/src/servers/app/drawing/AccelerantHWInterface.cpp +++ b/src/servers/app/drawing/AccelerantHWInterface.cpp @@ -733,7 +733,8 @@ AccelerantHWInterface::CopyRegion(const clipping_rect* sortedRectList, // FillRegion void -AccelerantHWInterface::FillRegion(/*const*/ BRegion& region, const RGBColor& color) +AccelerantHWInterface::FillRegion(/*const*/ BRegion& region, const RGBColor& color, + bool autoSync) { if (fAccFillRect && fAccAcquireEngine) { if (fAccAcquireEngine(B_2D_ACCELERATION, 0xff, &fSyncToken, &fEngineToken) >= B_OK) { @@ -750,7 +751,7 @@ AccelerantHWInterface::FillRegion(/*const*/ BRegion& region, const RGBColor& col fAccReleaseEngine(fEngineToken, &fSyncToken); // sync - if (fAccSyncToToken) + if (autoSync && fAccSyncToToken) fAccSyncToToken(&fSyncToken); } } @@ -781,6 +782,14 @@ AccelerantHWInterface::InvertRegion(/*const*/ BRegion& region) } } +// Sync +void +AccelerantHWInterface::Sync() +{ + if (fAccSyncToToken) + fAccSyncToToken(&fSyncToken); +} + // SetCursor void AccelerantHWInterface::SetCursor(ServerCursor* cursor) diff --git a/src/servers/app/drawing/AccelerantHWInterface.h b/src/servers/app/drawing/AccelerantHWInterface.h index 29d9f6bdf5..5a03903f92 100644 --- a/src/servers/app/drawing/AccelerantHWInterface.h +++ b/src/servers/app/drawing/AccelerantHWInterface.h @@ -59,9 +59,12 @@ public: uint32 count, int32 xOffset, int32 yOffset); virtual void FillRegion(/*const*/ BRegion& region, - const RGBColor& color); + const RGBColor& color, + bool autoSync); virtual void InvertRegion(/*const*/ BRegion& region); + virtual void Sync(); + // cursor handling virtual void SetCursor(ServerCursor* cursor); virtual void SetCursorVisible(bool visible); diff --git a/src/servers/app/drawing/DWindowHWInterface.cpp b/src/servers/app/drawing/DWindowHWInterface.cpp index e3527b32fb..15cb85fc7d 100644 --- a/src/servers/app/drawing/DWindowHWInterface.cpp +++ b/src/servers/app/drawing/DWindowHWInterface.cpp @@ -944,7 +944,7 @@ DWindowHWInterface::CopyRegion(const clipping_rect* sortedRectList, // FillRegion void -DWindowHWInterface::FillRegion(/*const*/ BRegion& region, const RGBColor& color) +DWindowHWInterface::FillRegion(/*const*/ BRegion& region, const RGBColor& color, bool autoSync) { if (fAccFillRect && fAccAcquireEngine) { if (fAccAcquireEngine(B_2D_ACCELERATION, 0xff, &fSyncToken, &fEngineToken) >= B_OK) { @@ -961,7 +961,7 @@ DWindowHWInterface::FillRegion(/*const*/ BRegion& region, const RGBColor& color) fAccReleaseEngine(fEngineToken, &fSyncToken); // sync - if (fAccSyncToToken) + if (autoSync && fAccSyncToToken) fAccSyncToToken(&fSyncToken); } } @@ -997,6 +997,14 @@ DWindowHWInterface::InvertRegion(/*const*/ BRegion& region) } } +// Sync +void +DWindowHWInterface::Sync() +{ + if (fAccSyncToToken) + fAccSyncToToken(&fSyncToken); +} + // FrontBuffer RenderingBuffer* DWindowHWInterface::FrontBuffer() const diff --git a/src/servers/app/drawing/DWindowHWInterface.h b/src/servers/app/drawing/DWindowHWInterface.h index 0304e64155..4b2007a70e 100644 --- a/src/servers/app/drawing/DWindowHWInterface.h +++ b/src/servers/app/drawing/DWindowHWInterface.h @@ -59,9 +59,12 @@ class DWindowHWInterface : public HWInterface { uint32 count, int32 xOffset, int32 yOffset); virtual void FillRegion(/*const*/ BRegion& region, - const RGBColor& color); + const RGBColor& color, + bool autoSync); virtual void InvertRegion(/*const*/ BRegion& region); + virtual void Sync(); + // frame buffer access virtual RenderingBuffer* FrontBuffer() const; virtual RenderingBuffer* BackBuffer() const; diff --git a/src/servers/app/drawing/DrawingEngine.cpp b/src/servers/app/drawing/DrawingEngine.cpp index 272b4b9cc5..fa616d3e7b 100644 --- a/src/servers/app/drawing/DrawingEngine.cpp +++ b/src/servers/app/drawing/DrawingEngine.cpp @@ -21,7 +21,7 @@ #include "ServerCursor.h" #include "RenderingBuffer.h" -#include "frame_buffer_support.h" +#include "drawing_support.h" // make_rect_valid static inline void @@ -82,7 +82,8 @@ class FontLocker { DrawingEngine::DrawingEngine(HWInterface* interface) : fPainter(new Painter()), fGraphicsCard(interface), - fAvailableHWAccleration(0) + fAvailableHWAccleration(0), + fSuspendSyncLevel(0) { } @@ -152,6 +153,24 @@ DrawingEngine::ConstrainClippingRegion(const BRegion* region) } } +// SuspendAutoSync +void +DrawingEngine::SuspendAutoSync() +{ + fSuspendSyncLevel++; +} + +// Sync +void +DrawingEngine::Sync() +{ + fSuspendSyncLevel--; + if (fSuspendSyncLevel == 0) + fGraphicsCard->Sync(); +} + +// #pragma mark - + // CopyRegion() does a topological sort of the rects in the // region. The algorithm was suggested by Ingo Weinhold. // It compares each rect with each rect and builds a tree @@ -596,20 +615,23 @@ DrawingEngine::FillRect(BRect r, const RGBColor& color) make_rect_valid(r); r = fPainter->ClipRect(r); if (r.IsValid()) { - fGraphicsCard->HideSoftwareCursor(r); + bool cursorTouched = fGraphicsCard->HideSoftwareCursor(r); // try hardware optimized version first if (fAvailableHWAccleration & HW_ACC_FILL_REGION) { BRegion region(r); region.IntersectWith(fPainter->ClippingRegion()); - fGraphicsCard->FillRegion(region, color); + fGraphicsCard->FillRegion(region, color, + fSuspendSyncLevel == 0 + || cursorTouched); } else { fPainter->FillRect(r, color.GetColor32()); fGraphicsCard->Invalidate(r); } - - fGraphicsCard->ShowSoftwareCursor(); + + if (cursorTouched) + fGraphicsCard->ShowSoftwareCursor(); } WriteUnlock(); @@ -626,13 +648,14 @@ DrawingEngine::FillRegion(BRegion& r, const RGBColor& color) // NOTE: region expected to be already clipped correctly!! if (WriteLock()) { BRect frame = r.Frame(); - fGraphicsCard->HideSoftwareCursor(frame); + bool cursorTouched = fGraphicsCard->HideSoftwareCursor(frame); bool doInSoftware = true; // try hardware optimized version first if ((fAvailableHWAccleration & HW_ACC_FILL_REGION) != 0 && frame.Width() * frame.Height() > 100) { - fGraphicsCard->FillRegion(r, color); + fGraphicsCard->FillRegion(r, color, fSuspendSyncLevel == 0 + || cursorTouched); doInSoftware = false; } @@ -646,7 +669,8 @@ DrawingEngine::FillRegion(BRegion& r, const RGBColor& color) fGraphicsCard->Invalidate(r.Frame()); } - fGraphicsCard->ShowSoftwareCursor(); + if (cursorTouched) + fGraphicsCard->ShowSoftwareCursor(); WriteUnlock(); } @@ -689,7 +713,7 @@ DrawingEngine::FillRect(BRect r, const DrawState *d) make_rect_valid(r); r = fPainter->ClipRect(r); if (r.IsValid()) { - fGraphicsCard->HideSoftwareCursor(r); + bool cursorTouched = fGraphicsCard->HideSoftwareCursor(r); bool doInSoftware = true; if ((r.Width() + 1) * (r.Height() + 1) > 100.0) { @@ -701,13 +725,17 @@ DrawingEngine::FillRect(BRect r, const DrawState *d) || d->GetDrawingMode() == B_OP_OVER)) { BRegion region(r); region.IntersectWith(fPainter->ClippingRegion()); - fGraphicsCard->FillRegion(region, d->HighColor()); + fGraphicsCard->FillRegion(region, d->HighColor(), + fSuspendSyncLevel == 0 + || cursorTouched); doInSoftware = false; } else if (d->GetPattern() == B_SOLID_LOW && d->GetDrawingMode() == B_OP_COPY) { BRegion region(r); region.IntersectWith(fPainter->ClippingRegion()); - fGraphicsCard->FillRegion(region, d->LowColor()); + fGraphicsCard->FillRegion(region, d->LowColor(), + fSuspendSyncLevel == 0 + || cursorTouched); doInSoftware = false; } } @@ -719,7 +747,8 @@ DrawingEngine::FillRect(BRect r, const DrawState *d) fGraphicsCard->Invalidate(r); } - fGraphicsCard->ShowSoftwareCursor(); + if (cursorTouched) + fGraphicsCard->ShowSoftwareCursor(); } WriteUnlock(); @@ -736,7 +765,7 @@ DrawingEngine::FillRegion(BRegion& r, const DrawState *d) if (WriteLock()) { BRect clipped = fPainter->ClipRect(r.Frame()); if (clipped.IsValid()) { - fGraphicsCard->HideSoftwareCursor(clipped); + bool cursorTouched = fGraphicsCard->HideSoftwareCursor(clipped); bool doInSoftware = true; // try hardware optimized version first @@ -745,12 +774,16 @@ DrawingEngine::FillRegion(BRegion& r, const DrawState *d) && (d->GetDrawingMode() == B_OP_COPY || d->GetDrawingMode() == B_OP_OVER)) { r.IntersectWith(fPainter->ClippingRegion()); - fGraphicsCard->FillRegion(r, d->HighColor()); + fGraphicsCard->FillRegion(r, d->HighColor(), + fSuspendSyncLevel == 0 + || cursorTouched); doInSoftware = false; } else if (d->GetPattern() == B_SOLID_LOW && d->GetDrawingMode() == B_OP_COPY) { r.IntersectWith(fPainter->ClippingRegion()); - fGraphicsCard->FillRegion(r, d->LowColor()); + fGraphicsCard->FillRegion(r, d->LowColor(), + fSuspendSyncLevel == 0 + || cursorTouched); doInSoftware = false; } } @@ -768,7 +801,8 @@ DrawingEngine::FillRegion(BRegion& r, const DrawState *d) fGraphicsCard->Invalidate(touched); } - fGraphicsCard->ShowSoftwareCursor(); + if (cursorTouched) + fGraphicsCard->ShowSoftwareCursor(); } WriteUnlock(); @@ -1270,3 +1304,4 @@ DrawingEngine::_CopyRect(uint8* src, uint32 width, uint32 height, } } + diff --git a/src/servers/app/DrawingEngine.h b/src/servers/app/drawing/DrawingEngine.h similarity index 98% rename from src/servers/app/DrawingEngine.h rename to src/servers/app/drawing/DrawingEngine.h index 9c0ee15051..9dfec9b6d6 100644 --- a/src/servers/app/DrawingEngine.h +++ b/src/servers/app/drawing/DrawingEngine.h @@ -66,6 +66,9 @@ public: // will remove any clipping (drawing allowed everywhere) void ConstrainClippingRegion(const BRegion* region); + void SuspendAutoSync(); + void Sync(); + // drawing functions void CopyRegion(/*const*/ BRegion* region, int32 xOffset, int32 yOffset); @@ -156,6 +159,7 @@ public: Painter* fPainter; HWInterface* fGraphicsCard; uint32 fAvailableHWAccleration; + int32 fSuspendSyncLevel; }; #endif // DRAWING_ENGINE_H_ diff --git a/src/servers/app/drawing/HWInterface.cpp b/src/servers/app/drawing/HWInterface.cpp index 55f2befe7e..e158145c4e 100644 --- a/src/servers/app/drawing/HWInterface.cpp +++ b/src/servers/app/drawing/HWInterface.cpp @@ -10,7 +10,7 @@ #include #include -#include "frame_buffer_support.h" +#include "drawing_support.h" #include "RenderingBuffer.h" #include "ServerCursor.h" @@ -302,7 +302,7 @@ HWInterface::CopyBackToFront(const BRect& frame) } // HideSoftwareCursor -void +bool HWInterface::HideSoftwareCursor(const BRect& area) { if (fCursorAreaBackup && !fCursorAreaBackup->cursor_hidden) { @@ -312,8 +312,10 @@ HWInterface::HideSoftwareCursor(const BRect& area) fCursorAreaBackup->bottom); if (area.Intersects(backupArea)) { _RestoreCursorArea(); + return true; } } + return false; } // HideSoftwareCursor @@ -404,10 +406,11 @@ HWInterface::_DrawCursor(BRect area) const *(uint32*)b = *(uint32*)s; // assumes backbuffer alpha = 255 // assuming pre-multiplied cursor bitmap - uint8 a = 255 - c[3]; - d[0] = ((b[0] * a) >> 8) + c[0]; - d[1] = ((b[1] * a) >> 8) + c[1]; - d[2] = ((b[2] * a) >> 8) + c[2]; + int a = 255 - c[3]; + d[0] = ((int)(b[0] * a + 255) >> 8) + c[0]; + d[1] = ((int)(b[1] * a + 255) >> 8) + c[1]; + d[2] = ((int)(b[2] * a + 255) >> 8) + c[2]; + s += 4; c += 4; d += 4; @@ -428,9 +431,10 @@ HWInterface::_DrawCursor(BRect area) const // assumes backbuffer alpha = 255 // assuming pre-multiplied cursor bitmap uint8 a = 255 - c[3]; - d[0] = ((s[0] * a) >> 8) + c[0]; - d[1] = ((s[1] * a) >> 8) + c[1]; - d[2] = ((s[2] * a) >> 8) + c[2]; + d[0] = ((s[0] * a + 255) >> 8) + c[0]; + d[1] = ((s[1] * a + 255) >> 8) + c[1]; + d[2] = ((s[2] * a + 255) >> 8) + c[2]; + s += 4; c += 4; d += 4; diff --git a/src/servers/app/drawing/HWInterface.h b/src/servers/app/drawing/HWInterface.h index 7e1eac6e16..3dfd818d64 100644 --- a/src/servers/app/drawing/HWInterface.h +++ b/src/servers/app/drawing/HWInterface.h @@ -73,9 +73,12 @@ class HWInterface : public MultiLocker { uint32 count, int32 xOffset, int32 yOffset) {} virtual void FillRegion(/*const*/ BRegion& region, - const RGBColor& color) {} + const RGBColor& color, + bool autoSync) {} virtual void InvertRegion(/*const*/ BRegion& region) {} + virtual void Sync() {} + // cursor handling (these do their own Read/Write locking) ServerCursor* Cursor() const { return fCursor; } virtual void SetCursor(ServerCursor* cursor); @@ -115,7 +118,7 @@ class HWInterface : public MultiLocker { // --- // NOTE: Investigate locking for these! The client code should already hold a // ReadLock, but maybe these functions should acquire a WriteLock! - void HideSoftwareCursor(const BRect& area); + bool HideSoftwareCursor(const BRect& area); void HideSoftwareCursor(); void ShowSoftwareCursor(); diff --git a/src/servers/app/drawing/Jamfile b/src/servers/app/drawing/Jamfile index f1053e5d3b..212bffafae 100644 --- a/src/servers/app/drawing/Jamfile +++ b/src/servers/app/drawing/Jamfile @@ -13,6 +13,7 @@ StaticLibrary libasdrawing.a : AccelerantBuffer.cpp AccelerantHWInterface.cpp BitmapBuffer.cpp + drawing_support.cpp DrawingEngine.cpp MallocBuffer.cpp UpdateQueue.cpp diff --git a/src/servers/app/drawing/Painter/Painter.cpp b/src/servers/app/drawing/Painter/Painter.cpp index 888a3e0990..45961edffe 100644 --- a/src/servers/app/drawing/Painter/Painter.cpp +++ b/src/servers/app/drawing/Painter/Painter.cpp @@ -26,7 +26,7 @@ #include #include -#include "frame_buffer_support.h" +#include "drawing_support.h" #include "DrawState.h" @@ -210,6 +210,8 @@ Painter::SetDrawState(const DrawState* data, bool updateFont) fLineJoinMode = data->LineJoinMode(); fMiterLimit = data->MiterLimit(); + // adopt the color *after* the pattern is set + // to set the renderers to the correct color SetHighColor(data->HighColor().GetColor32()); SetLowColor(data->LowColor().GetColor32()); @@ -240,7 +242,7 @@ Painter::SetHighColor(const rgb_color& color) void Painter::SetLowColor(const rgb_color& color) { - fPatternHandler->SetLowColor(color);; + fPatternHandler->SetLowColor(color); if (*(fPatternHandler->GetR5Pattern()) == B_SOLID_LOW) _SetRendererColor(color); } @@ -1356,12 +1358,8 @@ Painter::_DrawBitmap(const agg::rendering_buffer& srcBuffer, color_space format, return; } - if (!fSubpixelPrecise) { - // round off viewRect (in a way avoiding too much distortion) - viewRect.OffsetTo(roundf(viewRect.left), roundf(viewRect.top)); - viewRect.right = roundf(viewRect.right); - viewRect.bottom = roundf(viewRect.bottom); - } + if (!fSubpixelPrecise) + align_rect_to_pixels(&viewRect); double xScale = (viewRect.Width() + 1) / (bitmapRect.Width() + 1); double yScale = (viewRect.Height() + 1) / (bitmapRect.Height() + 1); diff --git a/src/servers/app/drawing/Painter/drawing_modes/DrawingMode.h b/src/servers/app/drawing/Painter/drawing_modes/DrawingMode.h index 68792b1100..7330934f36 100644 --- a/src/servers/app/drawing/Painter/drawing_modes/DrawingMode.h +++ b/src/servers/app/drawing/Painter/drawing_modes/DrawingMode.h @@ -9,7 +9,7 @@ #ifndef DRAWING_MODE_H #define DRAWING_MODE_H -#include "frame_buffer_support.h" +#include "drawing_support.h" #include "PatternHandler.h" #include "PixelFormat.h" diff --git a/src/servers/app/drawing/drawing_support.cpp b/src/servers/app/drawing/drawing_support.cpp new file mode 100644 index 0000000000..f69878b8f4 --- /dev/null +++ b/src/servers/app/drawing/drawing_support.cpp @@ -0,0 +1,13 @@ +#include "drawing_support.h" + +#include + +void +align_rect_to_pixels(BRect* rect) +{ + // round the rect with the least ammount of distortion + rect->OffsetTo(roundf(rect->left), roundf(rect->top)); + rect->right = roundf(rect->right); + rect->bottom = roundf(rect->bottom); +} + diff --git a/src/servers/app/drawing/frame_buffer_support.h b/src/servers/app/drawing/drawing_support.h similarity index 98% rename from src/servers/app/drawing/frame_buffer_support.h rename to src/servers/app/drawing/drawing_support.h index 37bae1a8a3..ca0764097d 100644 --- a/src/servers/app/drawing/frame_buffer_support.h +++ b/src/servers/app/drawing/drawing_support.h @@ -138,6 +138,9 @@ blend_line32(uint8* buffer, int32 pixels, uint8 r, uint8 g, uint8 b, uint8 a) gfxcpy32(buffer, tempBuffer, pixels * 4); } +void +align_rect_to_pixels(BRect* rect); + #endif // SUPPORT_H diff --git a/src/tests/servers/app/Jamfile b/src/tests/servers/app/Jamfile index bc443bd5db..727b859f75 100644 --- a/src/tests/servers/app/Jamfile +++ b/src/tests/servers/app/Jamfile @@ -103,6 +103,7 @@ Server haiku_app_server : AccelerantBuffer.cpp AccelerantHWInterface.cpp BitmapBuffer.cpp + drawing_support.cpp DrawingEngine.cpp MallocBuffer.cpp UpdateQueue.cpp @@ -148,6 +149,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 event_mask ; +SubInclude HAIKU_TOP src tests servers app following ; SubInclude HAIKU_TOP src tests servers app look_and_feel ; SubInclude HAIKU_TOP src tests servers app painter ; SubInclude HAIKU_TOP src tests servers app playground ; diff --git a/src/tests/servers/app/following/Jamfile b/src/tests/servers/app/following/Jamfile new file mode 100644 index 0000000000..3c01c443fd --- /dev/null +++ b/src/tests/servers/app/following/Jamfile @@ -0,0 +1,17 @@ +SubDir HAIKU_TOP src tests servers app following ; + +SetSubDirSupportedPlatformsBeOSCompatible ; +AddSubDirSupportedPlatforms libbe_test ; + +UseHeaders [ FDirName os app ] ; +UseHeaders [ FDirName os interface ] ; + +SimpleTest Following : + main.cpp + : be ; + +if ( $(TARGET_PLATFORM) = libbe_test ) { + HaikuInstall install-test-apps : $(HAIKU_APP_TEST_DIR) : Following + : tests!apps ; +} + diff --git a/src/tests/servers/app/following/main.cpp b/src/tests/servers/app/following/main.cpp new file mode 100644 index 0000000000..955a8a1895 --- /dev/null +++ b/src/tests/servers/app/following/main.cpp @@ -0,0 +1,164 @@ +// main.cpp + +#include +#include + +#include +#include +#include +#include +#include + +enum { + TRACKING_NONE = 0, + TRACKING_ALL, + TRACKING_RIGHT, + TRACKING_BOTTOM, + TRACKING_RIGHT_BOTTOM, +}; + +class TestView : public BView { + + public: + TestView(BRect frame, const char* name, + uint32 resizeFlags, uint32 flags) + : BView(frame, name, resizeFlags, flags), + fTracking(TRACKING_NONE), + fLastMousePos(0.0, 0.0) + { + rgb_color color; + color.red = rand() / 256; + color.green = rand() / 256; + color.blue = rand() / 256; + color.alpha = 255; + SetViewColor(color); + SetLowColor(color); + } + + virtual void Draw(BRect updateRect); + + virtual void MouseDown(BPoint where); + virtual void MouseUp(BPoint where); + virtual void MouseMoved(BPoint where, uint32 transit, + const BMessage* dragMessage); + + private: + uint32 fTracking; + + BPoint fLastMousePos; +}; + +// Draw +void +TestView::Draw(BRect updateRect) +{ + // text + SetHighColor(0, 0, 0, 255); + const char* message = "Click and drag to move this view!"; + DrawString(message, BPoint(20.0, 30.0)); + + BRect r(Bounds()); + r.right -= 15.0; + r.bottom -= 15.0; + StrokeLine(r.RightTop(), BPoint(r.right, Bounds().bottom)); + StrokeLine(r.LeftBottom(), BPoint(Bounds().right, r.bottom)); +} + +// MouseDown +void +TestView::MouseDown(BPoint where) +{ + BRect r(Bounds()); + r.right -= 15.0; + r.bottom -= 15.0; + if (r.Contains(where)) + fTracking = TRACKING_ALL; + else if (r.bottom < where.y && r.right < where.x) + fTracking = TRACKING_RIGHT_BOTTOM; + else if (r.bottom < where.y) + fTracking = TRACKING_BOTTOM; + else if (r.right < where.x) + fTracking = TRACKING_RIGHT; + + fLastMousePos = where; + SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS); +} + +// MouseUp +void +TestView::MouseUp(BPoint where) +{ + fTracking = TRACKING_NONE; +} + +// MouseMoved +void +TestView::MouseMoved(BPoint where, uint32 transit, + const BMessage* dragMessage) +{ + BPoint offset = where - fLastMousePos; + switch (fTracking) { + case TRACKING_ALL: + MoveBy(offset.x, offset.y); + // fLastMousePos stays fixed + break; + case TRACKING_RIGHT: + ResizeBy(offset.x, 0.0); + fLastMousePos = where; + break; + case TRACKING_BOTTOM: + ResizeBy(0.0, offset.y); + fLastMousePos = where; + break; + case TRACKING_RIGHT_BOTTOM: + ResizeBy(offset.x, offset.y); + fLastMousePos = where; + break; + } +} + + + +// show_window +void +show_window(BRect frame, const char* name) +{ + BWindow* window = new BWindow(frame, name, + B_TITLED_WINDOW, + B_ASYNCHRONOUS_CONTROLS | B_QUIT_ON_WINDOW_CLOSE); + + BView* view = new TestView(window->Bounds(), "test 1", B_FOLLOW_ALL, + B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE); + + window->AddChild(view); + + BRect bounds = view->Bounds(); + bounds.InsetBy(20, 20); + BView* view1 = new TestView(bounds, "test 2", B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM, + B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE); + view->AddChild(view1); + + bounds = view1->Bounds(); + bounds.InsetBy(20, 20); + BView* view2 = new TestView(bounds, "test 3", B_FOLLOW_NONE, + B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE); + view1->AddChild(view2); + + + window->Show(); +} + +// main +int +main(int argc, char** argv) +{ + BApplication* app = new BApplication("application/x.vnd-Haiku.Following"); + + BRect frame(50.0, 50.0, 300.0, 250.0); + show_window(frame, "Following Test"); + + app->Run(); + + delete app; + return 0; +} diff --git a/src/tests/servers/app/following/run b/src/tests/servers/app/following/run new file mode 100755 index 0000000000..fcce0ef409 --- /dev/null +++ b/src/tests/servers/app/following/run @@ -0,0 +1,18 @@ +#!/bin/sh + +../../../../../generated/tests/apps/run_haiku_registrar || exit + +if test -f ../../../../../generated/tests/apps/haiku_app_server; then + ../../../../../generated/tests/apps/haiku_app_server & +else + echo "You need to \"TARGET_PLATFORM=r5 jam install-test-apps\" first." +fi + +sleep 1s + +if test -f ../../../../../generated/tests/apps/Following; then + ../../../../../generated/tests/apps/Following +else + echo "You need to \"TARGET_PLATFORM=r5 jam install-test-apps\" first." +fi +