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.
This commit is contained in:
Julian Harnath 2015-11-12 10:41:35 +01:00
parent c37db53b57
commit e718dc9178
8 changed files with 90 additions and 34 deletions

View File

@ -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;
}

View File

@ -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;
};

View File

@ -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();
}

View File

@ -799,8 +799,11 @@ clip_to_rect(void* _canvas, const BRect& rect, bool inverse)
{
Canvas* const canvas = reinterpret_cast<Canvas*>(_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);

View File

@ -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);

View File

@ -50,7 +50,7 @@ public:
IntRect Frame() const
{ return fFrame; }
IntRect Bounds() const;
virtual IntRect Bounds() const;
void SetResizeMode(uint32 resizeMode)
{ fResizeMode = resizeMode; }

View File

@ -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<ServerBitmap> 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<VectorMaskType>::VectorAlphaMask(AlphaMask* previousMask,
template<class VectorMaskType>
ServerBitmap*
VectorAlphaMask<VectorMaskType>::_RenderSource()
VectorAlphaMask<VectorMaskType>::_RenderSource(const IntRect& canvasBounds)
{
fBounds = static_cast<VectorMaskType*>(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<VectorMaskType>::_RenderSource()
engine->SetRendererOffset(fBounds.left, fBounds.top);
OffscreenCanvas canvas(engine,
static_cast<VectorMaskType*>(this)->GetDrawState());
static_cast<VectorMaskType*>(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;
}

View File

@ -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<AlphaMask> 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;
};