From b6b4a21ec8b1823b1d2be99c17a58ee5f8c40028 Mon Sep 17 00:00:00 2001 From: Brandon Reinhart Date: Sun, 22 Oct 2023 22:34:41 -0500 Subject: [PATCH 01/13] Add mutable string accessor macro. --- src/c_interface.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/c_interface.rs b/src/c_interface.rs index 2d760c2..7a438a9 100644 --- a/src/c_interface.rs +++ b/src/c_interface.rs @@ -462,6 +462,30 @@ macro_rules! c_accessor_string { }; } +macro_rules! c_accessor_string_mut { + ($(#[$($attrss:tt)*])* $rust:ident, $rust_set:ident, $c:ident) => { + $(#[$($attrss)*])* + #[must_use] + pub fn $rust(&self) -> &str { + unsafe { + if !self.c_ptr_ref().$c.is_null() { + crate::c_interface::from_c_str(std::ffi::CStr::from_ptr(self.c_ptr_ref().$c)) + } else { + "" + } + } + } + + $(#[$($attrss)*])* + pub fn $rust_set(&mut self, value: String) { + let c_str = std::ffi::CString::new(value).expect("Null byte found in provided string"); + unsafe { + self.c_ptr_mut().$c = c_str.into_raw(); + } + } + }; +} + macro_rules! c_accessor_string_optional { ($(#[$($attrss:tt)*])* $rust:ident, $c:ident) => { $(#[$($attrss)*])* From f639f860d83cab15be5561f4c2427159564d0954 Mon Sep 17 00:00:00 2001 From: Brandon Reinhart Date: Sun, 22 Oct 2023 22:34:57 -0500 Subject: [PATCH 02/13] Add an attachment loader wrapper. --- src/attachment_loader.rs | 103 +++++++++++++++++++++++++++++++++++++++ src/lib.rs | 2 + 2 files changed, 105 insertions(+) create mode 100644 src/attachment_loader.rs diff --git a/src/attachment_loader.rs b/src/attachment_loader.rs new file mode 100644 index 0000000..35679c5 --- /dev/null +++ b/src/attachment_loader.rs @@ -0,0 +1,103 @@ +use crate::{ + c::{ + spAtlasAttachmentLoader_create, spAttachmentLoader, spAttachmentLoader_createAttachment, + spAttachmentLoader_dispose, + }, + c_interface::{NewFromPtr, SyncPtr}, + Atlas, Attachment, AttachmentType, Skin, +}; + +/// Error types related to [`AttachmentLoader`](`crate::AttachmentLoader`). +#[derive(Debug)] +pub enum AttachmentLoaderError { + /// Creating an attachment failed. Check error1() and error2(). + CreateAttachmentFailed, + InvalidArgument { + field: &'static str, + }, +} + +#[derive(Debug)] +pub struct AttachmentLoader { + c_attachment_loader: SyncPtr, +} + +impl NewFromPtr for AttachmentLoader { + unsafe fn new_from_ptr(c_attachment_loader: *mut spAttachmentLoader) -> Self { + Self { + c_attachment_loader: SyncPtr(c_attachment_loader), + } + } +} + +impl AttachmentLoader { + /// The spine runtime offers a default [`AttachmentLoader`](`crate::AttachmentLoader`) that + /// loads attachments from an [`Atlas`](`crate::Atlas`). + pub fn new_atlas_loader(atlas: &Atlas) -> Self { + unsafe { + let atlas_attachment_loader = spAtlasAttachmentLoader_create(atlas.c_ptr()); + let attachment_loader = &mut (*atlas_attachment_loader).super_0; + Self::new_from_ptr(attachment_loader) + } + } + + /// Creates an [`Attachment`](`crate::Attachment`) of a specified type. + /// + /// # Errors + /// + /// Returns [`AttachmentLoaderError::CreateAttachmentFailed`] if creating the attachment failed. + /// Check error1() and error2() for more information. + /// Returns [`AttachmentLoaderError::InvalidArgument`] if `name` or `path` contain a null byte. + pub fn create_attachment( + &self, + skin: Option, + attachment_type: AttachmentType, + name: &str, + path: &str, + ) -> Result { + let c_name = std::ffi::CString::new(name) + .map_err(|_| AttachmentLoaderError::InvalidArgument { field: "name" })?; + let c_path = std::ffi::CString::new(path) + .map_err(|_| AttachmentLoaderError::InvalidArgument { field: "path" })?; + + unsafe { + let c_name = c_name.as_ptr(); + let c_path = c_path.as_ptr(); + let c_skin = skin.map_or(std::ptr::null_mut(), |skin| skin.c_ptr()); + let c_sequence = std::ptr::null_mut(); // What is this for? + + let attachment = spAttachmentLoader_createAttachment( + self.c_ptr(), + c_skin, + attachment_type as u32, + c_name, + c_path, + c_sequence, + ); + + if attachment.is_null() { + Err(AttachmentLoaderError::CreateAttachmentFailed) + } else { + Ok(Attachment::new_from_ptr(attachment)) + } + } + } + + c_accessor_string!(error1, error1); + c_accessor_string!(error2, error2); + c_ptr!(c_attachment_loader, spAttachmentLoader); +} + +impl Clone for AttachmentLoader { + fn clone(&self) -> Self { + unsafe { AttachmentLoader::new_from_ptr(self.c_ptr()) } + } +} + +impl Drop for AttachmentLoader { + fn drop(&mut self) { + unsafe { + spAttachmentLoader_dispose(self.c_ptr()); + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 00c0d9f..ec59fd8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -48,6 +48,7 @@ mod animation_state_data; #[path = "atlas.rs"] mod atlas_mod; mod attachment; +mod attachment_loader; mod bone; mod bounding_box_attachment; mod clipping_attachment; @@ -73,6 +74,7 @@ pub use animation_state::*; pub use animation_state_data::*; pub use atlas_mod::{atlas, Atlas}; pub use attachment::*; +pub use attachment_loader::*; pub use bone::*; pub use bounding_box_attachment::*; pub use clipping_attachment::*; From 97023c096eb7fad6a824459147b468354b901c25 Mon Sep 17 00:00:00 2001 From: Brandon Reinhart Date: Sun, 22 Oct 2023 23:17:42 -0500 Subject: [PATCH 03/13] temp patch spine_c to refcount obv temporary, see https://github.com/EsotericSoftware/spine-runtimes/pull/2399 --- src/c/spine_c.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/c/spine_c.rs b/src/c/spine_c.rs index 07d907e..c009ec2 100644 --- a/src/c/spine_c.rs +++ b/src/c/spine_c.rs @@ -24428,6 +24428,10 @@ pub unsafe extern "C" fn spSlot_create( } #[no_mangle] pub unsafe extern "C" fn spSlot_dispose(mut self_0: *mut spSlot) { + if (*self_0).attachment != std::ptr::null_mut() { + spAttachment_dispose((*self_0).attachment); + } + _spFree((*self_0).deform as *mut c_void); _spFree((*self_0).darkColor as *mut c_void); _spFree(self_0 as *mut c_void); @@ -24449,6 +24453,15 @@ pub unsafe extern "C" fn spSlot_setAttachment( if attachment == (*self_0).attachment { return; } + + if attachment != std::ptr::null_mut() { + (*attachment).refCount += 1; + } + + if (*self_0).attachment != std::ptr::null_mut() { + spAttachment_dispose((*self_0).attachment); + } + if isVertexAttachment(attachment) == 0 || isVertexAttachment((*self_0).attachment) == 0 || (*(attachment as *mut spVertexAttachment)).timelineAttachment From 95e71b2355b766e4da274ceb68777c0e78de5141 Mon Sep 17 00:00:00 2001 From: Brandon Reinhart Date: Sun, 22 Oct 2023 23:40:06 -0500 Subject: [PATCH 04/13] Convenience fn for creating region attachments. --- src/attachment_loader.rs | 27 +++++++++++++++++++++- src/region_attachment.rs | 50 ++++++++++++++++++++++++++++++++-------- 2 files changed, 67 insertions(+), 10 deletions(-) diff --git a/src/attachment_loader.rs b/src/attachment_loader.rs index 35679c5..9771a9c 100644 --- a/src/attachment_loader.rs +++ b/src/attachment_loader.rs @@ -4,7 +4,7 @@ use crate::{ spAttachmentLoader_dispose, }, c_interface::{NewFromPtr, SyncPtr}, - Atlas, Attachment, AttachmentType, Skin, + Atlas, Attachment, AttachmentType, RegionAttachment, RegionProps, Skin, }; /// Error types related to [`AttachmentLoader`](`crate::AttachmentLoader`). @@ -83,6 +83,31 @@ impl AttachmentLoader { } } + /// Convenience function for creating a [`RegionAttachment`](`crate::RegionAttachment`). + /// + /// # Errors + /// + /// Returns [`AttachmentLoaderError::CreateAttachmentFailed`] if creating the attachment failed. + /// Check error1() and error2() for more information. + /// Returns [`AttachmentLoaderError::InvalidArgument`] if `name` or `path` contain a null byte. + pub fn create_region_attachment( + &self, + skin: Option, + name: &str, + path: &str, + props: &RegionProps, + ) -> Result { + let attachment = self.create_attachment(skin, AttachmentType::Region, name, path)?; + + let Some(mut region) = attachment.as_region() else { + return Err(AttachmentLoaderError::CreateAttachmentFailed); + }; + + region.update_from_props(props); + + Ok(attachment) + } + c_accessor_string!(error1, error1); c_accessor_string!(error2, error2); c_ptr!(c_attachment_loader, spAttachmentLoader); diff --git a/src/region_attachment.rs b/src/region_attachment.rs index 94fe569..d12bdd8 100644 --- a/src/region_attachment.rs +++ b/src/region_attachment.rs @@ -6,11 +6,24 @@ use crate::{ c_interface::SyncPtr, slot::Slot, texture_region::TextureRegion, + Color, }; #[cfg(feature = "mint")] use mint::Vector2; +#[derive(Debug)] +pub struct RegionProps { + pub x: f32, + pub y: f32, + pub scale_x: f32, + pub scale_y: f32, + pub rotation: f32, + pub width: f32, + pub height: f32, + pub color: Color, +} + /// An attachment which draws a texture. /// /// [Spine API Reference](http://esotericsoftware.com/spine-api-reference#RegionAttachment) @@ -57,52 +70,71 @@ impl RegionAttachment { } } + pub fn update_from_props(&mut self, props: &RegionProps) { + self.set_x(props.x); + self.set_y(props.y); + self.set_scale_x(props.scale_x); + self.set_scale_y(props.scale_y); + self.set_rotation(props.rotation); + self.set_width(props.width); + self.set_height(props.height); + *self.color_mut() = props.color; + self.update_region(); + } + c_attachment_accessors!(); - c_accessor_string!(path, path); - c_accessor!( + c_accessor_string_mut!(path, set_path, path); + c_accessor_mut!( /// The local x translation. x, + set_x, x, f32 ); - c_accessor!( + c_accessor_mut!( /// The local y translation. y, + set_y, y, f32 ); - c_accessor!( + c_accessor_mut!( /// The local scaleX. scale_x, + set_scale_x, scaleX, f32 ); - c_accessor!( + c_accessor_mut!( /// The local scaleY. scale_y, + set_scale_y, scaleY, f32 ); // TODO: docs: in degrees? counter-clockwise? - c_accessor!( + c_accessor_mut!( /// The local rotation. rotation, + set_rotation, rotation, f32 ); - c_accessor!( + c_accessor_mut!( /// The width of the region attachment in Spine. width, + set_width, width, f32 ); - c_accessor!( + c_accessor_mut!( /// The height of the region attachment in Spine. height, + set_height, height, f32 ); - c_accessor_color!(color, color); + c_accessor_color_mut!(color, color_mut, color); c_accessor_passthrough!(uvs, uvs, [c_float; 8]); c_accessor_passthrough!(offset, offset, [c_float; 8]); c_accessor_renderer_object!(); From 2f9e7e339f01282a0a078abfbf50a1ce11bb93af Mon Sep 17 00:00:00 2001 From: Brandon Reinhart Date: Mon, 23 Oct 2023 09:27:43 -0500 Subject: [PATCH 05/13] return nulerror --- src/c_interface.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/c_interface.rs b/src/c_interface.rs index 7a438a9..0019d2e 100644 --- a/src/c_interface.rs +++ b/src/c_interface.rs @@ -476,12 +476,16 @@ macro_rules! c_accessor_string_mut { } } + /// # Errors + /// + /// Returns [`std::ffi::NulError`] if an interior nul byte is found. $(#[$($attrss)*])* - pub fn $rust_set(&mut self, value: String) { - let c_str = std::ffi::CString::new(value).expect("Null byte found in provided string"); + pub fn $rust_set(&mut self, value: String) -> Result<(), std::ffi::NulError> { + let c_str = std::ffi::CString::new(value)?; unsafe { self.c_ptr_mut().$c = c_str.into_raw(); } + Ok(()) } }; } From a9030b90a4b9f40e891651d85af0a75b003cb1c3 Mon Sep 17 00:00:00 2001 From: Brandon Reinhart Date: Mon, 23 Oct 2023 09:27:58 -0500 Subject: [PATCH 06/13] clippy --- src/attachment_loader.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/attachment_loader.rs b/src/attachment_loader.rs index 9771a9c..b526dbe 100644 --- a/src/attachment_loader.rs +++ b/src/attachment_loader.rs @@ -4,7 +4,7 @@ use crate::{ spAttachmentLoader_dispose, }, c_interface::{NewFromPtr, SyncPtr}, - Atlas, Attachment, AttachmentType, RegionAttachment, RegionProps, Skin, + Atlas, Attachment, AttachmentType, RegionProps, Skin, }; /// Error types related to [`AttachmentLoader`](`crate::AttachmentLoader`). From 652707c307bf4da8982f12927603c35a6a91958d Mon Sep 17 00:00:00 2001 From: Brandon Reinhart Date: Mon, 23 Oct 2023 09:28:58 -0500 Subject: [PATCH 07/13] fix doc string refs --- src/attachment_loader.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/attachment_loader.rs b/src/attachment_loader.rs index b526dbe..1106007 100644 --- a/src/attachment_loader.rs +++ b/src/attachment_loader.rs @@ -10,7 +10,8 @@ use crate::{ /// Error types related to [`AttachmentLoader`](`crate::AttachmentLoader`). #[derive(Debug)] pub enum AttachmentLoaderError { - /// Creating an attachment failed. Check error1() and error2(). + /// Creating an attachment failed. + /// Check [`error1`](`Self::error1`) and [`error2`](`Self::error2`) for more information. CreateAttachmentFailed, InvalidArgument { field: &'static str, @@ -46,7 +47,7 @@ impl AttachmentLoader { /// # Errors /// /// Returns [`AttachmentLoaderError::CreateAttachmentFailed`] if creating the attachment failed. - /// Check error1() and error2() for more information. + /// Check [`error1`](`Self::error1`) and [`error2`](`Self::error2`) for more information. /// Returns [`AttachmentLoaderError::InvalidArgument`] if `name` or `path` contain a null byte. pub fn create_attachment( &self, From 1260f39093f8884a72104d8e0b85e8e79dc89615 Mon Sep 17 00:00:00 2001 From: Brandon Reinhart Date: Mon, 23 Oct 2023 12:37:18 -0500 Subject: [PATCH 08/13] move unsafe to interior As far as I can tell, this is safe to do if the attachment is valid, once esoteric updates spine with the ref count increment in spSlot_setAttachment --- src/slot.rs | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/slot.rs b/src/slot.rs index 6930c01..d8e10e3 100644 --- a/src/slot.rs +++ b/src/slot.rs @@ -55,19 +55,17 @@ impl Slot { // TODO: add attachment() accessor? /// Sets the attachment for this slot. - /// - /// # Safety - /// - /// The attachment must be compatible with this slot, usually by originating from it. - pub unsafe fn set_attachment(&mut self, attachment: Option) { - attachment.map_or_else( - || { - spSlot_setAttachment(self.c_ptr(), std::ptr::null_mut()); - }, - |attachment| { - spSlot_setAttachment(self.c_ptr(), attachment.c_ptr()); - }, - ); + pub fn set_attachment(&mut self, attachment: Option) { + unsafe { + attachment.map_or_else( + || { + spSlot_setAttachment(self.c_ptr(), std::ptr::null_mut()); + }, + |attachment| { + spSlot_setAttachment(self.c_ptr(), attachment.c_ptr()); + }, + ); + } } /// Sets this slot to the setup pose. From bf73a7e180a321af2101678602ddeca4b59d2c62 Mon Sep 17 00:00:00 2001 From: Brandon Reinhart Date: Tue, 24 Oct 2023 13:22:52 -0500 Subject: [PATCH 09/13] undo --- src/c/spine_c.rs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/c/spine_c.rs b/src/c/spine_c.rs index c009ec2..cdfb4d3 100644 --- a/src/c/spine_c.rs +++ b/src/c/spine_c.rs @@ -24428,10 +24428,6 @@ pub unsafe extern "C" fn spSlot_create( } #[no_mangle] pub unsafe extern "C" fn spSlot_dispose(mut self_0: *mut spSlot) { - if (*self_0).attachment != std::ptr::null_mut() { - spAttachment_dispose((*self_0).attachment); - } - _spFree((*self_0).deform as *mut c_void); _spFree((*self_0).darkColor as *mut c_void); _spFree(self_0 as *mut c_void); @@ -24454,14 +24450,6 @@ pub unsafe extern "C" fn spSlot_setAttachment( return; } - if attachment != std::ptr::null_mut() { - (*attachment).refCount += 1; - } - - if (*self_0).attachment != std::ptr::null_mut() { - spAttachment_dispose((*self_0).attachment); - } - if isVertexAttachment(attachment) == 0 || isVertexAttachment((*self_0).attachment) == 0 || (*(attachment as *mut spVertexAttachment)).timelineAttachment From e652845e21463eee20b5d1f1df9ccb216132d435 Mon Sep 17 00:00:00 2001 From: Brandon Reinhart Date: Tue, 24 Oct 2023 13:44:16 -0500 Subject: [PATCH 10/13] mark unsafe again --- src/slot.rs | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/src/slot.rs b/src/slot.rs index d8e10e3..f1b843e 100644 --- a/src/slot.rs +++ b/src/slot.rs @@ -2,10 +2,10 @@ use crate::{ attachment::Attachment, bone::Bone, c::{ - spAttachment, spBlendMode, spBone, spBoneData, spBoundingBoxAttachment, - spClippingAttachment, spMeshAttachment, spPointAttachment, spRegionAttachment, spSkeleton, - spSlot, spSlotData, spSlotData_setAttachmentName, spSlot_setAttachment, - spSlot_setToSetupPose, + spAttachment, spAttachment_dispose, spBlendMode, spBone, spBoneData, + spBoundingBoxAttachment, spClippingAttachment, spMeshAttachment, spPointAttachment, + spRegionAttachment, spSkeleton, spSlot, spSlotData, spSlotData_setAttachmentName, + spSlot_setAttachment, spSlot_setToSetupPose, }, c_interface::{to_c_str, CTmpRef, NewFromPtr, SyncPtr}, AttachmentType, BoneData, BoundingBoxAttachment, ClippingAttachment, MeshAttachment, @@ -55,17 +55,15 @@ impl Slot { // TODO: add attachment() accessor? /// Sets the attachment for this slot. - pub fn set_attachment(&mut self, attachment: Option) { - unsafe { - attachment.map_or_else( - || { - spSlot_setAttachment(self.c_ptr(), std::ptr::null_mut()); - }, - |attachment| { - spSlot_setAttachment(self.c_ptr(), attachment.c_ptr()); - }, - ); - } + pub unsafe fn set_attachment(&mut self, attachment: Option) { + attachment.map_or_else( + || { + spSlot_setAttachment(self.c_ptr(), std::ptr::null_mut()); + }, + |attachment| { + spSlot_setAttachment(self.c_ptr(), attachment.c_ptr()); + }, + ); } /// Sets this slot to the setup pose. From ea5fc8ccf2c931f7e908bee03f21aace8c0c4827 Mon Sep 17 00:00:00 2001 From: Brandon Reinhart Date: Tue, 24 Oct 2023 13:45:18 -0500 Subject: [PATCH 11/13] clippy --- src/slot.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/slot.rs b/src/slot.rs index f1b843e..a7abde5 100644 --- a/src/slot.rs +++ b/src/slot.rs @@ -2,10 +2,10 @@ use crate::{ attachment::Attachment, bone::Bone, c::{ - spAttachment, spAttachment_dispose, spBlendMode, spBone, spBoneData, - spBoundingBoxAttachment, spClippingAttachment, spMeshAttachment, spPointAttachment, - spRegionAttachment, spSkeleton, spSlot, spSlotData, spSlotData_setAttachmentName, - spSlot_setAttachment, spSlot_setToSetupPose, + spAttachment, spBlendMode, spBone, spBoneData, spBoundingBoxAttachment, + spClippingAttachment, spMeshAttachment, spPointAttachment, spRegionAttachment, spSkeleton, + spSlot, spSlotData, spSlotData_setAttachmentName, spSlot_setAttachment, + spSlot_setToSetupPose, }, c_interface::{to_c_str, CTmpRef, NewFromPtr, SyncPtr}, AttachmentType, BoneData, BoundingBoxAttachment, ClippingAttachment, MeshAttachment, @@ -55,6 +55,10 @@ impl Slot { // TODO: add attachment() accessor? /// Sets the attachment for this slot. + // + /// # Safety + /// + /// The attachment must be compatible with this slot, usually by originating from it. pub unsafe fn set_attachment(&mut self, attachment: Option) { attachment.map_or_else( || { From 3bba71cc74c0ffd64d79464615b2f655b34eb55f Mon Sep 17 00:00:00 2001 From: jabu <46233424+jabuwu@users.noreply.github.com> Date: Mon, 6 Nov 2023 06:30:02 -0600 Subject: [PATCH 12/13] Update src/attachment_loader.rs --- src/attachment_loader.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/attachment_loader.rs b/src/attachment_loader.rs index 1106007..fb911dd 100644 --- a/src/attachment_loader.rs +++ b/src/attachment_loader.rs @@ -89,7 +89,7 @@ impl AttachmentLoader { /// # Errors /// /// Returns [`AttachmentLoaderError::CreateAttachmentFailed`] if creating the attachment failed. - /// Check error1() and error2() for more information. + /// Check [`error1`](`Self::error1`) and [`error2`](`Self::error2`) for more information. /// Returns [`AttachmentLoaderError::InvalidArgument`] if `name` or `path` contain a null byte. pub fn create_region_attachment( &self, From f44e927a739be51393047ea01abb5ca6f29bec81 Mon Sep 17 00:00:00 2001 From: jabu <46233424+jabuwu@users.noreply.github.com> Date: Mon, 6 Nov 2023 06:31:52 -0600 Subject: [PATCH 13/13] Update slot.rs --- src/slot.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slot.rs b/src/slot.rs index a7abde5..6930c01 100644 --- a/src/slot.rs +++ b/src/slot.rs @@ -55,7 +55,7 @@ impl Slot { // TODO: add attachment() accessor? /// Sets the attachment for this slot. - // + /// /// # Safety /// /// The attachment must be compatible with this slot, usually by originating from it.