fltk/draw/
routines.rs

1#![allow(clippy::too_many_arguments)]
2
3use crate::enums::{Align, Color, ColorDepth, Cursor, Font, FrameType, Shortcut};
4use crate::image::RgbImage;
5use crate::prelude::*;
6use crate::surface::ImageSurface;
7use crate::utils::FlString;
8use fltk_sys::draw::*;
9use std::ffi::{CStr, CString};
10use std::mem;
11use std::os::raw;
12
13bitflags::bitflags! {
14    /// Defines the line styles supported by fltk
15    #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
16    pub struct LineStyle: i32 {
17        /// Solid line
18        const Solid = 0;
19        /// Dash
20        const Dash = 1;
21        /// Dot
22        const Dot = 2;
23        /// Dash dot
24        const DashDot = 3;
25        /// Dash dot dot
26        const DashDotDot = 4;
27        /// Cap flat
28        const CapFlat = 0x100;
29        /// Cap round
30        const CapRound = 0x200;
31        /// Cap square
32        const CapSquare = 0x300;
33        /// Join miter
34        const JoinMiter = 0x1000;
35        /// Join round
36        const JoinRound = 0x2000;
37        /// Join bevel
38        const JoinBevel = 0x3000;
39    }
40}
41
42/// Opaque type around `Fl_Region`
43pub struct Region(pub(crate) *mut raw::c_void);
44
45/// Shows a color map
46pub fn show_colormap(old_color: Color) -> Color {
47    unsafe { mem::transmute(Fl_show_colormap(old_color.bits())) }
48}
49
50/// Sets the color using rgb values
51pub fn set_color_rgb(r: u8, g: u8, b: u8) {
52    unsafe { Fl_set_color_rgb(r, g, b) }
53}
54
55/// Gets the last used color
56pub fn get_color() -> Color {
57    unsafe { mem::transmute(Fl_get_color()) }
58}
59
60/// Draws a line
61pub fn draw_line(x1: i32, y1: i32, x2: i32, y2: i32) {
62    unsafe {
63        Fl_line(x1, y1, x2, y2);
64    }
65}
66
67/// Draws a line from (x,y) to (x1,y1) and another from (x1,y1) to (x2,y2)
68pub fn draw_polyline(x1: i32, y1: i32, x2: i32, y2: i32, x3: i32, y3: i32) {
69    unsafe { Fl_polyline(x1, y1, x2, y2, x3, y3) }
70}
71
72/// Draws a point
73pub fn draw_point(x: i32, y: i32) {
74    unsafe { Fl_point(x, y) }
75}
76
77/// Draws a rectangle
78pub fn draw_rect(x: i32, y: i32, w: i32, h: i32) {
79    unsafe { Fl_rect(x, y, w, h) }
80}
81
82/// Draws a rectangle with border color
83pub fn draw_rect_with_color(x: i32, y: i32, w: i32, h: i32, color: Color) {
84    unsafe { Fl_rect_with_color(x, y, w, h, color.bits()) }
85}
86
87/// Draws a non-filled 3-sided polygon
88pub fn draw_loop(x1: i32, y1: i32, x2: i32, y2: i32, x3: i32, y3: i32) {
89    unsafe {
90        Fl_loop(x1, y1, x2, y2, x3, y3);
91    }
92}
93
94/// Draws a non-filled 4-sided polygon
95pub fn draw_loop_4sided(x1: i32, y1: i32, x2: i32, y2: i32, x3: i32, y3: i32, x4: i32, y4: i32) {
96    unsafe {
97        Fl_loop_4sided(x1, y1, x2, y2, x3, y3, x4, y4);
98    }
99}
100
101/// Draws a filled rectangle
102pub fn draw_rect_fill(x: i32, y: i32, w: i32, h: i32, color: Color) {
103    unsafe { Fl_rectf_with_color(x, y, w, h, color.bits()) }
104}
105
106/// Draws a focus rectangle
107pub fn draw_focus_rect(x: i32, y: i32, w: i32, h: i32) {
108    unsafe { Fl_focus_rect(x, y, w, h) }
109}
110
111/// Sets the drawing color
112pub fn set_draw_hex_color(color: u32) {
113    unsafe {
114        crate::app::open_display();
115    }
116    let (r, g, b) = crate::utils::hex2rgb(color);
117    unsafe { Fl_set_color_rgb(r, g, b) }
118}
119
120/// Sets the drawing color
121pub fn set_draw_rgb_color(r: u8, g: u8, b: u8) {
122    unsafe {
123        crate::app::open_display();
124    }
125    unsafe { Fl_set_color_rgb(r, g, b) }
126}
127
128/// Sets the drawing color
129pub fn set_draw_color(color: Color) {
130    unsafe {
131        crate::app::open_display();
132    }
133    unsafe { Fl_set_color_int(color.bits()) }
134}
135
136/// Draws a circle
137pub fn draw_circle(x: f64, y: f64, r: f64) {
138    unsafe {
139        Fl_circle(x, y, r);
140    }
141}
142
143/// Draws an arc
144pub fn draw_arc(x: i32, y: i32, width: i32, height: i32, a: f64, b: f64) {
145    unsafe {
146        Fl_arc(x, y, width, height, a, b);
147    }
148}
149
150/// Draws an arc
151pub fn draw_arc_with_radius(x: f64, y: f64, r: f64, start: f64, end: f64) {
152    unsafe { Fl_arc_with_radius(x, y, r, start, end) }
153}
154
155/// Draws a filled pie
156pub fn draw_pie(x: i32, y: i32, width: i32, height: i32, a: f64, b: f64) {
157    unsafe {
158        Fl_pie(x, y, width, height, a, b);
159    }
160}
161
162/// Sets the line style
163///
164/// # Warning
165/// You are required to change this back to
166/// [`set_line_style(LineStyle::Solid, 0)`](crate::draw::set_line_style)
167/// after finishing
168pub fn set_line_style(style: LineStyle, width: i32) {
169    unsafe {
170        crate::app::open_display();
171        Fl_line_style(
172            style.bits(),
173            width,
174            std::ptr::null_mut() as *mut std::os::raw::c_char,
175        );
176    }
177}
178
179/// Limits drawing to a region
180pub fn push_clip(x: i32, y: i32, w: i32, h: i32) {
181    unsafe {
182        Fl_push_clip(x, y, w, h);
183    }
184}
185
186/// Puts the drawing back
187pub fn pop_clip() {
188    unsafe {
189        Fl_pop_clip();
190    }
191}
192
193/// Sets the clip region
194pub fn set_clip_region(r: &Region) {
195    assert!(!r.0.is_null());
196    unsafe { Fl_set_clip_region(r.0) }
197}
198
199/// Gets the clip region
200pub fn clip_region() -> Region {
201    unsafe {
202        let ptr = Fl_clip_region();
203        assert!(!ptr.is_null());
204        Region(ptr)
205    }
206}
207
208/// Pushes an empty clip region onto the stack so nothing will be clipped
209pub fn push_no_clip() {
210    unsafe { Fl_push_no_clip() }
211}
212
213/// Returns whether the rectangle intersect with the current clip region
214pub fn not_clipped(x: i32, y: i32, w: i32, h: i32) -> bool {
215    unsafe { Fl_not_clipped(x, y, w, h) != 0 }
216}
217
218/// Restores the clip region
219pub fn restore_clip() {
220    unsafe { Fl_restore_clip() }
221}
222
223/// Transforms coordinate using the current transformation matrix
224pub fn transform_x(x: f64, y: f64) -> f64 {
225    unsafe { Fl_transform_x(x, y) }
226}
227
228/// Transforms coordinate using the current transformation matrix
229pub fn transform_y(x: f64, y: f64) -> f64 {
230    unsafe { Fl_transform_y(x, y) }
231}
232
233/// Transforms distance using current transformation matrix
234pub fn transform_dx(x: f64, y: f64) -> f64 {
235    unsafe { Fl_transform_dx(x, y) }
236}
237
238/// Transforms distance using current transformation matrix
239pub fn transform_dy(x: f64, y: f64) -> f64 {
240    unsafe { Fl_transform_dy(x, y) }
241}
242
243/// Adds coordinate pair to the vertex list without further transformations
244pub fn transformed_vertex(xf: f64, yf: f64) {
245    unsafe { Fl_transformed_vertex(xf, yf) }
246}
247
248/// Draws a filled rectangle
249pub fn draw_rectf(x: i32, y: i32, w: i32, h: i32) {
250    unsafe { Fl_rectf(x, y, w, h) }
251}
252
253/// Draws a filled rectangle with specified RGB color
254pub fn draw_rectf_with_rgb(
255    x: i32,
256    y: i32,
257    width: i32,
258    height: i32,
259    color_r: u8,
260    color_g: u8,
261    color_b: u8,
262) {
263    unsafe { Fl_rectf_with_rgb(x, y, width, height, color_r, color_g, color_b) }
264}
265
266/// Fills a 3-sided polygon. The polygon must be convex
267pub fn draw_polygon(x: i32, y: i32, x1: i32, y1: i32, x2: i32, y2: i32) {
268    unsafe { Fl_polygon(x, y, x1, y1, x2, y2) }
269}
270
271/// Fills a 4-sided polygon. The polygon must be convex
272pub fn draw_polygon_4sided(x1: i32, y1: i32, x2: i32, y2: i32, x3: i32, y3: i32, x4: i32, y4: i32) {
273    unsafe {
274        Fl_polygon_4sided(x1, y1, x2, y2, x3, y3, x4, y4);
275    }
276}
277
278/// Adds a series of points on a Bezier curve to the path
279pub fn draw_curve(x1: f64, y1: f64, x2: f64, y2: f64, x3: f64, y3: f64, x4: f64, y4: f64) {
280    unsafe {
281        Fl_curve(x1, y1, x2, y2, x3, y3, x4, y4);
282    }
283}
284
285/// Draws a horizontal line from (x,y) to (x1,y)
286pub fn draw_hline(x: i32, y: i32, x1: i32) {
287    unsafe { Fl_xyline(x, y, x1) }
288}
289
290/// Draws a horizontal line from (x,y) to (x1,y), then vertical from (x1,y) to (x1,y2)
291pub fn draw_hvline(x: i32, y: i32, x1: i32, y2: i32) {
292    unsafe { Fl_xyline2(x, y, x1, y2) }
293}
294
295/// Draws a horizontal line from (x,y) to (x1,y), then a vertical from (x1,y) to (x1,y2)
296/// and then another horizontal from (x1,y2) to (x3,y2)
297pub fn draw_hvhline(x: i32, y: i32, x1: i32, y2: i32, x3: i32) {
298    unsafe { Fl_xyline3(x, y, x1, y2, x3) }
299}
300
301/// Draws a vertical line from (x,y) to (x,y1)
302pub fn draw_vline(x: i32, y: i32, y1: i32) {
303    unsafe { Fl_yxline(x, y, y1) }
304}
305
306/// Draws a vertical line from (x,y) to (x,y1), then a horizontal from (x,y1) to (x2,y1)
307pub fn draw_vhline(x: i32, y: i32, y1: i32, x2: i32) {
308    unsafe { Fl_yxline2(x, y, y1, x2) }
309}
310
311/// Draws a vertical line from (x,y) to (x,y1) then a horizontal from (x,y1)
312/// to (x2,y1), then another vertical from (x2,y1) to (x2,y3)
313pub fn draw_vhvline(x: i32, y: i32, y1: i32, x2: i32, y3: i32) {
314    unsafe { Fl_yxline3(x, y, y1, x2, y3) }
315}
316
317/// Saves the current transformation matrix on the stack
318pub fn push_matrix() {
319    unsafe { Fl_push_matrix() }
320}
321
322/// Pops the current transformation matrix from the stack
323pub fn pop_matrix() {
324    unsafe { Fl_pop_matrix() }
325}
326
327/// Concatenates scaling transformation onto the current one
328pub fn scale_xy(x: f64, y: f64) {
329    unsafe { Fl_scale_xy(x, y) }
330}
331
332/// Concatenates scaling transformation onto the current one for both x & y
333pub fn scale(val: f64) {
334    unsafe { Fl_scale(val) }
335}
336
337/// Concatenates translation transformation onto the current one
338pub fn translate(x: f64, y: f64) {
339    unsafe { Fl_translate(x, y) }
340}
341
342/// Concatenates rotation transformation onto the current one
343pub fn rotate(d: f64) {
344    unsafe { Fl_rotate(d) }
345}
346
347/// Concatenates another transformation onto the current one
348pub fn mult_matrix(val_a: f64, val_b: f64, val_c: f64, val_d: f64, x: f64, y: f64) {
349    unsafe { Fl_mult_matrix(val_a, val_b, val_c, val_d, x, y) }
350}
351
352/// Starts drawing a list of points. Points are added to the list with `fl_vertex()`
353pub fn begin_points() {
354    unsafe { Fl_begin_points() }
355}
356
357/// Starts drawing a list of lines
358pub fn begin_line() {
359    unsafe { Fl_begin_line() }
360}
361
362/// Starts drawing a closed sequence of lines
363pub fn begin_loop() {
364    unsafe { Fl_begin_loop() }
365}
366
367/// Starts drawing a convex filled polygon
368pub fn begin_polygon() {
369    unsafe { Fl_begin_polygon() }
370}
371
372/// Adds a single vertex to the current path
373pub fn vertex(x: f64, y: f64) {
374    unsafe { Fl_vertex(x, y) }
375}
376
377/// Ends list of points, and draws
378pub fn end_points() {
379    unsafe { Fl_end_points() }
380}
381
382/// Ends list of lines, and draws
383pub fn end_line() {
384    unsafe { Fl_end_line() }
385}
386
387/// Ends closed sequence of lines, and draws
388pub fn end_loop() {
389    unsafe { Fl_end_loop() }
390}
391
392/// Ends closed sequence of lines, and draws
393pub fn end_polygon() {
394    unsafe { Fl_end_polygon() }
395}
396
397/// Starts drawing a complex filled polygon
398pub fn begin_complex_polygon() {
399    unsafe { Fl_begin_complex_polygon() }
400}
401
402/// Call `gap()` to separate loops of the path
403pub fn gap() {
404    unsafe { Fl_gap() }
405}
406
407/// Ends complex filled polygon, and draws
408pub fn end_complex_polygon() {
409    unsafe { Fl_end_complex_polygon() }
410}
411
412/// Sets the current font, which is then used in various drawing routines
413pub fn set_font(face: Font, fsize: i32) {
414    unsafe { Fl_set_draw_font(face.bits(), fsize) }
415}
416
417/// Gets the current font, which is used in various drawing routines
418pub fn font() -> Font {
419    unsafe { mem::transmute(Fl_font()) }
420}
421
422/// Gets the current font size, which is used in various drawing routines
423pub fn size() -> i32 {
424    unsafe { Fl_size() }
425}
426
427/// Returns the recommended minimum line spacing for the current font
428pub fn height() -> i32 {
429    unsafe { Fl_height() }
430}
431
432/// Sets the line spacing for the current font
433pub fn set_height(font: Font, size: i32) {
434    unsafe {
435        Fl_set_height(font.bits(), size);
436    }
437}
438
439/// Returns the recommended distance above the bottom of a `height()` tall box to
440/// draw the text at so it looks centered vertically in that box
441pub fn descent() -> i32 {
442    unsafe { Fl_descent() }
443}
444
445/// Returns the typographical width of a string
446pub fn width(txt: &str) -> f64 {
447    let len = txt.len();
448    let txt = CString::safe_new(txt);
449    unsafe { Fl_width(txt.as_ptr(), len as _) }
450}
451
452/// Measure the width and height of a text
453pub fn measure(txt: &str, draw_symbols: bool) -> (i32, i32) {
454    let txt = CString::safe_new(txt);
455    let (mut x, mut y) = (0, 0);
456    unsafe {
457        Fl_measure(txt.as_ptr(), &mut x, &mut y, i32::from(draw_symbols));
458    }
459    (x, y)
460}
461
462/// Measure the width and height of a text
463///
464/// If `width` is non-zero, it will wrap to that width
465pub fn wrap_measure(txt: &str, width: i32, draw_symbols: bool) -> (i32, i32) {
466    let txt = CString::safe_new(txt);
467    let (mut x, mut y) = (width, 0);
468    unsafe {
469        Fl_measure(txt.as_ptr(), &mut x, &mut y, i32::from(draw_symbols));
470    }
471    (x, y)
472}
473
474/// Measure the coordinates and size of the text where a bounding box using the
475/// returned data would fit the text
476pub fn text_extents(txt: &str) -> (i32, i32, i32, i32) {
477    let txt = CString::safe_new(txt);
478    let (mut x, mut y, mut w, mut h) = (0, 0, 0, 0);
479    unsafe {
480        Fl_text_extents(txt.as_ptr(), &mut x, &mut y, &mut w, &mut h);
481    }
482    (x, y, w, h)
483}
484
485/// Returns the typographical width of a single character
486pub fn char_width(c: char) -> f64 {
487    unsafe { Fl_char_width(c as u32) }
488}
489
490/// Converts text from Windows/X11 latin1 character set to local encoding
491pub fn latin1_to_local(txt: &str, n: i32) -> String {
492    let txt = CString::safe_new(txt);
493    unsafe {
494        let x = Fl_latin1_to_local(txt.as_ptr(), n);
495        assert!(!x.is_null());
496        CStr::from_ptr(x as *mut raw::c_char)
497            .to_string_lossy()
498            .to_string()
499    }
500}
501
502/// Converts text from local encoding to Windowx/X11 latin1 character set
503pub fn local_to_latin1(txt: &str, n: i32) -> String {
504    let txt = CString::safe_new(txt);
505    unsafe {
506        let x = Fl_local_to_latin1(txt.as_ptr(), n);
507        assert!(!x.is_null());
508        CStr::from_ptr(x as *mut raw::c_char)
509            .to_string_lossy()
510            .to_string()
511    }
512}
513
514/// Draws a string starting at the given x, y location
515pub fn draw_text(txt: &str, x: i32, y: i32) {
516    if size() == -1 && txt.len() == 1 {
517        return;
518    }
519    let txt = CString::safe_new(txt);
520    unsafe { Fl_draw(txt.as_ptr(), x, y) }
521}
522
523/// Draws a string starting at the given x, y location with width and height and alignment
524pub fn draw_text_boxed(string: &str, x: i32, y: i32, width: i32, height: i32, align: Align) {
525    if size() == -1 && string.len() == 1 {
526        return;
527    }
528    let s = CString::safe_new(string);
529    unsafe { Fl_draw_text_boxed(s.as_ptr(), x, y, width, height, align.bits()) }
530}
531
532/// Draws a string starting at the given x, y location, rotated to an angle
533pub fn draw_text_angled(angle: i32, txt: &str, x: i32, y: i32) {
534    if size() == -1 && txt.len() == 1 {
535        return;
536    }
537    let txt = CString::safe_new(txt);
538    unsafe { Fl_draw_text_angled(angle, txt.as_ptr(), x, y) }
539}
540
541/// Draws a UTF-8 string right to left starting at the given x, y location
542pub fn rtl_draw(txt: &str, x: i32, y: i32) {
543    if size() == -1 && txt.len() == 1 {
544        return;
545    }
546    let len = txt.len() as i32;
547    let txt = CString::safe_new(txt);
548    unsafe { Fl_rtl_draw(txt.as_ptr(), len, x, y) }
549}
550
551/// Draws a series of line segments around the given box.
552///
553/// The string must contain groups of 4 letters which specify one of 24 standard
554/// grayscale values, where 'A' is black and 'X' is white.
555/// The order of each set of 4 characters is: top, left, bottom, right.
556pub fn draw_frame(string: &str, x: i32, y: i32, width: i32, height: i32) {
557    assert!(string.len() % 4 == 0);
558    let s = CString::safe_new(string);
559    unsafe { Fl_frame(s.as_ptr(), x, y, width, height) }
560}
561
562/// Draws a series of line segments around the given box
563///
564/// Differs from `frame()` by the order of the line segments which is bottom, right, top, left.
565pub fn draw_frame2(string: &str, x: i32, y: i32, width: i32, height: i32) {
566    assert!(string.len() % 4 == 0);
567    let s = CString::safe_new(string);
568    unsafe { Fl_frame2(s.as_ptr(), x, y, width, height) }
569}
570
571/// Draws a box given the box type, size, position and color
572pub fn draw_box(box_type: FrameType, x: i32, y: i32, w: i32, h: i32, color: Color) {
573    unsafe { Fl_draw_box(box_type.as_i32(), x, y, w, h, color.bits()) }
574}
575
576/// Checks whether platform supports true alpha blending for RGBA images
577pub fn can_do_alpha_blending() -> bool {
578    unsafe { Fl_can_do_alpha_blending() != 0 }
579}
580
581/// Get a human-readable string from a shortcut value
582pub fn shortcut_label(shortcut: Shortcut) -> String {
583    unsafe {
584        let x = Fl_shortcut_label(shortcut.bits() as u32);
585        assert!(!x.is_null());
586        CStr::from_ptr(x as *mut raw::c_char)
587            .to_string_lossy()
588            .to_string()
589    }
590}
591
592/// Draws a selection rectangle, erasing a previous one by XOR'ing it first.
593pub fn overlay_rect(x: i32, y: i32, w: i32, h: i32) {
594    unsafe { Fl_overlay_rect(x, y, w, h) }
595}
596
597/// Erase a selection rectangle without drawing a new one
598pub fn overlay_clear() {
599    unsafe { Fl_overlay_clear() }
600}
601
602/// Sets the cursor style
603pub fn set_cursor(cursor: Cursor) {
604    unsafe { Fl_set_cursor(cursor as i32) }
605}
606
607/// Sets the cursor style
608pub fn set_cursor_with_color(cursor: Cursor, fg: Color, bg: Color) {
609    unsafe { Fl_set_cursor_with_color(cursor as i32, fg.bits() as i32, bg.bits() as i32) }
610}
611
612/// Sets the status
613pub fn set_status(x: i32, y: i32, w: i32, h: i32) {
614    unsafe { Fl_set_status(x, y, w, h) }
615}
616
617/// Sets spot within the window
618pub fn set_spot<Win: WindowExt>(font: Font, size: i32, x: i32, y: i32, w: i32, h: i32, win: &Win) {
619    unsafe {
620        Fl_set_spot(
621            font.bits(),
622            size,
623            x,
624            y,
625            w,
626            h,
627            win.as_widget_ptr() as *mut raw::c_void,
628        );
629    }
630}
631
632/// Resets the spot within the window
633pub fn reset_spot() {
634    unsafe { Fl_reset_spot() }
635}
636
637/**
638    Captures the window and returns raw data.
639    Example usage:
640    ```rust,no_run
641    use fltk::{prelude::*, *};
642    let mut win = window::Window::default();
643    let image = draw::capture_window(&mut win).unwrap();
644    ```
645    # Errors
646    The api can fail to capture the window as an image
647*/
648pub fn capture_window<Win: WindowExt>(win: &mut Win) -> Result<RgbImage, FltkError> {
649    let cp = win.w() * win.h() * 3;
650    win.show();
651    unsafe {
652        let x = Fl_read_image(std::ptr::null_mut(), 0, 0, win.w(), win.h(), 0);
653        if x.is_null() {
654            Err(FltkError::Internal(FltkErrorKind::FailedOperation))
655        } else {
656            let x = std::slice::from_raw_parts(x, cp as usize);
657            Ok(RgbImage::new(x, win.w(), win.h(), ColorDepth::Rgb8)?)
658        }
659    }
660}
661
662/**
663    Captures part of the window and returns raw data.
664    Example usage:
665    ```rust,no_run
666    use fltk::{prelude::*, *};
667    let mut win = window::Window::default();
668    let image = draw::capture_window(&mut win).unwrap();
669    ```
670    # Errors
671    The api can fail to capture the window as an image
672*/
673pub fn capture_window_part<Win: WindowExt>(
674    win: &mut Win,
675    x: i32,
676    y: i32,
677    w: i32,
678    h: i32,
679) -> Result<RgbImage, FltkError> {
680    let cp = win.w() * win.h() * 3;
681    win.show();
682    unsafe {
683        let x = Fl_capture_window_part(win.as_widget_ptr() as _, x, y, w, h);
684        if x.is_null() {
685            Err(FltkError::Internal(FltkErrorKind::FailedOperation))
686        } else {
687            let x = std::slice::from_raw_parts(x, cp as usize);
688            Ok(RgbImage::new(x, w, h, ColorDepth::Rgb8)?)
689        }
690    }
691}
692
693/**
694    Captures the image surface object and returns raw data.
695    Example usage:
696    ```rust,no_run
697    use fltk::{prelude::*, *};
698    let mut surface = surface::ImageSurface::new(100, 100, false);
699    let image = draw::capture_surface(&mut surface, 100, 100).unwrap();
700    ```
701    # Errors
702    The api can fail to capture the image surface as an image
703*/
704pub fn capture_surface(surface: &ImageSurface, w: i32, h: i32) -> Result<RgbImage, FltkError> {
705    let cp = w * h * 3;
706    unsafe {
707        ImageSurface::push_current(surface);
708        let x = Fl_read_image(std::ptr::null_mut(), 0, 0, w, h, 0);
709        ImageSurface::pop_current();
710        if x.is_null() {
711            Err(FltkError::Internal(FltkErrorKind::FailedOperation))
712        } else {
713            let x = std::slice::from_raw_parts(x, cp as usize);
714            Ok(RgbImage::new(x, w, h, ColorDepth::Rgb8)?)
715        }
716    }
717}
718
719/// Draw an image into a widget.
720/// Requires a call to [`app::set_visual(Mode::Rgb8).unwrap()`](`crate::app::set_visual`).
721/// Doesn't support transparency, for that you would have to use `RgbImage`.
722/// # Errors
723/// Errors on invalid or unsupported image formats
724pub fn draw_image(
725    data: &[u8],
726    x: i32,
727    y: i32,
728    w: i32,
729    h: i32,
730    depth: ColorDepth,
731) -> Result<(), FltkError> {
732    let sz = (w * h * depth as i32) as usize;
733    if sz > data.len() {
734        return Err(FltkError::Internal(FltkErrorKind::ImageFormatError));
735    }
736    unsafe {
737        Fl_draw_image(data.as_ptr(), x, y, w, h, depth as i32, 0);
738    }
739    Ok(())
740}
741
742/// Draw a check mark
743pub fn draw_check(x: i32, y: i32, w: i32, h: i32, col: Color) {
744    unsafe {
745        Fl_draw_check(x, y, w, h, col.bits());
746    }
747}
748
749/// Draw an image into a widget.
750/// Requires a call to [`app::set_visual(Mode::Rgb8).unwrap()`](`crate::app::set_visual`).
751/// A negative depth flips the image horizontally,
752/// while a negative line data flips it vertically.
753/// Allows passing a line-data parameter
754/// # Errors
755/// Errors on invalid or unsupported image formats
756/// # Safety
757/// Passing wrong line data can read to over or underflow
758pub unsafe fn draw_image_ext(
759    data: &[u8],
760    x: i32,
761    y: i32,
762    w: i32,
763    h: i32,
764    depth: i32,
765    line_data: i32,
766) {
767    unsafe {
768        Fl_draw_image(data.as_ptr(), x, y, w, h, depth, line_data);
769    }
770}
771
772/// Draws a rounded box
773pub fn draw_rbox(x: i32, y: i32, w: i32, h: i32, max_radius: i32, fill: bool, col: Color) {
774    let max_radius = if max_radius < 0 { 0 } else { max_radius };
775    let offset: [f64; 5] = [0.0, 0.07612, 0.29289, 0.61732, 1.0];
776    let mut rs = w * 2 / 5;
777    let rsy = h * 2 / 5;
778    if rs > rsy {
779        rs = rsy;
780    }
781    if rs > max_radius {
782        rs = max_radius;
783    }
784    if rs == 5 {
785        rs = 4;
786    }
787    if rs == 7 {
788        rs = 8;
789    }
790
791    let rs = f64::from(rs);
792    let x = f64::from(x);
793    let y = f64::from(y);
794    let w = f64::from(w);
795    let h = f64::from(h);
796    let old_col = get_color();
797    let len = offset.len();
798
799    set_draw_color(col);
800    if fill {
801        begin_polygon();
802    } else {
803        begin_loop();
804    }
805    unsafe {
806        for i in 0..len {
807            vertex(
808                0.5 + x + offset.get_unchecked(len - i - 1) * rs,
809                0.5 + y + offset.get_unchecked(i) * rs,
810            );
811        }
812        for i in 0..len {
813            vertex(
814                0.5 + x + offset.get_unchecked(i) * rs,
815                0.5 + y + h - 1.0 - offset.get_unchecked(len - i - 1) * rs,
816            );
817        }
818        for i in 0..len {
819            vertex(
820                0.5 + x + w - 1.0 - offset.get_unchecked(len - i - 1) * rs,
821                0.5 + y + h - 1.0 - offset.get_unchecked(i) * rs,
822            );
823        }
824        for i in 0..len {
825            vertex(
826                0.5 + x + w - 1.0 - offset.get_unchecked(i) * rs,
827                0.5 + y + offset.get_unchecked(len - i - 1) * rs,
828            );
829        }
830    }
831    if fill {
832        end_polygon();
833    } else {
834        end_loop();
835    }
836    set_draw_color(old_col);
837}
838
839#[cfg(feature = "enable-glwindow")]
840/// Start drawing using OpenGL functions inside a widget's draw routine
841/// # Safety
842/// Requires OpenGL support, Only works with SingleWindow
843pub unsafe fn gl_start() {
844    unsafe {
845        fltk_sys::window::Fl_gl_start();
846    }
847}
848
849#[cfg(feature = "enable-glwindow")]
850/// Finish drawing using OpenGL functions inside a widget's draw routine
851/// # Safety
852/// Requires OpenGL support, Only works with SingleWindow
853pub unsafe fn gl_finish() {
854    unsafe {
855        fltk_sys::window::Fl_gl_finish();
856    }
857}
858
859/// Draws a rounded rectangle
860pub fn draw_rounded_rect(x: i32, y: i32, w: i32, h: i32, r: i32) {
861    unsafe { Fl_rounded_rect(x, y, w, h, r) }
862}
863
864/// Draws a filled rounded rectangle
865pub fn draw_rounded_rectf(x: i32, y: i32, w: i32, h: i32, r: i32) {
866    unsafe { Fl_rounded_rectf(x, y, w, h, r) }
867}
868
869/// Draws a filled circle
870pub fn draw_circle_fill(x: i32, y: i32, d: i32, c: Color) {
871    unsafe {
872        Fl_draw_circle(x, y, d, c.bits());
873    }
874}
875
876/// Like `draw_text`, however uses FLTK's `fl_draw` which takes the length of the string, so it doesn't need to allocate
877pub fn draw_text_n(string: &str, x: i32, y: i32) {
878    let len = string.len();
879    if size() == -1 && len == 1 {
880        return;
881    }
882    unsafe { Fl_draw_text_n(string.as_ptr() as _, len as _, x, y) }
883}
884
885/// Override the drawing scale
886pub fn override_scale() -> f32 {
887    unsafe { Fl_override_scale() }
888}
889
890/// Restore the drawing scale
891pub fn restore_scale(s: f32) {
892    unsafe { Fl_restore_scale(s) }
893}