mirror of
				https://github.com/mozilla/gecko-dev.git
				synced 2025-11-04 10:18:41 +02:00 
			
		
		
		
	Depends on D170369 Differential Revision: https://phabricator.services.mozilla.com/D170370
		
			
				
	
	
		
			188 lines
		
	
	
	
		
			6.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			188 lines
		
	
	
	
		
			6.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* -*- 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/. */
 | 
						|
 | 
						|
// Main header first:
 | 
						|
#include "SVGMaskFrame.h"
 | 
						|
 | 
						|
// Keep others in (case-insensitive) order:
 | 
						|
#include "AutoReferenceChainGuard.h"
 | 
						|
#include "gfx2DGlue.h"
 | 
						|
#include "gfxContext.h"
 | 
						|
#include "mozilla/PresShell.h"
 | 
						|
#include "mozilla/RefPtr.h"
 | 
						|
#include "mozilla/SVGObserverUtils.h"
 | 
						|
#include "mozilla/SVGUtils.h"
 | 
						|
#include "mozilla/dom/SVGMaskElement.h"
 | 
						|
#include "mozilla/dom/SVGUnitTypesBinding.h"
 | 
						|
#include "mozilla/gfx/2D.h"
 | 
						|
 | 
						|
using namespace mozilla::dom;
 | 
						|
using namespace mozilla::dom::SVGUnitTypes_Binding;
 | 
						|
using namespace mozilla::gfx;
 | 
						|
using namespace mozilla::image;
 | 
						|
 | 
						|
nsIFrame* NS_NewSVGMaskFrame(mozilla::PresShell* aPresShell,
 | 
						|
                             mozilla::ComputedStyle* aStyle) {
 | 
						|
  return new (aPresShell)
 | 
						|
      mozilla::SVGMaskFrame(aStyle, aPresShell->GetPresContext());
 | 
						|
}
 | 
						|
 | 
						|
