Skip to content

Commit

Permalink
feat: roundRect (#601)
Browse files Browse the repository at this point in the history
  • Loading branch information
Brooooooklyn committed Jan 5, 2023
1 parent e1efc1d commit 0823325
Show file tree
Hide file tree
Showing 8 changed files with 189 additions and 0 deletions.
36 changes: 36 additions & 0 deletions __test__/draw.spec.ts
Expand Up @@ -847,6 +847,42 @@ test('strokeRect', async (t) => {
await snapshotImage(t)
})

test('strokeRoundRect', async (t) => {
const canvas = createCanvas(700, 300)
const ctx = canvas.getContext('2d')
// Rounded rectangle with zero radius (specified as a number)
ctx.strokeStyle = 'red'
ctx.beginPath()
ctx.roundRect(10, 20, 150, 100, 0)
ctx.stroke()

// Rounded rectangle with 40px radius (single element list)
ctx.strokeStyle = 'blue'
ctx.beginPath()
ctx.roundRect(10, 20, 150, 100, [40])
ctx.stroke()

// Rounded rectangle with 2 different radii
ctx.strokeStyle = 'orange'
ctx.beginPath()
ctx.roundRect(10, 150, 150, 100, [10, 40])
ctx.stroke()

// Rounded rectangle with four different radii
ctx.strokeStyle = 'green'
ctx.beginPath()
ctx.roundRect(400, 20, 200, 100, [0, 30, 50, 60])
ctx.stroke()

// Same rectangle drawn backwards
ctx.strokeStyle = 'magenta'
ctx.beginPath()
ctx.roundRect(400, 150, -200, 100, [0, 30, 50, 60])
ctx.stroke()

await snapshotImage(t, { canvas, ctx })
})

test('strokeText', async (t) => {
const { ctx, canvas } = t.context
ctx.fillStyle = 'yellow'
Expand Down
Binary file added __test__/snapshots/strokeRoundRect.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions index.d.ts
Expand Up @@ -220,6 +220,7 @@ export class Path2D {
moveTo(x: number, y: number): void
quadraticCurveTo(cpx: number, cpy: number, x: number, y: number): void
rect(x: number, y: number, w: number, h: number): void
roundRect(x: number, y: number, w: number, h: number, radii?: number | number[]): void

// PathKit methods
op(path: Path2D, operation: PathOp): Path2D
Expand Down
21 changes: 21 additions & 0 deletions skia-c/skia_c.cpp
Expand Up @@ -967,6 +967,27 @@ extern "C"
return result;
}

void skiac_path_round_rect(
skiac_path *c_path,
SkScalar x,
SkScalar y,
SkScalar width,
SkScalar height,
SkScalar *radii,
bool clockwise)
{
auto path = PATH_CAST;
SkScalar radii_vec[8];
for (size_t i = 0; i < 4; i++)
{
radii_vec[i * 2] = radii[i];
radii_vec[i * 2 + 1] = radii[i];
}
SkRect rect = SkRect::MakeXYWH(x, y, width, height);
auto ccw = clockwise ? SkPathDirection::kCW : SkPathDirection::kCCW;
path->addRoundRect(rect, radii_vec, ccw);
}

// PathEffect

skiac_path_effect *skiac_path_effect_make_dash_path(const float *intervals, int count, float phase)
Expand Down
8 changes: 8 additions & 0 deletions skia-c/skia_c.hpp
Expand Up @@ -373,6 +373,14 @@ extern "C"
bool skiac_path_is_empty(skiac_path *c_path);
bool skiac_path_hit_test(skiac_path *c_path, float x, float y, int type);
bool skiac_path_stroke_hit_test(skiac_path *c_path, float x, float y, float stroke_w);
void skiac_path_round_rect(
skiac_path *c_path,
SkScalar x,
SkScalar y,
SkScalar width,
SkScalar height,
SkScalar *radii,
bool clockwise);

// PathEffect
skiac_path_effect *skiac_path_effect_make_dash_path(const float *intervals, int count, float phase);
Expand Down
45 changes: 45 additions & 0 deletions src/ctx.rs
Expand Up @@ -174,6 +174,10 @@ impl Context {
self.path.add_rect(x, y, width, height);
}

pub fn round_rect(&mut self, x: f32, y: f32, width: f32, height: f32, radii: [f32; 4]) {
self.path.round_rect(x, y, width, height, radii);
}

pub fn save(&mut self) {
self.surface.canvas.save();
self.states.push(self.state.clone());
Expand Down Expand Up @@ -1294,6 +1298,47 @@ impl CanvasRenderingContext2D {
.rect(x as f32, y as f32, width as f32, height as f32);
}

#[napi]
pub fn round_rect(
&mut self,
x: f64,
y: f64,
width: f64,
height: f64,
radii: Either3<f64, Vec<f64>, Undefined>,
) {
// https://github.com/chromium/chromium/blob/111.0.5520.1/third_party/blink/renderer/modules/canvas/canvas2d/canvas_path.cc#L579
let radii_array: [f32; 4] = match radii {
Either3::A(radii) => [radii as f32; 4],
Either3::B(radii_vec) => match radii_vec.len() {
0 => [0f32; 4],
1 => [radii_vec[0] as f32; 4],
2 => [
radii_vec[0] as f32,
radii_vec[1] as f32,
radii_vec[0] as f32,
radii_vec[1] as f32,
],
3 => [
radii_vec[0] as f32,
radii_vec[1] as f32,
radii_vec[1] as f32,
radii_vec[2] as f32,
],
_ => [
radii_vec[0] as f32,
radii_vec[1] as f32,
radii_vec[2] as f32,
radii_vec[3] as f32,
],
},
Either3::C(_) => [0f32; 4],
};
self
.context
.round_rect(x as f32, y as f32, width as f32, height as f32, radii_array);
}

#[napi]
pub fn fill(
&mut self,
Expand Down
40 changes: 40 additions & 0 deletions src/path.rs
Expand Up @@ -257,6 +257,46 @@ impl Path {
.add_rect(x as f32, y as f32, width as f32, height as f32);
}

#[napi]
pub fn round_rect(
&mut self,
x: f64,
y: f64,
width: f64,
height: f64,
radii: Either3<f64, Vec<f64>, Undefined>,
) {
let radii_array: [f32; 4] = match radii {
Either3::A(radii) => [radii as f32; 4],
Either3::B(radii_vec) => match radii_vec.len() {
0 => [0f32; 4],
1 => [radii_vec[0] as f32; 4],
2 => [
radii_vec[0] as f32,
radii_vec[1] as f32,
radii_vec[0] as f32,
radii_vec[1] as f32,
],
3 => [
radii_vec[0] as f32,
radii_vec[1] as f32,
radii_vec[1] as f32,
radii_vec[2] as f32,
],
_ => [
radii_vec[0] as f32,
radii_vec[1] as f32,
radii_vec[2] as f32,
radii_vec[3] as f32,
],
},
Either3::C(_) => [0f32; 4],
};
self
.inner
.round_rect(x as f32, y as f32, width as f32, height as f32, radii_array);
}

#[napi]
pub fn op(&mut self, other: &Path, op: PathOp) -> &Self {
self.inner.op(&other.inner, op.into());
Expand Down
38 changes: 38 additions & 0 deletions src/sk.rs
Expand Up @@ -612,6 +612,16 @@ pub mod ffi {
pub fn skiac_path_stroke_hit_test(path: *mut skiac_path, x: f32, y: f32, stroke_w: f32)
-> bool;

pub fn skiac_path_round_rect(
path: *mut skiac_path,
x: f32,
y: f32,
width: f32,
height: f32,
radii: *const f32,
clockwise: bool,
);

pub fn skiac_path_effect_make_dash_path(
intervals: *const f32,
count: i32,
Expand Down Expand Up @@ -2680,6 +2690,34 @@ impl Path {
unsafe { ffi::skiac_path_dash(self.0, on, off, phase) }
}

pub fn round_rect(
&mut self,
mut x: f32,
mut y: f32,
mut width: f32,
mut height: f32,
mut radii: [f32; 4],
) {
// https://github.com/chromium/chromium/blob/111.0.5520.1/third_party/blink/renderer/modules/canvas/canvas2d/canvas_path.cc#L601
let mut clockwise = true;
if width < 0f32 {
clockwise = false;
x += width;
width = -width;
radii.swap(0, 1);
radii.swap(2, 3);
}
if height < 0f32 {
clockwise = !clockwise;
y += height;
height = -height;
radii.swap(0, 2);
radii.swap(1, 3);
}
unsafe { ffi::skiac_path_round_rect(self.0, x, y, width, height, radii.as_ptr(), clockwise) };
unsafe { ffi::skiac_path_move_to(self.0, x, y) };
}

fn ellipse_helper(&mut self, x: f32, y: f32, rx: f32, ry: f32, start_angle: f32, end_angle: f32) {
let sweep_degrees = radians_to_degrees(end_angle - start_angle);
let start_degrees = radians_to_degrees(start_angle);
Expand Down

0 comments on commit 0823325

Please sign in to comment.