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}