Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  SVGClipPathFrame.cpp   Sprache: 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 "SVGClipPathFrame.h"

// Keep others in (case-insensitive) order:
#include "AutoReferenceChainGuard.h"
#include "ImgDrawResult.h"
#include "gfxContext.h"
#include "mozilla/PresShell.h"
#include "mozilla/SVGGeometryFrame.h"
#include "mozilla/SVGObserverUtils.h"
#include "mozilla/SVGUtils.h"
#include "mozilla/dom/SVGClipPathElement.h"
#include "mozilla/dom/SVGGeometryElement.h"
#include "nsGkAtoms.h"

using namespace mozilla::dom;
using namespace mozilla::gfx;
using namespace mozilla::image;

//----------------------------------------------------------------------
// Implementation

nsIFrame* NS_NewSVGClipPathFrame(mozilla::PresShell* aPresShell,
                                 mozilla::ComputedStyle* aStyle) {
  return new (aPresShell)
      mozilla::SVGClipPathFrame(aStyle, aPresShell->GetPresContext());
}

namespace mozilla {

NS_IMPL_FRAMEARENA_HELPERS(SVGClipPathFrame)

void SVGClipPathFrame::ApplyClipPath(gfxContext& aContext,
                                     nsIFrame* aClippedFrame,
                                     const gfxMatrix& aMatrix) {
  nsIFrame* singleClipPathChild = nullptr;
  DebugOnly<bool> trivial = IsTrivial(&singleClipPathChild);
  MOZ_ASSERT(trivial, "Caller needs to use GetClipMask");

  const DrawTarget* drawTarget = aContext.GetDrawTarget();

  // No need for AutoReferenceChainGuard since simple clip paths by definition
  // don't reference another clip path.

  // Restore current transform after applying clip path:
  gfxContextMatrixAutoSaveRestore autoRestoreTransform(&aContext);

  RefPtr<Path> clipPath;

  if (singleClipPathChild) {
    SVGGeometryFrame* pathFrame = do_QueryFrame(singleClipPathChild);
    if (pathFrame && pathFrame->StyleVisibility()->IsVisible()) {
      SVGGeometryElement* pathElement =
          static_cast<SVGGeometryElement*>(pathFrame->GetContent());

      gfxMatrix toChildsUserSpace =
          SVGUtils::GetTransformMatrixInUserSpace(pathFrame) *
          (GetClipPathTransform(aClippedFrame) * aMatrix);

      gfxMatrix newMatrix = aContext.CurrentMatrixDouble()
                                .PreMultiply(toChildsUserSpace)
                                .NudgeToIntegers();
      if (!newMatrix.IsSingular()) {
        aContext.SetMatrixDouble(newMatrix);
        FillRule clipRule =
            SVGUtils::ToFillRule(pathFrame->StyleSVG()->mClipRule);
        clipPath = pathElement->GetOrBuildPath(drawTarget, clipRule);
      }
    }
  }

  if (clipPath) {
    aContext.Clip(clipPath);
  } else {
    // The spec says clip away everything if we have no children or the
    // clipping path otherwise can't be resolved:
    aContext.Clip(Rect());
  }
}

static void ComposeExtraMask(DrawTarget* aTarget, SourceSurface* aExtraMask) {
  MOZ_ASSERT(aExtraMask);

  Matrix origin = aTarget->GetTransform();
  aTarget->SetTransform(Matrix());
  aTarget->MaskSurface(ColorPattern(DeviceColor(0.0, 0.0, 0.0, 1.0)),
                       aExtraMask, Point(0, 0),
                       DrawOptions(1.0, CompositionOp::OP_IN));
  aTarget->SetTransform(origin);
}

void SVGClipPathFrame::PaintChildren(gfxContext& aMaskContext,
                                     nsIFrame* aClippedFrame,
                                     const gfxMatrix& aMatrix) {
  // Check if this clipPath is itself clipped by another clipPath:
  SVGClipPathFrame* clipPathThatClipsClipPath;
  // XXX check return value?
  SVGObserverUtils::GetAndObserveClipPath(this, &clipPathThatClipsClipPath);
  SVGUtils::MaskUsage maskUsage = SVGUtils::DetermineMaskUsage(thistrue);

  gfxGroupForBlendAutoSaveRestore autoGroupForBlend(&aMaskContext);
  if (maskUsage.ShouldApplyClipPath()) {
    clipPathThatClipsClipPath->ApplyClipPath(aMaskContext, aClippedFrame,
                                             aMatrix);
  } else if (maskUsage.ShouldGenerateClipMaskLayer()) {
    RefPtr<SourceSurface> maskSurface = clipPathThatClipsClipPath->GetClipMask(
        aMaskContext, aClippedFrame, aMatrix);
    // We want the mask to be untransformed so use the inverse of the current
    // transform as the maskTransform to compensate.
    Matrix maskTransform = aMaskContext.CurrentMatrix();
    maskTransform.Invert();
    autoGroupForBlend.PushGroupForBlendBack(gfxContentType::ALPHA, 1.0f,
                                            maskSurface, maskTransform);
  }

  // Paint our children into the mask:
  for (auto* kid : mFrames) {
    PaintFrameIntoMask(kid, aClippedFrame, aMaskContext);
  }

  if (maskUsage.ShouldApplyClipPath()) {
    aMaskContext.PopClip();
  }
}

void SVGClipPathFrame::PaintClipMask(gfxContext& aMaskContext,
                                     nsIFrame* aClippedFrame,
                                     const gfxMatrix& aMatrix,
                                     SourceSurface* aExtraMask) {
  static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;

  // A clipPath can reference another clipPath, creating a chain of clipPaths
  // that must all be applied.  We re-enter this method for each clipPath in a
  // chain, so we need to protect against reference chain related crashes etc.:
  AutoReferenceChainGuard refChainGuard(this, &mIsBeingProcessed,
                                        &sRefChainLengthCounter);
  if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
    return;  // Break reference chain
  }
  if (!IsValid()) {
    return;
  }

