Skip to main content

skia_canvas/
lib.rs

1//! GPU-accelerated, multi-threaded HTML Canvas-compatible 2D rendering for
2//! Rust and Node, powered by [Skia].
3//!
4//! # Rust consumers: use the [`prelude`]
5//!
6//! The crate-root modules are the stable, supported Rust API; the
7//! [`prelude`] re-exports them all. Public signatures never expose
8//! `skia_safe` or `neon` types -- a compile-time pin verifies this. The
9//! Node/Neon binding lives under the internal `node` module.
10//!
11//! ```no_run
12//! use skia_canvas::prelude::*;
13//!
14//! # fn run() -> Result<(), skia_canvas::error::Error> {
15//! let backend = Backend::new();
16//! let mut surface = backend.create_surface(
17//!     1920,
18//!     1080,
19//!     SurfaceOptions {
20//!         color_space: LinearColorSpace::DisplayP3,
21//!         ..SurfaceOptions::default()
22//!     },
23//! )?;
24//!
25//! surface.with_canvas(|canvas| {
26//!     canvas.clear(RgbaLinear::new_premultiplied(0.0, 0.0, 0.0, 0.0));
27//!     canvas.draw_rect(
28//!         Rect::from_xywh(100.0, 100.0, 200.0, 100.0),
29//!         &Paint::fill(RgbaLinear::opaque(1.0, 0.0, 0.0)),
30//!     );
31//! });
32//!
33//! let frame = surface.read_pixels()?;
34//! # let _ = frame;
35//! # Ok(())
36//! # }
37//! ```
38//!
39//! See [`docs/api/native-rust.md`][api-doc] in the repository for a longer
40//! reference (color spaces, alpha semantics, surfaces, paint, paths, shaders,
41//! filters, images, text, fonts).
42//!
43//! # Cargo features
44//!
45//! - `vulkan` -- enable the Vulkan backend (Linux / Windows).
46//! - `metal` -- enable the Metal backend (macOS).
47//! - `window` -- enable the [`winit`]-backed GUI window/event loop.
48//! - `freetype` -- bundle FreeType + WOFF2 support for font registration on
49//!   Linux containers / minimal images.
50//! - `node-addon` -- register the `#[neon::main]` entry point so the resulting
51//!   cdylib loads as a Node.js addon. Pure-Rust consumers should leave this
52//!   off.
53//!
54//! Pure-Rust consumers typically depend with `default-features = false` and
55//! pick the backend they need:
56//!
57//! ```toml
58//! [dependencies]
59//! skia-canvas = { version = "0.2", default-features = false, features = ["vulkan", "freetype"] }
60//! ```
61//!
62//! [Skia]: https://skia.org
63//! [api-doc]: https://github.com/phyrondev/phyron-skia-canvas/blob/main/docs/api/native-rust.md
64//! [`winit`]: https://docs.rs/winit
65
66#![allow(unused_braces)]
67#![allow(clippy::unnecessary_wraps)]
68
69#[cfg(feature = "node-addon")]
70use neon::prelude::*;
71
72// The public Rust API. Each module sits at the crate root; `prelude`
73// re-exports their contents so `use skia_canvas::prelude::*;` needs no
74// module prefixes. Public signatures never expose `skia_safe` or `neon`
75// types -- the Node/Neon binding lives under the internal `node` module.
76pub mod backend;
77pub mod color;
78pub mod error;
79pub mod filter;
80pub mod font;
81pub mod geometry;
82pub mod image;
83pub mod paint;
84pub mod path;
85pub mod pixels;
86pub mod recorder;
87pub mod shader;
88pub mod surface;
89pub mod text;
90
91/// Glob-importable re-export of the whole public API:
92/// `use skia_canvas::prelude::*;`.
93pub mod prelude {
94    pub use crate::{
95        backend::*, color::*, error::*, filter::*, font::*, geometry::*,
96        image::*, paint::*, path::*, pixels::*, recorder::*, shader::*,
97        surface::*, text::*,
98    };
99}
100
101// Shared internal infrastructure (not part of the public API).
102pub(crate) mod context;
103pub(crate) mod gpu;
104
105/// winit-backed windowing, behind the `window` feature.
106#[cfg(feature = "window")]
107pub mod gui;
108
109// Node.js / Neon binding -- internal; intentionally leaks `skia_safe` /
110// `neon` types, so it is `pub(crate)` and never part of the public API.
111// `dead_code` is allowed across the subtree: many binding methods exist
112// for JS-surface symmetry and aren't all reachable from Rust (and `pub`
113// no longer keeps them "live" now that the module is crate-private).
114#[allow(dead_code)]
115pub(crate) mod node;
116
117// Surface the non-colliding binding modules at the crate root so the
118// binding code keeps using `crate::gradient`, `crate::utils`, etc. The
119// names that collide with a public root module (filter/image/path/shader)
120// belong to the public API; the binding refers to those via `crate::node`.
121#[allow(unused_imports)]
122pub(crate) use node::{
123    canvas, color_filter, font_library, gradient, image_filter, mask_filter,
124    paragraph, pattern, texture, typography, utils,
125};
126
127#[cfg(feature = "node-addon")]
128use context::api as ctx;
129
130/// Module-level function to get backend status without creating a canvas.
131/// Returns JSON string with renderer, api, device, driver, threads, and
132/// gpuAvailable fields.
133#[cfg(feature = "node-addon")]
134fn backend(mut cx: FunctionContext) -> JsResult<JsString> {
135    let status = gpu::get_backend_status();
136    Ok(cx.string(status.to_string()))
137}
138
139#[cfg(feature = "node-addon")]
140#[neon::main]
141fn main(mut cx: ModuleContext) -> NeonResult<()> {
142    // initialize thread pool w/ non-default size if requested
143    if let Ok(value) = std::env::var("SKIA_CANVAS_THREADS")
144        && let Ok(num) = value.parse::<usize>()
145        && let Err(e) = rayon::ThreadPoolBuilder::new()
146            .num_threads(num)
147            .build_global()
148    {
149        eprintln!("Warning: failed to set custom thread pool size: {}", e);
150    }
151
152    // -- Image -------------------------------------------------------------------------------------
153
154    cx.export_function("Image_new", node::image::new)?;
155    cx.export_function("Image_get_src", node::image::get_src)?;
156    cx.export_function("Image_set_src", node::image::set_src)?;
157    cx.export_function("Image_set_data", node::image::set_data)?;
158    cx.export_function("Image_get_width", node::image::get_width)?;
159    cx.export_function("Image_get_height", node::image::get_height)?;
160    cx.export_function("Image_get_complete", node::image::get_complete)?;
161    cx.export_function("Image_pixels", node::image::pixels)?;
162
163    // -- Path2D ------------------------------------------------------------------------------------
164
165    cx.export_function("Path2D_new", node::path::new)?;
166    cx.export_function("Path2D_from_path", node::path::from_path)?;
167    cx.export_function("Path2D_from_svg", node::path::from_svg)?;
168    cx.export_function("Path2D_addPath", node::path::addPath)?;
169    cx.export_function("Path2D_closePath", node::path::closePath)?;
170    cx.export_function("Path2D_moveTo", node::path::moveTo)?;
171    cx.export_function("Path2D_lineTo", node::path::lineTo)?;
172    cx.export_function("Path2D_bezierCurveTo", node::path::bezierCurveTo)?;
173    cx.export_function(
174        "Path2D_quadraticCurveTo",
175        node::path::quadraticCurveTo,
176    )?;
177    cx.export_function("Path2D_conicCurveTo", node::path::conicCurveTo)?;
178    cx.export_function("Path2D_arc", node::path::arc)?;
179    cx.export_function("Path2D_arcTo", node::path::arcTo)?;
180    cx.export_function("Path2D_ellipse", node::path::ellipse)?;
181    cx.export_function("Path2D_rect", node::path::rect)?;
182    cx.export_function("Path2D_roundRect", node::path::roundRect)?;
183    cx.export_function("Path2D_op", node::path::op)?;
184    cx.export_function("Path2D_interpolate", node::path::interpolate)?;
185    cx.export_function("Path2D_simplify", node::path::simplify)?;
186    cx.export_function("Path2D_unwind", node::path::unwind)?;
187    cx.export_function("Path2D_round", node::path::round)?;
188    cx.export_function("Path2D_trim", node::path::trim)?;
189    cx.export_function("Path2D_jitter", node::path::jitter)?;
190    cx.export_function("Path2D_offset", node::path::offset)?;
191    cx.export_function("Path2D_transform", node::path::transform)?;
192    cx.export_function("Path2D_bounds", node::path::bounds)?;
193    cx.export_function("Path2D_contains", node::path::contains)?;
194    cx.export_function("Path2D_edges", node::path::edges)?;
195    cx.export_function("Path2D_get_d", node::path::get_d)?;
196    cx.export_function("Path2D_set_d", node::path::set_d)?;
197
198    // -- CanvasGradient
199    // ----------------------------------------------------------------------------
200
201    cx.export_function("CanvasGradient_linear", gradient::linear)?;
202    cx.export_function("CanvasGradient_radial", gradient::radial)?;
203    cx.export_function("CanvasGradient_conic", gradient::conic)?;
204    cx.export_function("CanvasGradient_addColorStop", gradient::addColorStop)?;
205    cx.export_function("CanvasGradient_repr", gradient::repr)?;
206    cx.export_function(
207        "CanvasGradient_get_interpolation",
208        gradient::get_interpolation,
209    )?;
210    cx.export_function(
211        "CanvasGradient_set_interpolation",
212        gradient::set_interpolation,
213    )?;
214    cx.export_function(
215        "CanvasGradient_get_hueInterpolation",
216        gradient::get_hueInterpolation,
217    )?;
218    cx.export_function(
219        "CanvasGradient_set_hueInterpolation",
220        gradient::set_hueInterpolation,
221    )?;
222
223    // -- CanvasPattern
224    // -----------------------------------------------------------------------------
225
226    cx.export_function("CanvasPattern_from_image", pattern::from_image)?;
227    cx.export_function(
228        "CanvasPattern_from_image_data",
229        pattern::from_image_data,
230    )?;
231    cx.export_function("CanvasPattern_from_canvas", pattern::from_canvas)?;
232    cx.export_function("CanvasPattern_setTransform", pattern::setTransform)?;
233    cx.export_function("CanvasPattern_repr", pattern::repr)?;
234
235    // -- CanvasTexture
236    // -----------------------------------------------------------------------------
237
238    cx.export_function("CanvasTexture_new", texture::new)?;
239    cx.export_function("CanvasTexture_repr", texture::repr)?;
240
241    // -- ColorFilter
242    // -------------------------------------------------------------------------------
243
244    cx.export_function("ColorFilter_makeMatrix", color_filter::makeMatrix)?;
245    cx.export_function(
246        "ColorFilter_makeSRGBToLinearGamma",
247        color_filter::makeSRGBToLinearGamma,
248    )?;
249    cx.export_function(
250        "ColorFilter_makeLinearToSRGBGamma",
251        color_filter::makeLinearToSRGBGamma,
252    )?;
253    cx.export_function("ColorFilter_makeBlend", color_filter::makeBlend)?;
254    cx.export_function("ColorFilter_makeCompose", color_filter::makeCompose)?;
255    cx.export_function("ColorFilter_makeLerp", color_filter::makeLerp)?;
256    cx.export_function(
257        "ColorFilter_makeHSLAMatrix",
258        color_filter::makeHSLAMatrix,
259    )?;
260    cx.export_function("ColorFilter_makeLighting", color_filter::makeLighting)?;
261    cx.export_function(
262        "ColorFilter_makeLumaColorFilter",
263        color_filter::makeLumaColorFilter,
264    )?;
265    cx.export_function("ColorFilter_makeTable", color_filter::makeTable)?;
266    cx.export_function(
267        "ColorFilter_makeTableARGB",
268        color_filter::makeTableARGB,
269    )?;
270    cx.export_function("ColorFilter_repr", color_filter::repr)?;
271    cx.export_function("ColorFilter_delete", color_filter::delete)?;
272
273    // -- ImageFilter
274    // -------------------------------------------------------------------------------
275
276    cx.export_function(
277        "ImageFilter_makeColorFilter",
278        image_filter::makeColorFilter,
279    )?;
280    cx.export_function("ImageFilter_makeCompose", image_filter::makeCompose)?;
281    cx.export_function("ImageFilter_makeBlur", image_filter::makeBlur)?;
282    cx.export_function(
283        "ImageFilter_makeDropShadow",
284        image_filter::makeDropShadow,
285    )?;
286    cx.export_function(
287        "ImageFilter_makeDropShadowOnly",
288        image_filter::makeDropShadowOnly,
289    )?;
290    cx.export_function("ImageFilter_makeOffset", image_filter::makeOffset)?;
291    cx.export_function("ImageFilter_makeDilate", image_filter::makeDilate)?;
292    cx.export_function("ImageFilter_makeErode", image_filter::makeErode)?;
293    cx.export_function("ImageFilter_makeMerge", image_filter::makeMerge)?;
294    cx.export_function("ImageFilter_makeEmpty", image_filter::makeEmpty)?;
295    cx.export_function("ImageFilter_makeTile", image_filter::makeTile)?;
296    // Advanced ImageFilter methods
297    cx.export_function("ImageFilter_makeBlend", image_filter::makeBlend)?;
298    cx.export_function(
299        "ImageFilter_makeArithmetic",
300        image_filter::makeArithmetic,
301    )?;
302    cx.export_function(
303        "ImageFilter_makeDisplacementMap",
304        image_filter::makeDisplacementMap,
305    )?;
306    cx.export_function(
307        "ImageFilter_makeMatrixConvolution",
308        image_filter::makeMatrixConvolution,
309    )?;
310    cx.export_function(
311        "ImageFilter_makeMatrixTransform",
312        image_filter::makeMatrixTransform,
313    )?;
314    cx.export_function(
315        "ImageFilter_makeMagnifier",
316        image_filter::makeMagnifier,
317    )?;
318    cx.export_function("ImageFilter_makeCrop", image_filter::makeCrop)?;
319    // Lighting ImageFilter methods
320    cx.export_function(
321        "ImageFilter_makeDistantLitDiffuse",
322        image_filter::makeDistantLitDiffuse,
323    )?;
324    cx.export_function(
325        "ImageFilter_makePointLitDiffuse",
326        image_filter::makePointLitDiffuse,
327    )?;
328    cx.export_function(
329        "ImageFilter_makeSpotLitDiffuse",
330        image_filter::makeSpotLitDiffuse,
331    )?;
332    cx.export_function(
333        "ImageFilter_makeDistantLitSpecular",
334        image_filter::makeDistantLitSpecular,
335    )?;
336    cx.export_function(
337        "ImageFilter_makePointLitSpecular",
338        image_filter::makePointLitSpecular,
339    )?;
340    cx.export_function(
341        "ImageFilter_makeSpotLitSpecular",
342        image_filter::makeSpotLitSpecular,
343    )?;
344    cx.export_function("ImageFilter_repr", image_filter::repr)?;
345    cx.export_function("ImageFilter_delete", image_filter::delete)?;
346
347    // -- MaskFilter
348    // --------------------------------------------------------------------
349
350    cx.export_function("MaskFilter_makeBlur", mask_filter::makeBlur)?;
351    cx.export_function("MaskFilter_delete", mask_filter::delete)?;
352
353    // -- Shader
354    // --------------------------------------------------------------------
355
356    cx.export_function(
357        "Shader_makeFractalNoise",
358        node::shader::makeFractalNoise,
359    )?;
360    cx.export_function("Shader_makeTurbulence", node::shader::makeTurbulence)?;
361    cx.export_function("Shader_delete", node::shader::delete)?;
362
363    // -- FontLibrary
364    // -------------------------------------------------------------------------------
365
366    cx.export_function("FontLibrary_get_families", font_library::get_families)?;
367    cx.export_function("FontLibrary_has", font_library::has)?;
368    cx.export_function("FontLibrary_family", font_library::family)?;
369    cx.export_function("FontLibrary_addFamily", font_library::addFamily)?;
370    cx.export_function(
371        "FontLibrary_addFamilyFromData",
372        font_library::addFamilyFromData,
373    )?;
374    cx.export_function("FontLibrary_reset", font_library::reset)?;
375
376    // -- ParagraphBuilder
377    // --------------------------------------------------------------------------
378
379    cx.export_function("ParagraphBuilder_new", paragraph::new)?;
380    cx.export_function("ParagraphBuilder_pushStyle", paragraph::pushStyle)?;
381    cx.export_function("ParagraphBuilder_pop", paragraph::pop)?;
382    cx.export_function("ParagraphBuilder_addText", paragraph::addText)?;
383    cx.export_function(
384        "ParagraphBuilder_addPlaceholder",
385        paragraph::addPlaceholder,
386    )?;
387    cx.export_function("ParagraphBuilder_build", paragraph::build)?;
388
389    // -- Paragraph
390    // ------------------------------------------------------------------------------
391
392    cx.export_function("Paragraph_layout", paragraph::layout)?;
393    cx.export_function("Paragraph_paint", paragraph::paint)?;
394    cx.export_function("Paragraph_getHeight", paragraph::getHeight)?;
395    cx.export_function("Paragraph_getLongestLine", paragraph::getLongestLine)?;
396    cx.export_function("Paragraph_getMaxWidth", paragraph::getMaxWidth)?;
397    cx.export_function(
398        "Paragraph_getMaxIntrinsicWidth",
399        paragraph::getMaxIntrinsicWidth,
400    )?;
401    cx.export_function(
402        "Paragraph_getMinIntrinsicWidth",
403        paragraph::getMinIntrinsicWidth,
404    )?;
405    cx.export_function(
406        "Paragraph_getAlphabeticBaseline",
407        paragraph::getAlphabeticBaseline,
408    )?;
409    cx.export_function(
410        "Paragraph_getIdeographicBaseline",
411        paragraph::getIdeographicBaseline,
412    )?;
413    cx.export_function("Paragraph_getLineMetrics", paragraph::getLineMetrics)?;
414    cx.export_function(
415        "Paragraph_getGlyphPositionAtCoordinate",
416        paragraph::getGlyphPositionAtCoordinate,
417    )?;
418    cx.export_function(
419        "Paragraph_getRectsForRange",
420        paragraph::getRectsForRange,
421    )?;
422    cx.export_function(
423        "Paragraph_didExceedMaxLines",
424        paragraph::didExceedMaxLines,
425    )?;
426    cx.export_function(
427        "Paragraph_getNumberOfLines",
428        paragraph::getNumberOfLines,
429    )?;
430    cx.export_function(
431        "Paragraph_getRectsForPlaceholders",
432        paragraph::getRectsForPlaceholders,
433    )?;
434    cx.export_function(
435        "Paragraph_getUnresolvedCodepoints",
436        paragraph::getUnresolvedCodepoints,
437    )?;
438
439    // -- Backend (module-level)
440    // --------------------------------------------------------------------
441
442    cx.export_function("backend", backend)?;
443
444    // -- Canvas ------------------------------------------------------------------------------------
445
446    cx.export_function("Canvas_new", canvas::new)?;
447
448    cx.export_function("Canvas_get_engine", canvas::get_engine)?;
449    cx.export_function("Canvas_set_engine", canvas::set_engine)?;
450    cx.export_function("Canvas_get_engine_status", canvas::get_engine_status)?;
451
452    cx.export_function("Canvas_get_width", canvas::get_width)?;
453    cx.export_function("Canvas_set_width", canvas::set_width)?;
454    cx.export_function("Canvas_get_height", canvas::get_height)?;
455    cx.export_function("Canvas_set_height", canvas::set_height)?;
456
457    cx.export_function("Canvas_save", canvas::save)?;
458    cx.export_function("Canvas_saveSync", canvas::saveSync)?;
459    cx.export_function("Canvas_toBuffer", canvas::toBuffer)?;
460    cx.export_function("Canvas_toBufferSync", canvas::toBufferSync)?;
461
462    // -- Context
463    // -----------------------------------------------------------------------------------
464
465    cx.export_function("CanvasRenderingContext2D_new", ctx::new)?;
466    cx.export_function("CanvasRenderingContext2D_resetSize", ctx::resetSize)?;
467    cx.export_function("CanvasRenderingContext2D_get_size", ctx::get_size)?;
468    cx.export_function("CanvasRenderingContext2D_set_size", ctx::set_size)?;
469    cx.export_function("CanvasRenderingContext2D_reset", ctx::reset)?;
470
471    // grid state
472    cx.export_function("CanvasRenderingContext2D_save", ctx::save)?;
473    cx.export_function("CanvasRenderingContext2D_restore", ctx::restore)?;
474    cx.export_function("CanvasRenderingContext2D_saveLayer", ctx::saveLayer)?;
475    cx.export_function("CanvasRenderingContext2D_transform", ctx::transform)?;
476    cx.export_function("CanvasRenderingContext2D_translate", ctx::translate)?;
477    cx.export_function("CanvasRenderingContext2D_scale", ctx::scale)?;
478    cx.export_function("CanvasRenderingContext2D_rotate", ctx::rotate)?;
479    cx.export_function(
480        "CanvasRenderingContext2D_resetTransform",
481        ctx::resetTransform,
482    )?;
483    cx.export_function(
484        "CanvasRenderingContext2D_get_currentTransform",
485        ctx::get_currentTransform,
486    )?;
487    cx.export_function(
488        "CanvasRenderingContext2D_set_currentTransform",
489        ctx::set_currentTransform,
490    )?;
491    cx.export_function(
492        "CanvasRenderingContext2D_createProjection",
493        ctx::createProjection,
494    )?;
495
496    // bézier paths
497    cx.export_function("CanvasRenderingContext2D_beginPath", ctx::beginPath)?;
498    cx.export_function("CanvasRenderingContext2D_rect", ctx::rect)?;
499    cx.export_function("CanvasRenderingContext2D_roundRect", ctx::roundRect)?;
500    cx.export_function("CanvasRenderingContext2D_arc", ctx::arc)?;
501    cx.export_function("CanvasRenderingContext2D_ellipse", ctx::ellipse)?;
502    cx.export_function("CanvasRenderingContext2D_moveTo", ctx::moveTo)?;
503    cx.export_function("CanvasRenderingContext2D_lineTo", ctx::lineTo)?;
504    cx.export_function("CanvasRenderingContext2D_arcTo", ctx::arcTo)?;
505    cx.export_function(
506        "CanvasRenderingContext2D_bezierCurveTo",
507        ctx::bezierCurveTo,
508    )?;
509    cx.export_function(
510        "CanvasRenderingContext2D_quadraticCurveTo",
511        ctx::quadraticCurveTo,
512    )?;
513    cx.export_function(
514        "CanvasRenderingContext2D_conicCurveTo",
515        ctx::conicCurveTo,
516    )?;
517    cx.export_function("CanvasRenderingContext2D_closePath", ctx::closePath)?;
518    cx.export_function(
519        "CanvasRenderingContext2D_isPointInPath",
520        ctx::isPointInPath,
521    )?;
522    cx.export_function(
523        "CanvasRenderingContext2D_isPointInStroke",
524        ctx::isPointInStroke,
525    )?;
526    cx.export_function("CanvasRenderingContext2D_clip", ctx::clip)?;
527
528    // fill & stroke
529    cx.export_function("CanvasRenderingContext2D_fill", ctx::fill)?;
530    cx.export_function("CanvasRenderingContext2D_stroke", ctx::stroke)?;
531    cx.export_function("CanvasRenderingContext2D_fillRect", ctx::fillRect)?;
532    cx.export_function("CanvasRenderingContext2D_strokeRect", ctx::strokeRect)?;
533    cx.export_function("CanvasRenderingContext2D_clearRect", ctx::clearRect)?;
534    cx.export_function(
535        "CanvasRenderingContext2D_get_fillStyle",
536        ctx::get_fillStyle,
537    )?;
538    cx.export_function(
539        "CanvasRenderingContext2D_set_fillStyle",
540        ctx::set_fillStyle,
541    )?;
542    cx.export_function(
543        "CanvasRenderingContext2D_get_strokeStyle",
544        ctx::get_strokeStyle,
545    )?;
546    cx.export_function(
547        "CanvasRenderingContext2D_set_strokeStyle",
548        ctx::set_strokeStyle,
549    )?;
550
551    // line style
552    cx.export_function(
553        "CanvasRenderingContext2D_getLineDash",
554        ctx::getLineDash,
555    )?;
556    cx.export_function(
557        "CanvasRenderingContext2D_setLineDash",
558        ctx::setLineDash,
559    )?;
560    cx.export_function(
561        "CanvasRenderingContext2D_get_lineCap",
562        ctx::get_lineCap,
563    )?;
564    cx.export_function(
565        "CanvasRenderingContext2D_set_lineCap",
566        ctx::set_lineCap,
567    )?;
568    cx.export_function(
569        "CanvasRenderingContext2D_get_lineDashFit",
570        ctx::get_lineDashFit,
571    )?;
572    cx.export_function(
573        "CanvasRenderingContext2D_set_lineDashFit",
574        ctx::set_lineDashFit,
575    )?;
576    cx.export_function(
577        "CanvasRenderingContext2D_get_lineDashMarker",
578        ctx::get_lineDashMarker,
579    )?;
580    cx.export_function(
581        "CanvasRenderingContext2D_set_lineDashMarker",
582        ctx::set_lineDashMarker,
583    )?;
584    cx.export_function(
585        "CanvasRenderingContext2D_get_lineDashOffset",
586        ctx::get_lineDashOffset,
587    )?;
588    cx.export_function(
589        "CanvasRenderingContext2D_set_lineDashOffset",
590        ctx::set_lineDashOffset,
591    )?;
592    cx.export_function(
593        "CanvasRenderingContext2D_get_lineJoin",
594        ctx::get_lineJoin,
595    )?;
596    cx.export_function(
597        "CanvasRenderingContext2D_set_lineJoin",
598        ctx::set_lineJoin,
599    )?;
600    cx.export_function(
601        "CanvasRenderingContext2D_get_lineWidth",
602        ctx::get_lineWidth,
603    )?;
604    cx.export_function(
605        "CanvasRenderingContext2D_set_lineWidth",
606        ctx::set_lineWidth,
607    )?;
608    cx.export_function(
609        "CanvasRenderingContext2D_get_miterLimit",
610        ctx::get_miterLimit,
611    )?;
612    cx.export_function(
613        "CanvasRenderingContext2D_set_miterLimit",
614        ctx::set_miterLimit,
615    )?;
616
617    // imagery
618    cx.export_function("CanvasRenderingContext2D_drawImage", ctx::drawImage)?;
619    cx.export_function("CanvasRenderingContext2D_drawCanvas", ctx::drawCanvas)?;
620    cx.export_function(
621        "CanvasRenderingContext2D_getImageData",
622        ctx::getImageData,
623    )?;
624    cx.export_function(
625        "CanvasRenderingContext2D_putImageData",
626        ctx::putImageData,
627    )?;
628    cx.export_function(
629        "CanvasRenderingContext2D_get_imageSmoothingEnabled",
630        ctx::get_imageSmoothingEnabled,
631    )?;
632    cx.export_function(
633        "CanvasRenderingContext2D_set_imageSmoothingEnabled",
634        ctx::set_imageSmoothingEnabled,
635    )?;
636    cx.export_function("CanvasRenderingContext2D_get_dither", ctx::get_dither)?;
637    cx.export_function("CanvasRenderingContext2D_set_dither", ctx::set_dither)?;
638    cx.export_function(
639        "CanvasRenderingContext2D_get_imageSmoothingQuality",
640        ctx::get_imageSmoothingQuality,
641    )?;
642    cx.export_function(
643        "CanvasRenderingContext2D_set_imageSmoothingQuality",
644        ctx::set_imageSmoothingQuality,
645    )?;
646
647    // typography
648    cx.export_function("CanvasRenderingContext2D_fillText", ctx::fillText)?;
649    cx.export_function("CanvasRenderingContext2D_strokeText", ctx::strokeText)?;
650    cx.export_function(
651        "CanvasRenderingContext2D_measureText",
652        ctx::measureText,
653    )?;
654    cx.export_function(
655        "CanvasRenderingContext2D_outlineText",
656        ctx::outlineText,
657    )?;
658    cx.export_function("CanvasRenderingContext2D_get_font", ctx::get_font)?;
659    cx.export_function("CanvasRenderingContext2D_set_font", ctx::set_font)?;
660    cx.export_function(
661        "CanvasRenderingContext2D_get_textAlign",
662        ctx::get_textAlign,
663    )?;
664    cx.export_function(
665        "CanvasRenderingContext2D_set_textAlign",
666        ctx::set_textAlign,
667    )?;
668    cx.export_function(
669        "CanvasRenderingContext2D_get_textBaseline",
670        ctx::get_textBaseline,
671    )?;
672    cx.export_function(
673        "CanvasRenderingContext2D_set_textBaseline",
674        ctx::set_textBaseline,
675    )?;
676    cx.export_function(
677        "CanvasRenderingContext2D_get_direction",
678        ctx::get_direction,
679    )?;
680    cx.export_function(
681        "CanvasRenderingContext2D_set_direction",
682        ctx::set_direction,
683    )?;
684    cx.export_function(
685        "CanvasRenderingContext2D_get_letterSpacing",
686        ctx::get_letterSpacing,
687    )?;
688    cx.export_function(
689        "CanvasRenderingContext2D_set_letterSpacing",
690        ctx::set_letterSpacing,
691    )?;
692    cx.export_function(
693        "CanvasRenderingContext2D_get_wordSpacing",
694        ctx::get_wordSpacing,
695    )?;
696    cx.export_function(
697        "CanvasRenderingContext2D_set_wordSpacing",
698        ctx::set_wordSpacing,
699    )?;
700    cx.export_function(
701        "CanvasRenderingContext2D_get_fontHinting",
702        ctx::get_fontHinting,
703    )?;
704    cx.export_function(
705        "CanvasRenderingContext2D_set_fontHinting",
706        ctx::set_fontHinting,
707    )?;
708    cx.export_function(
709        "CanvasRenderingContext2D_get_fontVariant",
710        ctx::get_fontVariant,
711    )?;
712    cx.export_function(
713        "CanvasRenderingContext2D_set_fontVariant",
714        ctx::set_fontVariant,
715    )?;
716    cx.export_function(
717        "CanvasRenderingContext2D_get_fontStretch",
718        ctx::get_fontStretch,
719    )?;
720    cx.export_function(
721        "CanvasRenderingContext2D_set_fontStretch",
722        ctx::set_fontStretch,
723    )?;
724    cx.export_function(
725        "CanvasRenderingContext2D_get_textWrap",
726        ctx::get_textWrap,
727    )?;
728    cx.export_function(
729        "CanvasRenderingContext2D_set_textWrap",
730        ctx::set_textWrap,
731    )?;
732    cx.export_function(
733        "CanvasRenderingContext2D_get_textDecoration",
734        ctx::get_textDecoration,
735    )?;
736    cx.export_function(
737        "CanvasRenderingContext2D_set_textDecoration",
738        ctx::set_textDecoration,
739    )?;
740    cx.export_function(
741        "CanvasRenderingContext2D_get_fontVariationSettings",
742        ctx::get_fontVariationSettings,
743    )?;
744    cx.export_function(
745        "CanvasRenderingContext2D_set_fontVariationSettings",
746        ctx::set_fontVariationSettings,
747    )?;
748
749    // effects
750    cx.export_function(
751        "CanvasRenderingContext2D_get_globalAlpha",
752        ctx::get_globalAlpha,
753    )?;
754    cx.export_function(
755        "CanvasRenderingContext2D_set_globalAlpha",
756        ctx::set_globalAlpha,
757    )?;
758    cx.export_function(
759        "CanvasRenderingContext2D_get_globalCompositeOperation",
760        ctx::get_globalCompositeOperation,
761    )?;
762    cx.export_function(
763        "CanvasRenderingContext2D_set_globalCompositeOperation",
764        ctx::set_globalCompositeOperation,
765    )?;
766    cx.export_function("CanvasRenderingContext2D_get_filter", ctx::get_filter)?;
767    cx.export_function("CanvasRenderingContext2D_set_filter", ctx::set_filter)?;
768    cx.export_function(
769        "CanvasRenderingContext2D_get_shadowBlur",
770        ctx::get_shadowBlur,
771    )?;
772    cx.export_function(
773        "CanvasRenderingContext2D_set_shadowBlur",
774        ctx::set_shadowBlur,
775    )?;
776    cx.export_function(
777        "CanvasRenderingContext2D_get_shadowColor",
778        ctx::get_shadowColor,
779    )?;
780    cx.export_function(
781        "CanvasRenderingContext2D_set_shadowColor",
782        ctx::set_shadowColor,
783    )?;
784    cx.export_function(
785        "CanvasRenderingContext2D_get_shadowOffsetX",
786        ctx::get_shadowOffsetX,
787    )?;
788    cx.export_function(
789        "CanvasRenderingContext2D_get_shadowOffsetY",
790        ctx::get_shadowOffsetY,
791    )?;
792    cx.export_function(
793        "CanvasRenderingContext2D_set_shadowOffsetX",
794        ctx::set_shadowOffsetX,
795    )?;
796    cx.export_function(
797        "CanvasRenderingContext2D_set_shadowOffsetY",
798        ctx::set_shadowOffsetY,
799    )?;
800
801    // Skia filter properties (CanvasKit parity)
802    cx.export_function(
803        "CanvasRenderingContext2D_get_colorFilter",
804        ctx::get_colorFilter,
805    )?;
806    cx.export_function(
807        "CanvasRenderingContext2D_set_colorFilter",
808        ctx::set_colorFilter,
809    )?;
810    cx.export_function(
811        "CanvasRenderingContext2D_get_skiaImageFilter",
812        ctx::get_skiaImageFilter,
813    )?;
814    cx.export_function(
815        "CanvasRenderingContext2D_set_skiaImageFilter",
816        ctx::set_skiaImageFilter,
817    )?;
818    cx.export_function(
819        "CanvasRenderingContext2D_get_skiaMaskFilter",
820        ctx::get_skiaMaskFilter,
821    )?;
822    cx.export_function(
823        "CanvasRenderingContext2D_set_skiaMaskFilter",
824        ctx::set_skiaMaskFilter,
825    )?;
826
827    // drawParagraph
828    cx.export_function(
829        "CanvasRenderingContext2D_drawParagraph",
830        paragraph::drawParagraph,
831    )?;
832
833    // -- Window -----------------------------------------------------------------------------------
834
835    #[cfg(feature = "window")]
836    {
837        cx.export_function("App_register", gui::register)?;
838        cx.export_function("App_activate", gui::activate)?;
839        cx.export_function("App_quit", gui::quit)?;
840        cx.export_function("App_closeWindow", gui::close)?;
841        cx.export_function("App_openWindow", gui::open)?;
842        cx.export_function("App_setRate", gui::set_rate)?;
843        cx.export_function("App_setMode", gui::set_mode)?;
844    }
845
846    Ok(())
847}