skia_rs_ffi/
lib.rs

1//! C FFI bindings for skia-rs.
2//!
3//! This crate provides C-compatible bindings for use from other languages.
4//! It exposes a C API that mirrors the Skia C API for drop-in compatibility.
5//!
6//! # Safety
7//!
8//! All FFI functions are inherently unsafe. Callers must ensure:
9//! - Pointers are valid and non-null (unless explicitly documented otherwise)
10//! - Proper lifetime management (using the appropriate `_unref` functions)
11//! - Thread safety requirements are followed (see below)
12//!
13//! # Reference Counting
14//!
15//! Objects follow Skia's reference counting model:
16//! - Objects are created with a reference count of 1
17//! - `sk_*_ref()` increments the reference count
18//! - `sk_*_unref()` decrements the reference count and frees when it reaches 0
19//! - Use `sk_refcnt_get_count()` to query the current count
20//!
21//! Reference counting operations (`ref`/`unref`) are **thread-safe** and use
22//! atomic operations internally.
23//!
24//! # Thread Safety
25//!
26//! skia-rs follows Skia's threading model. Understanding thread safety is
27//! critical for correct usage in multi-threaded applications.
28//!
29//! ## Thread Safety Categories
30//!
31//! | Category | Description | Examples |
32//! |----------|-------------|----------|
33//! | **Thread-Safe** | Can be accessed from any thread concurrently | `sk_*_ref()`, `sk_*_unref()`, `sk_refcnt_*()` |
34//! | **Thread-Compatible** | Safe if each instance accessed by one thread | Most objects |
35//! | **Main-Thread-Only** | Must only be used from main/UI thread | GPU contexts |
36//!
37//! ## Object-Specific Thread Safety
38//!
39//! ### Reference Counting (Thread-Safe)
40//! ```c
41//! // These operations are always thread-safe:
42//! sk_surface_ref(surface);      // Atomic increment
43//! sk_surface_unref(surface);    // Atomic decrement
44//! sk_refcnt_get_count(ptr);     // Atomic read
45//! sk_refcnt_is_unique(ptr);     // Atomic read
46//! ```
47//!
48//! ### Immutable Objects (Thread-Safe after creation)
49//! Once created, these objects are safe to read from multiple threads:
50//! - `sk_path_t` (after building is complete)
51//! - `sk_matrix_t` (value type, copied on use)
52//!
53//! ### Mutable Objects (Thread-Compatible)
54//! These must not be accessed concurrently from multiple threads:
55//! - `sk_surface_t` - Drawing operations are not thread-safe
56//! - `sk_paint_t` - Setters/getters must be externally synchronized
57//! - `sk_pathbuilder_t` - Building operations must be single-threaded
58//!
59//! ### GPU Objects (Special Restrictions)
60//! GPU-related objects have additional constraints:
61//! - Must be created/destroyed on the same thread as the GPU context
62//! - Drawing to GPU surfaces must occur on the GPU context thread
63//! - Flush operations must be called from the same thread
64//!
65//! ## Safe Patterns
66//!
67//! ### Pattern 1: Object-per-Thread
68//! ```c
69//! // Each thread creates its own objects
70//! void* thread_func(void* arg) {
71//!     sk_surface_t* surface = sk_surface_new_raster(800, 600);
72//!     sk_paint_t* paint = sk_paint_new();
73//!     // ... use exclusively in this thread ...
74//!     sk_paint_unref(paint);
75//!     sk_surface_unref(surface);
76//!     return NULL;
77//! }
78//! ```
79//!
80//! ### Pattern 2: Shared Immutable Data
81//! ```c
82//! // Build path on one thread, share read-only
83//! sk_path_t* shared_path;  // Global
84//!
85//! void init() {
86//!     sk_pathbuilder_t* builder = sk_pathbuilder_new();
87//!     sk_pathbuilder_add_circle(builder, 100, 100, 50);
88//!     shared_path = sk_pathbuilder_detach(builder);
89//!     sk_pathbuilder_unref(builder);
90//! }
91//!
92//! void* render_thread(void* arg) {
93//!     // Safe: path is immutable after creation
94//!     sk_rect_t bounds;
95//!     sk_path_get_bounds(shared_path, &bounds);
96//!     sk_path_contains(shared_path, 100, 100);
97//!     return NULL;
98//! }
99//! ```
100//!
101//! ### Pattern 3: Reference Counted Sharing
102//! ```c
103//! // Safe: ref counting is atomic
104//! sk_paint_t* paint = sk_paint_new();
105//!
106//! void share_to_thread(sk_paint_t* p) {
107//!     sk_paint_ref(p);  // Thread-safe increment
108//!     // Pass to another thread...
109//! }
110//!
111//! void* other_thread(void* paint_ptr) {
112//!     sk_paint_t* p = (sk_paint_t*)paint_ptr;
113//!     // Clone for thread-local modifications
114//!     sk_paint_t* local = sk_paint_clone(p);
115//!     sk_paint_unref(p);  // Thread-safe decrement
116//!     // ... use local exclusively ...
117//!     sk_paint_unref(local);
118//!     return NULL;
119//! }
120//! ```
121//!
122//! ## Unsafe Patterns (AVOID)
123//!
124//! ```c
125//! // UNSAFE: Concurrent mutation
126//! sk_paint_t* shared_paint;
127//!
128//! void* thread1(void* arg) {
129//!     sk_paint_set_color(shared_paint, 0xFFFF0000);  // DATA RACE!
130//!     return NULL;
131//! }
132//!
133//! void* thread2(void* arg) {
134//!     sk_paint_set_color(shared_paint, 0xFF0000FF);  // DATA RACE!
135//!     return NULL;
136//! }
137//!
138//! // UNSAFE: Drawing while modifying
139//! void* draw_thread(void* arg) {
140//!     sk_surface_draw_rect(surface, &rect, paint);  // DATA RACE with modifier!
141//!     return NULL;
142//! }
143//!
144//! void* modify_thread(void* arg) {
145//!     sk_paint_set_stroke_width(paint, 5.0);  // DATA RACE with drawer!
146//!     return NULL;
147//! }
148//! ```
149//!
150//! ## Error Checking
151//!
152//! After any FFI call, check for panics:
153//! ```c
154//! sk_surface_t* surface = sk_surface_new_raster(800, 600);
155//! if (sk_last_call_panicked()) {
156//!     // Handle error - surface may be NULL
157//!     fprintf(stderr, "Surface creation panicked\n");
158//! }
159//! ```
160//!
161//! ## Summary Table
162//!
163//! | Operation | Thread-Safe | Notes |
164//! |-----------|-------------|-------|
165//! | `sk_*_new()` | Yes | Returns unique object |
166//! | `sk_*_ref()` | Yes | Atomic increment |
167//! | `sk_*_unref()` | Yes | Atomic decrement |
168//! | `sk_*_clone()` | Yes* | Input must not be concurrently modified |
169//! | `sk_*_get_*()` | No | Requires external synchronization |
170//! | `sk_*_set_*()` | No | Requires external synchronization |
171//! | `sk_surface_draw_*()` | No | Single-threaded drawing only |
172//! | `sk_path_contains()` | Yes* | After path is immutable |
173//! | `sk_pathbuilder_*()` | No | Single-threaded building |
174//!
175//! # Panic Safety
176//!
177//! All FFI functions catch panics at the boundary to prevent unwinding
178//! into C code. Functions that panic will return a default/null value
179//! and set an error flag. Use `sk_last_call_panicked()` to check.
180
181#![warn(missing_docs)]
182#![warn(clippy::all)]
183#![allow(clippy::missing_safety_doc)]
184#![allow(unsafe_op_in_unsafe_fn)]
185#![allow(non_camel_case_types)] // FFI types follow C naming conventions
186
187pub mod abi;
188
189use std::ffi::{c_char, c_void};
190use std::panic::{self, AssertUnwindSafe};
191use std::ptr;
192use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
193
194// =============================================================================
195// Panic Catching Infrastructure
196// =============================================================================
197
198/// Global flag indicating if the last FFI call panicked.
199static LAST_PANIC: AtomicBool = AtomicBool::new(false);
200
201/// Check if the last FFI call panicked.
202///
203/// Returns true if a panic occurred, false otherwise.
204/// Reading this flag clears it.
205#[unsafe(no_mangle)]
206pub extern "C" fn sk_last_call_panicked() -> bool {
207    LAST_PANIC.swap(false, Ordering::SeqCst)
208}
209
210/// Catch panics and return a default value if one occurs.
211#[inline]
212fn catch_panic<T: Default, F: FnOnce() -> T + panic::UnwindSafe>(f: F) -> T {
213    match panic::catch_unwind(f) {
214        Ok(result) => result,
215        Err(_) => {
216            LAST_PANIC.store(true, Ordering::SeqCst);
217            T::default()
218        }
219    }
220}
221
222/// Catch panics in void-returning functions.
223#[inline]
224fn catch_panic_void<F: FnOnce() + panic::UnwindSafe>(f: F) {
225    if panic::catch_unwind(f).is_err() {
226        LAST_PANIC.store(true, Ordering::SeqCst);
227    }
228}
229
230// =============================================================================
231// Reference Counting Infrastructure
232// =============================================================================
233
234/// Reference counted wrapper for FFI objects.
235///
236/// This provides Skia-compatible reference counting semantics:
237/// - Created with refcount of 1
238/// - `ref()` increments
239/// - `unref()` decrements and frees when 0
240#[repr(C)]
241pub struct RefCounted<T> {
242    /// Reference count.
243    refcnt: AtomicU32,
244    /// The wrapped value.
245    value: T,
246}
247
248impl<T> RefCounted<T> {
249    /// Create a new reference counted object with refcount of 1.
250    pub fn new(value: T) -> *mut Self {
251        Box::into_raw(Box::new(Self {
252            refcnt: AtomicU32::new(1),
253            value,
254        }))
255    }
256
257    /// Increment the reference count.
258    ///
259    /// # Safety
260    /// Pointer must be valid and non-null.
261    pub unsafe fn ref_ptr(ptr: *mut Self) {
262        if let Some(rc) = ptr.as_ref() {
263            rc.refcnt.fetch_add(1, Ordering::Relaxed);
264        }
265    }
266
267    /// Decrement the reference count and free if it reaches 0.
268    ///
269    /// # Safety
270    /// Pointer must be valid and non-null.
271    /// Returns true if the object was freed.
272    pub unsafe fn unref_ptr(ptr: *mut Self) -> bool {
273        if ptr.is_null() {
274            return false;
275        }
276
277        let rc = &*ptr;
278        // Use AcqRel to ensure proper synchronization
279        if rc.refcnt.fetch_sub(1, Ordering::AcqRel) == 1 {
280            // Last reference, drop the box
281            drop(Box::from_raw(ptr));
282            true
283        } else {
284            false
285        }
286    }
287
288    /// Get the current reference count.
289    ///
290    /// # Safety
291    /// Pointer must be valid and non-null.
292    pub unsafe fn get_count(ptr: *const Self) -> u32 {
293        if let Some(rc) = ptr.as_ref() {
294            rc.refcnt.load(Ordering::Relaxed)
295        } else {
296            0
297        }
298    }
299
300    /// Check if this is the only reference.
301    ///
302    /// # Safety
303    /// Pointer must be valid and non-null.
304    pub unsafe fn is_unique(ptr: *const Self) -> bool {
305        Self::get_count(ptr) == 1
306    }
307
308    /// Get a reference to the inner value.
309    ///
310    /// # Safety
311    /// Pointer must be valid and non-null.
312    pub unsafe fn get_ref<'a>(ptr: *const Self) -> Option<&'a T> {
313        ptr.as_ref().map(|rc| &rc.value)
314    }
315
316    /// Get a mutable reference to the inner value.
317    ///
318    /// # Safety
319    /// Pointer must be valid and non-null.
320    /// Caller must ensure exclusive access.
321    pub unsafe fn get_mut<'a>(ptr: *mut Self) -> Option<&'a mut T> {
322        ptr.as_mut().map(|rc| &mut rc.value)
323    }
324}
325
326// =============================================================================
327// Reference Counting C API
328// =============================================================================
329
330/// Opaque reference counted object type.
331pub type sk_refcnt_t = c_void;
332
333/// Get the reference count of an object.
334///
335/// Returns 0 if the pointer is null.
336#[unsafe(no_mangle)]
337pub unsafe extern "C" fn sk_refcnt_get_count(ptr: *const sk_refcnt_t) -> u32 {
338    // All our refcounted types start with AtomicU32
339    if ptr.is_null() {
340        return 0;
341    }
342    let refcnt = ptr as *const AtomicU32;
343    (*refcnt).load(Ordering::Relaxed)
344}
345
346/// Check if an object has only one reference (is unique).
347#[unsafe(no_mangle)]
348pub unsafe extern "C" fn sk_refcnt_is_unique(ptr: *const sk_refcnt_t) -> bool {
349    sk_refcnt_get_count(ptr) == 1
350}
351
352// Re-export types for FFI
353use skia_rs_canvas::{PixelBuffer, RasterCanvas, Surface};
354use skia_rs_core::{
355    AlphaType, Color, ColorType, IPoint, IRect, ISize, ImageInfo, Matrix, Point, Rect, Scalar, Size,
356};
357use skia_rs_paint::{BlendMode, Paint, Style};
358use skia_rs_path::{FillType, Path, PathBuilder};
359
360// =============================================================================
361// Type Definitions
362// =============================================================================
363
364// Note: Reference counted types are defined in their respective sections:
365// - sk_surface_t = RefCounted<Surface>
366// - sk_paint_t = RefCounted<Paint>
367// - sk_path_t = RefCounted<Path>
368// - sk_pathbuilder_t = RefCounted<PathBuilder>
369
370/// C-compatible point structure.
371#[repr(C)]
372#[derive(Debug, Clone, Copy, Default)]
373pub struct sk_point_t {
374    /// X coordinate.
375    pub x: f32,
376    /// Y coordinate.
377    pub y: f32,
378}
379
380/// C-compatible integer point structure.
381#[repr(C)]
382#[derive(Debug, Clone, Copy, Default)]
383pub struct sk_ipoint_t {
384    /// X coordinate.
385    pub x: i32,
386    /// Y coordinate.
387    pub y: i32,
388}
389
390/// C-compatible size structure.
391#[repr(C)]
392#[derive(Debug, Clone, Copy, Default)]
393pub struct sk_size_t {
394    /// Width.
395    pub width: f32,
396    /// Height.
397    pub height: f32,
398}
399
400/// C-compatible integer size structure.
401#[repr(C)]
402#[derive(Debug, Clone, Copy, Default)]
403pub struct sk_isize_t {
404    /// Width.
405    pub width: i32,
406    /// Height.
407    pub height: i32,
408}
409
410/// C-compatible rectangle structure.
411#[repr(C)]
412#[derive(Debug, Clone, Copy, Default)]
413pub struct sk_rect_t {
414    /// Left edge.
415    pub left: f32,
416    /// Top edge.
417    pub top: f32,
418    /// Right edge.
419    pub right: f32,
420    /// Bottom edge.
421    pub bottom: f32,
422}
423
424/// C-compatible integer rectangle structure.
425#[repr(C)]
426#[derive(Debug, Clone, Copy, Default)]
427pub struct sk_irect_t {
428    /// Left edge.
429    pub left: i32,
430    /// Top edge.
431    pub top: i32,
432    /// Right edge.
433    pub right: i32,
434    /// Bottom edge.
435    pub bottom: i32,
436}
437
438/// C-compatible matrix structure.
439#[repr(C)]
440#[derive(Debug, Clone, Copy)]
441pub struct sk_matrix_t {
442    /// Matrix values (row-major).
443    pub values: [f32; 9],
444}
445
446impl Default for sk_matrix_t {
447    fn default() -> Self {
448        Self {
449            values: [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0],
450        }
451    }
452}
453
454/// C-compatible image info structure.
455#[repr(C)]
456#[derive(Debug, Clone, Copy)]
457pub struct sk_imageinfo_t {
458    /// Width.
459    pub width: i32,
460    /// Height.
461    pub height: i32,
462    /// Color type.
463    pub color_type: u32,
464    /// Alpha type.
465    pub alpha_type: u32,
466}
467
468/// C-compatible color (ARGB).
469pub type sk_color_t = u32;
470
471// =============================================================================
472// Conversion helpers
473// =============================================================================
474
475impl From<Point> for sk_point_t {
476    fn from(p: Point) -> Self {
477        Self { x: p.x, y: p.y }
478    }
479}
480
481impl From<sk_point_t> for Point {
482    fn from(p: sk_point_t) -> Self {
483        Point::new(p.x, p.y)
484    }
485}
486
487impl From<Rect> for sk_rect_t {
488    fn from(r: Rect) -> Self {
489        Self {
490            left: r.left,
491            top: r.top,
492            right: r.right,
493            bottom: r.bottom,
494        }
495    }
496}
497
498impl From<sk_rect_t> for Rect {
499    fn from(r: sk_rect_t) -> Self {
500        Rect::new(r.left, r.top, r.right, r.bottom)
501    }
502}
503
504impl From<Matrix> for sk_matrix_t {
505    fn from(m: Matrix) -> Self {
506        Self { values: m.values }
507    }
508}
509
510impl From<sk_matrix_t> for Matrix {
511    fn from(m: sk_matrix_t) -> Self {
512        Matrix { values: m.values }
513    }
514}
515
516// =============================================================================
517// Surface API (Reference Counted)
518// =============================================================================
519
520/// Reference counted surface type.
521pub type sk_surface_t = RefCounted<Surface>;
522
523/// Create a new raster surface.
524///
525/// Returns a surface with refcount of 1, or null on failure.
526#[unsafe(no_mangle)]
527pub unsafe extern "C" fn sk_surface_new_raster(width: i32, height: i32) -> *mut sk_surface_t {
528    catch_panic(|| match Surface::new_raster_n32_premul(width, height) {
529        Some(surface) => RefCounted::new(surface),
530        None => ptr::null_mut(),
531    })
532}
533
534/// Create a raster surface with specific image info.
535///
536/// Returns a surface with refcount of 1, or null on failure.
537#[unsafe(no_mangle)]
538pub unsafe extern "C" fn sk_surface_new_raster_with_info(
539    info: *const sk_imageinfo_t,
540) -> *mut sk_surface_t {
541    if info.is_null() {
542        return ptr::null_mut();
543    }
544
545    let info = &*info;
546    let color_type = match info.color_type {
547        0 => ColorType::Unknown,
548        1 => ColorType::Alpha8,
549        2 => ColorType::Rgb565,
550        3 => ColorType::Argb4444,
551        4 => ColorType::Rgba8888,
552        5 => ColorType::Bgra8888,
553        _ => ColorType::Rgba8888,
554    };
555
556    let alpha_type = match info.alpha_type {
557        0 => AlphaType::Unknown,
558        1 => AlphaType::Opaque,
559        2 => AlphaType::Premul,
560        3 => AlphaType::Unpremul,
561        _ => AlphaType::Premul,
562    };
563
564    let img_info = match ImageInfo::new(info.width, info.height, color_type, alpha_type) {
565        Ok(i) => i,
566        Err(_) => return ptr::null_mut(),
567    };
568
569    match Surface::new_raster(&img_info, None) {
570        Some(surface) => RefCounted::new(surface),
571        None => ptr::null_mut(),
572    }
573}
574
575/// Increment the reference count of a surface.
576#[unsafe(no_mangle)]
577pub unsafe extern "C" fn sk_surface_ref(surface: *mut sk_surface_t) {
578    RefCounted::ref_ptr(surface);
579}
580
581/// Decrement the reference count of a surface.
582///
583/// Frees the surface when the count reaches 0.
584#[unsafe(no_mangle)]
585pub unsafe extern "C" fn sk_surface_unref(surface: *mut sk_surface_t) {
586    catch_panic_void(AssertUnwindSafe(|| {
587        RefCounted::unref_ptr(surface);
588    }));
589}
590
591/// Get the reference count of a surface.
592#[unsafe(no_mangle)]
593pub unsafe extern "C" fn sk_surface_get_refcnt(surface: *const sk_surface_t) -> u32 {
594    RefCounted::get_count(surface)
595}
596
597/// Check if the surface has only one reference.
598#[unsafe(no_mangle)]
599pub unsafe extern "C" fn sk_surface_is_unique(surface: *const sk_surface_t) -> bool {
600    RefCounted::is_unique(surface)
601}
602
603/// Get the width of a surface.
604#[unsafe(no_mangle)]
605pub unsafe extern "C" fn sk_surface_get_width(surface: *const sk_surface_t) -> i32 {
606    RefCounted::get_ref(surface).map_or(0, |s| s.width())
607}
608
609/// Get the height of a surface.
610#[unsafe(no_mangle)]
611pub unsafe extern "C" fn sk_surface_get_height(surface: *const sk_surface_t) -> i32 {
612    RefCounted::get_ref(surface).map_or(0, |s| s.height())
613}
614
615/// Get the pixel data from a surface.
616#[unsafe(no_mangle)]
617pub unsafe extern "C" fn sk_surface_peek_pixels(
618    surface: *const sk_surface_t,
619    out_pixels: *mut *const u8,
620    out_row_bytes: *mut usize,
621) -> bool {
622    if out_pixels.is_null() || out_row_bytes.is_null() {
623        return false;
624    }
625
626    if let Some(s) = RefCounted::get_ref(surface) {
627        *out_pixels = s.pixels().as_ptr();
628        *out_row_bytes = s.row_bytes();
629        true
630    } else {
631        false
632    }
633}
634
635// =============================================================================
636// Paint API (Reference Counted)
637// =============================================================================
638
639/// Reference counted paint type.
640pub type sk_paint_t = RefCounted<Paint>;
641
642/// Create a new paint.
643///
644/// Returns a paint with refcount of 1.
645#[unsafe(no_mangle)]
646pub unsafe extern "C" fn sk_paint_new() -> *mut sk_paint_t {
647    catch_panic(|| RefCounted::new(Paint::new()))
648}
649
650/// Clone a paint.
651///
652/// Returns a new paint with refcount of 1.
653#[unsafe(no_mangle)]
654pub unsafe extern "C" fn sk_paint_clone(paint: *const sk_paint_t) -> *mut sk_paint_t {
655    RefCounted::get_ref(paint).map_or(ptr::null_mut(), |p| RefCounted::new(p.clone()))
656}
657
658/// Increment the reference count of a paint.
659#[unsafe(no_mangle)]
660pub unsafe extern "C" fn sk_paint_ref(paint: *mut sk_paint_t) {
661    RefCounted::ref_ptr(paint);
662}
663
664/// Decrement the reference count of a paint (alias for unref).
665#[unsafe(no_mangle)]
666pub unsafe extern "C" fn sk_paint_delete(paint: *mut sk_paint_t) {
667    RefCounted::unref_ptr(paint);
668}
669
670/// Decrement the reference count of a paint.
671///
672/// Frees the paint when the count reaches 0.
673#[unsafe(no_mangle)]
674pub unsafe extern "C" fn sk_paint_unref(paint: *mut sk_paint_t) {
675    RefCounted::unref_ptr(paint);
676}
677
678/// Get the reference count of a paint.
679#[unsafe(no_mangle)]
680pub unsafe extern "C" fn sk_paint_get_refcnt(paint: *const sk_paint_t) -> u32 {
681    RefCounted::get_count(paint)
682}
683
684/// Set the paint color.
685#[unsafe(no_mangle)]
686pub unsafe extern "C" fn sk_paint_set_color(paint: *mut sk_paint_t, color: sk_color_t) {
687    if let Some(p) = RefCounted::get_mut(paint) {
688        p.set_color32(Color(color));
689    }
690}
691
692/// Get the paint color.
693#[unsafe(no_mangle)]
694pub unsafe extern "C" fn sk_paint_get_color(paint: *const sk_paint_t) -> sk_color_t {
695    RefCounted::get_ref(paint).map_or(0, |p| p.color32().0)
696}
697
698/// Set the paint style.
699#[unsafe(no_mangle)]
700pub unsafe extern "C" fn sk_paint_set_style(paint: *mut sk_paint_t, style: u32) {
701    if let Some(p) = RefCounted::get_mut(paint) {
702        let style = match style {
703            0 => Style::Fill,
704            1 => Style::Stroke,
705            2 => Style::StrokeAndFill,
706            _ => Style::Fill,
707        };
708        p.set_style(style);
709    }
710}
711
712/// Set the stroke width.
713#[unsafe(no_mangle)]
714pub unsafe extern "C" fn sk_paint_set_stroke_width(paint: *mut sk_paint_t, width: f32) {
715    if let Some(p) = RefCounted::get_mut(paint) {
716        p.set_stroke_width(width);
717    }
718}
719
720/// Get the stroke width.
721#[unsafe(no_mangle)]
722pub unsafe extern "C" fn sk_paint_get_stroke_width(paint: *const sk_paint_t) -> f32 {
723    RefCounted::get_ref(paint).map_or(0.0, |p| p.stroke_width())
724}
725
726/// Set anti-alias.
727#[unsafe(no_mangle)]
728pub unsafe extern "C" fn sk_paint_set_antialias(paint: *mut sk_paint_t, aa: bool) {
729    if let Some(p) = RefCounted::get_mut(paint) {
730        p.set_anti_alias(aa);
731    }
732}
733
734/// Check if anti-alias is enabled.
735#[unsafe(no_mangle)]
736pub unsafe extern "C" fn sk_paint_is_antialias(paint: *const sk_paint_t) -> bool {
737    RefCounted::get_ref(paint).map_or(false, |p| p.is_anti_alias())
738}
739
740// =============================================================================
741// Path API (Reference Counted)
742// =============================================================================
743
744/// Reference counted path type.
745pub type sk_path_t = RefCounted<Path>;
746
747/// Create a new path.
748///
749/// Returns a path with refcount of 1.
750#[unsafe(no_mangle)]
751pub unsafe extern "C" fn sk_path_new() -> *mut sk_path_t {
752    RefCounted::new(Path::new())
753}
754
755/// Clone a path.
756///
757/// Returns a new path with refcount of 1.
758#[unsafe(no_mangle)]
759pub unsafe extern "C" fn sk_path_clone(path: *const sk_path_t) -> *mut sk_path_t {
760    RefCounted::get_ref(path).map_or(ptr::null_mut(), |p| RefCounted::new(p.clone()))
761}
762
763/// Increment the reference count of a path.
764#[unsafe(no_mangle)]
765pub unsafe extern "C" fn sk_path_ref(path: *mut sk_path_t) {
766    RefCounted::ref_ptr(path);
767}
768
769/// Decrement the reference count of a path (alias for unref).
770#[unsafe(no_mangle)]
771pub unsafe extern "C" fn sk_path_delete(path: *mut sk_path_t) {
772    RefCounted::unref_ptr(path);
773}
774
775/// Decrement the reference count of a path.
776///
777/// Frees the path when the count reaches 0.
778#[unsafe(no_mangle)]
779pub unsafe extern "C" fn sk_path_unref(path: *mut sk_path_t) {
780    RefCounted::unref_ptr(path);
781}
782
783/// Get the reference count of a path.
784#[unsafe(no_mangle)]
785pub unsafe extern "C" fn sk_path_get_refcnt(path: *const sk_path_t) -> u32 {
786    RefCounted::get_count(path)
787}
788
789/// Get the path bounds.
790#[unsafe(no_mangle)]
791pub unsafe extern "C" fn sk_path_get_bounds(path: *const sk_path_t, bounds: *mut sk_rect_t) {
792    if let (Some(p), Some(b)) = (RefCounted::get_ref(path), bounds.as_mut()) {
793        let rect = p.bounds();
794        *b = rect.into();
795    }
796}
797
798/// Check if path is empty.
799#[unsafe(no_mangle)]
800pub unsafe extern "C" fn sk_path_is_empty(path: *const sk_path_t) -> bool {
801    RefCounted::get_ref(path).map_or(true, |p| p.is_empty())
802}
803
804/// Get the fill type.
805#[unsafe(no_mangle)]
806pub unsafe extern "C" fn sk_path_get_filltype(path: *const sk_path_t) -> u32 {
807    RefCounted::get_ref(path).map_or(0, |p| match p.fill_type() {
808        FillType::Winding => 0,
809        FillType::EvenOdd => 1,
810        FillType::InverseWinding => 2,
811        FillType::InverseEvenOdd => 3,
812    })
813}
814
815/// Set the fill type.
816#[unsafe(no_mangle)]
817pub unsafe extern "C" fn sk_path_set_filltype(path: *mut sk_path_t, fill_type: u32) {
818    if let Some(p) = RefCounted::get_mut(path) {
819        let ft = match fill_type {
820            0 => FillType::Winding,
821            1 => FillType::EvenOdd,
822            2 => FillType::InverseWinding,
823            3 => FillType::InverseEvenOdd,
824            _ => FillType::Winding,
825        };
826        p.set_fill_type(ft);
827    }
828}
829
830/// Check if path contains a point.
831#[unsafe(no_mangle)]
832pub unsafe extern "C" fn sk_path_contains(path: *const sk_path_t, x: f32, y: f32) -> bool {
833    RefCounted::get_ref(path).map_or(false, |p| p.contains(Point::new(x, y)))
834}
835
836// =============================================================================
837// Path Builder API (Reference Counted)
838// =============================================================================
839
840/// Reference counted path builder type.
841pub type sk_pathbuilder_t = RefCounted<PathBuilder>;
842
843/// Create a new path builder.
844///
845/// Returns a builder with refcount of 1.
846#[unsafe(no_mangle)]
847pub unsafe extern "C" fn sk_pathbuilder_new() -> *mut sk_pathbuilder_t {
848    RefCounted::new(PathBuilder::new())
849}
850
851/// Increment the reference count of a path builder.
852#[unsafe(no_mangle)]
853pub unsafe extern "C" fn sk_pathbuilder_ref(builder: *mut sk_pathbuilder_t) {
854    RefCounted::ref_ptr(builder);
855}
856
857/// Decrement the reference count of a path builder (alias for unref).
858#[unsafe(no_mangle)]
859pub unsafe extern "C" fn sk_pathbuilder_delete(builder: *mut sk_pathbuilder_t) {
860    RefCounted::unref_ptr(builder);
861}
862
863/// Decrement the reference count of a path builder.
864///
865/// Frees the builder when the count reaches 0.
866#[unsafe(no_mangle)]
867pub unsafe extern "C" fn sk_pathbuilder_unref(builder: *mut sk_pathbuilder_t) {
868    RefCounted::unref_ptr(builder);
869}
870
871/// Move to a point.
872#[unsafe(no_mangle)]
873pub unsafe extern "C" fn sk_pathbuilder_move_to(builder: *mut sk_pathbuilder_t, x: f32, y: f32) {
874    if let Some(b) = RefCounted::get_mut(builder) {
875        b.move_to(x, y);
876    }
877}
878
879/// Line to a point.
880#[unsafe(no_mangle)]
881pub unsafe extern "C" fn sk_pathbuilder_line_to(builder: *mut sk_pathbuilder_t, x: f32, y: f32) {
882    if let Some(b) = RefCounted::get_mut(builder) {
883        b.line_to(x, y);
884    }
885}
886
887/// Quadratic bezier to a point.
888#[unsafe(no_mangle)]
889pub unsafe extern "C" fn sk_pathbuilder_quad_to(
890    builder: *mut sk_pathbuilder_t,
891    cx: f32,
892    cy: f32,
893    x: f32,
894    y: f32,
895) {
896    if let Some(b) = RefCounted::get_mut(builder) {
897        b.quad_to(cx, cy, x, y);
898    }
899}
900
901/// Cubic bezier to a point.
902#[unsafe(no_mangle)]
903pub unsafe extern "C" fn sk_pathbuilder_cubic_to(
904    builder: *mut sk_pathbuilder_t,
905    c1x: f32,
906    c1y: f32,
907    c2x: f32,
908    c2y: f32,
909    x: f32,
910    y: f32,
911) {
912    if let Some(b) = RefCounted::get_mut(builder) {
913        b.cubic_to(c1x, c1y, c2x, c2y, x, y);
914    }
915}
916
917/// Close the path.
918#[unsafe(no_mangle)]
919pub unsafe extern "C" fn sk_pathbuilder_close(builder: *mut sk_pathbuilder_t) {
920    if let Some(b) = RefCounted::get_mut(builder) {
921        b.close();
922    }
923}
924
925/// Add a rectangle.
926#[unsafe(no_mangle)]
927pub unsafe extern "C" fn sk_pathbuilder_add_rect(
928    builder: *mut sk_pathbuilder_t,
929    rect: *const sk_rect_t,
930) {
931    if let (Some(b), Some(r)) = (RefCounted::get_mut(builder), rect.as_ref()) {
932        b.add_rect(&Rect::from(*r));
933    }
934}
935
936/// Add an oval.
937#[unsafe(no_mangle)]
938pub unsafe extern "C" fn sk_pathbuilder_add_oval(
939    builder: *mut sk_pathbuilder_t,
940    rect: *const sk_rect_t,
941) {
942    if let (Some(b), Some(r)) = (RefCounted::get_mut(builder), rect.as_ref()) {
943        b.add_oval(&Rect::from(*r));
944    }
945}
946
947/// Add a circle.
948#[unsafe(no_mangle)]
949pub unsafe extern "C" fn sk_pathbuilder_add_circle(
950    builder: *mut sk_pathbuilder_t,
951    cx: f32,
952    cy: f32,
953    radius: f32,
954) {
955    if let Some(b) = RefCounted::get_mut(builder) {
956        b.add_circle(cx, cy, radius);
957    }
958}
959
960/// Build the path and reset the builder.
961///
962/// Returns a new path with refcount of 1.
963/// The builder is reset and can be reused.
964#[unsafe(no_mangle)]
965pub unsafe extern "C" fn sk_pathbuilder_detach(builder: *mut sk_pathbuilder_t) -> *mut sk_path_t {
966    if let Some(b) = RefCounted::get_mut(builder) {
967        let path = std::mem::replace(b, PathBuilder::new()).build();
968        RefCounted::new(path)
969    } else {
970        ptr::null_mut()
971    }
972}
973
974/// Build the path without resetting the builder.
975///
976/// Returns a new path with refcount of 1.
977/// The builder retains its current state.
978#[unsafe(no_mangle)]
979pub unsafe extern "C" fn sk_pathbuilder_snapshot(
980    builder: *const sk_pathbuilder_t,
981) -> *mut sk_path_t {
982    RefCounted::get_ref(builder).map_or(ptr::null_mut(), |b| RefCounted::new(b.clone().build()))
983}
984
985// =============================================================================
986// Matrix API
987// =============================================================================
988
989/// Set matrix to identity.
990#[unsafe(no_mangle)]
991pub unsafe extern "C" fn sk_matrix_set_identity(matrix: *mut sk_matrix_t) {
992    if let Some(m) = matrix.as_mut() {
993        *m = sk_matrix_t::default();
994    }
995}
996
997/// Set matrix to translate.
998#[unsafe(no_mangle)]
999pub unsafe extern "C" fn sk_matrix_set_translate(matrix: *mut sk_matrix_t, dx: f32, dy: f32) {
1000    if let Some(m) = matrix.as_mut() {
1001        *m = Matrix::translate(dx, dy).into();
1002    }
1003}
1004
1005/// Set matrix to scale.
1006#[unsafe(no_mangle)]
1007pub unsafe extern "C" fn sk_matrix_set_scale(matrix: *mut sk_matrix_t, sx: f32, sy: f32) {
1008    if let Some(m) = matrix.as_mut() {
1009        *m = Matrix::scale(sx, sy).into();
1010    }
1011}
1012
1013/// Set matrix to rotate (degrees).
1014#[unsafe(no_mangle)]
1015pub unsafe extern "C" fn sk_matrix_set_rotate(matrix: *mut sk_matrix_t, degrees: f32) {
1016    if let Some(m) = matrix.as_mut() {
1017        let radians = degrees * std::f32::consts::PI / 180.0;
1018        *m = Matrix::rotate(radians).into();
1019    }
1020}
1021
1022/// Concatenate two matrices.
1023#[unsafe(no_mangle)]
1024pub unsafe extern "C" fn sk_matrix_concat(
1025    result: *mut sk_matrix_t,
1026    a: *const sk_matrix_t,
1027    b: *const sk_matrix_t,
1028) {
1029    if let (Some(r), Some(a), Some(b)) = (result.as_mut(), a.as_ref(), b.as_ref()) {
1030        let ma: Matrix = (*a).into();
1031        let mb: Matrix = (*b).into();
1032        *r = ma.concat(&mb).into();
1033    }
1034}
1035
1036/// Map a point through a matrix.
1037#[unsafe(no_mangle)]
1038pub unsafe extern "C" fn sk_matrix_map_point(
1039    matrix: *const sk_matrix_t,
1040    point: *const sk_point_t,
1041    result: *mut sk_point_t,
1042) {
1043    if let (Some(m), Some(p), Some(r)) = (matrix.as_ref(), point.as_ref(), result.as_mut()) {
1044        let mat: Matrix = (*m).into();
1045        let pt: Point = (*p).into();
1046        *r = mat.map_point(pt).into();
1047    }
1048}
1049
1050// =============================================================================
1051// Utility functions
1052// =============================================================================
1053
1054/// Get the library version.
1055#[unsafe(no_mangle)]
1056pub unsafe extern "C" fn sk_version() -> *const c_char {
1057    static VERSION: &[u8] = b"skia-rs 0.1.0\0";
1058    VERSION.as_ptr() as *const c_char
1059}
1060
1061/// Check if the library is available.
1062#[unsafe(no_mangle)]
1063pub unsafe extern "C" fn sk_is_available() -> bool {
1064    true
1065}
1066
1067// =============================================================================
1068// Drawing helpers (simplified)
1069// =============================================================================
1070
1071/// Clear a surface with a color.
1072#[unsafe(no_mangle)]
1073pub unsafe extern "C" fn sk_surface_clear(surface: *mut sk_surface_t, color: sk_color_t) {
1074    if let Some(s) = RefCounted::get_mut(surface) {
1075        let mut canvas = s.raster_canvas();
1076        canvas.clear(Color(color));
1077    }
1078}
1079
1080/// Draw a rect on a surface.
1081#[unsafe(no_mangle)]
1082pub unsafe extern "C" fn sk_surface_draw_rect(
1083    surface: *mut sk_surface_t,
1084    rect: *const sk_rect_t,
1085    paint: *const sk_paint_t,
1086) {
1087    if let (Some(s), Some(r), Some(p)) = (
1088        RefCounted::get_mut(surface),
1089        rect.as_ref(),
1090        RefCounted::get_ref(paint),
1091    ) {
1092        let mut canvas = s.raster_canvas();
1093        canvas.draw_rect(&Rect::from(*r), p);
1094    }
1095}
1096
1097/// Draw a circle on a surface.
1098#[unsafe(no_mangle)]
1099pub unsafe extern "C" fn sk_surface_draw_circle(
1100    surface: *mut sk_surface_t,
1101    cx: f32,
1102    cy: f32,
1103    radius: f32,
1104    paint: *const sk_paint_t,
1105) {
1106    if let (Some(s), Some(p)) = (RefCounted::get_mut(surface), RefCounted::get_ref(paint)) {
1107        let mut canvas = s.raster_canvas();
1108        canvas.draw_circle(Point::new(cx, cy), radius, p);
1109    }
1110}
1111
1112/// Draw a path on a surface.
1113#[unsafe(no_mangle)]
1114pub unsafe extern "C" fn sk_surface_draw_path(
1115    surface: *mut sk_surface_t,
1116    path: *const sk_path_t,
1117    paint: *const sk_paint_t,
1118) {
1119    if let (Some(s), Some(path), Some(p)) = (
1120        RefCounted::get_mut(surface),
1121        RefCounted::get_ref(path),
1122        RefCounted::get_ref(paint),
1123    ) {
1124        let mut canvas = s.raster_canvas();
1125        canvas.draw_path(path, p);
1126    }
1127}
1128
1129/// Draw a line on a surface.
1130#[unsafe(no_mangle)]
1131pub unsafe extern "C" fn sk_surface_draw_line(
1132    surface: *mut sk_surface_t,
1133    x0: f32,
1134    y0: f32,
1135    x1: f32,
1136    y1: f32,
1137    paint: *const sk_paint_t,
1138) {
1139    if let (Some(s), Some(p)) = (RefCounted::get_mut(surface), RefCounted::get_ref(paint)) {
1140        let mut canvas = s.raster_canvas();
1141        canvas.draw_line(Point::new(x0, y0), Point::new(x1, y1), p);
1142    }
1143}
1144
1145#[cfg(test)]
1146mod tests {
1147    use super::*;
1148
1149    #[test]
1150    fn test_surface_creation() {
1151        unsafe {
1152            let surface = sk_surface_new_raster(100, 100);
1153            assert!(!surface.is_null());
1154            assert_eq!(sk_surface_get_width(surface), 100);
1155            assert_eq!(sk_surface_get_height(surface), 100);
1156            assert_eq!(sk_surface_get_refcnt(surface), 1);
1157            sk_surface_unref(surface);
1158        }
1159    }
1160
1161    #[test]
1162    fn test_surface_refcounting() {
1163        unsafe {
1164            let surface = sk_surface_new_raster(100, 100);
1165            assert_eq!(sk_surface_get_refcnt(surface), 1);
1166            assert!(sk_surface_is_unique(surface));
1167
1168            sk_surface_ref(surface);
1169            assert_eq!(sk_surface_get_refcnt(surface), 2);
1170            assert!(!sk_surface_is_unique(surface));
1171
1172            sk_surface_unref(surface);
1173            assert_eq!(sk_surface_get_refcnt(surface), 1);
1174            assert!(sk_surface_is_unique(surface));
1175
1176            sk_surface_unref(surface); // This frees it
1177        }
1178    }
1179
1180    #[test]
1181    fn test_paint_operations() {
1182        unsafe {
1183            let paint = sk_paint_new();
1184            assert!(!paint.is_null());
1185            assert_eq!(sk_paint_get_refcnt(paint), 1);
1186
1187            sk_paint_set_color(paint, 0xFF0000FF); // Blue
1188            assert_eq!(sk_paint_get_color(paint), 0xFF0000FF);
1189
1190            sk_paint_set_stroke_width(paint, 2.0);
1191            assert_eq!(sk_paint_get_stroke_width(paint), 2.0);
1192
1193            sk_paint_delete(paint);
1194        }
1195    }
1196
1197    #[test]
1198    fn test_paint_refcounting() {
1199        unsafe {
1200            let paint = sk_paint_new();
1201            assert_eq!(sk_paint_get_refcnt(paint), 1);
1202
1203            sk_paint_ref(paint);
1204            assert_eq!(sk_paint_get_refcnt(paint), 2);
1205
1206            sk_paint_unref(paint);
1207            assert_eq!(sk_paint_get_refcnt(paint), 1);
1208
1209            // Clone should create a new object with refcount 1
1210            let paint2 = sk_paint_clone(paint);
1211            assert_eq!(sk_paint_get_refcnt(paint), 1);
1212            assert_eq!(sk_paint_get_refcnt(paint2), 1);
1213
1214            sk_paint_unref(paint);
1215            sk_paint_unref(paint2);
1216        }
1217    }
1218
1219    #[test]
1220    fn test_path_builder() {
1221        unsafe {
1222            let builder = sk_pathbuilder_new();
1223            assert!(!builder.is_null());
1224
1225            sk_pathbuilder_move_to(builder, 0.0, 0.0);
1226            sk_pathbuilder_line_to(builder, 100.0, 0.0);
1227            sk_pathbuilder_line_to(builder, 100.0, 100.0);
1228            sk_pathbuilder_close(builder);
1229
1230            let path = sk_pathbuilder_detach(builder);
1231            assert!(!path.is_null());
1232            assert!(!sk_path_is_empty(path));
1233            assert_eq!(sk_path_get_refcnt(path), 1);
1234
1235            let mut bounds = sk_rect_t::default();
1236            sk_path_get_bounds(path, &mut bounds);
1237            assert_eq!(bounds.left, 0.0);
1238            assert_eq!(bounds.right, 100.0);
1239
1240            sk_path_delete(path);
1241            sk_pathbuilder_delete(builder);
1242        }
1243    }
1244
1245    #[test]
1246    fn test_path_refcounting() {
1247        unsafe {
1248            let path = sk_path_new();
1249            assert_eq!(sk_path_get_refcnt(path), 1);
1250
1251            sk_path_ref(path);
1252            assert_eq!(sk_path_get_refcnt(path), 2);
1253
1254            let path2 = sk_path_clone(path);
1255            assert_eq!(sk_path_get_refcnt(path), 2);
1256            assert_eq!(sk_path_get_refcnt(path2), 1);
1257
1258            sk_path_unref(path);
1259            assert_eq!(sk_path_get_refcnt(path), 1);
1260
1261            sk_path_unref(path);
1262            sk_path_unref(path2);
1263        }
1264    }
1265
1266    #[test]
1267    fn test_matrix_operations() {
1268        unsafe {
1269            let mut matrix = sk_matrix_t::default();
1270            sk_matrix_set_translate(&mut matrix, 10.0, 20.0);
1271
1272            let point = sk_point_t { x: 0.0, y: 0.0 };
1273            let mut result = sk_point_t::default();
1274            sk_matrix_map_point(&matrix, &point, &mut result);
1275
1276            assert_eq!(result.x, 10.0);
1277            assert_eq!(result.y, 20.0);
1278        }
1279    }
1280
1281    #[test]
1282    fn test_draw_rect() {
1283        unsafe {
1284            let surface = sk_surface_new_raster(100, 100);
1285            let paint = sk_paint_new();
1286            sk_paint_set_color(paint, 0xFFFF0000); // Red
1287
1288            sk_surface_clear(surface, 0xFFFFFFFF); // White
1289
1290            let rect = sk_rect_t {
1291                left: 10.0,
1292                top: 10.0,
1293                right: 50.0,
1294                bottom: 50.0,
1295            };
1296            sk_surface_draw_rect(surface, &rect, paint);
1297
1298            sk_paint_delete(paint);
1299            sk_surface_unref(surface);
1300        }
1301    }
1302
1303    #[test]
1304    fn test_refcnt_utility() {
1305        unsafe {
1306            let surface = sk_surface_new_raster(100, 100);
1307
1308            // Test generic refcnt functions
1309            let ptr = surface as *const sk_refcnt_t;
1310            assert_eq!(sk_refcnt_get_count(ptr), 1);
1311            assert!(sk_refcnt_is_unique(ptr));
1312
1313            sk_surface_ref(surface);
1314            assert_eq!(sk_refcnt_get_count(ptr), 2);
1315            assert!(!sk_refcnt_is_unique(ptr));
1316
1317            sk_surface_unref(surface);
1318            sk_surface_unref(surface);
1319        }
1320    }
1321}