Skip to content

Commit

Permalink
feat: allow set file path as Image#src
Browse files Browse the repository at this point in the history
  • Loading branch information
Brooooooklyn committed Nov 15, 2024
1 parent f1a7d7d commit 2c41d76
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 13 deletions.
35 changes: 35 additions & 0 deletions __test__/image.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,3 +193,38 @@ test('load invalid svg should throw error', async (t) => {
},
)
})

test('should be able to load file path as src', async (t) => {
const image = new Image()
const { promise, resolve } = Promise.withResolvers<void>()
image.onload = () => {
resolve()
}
const imagePath = join(__dirname, '../example/simple.png')
image.src = imagePath
await promise
t.is(image.width, 300)
t.is(image.height, 320)
t.is(image.naturalWidth, 300)
t.is(image.naturalHeight, 320)
t.is(image.src, imagePath)
})

test('should throw if src path is invalid', async (t) => {
await t.throwsAsync(
() =>
new Promise((_, reject) => {
const image = new Image()
image.onload = () => {
reject(new Error('should not be called'))
}
image.onerror = (err) => {
reject(err)
}
image.src = 'invalid/path/to/image.png'
}),
{
message: process.platform === 'win32' ? /The system cannot find the path specified/ : /No such file/,
},
)
})
2 changes: 1 addition & 1 deletion index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ export class Image {
readonly naturalHeight: number
readonly complete: boolean
alt: string
src: Buffer
src: Uint8Array | string
onload?(): void
onerror?(err: Error): void
}
Expand Down
2 changes: 1 addition & 1 deletion load-image.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ module.exports = async function loadImage(source, options = {}) {
if (typeof source === 'string' || source instanceof URL) {
// if the source exists as a file, construct image from that file
if (await exists(source)) {
return createImage(await fs.promises.readFile(source), options.alt)
return createImage(source, options.alt)
} else {
// the source is a remote url here
source = !(source instanceof URL) ? new URL(source) : source
Expand Down
34 changes: 23 additions & 11 deletions src/image.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::borrow::Cow;
use std::str::FromStr;
use std::{ptr, str};

Expand Down Expand Up @@ -124,7 +125,7 @@ pub struct Image {
pub(crate) need_regenerate_bitmap: bool,
pub(crate) is_svg: bool,
pub(crate) color_space: ColorSpace,
pub(crate) src: Option<Uint8Array>,
pub(crate) src: Option<Either<Uint8Array, String>>,
}

impl ObjectFinalize for Image {
Expand Down Expand Up @@ -218,14 +219,19 @@ impl Image {
}

#[napi(getter)]
pub fn get_src(&mut self) -> Option<&mut Uint8Array> {
self.src.as_mut()
pub fn get_src(&mut self) -> Option<Either<&mut Uint8Array, &str>> {
match self.src.as_mut() {
Some(Either::A(d)) => Some(Either::A(d)),
Some(Either::B(s)) => Some(Either::B(s.as_str())),
None => None,
}
}

#[napi(setter)]
pub fn set_src(&mut self, env: Env, this: This, data: Uint8Array) -> Result<()> {
let length = data.len();
if length <= 2 {
pub fn set_src(&mut self, env: Env, this: This, data: Either<Uint8Array, String>) -> Result<()> {
if let Either::A(d) = &data
&& d.len() <= 2
{
self.src = Some(data);
self.width = -1.0;
self.height = -1.0;
Expand Down Expand Up @@ -312,7 +318,7 @@ struct BitmapDecoder {
width: f64,
height: f64,
color_space: ColorSpace,
data: Option<Uint8Array>,
data: Option<Either<Uint8Array, String>>,
this_ref: Ref<Object>,
}

Expand Down Expand Up @@ -343,7 +349,13 @@ impl Task for BitmapDecoder {

fn compute(&mut self) -> Result<Self::Output> {
let data_ref = match self.data.as_ref() {
Some(data) => data.as_ref(),
Some(Either::A(data)) => Cow::Borrowed(data.as_ref()),
Some(Either::B(path)) => Cow::Owned(std::fs::read(path).map_err(|io_err| {
Error::new(
Status::GenericFailure,
format!("Failed to read {}: {io_err}", path),
)
})?),
None => {
return Ok(DecodedBitmap {
bitmap: DecodeStatus::Empty,
Expand All @@ -356,7 +368,7 @@ impl Task for BitmapDecoder {
let mut width = self.width;
let mut height = self.height;
let bitmap = if str::from_utf8(&data_ref[0..10]) == Ok("data:image") {
let data_str = str::from_utf8(data_ref)
let data_str = str::from_utf8(&data_ref)
.map_err(|e| Error::new(Status::InvalidArg, format!("Decode data url failed {e}")))?;
if let Some(base64_str) = data_str.split(',').last() {
let image_binary = STANDARD
Expand All @@ -377,14 +389,14 @@ impl Task for BitmapDecoder {
} else {
DecodeStatus::Empty
}
} else if let Some(kind) = infer::get(data_ref)
} else if let Some(kind) = infer::get(&data_ref)
&& kind.matcher_type() == infer::MatcherType::Image
{
DecodeStatus::Ok(BitmapInfo {
data: Bitmap::from_buffer(data_ref.as_ptr().cast_mut(), length),
is_svg: false,
})
} else if is_svg_image(data_ref, length) {
} else if is_svg_image(&data_ref, length) {
let font = get_font().map_err(SkError::from)?;
if (self.width - -1.0).abs() > f64::EPSILON && (self.height - -1.0).abs() > f64::EPSILON {
if let Some(bitmap) = Bitmap::from_svg_data_with_custom_size(
Expand Down

0 comments on commit 2c41d76

Please sign in to comment.