blob: d6985b30962f6e03b3a8302953bbd85a1b7ef999 [file] [log] [blame]
/*
* Copyright (C) 2006, 2007, 2009, 2010, 2011, 2012 Apple Inc. All rights
* reserved.
* Copyright (C) 2012, 2013 Adobe Systems Incorporated. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_CANVAS_CANVAS2D_CANVAS_PATH_H_
#define THIRD_PARTY_BLINK_RENDERER_MODULES_CANVAS_CANVAS2D_CANVAS_PATH_H_
#include "base/check.h"
#include "base/check_op.h"
#include "base/compiler_specific.h"
#include "third_party/blink/public/common/privacy_budget/identifiable_token.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_union_dompointinit_unrestricteddouble.h"
#include "third_party/blink/renderer/modules/canvas/canvas2d/identifiability_study_helper.h"
#include "third_party/blink/renderer/modules/modules_export.h"
#include "third_party/blink/renderer/platform/geometry/path.h"
#include "third_party/blink/renderer/platform/heap/collection_support/heap_vector.h"
#include "third_party/blink/renderer/platform/heap/forward.h" // IWYU pragma: keep (blink::Visitor)
#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
#include "third_party/blink/renderer/platform/heap/member.h"
#include "third_party/blink/renderer/platform/platform_export.h"
#include "third_party/blink/renderer/platform/transforms/affine_transform.h"
#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
#include "ui/gfx/geometry/point_f.h"
#include "ui/gfx/geometry/rect_f.h"
// https://github.com/include-what-you-use/include-what-you-use/issues/1546
// IWYU pragma: no_forward_declare WTF::internal::__thisIsHereToForceASemicolonAfterThisMacro
// IWYU pragma: no_include "third_party/blink/renderer/platform/heap/visitor.h"
namespace blink {
class ExceptionState;
class ExecutionContext;
class MODULES_EXPORT CanvasPath : public GarbageCollectedMixin {
public:
struct PLATFORM_EXPORT Line {
gfx::PointF start;
gfx::PointF end;
};
struct PLATFORM_EXPORT Arc {
float x;
float y;
float radius;
float start_angle_radians;
float sweep_angle_radians;
// Whether the path was closed.
bool closed;
};
virtual ~CanvasPath() = default;
void closePath();
void moveTo(double double_x, double double_y);
void lineTo(double double_x, double double_y);
void quadraticCurveTo(double double_cpx,
double double_cpy,
double double_x,
double double_y);
void bezierCurveTo(double double_cp1x,
double double_cp1y,
double double_cp2x,
double double_cp2y,
double double_x,
double double_y);
void arcTo(double double_x1,
double double_y1,
double double_x2,
double double_y2,
double double_radius,
ExceptionState&);
void arc(double double_x,
double double_y,
double double_radius,
double double_start_angle,
double double_end_angle,
bool anticlockwise,
ExceptionState&);
void ellipse(double double_x,
double double_y,
double double_radius_x,
double double_radius_y,
double double_rotation,
double double_start_angle,
double double_end_angle,
bool anticlockwise,
ExceptionState&);
void rect(double double_x,
double double_y,
double double_width,
double double_height);
void roundRect(
double double_x,
double double_y,
double double_width,
double double_height,
const HeapVector<Member<V8UnionDOMPointInitOrUnrestrictedDouble>>& radii,
ExceptionState& exception_state);
void roundRect(double double_x,
double double_y,
double double_width,
double double_height,
const Member<V8UnionDOMPointInitOrUnrestrictedDouble>& radius,
ExceptionState& exception_state);
bool IsTransformInvertible() const;
virtual AffineTransform GetTransform() const {
// This will be the identity matrix
return AffineTransform();
}
IdentifiableToken GetIdentifiableToken() const {
return identifiability_study_helper_.GetToken();
}
virtual ExecutionContext* GetTopExecutionContext() const = 0;
const Path& GetPath() const {
UpdatePathFromLineOrArcIfNecessary();
return path_;
}
// Returns true if the CanvasPath represents a line. In some cases (such as
// using constructor that takes a Path) this can return false even though the
// path is a line.
bool IsLine() const { return line_builder_.HasLineTo(); }
// Returns true if the CanvasPath represents an arc. In some cases (such as
// using constructor that takes a Path) this can return false even though the
// path is an arc
bool IsArc() const { return arc_builder_.HasArcOrIsClosed(); }
// Returns the points that make up the line. Only valid if IsLine() is true.
const Line& line() const {
DCHECK(IsLine());
return line_builder_.line();
}
// Returns the details of an arc. Only valid if IsArc() is true.
const Arc& arc() const {
DCHECK(IsArc());
return arc_builder_.arc();
}
bool IsEmpty() const {
return line_builder_.IsEmpty() && arc_builder_.IsEmpty() && path_.IsEmpty();
}
// The returned rectangle is not necessarily exact, and may contain much more
// than if you extract the SkPath and get the bounding rectangle from it. This
// function is intended for clipping, where it's ok if the result is larger
// than necessary.
gfx::RectF BoundingRect() const;
void Trace(Visitor*) const override;
protected:
CanvasPath() { path_.SetIsVolatile(true); }
explicit CanvasPath(const Path& path) : path_(path) {
path_.SetIsVolatile(true);
}
ALWAYS_INLINE void SetIsTransformInvertible(bool val) {
is_transform_invertible_ = val;
}
void Clear() {
line_builder_.Clear();
arc_builder_.Clear();
path_.Clear();
}
Path& GetModifiablePath() {
UpdatePathFromLineOrArcIfNecessaryForMutation();
return path_;
}
// This mirrors state that is stored in CanvasRenderingContext2DState. We
// replicate it here so that IsTransformInvertible() can be a non-virtual
// inline-able call. We do not replicate the whole CTM. Therefore
// GetTransform() remains virtual, which is okay because it is only called in
// code paths that handle non-invertible transforms.
bool is_transform_invertible_ = true;
IdentifiabilityStudyHelper identifiability_study_helper_;
private:
// Used to build up a line.
class LineBuilder {
public:
bool IsEmpty() const { return state_ == State::kEmpty; }
ALWAYS_INLINE void Clear() { state_ = State::kEmpty; }
void MoveTo(const gfx::PointF& point) {
DCHECK_EQ(state_, State::kEmpty);
state_ = State::kStartingPoint;
line_.start = point;
}
// Returns true if LineTo() may be called.
bool CanCreateLineTo() const { return state_ == State::kStartingPoint; }
void LineTo(const gfx::PointF& point) {
DCHECK(CanCreateLineTo());
state_ = State::kLine;
line_.end = point;
}
bool HasLineTo() const { return state_ == LineBuilder::State::kLine; }
const gfx::PointF& starting_point() const {
DCHECK(!IsEmpty());
return line_.start;
}
const gfx::PointF& ending_point() const {
DCHECK_EQ(state_, State::kLine);
return line_.end;
}
gfx::RectF BoundingRect() const;
ALWAYS_INLINE const Line& line() const {
DCHECK_EQ(state_, State::kLine);
return line_;
}
private:
enum class State {
kEmpty,
kStartingPoint,
kLine, // Indicates LineTo() was called.
};
State state_ = State::kEmpty;
Line line_;
};
// Tracks building an arc.
class ArcBuilder {
public:
bool IsEmpty() const { return state_ == State::kEmpty; }
ALWAYS_INLINE void Clear() { state_ = State::kEmpty; }
// Returns true if ArcTo() may be called.
bool CanCreateArcTo() const { return state_ == State::kEmpty; }
bool IsClosed() const { return state_ == State::kClosed; }
void Close() {
// Close() may be called multiple times.
DCHECK(state_ == State::kClosed || state_ == State::kArc);
state_ = State::kClosed;
arc_.closed = true;
}
void ArcTo(float x,
float y,
float radius,
float start_angle_radians,
float sweep_angle_radians) {
DCHECK(CanCreateArcTo());
state_ = State::kArc;
arc_.x = x;
arc_.y = y;
arc_.radius = radius;
arc_.start_angle_radians = start_angle_radians;
arc_.sweep_angle_radians = sweep_angle_radians;
arc_.closed = false;
}
bool HasArcOrIsClosed() const {
return state_ == State::kArc || state_ == State::kClosed;
}
gfx::RectF BoundingRect() const;
ALWAYS_INLINE const Arc& arc() const {
DCHECK_NE(state_, State::kEmpty);
return arc_;
}
void UpdatePath(Path& path) const;
private:
enum class State {
kEmpty,
kArc, // ArcTo() was called.
kClosed, // Close() was called after ArcTo().
};
State state_ = State::kEmpty;
Arc arc_;
};
bool DoesPathNeedUpdatingFromLineOrArc() const {
return (!line_builder_.IsEmpty() || !arc_builder_.IsEmpty()) &&
path_.IsEmpty();
}
// Updates `path_` from one of the builders if necessary.
void UpdatePathFromLineOrArcIfNecessary() const;
// Same as UpdatePathFromLineOrArcIfNecessary(), but also clears the builders.
void UpdatePathFromLineOrArcIfNecessaryForMutation();
LineBuilder line_builder_;
ArcBuilder arc_builder_;
// `path_` may be lazily updated from one of the builders. As such, it needs
// to be mutable.
mutable Path path_;
};
ALWAYS_INLINE bool CanvasPath::IsTransformInvertible() const {
// Verify that the cached is_transform_invertible_ remains in sync with the
// ground truth state.
DCHECK(is_transform_invertible_ == GetTransform().IsInvertible());
return is_transform_invertible_;
}
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_MODULES_CANVAS_CANVAS2D_CANVAS_PATH_H_