From e718dc917812898e8145a9238d79164d27dfbf87 Mon Sep 17 00:00:00 2001 From: Julian Harnath Date: Thu, 12 Nov 2015 10:41:35 +0100 Subject: [PATCH] app_server: Clip alpha masks to canvas size * Making the alpha masks independent of view size is a good thing, however it turns out that I was too optimistic about the consequences: webkit sometimes sets masks for the whole page, not just the currently visible area. E.g. on Github diff views, it was seen to set a clipping path which is about 1,000 x 10,000 pixels in size. Generating these huge masks eats up lots of memory and time. * We now clip the alpha masks to the current view size. This introduces a dependency between mask and view again, however a weaker one than it used to be before the mask rework. When the view is enlarged, and the alpha mask was previously clipped during rendering, we regenerate it at the new size. When the view is shrunk however, we don't do anything and just keep the now larger than necessary mask around (so we don't have to regenerate again when the view is subsequently enlarged again -- except if it then becomes even larger than it used to be). Changing the view origin is unaffected and still doesn't cause a regenerate. --- src/servers/app/Canvas.cpp | 12 ++++- src/servers/app/Canvas.h | 6 ++- src/servers/app/Layer.cpp | 10 +++- src/servers/app/ServerPicture.cpp | 7 ++- src/servers/app/ServerWindow.cpp | 2 +- src/servers/app/View.h | 2 +- src/servers/app/drawing/AlphaMask.cpp | 67 +++++++++++++++++++-------- src/servers/app/drawing/AlphaMask.h | 18 ++++--- 8 files changed, 90 insertions(+), 34 deletions(-) diff --git a/src/servers/app/Canvas.cpp b/src/servers/app/Canvas.cpp index b9fa40257c..8781aca427 100644 --- a/src/servers/app/Canvas.cpp +++ b/src/servers/app/Canvas.cpp @@ -289,10 +289,11 @@ Canvas::BlendLayer(Layer* layer) OffscreenCanvas::OffscreenCanvas(DrawingEngine* engine, - const DrawState& state) + const DrawState& state, const IntRect& bounds) : Canvas(state), - fDrawingEngine(engine) + fDrawingEngine(engine), + fBounds(bounds) { ResyncDrawState(); } @@ -313,3 +314,10 @@ OffscreenCanvas::UpdateCurrentDrawingRegion() fDrawingEngine->ConstrainClippingRegion(&fCurrentDrawingRegion); } } + + +IntRect +OffscreenCanvas::Bounds() const +{ + return fBounds; +} diff --git a/src/servers/app/Canvas.h b/src/servers/app/Canvas.h index ec96ffcf17..952910564f 100644 --- a/src/servers/app/Canvas.h +++ b/src/servers/app/Canvas.h @@ -60,6 +60,8 @@ public: void SetAlphaMask(AlphaMask* mask); AlphaMask* GetAlphaMask() const; + virtual IntRect Bounds() const = 0; + SimpleTransform LocalToScreenTransform() const; SimpleTransform ScreenToLocalTransform() const; SimpleTransform PenToScreenTransform() const; @@ -88,7 +90,7 @@ protected: class OffscreenCanvas : public Canvas { public: OffscreenCanvas(DrawingEngine* engine, - const DrawState& state); + const DrawState& state, const IntRect& bounds); virtual DrawingEngine* GetDrawingEngine() const { return fDrawingEngine; } @@ -97,6 +99,7 @@ public: virtual void UpdateCurrentDrawingRegion(); virtual ServerPicture* GetPicture(int32 token) const { /* TODO */ return NULL; } + virtual IntRect Bounds() const; protected: virtual void _LocalToScreenTransform(SimpleTransform&) const {} @@ -105,6 +108,7 @@ protected: private: DrawingEngine* fDrawingEngine; BRegion fCurrentDrawingRegion; + IntRect fBounds; }; diff --git a/src/servers/app/Layer.cpp b/src/servers/app/Layer.cpp index fe06f260a1..4fa8868a2e 100644 --- a/src/servers/app/Layer.cpp +++ b/src/servers/app/Layer.cpp @@ -60,6 +60,11 @@ public: fDrawingEngine->ConstrainClippingRegion(&fCurrentDrawingRegion); } + virtual IntRect Bounds() const + { + return fBitmapBounds; + } + protected: virtual void _LocalToScreenTransform(SimpleTransform&) const { @@ -142,7 +147,7 @@ Layer::RenderToBitmap(Canvas* canvas) IntPoint oldOffset; if (mask != NULL) { // Move alpha mask to bitmap origin - oldOffset = mask->SetViewOrigin(IntPoint(0, 0)); + oldOffset = mask->SetCanvasGeometry(IntPoint(0, 0), boundingBox); } canvas->CurrentState()->SetDrawingMode(B_OP_ALPHA); @@ -164,7 +169,8 @@ Layer::RenderToBitmap(Canvas* canvas) // Note: this needs to be adapted if setting alpha masks is // implemented as BPicture command (the mask now might be a different // one than before). - mask->SetViewOrigin(oldOffset); + layerCanvas.CurrentState()->CombinedTransform().Apply(oldOffset); + mask->SetCanvasGeometry(oldOffset, boundingBox); layerCanvas.ResyncDrawState(); } diff --git a/src/servers/app/ServerPicture.cpp b/src/servers/app/ServerPicture.cpp index d265336b1c..01808141a0 100644 --- a/src/servers/app/ServerPicture.cpp +++ b/src/servers/app/ServerPicture.cpp @@ -799,8 +799,11 @@ clip_to_rect(void* _canvas, const BRect& rect, bool inverse) { Canvas* const canvas = reinterpret_cast(_canvas); bool needDrawStateUpdate = canvas->ClipToRect(rect, inverse); - if (needDrawStateUpdate) + if (needDrawStateUpdate) { + canvas->CurrentState()->GetAlphaMask()->SetCanvasGeometry(BPoint(0, 0), + canvas->Bounds()); canvas->ResyncDrawState(); + } canvas->UpdateCurrentDrawingRegion(); } @@ -824,6 +827,8 @@ clip_to_shape(void* _canvas, int32 opCount, const uint32 opList[], shapeData.ptSize = ptCount * sizeof(BPoint); canvas->ClipToShape(&shapeData, inverse); + canvas->CurrentState()->GetAlphaMask()->SetCanvasGeometry(BPoint(0, 0), + canvas->Bounds()); canvas->ResyncDrawState(); free(shapeData.opList); diff --git a/src/servers/app/ServerWindow.cpp b/src/servers/app/ServerWindow.cpp index 6539d64f22..0e62db2983 100644 --- a/src/servers/app/ServerWindow.cpp +++ b/src/servers/app/ServerWindow.cpp @@ -3983,7 +3983,7 @@ ServerWindow::_UpdateDrawState(View* view) BPoint leftTop(0, 0); if (view->GetAlphaMask() != NULL) { view->LocalToScreenTransform().Apply(&leftTop); - view->GetAlphaMask()->SetViewOrigin(leftTop); + view->GetAlphaMask()->SetCanvasGeometry(leftTop, view->Bounds()); leftTop = BPoint(0, 0); } view->PenToScreenTransform().Apply(&leftTop); diff --git a/src/servers/app/View.h b/src/servers/app/View.h index fbf66409b9..cf19fa762e 100644 --- a/src/servers/app/View.h +++ b/src/servers/app/View.h @@ -50,7 +50,7 @@ public: IntRect Frame() const { return fFrame; } - IntRect Bounds() const; + virtual IntRect Bounds() const; void SetResizeMode(uint32 resizeMode) { fResizeMode = resizeMode; } diff --git a/src/servers/app/drawing/AlphaMask.cpp b/src/servers/app/drawing/AlphaMask.cpp index 9e5441dd19..35da811034 100644 --- a/src/servers/app/drawing/AlphaMask.cpp +++ b/src/servers/app/drawing/AlphaMask.cpp @@ -29,7 +29,9 @@ AlphaMask::AlphaMask(AlphaMask* previousMask, bool inverse) : fPreviousMask(previousMask), fBounds(), - fViewOrigin(), + fClippedToCanvas(true), + fCanvasOrigin(), + fCanvasBounds(), fInverse(inverse), fBackgroundOpacity(0), fBits(NULL), @@ -44,7 +46,9 @@ AlphaMask::AlphaMask(uint8 backgroundOpacity) : fPreviousMask(), fBounds(), - fViewOrigin(), + fClippedToCanvas(true), + fCanvasOrigin(), + fCanvasBounds(), fInverse(false), fBackgroundOpacity(backgroundOpacity), fBits(NULL), @@ -62,18 +66,30 @@ AlphaMask::~AlphaMask() IntPoint -AlphaMask::SetViewOrigin(IntPoint viewOrigin) +AlphaMask::SetCanvasGeometry(IntPoint origin, IntRect bounds) { - if (viewOrigin == fViewOrigin) - return fViewOrigin; + if (origin == fCanvasOrigin && bounds.Width() == fCanvasBounds.Width() + && bounds.Height() == fCanvasBounds.Height()) + return fCanvasOrigin; - IntPoint oldOrigin = fViewOrigin; - fViewOrigin = viewOrigin; + IntPoint oldOrigin = fCanvasOrigin; + fCanvasOrigin = origin; + IntRect oldBounds = fCanvasBounds; + fCanvasBounds = IntRect(0, 0, bounds.Width(), bounds.Height()); + + if (fClippedToCanvas && (fCanvasBounds.Width() > oldBounds.Width() + || fCanvasBounds.Height() > oldBounds.Height())) { + // The canvas is now larger than before and we previously + // drew the alpha mask clipped to the (old) bounds of the + // canvas. So we now have to redraw the alpha mask with the + // new size. + _Generate(); + } _AttachMaskToBuffer(); if (fPreviousMask != NULL) - fPreviousMask->SetViewOrigin(viewOrigin); + fPreviousMask->SetCanvasGeometry(origin, bounds); return oldOrigin; } @@ -101,7 +117,7 @@ AlphaMask::_CreateTemporaryBitmap(BRect bounds) const void AlphaMask::_Generate() { - ServerBitmap* const bitmap = _RenderSource(); + ServerBitmap* const bitmap = _RenderSource(fCanvasBounds); BReference bitmapRef(bitmap, true); if (bitmap == NULL) { _SetNoClipping(); @@ -156,6 +172,13 @@ AlphaMask::_SetNoClipping() } +const IntRect& +AlphaMask::_PreviousMaskBounds() const +{ + return fPreviousMask->fBounds; +} + + void AlphaMask::_AttachMaskToBuffer() { @@ -172,8 +195,8 @@ AlphaMask::_AttachMaskToBuffer() } const IntPoint maskOffset = _Offset(); - const int32 offsetX = fBounds.left + maskOffset.x + fViewOrigin.x; - const int32 offsetY = fBounds.top + maskOffset.y + fViewOrigin.y; + const int32 offsetX = fBounds.left + maskOffset.x + fCanvasOrigin.x; + const int32 offsetY = fBounds.top + maskOffset.y + fCanvasOrigin.y; fMask.attach(fBuffer, offsetX, offsetY, outsideOpacity); } @@ -192,7 +215,7 @@ UniformAlphaMask::UniformAlphaMask(uint8 opacity) ServerBitmap* -UniformAlphaMask::_RenderSource() +UniformAlphaMask::_RenderSource(const IntRect&) { return NULL; } @@ -220,11 +243,19 @@ VectorAlphaMask::VectorAlphaMask(AlphaMask* previousMask, template ServerBitmap* -VectorAlphaMask::_RenderSource() +VectorAlphaMask::_RenderSource(const IntRect& canvasBounds) { fBounds = static_cast(this)->DetermineBoundingBox(); + + if (fBounds.Width() > canvasBounds.Width() + || fBounds.Height() > canvasBounds.Height()) { + fBounds = fBounds & canvasBounds; + fClippedToCanvas = true; + } else + fClippedToCanvas = false; + if (fPreviousMask != NULL) - fBounds = fBounds & fPreviousMask->fBounds; + fBounds = fBounds & _PreviousMaskBounds(); if (!fBounds.IsValid()) return NULL; @@ -242,7 +273,7 @@ VectorAlphaMask::_RenderSource() engine->SetRendererOffset(fBounds.left, fBounds.top); OffscreenCanvas canvas(engine, - static_cast(this)->GetDrawState()); + static_cast(this)->GetDrawState(), fBounds); DrawState* const drawState = canvas.CurrentState(); drawState->SetDrawingMode(B_OP_ALPHA); @@ -286,7 +317,6 @@ PictureAlphaMask::PictureAlphaMask(AlphaMask* previousMask, fPicture(picture), fDrawState(new DrawState(drawState)) { - _Generate(); } @@ -340,8 +370,7 @@ ShapeAlphaMask::ShapeAlphaMask(AlphaMask* previousMask, fShape(shape), fDrawState() { - fBounds = fShape.DetermineBoundingBox(); - _Generate(); + fShapeBounds = fShape.DetermineBoundingBox(); } @@ -358,7 +387,7 @@ ShapeAlphaMask::DrawVectors(Canvas* canvas) BRect ShapeAlphaMask::DetermineBoundingBox() const { - return fBounds; + return fShapeBounds; } diff --git a/src/servers/app/drawing/AlphaMask.h b/src/servers/app/drawing/AlphaMask.h index dc44545256..592f486cb4 100644 --- a/src/servers/app/drawing/AlphaMask.h +++ b/src/servers/app/drawing/AlphaMask.h @@ -32,7 +32,8 @@ public: AlphaMask(uint8 backgroundOpacity); virtual ~AlphaMask(); - IntPoint SetViewOrigin(IntPoint viewOrigin); + IntPoint SetCanvasGeometry(IntPoint origin, + IntRect bounds); scanline_unpacked_masked_type* Scanline() { return &fScanline; } @@ -44,19 +45,22 @@ protected: ServerBitmap* _CreateTemporaryBitmap(BRect bounds) const; void _Generate(); void _SetNoClipping(); + const IntRect& _PreviousMaskBounds() const; private: - virtual ServerBitmap* _RenderSource() = 0; + virtual ServerBitmap* _RenderSource(const IntRect& canvasBounds) = 0; virtual IntPoint _Offset() = 0; void _AttachMaskToBuffer(); -public: +protected: BReference fPreviousMask; IntRect fBounds; + bool fClippedToCanvas; private: - IntPoint fViewOrigin; + IntPoint fCanvasOrigin; + IntRect fCanvasBounds; const bool fInverse; uint8 fBackgroundOpacity; @@ -72,7 +76,7 @@ public: UniformAlphaMask(uint8 opacity); private: - virtual ServerBitmap* _RenderSource(); + virtual ServerBitmap* _RenderSource(const IntRect& canvasBounds); virtual IntPoint _Offset(); }; @@ -87,7 +91,7 @@ public: BPoint where, bool inverse); private: - virtual ServerBitmap* _RenderSource(); + virtual ServerBitmap* _RenderSource(const IntRect& canvasBounds); virtual IntPoint _Offset(); protected: @@ -131,7 +135,7 @@ public: private: const shape_data& fShape; - BRect fBounds; + BRect fShapeBounds; DrawState fDrawState; };