  DrawTarget* maskDT = aMaskContext.GetDrawTarget();
  MOZ_ASSERT(maskDT->GetFormat() == SurfaceFormat::A8);

  // Paint this clipPath's contents into aMaskDT:
  // We need to set mMatrixForChildren here so that under the PaintSVG calls
  // on our children (below) our GetCanvasTM() method will return the correct
  // transform.
  mMatrixForChildren = GetClipPathTransform(aClippedFrame) * aMatrix;

  PaintChildren(aMaskContext, aClippedFrame, aMatrix);

  if (aExtraMask) {
    ComposeExtraMask(maskDT, aExtraMask);
  }
}

void SVGClipPathFrame::PaintFrameIntoMask(nsIFrame* aFrame,
                                          nsIFrame* aClippedFrame,
                                          gfxContext& aTarget) {
  ISVGDisplayableFrame* frame = do_QueryFrame(aFrame);
  if (!frame) {
    return;
  }

  // The CTM of each frame referencing us can be different.
  frame->NotifySVGChanged(ISVGDisplayableFrame::TRANSFORM_CHANGED);

  // Children of this clipPath may themselves be clipped.
  SVGClipPathFrame* clipPathThatClipsChild;
  // XXX check return value?
  if (SVGObserverUtils::GetAndObserveClipPath(aFrame,
                                              &clipPathThatClipsChild) ==
      SVGObserverUtils::eHasRefsSomeInvalid) {
    return;
  }

  SVGUtils::MaskUsage maskUsage = SVGUtils::DetermineMaskUsage(aFrame, true);
  gfxGroupForBlendAutoSaveRestore autoGroupForBlend(&aTarget);
  if (maskUsage.ShouldApplyClipPath()) {
    clipPathThatClipsChild->ApplyClipPath(
        aTarget, aClippedFrame,
        SVGUtils::GetTransformMatrixInUserSpace(aFrame) * mMatrixForChildren);
  } else if (maskUsage.ShouldGenerateClipMaskLayer()) {
    RefPtr<SourceSurface> maskSurface = clipPathThatClipsChild->GetClipMask(
        aTarget, aClippedFrame,
        SVGUtils::GetTransformMatrixInUserSpace(aFrame) * mMatrixForChildren);

    // We want the mask to be untransformed so use the inverse of the current
    // transform as the maskTransform to compensate.
    Matrix maskTransform = aTarget.CurrentMatrix();
    maskTransform.Invert();
    autoGroupForBlend.PushGroupForBlendBack(gfxContentType::ALPHA, 1.0f,
                                            maskSurface, maskTransform);
  }

  gfxMatrix toChildsUserSpace = mMatrixForChildren;
  nsIFrame* child = do_QueryFrame(frame);
  nsIContent* childContent = child->GetContent();
  if (childContent->IsSVGElement()) {
    toChildsUserSpace =
        SVGUtils::GetTransformMatrixInUserSpace(child) * mMatrixForChildren;
  }

  // clipPath does not result in any image rendering, so we just use a dummy
  // imgDrawingParams instead of requiring our caller to pass one.
  image::imgDrawingParams imgParams;

  // Our children have NS_STATE_SVG_CLIPPATH_CHILD set on them, and
  // SVGGeometryFrame::Render checks for that state bit and paints
  // only the geometry (opaque black) if set.
  frame->PaintSVG(aTarget, toChildsUserSpace, imgParams);

  if (maskUsage.ShouldApplyClipPath()) {
    aTarget.PopClip();
  }
}

