libnotcurses_sys/visual/
methods.rs

1//! `NcVisual*` methods and associated functions.
2
3use core::ptr::{null, null_mut};
4
5#[cfg(not(feature = "std"))]
6use alloc::format;
7
8use core::ffi::c_void;
9
10use crate::{
11    c_api::{self, NcResult_i32, NCRESULT_ERR},
12    cstring, error, error_ref_mut, Nc, NcBlitter, NcDirect, NcError, NcPalette, NcPixel, NcPlane,
13    NcResult, NcRgba, NcScale, NcTime, NcVisual, NcVisualGeometry, NcVisualOptions,
14};
15
16/// # NcVisual Constructors & destructors
17impl NcVisual {
18    /// Like [from_rgba][NcVisual#method.from_rgba], but 'bgra' is arranged as BGRA.
19    ///
20    /// *C style function: [ncvisual_from_bgra()][c_api::ncvisual_from_bgra].*
21    pub fn from_bgra<'a>(
22        bgra: &[u8],
23        rows: u32,
24        rowstride: u32,
25        cols: u32,
26    ) -> NcResult<&'a mut NcVisual> {
27        error_ref_mut![
28            unsafe {
29                c_api::ncvisual_from_bgra(
30                    bgra.as_ptr() as *const c_void,
31                    rows as i32,
32                    rowstride as i32,
33                    cols as i32,
34                )
35            },
36            &format![
37                "NcVisual::from_bgra(bgra, {}, {}, {})",
38                rows, rowstride, cols
39            ]
40        ]
41    }
42
43    /// Opens an `NcVisual` at `file`, extracts the codec and parameters and
44    /// decodes the first image to memory.
45    ///
46    /// *C style function: [ncvisual_from_file()][c_api::ncvisual_from_file].*
47    pub fn from_file<'a>(file: &str) -> NcResult<&'a mut NcVisual> {
48        let cs = cstring![file];
49        error_ref_mut![
50            unsafe { c_api::ncvisual_from_file(cs.as_ptr()) },
51            &format!("NcVisual::from_file({})", file)
52        ]
53    }
54
55    /// Promotes an `NcPlane` to an `NcVisual`.
56    ///
57    /// The plane may contain only spaces, half blocks, and full blocks.
58    /// This will be checked, and any other glyph will result in an error.
59    ///
60    /// This function exists so that planes can be subjected to `NcVisual`
61    /// transformations.
62    ///
63    /// If possible, it's better to create the `NcVisual` from memory using
64    /// [`from_rgba`][NcVisual#method.from_rgba].
65    ///
66    /// Use `None` for either or both of `beg_y` and `beg_x` in order to
67    /// use the current cursor position along that axis.
68    ///
69    /// Use `None` for either or both of `len_y` and `len_x` in order to
70    /// go through the boundary of the plane in that axis (same as `0`).
71    ///
72    /// *C style function: [ncvisual_from_plane()][c_api::ncvisual_from_plane].*
73    pub fn from_plane<'a>(
74        plane: &NcPlane,
75        blitter: impl Into<NcBlitter>,
76        beg_y: Option<u32>,
77        beg_x: Option<u32>,
78        len_y: Option<u32>,
79        len_x: Option<u32>,
80    ) -> NcResult<&'a mut NcVisual> {
81        let blitter = blitter.into();
82        error_ref_mut![
83            unsafe {
84                c_api::ncvisual_from_plane(
85                    plane,
86                    blitter.into(),
87                    beg_y.unwrap_or(u32::MAX) as i32,
88                    beg_x.unwrap_or(u32::MAX) as i32,
89                    len_y.unwrap_or(0),
90                    len_x.unwrap_or(0),
91                )
92            },
93            &format!(
94                "NcVisual::from_file(plane, {}, {:?}, {:?}, {:?}, {:?})",
95                blitter, beg_y, beg_x, len_y, len_x
96            )
97        ]
98    }
99
100    /// Constructs an `NcVisual` from a nul-terminated Sixel control `sequence`.
101    ///
102    /// *C style function: [ncvisual_from_sixel()][c_api::ncvisual_from_sixel].*
103    pub fn from_sixel<'a>(sequence: &str, len_y: u32, len_x: u32) -> NcResult<&'a mut NcVisual> {
104        let cs = cstring![sequence];
105        error_ref_mut![
106            unsafe { c_api::ncvisual_from_sixel(cs.as_ptr(), len_y, len_x) },
107            &format!("NcVisual::from_sixel({}, {}, {})", sequence, len_y, len_x)
108        ]
109    }
110
111    /// Like [`from_rgba`][NcVisual#method.from_rgba], but the pixels are
112    /// 4-byte RGBX. Alpha is filled in throughout using 'alpha'.
113    ///
114    /// `rowstride` must be a multiple of 4.
115    ///
116    /// *C style function: [ncvisual_from_rgb_loose()][c_api::ncvisual_from_rgb_loose].*
117    pub fn from_rgb_loose<'a>(
118        rgb: &[u8],
119        rows: u32,
120        rowstride: u32,
121        cols: u32,
122        alpha: u8,
123    ) -> NcResult<&'a mut NcVisual> {
124        error_ref_mut![
125            unsafe {
126                c_api::ncvisual_from_rgb_loose(
127                    rgb.as_ptr() as *const c_void,
128                    rows as i32,
129                    rowstride as i32,
130                    cols as i32,
131                    alpha as i32,
132                )
133            },
134            &format!(
135                "NcVisual::from_rgb_loose(rgba, {}, {}, {}, {})",
136                rows, rowstride, cols, alpha
137            )
138        ]
139    }
140
141    /// Like [`from_rgba`][NcVisual#method.from_rgba], but the pixels are
142    /// 3-byte RGB. Alpha is filled in throughout using 'alpha'.
143    ///
144    /// *C style function: [ncvisual_from_rgb_packed()][c_api::ncvisual_from_rgb_packed].*
145    pub fn from_rgb_packed<'a>(
146        rgb: &[u8],
147        rows: u32,
148        rowstride: u32,
149        cols: u32,
150        alpha: u8,
151    ) -> NcResult<&'a mut NcVisual> {
152        error_ref_mut![
153            unsafe {
154                c_api::ncvisual_from_rgb_packed(
155                    rgb.as_ptr() as *const c_void,
156                    rows as i32,
157                    rowstride as i32,
158                    cols as i32,
159                    alpha as i32,
160                )
161            },
162            &format!(
163                "NcVisual::from_rgb_packed(rgba, {}, {}, {}, {})",
164                rows, rowstride, cols, alpha
165            )
166        ]
167    }
168
169    /// Prepares an `NcVisual`, and its underlying `NcPlane`,
170    /// based off RGBA content in memory at `rgba`.
171    ///
172    /// `rgba` is laid out as `rows` lines, each of which is `rowstride` bytes
173    /// in length. Each line has `cols` 32-bit 8bpc RGBA pixels followed by
174    /// possible padding (there will be rowstride - cols * 4 bytes of padding).
175    ///
176    /// The total size of `rgba` is thus (rows * rowstride) bytes, of which
177    /// (rows * cols * 4) bytes are actual non-padding data.
178    ///
179    /// *C style function: [ncvisual_from_rgba()][c_api::ncvisual_from_rgba].*
180    pub fn from_rgba<'a>(
181        rgba: &[u8],
182        rows: u32,
183        rowstride: u32,
184        cols: u32,
185    ) -> NcResult<&'a mut NcVisual> {
186        error_ref_mut![
187            unsafe {
188                c_api::ncvisual_from_rgba(
189                    rgba.as_ptr() as *const c_void,
190                    rows as i32,
191                    rowstride as i32,
192                    cols as i32,
193                )
194            },
195            &format!(
196                "NcVisual::from_rgba(rgba, {}, {}, {})",
197                rows, rowstride, cols
198            )
199        ]
200    }
201
202    /// Like [`from_rgba`][NcVisual#method.from_rgba], but `data` is
203    /// `pstride`-byte palette-indexed pixels, arranged in `rows` lines of
204    /// `rowstride` bytes each, composed of `cols` pixels.
205    ///
206    /// `palette` is an array of at least `palsize` [`NcChannel`][crate::NcChannel]s.
207    ///
208    /// *C style function: [ncvisual_from_palidx()][c_api::ncvisual_from_palidx].*
209    pub fn from_palidx<'a>(
210        data: &[u8],
211        rows: u32,
212        rowstride: u32,
213        cols: u32,
214        //
215        palsize: u8,
216        pstride: u32,
217        palette: &NcPalette,
218    ) -> NcResult<&'a mut NcVisual> {
219        error_ref_mut![
220            unsafe {
221                c_api::ncvisual_from_palidx(
222                    data.as_ptr() as *const c_void,
223                    rows as i32,
224                    rowstride as i32,
225                    cols as i32,
226                    //
227                    palsize as i32,
228                    pstride as i32,
229                    palette.chans.as_slice().as_ptr(),
230                    //
231                    // palette.len(),
232                    //
233                )
234            },
235            &format!(
236                "NcVisual::from_palidx(data, {}, {}, {}, {}, {}, palette)",
237                rows, rowstride, cols, palsize, pstride
238            )
239        ]
240    }
241
242    /// Destroys this NcVisual.
243    ///
244    /// Rendered elements will not be disrupted, but the visual can be neither
245    /// decoded nor rendered any further.
246    ///
247    /// *C style function: [ncvisual_destroy()][c_api::ncvisual_destroy].*
248    pub fn destroy(&mut self) {
249        unsafe { c_api::ncvisual_destroy(self) }
250    }
251}
252
253/// # NcVisual Methods
254impl NcVisual {
255    /// Extracts the next frame from the NcVisual.
256    ///
257    /// Returns 0 for normal frames, and 1 to indicate EOF.
258    ///
259    /// *C style function: [ncvisual_decode()][c_api::ncvisual_decode].*
260    pub fn decode(&mut self) -> NcResult<NcResult_i32> {
261        let res = unsafe { c_api::ncvisual_decode(self) };
262        if res == NCRESULT_ERR {
263            Err(NcError::with_msg(res, "NcVisual.decode()"))
264        } else {
265            Ok(res)
266        }
267    }
268
269    /// Extracts the next frame from the NcVisual, ala [decode][NcVisual#method.decode],
270    /// but if we have reached the end, rewinds to the first frame.
271    ///
272    /// *A subsequent [NcVisual.render]() will render the first frame,
273    /// as if the ncvisual had been closed and reopened.*
274    ///
275    /// Returns 0 for normal frames and 1 to indicate EOF.
276    ///
277    /// *C style function: [ncvisual_decode_loop()][c_api::ncvisual_decode_loop].*
278    pub fn decode_loop(&mut self) -> NcResult<NcResult_i32> {
279        let res = unsafe { c_api::ncvisual_decode_loop(self) };
280        if res == NCRESULT_ERR {
281            Err(NcError::with_msg(res, "NcVisual.decode_loop()"))
282        } else {
283            Ok(res)
284        }
285    }
286
287    /// Returns [`NcVisualGeometry`].
288    ///
289    /// if [`Nc`] is not provided, only [`pix_yx`] will be filled in, with the
290    /// true pixel geometry of the current `NcVisual`.
291    ///
292    /// Additionally [`cdim_yx`] and [`maxpixel_yx`] are only ever filled in if we
293    /// know them, and `maxpixel_yx` is only defined for `NcBlitter`::PIXEL.
294    ///
295    /// # See also
296    /// - [`Nc.visual_geom`][Nc#method.visual_geom]
297    ///
298    /// [`pix_yx`]: NcVisualGeometry#structfield.pix_yx
299    /// [`cdim_yx`]: NcVisualGeometry#structfield.cdim_yx
300    /// [`scale_yx`]: NcVisualGeometry#structfield.scale_yx
301    /// [`maxpixel_yx`]: NcVisualGeometry#structfield.maxpixel_yx
302    /// [`blitter`]: NcVisualGeometry#structfield.blitter
303    ///
304    /// *C style function: [ncvisual_geom()][c_api::ncvisual_geom].*
305    pub fn geom(
306        &self,
307        nc: Option<&Nc>,
308        vopts: Option<&NcVisualOptions>,
309    ) -> NcResult<NcVisualGeometry> {
310        let mut vg = c_api::NcVGeom::new();
311
312        let nc_ptr: *const Nc = if let Some(nc) = nc { nc } else { null() };
313        let vo_ptr: *const NcVisualOptions =
314            if let Some(o) = vopts { o } else { &NcVisualOptions::default() };
315
316        let res = unsafe { crate::c_api::ncvisual_geom(nc_ptr, self, vo_ptr, &mut vg) };
317        if res <= c_api::NCRESULT_ERR {
318            return Err(NcError::with_msg(
319                res,
320                &format!["NcVisual.geom({:?}, {:?})", vopts, nc],
321            ));
322        }
323
324        let (pix_yx, cdim_yx, rpix_yx, rcell_yx, scale_yx, maxpixel_yx, beg_yx, len_yx);
325
326        // if an `Nc` context is not provided, only `pix_yx` will be filled in.
327        if nc.is_none() {
328            pix_yx = Some((vg.pixy, vg.pixx));
329
330            cdim_yx = None;
331            rpix_yx = None;
332            rcell_yx = None;
333            scale_yx = None;
334            maxpixel_yx = None;
335            beg_yx = None;
336            len_yx = None;
337        } else {
338            // `maxpixel_yx` only is defined for `Ncblitter::PIXEL`.
339            if vg.blitter == NcBlitter::Pixel.into() {
340                maxpixel_yx = Some((vg.maxpixely, vg.maxpixelx));
341            } else {
342                maxpixel_yx = None;
343            }
344
345            // `beg_yx` & `len_yx` can be safely ignored if they're both all 0.
346            if vg.begy | vg.begx | vg.leny | vg.lenx == 0 {
347                beg_yx = None;
348                len_yx = None;
349            } else {
350                beg_yx = Some((vg.begy, vg.begx));
351                len_yx = Some((vg.leny, vg.lenx));
352            }
353
354            // valid values for the following fields can't be 0 either:
355            if vg.pixy | vg.pixx == 0 {
356                pix_yx = None;
357            } else {
358                pix_yx = Some((vg.pixy, vg.pixx));
359            }
360            if vg.cdimy | vg.cdimx == 0 {
361                cdim_yx = None;
362            } else {
363                cdim_yx = Some((vg.cdimy, vg.cdimx));
364            }
365            if vg.scaley | vg.scalex == 0 {
366                scale_yx = None;
367            } else {
368                scale_yx = Some((vg.scaley, vg.scalex));
369            }
370            if vg.rpixy | vg.rpixx == 0 {
371                rpix_yx = None;
372            } else {
373                rpix_yx = Some((vg.rpixy, vg.rpixx));
374            }
375            if vg.rcelly | vg.rcellx == 0 {
376                rcell_yx = None;
377            } else {
378                rcell_yx = Some((vg.rcelly, vg.rcellx));
379            }
380        }
381
382        let vgeometry = NcVisualGeometry {
383            pix_yx,
384            cdim_yx,
385            rpix_yx,
386            rcell_yx,
387            scale_yx,
388            maxpixel_yx,
389            beg_yx,
390            len_yx,
391
392            blitter: (vg.blitter as crate::c_api::NcBlitter_u32).into(),
393        };
394        Ok(vgeometry)
395    }
396
397    /// Gets the default media (not plot) blitter for this environment when using
398    /// the specified scaling method.
399    ///
400    /// Currently, this means:
401    /// - if lacking UTF-8, [`NcBlitter::Ascii`].
402    /// - otherwise, if not using *[`NcScale::Stretch`]* then [`NcBlitter::Half`].
403    /// - otherwise, if sextants are not known to be good, [`NcBlitter::Quadrant`].
404    /// - otherwise [`NcBlitter::Sextant`]
405    ///
406    /// [`QUADRANT`] and [`SEXTANT`] both distort the original aspect ratio,
407    /// thus they are only used alongside *[`NcScale::Stretch`]*, while [`Half`]
408    /// is used otherwise.
409    ///
410    /// *C style function: [ncvisual_media_defblitter()][c_api::ncvisual_media_defblitter].*
411    ///
412    /// [`Half`]: NcBlitter::Half
413    /// [`Quadrant`]: NcBlitter::Quadrant
414    /// [`Sextant`]: NcBlitter::Sextant
415    pub fn media_defblitter(nc: &Nc, scale: impl Into<NcScale>) -> NcBlitter {
416        unsafe { c_api::ncvisual_media_defblitter(nc, scale.into().into()).into() }
417    }
418
419    /// Polyfills at the specified location using `rgba`.
420    ///
421    /// *C style function: [ncvisual_polyfill_yx()][c_api::ncvisual_polyfill_yx].*
422    pub fn polyfill_yx(&mut self, y: u32, x: u32, rgba: impl Into<NcRgba>) -> NcResult<()> {
423        error![
424            unsafe { c_api::ncvisual_polyfill_yx(self, y, x, rgba.into().into()) },
425            &format!["NcVisual.polyfill_yx({}, {}, rgba)", y, x]
426        ]
427    }
428
429    /// Renders the decoded frame according to the provided `options`.
430    ///
431    /// There are 3 options for choosing the the plane used for rendering:
432    /// 1. if the `options` have set the flag
433    /// [`NcVisualFlag::ChildPlane`][crate::NcVisualFlag#associatedconstant.ChildPlane]
434    /// then there must be a plane, which will be the father of the one created.
435    /// 2. if the flag is not set and there is no plane, a new plane is created
436    ///    as root of a new pile.
437    /// 3. if the flag is not set and there is a plane, we render to it.
438    ///
439    /// A subregion of the visual can be rendered using `beg_y`, `beg_x`,
440    /// `len_y`, and `len_x`.
441    ///
442    /// It is an error to specify any region beyond the boundaries of the frame.
443    ///
444    /// Returns the (possibly newly-created) plane to which we drew.
445    ///
446    /// Pixels may not be blitted to the standard plane.
447    ///
448    /// # Safety
449    /// You must be careful not to end up with multiple exclusive references
450    /// to the returned `NcPlane`, or with one exclusive reference
451    /// and one or more shared references.
452    ///
453    /// *C style function: [ncvisual_blit()][c_api::ncvisual_blit].*
454    pub unsafe fn blit(
455        &mut self,
456        nc: &mut Nc,
457        options: Option<&NcVisualOptions>,
458    ) -> NcResult<&mut NcPlane> {
459        let options_ptr = if let Some(o) = options { o } else { null() };
460        error_ref_mut![c_api::ncvisual_blit(nc, self, options_ptr), "NcVisual.blit"]
461    }
462
463    /// Resizes the visual to `cols` X `rows` pixels.
464    ///
465    /// This is a lossy transformation, unless the size is unchanged.
466    ///
467    /// *C style function: [ncvisual_resize()][c_api::ncvisual_resize].*
468    pub fn resize(&mut self, rows: u32, cols: u32) -> NcResult<()> {
469        error![
470            unsafe { c_api::ncvisual_resize(self, rows as i32, cols as i32) },
471            &format!["NcVisual.resize({}, {})", rows, cols]
472        ]
473    }
474
475    /// Resizes the visual to  in the image to `rows` X `cols` pixels, without
476    /// interpolating the color values.
477    ///
478    /// The original color is retained.
479    ///
480    /// *C style function:
481    /// [ncvisual_resize_noninterpolative()][c_api::ncvisual_resize_noninterpolative].*
482    pub fn resize_noninterpolative(&mut self, rows: u32, cols: u32) -> NcResult<()> {
483        error![
484            unsafe { c_api::ncvisual_resize_noninterpolative(self, rows as i32, cols as i32) },
485            &format!["NcVisual.resize_noninterpolative({}, {})", cols, rows]
486        ]
487    }
488
489    /// Rotates the visual `rads` radians.
490    ///
491    /// Only M_PI/2 and -M_PI/2 are supported at the moment,
492    /// but this will change. (FIXME)
493    ///
494    /// *C style function: [ncvisual_rotate()][c_api::ncvisual_rotate].*
495    pub fn rotate(&mut self, rads: f64) -> NcResult<()> {
496        error![
497            unsafe { c_api::ncvisual_rotate(self, rads) },
498            &format!["NcVisual.rotate({})", rads]
499        ]
500    }
501
502    /// Gets the specified pixel from this NcVisual.
503    ///
504    /// *C style function: [ncvisual_at_yx()][c_api::ncvisual_at_yx].*
505    pub fn at_yx(&self, y: u32, x: u32) -> NcResult<NcPixel> {
506        let mut pixel = 0;
507        let res = unsafe { c_api::ncvisual_at_yx(self, y, x, &mut pixel) };
508        error![res, "NcVisual.at_yx()", pixel.into()]
509    }
510
511    /// Sets the specified pixel.
512    ///
513    /// *C style function: [ncvisual_set_yx()][c_api::ncvisual_set_yx].*
514    pub fn set_yx(&mut self, y: u32, x: u32, pixel: impl Into<NcPixel>) -> NcResult<()> {
515        let pixel = pixel.into();
516        error![
517            unsafe { c_api::ncvisual_set_yx(self, y, x, pixel.into()) },
518            &format!["NcVisual.set_yx({}, {}, {:?})", y, x, pixel]
519        ]
520    }
521
522    /// Displays frames.
523    ///
524    /// *Provide as an argument to ncvisual_stream().*
525    ///
526    /// If you'd like subtitles to be decoded, provide an ncplane as the curry.
527    /// If the curry is `None`, subtitles will not be displayed.
528    ///
529    /// *C style function: [ncvisual_simple_streamer()][c_api::ncvisual_simple_streamer].*
530    pub fn simple_streamer(
531        &mut self,
532        options: &mut NcVisualOptions,
533        time: &NcTime,
534        curry: Option<&mut NcPlane>,
535    ) -> NcResult<()> {
536        if let Some(plane) = curry {
537            error![
538                unsafe {
539                    c_api::ncvisual_simple_streamer(
540                        self,
541                        options,
542                        time,
543                        plane as *mut _ as *mut c_void,
544                    )
545                },
546                &format![
547                    "NcVisual.simple_streamer({:?}, {:?}, ncplane)",
548                    options, time
549                ]
550            ]
551        } else {
552            error![
553                unsafe { c_api::ncvisual_simple_streamer(self, options, time, null_mut()) },
554                &format!["NcVisual.simple_streamer({:?}, {:?}, null)", options, time]
555            ]
556        }
557    }
558
559    // /// Streams the entirety of the media, according to its own timing.
560    // ///
561    // /// Blocking, obviously.
562    // ///
563    // /// If `streamer` is provided it will be called for each frame, and its
564    // /// return value handled as outlined for streamcb. If streamer() returns
565    // /// non-zero, the stream is aborted, and that value is returned.  By
566    // /// convention, return a positive number to indicate intentional abort from
567    // /// within streamer().
568    // ///
569    // /// `timescale` allows the frame duration time to be scaled. For an NcVisual
570    // /// naturally running at 30FPS, a 'timescale' of 0.1 will result in 300 FPS,
571    // /// and a `timescale` of 10 will result in 3 FPS. It is an error to supply
572    // /// `timescale` less than or equal to 0.
573    // ///
574    // /// *C style function: [ncvisual_streamer()][c_api::ncvisual_streamer].*
575    // //
576    // // TODO
577    // pub fn streamer(
578    //     &mut self,
579    //     nc: &mut Nc,
580    //     options: &NcVisualOptions,
581    //     timescale: f32,
582    //     streamer: Option<NcStreamCb>,
583    //     curry: Option<&mut NcPlane>,
584    // ) -> NcResult<()> {
585    //     debug_assert![timescale > 0.0];
586    //     todo![]
587    // }
588
589    /// If a subtitle ought be displayed at this time, return a new plane
590    ///
591    /// The returned plane is bound to `parent` and contains the subtitle,
592    /// which might be text or graphics (depending on the input format).
593    ///
594    /// *C style function: [ncvisual_subtitle_plane()][c_api::ncvisual_subtitle_plane].*
595    pub fn subtitle_plane(&self, parent: &mut NcPlane) -> NcResult<&mut NcPlane> {
596        error_ref_mut![unsafe { c_api::ncvisual_subtitle_plane(parent, self) }]
597    }
598}
599
600/// # `NcDirectF` Constructors & destructors
601impl NcVisual {
602    /// Loads media from disk, but do not yet renders it (presumably because you
603    /// want to get its geometry via [ncdirectf_geom()][0], or to use the same
604    /// file with [ncdirectf_render()][1] multiple times).
605    ///
606    /// You must destroy the result with [ncdirectf_free()][2];
607    ///
608    /// [0]: NcVisual#method.ncdirectf_geom
609    /// [1]: NcVisual#method.ncdirectf_render
610    /// [2]: NcVisual#method.ncdirectf_free
611    ///
612    /// *C style function: [ncdirectf_from_file()][c_api::ncdirectf_from_file].*
613    pub fn ncdirectf_from_file<'a>(ncd: &mut NcDirect, file: &str) -> NcResult<&'a mut NcVisual> {
614        let cs = cstring![file];
615        error_ref_mut![
616            unsafe { c_api::ncdirectf_from_file(ncd, cs.as_ptr()) },
617            &format!("NcVisual::ncdirectf_from_file(ncd, {})", file)
618        ]
619    }
620
621    /// Frees a [`NcVisual`] returned from [ncdirectf_from_file()][0].
622    ///
623    /// [0]: NcVisual#method.ncdirectf_from_file
624    ///
625    /// *C style function: [ncdirectf_free()][c_api::ncdirectf_free].*
626    pub fn ncdirectf_free(&mut self) {
627        unsafe { c_api::ncdirectf_free(self) };
628    }
629}
630
631/// # `NcDirectF` Methods
632impl NcVisual {
633    /// Same as [`NcDirect.render_frame()`][0], except `frame` must already have
634    /// been loaded.
635    ///
636    /// A loaded frame may be rendered in different ways before it is destroyed.
637    ///
638    /// [0]: NcDirect#method.render_frame
639    ///
640    /// *C style function: [ncdirectf_render()][c_api::ncdirectf_render].*
641    pub fn ncdirectf_render(
642        &mut self,
643        ncd: &mut NcDirect,
644        options: &NcVisualOptions,
645    ) -> NcResult<&mut NcPlane> {
646        error_ref_mut![
647            unsafe { c_api::ncdirectf_render(ncd, self, options) },
648            "NcVisual.ncdirectf_render()"
649        ]
650    }
651    /// Having loaded the `frame`, get the geometry of a potential render.
652    ///
653    /// *C style function: [ncdirectf_geom()][c_api::ncdirectf_geom].*
654    pub fn ncdirectf_geom(
655        &mut self,
656        ncd: &mut NcDirect,
657        options: &NcVisualOptions,
658    ) -> NcResult<NcVisualGeometry> {
659        let mut geom = c_api::NcVGeom::new();
660
661        let res = unsafe { c_api::ncdirectf_geom(ncd, self, options, &mut geom) };
662        error![res, "NcVisual.ncdirectf_geom()", geom.into()];
663    }
664}