michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "gfxBlur.h" michael@0: #include "gfxContext.h" michael@0: #include "gfxImageSurface.h" michael@0: #include "gfxPlatform.h" michael@0: michael@0: #include "mozilla/gfx/Blur.h" michael@0: #include "mozilla/gfx/2D.h" michael@0: michael@0: using namespace mozilla::gfx; michael@0: michael@0: gfxAlphaBoxBlur::gfxAlphaBoxBlur() michael@0: : mBlur(nullptr) michael@0: { michael@0: } michael@0: michael@0: gfxAlphaBoxBlur::~gfxAlphaBoxBlur() michael@0: { michael@0: mContext = nullptr; michael@0: delete mBlur; michael@0: } michael@0: michael@0: gfxContext* michael@0: gfxAlphaBoxBlur::Init(const gfxRect& aRect, michael@0: const gfxIntSize& aSpreadRadius, michael@0: const gfxIntSize& aBlurRadius, michael@0: const gfxRect* aDirtyRect, michael@0: const gfxRect* aSkipRect) michael@0: { michael@0: mozilla::gfx::Rect rect(Float(aRect.x), Float(aRect.y), michael@0: Float(aRect.width), Float(aRect.height)); michael@0: IntSize spreadRadius(aSpreadRadius.width, aSpreadRadius.height); michael@0: IntSize blurRadius(aBlurRadius.width, aBlurRadius.height); michael@0: nsAutoPtr dirtyRect; michael@0: if (aDirtyRect) { michael@0: dirtyRect = new mozilla::gfx::Rect(Float(aDirtyRect->x), michael@0: Float(aDirtyRect->y), michael@0: Float(aDirtyRect->width), michael@0: Float(aDirtyRect->height)); michael@0: } michael@0: nsAutoPtr skipRect; michael@0: if (aSkipRect) { michael@0: skipRect = new mozilla::gfx::Rect(Float(aSkipRect->x), michael@0: Float(aSkipRect->y), michael@0: Float(aSkipRect->width), michael@0: Float(aSkipRect->height)); michael@0: } michael@0: michael@0: mBlur = new AlphaBoxBlur(rect, spreadRadius, blurRadius, dirtyRect, skipRect); michael@0: size_t blurDataSize = mBlur->GetSurfaceAllocationSize(); michael@0: if (blurDataSize == 0) michael@0: return nullptr; michael@0: michael@0: IntSize size = mBlur->GetSize(); michael@0: michael@0: // Make an alpha-only surface to draw on. We will play with the data after michael@0: // everything is drawn to create a blur effect. michael@0: mData = new (std::nothrow) unsigned char[blurDataSize]; michael@0: if (!mData) { michael@0: return nullptr; michael@0: } michael@0: memset(mData, 0, blurDataSize); michael@0: michael@0: mozilla::RefPtr dt = michael@0: gfxPlatform::GetPlatform()->CreateDrawTargetForData(mData, size, michael@0: mBlur->GetStride(), michael@0: SurfaceFormat::A8); michael@0: if (!dt) { michael@0: nsRefPtr image = michael@0: new gfxImageSurface(mData, michael@0: gfxIntSize(size.width, size.height), michael@0: mBlur->GetStride(), michael@0: gfxImageFormat::A8); michael@0: dt = Factory::CreateDrawTargetForCairoSurface(image->CairoSurface(), size); michael@0: if (!dt) { michael@0: return nullptr; michael@0: } michael@0: } michael@0: michael@0: IntRect irect = mBlur->GetRect(); michael@0: gfxPoint topleft(irect.TopLeft().x, irect.TopLeft().y); michael@0: michael@0: mContext = new gfxContext(dt); michael@0: mContext->Translate(-topleft); michael@0: michael@0: return mContext; michael@0: } michael@0: michael@0: void michael@0: gfxAlphaBoxBlur::Paint(gfxContext* aDestinationCtx) michael@0: { michael@0: if (!mContext) michael@0: return; michael@0: michael@0: mBlur->Blur(mData); michael@0: michael@0: mozilla::gfx::Rect* dirtyRect = mBlur->GetDirtyRect(); michael@0: michael@0: DrawTarget *dest = aDestinationCtx->GetDrawTarget(); michael@0: if (!dest) { michael@0: NS_WARNING("Blurring not supported for Thebes contexts!"); michael@0: return; michael@0: } michael@0: michael@0: mozilla::RefPtr mask michael@0: = dest->CreateSourceSurfaceFromData(mData, michael@0: mBlur->GetSize(), michael@0: mBlur->GetStride(), michael@0: SurfaceFormat::A8); michael@0: if (!mask) { michael@0: NS_ERROR("Failed to create mask!"); michael@0: return; michael@0: } michael@0: michael@0: nsRefPtr thebesPat = aDestinationCtx->GetPattern(); michael@0: Pattern* pat = thebesPat->GetPattern(dest, nullptr); michael@0: michael@0: Matrix oldTransform = dest->GetTransform(); michael@0: Matrix newTransform = oldTransform; michael@0: newTransform.Translate(mBlur->GetRect().x, mBlur->GetRect().y); michael@0: michael@0: // Avoid a semi-expensive clip operation if we can, otherwise michael@0: // clip to the dirty rect michael@0: if (dirtyRect) { michael@0: dest->PushClipRect(*dirtyRect); michael@0: } michael@0: michael@0: dest->SetTransform(newTransform); michael@0: dest->MaskSurface(*pat, mask, Point(0, 0)); michael@0: dest->SetTransform(oldTransform); michael@0: michael@0: if (dirtyRect) { michael@0: dest->PopClip(); michael@0: } michael@0: } michael@0: michael@0: gfxIntSize gfxAlphaBoxBlur::CalculateBlurRadius(const gfxPoint& aStd) michael@0: { michael@0: mozilla::gfx::Point std(Float(aStd.x), Float(aStd.y)); michael@0: IntSize size = AlphaBoxBlur::CalculateBlurRadius(std); michael@0: return gfxIntSize(size.width, size.height); michael@0: } michael@0: michael@0: /* static */ void michael@0: gfxAlphaBoxBlur::BlurRectangle(gfxContext *aDestinationCtx, michael@0: const gfxRect& aRect, michael@0: gfxCornerSizes* aCornerRadii, michael@0: const gfxPoint& aBlurStdDev, michael@0: const gfxRGBA& aShadowColor, michael@0: const gfxRect& aDirtyRect, michael@0: const gfxRect& aSkipRect) michael@0: { michael@0: gfxIntSize blurRadius = CalculateBlurRadius(aBlurStdDev); michael@0: michael@0: // Create the temporary surface for blurring michael@0: gfxAlphaBoxBlur blur; michael@0: gfxContext *dest = blur.Init(aRect, gfxIntSize(), blurRadius, &aDirtyRect, &aSkipRect); michael@0: michael@0: if (!dest) { michael@0: return; michael@0: } michael@0: michael@0: gfxRect shadowGfxRect = aRect; michael@0: shadowGfxRect.Round(); michael@0: michael@0: dest->NewPath(); michael@0: if (aCornerRadii) { michael@0: dest->RoundedRectangle(shadowGfxRect, *aCornerRadii); michael@0: } else { michael@0: dest->Rectangle(shadowGfxRect); michael@0: } michael@0: dest->Fill(); michael@0: michael@0: aDestinationCtx->SetColor(aShadowColor); michael@0: blur.Paint(aDestinationCtx); michael@0: }