diff --git a/src/servers/app/AlphaMask.cpp b/src/servers/app/AlphaMask.cpp index 0c6ab9babc..69b0eb2c0a 100644 --- a/src/servers/app/AlphaMask.cpp +++ b/src/servers/app/AlphaMask.cpp @@ -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; } diff --git a/src/servers/app/AlphaMask.h b/src/servers/app/AlphaMask.h index 9521262e4a..661ec7768e 100644 --- a/src/servers/app/AlphaMask.h +++ b/src/servers/app/AlphaMask.h @@ -6,6 +6,7 @@ #ifndef ALPHA_MASK_H #define ALPHA_MASK_H +#include #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; diff --git a/src/servers/app/DrawState.cpp b/src/servers/app/DrawState.cpp index f2b18ec975..767d77607b 100644 --- a/src/servers/app/DrawState.cpp +++ b/src/servers/app/DrawState.cpp @@ -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); + } diff --git a/src/servers/app/ServerWindow.cpp b/src/servers/app/ServerWindow.cpp index fd19504cc2..1083673e07 100644 --- a/src/servers/app/ServerWindow.cpp +++ b/src/servers/app/ServerWindow.cpp @@ -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(); diff --git a/src/servers/app/agg_clipped_alpha_mask.h b/src/servers/app/agg_clipped_alpha_mask.h index 5e9c22d1d2..73816ad1d7 100644 --- a/src/servers/app/agg_clipped_alpha_mask.h +++ b/src/servers/app/agg_clipped_alpha_mask.h @@ -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; }; }