mirror of
				https://github.com/mozilla/gecko-dev.git
				synced 2025-11-04 02:09:05 +02:00 
			
		
		
		
	Use this as a wrapper for `path()` and `shape()`, so it'd be easier to specialize its `Animate` trait. Differential Revision: https://phabricator.services.mozilla.com/D205490
		
			
				
	
	
		
			217 lines
		
	
	
	
		
			8.6 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			217 lines
		
	
	
	
		
			8.6 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 "CSSClipPathInstance.h"
 | 
						|
 | 
						|
#include "mozilla/dom/SVGElement.h"
 | 
						|
#include "mozilla/dom/SVGPathData.h"
 | 
						|
#include "mozilla/gfx/2D.h"
 | 
						|
#include "mozilla/gfx/PathHelpers.h"
 | 
						|
#include "mozilla/ShapeUtils.h"
 | 
						|
#include "mozilla/SVGUtils.h"
 | 
						|
#include "gfx2DGlue.h"
 | 
						|
#include "gfxContext.h"
 | 
						|
#include "gfxPlatform.h"
 | 
						|
#include "nsIFrame.h"
 | 
						|
#include "nsLayoutUtils.h"
 | 
						|
 | 
						|
using namespace mozilla::dom;
 | 
						|
using namespace mozilla::gfx;
 | 
						|
 | 
						|