already_AddRefed<SourceSurface> SVGClipPathFrame::GetClipMask(
    gfxContext& aReferenceContext, nsIFrame* aClippedFrame,
    const gfxMatrix& aMatrix, SourceSurface* aExtraMask) {
  RefPtr<DrawTarget> maskDT =
      aReferenceContext.GetDrawTarget()->CreateClippedDrawTarget(
          Rect(), SurfaceFormat::A8);
  if (!maskDT) {
    return nullptr;
  }

  gfxContext maskContext(maskDT, /* aPreserveTransform */ true);
  PaintClipMask(maskContext, aClippedFrame, aMatrix, aExtraMask);

  RefPtr<SourceSurface> surface = maskDT->Snapshot();
  return surface.forget();
}

bool SVGClipPathFrame::PointIsInsideClipPath(nsIFrame* aClippedFrame,
                                             const gfxPoint& aPoint) {
  static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;

  // A clipPath can reference another clipPath, creating a chain of clipPaths
  // that must all be applied.  We re-enter this method for each clipPath in a
  // chain, so we need to protect against reference chain related crashes etc.:
  AutoReferenceChainGuard refChainGuard(this, &mIsBeingProcessed,
                                        &sRefChainLengthCounter);
  if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
    return false;  // Break reference chain
  }
  if (!IsValid()) {
    return false;
  }

  gfxMatrix matrix = GetClipPathTransform(aClippedFrame);
  if (!matrix.Invert()) {
    return false;
  }
  gfxPoint point = matrix.TransformPoint(aPoint);

  // clipPath elements can themselves be clipped by a different clip path. In
  // that case the other clip path further clips away the element that is being
  // clipped by the original clipPath. If this clipPath is being clipped by a
  // different clip path we need to check if it prevents the original element
  // from receiving events at aPoint:
  SVGClipPathFrame* clipPathFrame;
  // XXX check return value?
  SVGObserverUtils::GetAndObserveClipPath(this, &clipPathFrame);
  if (clipPathFrame &&
      !clipPathFrame->PointIsInsideClipPath(aClippedFrame, aPoint)) {
    return false;
  }

  for (auto* kid : mFrames) {
    ISVGDisplayableFrame* SVGFrame = do_QueryFrame(kid);
    if (SVGFrame) {
      gfxPoint pointForChild = point;

      gfxMatrix m = SVGUtils::GetTransformMatrixInUserSpace(kid);
      if (!m.IsIdentity()) {
        if (!m.Invert()) {
          return false;
        }
        pointForChild = m.TransformPoint(point);
      }
      if (SVGFrame->GetFrameForPoint(pointForChild)) {
        return true;
      }
    }
  }

  return false;
}

bool SVGClipPathFrame::IsTrivial(nsIFrame** aSingleChild) {
  // If the clip path is clipped then it's non-trivial
  if (SVGObserverUtils::GetAndObserveClipPath(this, nullptr) ==
      SVGObserverUtils::eHasRefsAllValid) {
    return false;
  }

  if (aSingleChild) {
    *aSingleChild = nullptr;
  }

  nsIFrame* foundChild = nullptr;
  for (auto* kid : mFrames) {
    ISVGDisplayableFrame* svgChild = do_QueryFrame(kid);
    if (!svgChild) {
      continue;
    }
    // We consider a non-trivial clipPath to be one containing
    // either more than one svg child and/or a svg container
    if (foundChild || svgChild->IsDisplayContainer()) {
      return false;
    }

    // or where the child is itself clipped
    if (SVGObserverUtils::GetAndObserveClipPath(kid, nullptr) ==
        SVGObserverUtils::eHasRefsAllValid) {
      return false;
    }

    foundChild = kid;
  }
  if (aSingleChild) {
    *aSingleChild = foundChild;
  }
  return true;
}