namespace mozilla {
 | 
						|
 | 
						|
NS_IMPL_FRAMEARENA_HELPERS(SVGMaskFrame)
 | 
						|
 | 
						|
already_AddRefed<SourceSurface> SVGMaskFrame::GetMaskForMaskedFrame(
 | 
						|
    MaskParams& aParams) {
 | 
						|
  // Make sure we break reference loops and over long reference chains:
 | 
						|
  static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
 | 
						|
  AutoReferenceChainGuard refChainGuard(this, &mInUse, &sRefChainLengthCounter);
 | 
						|
  if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
 | 
						|
    // Break reference chain
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  gfxRect maskArea = GetMaskArea(aParams.maskedFrame);
 | 
						|
  if (maskArea.IsEmpty()) {
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
  // Get the clip extents in device space:
 | 
						|
  // Minimizing the mask surface extents (using both the current clip extents
 | 
						|
  // and maskArea) is important for performance.
 | 
						|
  //
 | 
						|
  gfxRect maskSurfaceRectDouble = aParams.toUserSpace.TransformBounds(maskArea);
 | 
						|
  Rect maskSurfaceRect = ToRect(maskSurfaceRectDouble);
 | 
						|
  maskSurfaceRect.RoundOut();
 | 
						|
 | 
						|
  StyleMaskType maskType;
 | 
						|
  if (aParams.maskMode == StyleMaskMode::MatchSource) {
 | 
						|
    maskType = StyleSVGReset()->mMaskType;
 | 
						|
  } else {
 | 
						|
    maskType = aParams.maskMode == StyleMaskMode::Luminance
 | 
						|
                   ? StyleMaskType::Luminance
 | 
						|
                   : StyleMaskType::Alpha;
 | 
						|
  }
 | 
						|
 | 
						|
  RefPtr<DrawTarget> maskDT;
 | 
						|
  if (maskType == StyleMaskType::Luminance) {
 | 
						|
    maskDT = aParams.dt->CreateClippedDrawTarget(maskSurfaceRect,
 | 
						|
                                                 SurfaceFormat::B8G8R8A8);
 | 
						|
  } else {
 | 
						|
    maskDT =
 | 
						|
        aParams.dt->CreateClippedDrawTarget(maskSurfaceRect, SurfaceFormat::A8);
 | 
						|
  }
 | 
						|
 | 
						|
  if (!maskDT || !maskDT->IsValid()) {
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  gfxContext tmpCtx(maskDT, /* aPreserveTransform */ true);
 | 
						|
 | 
						|
  mMatrixForChildren =
 | 
						|
      GetMaskTransform(aParams.maskedFrame) * aParams.toUserSpace;
 | 
						|
 | 
						|
  for (nsIFrame* kid = mFrames.FirstChild(); kid; kid = kid->GetNextSibling()) {
 | 
						|
    gfxMatrix m = mMatrixForChildren;
 | 
						|
 | 
						|
    // The CTM of each frame referencing us can be different
 | 
						|
    ISVGDisplayableFrame* SVGFrame = do_QueryFrame(kid);
 | 
						|
    if (SVGFrame) {
 | 
						|
      SVGFrame->NotifySVGChanged(ISVGDisplayableFrame::TRANSFORM_CHANGED);
 | 
						|
      m = SVGUtils::GetTransformMatrixInUserSpace(kid) * m;
 | 
						|
    }
 | 
						|
 | 
						|
    SVGUtils::PaintFrameWithEffects(kid, tmpCtx, m, aParams.imgParams);
 | 
						|
  }
 | 
						|
 | 
						|
  RefPtr<SourceSurface> surface;
 | 
						|
  if (maskType == StyleMaskType::Luminance) {
 | 
						|
    auto luminanceType = LuminanceType::LUMINANCE;
 | 
						|
    if (StyleSVG()->mColorInterpolation == StyleColorInterpolation::Linearrgb) {
 | 
						|
      luminanceType = LuminanceType::LINEARRGB;
 | 
						|
    }
 | 
						|
 | 
						|
    RefPtr<SourceSurface> maskSnapshot =
 | 
						|
        maskDT->IntoLuminanceSource(luminanceType, aParams.opacity);
 | 
						|
    if (!maskSnapshot) {
 | 
						|
      return nullptr;
 | 
						|
    }
 | 
						|
    surface = std::move(maskSnapshot);
 | 
						|
  } else {
 | 
						|
    maskDT->FillRect(maskSurfaceRect,
 | 
						|
                     ColorPattern(DeviceColor::MaskWhite(aParams.opacity)),
 | 
						|
                     DrawOptions(1, CompositionOp::OP_IN));
 | 
						|
    RefPtr<SourceSurface> maskSnapshot = maskDT->Snapshot();
 | 
						|
    if (!maskSnapshot) {
 | 
						|
      return nullptr;
 | 
						|
    }
 | 
						|
    surface = std::move(maskSnapshot);
 | 
						|
  }
 | 
						|
 | 
						|
  return surface.forget();
 | 
						|
}
 | 
						|
 | 
						|
gfxRect SVGMaskFrame::GetMaskArea(nsIFrame* aMaskedFrame) {
 | 
						|
  SVGMaskElement* maskElem = static_cast<SVGMaskElement*>(GetContent());
 | 
						|
 | 
						|
  uint16_t units =
 | 
						|
      maskElem->mEnumAttributes[SVGMaskElement::MASKUNITS].GetAnimValue();
 | 
						|
  gfxRect bbox;
 | 
						|
  if (units == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
 | 
						|
    bbox =
 | 
						|
        SVGUtils::GetBBox(aMaskedFrame, SVGUtils::eUseFrameBoundsForOuterSVG |
 | 
						|
                                            SVGUtils::eBBoxIncludeFillGeometry);
 | 
						|
  }
 | 
						|
 | 
						|
  // Bounds in the user space of aMaskedFrame
 | 
						|
  gfxRect maskArea = SVGUtils::GetRelativeRect(
 | 
						|
      units, &maskElem->mLengthAttributes[SVGMaskElement::ATTR_X], bbox,
 | 
						|
      aMaskedFrame);
 | 
						|
 | 
						|
  return maskArea;
 | 
						|
}
 | 
						|
 | 
						|
nsresult SVGMaskFrame::AttributeChanged(int32_t aNameSpaceID,
 | 
						|
                                        nsAtom* aAttribute, int32_t aModType) {
 | 
						|
  if (aNameSpaceID == kNameSpaceID_None &&
 | 
						|
      (aAttribute == nsGkAtoms::x || aAttribute == nsGkAtoms::y ||
 | 
						|
       aAttribute == nsGkAtoms::width || aAttribute == nsGkAtoms::height ||
 | 
						|
       aAttribute == nsGkAtoms::maskUnits ||
 | 
						|
       aAttribute == nsGkAtoms::maskContentUnits)) {
 | 
						|
    SVGObserverUtils::InvalidateDirectRenderingObservers(this);
 | 
						|
  }
 | 
						|
 | 
						|
  return SVGContainerFrame::AttributeChanged(aNameSpaceID, aAttribute,
 | 
						|
                                             aModType);
 | 
						|
}
 | 
						|
 | 
						|
#ifdef DEBUG
 | 
						|
void SVGMaskFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
 | 
						|
                        nsIFrame* aPrevInFlow) {
 | 
						|
  NS_ASSERTION(aContent->IsSVGElement(nsGkAtoms::mask),
 | 
						|
               "Content is not an SVG mask");
 | 
						|
 | 
						|
  SVGContainerFrame::Init(aContent, aParent, aPrevInFlow);
 | 
						|
}
 | 
						|
#endif /* DEBUG */
 | 
						|
 | 
						|
gfxMatrix SVGMaskFrame::GetCanvasTM() { return mMatrixForChildren; }
 | 
						|
 | 
						|
gfxMatrix SVGMaskFrame::GetMaskTransform(nsIFrame* aMaskedFrame) {
 | 
						|
  SVGMaskElement* content = static_cast<SVGMaskElement*>(GetContent());
 | 
						|
 | 
						|
  SVGAnimatedEnumeration* maskContentUnits =
 | 
						|
      &content->mEnumAttributes[SVGMaskElement::MASKCONTENTUNITS];
 | 
						|
 | 
						|
  uint32_t flags = SVGUtils::eBBoxIncludeFillGeometry |
 | 
						|
                   (aMaskedFrame->StyleBorder()->mBoxDecorationBreak ==
 | 
						|
                            StyleBoxDecorationBreak::Clone
 | 
						|
                        ? SVGUtils::eIncludeOnlyCurrentFrameForNonSVGElement
 | 
						|
                        : 0);
 | 
						|
 | 
						|
  return SVGUtils::AdjustMatrixForUnits(gfxMatrix(), maskContentUnits,
 | 
						|
                                        aMaskedFrame, flags);
 | 
						|
}
 | 
						|
 | 
						|
}  // namespace mozilla
 |