diff --git a/skia-c/skia_c.cpp b/skia-c/skia_c.cpp index 69d77018..1b19c37b 100644 --- a/skia-c/skia_c.cpp +++ b/skia-c/skia_c.cpp @@ -61,6 +61,38 @@ extern "C" SkGraphics::PurgeAllCaches(); } + void skiac_create_picture_recorder(uint32_t width, uint32_t height, skiac_picture_recorder_created *pr) + { + auto picture_recorder = new SkPictureRecorder(); + auto canvas = picture_recorder->beginRecording((SkScalar)width, (SkScalar)height); + auto surface = canvas->getSurface(); + pr->recorder = reinterpret_cast(picture_recorder); + pr->canvas = reinterpret_cast(canvas); + pr->surface = reinterpret_cast(surface); + } + + void skiac_picture_recorder_destroy(skiac_picture_recorder *pr) + { + delete reinterpret_cast(pr); + } + + skiac_picture *skiac_picture_recorder_finish_as_picture(skiac_picture_recorder *pr) + { + auto picture_recorder = reinterpret_cast(pr); + auto picture = picture_recorder->finishRecordingAsPicture().release(); + return reinterpret_cast(picture); + } + + void skiac_picture_ref(skiac_picture *picture) + { + SkSafeRef(reinterpret_cast(picture)); + } + + void skiac_picture_destroy(skiac_picture *picture) + { + SkSafeUnref(reinterpret_cast(picture)); + } + // Surface static SkSurface *skiac_surface_create(int width, int height, SkAlphaType alphaType, uint8_t cs) @@ -133,7 +165,7 @@ extern "C" void skiac_surface_destroy(skiac_surface *c_surface) { // SkSurface is ref counted. - SURFACE_CAST->unref(); + SkSafeUnref(SURFACE_CAST); } skiac_surface *skiac_surface_copy_rgba( @@ -356,6 +388,16 @@ extern "C" CANVAS_CAST->drawImageRect(image, src, dst, sampling, nullptr, SkCanvas::kFast_SrcRectConstraint); } + void skiac_canvas_draw_picture( + skiac_canvas *c_canvas, + skiac_picture *c_picture, + skiac_matrix *c_matrix, + skiac_paint *c_paint) + { + auto picture = reinterpret_cast(c_picture); + CANVAS_CAST->drawPicture(picture, MATRIX_CAST, PAINT_CAST); + } + void skiac_canvas_get_line_metrics_or_draw_text( const char *text, size_t text_len, diff --git a/skia-c/skia_c.hpp b/skia-c/skia_c.hpp index fbb48a43..58ad9e14 100644 --- a/skia-c/skia_c.hpp +++ b/skia-c/skia_c.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -47,10 +48,12 @@ using namespace skia::textlayout; +typedef struct skiac_picture_recorder skiac_picture_recorder; typedef struct skiac_surface skiac_surface; typedef struct skiac_canvas skiac_canvas; typedef struct skiac_paint skiac_paint; typedef struct skiac_path skiac_path; +typedef struct skiac_picture skiac_picture; typedef struct skiac_shader skiac_shader; typedef struct skiac_path_effect skiac_path_effect; typedef struct skiac_matrix skiac_matrix; @@ -137,6 +140,13 @@ struct skiac_font_collection } }; +struct skiac_picture_recorder_created +{ + skiac_picture_recorder *recorder; + skiac_canvas *canvas; + skiac_surface *surface; +}; + struct skiac_line_metrics { float ascent; @@ -212,6 +222,13 @@ struct skiac_mapped_point extern "C" { void skiac_clear_all_cache(); + // PictureRecorder + void skiac_create_picture_recorder(uint32_t width, uint32_t height, skiac_picture_recorder_created *pr); + void skiac_picture_recorder_destroy(skiac_picture_recorder *pr); + // Picture + skiac_picture *skiac_picture_recorder_finish_as_picture(skiac_picture_recorder *pr); + void skiac_picture_ref(skiac_picture *picture); + void skiac_picture_destroy(skiac_picture *picture); // Surface 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); @@ -284,6 +301,11 @@ extern "C" float dw, float dh, int filter_quality); + void skiac_canvas_draw_picture( + skiac_canvas *c_canvas, + skiac_picture *c_picture, + skiac_matrix *c_matrix, + skiac_paint *c_paint); void skiac_canvas_get_line_metrics_or_draw_text( const char *text, size_t text_len, diff --git a/src/ctx.rs b/src/ctx.rs index 2b6b2806..2462e7e1 100644 --- a/src/ctx.rs +++ b/src/ctx.rs @@ -43,13 +43,26 @@ pub struct Context { pub alpha: bool, pub(crate) states: Vec, state: Context2dRenderingState, - pub width: u32, - pub height: u32, pub color_space: ColorSpace, pub stream: Option, } impl Context { + pub fn new_recorder(width: u32, height: u32, color_space: ColorSpace) -> Result { + let surface = Surface::new_picture_recorder(width, height).ok_or_else(|| { + Error::from_reason("Create skia picture recorder surface failed".to_owned()) + })?; + Ok(Self { + surface, + alpha: true, + path: SkPath::new(), + states: vec![], + state: Context2dRenderingState::default(), + color_space, + stream: None, + }) + } + pub fn new_svg( width: u32, height: u32, @@ -70,8 +83,6 @@ impl Context { path: SkPath::new(), states: vec![], state: Context2dRenderingState::default(), - width, - height, color_space, stream: Some(stream), }) @@ -86,8 +97,6 @@ impl Context { path: SkPath::new(), states: vec![], state: Context2dRenderingState::default(), - width, - height, color_space, stream: None, }) @@ -659,6 +668,7 @@ impl Context { paint: &Paint, ) -> result::Result<(), SkError> { let state = &self.state; + let width = self.surface.width; let weight = state.font_style.weight; let stretch = state.font_style.stretch; let slant = state.font_style.style; @@ -671,7 +681,7 @@ impl Context { x, y, max_width, - self.width as f32, + width as f32, weight, stretch as i32, slant, @@ -691,7 +701,7 @@ impl Context { x, y, max_width, - self.width as f32, + self.surface.width as f32, weight, stretch as i32, slant, @@ -785,7 +795,9 @@ pub struct CanvasRenderingContext2D { impl ObjectFinalize for CanvasRenderingContext2D { fn finalize(self, mut env: Env) -> Result<()> { - env.adjust_external_memory(-((self.context.width * self.context.height * 4) as i64))?; + env.adjust_external_memory( + -((self.context.surface.width * self.context.surface.height * 4) as i64), + )?; Ok(()) } } @@ -803,7 +815,7 @@ impl CanvasRenderingContext2D { let context = if let Some(flag) = flag { Context::new_svg(width, height, flag.into(), color_space)? } else { - Context::new(width, height, color_space)? + Context::new_recorder(width, height, color_space)? }; Ok(Self { context }) } diff --git a/src/lib.rs b/src/lib.rs index 0e2089a7..8d751b52 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -251,7 +251,12 @@ impl CanvasElement { "png" => ContextData::Png(surface_ref), "avif" => { let cfg = AvifConfig::from(&quality_or_config); - ContextData::Avif(surface_ref, cfg.into(), ctx2d.width, ctx2d.height) + ContextData::Avif( + surface_ref, + cfg.into(), + ctx2d.surface.width, + ctx2d.surface.height, + ) } _ => { return Err(Error::new( @@ -313,8 +318,8 @@ fn get_data_ref( let config = AvifConfig::from(quality_or_config).into(); let output = avif::encode( unsafe { slice::from_raw_parts(data, size) }, - ctx2d.width, - ctx2d.height, + ctx2d.surface.width, + ctx2d.surface.height, &config, ) .map_err(|e| Error::new(Status::GenericFailure, format!("{}", e)))?; @@ -462,7 +467,10 @@ impl SVGCanvas { #[napi] pub fn get_content(&self, env: Env) -> Result { let svg_data_stream = self.ctx.context.stream.as_ref().unwrap(); - let svg_data = svg_data_stream.data(self.ctx.context.width, self.ctx.context.height); + let svg_data = svg_data_stream.data( + self.ctx.context.surface.width, + self.ctx.context.surface.height, + ); unsafe { env .create_buffer_with_borrowed_data(svg_data.0.ptr, svg_data.0.size, svg_data, |d, _| { diff --git a/src/sk.rs b/src/sk.rs index 0116cc87..8205c95b 100644 --- a/src/sk.rs +++ b/src/sk.rs @@ -38,6 +38,26 @@ pub mod ffi { pub canvas: *mut skiac_canvas, } + #[repr(C)] + #[derive(Copy, Clone, Debug)] + pub struct skiac_picture_recorder_created { + pub recorder: *mut skiac_picture_recorder, + pub canvas: *mut skiac_canvas, + pub surface: *mut skiac_surface, + } + + #[repr(C)] + #[derive(Copy, Clone, Debug)] + pub struct skiac_picture_recorder { + _unused: [u8; 0], + } + + #[repr(C)] + #[derive(Copy, Clone, Debug)] + pub struct skiac_picture { + _unused: [u8; 0], + } + #[repr(C)] #[derive(Copy, Clone, Debug)] pub struct skiac_canvas { @@ -248,6 +268,22 @@ pub mod ffi { pub fn skiac_clear_all_cache(); + pub fn skiac_create_picture_recorder( + width: u32, + height: u32, + pr: *mut skiac_picture_recorder_created, + ); + + pub fn skiac_picture_recorder_destroy(pr: *mut skiac_picture_recorder); + + pub fn skiac_picture_recorder_finish_as_picture( + pr: *mut skiac_picture_recorder, + ) -> *mut skiac_picture; + + pub fn skiac_picture_ref(picture: *mut skiac_picture); + + pub fn skiac_picture_destroy(picture: *mut skiac_picture); + pub fn skiac_surface_create_rgba_premultiplied( width: i32, height: i32, @@ -392,6 +428,13 @@ pub mod ffi { filter_quality: i32, ); + pub fn skiac_canvas_draw_picture( + canvas: *mut skiac_canvas, + picture: *mut skiac_picture, + matrix: *mut skiac_matrix, + paint: *mut skiac_paint, + ); + pub fn skiac_canvas_get_line_metrics_or_draw_text( text: *const c_char, text_len: usize, @@ -1516,19 +1559,44 @@ impl TryFrom for SvgExportFlag { } } +pub struct Picture(*mut ffi::skiac_picture); + +impl Clone for Picture { + fn clone(&self) -> Self { + unsafe { + ffi::skiac_picture_ref(self.0); + } + Self(self.0) + } +} + +impl Drop for Picture { + fn drop(&mut self) { + unsafe { + ffi::skiac_picture_destroy(self.0); + } + } +} + +unsafe impl Send for Picture {} +unsafe impl Sync for Picture {} + pub struct Surface { ptr: *mut ffi::skiac_surface, pub(crate) canvas: Canvas, + picture_recorder: *mut ffi::skiac_picture_recorder, + pub width: u32, + pub height: u32, } impl Surface { 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, - )) + Self::from_ptr( + ffi::skiac_surface_create_rgba(width as i32, height as i32, color_space as u8), + width, + height, + ) } } @@ -1538,11 +1606,15 @@ impl Surface { color_space: ColorSpace, ) -> Option { unsafe { - Self::from_ptr(ffi::skiac_surface_create_rgba_premultiplied( - width as i32, - height as i32, - color_space as u8, - )) + Self::from_ptr( + ffi::skiac_surface_create_rgba_premultiplied( + width as i32, + height as i32, + color_space as u8, + ), + width, + height, + ) } } @@ -1575,22 +1647,53 @@ impl Surface { Self { ptr: svg_surface.surface, canvas: Canvas(svg_surface.canvas), + picture_recorder: ptr::null_mut(), + width, + height, }, SkWMemoryStream(svg_surface.stream), )) } - unsafe fn from_ptr(ptr: *mut ffi::skiac_surface) -> Option { + pub fn new_picture_recorder(width: u32, height: u32) -> Option { + let mut pr = ffi::skiac_picture_recorder_created { + surface: ptr::null_mut(), + recorder: ptr::null_mut(), + canvas: ptr::null_mut(), + }; + unsafe { + ffi::skiac_create_picture_recorder(width, height, &mut pr); + }; + if pr.recorder.is_null() || pr.surface.is_null() || pr.canvas.is_null() { + return None; + } + Some(Surface { + ptr: pr.surface, + canvas: Canvas(pr.canvas), + picture_recorder: pr.recorder, + width, + height, + }) + } + + unsafe fn from_ptr(ptr: *mut ffi::skiac_surface, width: u32, height: u32) -> Option { if ptr.is_null() { None } else { Some(Surface { ptr, canvas: Canvas(ffi::skiac_surface_get_canvas(ptr)), + picture_recorder: ptr::null_mut(), + width, + height, }) } } + pub fn is_recorder(&self) -> bool { + !self.picture_recorder.is_null() + } + pub fn copy_rgba( &self, x: u32, @@ -1600,27 +1703,28 @@ impl Surface { color_space: ColorSpace, ) -> Option { unsafe { - Self::from_ptr(ffi::skiac_surface_copy_rgba( - self.ptr, - x, - y, + Self::from_ptr( + ffi::skiac_surface_copy_rgba(self.ptr, x, y, width, height, color_space as u8), width, height, - color_space as u8, - )) + ) } } pub fn try_clone(&self, color_space: ColorSpace) -> Option { unsafe { - Self::from_ptr(ffi::skiac_surface_copy_rgba( - self.ptr, - 0, - 0, - self.width(), - self.height(), - color_space as u8, - )) + Self::from_ptr( + ffi::skiac_surface_copy_rgba( + self.ptr, + 0, + 0, + self.width(), + self.height(), + color_space as u8, + ), + self.width, + self.height, + ) } } @@ -1764,8 +1868,14 @@ impl std::ops::DerefMut for Surface { impl Drop for Surface { fn drop(&mut self) { - unsafe { - ffi::skiac_surface_destroy(self.ptr); + if self.picture_recorder.is_null() { + unsafe { + ffi::skiac_surface_destroy(self.ptr); + } + } else { + unsafe { + ffi::skiac_picture_recorder_destroy(self.picture_recorder); + } } } }