Skip to content

Commit

Permalink
feat: support isPointInPath
Browse files Browse the repository at this point in the history
  • Loading branch information
doodlewind committed Jan 27, 2021
1 parent a5cda30 commit 2150788
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 1 deletion.
24 changes: 23 additions & 1 deletion __test__/draw.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,29 @@ test('getImageData', async (t) => {
await snapshotImage(t)
})

test.todo('isPointInPath')
test('isPointInPath', (t) => {
const { ctx } = t.context

ctx.rect(0, 0, 100, 100)
t.is(ctx.isPointInPath(50, -1), false) // Outside the rect
t.is(ctx.isPointInPath(50, 0), true) // On the edge of the rect
t.is(ctx.isPointInPath(50, 1), true) // Inside the rect

ctx.rect(40, 40, 20, 20) // Overlap the area center
t.is(ctx.isPointInPath(50, 50), true)
t.is(ctx.isPointInPath(50, 50, 'nonzero'), true)
t.is(ctx.isPointInPath(50, 50, 'evenodd'), false)

const path = new Path2D()
path.rect(0, 0, 100, 100)
t.is(ctx.isPointInPath(path, 50, -1), false)
t.is(ctx.isPointInPath(path, 50, 1), true)

path.rect(40, 40, 20, 20)
t.is(ctx.isPointInPath(50, 50), true)
t.is(ctx.isPointInPath(path, 50, 50, 'nonzero'), true)
t.is(ctx.isPointInPath(path, 50, 50, 'evenodd'), false)
})

test('isPointInStroke', (t) => {
const { ctx } = t.context
Expand Down
9 changes: 9 additions & 0 deletions skia-c/skia_c.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,15 @@ extern "C"
return PATH_CAST->isEmpty();
}

bool skiac_path_hit_test(skiac_path *c_path, float x, float y, int type)
{
auto prev_fill = PATH_CAST->getFillType();
PATH_CAST->setFillType((SkPathFillType)type);
auto result = PATH_CAST->contains(x, y);
PATH_CAST->setFillType(prev_fill);
return result;
}

bool skiac_path_stroke_hit_test(skiac_path *c_path, float x, float y, float stroke_w)
{
auto path = PATH_CAST;
Expand Down
1 change: 1 addition & 0 deletions skia-c/skia_c.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ extern "C"
void skiac_path_transform(skiac_path *c_path, skiac_transform c_transform);
void skiac_path_transform_matrix(skiac_path *c_path, skiac_matrix *c_matrix);
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);

// PathEffect
Expand Down
62 changes: 62 additions & 0 deletions src/ctx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ impl Context {
Property::new(&env, "createLinearGradient")?.with_method(create_linear_gradient),
Property::new(&env, "createRadialGradient")?.with_method(create_radial_gradient),
Property::new(&env, "drawImage")?.with_method(draw_image),
Property::new(&env, "isPointInPath")?.with_method(is_point_in_path),
Property::new(&env, "isPointInStroke")?.with_method(is_point_in_stroke),
Property::new(&env, "ellipse")?.with_method(ellipse),
Property::new(&env, "lineTo")?.with_method(line_to),
Expand Down Expand Up @@ -684,6 +685,67 @@ fn draw_image(ctx: CallContext) -> Result<JsUndefined> {
ctx.env.get_undefined()
}

#[js_function(4)]
fn is_point_in_path(ctx: CallContext) -> Result<JsBoolean> {
let this = ctx.this_unchecked::<JsObject>();
let context_2d = ctx.env.unwrap::<Context>(&this)?;
let result;

if ctx.length == 2 {
let x: f64 = ctx.get::<JsNumber>(0)?.try_into()?;
let y: f64 = ctx.get::<JsNumber>(1)?.try_into()?;
result = context_2d
.path
.hit_test(y as f32, x as f32, FillType::Winding);
return ctx.env.get_boolean(result);
} else if ctx.length == 3 {
let input = ctx.get::<JsUnknown>(0)?;
match input.get_type()? {
ValueType::Number => {
let x: f64 = ctx.get::<JsNumber>(0)?.try_into()?;
let y: f64 = ctx.get::<JsNumber>(1)?.try_into()?;
let fill_rule_js = ctx.get::<JsString>(2)?.into_utf8()?;
result = context_2d.path.hit_test(
y as f32,
x as f32,
FillType::from_str(fill_rule_js.as_str()?)?,
);
}
ValueType::Object => {
let x: f64 = ctx.get::<JsNumber>(1)?.try_into()?;
let y: f64 = ctx.get::<JsNumber>(2)?.try_into()?;
let path_js = ctx.get::<JsObject>(0)?;
let path = ctx.env.unwrap::<Path>(&path_js)?;
result = path.hit_test(x as f32, y as f32, FillType::Winding);
}
_ => {
return Err(Error::new(
Status::InvalidArg,
format!("Invalid isPointInPath argument"),
))
}
}
return ctx.env.get_boolean(result);
} else if ctx.length == 4 {
let path_js = ctx.get::<JsObject>(0)?;
let path = ctx.env.unwrap::<Path>(&path_js)?;
let x: f64 = ctx.get::<JsNumber>(1)?.try_into()?;
let y: f64 = ctx.get::<JsNumber>(2)?.try_into()?;
let fill_rule_js = ctx.get::<JsString>(3)?.into_utf8()?;
result = path.hit_test(
y as f32,
x as f32,
FillType::from_str(fill_rule_js.as_str()?)?,
);
return ctx.env.get_boolean(result);
} else {
return Err(Error::new(
Status::InvalidArg,
format!("Invalid isPointInPath arguments length"),
));
}
}

#[js_function(3)]
fn is_point_in_stroke(ctx: CallContext) -> Result<JsBoolean> {
let this = ctx.this_unchecked::<JsObject>();
Expand Down
7 changes: 7 additions & 0 deletions src/sk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,8 @@ mod ffi {

pub fn skiac_path_is_empty(path: *mut skiac_path) -> bool;

pub fn skiac_path_hit_test(path: *mut skiac_path, x: f32, y: f32, kind: i32) -> bool;

pub fn skiac_path_stroke_hit_test(path: *mut skiac_path, x: f32, y: f32, stroke_w: f32)
-> bool;

Expand Down Expand Up @@ -1720,6 +1722,11 @@ impl Path {
unsafe { ffi::skiac_path_is_empty(self.0) }
}

#[inline]
pub fn hit_test(&self, x: f32, y: f32, kind: FillType) -> bool {
unsafe { ffi::skiac_path_hit_test(self.0, x, y, kind as i32) }
}

#[inline]
pub fn stroke_hit_test(&self, x: f32, y: f32, stroke_w: f32) -> bool {
unsafe { ffi::skiac_path_stroke_hit_test(self.0, x, y, stroke_w) }
Expand Down

0 comments on commit 2150788

Please sign in to comment.