Bug 845125 - Add gfxImageSurface constructor that allocates more space than needed, change AlphaBoxBlur to not hold on to the data and let the callers manage it. r=jmuizelaar

This commit is contained in:
Milan Sreckovic 2013-04-19 12:13:18 +02:00
Родитель a1f1d83c09
Коммит 1de038645a
7 изменённых файлов: 139 добавлений и 93 удалений

Просмотреть файл

@ -1,3 +1,5 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
@ -333,8 +335,7 @@ AlphaBoxBlur::AlphaBoxBlur(const Rect& aRect,
const Rect* aSkipRect)
: mSpreadRadius(aSpreadRadius),
mBlurRadius(aBlurRadius),
mData(nullptr),
mFreeData(true)
mSurfaceAllocationSize(-1)
{
Rect rect(aRect);
rect.Inflate(Size(aBlurRadius + aSpreadRadius));
@ -385,38 +386,30 @@ AlphaBoxBlur::AlphaBoxBlur(const Rect& aRect,
// in our blurring code.
CheckedInt<int32_t> size = CheckedInt<int32_t>(mStride) * mRect.height + 3;
if (size.isValid()) {
mData = new uint8_t[size.value()];
memset(mData, 0, size.value());
mSurfaceAllocationSize = size.value();
}
}
}
AlphaBoxBlur::AlphaBoxBlur(uint8_t* aData,
const Rect& aRect,
AlphaBoxBlur::AlphaBoxBlur(const Rect& aRect,
int32_t aStride,
float aSigma)
: mRect(int32_t(aRect.x), int32_t(aRect.y),
int32_t(aRect.width), int32_t(aRect.height)),
mSpreadRadius(),
mBlurRadius(CalculateBlurRadius(Point(aSigma, aSigma))),
mData(aData),
mFreeData(false),
mStride(aStride)
mStride(aStride),
mSurfaceAllocationSize(-1)
{
CheckedInt<int32_t> minDataSize = CheckedInt<int32_t>(aRect.width*aRect.height);
if (minDataSize.isValid()) {
mSurfaceAllocationSize = minDataSize.value();
}
}
AlphaBoxBlur::~AlphaBoxBlur()
{
if (mFreeData) {
delete [] mData;
}
}
unsigned char*
AlphaBoxBlur::GetData()
{
return mData;
}
IntSize
@ -448,10 +441,16 @@ AlphaBoxBlur::GetDirtyRect()
return nullptr;
}
void
AlphaBoxBlur::Blur()
int32_t
AlphaBoxBlur::GetSurfaceAllocationSize() const
{
if (!mData) {
return mSurfaceAllocationSize;
}
void
AlphaBoxBlur::Blur(uint8_t* aData)
{
if (!aData) {
return;
}
@ -468,8 +467,8 @@ AlphaBoxBlur::Blur()
memset(tmpData, 0, szB);
SpreadHorizontal(mData, tmpData, mSpreadRadius.width, GetSize().width, GetSize().height, stride, mSkipRect);
SpreadVertical(tmpData, mData, mSpreadRadius.height, GetSize().width, GetSize().height, stride, mSkipRect);
SpreadHorizontal(aData, tmpData, mSpreadRadius.width, GetSize().width, GetSize().height, stride, mSkipRect);
SpreadVertical(tmpData, aData, mSpreadRadius.height, GetSize().width, GetSize().height, stride, mSkipRect);
delete [] tmpData;
}
@ -494,7 +493,7 @@ AlphaBoxBlur::Blur()
uint8_t* tmpData = new uint8_t[szB];
memset(tmpData, 0, szB);
uint8_t* a = mData;
uint8_t* a = aData;
uint8_t* b = tmpData;
if (mBlurRadius.width > 0) {
BoxBlurHorizontal(a, b, horizontalLobes[0][0], horizontalLobes[0][1], stride, GetSize().height, mSkipRect);
@ -502,7 +501,7 @@ AlphaBoxBlur::Blur()
BoxBlurHorizontal(a, b, horizontalLobes[2][0], horizontalLobes[2][1], stride, GetSize().height, mSkipRect);
} else {
a = tmpData;
b = mData;
b = aData;
}
// The result is in 'b' here.
if (mBlurRadius.height > 0) {
@ -514,7 +513,7 @@ AlphaBoxBlur::Blur()
}
// The result is in 'a' here.
if (a == tmpData) {
memcpy(mData, tmpData, szB);
memcpy(aData, tmpData, szB);
}
delete [] tmpData;
} else {
@ -526,20 +525,20 @@ AlphaBoxBlur::Blur()
#ifdef USE_SSE2
if (Factory::HasSSE2()) {
BoxBlur_SSE2(horizontalLobes[0][0], horizontalLobes[0][1], verticalLobes[0][0],
BoxBlur_SSE2(aData, horizontalLobes[0][0], horizontalLobes[0][1], verticalLobes[0][0],
verticalLobes[0][1], integralImage, integralImageStride);
BoxBlur_SSE2(horizontalLobes[1][0], horizontalLobes[1][1], verticalLobes[1][0],
BoxBlur_SSE2(aData, horizontalLobes[1][0], horizontalLobes[1][1], verticalLobes[1][0],
verticalLobes[1][1], integralImage, integralImageStride);
BoxBlur_SSE2(horizontalLobes[2][0], horizontalLobes[2][1], verticalLobes[2][0],
BoxBlur_SSE2(aData, horizontalLobes[2][0], horizontalLobes[2][1], verticalLobes[2][0],
verticalLobes[2][1], integralImage, integralImageStride);
} else
#endif
{
BoxBlur_C(horizontalLobes[0][0], horizontalLobes[0][1], verticalLobes[0][0],
BoxBlur_C(aData, horizontalLobes[0][0], horizontalLobes[0][1], verticalLobes[0][0],
verticalLobes[0][1], integralImage, integralImageStride);
BoxBlur_C(horizontalLobes[1][0], horizontalLobes[1][1], verticalLobes[1][0],
BoxBlur_C(aData, horizontalLobes[1][0], horizontalLobes[1][1], verticalLobes[1][0],
verticalLobes[1][1], integralImage, integralImageStride);
BoxBlur_C(horizontalLobes[2][0], horizontalLobes[2][1], verticalLobes[2][0],
BoxBlur_C(aData, horizontalLobes[2][0], horizontalLobes[2][1], verticalLobes[2][0],
verticalLobes[2][1], integralImage, integralImageStride);
}
}
@ -626,7 +625,8 @@ GenerateIntegralImage_C(int32_t aLeftInflation, int32_t aRightInflation,
* Attempt to do an in-place box blur using an integral image.
*/
void
AlphaBoxBlur::BoxBlur_C(int32_t aLeftLobe,
AlphaBoxBlur::BoxBlur_C(uint8_t* aData,
int32_t aLeftLobe,
int32_t aRightLobe,
int32_t aTopLobe,
int32_t aBottomLobe,
@ -655,7 +655,7 @@ AlphaBoxBlur::BoxBlur_C(int32_t aLeftLobe,
int32_t leftInflation = RoundUpToMultipleOf4(aLeftLobe).value();
GenerateIntegralImage_C(leftInflation, aRightLobe, aTopLobe, aBottomLobe,
aIntegralImage, aIntegralImageStride, mData,
aIntegralImage, aIntegralImageStride, aData,
mStride, size);
uint32_t reciprocal = uint32_t((uint64_t(1) << 32) / boxSize);
@ -665,7 +665,7 @@ AlphaBoxBlur::BoxBlur_C(int32_t aLeftLobe,
// Storing these locally makes this about 30% faster! Presumably the compiler
// can't be sure we're not altering the member variables in this loop.
IntRect skipRect = mSkipRect;
uint8_t *data = mData;
uint8_t *data = aData;
int32_t stride = mStride;
for (int32_t y = 0; y < size.height; y++) {
bool inSkipRectY = y > skipRect.y && y < skipRect.YMost();

Просмотреть файл

@ -1,3 +1,5 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
@ -24,21 +26,21 @@ namespace gfx {
* would be obtained using a different (rotated) set of axes. A triple
* box blur is a very close approximation of a Gaussian.
*
* Creates an 8-bit alpha channel context for callers to draw in,
* spreads the contents of that context, and blurs the contents.
* This is a "service" class; the constructors set up all the information
* based on the values and compute the minimum size for an 8-bit alpha
* channel context.
* The callers are responsible for creating and managing the backing surface
* and passing the pointer to the data to the Blur() method. This class does
* not retain the pointer to the data outside of the Blur() call.
*
* A spread N makes each output pixel the maximum value of all source
* pixels within a square of side length 2N+1 centered on the output pixel.
*
* A temporary surface is created in the Init function. The caller then draws
* any desired content onto the context acquired through GetContext, and lastly
* calls Paint to apply the blurred content as an alpha mask.
*/
class GFX2D_API AlphaBoxBlur
{
public:
/** Constructs a box blur and initializes the backing surface.
/** Constructs a box blur and computes the backing surface size.
*
* @param aRect The coordinates of the surface to create in device units.
*
@ -62,29 +64,19 @@ public:
const Rect* aDirtyRect,
const Rect* aSkipRect);
AlphaBoxBlur(uint8_t* aData,
const Rect& aRect,
AlphaBoxBlur(const Rect& aRect,
int32_t aStride,
float aSigma);
~AlphaBoxBlur();
/**
* Return the pointer to memory allocated by the constructor for the 8-bit
* alpha surface you need to be blurred. After you draw to this surface, call
* Blur(), below, to have its contents blurred.
*/
unsigned char* GetData();
/**
* Return the size, in pixels, of the 8-bit alpha surface backed by the
* pointer returned by GetData().
* Return the size, in pixels, of the 8-bit alpha surface we'd use.
*/
IntSize GetSize();
/**
* Return the stride, in bytes, of the 8-bit alpha surface backed by the
* pointer returned by GetData().
* Return the stride, in bytes, of the 8-bit alpha surface we'd use.
*/
int32_t GetStride();
@ -100,10 +92,20 @@ public:
Rect* GetDirtyRect();
/**
* Perform the blur in-place on the surface backed by the pointer returned by
* GetData().
* Return the minimum buffer size that should be given to Blur() method. If
* negative, the class is not properly setup for blurring. Note that this
* includes the extra three bytes on top of the stride*width, where something
* like gfxImageSurface::GetDataSize() would report without it, even if it
* happens to have the extra bytes.
*/
void Blur();
int32_t GetSurfaceAllocationSize() const;
/**
* Perform the blur in-place on the surface backed by specified 8-bit
* alpha surface data. The size must be at least that returned by
* GetSurfaceAllocationSize() or bad things will happen.
*/
void Blur(uint8_t* aData);
/**
* Calculates a blur radius that, when used with box blur, approximates a
@ -115,9 +117,11 @@ public:
private:
void BoxBlur_C(int32_t aLeftLobe, int32_t aRightLobe, int32_t aTopLobe,
void BoxBlur_C(uint8_t* aData,
int32_t aLeftLobe, int32_t aRightLobe, int32_t aTopLobe,
int32_t aBottomLobe, uint32_t *aIntegralImage, size_t aIntegralImageStride);
void BoxBlur_SSE2(int32_t aLeftLobe, int32_t aRightLobe, int32_t aTopLobe,
void BoxBlur_SSE2(uint8_t* aData,
int32_t aLeftLobe, int32_t aRightLobe, int32_t aTopLobe,
int32_t aBottomLobe, uint32_t *aIntegralImage, size_t aIntegralImageStride);
static CheckedInt<int32_t> RoundUpToMultipleOf4(int32_t aVal);
@ -150,20 +154,15 @@ private:
IntSize mBlurRadius;
/**
* A pointer to the backing 8-bit alpha surface.
*/
uint8_t* mData;
/**
* True if we need to dispose the data.
*/
bool mFreeData;
/**
* The stride of the data contained in mData.
* The stride of the data passed to Blur()
*/
int32_t mStride;
/**
* The minimum size of the buffer needed for the Blur() operation.
*/
int32_t mSurfaceAllocationSize;
/**
* Whether mDirtyRect contains valid data.
*/

Просмотреть файл

@ -174,7 +174,8 @@ GenerateIntegralImage_SSE2(int32_t aLeftInflation, int32_t aRightInflation,
* Attempt to do an in-place box blur using an integral image.
*/
void
AlphaBoxBlur::BoxBlur_SSE2(int32_t aLeftLobe,
AlphaBoxBlur::BoxBlur_SSE2(uint8_t* aData,
int32_t aLeftLobe,
int32_t aRightLobe,
int32_t aTopLobe,
int32_t aBottomLobe,
@ -204,7 +205,7 @@ AlphaBoxBlur::BoxBlur_SSE2(int32_t aLeftLobe,
int32_t leftInflation = RoundUpToMultipleOf4(aLeftLobe).value();
GenerateIntegralImage_SSE2(leftInflation, aRightLobe, aTopLobe, aBottomLobe,
aIntegralImage, aIntegralImageStride, mData,
aIntegralImage, aIntegralImageStride, aData,
mStride, size);
__m128i divisor = _mm_set1_epi32(reciprocal);
@ -216,7 +217,7 @@ AlphaBoxBlur::BoxBlur_SSE2(int32_t aLeftLobe,
IntRect skipRect = mSkipRect;
int32_t stride = mStride;
uint8_t *data = mData;
uint8_t *data = aData;
for (int32_t y = 0; y < size.height; y++) {
bool inSkipRectY = y > skipRect.y && y < skipRect.YMost();

Просмотреть файл

@ -428,11 +428,10 @@ DrawTargetCairo::DrawSurfaceWithShadow(SourceSurface *aSurface,
MOZ_ASSERT(cairo_surface_get_type(blursurf) == CAIRO_SURFACE_TYPE_IMAGE);
Rect extents(0, 0, width, height);
AlphaBoxBlur blur(cairo_image_surface_get_data(blursurf),
extents,
AlphaBoxBlur blur(extents,
cairo_image_surface_get_stride(blursurf),
aSigma);
blur.Blur();
blur.Blur(cairo_image_surface_get_data(blursurf));
} else {
blursurf = sourcesurf;
surf = sourcesurf;

Просмотреть файл

@ -16,8 +16,6 @@ gfxAlphaBoxBlur::gfxAlphaBoxBlur()
gfxAlphaBoxBlur::~gfxAlphaBoxBlur()
{
// Drop references to mContext and mImageSurface before we delete mBlur,
// because the image surface points to data in mBlur.
mContext = nullptr;
mImageSurface = nullptr;
delete mBlur;
@ -50,17 +48,19 @@ gfxAlphaBoxBlur::Init(const gfxRect& aRect,
}
mBlur = new AlphaBoxBlur(rect, spreadRadius, blurRadius, dirtyRect, skipRect);
unsigned char* data = mBlur->GetData();
if (!data)
return nullptr;
int32_t blurDataSize = mBlur->GetSurfaceAllocationSize();
if (blurDataSize <= 0)
return nullptr;
IntSize size = mBlur->GetSize();
// Make an alpha-only surface to draw on. We will play with the data after
// everything is drawn to create a blur effect.
mImageSurface = new gfxImageSurface(data, gfxIntSize(size.width, size.height),
mImageSurface = new gfxImageSurface(gfxIntSize(size.width, size.height),
gfxASurface::ImageFormatA8,
mBlur->GetStride(),
gfxASurface::ImageFormatA8);
blurDataSize,
true);
if (mImageSurface->CairoStatus())
return nullptr;
@ -83,7 +83,7 @@ gfxAlphaBoxBlur::Paint(gfxContext* aDestinationCtx, const gfxPoint& offset)
if (!mContext)
return;
mBlur->Blur();
mBlur->Blur(mImageSurface->Data());
mozilla::gfx::Rect* dirtyrect = mBlur->GetDirtyRect();

Просмотреть файл

@ -95,12 +95,26 @@ TryAllocAlignedBytes(size_t aSize)
#endif
}
gfxImageSurface::gfxImageSurface(const gfxIntSize& size, gfxImageFormat format, bool aClear) :
mSize(size), mOwnsData(false), mData(nullptr), mFormat(format)
gfxImageSurface::gfxImageSurface(const gfxIntSize& size, gfxImageFormat format, bool aClear)
: mSize(size), mData(nullptr), mFormat(format)
{
mStride = ComputeStride();
AllocateAndInit(0, 0, aClear);
}
if (!CheckSurfaceSize(size))
void
gfxImageSurface::AllocateAndInit(long aStride, int32_t aMinimalAllocation,
bool aClear)
{
// The callers should set mSize and mFormat.
MOZ_ASSERT(!mData);
mData = nullptr;
mOwnsData = false;
mStride = aStride > 0 ? aStride : ComputeStride();
if (aMinimalAllocation < mSize.height * mStride)
aMinimalAllocation = mSize.height * mStride;
if (!CheckSurfaceSize(mSize))
MakeInvalid();
// if we have a zero-sized surface, just leave mData nullptr
@ -108,18 +122,18 @@ gfxImageSurface::gfxImageSurface(const gfxIntSize& size, gfxImageFormat format,
// This can fail to allocate memory aligned as we requested,
// or it can fail to allocate any memory at all.
mData = (unsigned char *) TryAllocAlignedBytes(mSize.height * mStride);
mData = (unsigned char *) TryAllocAlignedBytes(aMinimalAllocation);
if (!mData)
return;
if (aClear)
memset(mData, 0, mSize.height * mStride);
memset(mData, 0, aMinimalAllocation);
}
mOwnsData = true;
cairo_surface_t *surface =
cairo_image_surface_create_for_data((unsigned char*)mData,
(cairo_format_t)format,
(cairo_format_t)mFormat,
mSize.width,
mSize.height,
mStride);
@ -132,6 +146,13 @@ gfxImageSurface::gfxImageSurface(const gfxIntSize& size, gfxImageFormat format,
}
}
gfxImageSurface::gfxImageSurface(const gfxIntSize& size, gfxImageFormat format,
long aStride, int32_t aExtraBytes, bool aClear)
: mSize(size), mData(nullptr), mFormat(format)
{
AllocateAndInit(aStride, aExtraBytes, aClear);
}
gfxImageSurface::gfxImageSurface(cairo_surface_t *csurf)
{
mSize.width = cairo_image_surface_get_width(csurf);

Просмотреть файл

@ -46,6 +46,25 @@ public:
* @see gfxImageFormat
*/
gfxImageSurface(const gfxIntSize& size, gfxImageFormat format, bool aClear = true);
/**
* Construct an image surface, with a specified stride and allowing the
* allocation of more memory than required for the storage of the surface
* itself. When aStride and aMinimalAllocation are <=0, this constructor
* is the equivalent of the preceeding one.
*
* @param format Format of the data
* @param aSize The size of the buffer
* @param aStride The stride of the buffer - if <=0, use ComputeStride()
* @param aMinimalAllocation Allocate at least this many bytes. If smaller
* than width * stride, or width*stride <=0, this value is ignored.
* @param aClear
*
* @see gfxImageFormat
*/
gfxImageSurface(const gfxIntSize& aSize, gfxImageFormat aFormat,
long aStride, int32_t aMinimalAllocation, bool aClear);
gfxImageSurface(cairo_surface_t *csurf);
virtual ~gfxImageSurface();
@ -104,7 +123,14 @@ protected:
gfxImageSurface();
void InitWithData(unsigned char *aData, const gfxIntSize& aSize,
long aStride, gfxImageFormat aFormat);
/**
* See the parameters to the matching constructor. This should only
* be called once, in the constructor, which has already set mSize
* and mFormat.
*/
void AllocateAndInit(long aStride, int32_t aMinimalAllocation, bool aClear);
void InitFromSurface(cairo_surface_t *csurf);
long ComputeStride() const { return ComputeStride(mSize, mFormat); }