bool SVGClipPathFrame::IsValid() {
  if (SVGObserverUtils::GetAndObserveClipPath(this, nullptr) ==
      SVGObserverUtils::eHasRefsSomeInvalid) {
    return false;
  }

  for (auto* kid : mFrames) {
    LayoutFrameType kidType = kid->Type();

    if (kidType == LayoutFrameType::SVGUse) {
      for (nsIFrame* grandKid : kid->PrincipalChildList()) {
        LayoutFrameType grandKidType = grandKid->Type();

        if (grandKidType != LayoutFrameType::SVGGeometry &&
            grandKidType != LayoutFrameType::SVGText) {
          return false;
        }
      }
      continue;
    }

    if (kidType != LayoutFrameType::SVGGeometry &&
        kidType != LayoutFrameType::SVGText) {
      return false;
    }
  }

  return true;
}

nsresult SVGClipPathFrame::AttributeChanged(int32_t aNameSpaceID,
                                            nsAtom* aAttribute,
                                            int32_t aModType) {
  if (aNameSpaceID == kNameSpaceID_None &&
      aAttribute == nsGkAtoms::clipPathUnits) {
    SVGObserverUtils::InvalidateRenderingObservers(this);
  }
  return SVGContainerFrame::AttributeChanged(aNameSpaceID, aAttribute,
                                             aModType);
}

#ifdef DEBUG
void SVGClipPathFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
                            nsIFrame* aPrevInFlow) {
  NS_ASSERTION(aContent->IsSVGElement(nsGkAtoms::clipPath),
               "Content is not an SVG clipPath!");

  SVGContainerFrame::Init(aContent, aParent, aPrevInFlow);
}
#endif

gfxMatrix SVGClipPathFrame::GetCanvasTM() { return mMatrixForChildren; }

gfxMatrix SVGClipPathFrame::GetClipPathTransform(nsIFrame* aClippedFrame) {
  gfxMatrix tm = SVGUtils::GetTransformMatrixInUserSpace(this);

  auto* content = static_cast<SVGClipPathElement*>(GetContent());
  SVGAnimatedEnumeration* clipPathUnits =
      &content->mEnumAttributes[SVGClipPathElement::CLIPPATHUNITS];

  uint32_t flags = SVGUtils::eBBoxIncludeFillGeometry |
                   (aClippedFrame->StyleBorder()->mBoxDecorationBreak ==
                            StyleBoxDecorationBreak::Clone
                        ? SVGUtils::eIncludeOnlyCurrentFrameForNonSVGElement
                        : 0);

  return SVGUtils::AdjustMatrixForUnits(tm, clipPathUnits, aClippedFrame,
                                        flags);
}

SVGBBox SVGClipPathFrame::GetBBoxForClipPathFrame(const SVGBBox& aBBox,
                                                  const gfxMatrix& aMatrix,
                                                  uint32_t aFlags) {
  SVGClipPathFrame* clipPathThatClipsClipPath;
  if (SVGObserverUtils::GetAndObserveClipPath(this,
                                              &clipPathThatClipsClipPath) ==
      SVGObserverUtils::eHasRefsSomeInvalid) {
    return SVGBBox();
  }

  nsIContent* node = GetContent()->GetFirstChild();
  SVGBBox unionBBox;
  for (; node; node = node->GetNextSibling()) {
    if (nsIFrame* frame = node->GetPrimaryFrame()) {
      ISVGDisplayableFrame* svg = do_QueryFrame(frame);
      if (svg) {
        gfxMatrix matrix =
            SVGUtils::GetTransformMatrixInUserSpace(frame) * aMatrix;
        SVGBBox tmpBBox = svg->GetBBoxContribution(
            gfx::ToMatrix(matrix), SVGUtils::eBBoxIncludeFillGeometry);
        SVGClipPathFrame* clipPathFrame;
        if (SVGObserverUtils::GetAndObserveClipPath(frame, &clipPathFrame) !=
                SVGObserverUtils::eHasRefsSomeInvalid &&
            clipPathFrame) {
          tmpBBox =
              clipPathFrame->GetBBoxForClipPathFrame(tmpBBox, aMatrix, aFlags);
        }
        if (!(aFlags & SVGUtils::eDoNotClipToBBoxOfContentInsideClipPath)) {
          tmpBBox.Intersect(aBBox);
        }
        unionBBox.UnionEdges(tmpBBox);
      }
    }
  }

  if (clipPathThatClipsClipPath) {
    unionBBox.Intersect(clipPathThatClipsClipPath->GetBBoxForClipPathFrame(
        aBBox, aMatrix, aFlags));
  }
  return unionBBox;
}

}  // namespace mozilla

92%


¤ Dauer der Verarbeitung: 0.15 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge