Skip to content

Commit

Permalink
feat: add image class poc
Browse files Browse the repository at this point in the history
  • Loading branch information
doodlewind committed Jan 18, 2021
1 parent 5ca9fde commit 11e04a5
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 1 deletion.
21 changes: 21 additions & 0 deletions example/image.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const { promises } = require('fs')
const { join } = require('path')

const { createCanvas, Image } = require('../index')

const canvas = createCanvas(1024, 768)

const ctx = canvas.getContext('2d')

async function main() {
const file = await promises.readFile(join(__dirname, 'tiger.png'))
const image = new Image()
image.src = file
// console.log(image, image.width, image.height)
// ctx.drawImage(image, 0, 0)

const output = await canvas.png()
await promises.writeFile(join(__dirname, 'tiger-tmp.png'), output)
}

main()
6 changes: 6 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ export class ImageData {
constructor(data: Uint8ClampedArray, sw: number, sh?: number)
}

export class Image {
readonly width: number
readonly height: number
src: Buffer
}

export class Path2D {
constructor(path?: Path2D | string)

Expand Down
7 changes: 6 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ const { loadBinding } = require('@node-rs/helper')
* loadBinding helper will load `skia.[PLATFORM].node` from `__dirname` first
* If failed to load addon, it will fallback to load from `@napi-rs/skia-[PLATFORM]`
*/
const { CanvasRenderingContext2D, CanvasElement, Path2D, ImageData } = loadBinding(__dirname, 'skia', '@napi-rs/skia')
const { CanvasRenderingContext2D, CanvasElement, Path2D, ImageData, Image } = loadBinding(
__dirname,
'skia',
'@napi-rs/skia',
)

CanvasRenderingContext2D.prototype.getImageData = function getImageData(x, y, w, h) {
const data = this._getImageData(x, y, w, h)
Expand Down Expand Up @@ -61,4 +65,5 @@ module.exports = {
createCanvas,
Path2D,
ImageData,
Image,
}
94 changes: 94 additions & 0 deletions src/image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,3 +117,97 @@ fn image_data_constructor(ctx: CallContext) -> Result<JsUndefined> {
])?;
ctx.env.get_undefined()
}

#[derive(Debug, Clone)]
pub struct Image {
pub(crate) width: u32,
pub(crate) height: u32,
bitmap: *mut u8,
}

impl Drop for Image {
fn drop(&mut self) {
let len = (self.width * self.height * 4) as usize;
unsafe { Vec::from_raw_parts(self.bitmap, len, len) };
}
}

impl Image {
pub fn create_js_class(env: &Env) -> Result<JsFunction> {
env.define_class(
"Image",
image_constructor,
&vec![
Property::new(&env, "width")?
.with_setter(set_noop)
.with_getter(get_width),
Property::new(&env, "height")?
.with_setter(set_noop)
.with_getter(get_height),
Property::new(&env, "src")?
.with_setter(set_src)
.with_getter(get_src),
],
)
}
}

#[js_function]
fn image_constructor(ctx: CallContext) -> Result<JsUndefined> {
let mut initial_data = ManuallyDrop::new(vec![0u8; 0]);
let data_ptr = initial_data.as_mut_ptr();
let image = Image {
width: 0u32,
height: 0u32,
bitmap: data_ptr,
};
let mut this = ctx.this_unchecked::<JsObject>();
ctx.env.wrap(&mut this, image)?;
ctx.env.get_undefined()
}

#[js_function]
fn get_width(ctx: CallContext) -> Result<JsNumber> {
let this = ctx.this_unchecked::<JsObject>();
let image = ctx.env.unwrap::<Image>(&this)?;

ctx.env.create_double(image.width as f64)
}

#[js_function]
fn get_height(ctx: CallContext) -> Result<JsNumber> {
let this = ctx.this_unchecked::<JsObject>();
let image = ctx.env.unwrap::<Image>(&this)?;

ctx.env.create_double(image.height as f64)
}

#[js_function(1)]
fn set_noop(ctx: CallContext) -> Result<JsUndefined> {
ctx.env.get_undefined()
}

#[js_function]
fn get_src(ctx: CallContext) -> Result<JsUndefined> {
ctx.env.get_undefined() // TODO
}

#[js_function(1)]
fn set_src(ctx: CallContext) -> Result<JsUndefined> {
let this = ctx.this_unchecked::<JsObject>();
let mut image = ctx.env.unwrap::<Image>(&this)?;

let src_arg = ctx.get::<JsUnknown>(0)?;
let src_data_ab = unsafe { src_arg.cast::<JsTypedArray>() }.into_value()?;
if src_data_ab.typedarray_type != TypedArrayType::Uint8 {
return Err(Error::new(
Status::InvalidArg,
"Image src setter: Argument 1 does not implement interface Buffer."
.to_owned(),
));
}
let arraybuffer_length = src_data_ab.len();
println!("buffer length {}", arraybuffer_length);

ctx.env.get_undefined()
}
4 changes: 4 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,17 @@ fn init(mut exports: JsObject, env: Env) -> Result<()> {

let image_data_class = image::ImageData::create_js_class(&env)?;

let image_class = image::Image::create_js_class(&env)?;

exports.set_named_property("CanvasRenderingContext2D", canvas_rendering_context2d)?;

exports.set_named_property("CanvasElement", canvas_element)?;

exports.set_named_property("Path2D", path_class)?;

exports.set_named_property("ImageData", image_data_class)?;

exports.set_named_property("Image", image_class)?;
Ok(())
}

Expand Down

0 comments on commit 11e04a5

Please sign in to comment.