Blame layout/svg/nsCSSClipPathInstance.cpp

Packit f0b94e
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
Packit f0b94e
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
Packit f0b94e
/* This Source Code Form is subject to the terms of the Mozilla Public
Packit f0b94e
 * License, v. 2.0. If a copy of the MPL was not distributed with this
Packit f0b94e
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
Packit f0b94e
Packit f0b94e
// Main header first:
Packit f0b94e
#include "nsCSSClipPathInstance.h"
Packit f0b94e
Packit f0b94e
#include "gfx2DGlue.h"
Packit f0b94e
#include "gfxContext.h"
Packit f0b94e
#include "gfxPlatform.h"
Packit f0b94e
#include "mozilla/gfx/2D.h"
Packit f0b94e
#include "mozilla/gfx/PathHelpers.h"
Packit f0b94e
#include "mozilla/ShapeUtils.h"
Packit f0b94e
#include "nsCSSRendering.h"
Packit f0b94e
#include "nsIFrame.h"
Packit f0b94e
#include "nsLayoutUtils.h"
Packit f0b94e
#ifdef MOZ_OLD_STYLE
Packit f0b94e
#include "nsRuleNode.h"
Packit f0b94e
#endif
Packit f0b94e
#include "nsSVGElement.h"
Packit f0b94e
#include "nsSVGUtils.h"
Packit f0b94e
#include "nsSVGViewBox.h"
Packit f0b94e
Packit f0b94e
using namespace mozilla;
Packit f0b94e
using namespace mozilla::dom;
Packit f0b94e
using namespace mozilla::gfx;
Packit f0b94e
Packit f0b94e
/* static*/ void nsCSSClipPathInstance::ApplyBasicShapeClip(
Packit f0b94e
    gfxContext& aContext, nsIFrame* aFrame) {
Packit f0b94e
  auto& clipPathStyle = aFrame->StyleSVGReset()->mClipPath;
Packit f0b94e
Packit f0b94e
#ifdef DEBUG
Packit f0b94e
  StyleShapeSourceType type = clipPathStyle.GetType();
Packit f0b94e
  MOZ_ASSERT(
Packit f0b94e
      type == StyleShapeSourceType::Shape || type == StyleShapeSourceType::Box,
Packit f0b94e
      "This function is used with basic-shape and geometry-box only.");
Packit f0b94e
#endif
Packit f0b94e
Packit f0b94e
  nsCSSClipPathInstance instance(aFrame, clipPathStyle);
Packit f0b94e
Packit f0b94e
  aContext.NewPath();
Packit f0b94e
  RefPtr<Path> path = instance.CreateClipPath(aContext.GetDrawTarget());
Packit f0b94e
  aContext.SetPath(path);
Packit f0b94e
  aContext.Clip();
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
/* static*/ bool nsCSSClipPathInstance::HitTestBasicShapeClip(
Packit f0b94e
    nsIFrame* aFrame, const gfxPoint& aPoint) {
Packit f0b94e
  auto& clipPathStyle = aFrame->StyleSVGReset()->mClipPath;
Packit f0b94e
  StyleShapeSourceType type = clipPathStyle.GetType();
Packit f0b94e
  MOZ_ASSERT(type != StyleShapeSourceType::None, "unexpected none value");
Packit f0b94e
  // In the future nsCSSClipPathInstance may handle <clipPath> references as
Packit f0b94e
  // well. For the time being return early.
Packit f0b94e
  if (type == StyleShapeSourceType::URL) {
Packit f0b94e
    return false;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  nsCSSClipPathInstance instance(aFrame, clipPathStyle);
Packit f0b94e
Packit f0b94e
  RefPtr<DrawTarget> drawTarget =
Packit f0b94e
      gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
Packit f0b94e
  RefPtr<Path> path = instance.CreateClipPath(drawTarget);
Packit f0b94e
  float pixelRatio = float(nsPresContext::AppUnitsPerCSSPixel()) /
Packit f0b94e
                     aFrame->PresContext()->AppUnitsPerDevPixel();
Packit f0b94e
  return path->ContainsPoint(ToPoint(aPoint) * pixelRatio, Matrix());
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
/* static */ Rect nsCSSClipPathInstance::GetBoundingRectForBasicShapeClip(
Packit f0b94e
    nsIFrame* aFrame, const StyleShapeSource& aClipPathStyle) {
Packit f0b94e
  MOZ_ASSERT(aClipPathStyle.GetType() == StyleShapeSourceType::Shape ||
Packit f0b94e
             aClipPathStyle.GetType() == StyleShapeSourceType::Box);
Packit f0b94e
Packit f0b94e
  nsCSSClipPathInstance instance(aFrame, aClipPathStyle);
Packit f0b94e
Packit f0b94e
  RefPtr<DrawTarget> drawTarget =
Packit f0b94e
      gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
Packit f0b94e
  RefPtr<Path> path = instance.CreateClipPath(drawTarget);
Packit f0b94e
  return path->GetBounds();
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
already_AddRefed<Path> nsCSSClipPathInstance::CreateClipPath(
Packit f0b94e
    DrawTarget* aDrawTarget) {
Packit f0b94e
  nsRect r = nsLayoutUtils::ComputeGeometryBox(
Packit f0b94e
      mTargetFrame, mClipPathStyle.GetReferenceBox());
Packit f0b94e
Packit f0b94e
  if (mClipPathStyle.GetType() != StyleShapeSourceType::Shape) {
Packit f0b94e
    // TODO Clip to border-radius/reference box if no shape
Packit f0b94e
    // was specified.
Packit f0b94e
    RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder();
Packit f0b94e
    return builder->Finish();
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  nscoord appUnitsPerDevPixel =
Packit f0b94e
      mTargetFrame->PresContext()->AppUnitsPerDevPixel();
Packit f0b94e
  r = ToAppUnits(r.ToNearestPixels(appUnitsPerDevPixel), appUnitsPerDevPixel);
Packit f0b94e
Packit f0b94e
  const UniquePtr<StyleBasicShape>& basicShape = mClipPathStyle.GetBasicShape();
Packit f0b94e
  switch (basicShape->GetShapeType()) {
Packit f0b94e
    case StyleBasicShapeType::Circle:
Packit f0b94e
      return CreateClipPathCircle(aDrawTarget, r);
Packit f0b94e
    case StyleBasicShapeType::Ellipse:
Packit f0b94e
      return CreateClipPathEllipse(aDrawTarget, r);
Packit f0b94e
    case StyleBasicShapeType::Polygon:
Packit f0b94e
      return CreateClipPathPolygon(aDrawTarget, r);
Packit f0b94e
    case StyleBasicShapeType::Inset:
Packit f0b94e
      return CreateClipPathInset(aDrawTarget, r);
Packit f0b94e
      break;
Packit f0b94e
    default:
Packit f0b94e
      MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unexpected shape type");
Packit f0b94e
  }
Packit f0b94e
  // Return an empty Path:
Packit f0b94e
  RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder();
Packit f0b94e
  return builder->Finish();
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
already_AddRefed<Path> nsCSSClipPathInstance::CreateClipPathCircle(
Packit f0b94e
    DrawTarget* aDrawTarget, const nsRect& aRefBox) {
Packit f0b94e
  const UniquePtr<StyleBasicShape>& basicShape = mClipPathStyle.GetBasicShape();
Packit f0b94e
Packit f0b94e
  RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder();
Packit f0b94e
Packit f0b94e
  nsPoint center =
Packit f0b94e
      ShapeUtils::ComputeCircleOrEllipseCenter(basicShape, aRefBox);
Packit f0b94e
  nscoord r = ShapeUtils::ComputeCircleRadius(basicShape, center, aRefBox);
Packit f0b94e
  nscoord appUnitsPerDevPixel =
Packit f0b94e
      mTargetFrame->PresContext()->AppUnitsPerDevPixel();
Packit f0b94e
  builder->Arc(Point(center.x, center.y) / appUnitsPerDevPixel,
Packit f0b94e
               r / appUnitsPerDevPixel, 0, Float(2 * M_PI));
Packit f0b94e
  builder->Close();
Packit f0b94e
  return builder->Finish();
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
already_AddRefed<Path> nsCSSClipPathInstance::CreateClipPathEllipse(
Packit f0b94e
    DrawTarget* aDrawTarget, const nsRect& aRefBox) {
Packit f0b94e
  const UniquePtr<StyleBasicShape>& basicShape = mClipPathStyle.GetBasicShape();
Packit f0b94e
Packit f0b94e
  RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder();
Packit f0b94e
Packit f0b94e
  nsPoint center =
Packit f0b94e
      ShapeUtils::ComputeCircleOrEllipseCenter(basicShape, aRefBox);
Packit f0b94e
  nsSize radii = ShapeUtils::ComputeEllipseRadii(basicShape, center, aRefBox);
Packit f0b94e
  nscoord appUnitsPerDevPixel =
Packit f0b94e
      mTargetFrame->PresContext()->AppUnitsPerDevPixel();
Packit f0b94e
  EllipseToBezier(builder.get(),
Packit f0b94e
                  Point(center.x, center.y) / appUnitsPerDevPixel,
Packit f0b94e
                  Size(radii.width, radii.height) / appUnitsPerDevPixel);
Packit f0b94e
  builder->Close();
Packit f0b94e
  return builder->Finish();
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
already_AddRefed<Path> nsCSSClipPathInstance::CreateClipPathPolygon(
Packit f0b94e
    DrawTarget* aDrawTarget, const nsRect& aRefBox) {
Packit f0b94e
  const UniquePtr<StyleBasicShape>& basicShape = mClipPathStyle.GetBasicShape();
Packit f0b94e
  FillRule fillRule = basicShape->GetFillRule() == StyleFillRule::Nonzero
Packit f0b94e
                          ? FillRule::FILL_WINDING
Packit f0b94e
                          : FillRule::FILL_EVEN_ODD;
Packit f0b94e
  RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder(fillRule);
Packit f0b94e
Packit f0b94e
  nsTArray<nsPoint> vertices =
Packit f0b94e
      ShapeUtils::ComputePolygonVertices(basicShape, aRefBox);
Packit f0b94e
  if (vertices.IsEmpty()) {
Packit f0b94e
    MOZ_ASSERT_UNREACHABLE(
Packit f0b94e
        "ComputePolygonVertices() should've given us some vertices!");
Packit f0b94e
  } else {
Packit f0b94e
    nscoord appUnitsPerDevPixel =
Packit f0b94e
        mTargetFrame->PresContext()->AppUnitsPerDevPixel();
Packit f0b94e
    builder->MoveTo(NSPointToPoint(vertices[0], appUnitsPerDevPixel));
Packit f0b94e
    for (size_t i = 1; i < vertices.Length(); ++i) {
Packit f0b94e
      builder->LineTo(NSPointToPoint(vertices[i], appUnitsPerDevPixel));
Packit f0b94e
    }
Packit f0b94e
  }
Packit f0b94e
  builder->Close();
Packit f0b94e
  return builder->Finish();
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
already_AddRefed<Path> nsCSSClipPathInstance::CreateClipPathInset(
Packit f0b94e
    DrawTarget* aDrawTarget, const nsRect& aRefBox) {
Packit f0b94e
  const UniquePtr<StyleBasicShape>& basicShape = mClipPathStyle.GetBasicShape();
Packit f0b94e
Packit f0b94e
  RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder();
Packit f0b94e
Packit f0b94e
  nscoord appUnitsPerDevPixel =
Packit f0b94e
      mTargetFrame->PresContext()->AppUnitsPerDevPixel();
Packit f0b94e
Packit f0b94e
  nsRect insetRect = ShapeUtils::ComputeInsetRect(basicShape, aRefBox);
Packit f0b94e
  const Rect insetRectPixels = NSRectToRect(insetRect, appUnitsPerDevPixel);
Packit f0b94e
  nscoord appUnitsRadii[8];
Packit f0b94e
Packit f0b94e
  if (ShapeUtils::ComputeInsetRadii(basicShape, insetRect, aRefBox,
Packit f0b94e
                                    appUnitsRadii)) {
Packit f0b94e
    RectCornerRadii corners;
Packit f0b94e
    nsCSSRendering::ComputePixelRadii(appUnitsRadii, appUnitsPerDevPixel,
Packit f0b94e
                                      &corners);
Packit f0b94e
Packit f0b94e
    AppendRoundedRectToPath(builder, insetRectPixels, corners, true);
Packit f0b94e
  } else {
Packit f0b94e
    AppendRectToPath(builder, insetRectPixels, true);
Packit f0b94e
  }
Packit f0b94e
  return builder->Finish();
Packit f0b94e
}