From a4a8104cf776d4ec5e31858fb24a0251a4a88c3a Mon Sep 17 00:00:00 2001 From: Osei Fortune Date: Thu, 12 Dec 2024 16:22:20 -0400 Subject: [PATCH] chore: updates --- .../canvas-c/src/webgpu/gpu_canvas_context.rs | 26 +- crates/canvas-c/src/webgpu/gpu_queue.rs | 148 +++++-- crates/canvas-c/src/webgpu/structs.rs | 20 + napi/canvas-napi/NSCTouchHandler.ts | 250 ++++++++++++ napi/canvas-napi/canvas.ts | 374 +++++++++++++++++- napi/canvas-napi/examples/node/main.mts | 31 +- .../examples/node/touchParticles.ts | 184 +++++++++ napi/canvas-napi/index.d.ts | 2 +- napi/canvas-napi/index.js | 36 +- napi/canvas-napi/package.json | 2 +- napi/canvas-napi/src/gl2/mod.rs | 2 +- napi/canvas-napi/src/gpu/buffer.rs | 2 +- napi/canvas-napi/src/gpu/context.rs | 2 +- napi/canvas-napi/src/gpu/queue.rs | 116 ++++-- napi/canvas-napi/tsconfig.json | 2 +- 15 files changed, 1078 insertions(+), 119 deletions(-) create mode 100644 napi/canvas-napi/NSCTouchHandler.ts create mode 100644 napi/canvas-napi/examples/node/touchParticles.ts diff --git a/crates/canvas-c/src/webgpu/gpu_canvas_context.rs b/crates/canvas-c/src/webgpu/gpu_canvas_context.rs index c753f010..7283c13a 100644 --- a/crates/canvas-c/src/webgpu/gpu_canvas_context.rs +++ b/crates/canvas-c/src/webgpu/gpu_canvas_context.rs @@ -21,21 +21,21 @@ use super::{ //use wgpu_core::gfx_select; #[derive(Copy, Clone, Debug)] -struct TextureData { - usage: wgt::TextureUsages, - dimension: wgt::TextureDimension, - size: wgt::Extent3d, - format: wgt::TextureFormat, - mip_level_count: u32, - sample_count: u32, +pub struct TextureData { + pub usage: wgt::TextureUsages, + pub dimension: wgt::TextureDimension, + pub size: wgt::Extent3d, + pub format: wgt::TextureFormat, + pub mip_level_count: u32, + pub sample_count: u32, } #[derive(Debug)] pub struct SurfaceData { - device: Arc, - error_sink: ErrorSink, - texture_data: TextureData, - previous_configuration: wgt::SurfaceConfiguration>, + pub(crate) device: Arc, + pub(crate) error_sink: ErrorSink, + pub texture_data: TextureData, + pub(crate) previous_configuration: wgt::SurfaceConfiguration>, } #[derive(Copy, Clone, Debug)] @@ -46,8 +46,8 @@ pub struct ViewData { #[derive(Copy, Clone, Debug)] pub struct ReadBackTexture { - texture: wgpu_core::id::TextureId, - data: TextureData, + pub(crate) texture: wgpu_core::id::TextureId, + pub(crate) data: TextureData, } pub struct CanvasGPUCanvasContext { diff --git a/crates/canvas-c/src/webgpu/gpu_queue.rs b/crates/canvas-c/src/webgpu/gpu_queue.rs index 4949f687..85a44128 100644 --- a/crates/canvas-c/src/webgpu/gpu_queue.rs +++ b/crates/canvas-c/src/webgpu/gpu_queue.rs @@ -9,7 +9,10 @@ use crate::webgpu::enums::CanvasGPUTextureFormat; //use wgpu_core::gfx_select; use crate::webgpu::error::{handle_error, handle_error_fatal}; use crate::webgpu::prelude::label_to_ptr; -use crate::webgpu::structs::{CanvasImageCopyCanvasRenderingContext2D, CanvasImageCopyImageAsset, CanvasImageCopyWebGL}; +use crate::webgpu::structs::{ + CanvasImageCopyCanvasRenderingContext2D, CanvasImageCopyGPUContext, CanvasImageCopyImageAsset, + CanvasImageCopyWebGL, +}; use canvas_webgl::utils::gl::bytes_per_pixel; use std::borrow::Cow; use std::os::raw::{c_char, c_void}; @@ -40,7 +43,9 @@ pub struct CanvasGPUQueue { unsafe impl Send for CanvasGPUQueue {} #[no_mangle] -pub unsafe extern "C" fn canvas_native_webgpu_queue_get_label(queue: *const CanvasGPUQueue) -> *mut c_char { +pub unsafe extern "C" fn canvas_native_webgpu_queue_get_label( + queue: *const CanvasGPUQueue, +) -> *mut c_char { if queue.is_null() { return std::ptr::null_mut(); } @@ -89,7 +94,6 @@ fn get_offset_image( break; } - let src_start = (src_y * img_width + x_offset) * bytes_per_pixel; let src_end = src_start + width * bytes_per_pixel; @@ -99,8 +103,7 @@ fn get_offset_image( let dst_start = row * width * bytes_per_pixel; let copy_len = src_row_data.len().min(width * bytes_per_pixel); - result[dst_start..dst_start + copy_len] - .copy_from_slice(&src_row_data[0..copy_len]); + result[dst_start..dst_start + copy_len].copy_from_slice(&src_row_data[0..copy_len]); } result @@ -123,12 +126,10 @@ pub unsafe extern "C" fn canvas_native_webgpu_queue_copy_webgl_to_texture( } let webgl = &*source.source; - webgl.0.make_current(); let width = webgl.0.get_drawing_buffer_width(); let height = webgl.0.get_drawing_buffer_height(); - let row_size = bytes_per_pixel(gl_bindings::RGBA as u32, gl_bindings::RGBA as u32) as i32; let mut bytes = vec![0u8; (width * height * row_size) as usize]; @@ -156,7 +157,6 @@ pub unsafe extern "C" fn canvas_native_webgpu_queue_copy_webgl_to_texture( } } - let ext_source = CanvasImageCopyExternalImage { source: bytes.as_ptr(), source_size: bytes.len(), @@ -166,7 +166,12 @@ pub unsafe extern "C" fn canvas_native_webgpu_queue_copy_webgl_to_texture( height: height as u32, }; - canvas_native_webgpu_queue_copy_external_image_to_texture(queue, &ext_source, destination, size); + canvas_native_webgpu_queue_copy_external_image_to_texture( + queue, + &ext_source, + destination, + size, + ); } #[no_mangle] @@ -180,7 +185,6 @@ pub unsafe extern "C" fn canvas_native_webgpu_queue_copy_context_to_texture( return; } - let source = &*source; let destination = &*destination; @@ -191,7 +195,6 @@ pub unsafe extern "C" fn canvas_native_webgpu_queue_copy_context_to_texture( let (width, height) = context.context.dimensions(); - let queue = &*queue; let queue_id = queue.queue.id; @@ -203,10 +206,17 @@ pub unsafe extern "C" fn canvas_native_webgpu_queue_copy_context_to_texture( match destination_texture.format { CanvasGPUTextureFormat::Bgra8Unorm | CanvasGPUTextureFormat::Bgra8UnormSrgb => { - context.context.get_pixels_format(data.as_mut_slice(), (0, 0), (width as i32, height as i32), canvas_2d::context::ColorType::BGRA8888); + context.context.get_pixels_format( + data.as_mut_slice(), + (0, 0), + (width as i32, height as i32), + canvas_2d::context::ColorType::BGRA8888, + ); } _ => { - context.context.get_pixels(data.as_mut_slice(), (0, 0), (width as i32, height as i32)); + context + .context + .get_pixels(data.as_mut_slice(), (0, 0), (width as i32, height as i32)); } } @@ -267,7 +277,6 @@ pub unsafe extern "C" fn canvas_native_webgpu_queue_copy_context_to_texture( } } - #[no_mangle] pub unsafe extern "C" fn canvas_native_webgpu_queue_copy_image_asset_to_texture( queue: *const CanvasGPUQueue, @@ -300,14 +309,26 @@ pub unsafe extern "C" fn canvas_native_webgpu_queue_copy_image_asset_to_texture( match texture.format { CanvasGPUTextureFormat::Bgra8Unorm | CanvasGPUTextureFormat::Bgra8UnormSrgb => { let mut bytes = bytes.to_vec(); - canvas_core::image_asset::ImageAsset::rgba_to_bgra_in_place(bytes.as_mut_slice()); + canvas_core::image_asset::ImageAsset::rgba_to_bgra_in_place( + bytes.as_mut_slice(), + ); ext_source.source = bytes.as_ptr(); - canvas_native_webgpu_queue_copy_external_image_to_texture(queue, &ext_source, destination, size); + canvas_native_webgpu_queue_copy_external_image_to_texture( + queue, + &ext_source, + destination, + size, + ); } _ => { - canvas_native_webgpu_queue_copy_external_image_to_texture(queue, &ext_source, destination, size); + canvas_native_webgpu_queue_copy_external_image_to_texture( + queue, + &ext_source, + destination, + size, + ); } } } @@ -394,6 +415,90 @@ pub unsafe extern "C" fn canvas_native_webgpu_queue_copy_external_image_to_textu } } +#[no_mangle] +pub unsafe extern "C" fn canvas_native_webgpu_queue_copy_gpu_context_to_texture( + queue: *const CanvasGPUQueue, + source: *const CanvasImageCopyGPUContext, + destination: *const CanvasImageCopyTexture, + size: *const CanvasExtent3d, +) { + if queue.is_null() || source.is_null() || destination.is_null() || size.is_null() { + return; + } + + let source = &*source; + let destination = &*destination; + + if source.source.is_null() || destination.texture.is_null() { + return; + } + let context = + &mut *(source.source as *mut crate::webgpu::gpu_canvas_context::CanvasGPUCanvasContext); + + let mut texture = None; + if let Some(current_texture) = context.current_texture.lock().as_ref() { + texture = Some(current_texture.texture); + } else if let Some(read_back_texture) = context.read_back_texture.lock().as_ref() { + texture = Some(read_back_texture.texture); + } + + if let Some(texture) = texture { + let queue = &*queue; + + let global = queue.queue.instance.global(); + + if let Some(data) = context.data.lock().as_ref() { + let label = Cow::Borrowed("copyExternalImageToTexture:Encoder"); + let (encoder, error) = global.device_create_command_encoder( + data.device.device, + &wgt::CommandEncoderDescriptor { + label: wgpu_core::Label::from(label), + }, + None, + ); + if let Some(error) = error { + // todo log error + return; + } + + let source = wgt::TexelCopyTextureInfo { + texture, + mip_level: data.texture_data.mip_level_count, + origin: wgt::Origin3d { + x: source.origin.x, + y: source.origin.y, + z: 0, + }, + aspect: Default::default(), + }; + let destination_texture = unsafe { &*destination.texture }; + let dest = wgt::TexelCopyTextureInfo { + texture: destination_texture.texture, + mip_level: destination.mip_level, + origin: destination.origin.into(), + aspect: destination.aspect.into(), + }; + + let size = *size; + + let size: wgt::Extent3d = size.into(); + + if let Err(cause) = + global.command_encoder_copy_texture_to_texture(encoder, &source, &dest, &size) + { + handle_error( + global, + queue.error_sink.as_ref(), + cause, + "", + None, + "canvas_native_webgpu_queue_copy_gpu_context_to_texture", + ); + } + } + } +} + #[no_mangle] pub unsafe extern "C" fn canvas_native_webgpu_queue_on_submitted_work_done( queue: *const CanvasGPUQueue, @@ -447,8 +552,7 @@ pub unsafe extern "C" fn canvas_native_webgpu_queue_submit( }) .collect::>(); - if let Err((_, cause)) = global.queue_submit(queue_id, &command_buffer_ids) - { + if let Err((_, cause)) = global.queue_submit(queue_id, &command_buffer_ids) { handle_error_fatal(global, cause, "canvas_native_webgpu_queue_submit"); } @@ -488,8 +592,7 @@ pub unsafe extern "C" fn canvas_native_webgpu_queue_write_buffer( None => &data[data_offset..], }; - if let Err(cause) = global.queue_write_buffer(queue_id, buffer_id, buffer_offset, data) - { + if let Err(cause) = global.queue_write_buffer(queue_id, buffer_id, buffer_offset, data) { handle_error( global, queue.error_sink.as_ref(), @@ -541,7 +644,8 @@ pub unsafe extern "C" fn canvas_native_webgpu_queue_write_texture( let size: wgt::Extent3d = size.into(); - if let Err(cause) = global.queue_write_texture(queue_id, &destination, data, &data_layout, &size) + if let Err(cause) = + global.queue_write_texture(queue_id, &destination, data, &data_layout, &size) { handle_error( global, diff --git a/crates/canvas-c/src/webgpu/structs.rs b/crates/canvas-c/src/webgpu/structs.rs index d6bd5eec..620e2a26 100644 --- a/crates/canvas-c/src/webgpu/structs.rs +++ b/crates/canvas-c/src/webgpu/structs.rs @@ -228,6 +228,26 @@ pub struct CanvasImageCopyExternalImage { pub height: u32, } +#[repr(C)] +#[derive(Debug)] +pub struct CanvasImageCopyGPUContext { + /// The texture to be copied from. The copy source data is captured at the moment + /// the copy is issued. + pub source: *const crate::webgpu::gpu_canvas_context::CanvasGPUCanvasContext, + /// The base texel used for copying from the external image. Together + /// with the `copy_size` argument to copy functions, defines the + /// sub-region of the image to copy. + /// + /// Relative to the top left of the image. + /// + /// Must be [`Origin2d::ZERO`] if [`DownlevelFlags::UNRESTRICTED_EXTERNAL_TEXTURE_COPIES`] is not supported. + pub origin: CanvasOrigin2d, + /// If the Y coordinate of the image should be flipped. Even if this is + /// true, `origin` is still relative to the top left. + pub flip_y: bool, +} + + #[repr(C)] #[derive(Debug)] diff --git a/napi/canvas-napi/NSCTouchHandler.ts b/napi/canvas-napi/NSCTouchHandler.ts new file mode 100644 index 00000000..b61da431 --- /dev/null +++ b/napi/canvas-napi/NSCTouchHandler.ts @@ -0,0 +1,250 @@ +import '@nativescript/foundation'; + +import type { NSCCanvas } from './canvas'; + +class Pointer { + pointerId: number; + location: CGPoint; + touch: NSTouch; + released: boolean; + + constructor(pointerId: number, location: CGPoint, touch: NSTouch, released: boolean) { + this.pointerId = pointerId; + this.location = location; + this.touch = touch; + this.released = released; + } +} + +class NSCClickGestureRecognizerImpl extends NSGestureRecognizer { + _owner?: WeakRef; + _view?: WeakRef; + static { + NativeClass(this); + } + + static initWithOwner(owner: NSCTouchHandler) { + const ret = NSCClickGestureRecognizerImpl.alloc().initWithTargetAction(owner, null); + ret._owner = new WeakRef(owner); + return ret; + } + + mouseDown(event: NSEvent) { + super.mouseDown(event); + const owner = this._owner?.deref?.(); + + if (!owner) { + this._view?.deref?.()?.mouseDown?.(event); + return; + } + const allTouches = event; + if (allTouches) { + // const touches = allTouches.allObjects as NSArray; + // const count = touches.count; + // for (let i = 0; i < count; i++) { + // const touch = touches.objectAtIndex(i); + // const location = touch.locationInView(this.view); + // const pointer = new Pointer(owner.nextId, location, touch, false); + // owner.pointers.push(pointer); + // owner.onPress(pointer.pointerId, location.x, location.y, this); + // owner.nextId++; + // } + } + + // this._view?.deref?.()?.mouseDown?.(event); + } + + mouseUp(event: NSEvent) { + super.mouseUp(event); + // const owner = this._owner?.deref?.(); + // if (!owner) { + // this._view?.deref?.()?.mouseUp?.(event); + // return; + // } + // + // const allTouches = event.allTouches(); + // if (allTouches) { + // const touches = allTouches.allObjects as NSArray; + // const count = touches.count; + // for (let i = 0; i < count; i++) { + // const touch = touches.objectAtIndex(i); + // const location = touch.locationInView(this.view); + // const index = owner.pointers.findIndex((pointer) => { + // return touch.isEqual(pointer.touch); + // }); + // if (index === -1) { + // continue; + // } + // const pointer = owner.pointers[index]; + // if (pointer.released) { + // owner.pointers.splice(index, 1); + // continue; + // } + // + // owner.onRelease(pointer.pointerId, location.x, location.y, this); + // pointer.released = true; + // owner.pointers.splice(index, 1); + // } + // } + // + // this._view?.deref?.()?.mouseUp?.(event); + } + + mouseDragged(event: NSEvent) { + super.mouseDragged(event); + // const owner = this._owner?.deref?.(); + // if (!owner) { + // this._view?.deref?.()?.mouseDragged?.(event); + // return; + // } + // + // const view = this._view?.deref?.(); + // const allTouches = event.allTouches(); + // if (allTouches) { + // const touches = allTouches.allObjects as NSArray; + // if (touches) { + // const ret = { + // event: 'move', + // pointers: new Array(owner.pointers.length) + // }; + // for (const [index, pointer] of owner.pointers.entries()) { + // const location = pointer.touch.locationInView(view); + // const pointerId = pointer.pointerId; + // ret.pointers[index] = { + // ptrId: pointerId, + // isPrimary: index === 0, + // x: location.x, + // y: location.y + // + // }; + // } + // view.touchEventListener(ret, this); + // } + // } + // + // view?.mouseDragged?.(event); + } + + touchesCancelledWithEvent(event: NSEvent) { + const owner = this._owner?.deref?.(); + if (!owner) { + this._view?.deref?.()?.touchesCancelledWithEvent?.(event); + return; + } + + const allTouches = event.allTouches(); + if (allTouches) { + const touches = allTouches.allObjects as NSArray; + const count = touches.count; + for (let i = 0; i < count; i++) { + const touch = touches.objectAtIndex(i); + const location = touch.locationInView(this.view); + const index = owner.pointers.findIndex((pointer) => { + return touch.isEqual(pointer.touch); + }); + if (index === -1) { + continue; + } + const pointer = owner.pointers[index]; + if (pointer.released) { + owner.pointers.splice(index, 1); + continue; + } + + owner.onCancel(pointer.pointerId, location.x, location.y, this); + pointer.released = true; + owner.pointers.splice(index, 1); + } + } + + this._view?.deref?.()?.touchesCancelledWithEvent?.(event); + } +} + +class NSCPanGestureRecognizerImpl extends NSPanGestureRecognizer { + _owner: WeakRef; + static { + NativeClass(this); + } + + static initWithOwner(owner: NSCTouchHandler) { + const ret = new NSCPanGestureRecognizerImpl(); + ret._owner = new WeakRef(owner); + return ret; + } +} + +class NSCMagnificationGestureRecognizerImpl extends NSMagnificationGestureRecognizer { + _owner: WeakRef; + static { + NativeClass(this); + } + + static initWithOwner(owner: NSCTouchHandler) { + const ret = new NSCMagnificationGestureRecognizerImpl(); + ret._owner = new WeakRef(owner); + return ret; + } +} + +class NSCTouchHandler extends NSObject { + static { + NativeClass(this); + } + view?: WeakRef; + nextId = 0; + pointers: Array = []; + gestureRecognizer: NSCClickGestureRecognizerImpl; + panRecognizer: NSCPanGestureRecognizerImpl; + magnificationRecognizer: NSCMagnificationGestureRecognizerImpl; + + static initWithOwner(owner: NSCCanvas) { + const ret = NSCTouchHandler.new(); + ret.view = new WeakRef(owner); + ret.gestureRecognizer = NSCClickGestureRecognizerImpl.initWithOwner(ret); + ret.panRecognizer = NSCPanGestureRecognizerImpl.initWithOwner(ret); + ret.magnificationRecognizer = NSCMagnificationGestureRecognizerImpl.initWithOwner(ret); + return ret; + } + + onPress(ptrId: number, x: number, y: number, gestureRecognizer: NSGestureRecognizer) { + const object = { + event: 'down', + ptrId, + isPrimary: this.pointers[0]?.pointerId === ptrId, + x, + y, + }; + this.view?.deref?.()?.touchEventListener?.(object, gestureRecognizer); + } + + onRelease(ptrId: number, x: number, y: number, gestureRecognizer: NSGestureRecognizer) { + const object = { + event: 'up', + ptrId, + isPrimary: this.pointers[0]?.pointerId === ptrId, + x, + y, + }; + this.view?.deref?.()?.touchEventListener?.(object, gestureRecognizer); + } + + onCancel(ptrId: number, x: number, y: number, gestureRecognizer: NSGestureRecognizer) { + const object = { + event: 'cancel', + ptrId, + isPrimary: this.pointers[0]?.pointerId === ptrId, + x, + y, + }; + + this.view?.deref?.()?.touchEventListener?.(object, gestureRecognizer); + } + + static ObjCExposedMethods = { + mouseEntered: { returns: interop.types.void, params: [NSEvent] }, + mouseExited: { returns: interop.types.void, params: [NSEvent] }, + }; +} + +export default NSCTouchHandler; diff --git a/napi/canvas-napi/canvas.ts b/napi/canvas-napi/canvas.ts index 03631420..d6b316ed 100644 --- a/napi/canvas-napi/canvas.ts +++ b/napi/canvas-napi/canvas.ts @@ -3,7 +3,6 @@ import { Event } from '@nativescript/foundation/dom/dom-utils.js'; import { type YogaNodeLayout } from '@nativescript/foundation/layout/index.js'; import { view } from '@nativescript/foundation/views/decorators/view.js'; import { ViewBase } from '@nativescript/foundation/views/view/view-base.js'; - // @ts-ignore const require = createRequire(import.meta.url); @@ -24,6 +23,11 @@ class NSCMTLView extends NSView { _queue?: MTLCommandQueue; _canvas: WeakRef | null = null; + //@ts-ignore + // isFlipped() { + // return true; + // } + get mtlLayer() { return this.layer as CAMetalLayer; } @@ -135,7 +139,173 @@ function getGPU() { return globalThis.__gpu; } -class NSCCanvas extends NSView { +interface MouseEventOptions extends EventInit { + screenX?: number; + screenY?: number; + clientX?: number; + clientY?: number; + ctrlKey?: boolean; + shiftKey?: boolean; + altKey?: boolean; + metaKey?: boolean; + button?: number; + buttons?: number; + relatedTarget?: any; + region?: any; + movementX?: number; + movementY?: number; + pageX?: number; + pageY?: number; +} + +const isCancelled_ = Symbol('[[isCancelled]]'); + +export class MouseEvent extends Event { + readonly screenX: number; + readonly screenY: number; + readonly clientX: number; + readonly clientY: number; + readonly ctrlKey: boolean; + readonly shiftKey: boolean; + readonly altKey: boolean; + readonly metaKey: boolean; + readonly button: number; + readonly buttons: number; + readonly relatedTarget: any; + readonly region: any; + readonly movementX: number; + readonly movementY: number; + readonly pageX: number; + readonly pageY: number; + [isCancelled_] = false; + + constructor(type: 'dblclick' | 'mousedown' | 'mouseenter' | 'mouseleave' | 'mousemove' | 'mouseout' | 'mouseover' | 'mouseup', options?: MouseEventOptions) { + super(type as any, options); + this.screenX = options?.screenX ?? 0; + this.screenY = options?.screenY ?? 0; + this.clientX = options?.clientX ?? 0; + this.clientY = options?.clientY ?? 0; + this.ctrlKey = options?.ctrlKey ?? false; + this.shiftKey = options?.ctrlKey ?? false; + this.altKey = options?.ctrlKey ?? false; + this.metaKey = options?.ctrlKey ?? false; + this.button = options?.button ?? 0; + this.buttons = options?.buttons ?? 0; + this.relatedTarget = options?.relatedTarget ?? null; + this.region = options?.region ?? null; + this.movementX = options?.movementX ?? 0; + this.movementY = options?.movementY ?? 0; + this.pageX = options?.pageX ?? 0; + this.pageY = options?.pageY ?? 0; + } + + get x() { + return this.clientX; + } + + get y() { + return this.clientY; + } + + preventDefault() { + super.preventDefault(); + this[isCancelled_] = true; + } +} + +interface PointerEventOptions extends MouseEventOptions { + pointerId?: number; + width?: number; + height?: number; + pressure?: number; + tangentialPressure?: number; + pointerType?: 'mouse' | 'touch' | 'pen'; + tiltX?: number; + tiltY?: number; + twist?: number; + isPrimary?: boolean; +} + +export class PointerEvent extends MouseEvent { + readonly type: string; + readonly pointerType: string; + readonly pointerId: number; + readonly width: number; + readonly height: number; + readonly pressure: number; + readonly tangentialPressure: number; + readonly tiltX?: number; + readonly tiltY?: number; + readonly twist?: number; + readonly isPrimary?: boolean; + + constructor(type: 'pointerover' | 'pointerenter' | 'pointerdown' | 'pointermove' | 'pointerrawupdate' | 'pointerup' | 'pointercancel' | 'pointerout' | 'pointerleave' | 'gotpointercapture' | 'lostpointercapture', options?: PointerEventOptions) { + super(type as any, options); + this.pointerType = options?.pointerType ?? ''; + this.type = type; + this.pointerId = options?.pointerId ?? 0; + this.width = options?.width ?? 1; + this.height = options?.height ?? 1; + this.pressure = options?.pressure ?? 0; + this.tangentialPressure = options?.tangentialPressure ?? 0; + this.tiltX = options?.tiltX ?? 0; + this.tiltY = options?.tiltY ?? 0; + this.twist = options?.twist ?? 0; + this.isPrimary = options?.isPrimary ?? false; + } +} + +interface WheelEventOptions extends EventInit { + deltaX?: number; + deltaY?: number; + deltaZ?: number; + deltaMode?: number; +} + +export class WheelEvent extends Event { + readonly deltaX?: number; + readonly deltaY?: number; + readonly deltaZ?: number; + readonly deltaMode?: number; + + constructor(type: 'wheel', options?: WheelEventOptions) { + super(type, options); + this.deltaX = options?.deltaX ?? 0; + this.deltaY = options?.deltaY ?? 0; + this.deltaZ = options?.deltaZ ?? 0; + this.deltaMode = options?.deltaMode ?? 0; + } +} + +const buildMouseEvent = ( + view: NSView, + event: NSEvent +): { + clientX: number; + clientY: number; + screenX: number; + screenY: number; + pageX: number; + pageY: number; +} => { + const location = view.convertPointToView(event.locationInWindow, this as never); + const clientX = location.x; + const clientY = location.y; + const screenX = location.x; + const screenY = location.y; + const pageX = location.x; + const pageY = location.y; + return { + clientX, + clientY, + screenX, + screenY, + pageX, + pageY, + }; +}; + +export class NSCCanvas extends NSView { static { NativeClass(this); } @@ -160,6 +330,13 @@ class NSCCanvas extends NSView { _is2D: boolean = false; + //@ts-ignore + get isFlipped() { + return true; + } + + touchEventListener?: (object: any, gesture: NSGestureRecognizer) => void; + get is2D() { return this._is2D; } @@ -175,10 +352,33 @@ class NSCCanvas extends NSView { return this._fit; } - initWithFrame(frame: CGRect) { - super.initWithFrame(frame); - this.wantsLayer = true; + ignoreTouchEvents = false; + + toDataURL(format?: string, quality?: number) { + if (this.engine === Engine.None) { + const rect = NSMakeRect(0, 0, this.surfaceWidth, this.surfaceHeight); + const rep = NSBitmapImageRep.alloc().initWithBitmapDataPlanesPixelsWidePixelsHighBitsPerSampleSamplesPerPixelHasAlphaIsPlanarColorSpaceNameBytesPerRowBitsPerPixel(null, this.surfaceWidth, this.surfaceHeight, 8, 4, true, false, NSDeviceRGBColorSpace, 0, 0); + + NSGraphicsContext.currentContext = NSGraphicsContext.graphicsContextWithBitmapImageRep(rep); + NSColor.whiteColor.setFill(); + NSRectFill(rect); + NSGraphicsContext.currentContext = null as never; + const image = NSImage.alloc().initWithSize(rect.size); + image.addRepresentation(rep); + switch (format) { + case 'image/jpeg': + case 'image/jpg': + break; + } + } + + return 'data:,'; + } + + initWithFrame(frame: CGRect) { + const thiz = super.initWithFrame(frame); + thiz.wantsLayer = true; const scale = NSScreen.mainScreen.backingScaleFactor; const unscaledWidth = Math.floor(300 / scale); @@ -195,7 +395,9 @@ class NSCCanvas extends NSView { this.initializeView(); - return this; + // this.addGestureRecognizer(this._handler.gestureRecognizer); + + return thiz; } initializeView() { @@ -215,6 +417,149 @@ class NSCCanvas extends NSView { (mtlView.layer as CAMetalLayer).isOpaque = false; } + mouseDown(event: NSEvent) { + const canvas = this._canvas?.deref?.(); + if (!canvas) { + return; + } + + const eventData = buildMouseEvent(this, event); + + const pointerDown = new PointerEvent('pointerdown', { + pointerId: 1, + pointerType: 'mouse', + isPrimary: true, + cancelable: true, + ...eventData, + }); + + canvas.dispatchEvent(pointerDown); + + if (pointerDown[isCancelled_]) { + return; + } + + const ret = new MouseEvent('mousedown', eventData); + + canvas.dispatchEvent(ret); + } + + mouseUp(event: NSEvent) { + const canvas = this._canvas?.deref?.(); + if (!canvas) { + return; + } + const eventData = buildMouseEvent(this, event); + const pointerDown = new PointerEvent('pointerup', { + pointerId: 1, + pointerType: 'mouse', + isPrimary: true, + cancelable: true, + ...eventData, + }); + + canvas.dispatchEvent(pointerDown); + + if (pointerDown[isCancelled_]) { + return; + } + + const ret = new MouseEvent('mouseup', eventData); + + canvas.dispatchEvent(ret); + } + + mouseDragged(event: NSEvent) {} + + scrollWheel(event: NSEvent) { + const canvas = this._canvas?.deref?.(); + if (!canvas) { + return; + } + const wheel = new WheelEvent('wheel', { + deltaX: event.deltaX, + deltaY: event.deltaY, + deltaZ: event.deltaY, + }); + + canvas.dispatchEvent(wheel); + } + + mouseEntered(event: NSEvent) { + const canvas = this._canvas?.deref?.(); + if (!canvas) { + return; + } + const eventData = buildMouseEvent(this, event); + const pointerDown = new PointerEvent('pointerenter', { + pointerId: 1, + pointerType: 'mouse', + isPrimary: true, + cancelable: true, + ...eventData, + }); + + canvas.dispatchEvent(pointerDown); + + if (pointerDown[isCancelled_]) { + return; + } + + const ret = new MouseEvent('mouseenter', eventData); + + canvas.dispatchEvent(ret); + } + + mouseExited(event: NSEvent) { + const canvas = this._canvas?.deref?.(); + if (!canvas) { + return; + } + const eventData = buildMouseEvent(this, event); + const pointerDown = new PointerEvent('pointerleave', { + pointerId: 1, + pointerType: 'mouse', + isPrimary: true, + cancelable: true, + ...eventData, + }); + + canvas.dispatchEvent(pointerDown); + + if (pointerDown[isCancelled_]) { + return; + } + + const ret = new MouseEvent('mouseleave', eventData); + + canvas.dispatchEvent(ret); + } + + mouseMoved(event: NSEvent) { + const canvas = this._canvas?.deref?.(); + if (!canvas) { + return; + } + const eventData = buildMouseEvent(this, event); + const pointerDown = new PointerEvent('pointermove', { + pointerId: 1, + pointerType: 'mouse', + isPrimary: true, + cancelable: true, + ...eventData, + }); + + canvas.dispatchEvent(pointerDown); + + if (pointerDown[isCancelled_]) { + return; + } + + const ret = new MouseEvent('mousemove', eventData); + + canvas.dispatchEvent(ret); + } + mtlViewLayerPtr?: interop.Pointer; mtlViewPtr?: interop.Pointer; glViewPtr?: interop.Pointer; @@ -256,9 +601,16 @@ class NSCCanvas extends NSView { __isLoaded = false; + trackingArea?: NSTrackingArea | null; + layout(): void { super.layout(); - + if (this.trackingArea) { + this.removeTrackingArea(this.trackingArea); + this.trackingArea ??= null; + } + this.trackingArea = NSTrackingArea.alloc().initWithRectOptionsOwnerUserInfo(this.bounds, NSTrackingAreaOptions.MouseEnteredAndExited | NSTrackingAreaOptions.MouseMoved | NSTrackingAreaOptions.ActiveAlways, this, null); + this.addTrackingArea(this.trackingArea); if (!this.__isLoaded && this.surfaceWidth > 0 && this.surfaceHeight > 0) { this.__isLoaded = true; this.scaleSurface(); @@ -521,6 +873,9 @@ export class Canvas extends ViewBase { this.style.width = '100%'; this.style.height = 'auto'; this.nativeView = this._canvas; + this._canvas.touchEventListener = (object, gesture) => { + console.log(object); + }; } nativeView?: NSCCanvas = undefined; @@ -632,9 +987,7 @@ export class Canvas extends ViewBase { return 'data:,'; } if (!this.native) { - // todo - //return this._canvas.toDataURL(type, encoderOptions); - return 'data:,'; + return this._canvas.toDataURL(type, encoderOptions); } if (this._contextType === 4) { return this._gpuContext?.toDataURL(type ?? 'image/png', encoderOptions ?? 0.92); @@ -664,7 +1017,6 @@ export class Canvas extends ViewBase { if (Canvas.forceGL) { const handle = interop.handleof(this._canvas.glkView); - this._2dContext = CanvasRenderingContext2D.withView(handle.toNumber(), this._canvas.surfaceWidth, this._canvas.surfaceHeight, scale, opts.alpha, opts.fontColor, 90, 1); this._canvas.glkView!.isHidden = false; this._contextType = ContextType.Canvas; diff --git a/napi/canvas-napi/examples/node/main.mts b/napi/canvas-napi/examples/node/main.mts index db52dbc2..69550680 100644 --- a/napi/canvas-napi/examples/node/main.mts +++ b/napi/canvas-napi/examples/node/main.mts @@ -6,14 +6,12 @@ import '../app.ts'; import '../../canvas'; import installPolyfills from '../../polyfill'; import three from './three'; -const { webgpuCube, cube } = three; - // import { run as texturedCube } from './texturedCube'; // import { run as twoCubes } from './twoCubes.ts'; // import { run as computeBoids } from './gpgpu/computeBoids'; // import { run as wireframe } from './graphicsTechniques/wireframe'; -import { run as renderBundles } from './renderBundles'; -import { run as cubeMap } from './cubeMap'; + +const { webgpuCube, cube } = three; // @ts-ignore const require = createRequire(import.meta.url); @@ -268,8 +266,8 @@ function flappyBird(canvas) { my = evt.offsetY; if (mx == null || my == null) { - mx = evt.changedTouches[0].clientX; - my = evt.changedTouches[0].clientY; + mx = evt.clientX; + my = evt.clientY; } if (okbtn.x < mx && mx < okbtn.x + okbtn.width && okbtn.y < my && my < okbtn.y + okbtn.height) { @@ -282,15 +280,15 @@ 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); + canvas.addEventListener('mousedown', onpress); - var evt = 'touchstart'; + var evt = 'mousedown'; if (width >= 500) { - // width = 320; - // height = 480; + width = 320; + height = 480; canvas.style.border = '1px solid #000'; evt = 'mousedown'; } @@ -1522,11 +1520,14 @@ fn main() -> @location(0) vec4f { } const canvas = document.createElement('canvas'); + canvas.addEventListener('ready', (event) => { console.log('ready'); + // touchParticles(canvas); + flappyBird(canvas); // webgpuTriangle(canvas); // doGL() - // swarm(canvas); + //swarm(canvas); //texturedCube(canvas); // twoCubes(canvas); // computeBoids(canvas); @@ -1534,7 +1535,7 @@ canvas.addEventListener('ready', (event) => { // renderBundles(canvas); //webgpuCube(canvas); //cubeMap(canvas); - cube(canvas); + //cube(canvas); }); canvas.width = NSScreen.mainScreen.frame.size.width; @@ -1544,7 +1545,7 @@ windowDoc.setAttribute('styleMask', ( NSWindowStyleMask.Titled | NSWindowStyleMask.Closable | NSWindowStyleMask.Miniaturizable | NSWindowStyleMask.Resizable | NSWindowStyleMask.FullSizeContentView ) as never); -const color = NSColor.colorWithCalibratedHueSaturationBrightnessAlpha(0,0,0.2, 0.5); +const color = NSColor.colorWithCalibratedHueSaturationBrightnessAlpha(0, 0, 0.2, 0.5); const background = `rgba(${color.redComponent * 255}, ${color.greenComponent * 255}, ${color.blueComponent * 255}, ${color.alphaComponent})`; // window.style.backgroundColor = background; windowDoc.appendChild(canvas); diff --git a/napi/canvas-napi/examples/node/touchParticles.ts b/napi/canvas-napi/examples/node/touchParticles.ts new file mode 100644 index 00000000..5305170f --- /dev/null +++ b/napi/canvas-napi/examples/node/touchParticles.ts @@ -0,0 +1,184 @@ +let LAF = 0; + +var config = { + particleNumber: 10, + maxParticleSize: 10, + maxSpeed: 40, + colorVariation: 10, +}; +var width = 0, + height = 0; +var particles = [], + drawParticle; + +var colorPalette = { + bg: { r: 12, g: 9, b: 29 }, + matter: [ + { r: 36, g: 18, b: 42 }, // darkPRPL + { r: 78, g: 36, b: 42 }, // rockDust + { r: 252, g: 178, b: 96 }, // solorFlare + { r: 253, g: 238, b: 152 }, // totesASun + ], +}; + +// Provides some nice color variation +// Accepts an rgba object +// returns a modified rgba object or a rgba string if true is passed in for argument 2 +var colorVariation = function (color, returnString) { + var r, g, b, a, variation; + r = Math.round(Math.random() * config.colorVariation - config.colorVariation / 2 + color.r); + g = Math.round(Math.random() * config.colorVariation - config.colorVariation / 2 + color.g); + b = Math.round(Math.random() * config.colorVariation - config.colorVariation / 2 + color.b); + a = Math.random() + 0.5; + if (returnString) { + return `rgba(${r},${g},${b},${a})`; + } else { + return { r, g, b, a }; + } +}; + +// Particle Constructor +var Particle = function (x, y) { + // X Coordinate + this.x = x || Math.round(Math.random() * width); + // Y Coordinate + this.y = y || Math.round(Math.random() * height); + // Radius of the space dust + this.r = Math.ceil(Math.random() * config.maxParticleSize); + // Color of the rock, given some randomness + this.c = colorVariation(colorPalette.matter[Math.floor(Math.random() * colorPalette.matter.length)], true); + // Speed of which the rock travels + this.s = Math.pow(Math.ceil(Math.random() * config.maxSpeed), 0.7); + // Direction the Rock flies + this.d = Math.round(Math.random() * 360); +}; + +function touchParticles(canvas, w?: number, h?: number) { + canvas.width = canvas.clientWidth * window.devicePixelRatio; + canvas.height = canvas.clientHeight * window.devicePixelRatio; + width = w ?? canvas.width; + height = h ?? canvas.height; + + const context = canvas.getContext ? canvas.getContext('2d') : canvas; + + //context.scale(window.devicePixelRatio, window.devicePixelRatio); + + // const ptr = context.canvas.nativeView.getNativeContext(); + // console.log('context.canvas.nativeView.getNativeContext()', ptr); + //const v8Ctx = global.__getCanvasRenderingContext2DImpl(String(ptr)); + + const ctx = context; + + // Set Canvas to be window size + // canvas.width = window.innerWidth; + //canvas.height = window.innerHeight; + + // Configuration, Play with these + + // Colors + + // Some Variables hanging out + var centerX = width / 2, + //@ts-ignore + centerY = height / 2, + drawBg; + + // Draws the background for the canvas, because space + drawBg = function (ctx, color) { + ctx.fillStyle = 'rgb(' + color.r + ',' + color.g + ',' + color.b + ')'; + // ctx.clearRect(0, 0, width, height); + ctx.fillRect(0, 0, width, height); + }; + + // Used to find the rocks next point in space, accounting for speed and direction + var updateParticleModel = function (p) { + var a = 180 - (p.d + 90); // find the 3rd angle + p.d > 0 && p.d < 180 ? (p.x += (p.s * Math.sin(p.d)) / Math.sin(p.s)) : (p.x -= (p.s * Math.sin(p.d)) / Math.sin(p.s)); + p.d > 90 && p.d < 270 ? (p.y += (p.s * Math.sin(a)) / Math.sin(p.s)) : (p.y -= (p.s * Math.sin(a)) / Math.sin(p.s)); + return p; + }; + + // Just the function that physically draws the particles + // Physically? sure why not, physically. + drawParticle = function (x, y, r, c) { + ctx.beginPath(); + ctx.fillStyle = c; + ctx.arc(x, y, r, 0, 2 * Math.PI, false); + ctx.fill(); + ctx.closePath(); + }; + + // Remove particles that aren't on the canvas + + // That thing + var requestAnimFrame = requestAnimationFrame; + + // Our Frame function + var frame = function () { + LAF = requestAnimFrame(frame); + // Draw background first + drawBg(ctx, colorPalette.bg); + // Update Particle models to new position + particles.map((p) => { + return updateParticleModel(p); + }); + // Draw em' + for (const p of particles) { + drawParticle(p.x, p.y, p.r, p.c); + } + + context.render(); + + // Play the same song? Ok! + }; + + // Click listener + + //canvas.parent.on(GestureTypes.tap as any, (args) => {}); + canvas.addEventListener('mousedown', (args) => { + cleanUpArray(); + initParticles(args.clientX * window.devicePixelRatio, args.clientY * window.devicePixelRatio); + }); + + canvas.addEventListener('mousemove', (args) => { + cleanUpArray(); + initParticles(args.clientX * window.devicePixelRatio, args.clientY * window.devicePixelRatio); + }); + + // canvas.parent.on(GestureTypes.touch as any, (args: TouchGestureEventData) => { + // var x = args.getX(), + // y = (args.getY()); + // cleanUpArray(); + // initParticles(x, y); + // }); + + // First Frame + frame(); + + // First particle explosion + initParticles(config.particleNumber); +} + +function cleanUpArray() { + particles = particles.filter((p) => { + return p.x > -100 && p.y > -100; + }); +} + +function initParticles(x = 0, y = 0) { + const numParticles = config.particleNumber; + for (let i = 0; i < numParticles; i++) { + particles.push(new Particle(x, y)); + } + + for (const p of particles) { + drawParticle(p.x, p.y, p.r, p.c); + } +} + +function cancelTouchParticles() { + cancelAnimationFrame(LAF); + LAF = 0; +} + +export { initParticles, touchParticles, cleanUpArray, cancelTouchParticles }; diff --git a/napi/canvas-napi/index.d.ts b/napi/canvas-napi/index.d.ts index bd8ae16a..234e32a1 100644 --- a/napi/canvas-napi/index.d.ts +++ b/napi/canvas-napi/index.d.ts @@ -887,7 +887,7 @@ export declare class GPUTextureView { } export type g_p_u_buffer = GPUBuffer; export declare class GPUBuffer { - get state(): GPUMapState; + get mapState(): GPUMapState; get label(): string; get usage(): number; getMappedRange(offset?: number | undefined | null, size?: number | undefined | null): ArrayBuffer; diff --git a/napi/canvas-napi/index.js b/napi/canvas-napi/index.js index f8427bfb..1d7ec8eb 100644 --- a/napi/canvas-napi/index.js +++ b/napi/canvas-napi/index.js @@ -37,7 +37,7 @@ switch (platform) { if (localFileExisted) { nativeBinding = require('./canvas-napi.android-arm64.node'); } else { - nativeBinding = require('canvas-napi-android-arm64'); + nativeBinding = require('@nativescript/canvas-napi-android-arm64'); } } catch (e) { loadError = e; @@ -49,7 +49,7 @@ switch (platform) { if (localFileExisted) { nativeBinding = require('./canvas-napi.android-arm-eabi.node'); } else { - nativeBinding = require('canvas-napi-android-arm-eabi'); + nativeBinding = require('@nativescript/canvas-napi-android-arm-eabi'); } } catch (e) { loadError = e; @@ -67,7 +67,7 @@ switch (platform) { if (localFileExisted) { nativeBinding = require('./canvas-napi.win32-x64-msvc.node'); } else { - nativeBinding = require('canvas-napi-win32-x64-msvc'); + nativeBinding = require('@nativescript/canvas-napi-win32-x64-msvc'); } } catch (e) { loadError = e; @@ -79,7 +79,7 @@ switch (platform) { if (localFileExisted) { nativeBinding = require('./canvas-napi.win32-ia32-msvc.node'); } else { - nativeBinding = require('canvas-napi-win32-ia32-msvc'); + nativeBinding = require('@nativescript/canvas-napi-win32-ia32-msvc'); } } catch (e) { loadError = e; @@ -91,7 +91,7 @@ switch (platform) { if (localFileExisted) { nativeBinding = require('./canvas-napi.win32-arm64-msvc.node'); } else { - nativeBinding = require('canvas-napi-win32-arm64-msvc'); + nativeBinding = require('@nativescript/canvas-napi-win32-arm64-msvc'); } } catch (e) { loadError = e; @@ -107,7 +107,7 @@ switch (platform) { if (localFileExisted) { nativeBinding = require('./canvas-napi.darwin-universal.node'); } else { - nativeBinding = require('canvas-napi-darwin-universal'); + nativeBinding = require('@nativescript/canvas-napi-darwin-universal'); } break; } catch {} @@ -118,7 +118,7 @@ switch (platform) { if (localFileExisted) { nativeBinding = require('./canvas-napi.darwin-x64.node'); } else { - nativeBinding = require('canvas-napi-darwin-x64'); + nativeBinding = require('@nativescript/canvas-napi-darwin-x64'); } } catch (e) { loadError = e; @@ -130,7 +130,7 @@ switch (platform) { if (localFileExisted) { nativeBinding = require('./canvas-napi.darwin-arm64.node'); } else { - nativeBinding = require('canvas-napi-darwin-arm64'); + nativeBinding = require('@nativescript/canvas-napi-darwin-arm64'); } } catch (e) { loadError = e; @@ -149,7 +149,7 @@ switch (platform) { if (localFileExisted) { nativeBinding = require('./canvas-napi.freebsd-x64.node'); } else { - nativeBinding = require('canvas-napi-freebsd-x64'); + nativeBinding = require('@nativescript/canvas-napi-freebsd-x64'); } } catch (e) { loadError = e; @@ -164,7 +164,7 @@ switch (platform) { if (localFileExisted) { nativeBinding = require('./canvas-napi.linux-x64-musl.node'); } else { - nativeBinding = require('canvas-napi-linux-x64-musl'); + nativeBinding = require('@nativescript/canvas-napi-linux-x64-musl'); } } catch (e) { loadError = e; @@ -175,7 +175,7 @@ switch (platform) { if (localFileExisted) { nativeBinding = require('./canvas-napi.linux-x64-gnu.node'); } else { - nativeBinding = require('canvas-napi-linux-x64-gnu'); + nativeBinding = require('@nativescript/canvas-napi-linux-x64-gnu'); } } catch (e) { loadError = e; @@ -189,7 +189,7 @@ switch (platform) { if (localFileExisted) { nativeBinding = require('./canvas-napi.linux-arm64-musl.node'); } else { - nativeBinding = require('canvas-napi-linux-arm64-musl'); + nativeBinding = require('@nativescript/canvas-napi-linux-arm64-musl'); } } catch (e) { loadError = e; @@ -200,7 +200,7 @@ switch (platform) { if (localFileExisted) { nativeBinding = require('./canvas-napi.linux-arm64-gnu.node'); } else { - nativeBinding = require('canvas-napi-linux-arm64-gnu'); + nativeBinding = require('@nativescript/canvas-napi-linux-arm64-gnu'); } } catch (e) { loadError = e; @@ -214,7 +214,7 @@ switch (platform) { if (localFileExisted) { nativeBinding = require('./canvas-napi.linux-arm-musleabihf.node'); } else { - nativeBinding = require('canvas-napi-linux-arm-musleabihf'); + nativeBinding = require('@nativescript/canvas-napi-linux-arm-musleabihf'); } } catch (e) { loadError = e; @@ -225,7 +225,7 @@ switch (platform) { if (localFileExisted) { nativeBinding = require('./canvas-napi.linux-arm-gnueabihf.node'); } else { - nativeBinding = require('canvas-napi-linux-arm-gnueabihf'); + nativeBinding = require('@nativescript/canvas-napi-linux-arm-gnueabihf'); } } catch (e) { loadError = e; @@ -239,7 +239,7 @@ switch (platform) { if (localFileExisted) { nativeBinding = require('./canvas-napi.linux-riscv64-musl.node'); } else { - nativeBinding = require('canvas-napi-linux-riscv64-musl'); + nativeBinding = require('@nativescript/canvas-napi-linux-riscv64-musl'); } } catch (e) { loadError = e; @@ -250,7 +250,7 @@ switch (platform) { if (localFileExisted) { nativeBinding = require('./canvas-napi.linux-riscv64-gnu.node'); } else { - nativeBinding = require('canvas-napi-linux-riscv64-gnu'); + nativeBinding = require('@nativescript/canvas-napi-linux-riscv64-gnu'); } } catch (e) { loadError = e; @@ -263,7 +263,7 @@ switch (platform) { if (localFileExisted) { nativeBinding = require('./canvas-napi.linux-s390x-gnu.node'); } else { - nativeBinding = require('canvas-napi-linux-s390x-gnu'); + nativeBinding = require('@nativescript/canvas-napi-linux-s390x-gnu'); } } catch (e) { loadError = e; diff --git a/napi/canvas-napi/package.json b/napi/canvas-napi/package.json index 91603b4c..e371aced 100644 --- a/napi/canvas-napi/package.json +++ b/napi/canvas-napi/package.json @@ -1,5 +1,5 @@ { - "name": "canvas-napi", + "name": "@nativescript/canvas-napi", "version": "0.0.0", "main": "index.js", "types": "index.d.ts", diff --git a/napi/canvas-napi/src/gl2/mod.rs b/napi/canvas-napi/src/gl2/mod.rs index c3221ba6..00376cf6 100644 --- a/napi/canvas-napi/src/gl2/mod.rs +++ b/napi/canvas-napi/src/gl2/mod.rs @@ -21,7 +21,7 @@ use crate::{impl_webgl2_context_constants, impl_webgl_context, impl_webgl_contex #[napi(custom_finalize)] pub struct web_g_l_2_rendering_context { - state: *mut WebGLState, + pub(crate) state: *mut WebGLState, } impl_webgl_context!(web_g_l_2_rendering_context); diff --git a/napi/canvas-napi/src/gpu/buffer.rs b/napi/canvas-napi/src/gpu/buffer.rs index f9824d3d..4dfc1eba 100644 --- a/napi/canvas-napi/src/gpu/buffer.rs +++ b/napi/canvas-napi/src/gpu/buffer.rs @@ -127,7 +127,7 @@ impl g_p_u_buffer { } } #[napi(getter)] - pub fn get_state(&self) -> GPUMapState { + pub fn get_map_state(&self) -> GPUMapState { *self.state.lock().unwrap_or_else(|mut e| { self.state.clear_poison(); e.into_inner() diff --git a/napi/canvas-napi/src/gpu/context.rs b/napi/canvas-napi/src/gpu/context.rs index 50d0f6fa..4d41111b 100644 --- a/napi/canvas-napi/src/gpu/context.rs +++ b/napi/canvas-napi/src/gpu/context.rs @@ -16,7 +16,7 @@ use std::sync::Arc; #[napi] pub struct g_p_u_canvas_context { - context: Arc, + pub(crate) context: Arc, } #[napi(object)] diff --git a/napi/canvas-napi/src/gpu/queue.rs b/napi/canvas-napi/src/gpu/queue.rs index 83815bb2..38e147b8 100644 --- a/napi/canvas-napi/src/gpu/queue.rs +++ b/napi/canvas-napi/src/gpu/queue.rs @@ -13,10 +13,13 @@ use crate::gpu::objects::{ use crate::image_asset::ImageAsset; use canvas_c::webgpu::gpu_command_encoder::CanvasImageCopyTexture; use canvas_c::webgpu::structs::{ - CanvasExtent3d, CanvasImageCopyExternalImage, CanvasImageCopyImageAsset, CanvasOrigin2d, + CanvasExtent3d, CanvasImageCopyCanvasRenderingContext2D, CanvasImageCopyExternalImage, + CanvasImageCopyGPUContext, CanvasImageCopyImageAsset, CanvasImageCopyWebGL, CanvasOrigin2d, CanvasOrigin3d, }; -use napi::bindgen_prelude::{Buffer, ClassInstance, Either3, Either4, Either5, Either6, Float32Array, ObjectFinalize}; +use napi::bindgen_prelude::{ + Buffer, ClassInstance, Either3, Either4, Either5, Either6, Float32Array, ObjectFinalize, +}; use napi::*; use napi_derive::napi; use std::ffi::CString; @@ -59,6 +62,19 @@ impl g_p_u_queue { depth_or_array_layers: dict.depth_or_array_layers.unwrap_or(1), }, }; + let origin = source + .origin + .map(|origin| match origin { + Either::A(array) => CanvasOrigin2d { + x: *array.get(0).unwrap_or(&0), + y: *array.get(1).unwrap_or(&0), + }, + Either::B(dict) => CanvasOrigin2d { + x: dict.x.unwrap_or_default(), + y: dict.y.unwrap_or_default(), + }, + }) + .unwrap_or(CanvasOrigin2d { x: 0, y: 0 }); let dst = CanvasImageCopyTexture { texture: Arc::as_ptr(&destination.texture.texture), mip_level: destination.mip_level.unwrap_or(0), @@ -88,19 +104,7 @@ impl g_p_u_queue { let src = CanvasImageCopyExternalImage { source: data.as_ptr(), source_size: data.len(), - origin: source - .origin - .map(|origin| match origin { - Either::A(array) => CanvasOrigin2d { - x: *array.get(0).unwrap_or(&0), - y: *array.get(1).unwrap_or(&0), - }, - Either::B(dict) => CanvasOrigin2d { - x: dict.x.unwrap_or_default(), - y: dict.y.unwrap_or_default(), - }, - }) - .unwrap_or(CanvasOrigin2d { x: 0, y: 0 }), + origin, flip_y: source.flip_y.unwrap_or(false), width, height, @@ -117,19 +121,7 @@ impl g_p_u_queue { Either6::B(image_asset) => { let src = CanvasImageCopyImageAsset { source: Arc::as_ptr(&image_asset.asset), - origin: source - .origin - .map(|origin| match origin { - Either::A(array) => CanvasOrigin2d { - x: *array.get(0).unwrap_or(&0), - y: *array.get(1).unwrap_or(&0), - }, - Either::B(dict) => CanvasOrigin2d { - x: dict.x.unwrap_or_default(), - y: dict.y.unwrap_or_default(), - }, - }) - .unwrap_or(CanvasOrigin2d { x: 0, y: 0 }), + origin, flip_y: source.flip_y.unwrap_or(false), }; unsafe { @@ -141,10 +133,66 @@ impl g_p_u_queue { ) } } - Either6::C(c2d) => {} - Either6::D(gl) => {} - Either6::E(gl2) => {} - Either6::F(gpu) => {} + Either6::C(c2d) => { + let src = CanvasImageCopyCanvasRenderingContext2D { + source: c2d.context, + origin, + flip_y: source.flip_y.unwrap_or(false), + }; + unsafe { + canvas_c::webgpu::gpu_queue::canvas_native_webgpu_queue_copy_context_to_texture( + Arc::as_ptr(&self.queue), + &src, + &dst, + &size, + ) + } + } + Either6::D(gl) => { + let src = CanvasImageCopyWebGL { + source: gl.state, + origin, + flip_y: source.flip_y.unwrap_or(false), + }; + unsafe { + canvas_c::webgpu::gpu_queue::canvas_native_webgpu_queue_copy_webgl_to_texture( + Arc::as_ptr(&self.queue), + &src, + &dst, + &size, + ) + } + } + Either6::E(gl2) => { + let src = CanvasImageCopyWebGL { + source: gl2.state, + origin, + flip_y: source.flip_y.unwrap_or(false), + }; + unsafe { + canvas_c::webgpu::gpu_queue::canvas_native_webgpu_queue_copy_webgl_to_texture( + Arc::as_ptr(&self.queue), + &src, + &dst, + &size, + ) + } + } + Either6::F(gpu) => { + let src = CanvasImageCopyGPUContext { + source: Arc::as_ptr(&gpu.context), + origin, + flip_y: source.flip_y.unwrap_or(false), + }; + unsafe { + canvas_c::webgpu::gpu_queue::canvas_native_webgpu_queue_copy_gpu_context_to_texture( + Arc::as_ptr(&self.queue), + &src, + &dst, + &size, + ) + } + } } } @@ -167,7 +215,7 @@ impl g_p_u_queue { buffer_data.as_ptr(), buffer_data.len(), data_offset.unwrap_or(0) as usize, - size.unwrap_or(-1) as isize + size.unwrap_or(-1) as isize, ); Ok(()) }, @@ -184,7 +232,7 @@ impl g_p_u_queue { ); } Ok(()) - }, + } Either4::C(view) => { unsafe { canvas_c::webgpu::gpu_queue::canvas_native_webgpu_queue_write_buffer( diff --git a/napi/canvas-napi/tsconfig.json b/napi/canvas-napi/tsconfig.json index 655682b9..0c899ff5 100644 --- a/napi/canvas-napi/tsconfig.json +++ b/napi/canvas-napi/tsconfig.json @@ -14,6 +14,6 @@ "noEmit": false }, "extends": "@tsconfig/node22/tsconfig.json", - "include": ["canvas.ts", "index.d.ts", "utils/*.ts", "references.d.ts"], + "include": ["canvas.ts", "NSCTouchHandler.ts", "index.d.ts", "utils/*.ts", "references.d.ts"], "exclude": ["node_modules", "examples"] }