namespace mozilla {
 | 
						|
 | 
						|
/* static*/
 | 
						|
void CSSClipPathInstance::ApplyBasicShapeOrPathClip(
 | 
						|
    gfxContext& aContext, nsIFrame* aFrame, const gfxMatrix& aTransform) {
 | 
						|
  RefPtr<Path> path =
 | 
						|
      CreateClipPathForFrame(aContext.GetDrawTarget(), aFrame, aTransform);
 | 
						|
  if (!path) {
 | 
						|
    // This behavior matches |SVGClipPathFrame::ApplyClipPath()|.
 | 
						|
    // https://www.w3.org/TR/css-masking-1/#ClipPathElement:
 | 
						|
    // "An empty clipping path will completely clip away the element that had
 | 
						|
    // the clip-path property applied."
 | 
						|
    aContext.Clip(Rect());
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  aContext.Clip(path);
 | 
						|
}
 | 
						|
 | 
						|
/* static*/
 | 
						|
RefPtr<Path> CSSClipPathInstance::CreateClipPathForFrame(
 | 
						|
    gfx::DrawTarget* aDt, nsIFrame* aFrame, const gfxMatrix& aTransform) {
 | 
						|
  const auto& clipPathStyle = aFrame->StyleSVGReset()->mClipPath;
 | 
						|
  MOZ_ASSERT(clipPathStyle.IsShape() || clipPathStyle.IsBox(),
 | 
						|
             "This is used with basic-shape, and geometry-box only");
 | 
						|
 | 
						|
  CSSClipPathInstance instance(aFrame, clipPathStyle);
 | 
						|
 | 
						|
  return instance.CreateClipPath(aDt, aTransform);
 | 
						|
}
 | 
						|
 | 
						|
/* static*/
 | 
						|
bool CSSClipPathInstance::HitTestBasicShapeOrPathClip(nsIFrame* aFrame,
 | 
						|
                                                      const gfxPoint& aPoint) {
 | 
						|
  const auto& clipPathStyle = aFrame->StyleSVGReset()->mClipPath;
 | 
						|
  MOZ_ASSERT(!clipPathStyle.IsNone(), "unexpected none value");
 | 
						|
  MOZ_ASSERT(!clipPathStyle.IsUrl(), "unexpected url value");
 | 
						|
 | 
						|
  CSSClipPathInstance instance(aFrame, clipPathStyle);
 | 
						|
 | 
						|
  RefPtr<DrawTarget> drawTarget =
 | 
						|
      gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
 | 
						|
  RefPtr<Path> path = instance.CreateClipPath(
 | 
						|
      drawTarget, SVGUtils::GetCSSPxToDevPxMatrix(aFrame));
 | 
						|
  float pixelRatio = float(AppUnitsPerCSSPixel()) /
 | 
						|
                     float(aFrame->PresContext()->AppUnitsPerDevPixel());
 | 
						|
  return path && path->ContainsPoint(ToPoint(aPoint) * pixelRatio, Matrix());
 | 
						|
}
 | 
						|
 | 
						|
/* static */
 | 
						|
Maybe<Rect> CSSClipPathInstance::GetBoundingRectForBasicShapeOrPathClip(
 | 
						|
    nsIFrame* aFrame, const StyleClipPath& aClipPathStyle) {
 | 
						|
  MOZ_ASSERT(aClipPathStyle.IsShape() || aClipPathStyle.IsBox());
 | 
						|
 | 
						|
  CSSClipPathInstance instance(aFrame, aClipPathStyle);
 | 
						|
 | 
						|
  RefPtr<DrawTarget> drawTarget =
 | 
						|
      gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
 | 
						|
  RefPtr<Path> path = instance.CreateClipPath(
 | 
						|
      drawTarget, SVGUtils::GetCSSPxToDevPxMatrix(aFrame));
 | 
						|
  return path ? Some(path->GetBounds()) : Nothing();
 | 
						|
}
 | 
						|
 | 
						|
already_AddRefed<Path> CSSClipPathInstance::CreateClipPath(
 | 
						|
    DrawTarget* aDrawTarget, const gfxMatrix& aTransform) {
 | 
						|
  nscoord appUnitsPerDevPixel =
 | 
						|
      mTargetFrame->PresContext()->AppUnitsPerDevPixel();
 | 
						|
 | 
						|
  nsRect r = nsLayoutUtils::ComputeClipPathGeometryBox(
 | 
						|
      mTargetFrame, mClipPathStyle.IsBox() ? mClipPathStyle.AsBox()
 | 
						|
                                           : mClipPathStyle.AsShape()._1);
 | 
						|
 | 
						|
  gfxRect rr(r.x, r.y, r.width, r.height);
 | 
						|
  rr.Scale(1.0 / AppUnitsPerCSSPixel());
 | 
						|
  rr = aTransform.TransformRect(rr);
 | 
						|
  rr.Scale(appUnitsPerDevPixel);
 | 
						|
  rr.Round();
 | 
						|
 | 
						|
  r = nsRect(int(rr.x), int(rr.y), int(rr.width), int(rr.height));
 | 
						|
 | 
						|
  if (mClipPathStyle.IsBox()) {
 | 
						|
    RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder();
 | 
						|
    AppendRectToPath(builder, NSRectToRect(r, appUnitsPerDevPixel), true);
 | 
						|
    return builder->Finish();
 | 
						|
  }
 | 
						|
 | 
						|
  MOZ_ASSERT(mClipPathStyle.IsShape());
 | 
						|
 | 
						|
  r = ToAppUnits(r.ToNearestPixels(appUnitsPerDevPixel), appUnitsPerDevPixel);
 | 
						|
 | 
						|
  const auto& basicShape = *mClipPathStyle.AsShape()._0;
 | 
						|
  switch (basicShape.tag) {
 | 
						|
    case StyleBasicShape::Tag::Circle:
 | 
						|
      return CreateClipPathCircle(aDrawTarget, r);
 | 
						|
    case StyleBasicShape::Tag::Ellipse:
 | 
						|
      return CreateClipPathEllipse(aDrawTarget, r);
 | 
						|
    case StyleBasicShape::Tag::Polygon:
 | 
						|
      return CreateClipPathPolygon(aDrawTarget, r);
 | 
						|
    case StyleBasicShape::Tag::Rect:
 | 
						|
      return CreateClipPathInset(aDrawTarget, r);
 | 
						|
    case StyleBasicShape::Tag::PathOrShape:
 | 
						|
      return basicShape.AsPathOrShape().IsPath()
 | 
						|
                 ? CreateClipPathPath(aDrawTarget, r)
 | 
						|
                 : CreateClipPathShape(aDrawTarget, r);
 | 
						|
    default:
 | 
						|
      MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unexpected shape type");
 | 
						|
  }
 | 
						|
  // Return an empty Path:
 | 
						|
  RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder();
 | 
						|
  return builder->Finish();
 | 
						|
}
 | 
						|
 | 
						|
already_AddRefed<Path> CSSClipPathInstance::CreateClipPathCircle(
 | 
						|
    DrawTarget* aDrawTarget, const nsRect& aRefBox) {
 | 
						|
  const StyleBasicShape& shape = *mClipPathStyle.AsShape()._0;
 | 
						|
  const nsPoint& center =
 | 
						|
      ShapeUtils::ComputeCircleOrEllipseCenter(shape, aRefBox);
 | 
						|
  RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder();
 | 
						|
  return ShapeUtils::BuildCirclePath(
 | 
						|
      shape, aRefBox, center,
 | 
						|
      mTargetFrame->PresContext()->AppUnitsPerDevPixel(), builder);
 | 
						|
}
 | 
						|
 | 
						|
already_AddRefed<Path> CSSClipPathInstance::CreateClipPathEllipse(
 | 
						|
    DrawTarget* aDrawTarget, const nsRect& aRefBox) {
 | 
						|
  const StyleBasicShape& shape = *mClipPathStyle.AsShape()._0;
 | 
						|
  const nsPoint& center =
 | 
						|
      ShapeUtils::ComputeCircleOrEllipseCenter(shape, aRefBox);
 | 
						|
  RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder();
 | 
						|
  return ShapeUtils::BuildEllipsePath(
 | 
						|
      shape, aRefBox, center,
 | 
						|
      mTargetFrame->PresContext()->AppUnitsPerDevPixel(), builder);
 | 
						|
}
 | 
						|
 | 
						|
already_AddRefed<Path> CSSClipPathInstance::CreateClipPathPolygon(
 | 
						|
    DrawTarget* aDrawTarget, const nsRect& aRefBox) {
 | 
						|
  const auto& basicShape = *mClipPathStyle.AsShape()._0;
 | 
						|
  auto fillRule = basicShape.AsPolygon().fill == StyleFillRule::Nonzero
 | 
						|
                      ? FillRule::FILL_WINDING
 | 
						|
                      : FillRule::FILL_EVEN_ODD;
 | 
						|
  RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder(fillRule);
 | 
						|
  return ShapeUtils::BuildPolygonPath(
 | 
						|
      basicShape, aRefBox, mTargetFrame->PresContext()->AppUnitsPerDevPixel(),
 | 
						|
      builder);
 | 
						|
}
 | 
						|
 | 
						|
already_AddRefed<Path> CSSClipPathInstance::CreateClipPathInset(
 | 
						|
    DrawTarget* aDrawTarget, const nsRect& aRefBox) {
 | 
						|
  RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder();
 | 
						|
  return ShapeUtils::BuildInsetPath(
 | 
						|
      *mClipPathStyle.AsShape()._0, aRefBox,
 | 
						|
      mTargetFrame->PresContext()->AppUnitsPerDevPixel(), builder);
 | 
						|
}
 | 
						|
 | 
						|
already_AddRefed<Path> CSSClipPathInstance::CreateClipPathPath(
 | 
						|
    DrawTarget* aDrawTarget, const nsRect& aRefBox) {
 | 
						|
  const auto& path = mClipPathStyle.AsShape()._0->AsPathOrShape().AsPath();
 | 
						|
 | 
						|
  RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder(
 | 
						|
      path.fill == StyleFillRule::Nonzero ? FillRule::FILL_WINDING
 | 
						|
                                          : FillRule::FILL_EVEN_ODD);
 | 
						|
  const nscoord appUnitsPerDevPixel =
 | 
						|
      mTargetFrame->PresContext()->AppUnitsPerDevPixel();
 | 
						|
  const Point offset =
 | 
						|
      LayoutDevicePoint::FromAppUnits(aRefBox.TopLeft(), appUnitsPerDevPixel)
 | 
						|
          .ToUnknownPoint();
 | 
						|
  const float scale = mTargetFrame->Style()->EffectiveZoom().Zoom(
 | 
						|
      float(AppUnitsPerCSSPixel()) / float(appUnitsPerDevPixel));
 | 
						|
  return SVGPathData::BuildPath(path.path._0.AsSpan(), builder,
 | 
						|
                                StyleStrokeLinecap::Butt, 0.0, {}, offset,
 | 
						|
                                scale);
 | 
						|
}
 | 
						|
 | 
						|
already_AddRefed<Path> CSSClipPathInstance::CreateClipPathShape(
 | 
						|
    DrawTarget* aDrawTarget, const nsRect& aRefBox) {
 | 
						|
  const auto& shape = mClipPathStyle.AsShape()._0->AsPathOrShape().AsShape();
 | 
						|
 | 
						|
  RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder(
 | 
						|
      shape.fill == StyleFillRule::Nonzero ? FillRule::FILL_WINDING
 | 
						|
                                           : FillRule::FILL_EVEN_ODD);
 | 
						|
  const nscoord appUnitsPerDevPixel =
 | 
						|
      mTargetFrame->PresContext()->AppUnitsPerDevPixel();
 | 
						|
  const CSSSize basis = CSSSize::FromAppUnits(aRefBox.Size());
 | 
						|
  const Point offset =
 | 
						|
      LayoutDevicePoint::FromAppUnits(aRefBox.TopLeft(), appUnitsPerDevPixel)
 | 
						|
          .ToUnknownPoint();
 | 
						|
  const float scale = mTargetFrame->Style()->EffectiveZoom().Zoom(
 | 
						|
      float(AppUnitsPerCSSPixel()) / float(appUnitsPerDevPixel));
 | 
						|
  return SVGPathData::BuildPath(shape.commands.AsSpan(), builder,
 | 
						|
                                StyleStrokeLinecap::Butt, 0.0, basis, offset,
 | 
						|
                                scale);
 | 
						|
}
 | 
						|
 | 
						|
}  // namespace mozilla
 |