Vendor things
This commit is contained in:
parent
5deceec006
commit
977e3c17e5
19434 changed files with 10682014 additions and 0 deletions
426
third-party/vendor/tiny-skia-path/src/path_builder.rs
vendored
Normal file
426
third-party/vendor/tiny-skia-path/src/path_builder.rs
vendored
Normal file
|
|
@ -0,0 +1,426 @@
|
|||
// Copyright 2006 The Android Open Source Project
|
||||
// Copyright 2020 Yevhenii Reizner
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// NOTE: this is not SkPathBuilder, but rather a reimplementation of SkPath.
|
||||
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
|
||||
use crate::{Path, Point, Rect};
|
||||
|
||||
use crate::path::PathVerb;
|
||||
use crate::path_geometry;
|
||||
use crate::scalar::{Scalar, SCALAR_ROOT_2_OVER_2};
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||
pub(crate) enum PathDirection {
|
||||
/// Clockwise direction for adding closed contours.
|
||||
CW,
|
||||
/// Counter-clockwise direction for adding closed contours.
|
||||
CCW,
|
||||
}
|
||||
|
||||
/// A path builder.
|
||||
#[derive(Clone, Default, Debug)]
|
||||
pub struct PathBuilder {
|
||||
pub(crate) verbs: Vec<PathVerb>,
|
||||
pub(crate) points: Vec<Point>,
|
||||
pub(crate) last_move_to_index: usize,
|
||||
pub(crate) move_to_required: bool,
|
||||
}
|
||||
|
||||
impl PathBuilder {
|
||||
/// Creates a new builder.
|
||||
pub fn new() -> Self {
|
||||
PathBuilder {
|
||||
verbs: Vec::new(),
|
||||
points: Vec::new(),
|
||||
last_move_to_index: 0,
|
||||
move_to_required: true,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new builder with a specified capacity.
|
||||
///
|
||||
/// Number of points depends on a verb type:
|
||||
///
|
||||
/// - Move - 1
|
||||
/// - Line - 1
|
||||
/// - Quad - 2
|
||||
/// - Cubic - 3
|
||||
/// - Close - 0
|
||||
pub fn with_capacity(verbs_capacity: usize, points_capacity: usize) -> Self {
|
||||
PathBuilder {
|
||||
verbs: Vec::with_capacity(verbs_capacity),
|
||||
points: Vec::with_capacity(points_capacity),
|
||||
last_move_to_index: 0,
|
||||
move_to_required: true,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new `Path` from `Rect`.
|
||||
///
|
||||
/// Never fails since `Rect` is always valid.
|
||||
///
|
||||
/// Segments are created clockwise: TopLeft -> TopRight -> BottomRight -> BottomLeft
|
||||
///
|
||||
/// The contour is closed.
|
||||
pub fn from_rect(rect: Rect) -> Path {
|
||||
let verbs = vec![
|
||||
PathVerb::Move,
|
||||
PathVerb::Line,
|
||||
PathVerb::Line,
|
||||
PathVerb::Line,
|
||||
PathVerb::Close,
|
||||
];
|
||||
|
||||
let points = vec![
|
||||
Point::from_xy(rect.left(), rect.top()),
|
||||
Point::from_xy(rect.right(), rect.top()),
|
||||
Point::from_xy(rect.right(), rect.bottom()),
|
||||
Point::from_xy(rect.left(), rect.bottom()),
|
||||
];
|
||||
|
||||
Path {
|
||||
bounds: rect,
|
||||
verbs,
|
||||
points,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new `Path` from a circle.
|
||||
///
|
||||
/// See [`PathBuilder::push_circle`] for details.
|
||||
pub fn from_circle(cx: f32, cy: f32, radius: f32) -> Option<Path> {
|
||||
let mut b = PathBuilder::new();
|
||||
b.push_circle(cx, cy, radius);
|
||||
b.finish()
|
||||
}
|
||||
|
||||
/// Creates a new `Path` from an oval.
|
||||
///
|
||||
/// See [`PathBuilder::push_oval`] for details.
|
||||
pub fn from_oval(oval: Rect) -> Option<Path> {
|
||||
let mut b = PathBuilder::new();
|
||||
b.push_oval(oval);
|
||||
b.finish()
|
||||
}
|
||||
|
||||
pub(crate) fn reserve(&mut self, additional_verbs: usize, additional_points: usize) {
|
||||
self.verbs.reserve(additional_verbs);
|
||||
self.points.reserve(additional_points);
|
||||
}
|
||||
|
||||
/// Returns the current number of segments in the builder.
|
||||
pub fn len(&self) -> usize {
|
||||
self.verbs.len()
|
||||
}
|
||||
|
||||
/// Checks if the builder has any segments added.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.verbs.is_empty()
|
||||
}
|
||||
|
||||
/// Adds beginning of a contour.
|
||||
///
|
||||
/// Multiple continuous MoveTo segments are not allowed.
|
||||
/// If the previous segment was also MoveTo, it will be overwritten with the current one.
|
||||
pub fn move_to(&mut self, x: f32, y: f32) {
|
||||
if let Some(PathVerb::Move) = self.verbs.last() {
|
||||
let last_idx = self.points.len() - 1;
|
||||
self.points[last_idx] = Point::from_xy(x, y);
|
||||
} else {
|
||||
self.last_move_to_index = self.points.len();
|
||||
self.move_to_required = false;
|
||||
|
||||
self.verbs.push(PathVerb::Move);
|
||||
self.points.push(Point::from_xy(x, y));
|
||||
}
|
||||
}
|
||||
|
||||
fn inject_move_to_if_needed(&mut self) {
|
||||
if self.move_to_required {
|
||||
match self.points.get(self.last_move_to_index).cloned() {
|
||||
Some(p) => self.move_to(p.x, p.y),
|
||||
None => self.move_to(0.0, 0.0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds a line from the last point.
|
||||
///
|
||||
/// - If `Path` is empty - adds Move(0, 0) first.
|
||||
/// - If `Path` ends with Close - adds Move(last_x, last_y) first.
|
||||
pub fn line_to(&mut self, x: f32, y: f32) {
|
||||
self.inject_move_to_if_needed();
|
||||
|
||||
self.verbs.push(PathVerb::Line);
|
||||
self.points.push(Point::from_xy(x, y));
|
||||
}
|
||||
|
||||
/// Adds a quad curve from the last point to `x`, `y`.
|
||||
///
|
||||
/// - If `Path` is empty - adds Move(0, 0) first.
|
||||
/// - If `Path` ends with Close - adds Move(last_x, last_y) first.
|
||||
pub fn quad_to(&mut self, x1: f32, y1: f32, x: f32, y: f32) {
|
||||
self.inject_move_to_if_needed();
|
||||
|
||||
self.verbs.push(PathVerb::Quad);
|
||||
self.points.push(Point::from_xy(x1, y1));
|
||||
self.points.push(Point::from_xy(x, y));
|
||||
}
|
||||
|
||||
pub(crate) fn quad_to_pt(&mut self, p1: Point, p: Point) {
|
||||
self.quad_to(p1.x, p1.y, p.x, p.y);
|
||||
}
|
||||
|
||||
// We do not support conic segments, but Skia still relies on them from time to time.
|
||||
// This method will simply convert the input data into quad segments.
|
||||
pub(crate) fn conic_to(&mut self, x1: f32, y1: f32, x: f32, y: f32, weight: f32) {
|
||||
// check for <= 0 or NaN with this test
|
||||
if !(weight > 0.0) {
|
||||
self.line_to(x, y);
|
||||
} else if !weight.is_finite() {
|
||||
self.line_to(x1, y1);
|
||||
self.line_to(x, y);
|
||||
} else if weight == 1.0 {
|
||||
self.quad_to(x1, y1, x, y);
|
||||
} else {
|
||||
self.inject_move_to_if_needed();
|
||||
|
||||
let last = self.last_point().unwrap();
|
||||
let quadder = path_geometry::AutoConicToQuads::compute(
|
||||
last,
|
||||
Point::from_xy(x1, y1),
|
||||
Point::from_xy(x, y),
|
||||
weight,
|
||||
);
|
||||
if let Some(quadder) = quadder {
|
||||
// Points are ordered as: 0 - 1 2 - 3 4 - 5 6 - ..
|
||||
// `count` is a number of pairs +1
|
||||
let mut offset = 1;
|
||||
for _ in 0..quadder.len {
|
||||
let pt1 = quadder.points[offset + 0];
|
||||
let pt2 = quadder.points[offset + 1];
|
||||
self.quad_to(pt1.x, pt1.y, pt2.x, pt2.y);
|
||||
offset += 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn conic_points_to(&mut self, pt1: Point, pt2: Point, weight: f32) {
|
||||
self.conic_to(pt1.x, pt1.y, pt2.x, pt2.y, weight);
|
||||
}
|
||||
|
||||
/// Adds a cubic curve from the last point to `x`, `y`.
|
||||
///
|
||||
/// - If `Path` is empty - adds Move(0, 0) first.
|
||||
/// - If `Path` ends with Close - adds Move(last_x, last_y) first.
|
||||
pub fn cubic_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, x: f32, y: f32) {
|
||||
self.inject_move_to_if_needed();
|
||||
|
||||
self.verbs.push(PathVerb::Cubic);
|
||||
self.points.push(Point::from_xy(x1, y1));
|
||||
self.points.push(Point::from_xy(x2, y2));
|
||||
self.points.push(Point::from_xy(x, y));
|
||||
}
|
||||
|
||||
pub(crate) fn cubic_to_pt(&mut self, p1: Point, p2: Point, p: Point) {
|
||||
self.cubic_to(p1.x, p1.y, p2.x, p2.y, p.x, p.y);
|
||||
}
|
||||
|
||||
/// Closes the current contour.
|
||||
///
|
||||
/// A closed contour connects the first and the last Point
|
||||
/// with a line, forming a continuous loop.
|
||||
///
|
||||
/// Does nothing when `Path` is empty or already closed.
|
||||
///
|
||||
/// Open and closed contour will be filled the same way.
|
||||
/// Stroking an open contour will add LineCap at contour's start and end.
|
||||
/// Stroking an closed contour will add LineJoin at contour's start and end.
|
||||
pub fn close(&mut self) {
|
||||
// don't add a close if it's the first verb or a repeat
|
||||
if !self.verbs.is_empty() {
|
||||
if self.verbs.last().cloned() != Some(PathVerb::Close) {
|
||||
self.verbs.push(PathVerb::Close);
|
||||
}
|
||||
}
|
||||
|
||||
self.move_to_required = true;
|
||||
}
|
||||
|
||||
/// Returns the last point if any.
|
||||
pub fn last_point(&self) -> Option<Point> {
|
||||
self.points.last().cloned()
|
||||
}
|
||||
|
||||
pub(crate) fn set_last_point(&mut self, pt: Point) {
|
||||
match self.points.last_mut() {
|
||||
Some(last) => *last = pt,
|
||||
None => self.move_to(pt.x, pt.y),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn is_zero_length_since_point(&self, start_pt_index: usize) -> bool {
|
||||
let count = self.points.len() - start_pt_index;
|
||||
if count < 2 {
|
||||
return true;
|
||||
}
|
||||
|
||||
let first = self.points[start_pt_index];
|
||||
for i in 1..count {
|
||||
if first != self.points[start_pt_index + i] {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
/// Adds a rectangle contour.
|
||||
///
|
||||
/// The contour is closed and has a clock-wise direction.
|
||||
///
|
||||
/// Does nothing when:
|
||||
/// - any value is not finite or really large
|
||||
pub fn push_rect(&mut self, x: f32, y: f32, w: f32, h: f32) {
|
||||
if let Some(rect) = Rect::from_xywh(x, y, w, h) {
|
||||
self.move_to(rect.left(), rect.top());
|
||||
self.line_to(rect.right(), rect.top());
|
||||
self.line_to(rect.right(), rect.bottom());
|
||||
self.line_to(rect.left(), rect.bottom());
|
||||
self.close();
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds an oval contour bounded by the provided rectangle.
|
||||
///
|
||||
/// The contour is closed and has a clock-wise direction.
|
||||
pub fn push_oval(&mut self, oval: Rect) {
|
||||
let cx = oval.left().half() + oval.right().half();
|
||||
let cy = oval.top().half() + oval.bottom().half();
|
||||
|
||||
let oval_points = [
|
||||
Point::from_xy(cx, oval.bottom()),
|
||||
Point::from_xy(oval.left(), cy),
|
||||
Point::from_xy(cx, oval.top()),
|
||||
Point::from_xy(oval.right(), cy),
|
||||
];
|
||||
|
||||
let rect_points = [
|
||||
Point::from_xy(oval.right(), oval.bottom()),
|
||||
Point::from_xy(oval.left(), oval.bottom()),
|
||||
Point::from_xy(oval.left(), oval.top()),
|
||||
Point::from_xy(oval.right(), oval.top()),
|
||||
];
|
||||
|
||||
let weight = SCALAR_ROOT_2_OVER_2;
|
||||
self.move_to(oval_points[3].x, oval_points[3].y);
|
||||
for (p1, p2) in rect_points.iter().zip(oval_points.iter()) {
|
||||
self.conic_points_to(*p1, *p2, weight);
|
||||
}
|
||||
self.close();
|
||||
}
|
||||
|
||||
/// Adds a circle contour.
|
||||
///
|
||||
/// The contour is closed and has a clock-wise direction.
|
||||
///
|
||||
/// Does nothing when:
|
||||
/// - `radius` <= 0
|
||||
/// - any value is not finite or really large
|
||||
pub fn push_circle(&mut self, x: f32, y: f32, r: f32) {
|
||||
if let Some(r) = Rect::from_xywh(x - r, y - r, r + r, r + r) {
|
||||
self.push_oval(r);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn push_path(&mut self, other: &PathBuilder) {
|
||||
if other.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
if self.last_move_to_index != 0 {
|
||||
self.last_move_to_index = self.points.len() + other.last_move_to_index;
|
||||
}
|
||||
|
||||
self.verbs.extend_from_slice(&other.verbs);
|
||||
self.points.extend_from_slice(&other.points);
|
||||
}
|
||||
|
||||
/// Appends, in a reverse order, the first contour of path ignoring path's last point.
|
||||
pub(crate) fn reverse_path_to(&mut self, other: &PathBuilder) {
|
||||
if other.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
debug_assert_eq!(other.verbs[0], PathVerb::Move);
|
||||
|
||||
let mut points_offset = other.points.len() - 1;
|
||||
for verb in other.verbs.iter().rev() {
|
||||
match verb {
|
||||
PathVerb::Move => {
|
||||
// if the path has multiple contours, stop after reversing the last
|
||||
break;
|
||||
}
|
||||
PathVerb::Line => {
|
||||
// We're moving one point back manually, to prevent points_offset overflow.
|
||||
let pt = other.points[points_offset - 1];
|
||||
points_offset -= 1;
|
||||
self.line_to(pt.x, pt.y);
|
||||
}
|
||||
PathVerb::Quad => {
|
||||
let pt1 = other.points[points_offset - 1];
|
||||
let pt2 = other.points[points_offset - 2];
|
||||
points_offset -= 2;
|
||||
self.quad_to(pt1.x, pt1.y, pt2.x, pt2.y);
|
||||
}
|
||||
PathVerb::Cubic => {
|
||||
let pt1 = other.points[points_offset - 1];
|
||||
let pt2 = other.points[points_offset - 2];
|
||||
let pt3 = other.points[points_offset - 3];
|
||||
points_offset -= 3;
|
||||
self.cubic_to(pt1.x, pt1.y, pt2.x, pt2.y, pt3.x, pt3.y);
|
||||
}
|
||||
PathVerb::Close => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Reset the builder.
|
||||
///
|
||||
/// Memory is not deallocated.
|
||||
pub fn clear(&mut self) {
|
||||
self.verbs.clear();
|
||||
self.points.clear();
|
||||
self.last_move_to_index = 0;
|
||||
self.move_to_required = true;
|
||||
}
|
||||
|
||||
/// Finishes the builder and returns a `Path`.
|
||||
///
|
||||
/// Returns `None` when `Path` is empty or has invalid bounds.
|
||||
pub fn finish(self) -> Option<Path> {
|
||||
if self.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Just a move to? Bail.
|
||||
if self.verbs.len() == 1 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let bounds = Rect::from_points(&self.points)?;
|
||||
|
||||
Some(Path {
|
||||
bounds,
|
||||
verbs: self.verbs,
|
||||
points: self.points,
|
||||
})
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue