feat(image): implement overlay (#33)

* feat(image): implement `overlay`

* ci: fix x86_64-pc-windows-msvc build
This commit is contained in:
Bryan Lee 2023-01-13 23:42:44 +08:00 committed by GitHub
parent 6a095ccd5d
commit 8bcc5de976
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 57 additions and 7 deletions

View File

@ -33,7 +33,7 @@ jobs:
strip -x packages/*/*.node
- host: windows-latest
build: |
choco install meson -y
python -m pip install meson
yarn workspace @napi-rs/image build --features with_simd
target: x86_64-pc-windows-msvc
- host: ubuntu-latest

View File

@ -166,4 +166,11 @@ writeFileSync(
)
console.info(chalk.green('Encoding webp from JPEG with EXIF done'))
writeFileSync(
'output-overlay-png.png',
await new Transformer(PNG).overlay(PNG, 200, 200).png()
)
console.info(chalk.green('Overlay an image done'))
```

View File

@ -64,3 +64,10 @@ writeFileSync(
)
console.info(chalk.green('Encoding webp from JPEG with EXIF done'))
writeFileSync(
'output-overlay-png.png',
await new Transformer(PNG).overlay(PNG, 200, 200).png()
)
console.info(chalk.green('Overlay an image done'))

View File

@ -419,6 +419,28 @@ export const enum ResizeFilterType {
}
```
#### `overlay`
```ts
/**
* Overlay an image at a given coordinate (x, y)
*/
overlay(onTop: Buffer, x: number, y: number): this
```
```ts
import { writeFileSync } from 'fs'
import { Transformer } from '@napi-rs/image'
const imageOutputPng = await new Transformer(PNG).overlay(PNG, 200, 200).png()
writeFileSync(
'output-overlay-png.png',
imageOutputPng
)
```
**ResizeFilterType**:
To test the different sampling filters on a real example, you can find two

View File

@ -300,6 +300,8 @@ export interface Metadata {
}
export class Transformer {
constructor(input: Buffer)
/** Overlay an image at a given coordinate (x, y) */
overlay(onTop: Buffer, x: number, y: number): this
static fromRgbaPixels(input: Buffer | Uint8ClampedArray, width: number, height: number): Transformer
metadata(withExif?: boolean | undefined | null, signal?: AbortSignal | undefined | null): Promise<Metadata>
/**

View File

@ -2,6 +2,7 @@ use std::collections::HashMap;
use std::io::Cursor;
use std::sync::Arc;
use image::imageops::overlay;
use image::{
imageops::FilterType, ColorType, DynamicImage, ImageBuffer, ImageEncoder, ImageFormat,
};
@ -70,7 +71,7 @@ impl TryFrom<u16> for Orientation {
8 => Ok(Orientation::Rotate270Cw),
_ => Err(Error::new(
Status::InvalidArg,
format!("Invalid orientation {}", value),
format!("Invalid orientation {value}"),
)),
}
}
@ -214,7 +215,7 @@ impl ThreadSafeDynamicImage {
let image_format = image::guess_format(input_buf).map_err(|err| {
Error::new(
Status::InvalidArg,
format!("Guess format from input image failed {}", err),
format!("Guess format from input image failed {err}"),
)
})?;
if with_exif {
@ -227,7 +228,7 @@ impl ThreadSafeDynamicImage {
let avif = libavif::decode_rgb(input_buf).map_err(|err| {
Error::new(
Status::InvalidArg,
format!("Decode avif image failed {}", err),
format!("Decode avif image failed {err}"),
)
})?;
let decoded_rgb = avif.to_vec();
@ -249,7 +250,7 @@ impl ThreadSafeDynamicImage {
}
} else {
image::load_from_memory_with_format(input_buf, image_format)
.map_err(|err| Error::new(Status::InvalidArg, format!("Decode image failed {}", err)))?
.map_err(|err| Error::new(Status::InvalidArg, format!("Decode image failed {err}")))?
};
let color_type = dynamic_image.color();
image.replace(ImageMetaData {
@ -476,6 +477,7 @@ impl Task for EncodeTask {
if let Some((x, y, width, height)) = self.image_transform_args.crop {
meta.image = meta.image.crop_imm(x, y, width, height);
}
let dynamic_image = &mut meta.image;
let color_type = &meta.color_type;
let width = dynamic_image.width();
@ -521,7 +523,7 @@ impl Task for EncodeTask {
.map_err(|err| {
Error::new(
Status::GenericFailure,
format!("Encode output png failed {}", err),
format!("Encode output png failed {err}"),
)
})?;
return Ok(EncodeOutput::Buffer(output.into_inner()));
@ -535,7 +537,7 @@ impl Task for EncodeTask {
encoder.encode_image(dynamic_image).map_err(|err| {
Error::new(
Status::GenericFailure,
format!("Encode output jpeg failed {}", err),
format!("Encode output jpeg failed {err}"),
)
})?;
return Ok(EncodeOutput::Buffer(output.into_inner()));
@ -600,6 +602,16 @@ impl Transformer {
}
}
#[napi]
/// Overlay an image at a given coordinate (x, y)
pub fn overlay(&self, on_top: Buffer, x: i64, y: i64) -> Result<&Self> {
let bottom = self.dynamic_image.get(true)?;
let top = ThreadSafeDynamicImage::new(on_top);
let top_image_meta = top.get(true)?;
overlay(&mut bottom.image, &top_image_meta.image, x, y);
Ok(self)
}
#[napi]
pub fn from_rgba_pixels(
input: Either<Buffer, Uint8ClampedArray>,