app_server: Implemented nested clipping paths
* The alpha mask is no longer stored with 75% more memory than needed, resolving a TODO. * AlphaMasks are now BReferenceables. * AlphaMasks are transferred to a pushed DrawState. * When an AlphaMask is set on a DrawState, it sets the previous state's mask to the AlphaMask. That one now takes care of updating not only itself, but the previous mask as well (which works recursively). * In AlphaMask::Generate(), a combination happens with the previous state's mask, which again works recursively in case the previous mask also needs to be updated. This step is combined with extracting the alpha channel from the UtilityBitmap used to play the ServerPicture. * Fixed some out of bounds access to memory in the "outside" case in agg_clipped_alpha_mask.h. This happened when the requested region was both before and after where the mask has data.
This commit is contained in:
parent
7bc9f84556
commit
77214b5abc
|
@ -21,6 +21,8 @@
|
|||
AlphaMask::AlphaMask(ServerPicture* picture, bool inverse, BPoint origin,
|
||||
const DrawState& drawState)
|
||||
:
|
||||
fPreviousMask(NULL),
|
||||
|
||||
fPicture(picture),
|
||||
fInverse(inverse),
|
||||
fOrigin(origin),
|
||||
|
@ -44,8 +46,8 @@ AlphaMask::AlphaMask(ServerPicture* picture, bool inverse, BPoint origin,
|
|||
AlphaMask::~AlphaMask()
|
||||
{
|
||||
fPicture->ReleaseReference();
|
||||
if (fCachedBitmap)
|
||||
fCachedBitmap->ReleaseReference();
|
||||
delete[] fCachedBitmap;
|
||||
SetPrevious(NULL);
|
||||
}
|
||||
|
||||
|
||||
|
@ -54,6 +56,25 @@ AlphaMask::Update(BRect bounds, BPoint offset)
|
|||
{
|
||||
fViewBounds = bounds;
|
||||
fViewOffset = offset;
|
||||
|
||||
if (fPreviousMask != NULL)
|
||||
fPreviousMask->Update(bounds, offset);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AlphaMask::SetPrevious(AlphaMask* mask)
|
||||
{
|
||||
// Since multiple DrawStates can point to the same AlphaMask,
|
||||
// don't accept ourself as the "previous" mask on the state stack.
|
||||
if (mask == this || mask == fPreviousMask)
|
||||
return;
|
||||
|
||||
if (mask != NULL)
|
||||
mask->AcquireReference();
|
||||
if (fPreviousMask != NULL)
|
||||
fPreviousMask->ReleaseReference();
|
||||
fPreviousMask = mask;
|
||||
}
|
||||
|
||||
|
||||
|
@ -70,23 +91,77 @@ AlphaMask::Generate()
|
|||
return &fScanline;
|
||||
}
|
||||
|
||||
if (fCachedBitmap != NULL)
|
||||
fCachedBitmap->ReleaseReference();
|
||||
uint32 width = fViewBounds.IntegerWidth() + 1;
|
||||
uint32 height = fViewBounds.IntegerHeight() + 1;
|
||||
|
||||
if (fViewBounds != fCachedBounds || fCachedBitmap == NULL) {
|
||||
delete[] fCachedBitmap;
|
||||
fCachedBitmap = new(std::nothrow) uint8[width * height];
|
||||
}
|
||||
|
||||
// If rendering the picture fails, we will draw without any clipping.
|
||||
ServerBitmap* bitmap = _RenderPicture();
|
||||
if (bitmap == NULL) {
|
||||
fCachedBitmap = NULL;
|
||||
if (bitmap == NULL || fCachedBitmap == NULL) {
|
||||
fBuffer.attach(NULL, 0, 0, 0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fCachedBitmap = bitmap;
|
||||
uint8* bits = bitmap->Bits();
|
||||
uint32 bytesPerRow = bitmap->BytesPerRow();
|
||||
uint8* row = bits;
|
||||
uint8* pixel = fCachedBitmap;
|
||||
|
||||
// Let any previous masks also regenerate themselves. Updating the cached
|
||||
// mask bitmap is only necessary after the view size changed or the
|
||||
// scrolling offset, which definitely affects any masks of lower states
|
||||
// as well, so it works recursively until the bottom mask is regenerated.
|
||||
bool transferBitmap = true;
|
||||
if (fPreviousMask != NULL) {
|
||||
fPreviousMask->Generate();
|
||||
if (fPreviousMask->fCachedBitmap != NULL) {
|
||||
uint8* previousBits = fPreviousMask->fCachedBitmap;
|
||||
for (uint32 y = 0; y < height; y++) {
|
||||
for (uint32 x = 0; x < width; x++) {
|
||||
if (previousBits[0] != 0) {
|
||||
if (fInverse)
|
||||
pixel[0] = 255 - row[3];
|
||||
else
|
||||
pixel[0] = row[3];
|
||||
pixel[0] = pixel[0] * previousBits[0] / 255;
|
||||
} else
|
||||
pixel[0] = 0;
|
||||
previousBits++;
|
||||
pixel++;
|
||||
row += 4;
|
||||
}
|
||||
bits += bytesPerRow;
|
||||
row = bits;
|
||||
}
|
||||
transferBitmap = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (transferBitmap) {
|
||||
for (uint32 y = 0; y < height; y++) {
|
||||
for (uint32 x = 0; x < width; x++) {
|
||||
if (fInverse)
|
||||
pixel[0] = 255 - row[3];
|
||||
else
|
||||
pixel[0] = row[3];
|
||||
pixel++;
|
||||
row += 4;
|
||||
}
|
||||
bits += bytesPerRow;
|
||||
row = bits;
|
||||
}
|
||||
}
|
||||
|
||||
bitmap->ReleaseReference();
|
||||
|
||||
fCachedBounds = fViewBounds;
|
||||
fCachedOffset = fViewOffset;
|
||||
|
||||
fBuffer.attach(fCachedBitmap->Bits(), fCachedBitmap->Width(),
|
||||
fCachedBitmap->Height(), fCachedBitmap->BytesPerRow());
|
||||
fBuffer.attach(fCachedBitmap, width, height, width);
|
||||
|
||||
fCachedMask.attach(fBuffer, fViewOffset.x + fOrigin.x,
|
||||
fViewOffset.y + fOrigin.y, fInverse ? 255 : 0);
|
||||
|
@ -98,8 +173,6 @@ AlphaMask::Generate()
|
|||
ServerBitmap*
|
||||
AlphaMask::_RenderPicture() const
|
||||
{
|
||||
// TODO: Only the alpha channel is relevant, but there is no B_ALPHA8
|
||||
// color space, so we use 300% more memory than needed.
|
||||
UtilityBitmap* bitmap = new(std::nothrow) UtilityBitmap(fViewBounds,
|
||||
B_RGBA32, 0);
|
||||
if (bitmap == NULL)
|
||||
|
@ -132,16 +205,6 @@ AlphaMask::_RenderPicture() const
|
|||
context.PopState();
|
||||
delete engine;
|
||||
|
||||
if (!fInverse)
|
||||
return bitmap;
|
||||
|
||||
// Compute the inverse of our bitmap. There probably is a better way.
|
||||
uint32 size = bitmap->BitsLength();
|
||||
uint8* bits = (uint8*)bitmap->Bits();
|
||||
|
||||
for (uint32 i = 3; i < size; i += 4)
|
||||
bits[i] = 255 - bits[i];
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#ifndef ALPHA_MASK_H
|
||||
#define ALPHA_MASK_H
|
||||
|
||||
#include <Referenceable.h>
|
||||
|
||||
#include "agg_clipped_alpha_mask.h"
|
||||
#include "ServerPicture.h"
|
||||
|
@ -18,7 +19,7 @@ class ServerBitmap;
|
|||
class ServerPicture;
|
||||
|
||||
|
||||
class AlphaMask {
|
||||
class AlphaMask : public BReferenceable {
|
||||
public:
|
||||
AlphaMask(ServerPicture* mask, bool inverse,
|
||||
BPoint origin, const DrawState& drawState);
|
||||
|
@ -26,6 +27,8 @@ public:
|
|||
|
||||
void Update(BRect bounds, BPoint offset);
|
||||
|
||||
void SetPrevious(AlphaMask* mask);
|
||||
|
||||
scanline_unpacked_masked_type* Generate();
|
||||
|
||||
private:
|
||||
|
@ -33,6 +36,8 @@ private:
|
|||
|
||||
|
||||
private:
|
||||
AlphaMask* fPreviousMask;
|
||||
|
||||
ServerPicture* fPicture;
|
||||
const bool fInverse;
|
||||
BPoint fOrigin;
|
||||
|
@ -41,7 +46,7 @@ private:
|
|||
BRect fViewBounds;
|
||||
BPoint fViewOffset;
|
||||
|
||||
ServerBitmap* fCachedBitmap;
|
||||
uint8* fCachedBitmap;
|
||||
BRect fCachedBounds;
|
||||
BPoint fCachedOffset;
|
||||
|
||||
|
|
|
@ -101,7 +101,8 @@ DrawState::~DrawState()
|
|||
{
|
||||
delete fClippingRegion;
|
||||
delete fPreviousState;
|
||||
delete fAlphaMask;
|
||||
if (fAlphaMask != NULL)
|
||||
fAlphaMask->ReleaseReference();
|
||||
}
|
||||
|
||||
|
||||
|
@ -115,6 +116,7 @@ DrawState::PushState()
|
|||
next->fOrigin = BPoint(0.0, 0.0);
|
||||
next->fScale = 1.0;
|
||||
next->fPreviousState = this;
|
||||
next->SetAlphaMask(fAlphaMask);
|
||||
}
|
||||
|
||||
return next;
|
||||
|
@ -394,9 +396,17 @@ DrawState::SetAlphaMask(AlphaMask* mask)
|
|||
{
|
||||
// NOTE: In BeOS, it wasn't possible to clip to a BPicture and keep
|
||||
// regular custom clipping to a BRegion at the same time.
|
||||
if (fAlphaMask == mask)
|
||||
return;
|
||||
|
||||
delete fAlphaMask;
|
||||
if (mask != NULL)
|
||||
mask->AcquireReference();
|
||||
if (fAlphaMask != NULL)
|
||||
fAlphaMask->ReleaseReference();
|
||||
fAlphaMask = mask;
|
||||
if (fAlphaMask != NULL && fPreviousState != NULL)
|
||||
fAlphaMask->SetPrevious(fPreviousState->fAlphaMask);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1883,8 +1883,11 @@ fDesktop->LockSingleWindow();
|
|||
if (picture == NULL)
|
||||
break;
|
||||
|
||||
fCurrentView->SetAlphaMask(new(std::nothrow) AlphaMask(
|
||||
picture, inverse, where, *fCurrentView->CurrentState()));
|
||||
AlphaMask* mask = new(std::nothrow) AlphaMask(
|
||||
picture, inverse, where, *fCurrentView->CurrentState());
|
||||
fCurrentView->SetAlphaMask(mask);
|
||||
if (mask != NULL)
|
||||
mask->ReleaseReference();
|
||||
_UpdateDrawState(fCurrentView);
|
||||
|
||||
picture->ReleaseReference();
|
||||
|
|
|
@ -72,27 +72,27 @@ namespace agg
|
|||
x = 0;
|
||||
}
|
||||
|
||||
int rest = 0;
|
||||
if(x + count > xmax)
|
||||
{
|
||||
int rest = x + count - xmax - 1;
|
||||
rest = x + count - xmax - 1;
|
||||
count -= rest;
|
||||
if(count <= 0)
|
||||
{
|
||||
memset(dst, m_outside, num_pix * sizeof(cover_type));
|
||||
return;
|
||||
}
|
||||
memset(covers + count, m_outside, rest * sizeof(cover_type));
|
||||
}
|
||||
|
||||
const int8u* mask = m_rbuf->row_ptr(y) + x * Step + Offset;
|
||||
do
|
||||
while(count != 0)
|
||||
{
|
||||
*covers = (cover_type)((cover_full + (*covers) * (*mask))
|
||||
>> cover_shift);
|
||||
++covers;
|
||||
mask += Step;
|
||||
--count;
|
||||
}
|
||||
|
||||
if(rest > 0)
|
||||
{
|
||||
memset(covers, m_outside, rest * sizeof(cover_type));
|
||||
}
|
||||
while(--count);
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -102,11 +102,8 @@ namespace agg
|
|||
rendering_buffer* m_rbuf;
|
||||
int8u m_outside;
|
||||
|
||||
// TODO this assumes an RGBA bitmap and only uses the alpha channel.
|
||||
// We should keep the masking bitmap as an 8-bit bitmap with only the
|
||||
// alpha channel, to save memory. (this would be Step=1, Offset=0)
|
||||
static const int Step = 4;
|
||||
static const int Offset = 3;
|
||||
static const int Step = 1;
|
||||
static const int Offset = 0;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue