From 4b64310ff3adb41888e7f4bfae7f3557c062620c Mon Sep 17 00:00:00 2001 From: LongYinan Date: Thu, 30 Sep 2021 17:53:35 +0800 Subject: [PATCH] feat: support colorSpace: 'display-p3' --- __test__/filter.spec.ts | 4 +- index.d.ts | 17 +++++-- index.js | 19 +++---- skia-c/skia_c.cpp | 38 +++++++------- skia-c/skia_c.hpp | 15 +++--- src/ctx.rs | 52 +++++++++++++++----- src/error.rs | 2 + src/image.rs | 57 ++++++++++++++++++--- src/lib.rs | 28 +++++------ src/sk.rs | 106 +++++++++++++++++++++++++++++++++++----- 10 files changed, 253 insertions(+), 85 deletions(-) diff --git a/__test__/filter.spec.ts b/__test__/filter.spec.ts index bf28a837..3ecafc55 100644 --- a/__test__/filter.spec.ts +++ b/__test__/filter.spec.ts @@ -13,9 +13,7 @@ const test = ava as TestInterface<{ }> const FIREFOX = readFileSync(join(__dirname, 'fixtures', 'firefox-logo.svg')) -const FIREFOX_IMAGE = new Image() -FIREFOX_IMAGE.width = 200 -FIREFOX_IMAGE.height = 206.433 +const FIREFOX_IMAGE = new Image(200, 206.433) FIREFOX_IMAGE.src = FIREFOX test.beforeEach((t) => { diff --git a/index.d.ts b/index.d.ts index 44e0853b..d3a4bdab 100644 --- a/index.d.ts +++ b/index.d.ts @@ -177,11 +177,15 @@ export class ImageData { */ readonly width: number - constructor(sw: number, sh: number) + constructor(sw: number, sh: number, attr?: { colorSpace?: ColorSpace }) + constructor(imageData: ImageData, attr?: { colorSpace?: ColorSpace }) constructor(data: Uint8ClampedArray, sw: number, sh?: number) } export class Image { + constructor() + // attrs only affects SVG + constructor(width: number, height: number, attrs?: { colorSpace?: ColorSpace }) width: number height: number readonly naturalWidth: number @@ -277,10 +281,17 @@ export interface SKRSContext2D } } +export type ColorSpace = 'srgb' | 'display-p3' + +export interface ContextAttributes { + alpha?: boolean + colorSpace?: ColorSpace +} + export interface SvgCanvas { width: number height: number - getContext(contextType: '2d', contextAttributes?: { alpha: boolean }): SKRSContext2D + getContext(contextType: '2d', contextAttributes?: ContextAttributes): SKRSContext2D getContent(): Buffer } @@ -290,7 +301,7 @@ export class Canvas { width: number height: number - getContext(contextType: '2d', contextAttributes?: { alpha: boolean }): SKRSContext2D + getContext(contextType: '2d', contextAttributes?: ContextAttributes): SKRSContext2D encodeSync(format: 'webp' | 'jpeg', quality?: number): Buffer encodeSync(format: 'png'): Buffer encode(format: 'webp' | 'jpeg', quality?: number): Promise diff --git a/index.js b/index.js index d420283a..e009a182 100644 --- a/index.js +++ b/index.js @@ -163,20 +163,17 @@ function createCanvas(width, height, flag) { const canvasElement = isSvgBackend ? new SVGCanvas(width, height) : new CanvasElement(width, height) let ctx - canvasElement.getContext = function getContext(type, attr) { + canvasElement.getContext = function getContext(type, attr = {}) { if (type !== '2d') { throw new Error('Unsupported type') } + const attrs = { alpha: true, colorSpace: 'srgb', ...attr } ctx = ctx ? ctx : isSvgBackend - ? new CanvasRenderingContext2D(this.width, this.height, GlobalFontsSingleton, flag) - : new CanvasRenderingContext2D(this.width, this.height, GlobalFontsSingleton) - if (attr) { - createContext(ctx, this.width, this.height, attr) - } else { - createContext(ctx, this.width, this.height) - } + ? new CanvasRenderingContext2D(this.width, this.height, GlobalFontsSingleton, attrs.colorSpace, flag) + : new CanvasRenderingContext2D(this.width, this.height, GlobalFontsSingleton, attrs.colorSpace) + createContext(ctx, this.width, this.height, attrs) // napi can not define writable: true but enumerable: false property Object.defineProperty(ctx, '_fillStyle', { @@ -194,11 +191,11 @@ function createCanvas(width, height, flag) { }) Object.defineProperty(ctx, 'createImageData', { - value: function createImageData(widthOrImage, height) { + value: function createImageData(widthOrImage, height, attrs = {}) { if (widthOrImage instanceof ImageData) { - return new ImageData(widthOrImage.width, widthOrImage.height) + return new ImageData(widthOrImage.data, widthOrImage.width, widthOrImage.height) } - return new ImageData(widthOrImage, height) + return new ImageData(widthOrImage, height, { colorSpace: 'srgb', ...attrs }) }, configurable: false, enumerable: false, diff --git a/skia-c/skia_c.cpp b/skia-c/skia_c.cpp index 0885d8e0..1ad7da25 100644 --- a/skia-c/skia_c.cpp +++ b/skia-c/skia_c.cpp @@ -12,6 +12,7 @@ #define MASK_FILTER_CAST reinterpret_cast(c_mask_filter) #define IMAGE_FILTER_CAST reinterpret_cast(c_image_filter) #define TYPEFACE_CAST reinterpret_cast(c_typeface) +#define COLOR_SPACE_CAST cs == 0 ? SkColorSpace::MakeSRGB() : SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDisplayP3) #define MAX_LAYOUT_WIDTH 100000 #define HANGING_AS_PERCENT_OF_ASCENT 80 @@ -57,12 +58,12 @@ extern "C" // Surface - static SkSurface *skiac_surface_create(int width, int height, SkAlphaType alphaType) + static SkSurface *skiac_surface_create(int width, int height, SkAlphaType alphaType, uint8_t cs) { // Init() is idempotent, so can be called more than once with no adverse effect. SkGraphics::Init(); - - auto info = SkImageInfo::Make(width, height, kRGBA_8888_SkColorType, alphaType); + auto color_space = COLOR_SPACE_CAST; + auto info = SkImageInfo::Make(width, height, kRGBA_8888_SkColorType, alphaType, color_space); auto surface = SkSurface::MakeRaster(info); if (surface) @@ -76,7 +77,7 @@ extern "C" } } - void skiac_surface_create_svg(skiac_svg_surface *c_surface, int w, int h, int alphaType, uint32_t flag) + void skiac_surface_create_svg(skiac_svg_surface *c_surface, int w, int h, int alphaType, uint32_t flag, uint8_t cs) { auto w_stream = new SkDynamicMemoryWStream(); @@ -85,7 +86,7 @@ extern "C" { return; } - auto surface = skiac_surface_create(w, h, (SkAlphaType)alphaType); + auto surface = skiac_surface_create(w, h, (SkAlphaType)alphaType, cs); if (!surface) { return; @@ -95,16 +96,16 @@ extern "C" c_surface->canvas = reinterpret_cast(canvas.release()); } - skiac_surface *skiac_surface_create_rgba_premultiplied(int width, int height) + skiac_surface *skiac_surface_create_rgba_premultiplied(int width, int height, uint8_t cs) { return reinterpret_cast( - skiac_surface_create(width, height, kPremul_SkAlphaType)); + skiac_surface_create(width, height, kPremul_SkAlphaType, cs)); } - skiac_surface *skiac_surface_create_rgba(int width, int height) + skiac_surface *skiac_surface_create_rgba(int width, int height, uint8_t cs) { return reinterpret_cast( - skiac_surface_create(width, height, kUnpremul_SkAlphaType)); + skiac_surface_create(width, height, kUnpremul_SkAlphaType, cs)); } bool skiac_surface_save(skiac_surface *c_surface, const char *path) @@ -132,10 +133,10 @@ extern "C" skiac_surface *skiac_surface_copy_rgba( skiac_surface *c_surface, - uint32_t x, uint32_t y, uint32_t width, uint32_t height) + uint32_t x, uint32_t y, uint32_t width, uint32_t height, uint8_t cs) { // x, y, width, height are source rectangle coordinates. - auto copy = skiac_surface_create((int)width, (int)height, kUnpremul_SkAlphaType); + auto copy = skiac_surface_create((int)width, (int)height, kUnpremul_SkAlphaType, cs); if (!copy) { return nullptr; @@ -179,9 +180,10 @@ extern "C" } } - bool skiac_surface_read_pixels_rect(skiac_surface *c_surface, uint8_t *data, int x, int y, int w, int h) + bool skiac_surface_read_pixels_rect(skiac_surface *c_surface, uint8_t *data, int x, int y, int w, int h, uint8_t cs) { - auto image_info = SkImageInfo::Make(w, h, SkColorType::kRGBA_8888_SkColorType, SkAlphaType::kUnpremul_SkAlphaType, SkColorSpace::MakeSRGB()); + auto color_space = COLOR_SPACE_CAST; + auto image_info = SkImageInfo::Make(w, h, SkColorType::kRGBA_8888_SkColorType, SkAlphaType::kUnpremul_SkAlphaType, color_space); auto result = SURFACE_CAST->readPixels(image_info, data, w * 4, x, y); return result; } @@ -508,9 +510,10 @@ extern "C" CANVAS_CAST->writePixels(info, pixels, row_bytes, x, y); } - void skiac_canvas_write_pixels_dirty(skiac_canvas *c_canvas, int width, int height, uint8_t *pixels, size_t row_bytes, size_t length, float x, float y, float dirty_x, float dirty_y, float dirty_width, float dirty_height) + void skiac_canvas_write_pixels_dirty(skiac_canvas *c_canvas, int width, int height, uint8_t *pixels, size_t row_bytes, size_t length, float x, float y, float dirty_x, float dirty_y, float dirty_width, float dirty_height, uint8_t cs) { - auto info = SkImageInfo::Make(width, height, SkColorType::kRGBA_8888_SkColorType, SkAlphaType::kUnpremul_SkAlphaType); + auto color_space = COLOR_SPACE_CAST; + auto info = SkImageInfo::Make(width, height, SkColorType::kRGBA_8888_SkColorType, SkAlphaType::kUnpremul_SkAlphaType, color_space); auto data = SkData::MakeFromMalloc(pixels, length); auto image = SkImage::MakeRasterData(info, data, row_bytes); auto src_rect = SkRect::MakeXYWH(dirty_x, dirty_y, dirty_width, dirty_height); @@ -1317,8 +1320,9 @@ extern "C" bitmap_info->height = info.height(); } - void skiac_bitmap_make_from_svg(const uint8_t *data, size_t length, float width, float height, skiac_bitmap_info *bitmap_info) + void skiac_bitmap_make_from_svg(const uint8_t *data, size_t length, float width, float height, skiac_bitmap_info *bitmap_info, uint8_t cs) { + auto color_space = COLOR_SPACE_CAST; auto svg_stream = new SkMemoryStream(data, length, false); auto svg_dom = SkSVGDOM::MakeFromStream(*svg_stream); auto svg_root = svg_dom->getRoot(); @@ -1345,7 +1349,7 @@ extern "C" image_w = width; image_h = height; } - auto imageinfo = SkImageInfo::Make(image_w, image_h, kRGBA_8888_SkColorType, SkAlphaType::kOpaque_SkAlphaType); + auto imageinfo = SkImageInfo::Make(image_w, image_h, kRGBA_8888_SkColorType, SkAlphaType::kOpaque_SkAlphaType, color_space); auto bitmap = new SkBitmap(); bitmap->allocPixels(imageinfo); auto sk_svg_canvas = new SkCanvas(*bitmap); diff --git a/skia-c/skia_c.hpp b/skia-c/skia_c.hpp index 719158bb..5e991311 100644 --- a/skia-c/skia_c.hpp +++ b/skia-c/skia_c.hpp @@ -203,21 +203,22 @@ extern "C" { // Surface - skiac_surface *skiac_surface_create_rgba_premultiplied(int width, int height); - void skiac_surface_create_svg(skiac_svg_surface *c_surface, int width, int height, int alphaType, uint32_t flag); - skiac_surface *skiac_surface_create_rgba(int width, int height); + skiac_surface *skiac_surface_create_rgba_premultiplied(int width, int height, uint8_t cs); + void skiac_surface_create_svg(skiac_svg_surface *c_surface, int width, int height, int alphaType, uint32_t flag, uint8_t cs); + skiac_surface *skiac_surface_create_rgba(int width, int height, uint8_t cs); void skiac_surface_destroy(skiac_surface *c_surface); skiac_surface *skiac_surface_copy_rgba( skiac_surface *c_surface, uint32_t x, uint32_t y, uint32_t width, - uint32_t height); + uint32_t height, + uint8_t cs); skiac_canvas *skiac_surface_get_canvas(skiac_surface *c_surface); int skiac_surface_get_width(skiac_surface *c_surface); int skiac_surface_get_height(skiac_surface *c_surface); void skiac_surface_read_pixels(skiac_surface *c_surface, skiac_surface_data *data); - bool skiac_surface_read_pixels_rect(skiac_surface *c_surface, uint8_t *data, int x, int y, int w, int h); + bool skiac_surface_read_pixels_rect(skiac_surface *c_surface, uint8_t *data, int x, int y, int w, int h, uint8_t cs); void skiac_surface_png_data(skiac_surface *c_surface, skiac_sk_data *data); void skiac_surface_encode_data(skiac_surface *c_surface, skiac_sk_data *data, int format, int quality); int skiac_surface_get_alpha_type(skiac_surface *c_surface); @@ -285,7 +286,7 @@ extern "C" void skiac_canvas_restore(skiac_canvas *c_canvas); void skiac_canvas_reset(skiac_canvas *c_canvas); void skiac_canvas_write_pixels(skiac_canvas *c_canvas, int width, int height, uint8_t *pixels, size_t row_bytes, int x, int y); - void skiac_canvas_write_pixels_dirty(skiac_canvas *c_canvas, int width, int height, uint8_t *pixels, size_t row_bytes, size_t length, float x, float y, float dirty_x, float dirty_y, float dirty_width, float dirty_height); + void skiac_canvas_write_pixels_dirty(skiac_canvas *c_canvas, int width, int height, uint8_t *pixels, size_t row_bytes, size_t length, float x, float y, float dirty_x, float dirty_y, float dirty_width, float dirty_height, uint8_t cs); // Paint skiac_paint *skiac_paint_create(); @@ -425,7 +426,7 @@ extern "C" // Bitmap void skiac_bitmap_make_from_buffer(const uint8_t *ptr, size_t size, skiac_bitmap_info *bitmap_info); - void skiac_bitmap_make_from_svg(const uint8_t *data, size_t length, float width, float height, skiac_bitmap_info *bitmap_info); + void skiac_bitmap_make_from_svg(const uint8_t *data, size_t length, float width, float height, skiac_bitmap_info *bitmap_info, uint8_t cs); skiac_bitmap *skiac_bitmap_make_from_image_data(uint8_t *ptr, size_t width, size_t height, size_t row_bytes, size_t size, int ct, int at); size_t skiac_bitmap_get_width(skiac_bitmap *c_bitmap); size_t skiac_bitmap_get_height(skiac_bitmap *c_bitmap); diff --git a/src/ctx.rs b/src/ctx.rs index 7bf665a1..e188347c 100644 --- a/src/ctx.rs +++ b/src/ctx.rs @@ -45,6 +45,7 @@ pub struct Context { pub font_collection: Rc, pub width: u32, pub height: u32, + pub color_space: ColorSpace, pub stream: Option, pub filter: Option, filters_string: String, @@ -168,11 +169,17 @@ impl Context { width: u32, height: u32, svg_export_flag: SvgExportFlag, + color_space: ColorSpace, font_collection: &mut Rc, ) -> Result { - let (surface, stream) = - Surface::new_svg(width, height, AlphaType::Unpremultiplied, svg_export_flag) - .ok_or_else(|| Error::from_reason("Create skia svg surface failed".to_owned()))?; + let (surface, stream) = Surface::new_svg( + width, + height, + AlphaType::Unpremultiplied, + svg_export_flag, + color_space, + ) + .ok_or_else(|| Error::from_reason("Create skia svg surface failed".to_owned()))?; Ok(Context { surface, alpha: true, @@ -182,14 +189,20 @@ impl Context { font_collection: font_collection.clone(), width, height, + color_space, stream: Some(stream), filter: None, filters_string: "none".to_owned(), }) } - pub fn new(width: u32, height: u32, font_collection: &mut Rc) -> Result { - let surface = Surface::new_rgba(width, height) + pub fn new( + width: u32, + height: u32, + color_space: ColorSpace, + font_collection: &mut Rc, + ) -> Result { + let surface = Surface::new_rgba(width, height, color_space) .ok_or_else(|| Error::from_reason("Create skia surface failed".to_owned()))?; Ok(Context { surface, @@ -200,6 +213,7 @@ impl Context { font_collection: font_collection.clone(), width, height, + color_space, stream: None, filter: None, filters_string: "none".to_owned(), @@ -719,23 +733,26 @@ impl Context { } } -#[js_function(4)] +#[js_function(5)] fn context_2d_constructor(ctx: CallContext) -> Result { let width = ctx.get::(0)?.get_uint32()?; let height = ctx.get::(1)?.get_uint32()?; let font_collection_js = ctx.get::(2)?; let font_collection = ctx.env.unwrap::>(&font_collection_js)?; + let color_space = ctx.get::(3)?.into_utf8()?; + let color_space = ColorSpace::from_str(color_space.as_str()?)?; let mut this = ctx.this_unchecked::(); - let context_2d = if ctx.length == 3 { - Context::new(width, height, font_collection)? + let context_2d = if ctx.length == 4 { + Context::new(width, height, color_space, font_collection)? } else { // SVG Canvas - let flag = ctx.get::(3)?.get_uint32()?; + let flag = ctx.get::(4)?.get_uint32()?; Context::new_svg( width, height, SvgExportFlag::try_from(flag)?, + color_space, font_collection, )? }; @@ -1362,7 +1379,7 @@ fn fill_text(ctx: CallContext) -> Result { ctx.env.get_undefined() } -#[js_function(4)] +#[js_function(5)] fn get_image_data(ctx: CallContext) -> Result { let this = ctx.this_unchecked::(); let context_2d = ctx.env.unwrap::(&this)?; @@ -1370,9 +1387,21 @@ fn get_image_data(ctx: CallContext) -> Result { let y = ctx.get::(1)?.get_uint32()?; let width = ctx.get::(2)?.get_uint32()?; let height = ctx.get::(3)?.get_uint32()?; + let color_space = if ctx.length == 5 { + let image_settings = ctx.get::(4)?; + let cs = image_settings.get_named_property_unchecked::("colorSpace")?; + if cs.get_type()? == ValueType::String { + let color_space_js = unsafe { cs.cast::() }.into_utf8()?; + ColorSpace::from_str(color_space_js.as_str()?)? + } else { + ColorSpace::default() + } + } else { + ColorSpace::default() + }; let pixels = context_2d .surface - .read_pixels(x, y, width, height) + .read_pixels(x, y, width, height, color_space) .ok_or_else(|| { Error::new( Status::GenericFailure, @@ -1462,6 +1491,7 @@ fn put_image_data(ctx: CallContext) -> Result { dirty_y, dirty_width, dirty_height, + image_data.color_space, ); context_2d.surface.canvas.restore(); }; diff --git a/src/error.rs b/src/error.rs index b894d609..c6fea91a 100644 --- a/src/error.rs +++ b/src/error.rs @@ -4,6 +4,8 @@ use crate::sk::Matrix; #[derive(Error, Debug)] pub enum SkError { + #[error("[`{0}`] is not valid ColorSpace value")] + StringToColorSpaceError(String), #[error("[`{0}`] is not valid Blend value")] StringToBlendError(String), #[error("[`{0}`] is not valid FillRule value")] diff --git a/src/image.rs b/src/image.rs index 2979fbe4..eb4b7334 100644 --- a/src/image.rs +++ b/src/image.rs @@ -1,18 +1,21 @@ use std::mem::ManuallyDrop; use std::slice; use std::str; +use std::str::FromStr; use base64::decode; use napi::*; use crate::ctx::ImageOrCanvas; use crate::sk::Bitmap; +use crate::sk::ColorSpace; #[derive(Debug, Clone)] pub struct ImageData { pub(crate) width: usize, pub(crate) height: usize, pub(crate) data: *const u8, + pub(crate) color_space: ColorSpace, } impl Drop for ImageData { @@ -32,11 +35,20 @@ impl ImageData { fn image_data_constructor(ctx: CallContext) -> Result { let first_arg = ctx.get::(0)?; let first_arg_type = first_arg.get_type()?; - let ((js_width, width), (js_height, height), arraybuffer_length, mut initial_data) = + let ((js_width, width), (js_height, height), arraybuffer_length, mut initial_data, color_space) = match first_arg_type { ValueType::Number => { let js_width = unsafe { first_arg.cast::() }; let js_height = ctx.get::(1)?; + let color_space = if ctx.length == 3 { + let image_settings = ctx.get::(2)?; + let js_color_space = image_settings + .get_named_property::("colorSpace")? + .into_utf8()?; + ColorSpace::from_str(js_color_space.as_str()?)? + } else { + ColorSpace::default() + }; let width = js_width.get_uint32()?; let height = js_height.get_uint32()?; let arraybuffer_length = (width * height * 4) as usize; @@ -45,6 +57,7 @@ fn image_data_constructor(ctx: CallContext) -> Result { (js_height, height), arraybuffer_length, ManuallyDrop::new(vec![0u8; arraybuffer_length]), + color_space, )) } ValueType::Object => { @@ -60,7 +73,7 @@ fn image_data_constructor(ctx: CallContext) -> Result { let arraybuffer_length = arraybuffer.len(); let js_width = ctx.get::(1)?; let width = js_width.get_uint32()?; - let (js_height, height) = if ctx.length == 3 { + let (js_height, height) = if ctx.length >= 3 { let js_height = ctx.get::(2)?; let height = js_height.get_uint32()?; if height * width * 4 != arraybuffer_length as u32 { @@ -81,6 +94,7 @@ fn image_data_constructor(ctx: CallContext) -> Result { ManuallyDrop::new(unsafe { slice::from_raw_parts(arraybuffer.as_ptr() as *const u8, arraybuffer_length).to_owned() }), + ColorSpace::default(), )) } _ => Err(Error::new( @@ -96,6 +110,7 @@ fn image_data_constructor(ctx: CallContext) -> Result { width: width as usize, height: height as usize, data: data_ptr, + color_space, }; let arraybuffer = unsafe { ctx @@ -131,6 +146,7 @@ pub(crate) struct Image { height: f64, pub(crate) need_regenerate_bitmap: bool, pub(crate) is_svg: bool, + pub(crate) color_space: ColorSpace, } impl Image { @@ -146,6 +162,7 @@ impl Image { data.as_ref().len(), self.width as f32, self.height as f32, + self.color_space, ); } } @@ -182,16 +199,33 @@ impl Image { } } -#[js_function] +#[js_function(3)] fn image_constructor(ctx: CallContext) -> Result { + let width = if ctx.length > 0 { + ctx.get::(0)?.get_double()? + } else { + -1.0 + }; + let height = if ctx.length > 1 { + ctx.get::(1)?.get_double()? + } else { + -1.0 + }; + let color_space = if ctx.length == 3 { + let color_space = ctx.get::(2)?.into_utf8()?; + ColorSpace::from_str(color_space.as_str()?)? + } else { + ColorSpace::default() + }; let js_image = Image { complete: false, bitmap: None, alt: "".to_string(), - width: -1.0, - height: -1.0, + width, + height, need_regenerate_bitmap: false, is_svg: false, + color_space, }; let mut this = ctx.this_unchecked::(); this.set_named_property("_src", ctx.env.get_undefined()?)?; @@ -324,7 +358,18 @@ fn set_src(ctx: CallContext) -> Result { image.complete = true; image.is_svg = is_svg; if is_svg { - let bitmap = Bitmap::from_svg_data(src_data.as_ptr(), length); + let bitmap = + if (image.width - -1.0).abs() > f64::EPSILON && (image.height - -1.0).abs() > f64::EPSILON { + Bitmap::from_svg_data_with_custom_size( + src_data.as_ptr(), + length, + image.width as f32, + image.height as f32, + image.color_space, + ) + } else { + Bitmap::from_svg_data(src_data.as_ptr(), length, image.color_space) + }; if let Some(b) = bitmap.as_ref() { if (image.width - -1.0).abs() < f64::EPSILON { image.width = b.0.width as f64; diff --git a/src/lib.rs b/src/lib.rs index f16d25ed..ca82dccb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -122,21 +122,19 @@ fn create_context(ctx: CallContext) -> Result { let context_2d_object = ctx.get::(0)?; let context_2d = ctx.env.unwrap::(&context_2d_object)?; - if ctx.length == 4 { - let w = ctx.get::(1)?.get_double()?; - let h = ctx.get::(2)?.get_double()?; - let attrs = ctx.get::(3)?; - let alpha = attrs - .get_named_property_unchecked::("alpha")? - .get_value()?; - if !alpha { - let mut fill_paint = context_2d.fill_paint()?; - fill_paint.set_color(255, 255, 255, 255); - context_2d.alpha = false; - context_2d - .surface - .draw_rect(0f32, 0f32, w as f32, h as f32, &fill_paint); - } + let w = ctx.get::(1)?.get_double()?; + let h = ctx.get::(2)?.get_double()?; + let attrs = ctx.get::(3)?; + let alpha = attrs + .get_named_property_unchecked::("alpha")? + .get_value()?; + if !alpha { + let mut fill_paint = context_2d.fill_paint()?; + fill_paint.set_color(255, 255, 255, 255); + context_2d.alpha = false; + context_2d + .surface + .draw_rect(0f32, 0f32, w as f32, h as f32, &fill_paint); } ctx.env.get_undefined() diff --git a/src/sk.rs b/src/sk.rs index 645e2c72..1c1a84ed 100644 --- a/src/sk.rs +++ b/src/sk.rs @@ -211,7 +211,11 @@ mod ffi { extern "C" { - pub fn skiac_surface_create_rgba_premultiplied(width: i32, height: i32) -> *mut skiac_surface; + pub fn skiac_surface_create_rgba_premultiplied( + width: i32, + height: i32, + cs: u8, + ) -> *mut skiac_surface; pub fn skiac_surface_create_svg( c_surface: *mut skiac_svg_surface, @@ -219,9 +223,10 @@ mod ffi { height: i32, alphaType: i32, flag: u32, + cs: u8, ); - pub fn skiac_surface_create_rgba(width: i32, height: i32) -> *mut skiac_surface; + pub fn skiac_surface_create_rgba(width: i32, height: i32, cs: u8) -> *mut skiac_surface; pub fn skiac_surface_destroy(surface: *mut skiac_surface); @@ -231,6 +236,7 @@ mod ffi { y: u32, width: u32, height: u32, + cs: u8, ) -> *mut skiac_surface; pub fn skiac_surface_save(c_surface: *mut skiac_surface, path: *const c_char) -> bool; @@ -250,6 +256,7 @@ mod ffi { y: i32, w: i32, h: i32, + color_space: u8, ) -> bool; pub fn skiac_surface_png_data(surface: *mut skiac_surface, data: *mut skiac_sk_data); @@ -401,6 +408,7 @@ mod ffi { dirty_y: f32, dirty_width: f32, dirty_height: f32, + color_space: u8, ); pub fn skiac_paint_create() -> *mut skiac_paint; @@ -727,6 +735,7 @@ mod ffi { width: f32, height: f32, info: *mut skiac_bitmap_info, + cs: u8, ); pub fn skiac_bitmap_make_from_image_data( @@ -859,6 +868,31 @@ pub enum ColorType { R16G16B16A16Unorm, } +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +#[repr(u8)] +pub enum ColorSpace { + Srgb, + DisplayP3, +} + +impl Default for ColorSpace { + fn default() -> Self { + Self::Srgb + } +} + +impl FromStr for ColorSpace { + type Err = SkError; + + fn from_str(value: &str) -> Result { + match value { + "srgb" => Ok(Self::Srgb), + "display-p3" | "p3" => Ok(Self::DisplayP3), + _ => Err(SkError::StringToColorSpaceError(value.to_owned())), + } + } +} + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub enum PaintStyle { Fill = 0, @@ -1439,15 +1473,26 @@ pub struct Surface { } impl Surface { - pub fn new_rgba(width: u32, height: u32) -> Option { - unsafe { Self::from_ptr(ffi::skiac_surface_create_rgba(width as i32, height as i32)) } + pub fn new_rgba(width: u32, height: u32, color_space: ColorSpace) -> Option { + unsafe { + Self::from_ptr(ffi::skiac_surface_create_rgba( + width as i32, + height as i32, + color_space as u8, + )) + } } - pub fn new_rgba_premultiplied(width: u32, height: u32) -> Option { + pub fn new_rgba_premultiplied( + width: u32, + height: u32, + color_space: ColorSpace, + ) -> Option { unsafe { Self::from_ptr(ffi::skiac_surface_create_rgba_premultiplied( width as i32, height as i32, + color_space as u8, )) } } @@ -1457,6 +1502,7 @@ impl Surface { height: u32, alpha_type: AlphaType, flag: SvgExportFlag, + color_space: ColorSpace, ) -> Option<(Surface, SkWMemoryStream)> { let mut svg_surface = ffi::skiac_svg_surface { stream: ptr::null_mut(), @@ -1470,6 +1516,7 @@ impl Surface { height as i32, alpha_type as i32, flag as u32, + color_space as u8, ); }; if svg_surface.surface.is_null() { @@ -1495,11 +1542,27 @@ impl Surface { } } - pub fn copy_rgba(&self, x: u32, y: u32, width: u32, height: u32) -> Option { - unsafe { Self::from_ptr(ffi::skiac_surface_copy_rgba(self.ptr, x, y, width, height)) } + pub fn copy_rgba( + &self, + x: u32, + y: u32, + width: u32, + height: u32, + color_space: ColorSpace, + ) -> Option { + unsafe { + Self::from_ptr(ffi::skiac_surface_copy_rgba( + self.ptr, + x, + y, + width, + height, + color_space as u8, + )) + } } - pub fn try_clone(&self) -> Option { + pub fn try_clone(&self, color_space: ColorSpace) -> Option { unsafe { Self::from_ptr(ffi::skiac_surface_copy_rgba( self.ptr, @@ -1507,6 +1570,7 @@ impl Surface { 0, self.width(), self.height(), + color_space as u8, )) } } @@ -1535,7 +1599,14 @@ impl Surface { } } - pub fn read_pixels(&self, x: u32, y: u32, width: u32, height: u32) -> Option> { + pub fn read_pixels( + &self, + x: u32, + y: u32, + width: u32, + height: u32, + color_space: ColorSpace, + ) -> Option> { let mut result = vec![0; (width * height * 4) as usize]; let status = unsafe { ffi::skiac_surface_read_pixels_rect( @@ -1545,6 +1616,7 @@ impl Surface { y as i32, width as i32, height as i32, + color_space as u8, ) }; if status { @@ -2088,6 +2160,7 @@ impl Canvas { dirty_y: f32, dirty_width: f32, dirty_height: f32, + color_space: ColorSpace, ) { unsafe { ffi::skiac_canvas_write_pixels_dirty( @@ -2103,6 +2176,7 @@ impl Canvas { dirty_y, dirty_width, dirty_height, + color_space as u8, ) } } @@ -3167,14 +3241,14 @@ impl Bitmap { } } - pub fn from_svg_data(data: *const u8, size: usize) -> Option { + pub fn from_svg_data(data: *const u8, size: usize, color_space: ColorSpace) -> Option { let mut bitmap_info = ffi::skiac_bitmap_info { bitmap: ptr::null_mut(), width: 0, height: 0, }; unsafe { - ffi::skiac_bitmap_make_from_svg(data, size, -1.0, -1.0, &mut bitmap_info); + ffi::skiac_bitmap_make_from_svg(data, size, -1.0, -1.0, &mut bitmap_info, color_space as u8); if bitmap_info.bitmap.is_null() { return None; @@ -3188,6 +3262,7 @@ impl Bitmap { size: usize, width: f32, height: f32, + color_space: ColorSpace, ) -> Option { let mut bitmap_info = ffi::skiac_bitmap_info { bitmap: ptr::null_mut(), @@ -3195,7 +3270,14 @@ impl Bitmap { height: 0, }; unsafe { - ffi::skiac_bitmap_make_from_svg(data, size, width, height, &mut bitmap_info); + ffi::skiac_bitmap_make_from_svg( + data, + size, + width, + height, + &mut bitmap_info, + color_space as u8, + ); if bitmap_info.bitmap.is_null() { return None;