diff --git a/crates/canvas-2d/src/context/mod.rs b/crates/canvas-2d/src/context/mod.rs index ad89e0b3..65bf7d79 100644 --- a/crates/canvas-2d/src/context/mod.rs +++ b/crates/canvas-2d/src/context/mod.rs @@ -560,6 +560,14 @@ impl Context { }; } + pub fn reset(&mut self) { + self.reset_state(); + self.reset_transform(); + self.with_canvas_dirty(|canvas| { + canvas.clear(Color::TRANSPARENT); + }); + } + pub fn reset_state(&mut self) { let direction = self.state.direction; self.state = State::default(); diff --git a/crates/canvas-c/src/c2d/context.rs b/crates/canvas-c/src/c2d/context.rs index 7a05e1ad..4204aa33 100644 --- a/crates/canvas-c/src/c2d/context.rs +++ b/crates/canvas-c/src/c2d/context.rs @@ -2457,6 +2457,14 @@ pub extern "C" fn canvas_native_context_rect( context.context.rect(x, y, width, height) } +#[no_mangle] +pub extern "C" fn canvas_native_context_reset( + context: *mut CanvasRenderingContext2D +) { + let context = unsafe { &mut *context }; + context.context.reset() +} + #[no_mangle] pub extern "C" fn canvas_native_context_round_rect( context: *mut CanvasRenderingContext2D, diff --git a/napi/canvas-napi/index.d.ts b/napi/canvas-napi/index.d.ts index 7efdc5ca..91683789 100644 --- a/napi/canvas-napi/index.d.ts +++ b/napi/canvas-napi/index.d.ts @@ -41,9 +41,28 @@ export declare class ImageData { get height(): number; get data(): Buffer; } +export declare class TextMetrics { + get width(): number; + get actualBoundingBoxLeft(): number; + get actualBoundingBoxRight(): number; + get actualBoundingBoxAscent(): number; + get actualBoundingBoxDescent(): number; + get fontBoundingBoxAscent(): number; + get fontBoundingBoxDescent(): number; + get emHeightAscent(): number; + get emHeightDescent(): number; + get hangingBaseline(): number; + get alphabeticBaseline(): number; + get ideographicBaseline(): number; +} export declare class CanvasRenderingContext2D { static withView(view: number, width: number, height: number, density: number, alpha: boolean, fontColor: number, ppi: number, direction: number): CanvasRenderingContext2D; + static withMtlViewDeviceQueue(view: number, device: number, queue: number, alpha: boolean, density: number, samples: number, fontColor: number, ppi: number, direction: number): CanvasRenderingContext2D; + present(): void; + flush(): void; + render(): void; static withCpu(width: number, height: number, density: number, alpha: boolean, fontColor: number, ppi: number, direction: number): CanvasRenderingContext2D; + toDataURL(format: string, encoderOptions?: number | undefined | null): string; get direction(): string; set direction(direction: string); get fillStyle(): unknown; @@ -109,6 +128,7 @@ export declare class CanvasRenderingContext2D { createPattern(image: ImageAsset, repetition?: string | undefined | null): CanvasPattern; createRadialGradient(x0: number, y0: number, r0: number, x1: number, y1: number, r1: number): CanvasGradient; drawFocusIfNeeded(elementPath: object | Path2D, element?: object | undefined | null): void; + drawImage(image: ImageAsset, sx?: number | undefined | null, sy?: number | undefined | null, sWidth?: number | undefined | null, sHeight?: number | undefined | null, dx?: number | undefined | null, dy?: number | undefined | null, dWidth?: number | undefined | null, dHeight?: number | undefined | null): void; ellipse(x: number, y: number, radiusX: number, radiusY: number, rotation: number, startAngle: number, endAngle: number, anticlockwise?: boolean | undefined | null): void; fill(path?: Path2D | undefined | null, rule?: string | undefined | null): void; fillRect(x: number, y: number, width: number, height: number): void; @@ -116,12 +136,17 @@ export declare class CanvasRenderingContext2D { getImageData(x: number, y: number, width: number, height: number): ImageData; getLineDash(): Array; getTransform(): DOMMatrix; + isContextLost(): boolean; + isPointInPath(xOrPath: number | Path2D, y?: number | undefined | null, ruleOrY?: number | string | undefined | null, rule?: string | undefined | null): boolean; + isPointInStroke(xOrPath: number | Path2D, xOrY?: number | undefined | null, y?: number | undefined | null): boolean; lineTo(x: number, y: number): void; + measureText(text: string): TextMetrics; moveTo(x: number, y: number): void; - render(): void; - toDataURL(format: string, encoderOptions?: number | undefined | null): string; - drawImage(image: ImageAsset, sx?: number | undefined | null, sy?: number | undefined | null, sWidth?: number | undefined | null, sHeight?: number | undefined | null, dx?: number | undefined | null, dy?: number | undefined | null, dWidth?: number | undefined | null, dHeight?: number | undefined | null): void; + putImageData(imageData: ImageData, dx: number, dy: number, dirtyX?: number | undefined | null, dirtyY?: number | undefined | null, dirtyWidth?: number | undefined | null, dirtyHeight?: number | undefined | null): void; + quadraticCurveTo(cpx: number, cpy: number, x: number, y: number): void; rect(x: number, y: number, width: number, height: number): void; + reset(): void; + resetTransform(): void; restore(): void; rotate(angle: number): void; roundRect(x: number, y: number, width: number, height: number, radii: number | Array): void; diff --git a/napi/canvas-napi/index.js b/napi/canvas-napi/index.js index c73bb0a4..1e31e3ac 100644 --- a/napi/canvas-napi/index.js +++ b/napi/canvas-napi/index.js @@ -284,13 +284,14 @@ if (!nativeBinding) { throw new Error(`Failed to load native binding`); } -const { WebGLRenderingContext, Path2D, CanvasPattern, CanvasGradient, ImageData, CanvasRenderingContext2D, ImageAsset, DomMatrix } = nativeBinding; +const { WebGLRenderingContext, Path2D, CanvasPattern, CanvasGradient, ImageData, TextMetrics, CanvasRenderingContext2D, ImageAsset, DomMatrix } = nativeBinding; module.exports.WebGLRenderingContext = WebGLRenderingContext; module.exports.Path2D = Path2D; module.exports.CanvasPattern = CanvasPattern; module.exports.CanvasGradient = CanvasGradient; module.exports.ImageData = ImageData; +module.exports.TextMetrics = TextMetrics; module.exports.CanvasRenderingContext2D = CanvasRenderingContext2D; module.exports.ImageAsset = ImageAsset; module.exports.DomMatrix = DomMatrix; diff --git a/napi/canvas-napi/main.mjs b/napi/canvas-napi/main.mjs index 2d588da7..3440699d 100644 --- a/napi/canvas-napi/main.mjs +++ b/napi/canvas-napi/main.mjs @@ -55,6 +55,58 @@ export class NSCMTLView extends NSView { static { NativeClass(this); } + _device; + _queue; + _canvas; + + get queue() { + return this._queue; + } + + get device() { + return this._device; + } + + initWithFrame(frameRect) { + super.initWithFrame(frameRect); + this.wantsLayer = true; + const layer = CAMetalLayer.layer(); + this._device = MTLCreateSystemDefaultDevice(); + this._queue = this._device.newCommandQueue(); + layer.device = this._device; + layer.presentsWithTransaction = false; + layer.framebufferOnly = false; + layer.pixelFormat = MTLPixelFormat.BGRA8Unorm; + + this.layer = layer; + return this; + } + + + /** + * @return {CGSize} + */ + get drawableSize() { + return this.layer.drawableSize; + } + + /** + * @param {CGSize} value + */ + set drawableSize(value) { + this.layer.drawableSize = value; + } + + + present() { + this._canvas?.getContext('2d').flush(); + this._canvas?.getContext('2d').present(); + } + + static ObjCExposedMethods = { + present: { returns: interop.types.void, params: [] } + }; + } export class NSCCanvas extends NSView { @@ -89,6 +141,7 @@ export class CanvasGLView extends NSOpenGLView { } } + export class ViewController extends NSViewController { static { NativeClass(this); @@ -105,15 +158,25 @@ export class ViewController extends NSViewController { this.view.addSubview(this.canvas); glview.frame = this.view.frame; + mtlview.frame = this.view.frame; + mtlview.drawableSize = new CGSize({ + width: this.view.frame.size.width * NSScreen.mainScreen.backingScaleFactor, + height: this.view.frame.size.height * NSScreen.mainScreen.backingScaleFactor + }); + + this.canvas.addSubview(mtlview); - this.canvas.addSubview(glview); + //this.canvas.addSubview(glview); - glview.wantsLayer = true; + // glview.wantsLayer = true; } } const glview = CanvasGLView.alloc().initWithFrame({ x: 0, y: 0, width: 0, height: 0 }); +const mtlview = NSCMTLView.alloc().initWithFrame({ x: 0, y: 0, width: 0, height: 0 }); + + let isDoingOrDone = false; function mdnShadowColor(ctx) { @@ -399,8 +462,8 @@ function flappyBird(canvas) { } function main() { - width = canvas.clientWidth * window.devicePixelRatio; - height = canvas.clientHeight * window.devicePixelRatio; + width = canvas.clientWidth //* window.devicePixelRatio; + height = canvas.clientHeight // * window.devicePixelRatio; canvas.addEventListener('touchstart', onpress); @@ -981,6 +1044,47 @@ function mdnPattern(canvas) { ctx.fillRect(0, 0, canvas.width, canvas.height); } +function mdnPutImageData(canvas) { + const ctx = canvas.getContext('2d'); + + function putImageData( + ctx, + imageData, + dx, + dy, + dirtyX, + dirtyY, + dirtyWidth, + dirtyHeight + ) { + const data = imageData.data; + const height = imageData.height; + const width = imageData.width; + dirtyX = dirtyX || 0; + dirtyY = dirtyY || 0; + dirtyWidth = dirtyWidth !== undefined ? dirtyWidth : width; + dirtyHeight = dirtyHeight !== undefined ? dirtyHeight : height; + const limitBottom = dirtyY + dirtyHeight; + const limitRight = dirtyX + dirtyWidth; + for (let y = dirtyY; y < limitBottom; y++) { + for (let x = dirtyX; x < limitRight; x++) { + const pos = y * width + x; + ctx.fillStyle = `rgb(${data[pos * 4 + 0]} ${data[pos * 4 + 1]} + ${data[pos * 4 + 2]} / ${data[pos * 4 + 3] / 255})`; + ctx.fillRect(x + dx, y + dy, 1, 1); + } + } + } + +// Draw content onto the canvas + ctx.fillRect(0, 0, 100, 100); +// Create an ImageData object from it + const imagedata = ctx.getImageData(0, 0, 100, 100); +// use the putImageData function that illustrates how putImageData works + putImageData(ctx, imagedata, 150, 0, 50, 50, 25, 25); + ctx.render(); +} + function mdnRadialGradient(canvas) { const ctx = canvas.getContext('2d'); @@ -1028,7 +1132,31 @@ function doTheThing() { // const glContext = WebGLRenderingContext.withView(handle.toNumber(), 1, true, false, false, false, 1, true, false, false, false, false, false); // console.log(glContext, 'drawingBufferWidth', glContext.drawingBufferWidth, 'drawingBufferHeight', glContext.drawingBufferHeight); - const ctx = CanvasRenderingContext2D.withView(handle.toNumber(), glview.frame.size.width * scale, glview.frame.size.height * scale, 1, true, 0, 90, 1); + //const ctx = CanvasRenderingContext2D.withView(handle.toNumber(), glview.frame.size.width * scale, glview.frame.size.height * scale, 1, true, 0, 90, 1); + + + const mtlViewHandle = interop.handleof(mtlview); + const deviceHandle = interop.handleof(mtlview.device); + const queueHandle = interop.handleof(mtlview.queue); + const ctx = CanvasRenderingContext2D.withMtlViewDeviceQueue( + mtlViewHandle.toNumber(), deviceHandle.toNumber(), queueHandle.toNumber(), true, scale, 1, 0, 90, 1 + ); + + const mtlCanvas = { + width: mtlview.frame.size.width, + height: mtlview.frame.size.height, + clientWidth: mtlview.frame.size.width, + clientHeight: mtlview.frame.size.height, + addEventListener: function() { + }, + getContext: function() { + return ctx; + }, + style: {} + }; + + mtlview._canvas = mtlCanvas; + ctx.fillStyle = 'white'; ctx.fillRect(0, 0, 1000, 1000); @@ -1056,16 +1184,22 @@ function doTheThing() { style: {} }; -// flappyBird(canvas); + //flappyBird(mtlCanvas); - solarSystem(canvas); + solarSystem(canvas); // swarm(canvas); - //breathe_demo(canvas); + // breathe_demo(canvas); //mdnPattern(canvas); - //mdnRadialGradient(canvas); + // mdnRadialGradient(canvas); + //mdnPutImageData(canvas); + + // + // ctx.font = '50px serif'; + // ctx.strokeText('Hello world', 50, 90); + -// ctx.render(); + // ctx.render(); /* diff --git a/napi/canvas-napi/src/c2d/mod.rs b/napi/canvas-napi/src/c2d/mod.rs index c5cd812e..f0d65688 100644 --- a/napi/canvas-napi/src/c2d/mod.rs +++ b/napi/canvas-napi/src/c2d/mod.rs @@ -2,11 +2,13 @@ pub mod path; mod pattern; mod gradient; mod image_data; +mod text_metrics; use crate::c2d::gradient::CanvasGradient; use crate::c2d::image_data::ImageData; use crate::c2d::path::Path2D; use crate::c2d::pattern::CanvasPattern; +use crate::c2d::text_metrics::TextMetrics; use crate::dom_matrix::DOMMatrix; use crate::image_asset::ImageAsset; use canvas_2d::context::compositing::composite_operation_type::CompositeOperationType; @@ -66,6 +68,59 @@ impl CanvasRenderingContext2D { } + #[napi(factory)] + #[cfg(any(target_os = "macos", target_os = "ios"))] + pub fn with_mtl_view_device_queue( + view: i64, + device: i64, + queue: i64, + alpha: bool, + density: f64, + samples: u32, + font_color: i32, + ppi: f64, + direction: i32, + ) -> Self { + let ctx_2d = canvas_c::CanvasRenderingContext2D::new_metal( + canvas_2d::context::Context::new_metal_device_queue( + view as _, + device as _, + queue as _, + density as f32, + samples as _, + alpha, + font_color, + ppi as f32, + canvas_2d::context::text_styles::text_direction::TextDirection::from(direction as u32), + ), + alpha, + ); + + CanvasRenderingContext2D { + context: Box::into_raw( + Box::new(ctx_2d) as _, + ), + } + } + + #[napi] + pub fn present(&self) { + let context = unsafe { &mut *self.context }; + canvas_2d::context::Context::present(context.get_context_mut()); + } + + #[napi] + pub fn flush(&self) { + canvas_c::canvas_native_context_flush(self.context); + } + + + #[napi] + pub fn render(&self) { + canvas_c::canvas_native_context_render(self.context); + } + + #[napi(factory)] pub fn with_cpu( width: f64, @@ -89,6 +144,19 @@ impl CanvasRenderingContext2D { } } + #[napi(js_name = "toDataURL")] + pub fn to_data_url(&self, format: String, encoderOptions: Option) -> String { + let c_str = CString::new(format).unwrap(); + let quality = encoderOptions + .map(|v| v as f32) + .unwrap_or(0.92) + .try_into() + .unwrap_or(0.92); + let quality: u32 = (quality * 100.) as u32; + let ret = canvas_c::canvas_native_to_data_url(self.context, c_str.as_ptr(), quality); + unsafe { CString::from_raw(ret as _).to_string_lossy().to_string() } + } + #[napi(getter)] pub fn direction(&self) -> &str { @@ -770,6 +838,33 @@ impl CanvasRenderingContext2D { #[napi] pub fn draw_focus_if_needed(&self, element_path: Either>, element: Option) {} + + #[napi] + pub fn draw_image(&self, image: ClassInstance, sx: Option, sy: Option, s_width: Option, s_height: Option, + dx: Option, dy: Option, d_width: Option, d_height: Option, + ) { + let image = Arc::as_ptr(&image.asset); + match (sx, sy, s_width, s_height, dx, dy, d_height, d_width) { + (Some(dx), Some(dy), None, None, None, None, None, None) => { + canvas_c::canvas_native_context_draw_image_dx_dy_asset( + self.context, image as _, dx as f32, dy as f32, + ); + } + (Some(dx), Some(dy), Some(d_width), Some(d_height), None, None, None, None) => { + canvas_c::canvas_native_context_draw_image_dx_dy_dw_dh_asset( + self.context, image as _, dx as f32, dy as f32, d_width as f32, d_height as f32, + ); + } + (Some(sx), Some(sy), Some(s_width), Some(s_height), Some(dx), Some(dy), Some(d_width), Some(d_height)) => { + canvas_c::canvas_native_context_draw_image_asset( + self.context, image as _, sx as f32, sy as f32, s_width as f32, s_height as f32, + dx as f32, dy as f32, d_width as f32, d_height as f32, + ); + } + _ => {} + } + } + #[napi] pub fn ellipse(&self, x: f64, @@ -882,69 +977,166 @@ impl CanvasRenderingContext2D { } #[napi] - pub fn is_context_lost(&self) -> bool{ + pub fn is_context_lost(&self) -> bool { // todo false } - #[napi] - pub fn line_to(&self, x: f64, y: f64) { - canvas_c::canvas_native_context_line_to(self.context, x as f32, y as f32) + pub fn is_point_in_path(&self, x_or_path: Either>, y: Option, rule_or_y: Option>, rule: Option) -> bool { + let mut x_inner = None; + let mut y_inner = None; + let mut rule_inner = CanvasFillRule::NonZero; + match x_or_path { + Either::A(x) => { + x_inner = Some(x as f32); + if let Some(y) = y { + y_inner = Some(y as f32); + + return if let Some(rule_or_y) = rule_or_y { + match rule_or_y { + Either::A(y) => { + // return early + canvas_c::canvas_native_context_is_point_in_path( + self.context, x as f32, y as f32, rule_inner, + ) + } + Either::B(rule) => { + if let Some(rule) = rule.into_utf8().ok() { + if let Ok(rule) = rule.as_str() { + match rule { + "evenodd" => { + rule_inner = CanvasFillRule::EvenOdd; + } + _ => {} + } + } + } + + canvas_c::canvas_native_context_is_point_in_path( + self.context, x as f32, y as f32, rule_inner, + ) + } + } + } else { + canvas_c::canvas_native_context_is_point_in_path( + self.context, x as f32, y as f32, rule_inner, + ) + }; + } + false + } + Either::B(path) => { + match (y, rule_or_y, rule) { + (Some(x), Some(rule_or_y), Some(rule)) => { + match rule_or_y { + Either::A(y) => { + if let Some(rule) = rule.into_utf8().ok() { + if let Ok(rule) = rule.as_str() { + match rule { + "evenodd" => { + rule_inner = CanvasFillRule::EvenOdd; + } + _ => {} + } + } + } + + canvas_c::canvas_native_context_is_point_in_path_with_path( + self.context, path.path, x as f32, y as f32, rule_inner, + ) + } + Either::B(_) => { + false + } + } + } + (Some(x), Some(rule_or_y), _) => { + match rule_or_y { + Either::A(y) => { + canvas_c::canvas_native_context_is_point_in_path( + self.context, x as f32, y as f32, rule_inner, + ) + } + Either::B(_) => { + false + } + } + } + _ => false + } + } + } } + #[napi] - pub fn move_to(&self, x: f64, y: f64) { - canvas_c::canvas_native_context_move_to(self.context, x as f32, y as f32) + pub fn is_point_in_stroke(&self, x_or_path: Either>, x_or_y: Option, y: Option) -> bool { + match x_or_path { + Either::A(x) => { + if let Some(y) = x_or_y { + return canvas_c::canvas_native_context_is_point_in_stroke( + self.context, x as f32, y as f32, + ); + } + false + } + Either::B(path) => { + match (x_or_y, y) { + (Some(x), Some(y)) => { + canvas_c::canvas_native_context_is_point_in_stroke_with_path( + self.context, path.path, x as f32, y as f32, + ) + } + _ => false + } + } + } } #[napi] - pub fn render(&self) { - canvas_c::canvas_native_context_flush(self.context); - canvas_c::canvas_native_context_render(self.context); + pub fn line_to(&self, x: f64, y: f64) { + canvas_c::canvas_native_context_line_to(self.context, x as f32, y as f32) } - #[napi(js_name = "toDataURL")] - pub fn to_data_url(&self, format: String, encoderOptions: Option) -> String { - let c_str = CString::new(format).unwrap(); - let quality = encoderOptions - .map(|v| v as f32) - .unwrap_or(0.92) - .try_into() - .unwrap_or(0.92); - let quality: u32 = (quality * 100.) as u32; - let ret = canvas_c::canvas_native_to_data_url(self.context, c_str.as_ptr(), quality); - unsafe { CString::from_raw(ret as _).to_string_lossy().to_string() } + #[napi] + pub fn measure_text(&self, env: Env, text: String) -> Result> { + let text = CString::new(text).unwrap(); + TextMetrics { + metrics: canvas_c::canvas_native_context_measure_text(self.context, text.as_ptr()) + }.into_instance(env) } #[napi] - pub fn draw_image(&self, image: ClassInstance, sx: Option, sy: Option, s_width: Option, s_height: Option, - dx: Option, dy: Option, d_width: Option, d_height: Option, - ) { - let image = Arc::as_ptr(&image.asset); - match (sx, sy, s_width, s_height, dx, dy, d_height, d_width) { - (Some(dx), Some(dy), None, None, None, None, None, None) => { - canvas_c::canvas_native_context_draw_image_dx_dy_asset( - self.context, image as _, dx as f32, dy as f32, - ); - } - (Some(dx), Some(dy), Some(d_width), Some(d_height), None, None, None, None) => { - canvas_c::canvas_native_context_draw_image_dx_dy_dw_dh_asset( - self.context, image as _, dx as f32, dy as f32, d_width as f32, d_height as f32, - ); + pub fn move_to(&self, x: f64, y: f64) { + canvas_c::canvas_native_context_move_to(self.context, x as f32, y as f32) + } + + #[napi] + pub fn put_image_data(&self, image_data: ClassInstance, dx: f64, dy: f64, dirty_x: Option, dirty_y: Option, dirty_width: Option, dirty_height: Option) { + match (dirty_x, dirty_y, dirty_width, dirty_height) { + (Some(x), Some(y), Some(width), Some(height)) => { + canvas_c::canvas_native_context_put_image_data( + self.context, image_data.data, dx as f32, dy as f32, x as f32, y as f32, width as f32, height as f32, + ) } - (Some(sx), Some(sy), Some(s_width), Some(s_height), Some(dx), Some(dy), Some(d_width), Some(d_height)) => { - canvas_c::canvas_native_context_draw_image_asset( - self.context, image as _, sx as f32, sy as f32, s_width as f32, s_height as f32, - dx as f32, dy as f32, d_width as f32, d_height as f32, - ); + _ => { + canvas_c::canvas_native_context_put_image_data( + self.context, image_data.data, dx as f32, dy as f32, 0., 0., image_data.width_inner() as f32, image_data.height_inner() as f32, + ) } - _ => {} } } + #[napi] + pub fn quadratic_curve_to(&self, cpx: f64, cpy: f64, x: f64, y: f64) { + canvas_c::canvas_native_context_quadratic_curve_to( + self.context, cpx as f32, cpy as f32, x as f32, y as f32) + } + + #[napi] pub fn rect(&self, x: f64, y: f64, width: f64, height: f64) { canvas_c::canvas_native_context_rect( @@ -956,12 +1148,22 @@ impl CanvasRenderingContext2D { ) } + #[napi] + pub fn reset(&self) { + canvas_c::canvas_native_context_reset(self.context); + } + + #[napi] + pub fn reset_transform(&self) { + canvas_c::canvas_native_context_reset_transform(self.context); + } + + #[napi] pub fn restore(&self) { canvas_c::canvas_native_context_restore(self.context); } - #[napi] pub fn rotate(&self, angle: f64) { canvas_c::canvas_native_context_rotate(self.context, angle as f32); diff --git a/napi/canvas-napi/src/c2d/text_metrics.rs b/napi/canvas-napi/src/c2d/text_metrics.rs new file mode 100644 index 00000000..1f96e698 --- /dev/null +++ b/napi/canvas-napi/src/c2d/text_metrics.rs @@ -0,0 +1,103 @@ +use canvas_c::TextMetrics as CTextMetrics; + +use napi::bindgen_prelude::ObjectFinalize; +use napi::*; +use napi_derive::napi; + +#[napi(custom_finalize)] +pub struct TextMetrics { + pub(crate) metrics: *mut CTextMetrics, +} + +impl ObjectFinalize for TextMetrics { + fn finalize(self, _: Env) -> Result<()> { + canvas_c::canvas_native_text_metrics_release(self.metrics); + Ok(()) + } +} + + +#[napi] +impl TextMetrics { + #[napi(getter)] + pub fn get_width(&self) -> f64 { + canvas_c::canvas_native_text_metrics_get_width(self.metrics) as f64 + } + + #[napi(getter)] + pub fn get_actual_bounding_box_left( + &self + ) -> f64 { + canvas_c::canvas_native_text_metrics_get_actual_bounding_box_left(self.metrics) as f64 + } + + #[napi(getter)] + pub fn get_actual_bounding_box_right( + &self + ) -> f64 { + canvas_c::canvas_native_text_metrics_get_actual_bounding_box_right(self.metrics) as f64 + } + + #[napi(getter)] + pub fn get_actual_bounding_box_ascent( + &self + ) -> f64 { + canvas_c::canvas_native_text_metrics_get_actual_bounding_box_ascent(self.metrics) as f64 + } + + #[napi(getter)] + pub fn get_actual_bounding_box_descent( + &self + ) -> f64 { + canvas_c::canvas_native_text_metrics_get_actual_bounding_box_descent(self.metrics) as f64 + } + + #[napi(getter)] + pub fn get_font_bounding_box_ascent( + &self + ) -> f64 { + canvas_c::canvas_native_text_metrics_get_font_bounding_box_ascent(self.metrics) as f64 + } + + #[napi(getter)] + pub fn get_font_bounding_box_descent( + &self + ) -> f64 { + canvas_c::canvas_native_text_metrics_get_font_bounding_box_descent(self.metrics) as f64 + } + + #[napi(getter)] + pub fn get_em_height_ascent( + &self + ) -> f64 { + canvas_c::canvas_native_text_metrics_get_em_height_ascent(self.metrics) as f64 + } + + #[napi(getter)] + pub fn get_em_height_descent( + &self + ) -> f64 { + canvas_c::canvas_native_text_metrics_get_em_height_descent(self.metrics) as f64 + } + + #[napi(getter)] + pub fn get_hanging_baseline( + &self + ) -> f64 { + canvas_c::canvas_native_text_metrics_get_hanging_baseline(self.metrics) as f64 + } + + #[napi(getter)] + pub fn get_alphabetic_baseline( + &self + ) -> f64 { + canvas_c::canvas_native_text_metrics_get_alphabetic_baseline(self.metrics) as f64 + } + + #[napi(getter)] + pub fn get_ideographic_baseline( + &self + ) -> f64 { + canvas_c::canvas_native_text_metrics_get_ideographic_baseline(self.metrics) as f64 + } +} diff --git a/packages/canvas/src-native/canvas-ios/CanvasNative.xcodeproj/project.xcworkspace/xcuserdata/triniwiz.xcuserdatad/UserInterfaceState.xcuserstate b/packages/canvas/src-native/canvas-ios/CanvasNative.xcodeproj/project.xcworkspace/xcuserdata/triniwiz.xcuserdatad/UserInterfaceState.xcuserstate index f778f667..0d7c4369 100644 Binary files a/packages/canvas/src-native/canvas-ios/CanvasNative.xcodeproj/project.xcworkspace/xcuserdata/triniwiz.xcuserdatad/UserInterfaceState.xcuserstate and b/packages/canvas/src-native/canvas-ios/CanvasNative.xcodeproj/project.xcworkspace/xcuserdata/triniwiz.xcuserdatad/UserInterfaceState.xcuserstate differ