wxdragon/dc/
mod.rs

1/// Background mode constants for device contexts
2#[derive(Debug, Clone, Copy, PartialEq, Eq)]
3pub enum BackgroundMode {
4    /// Transparent background mode
5    Transparent,
6    /// Solid background mode
7    Solid,
8}
9
10impl BackgroundMode {
11    /// Convert to the raw FFI value
12    pub fn to_raw(&self) -> i32 {
13        match self {
14            BackgroundMode::Transparent => wxdragon_sys::WXD_TRANSPARENT as i32,
15            BackgroundMode::Solid => wxdragon_sys::WXD_SOLID as i32,
16        }
17    }
18}
19
20widget_style_enum!(
21    name: PenStyle,
22    doc: "Style flags for DC pen.",
23    variants: {
24        Solid: wxdragon_sys::WXD_PENSTYLE_SOLID, "Solid line style.",
25        Dot: wxdragon_sys::WXD_PENSTYLE_DOT, "Dotted line style.",
26        LongDash: wxdragon_sys::WXD_PENSTYLE_LONG_DASH, "Long dashed line style.",
27        ShortDash: wxdragon_sys::WXD_PENSTYLE_SHORT_DASH, "Short dashed line style.",
28        DotDash: wxdragon_sys::WXD_PENSTYLE_DOT_DASH, "Dot and dash line style.",
29        Transparent: wxdragon_sys::WXD_PENSTYLE_TRANSPARENT, "Transparent pen.",
30        Stipple: wxdragon_sys::WXD_PENSTYLE_STIPPLE, "Stippled pen.",
31        UserDash: wxdragon_sys::WXD_PENSTYLE_USER_DASH, "User-defined dash pattern.",
32        BDiagonalHatch: wxdragon_sys::WXD_PENSTYLE_BDIAGONAL_HATCH, "Backward diagonal hatch pattern.",
33        CrossDiagHatch: wxdragon_sys::WXD_PENSTYLE_CROSSDIAG_HATCH, "Cross-diagonal hatch pattern.",
34        FDiagonalHatch: wxdragon_sys::WXD_PENSTYLE_FDIAGONAL_HATCH, "Forward diagonal hatch pattern.",
35        CrossHatch: wxdragon_sys::WXD_PENSTYLE_CROSS_HATCH, "Cross hatch pattern.",
36        HorizontalHatch: wxdragon_sys::WXD_PENSTYLE_HORIZONTAL_HATCH, "Horizontal hatch pattern.",
37        VerticalHatch: wxdragon_sys::WXD_PENSTYLE_VERTICAL_HATCH, "Vertical hatch pattern."
38    },
39    default_variant: Solid
40);
41
42widget_style_enum!(
43    name: BrushStyle,
44    doc: "Style flags for DC brush.",
45    variants: {
46        Solid: wxdragon_sys::WXD_BRUSHSTYLE_SOLID, "Solid brush.",
47        Transparent: wxdragon_sys::WXD_BRUSHSTYLE_TRANSPARENT, "Transparent brush.",
48        BDiagonalHatch: wxdragon_sys::WXD_BRUSHSTYLE_BDIAGONAL_HATCH, "Backward diagonal hatch pattern.",
49        CrossDiagHatch: wxdragon_sys::WXD_BRUSHSTYLE_CROSSDIAG_HATCH, "Cross-diagonal hatch pattern.",
50        FDiagonalHatch: wxdragon_sys::WXD_BRUSHSTYLE_FDIAGONAL_HATCH, "Forward diagonal hatch pattern.",
51        CrossHatch: wxdragon_sys::WXD_BRUSHSTYLE_CROSS_HATCH, "Cross hatch pattern.",
52        HorizontalHatch: wxdragon_sys::WXD_BRUSHSTYLE_HORIZONTAL_HATCH, "Horizontal hatch pattern.",
53        VerticalHatch: wxdragon_sys::WXD_BRUSHSTYLE_VERTICAL_HATCH, "Vertical hatch pattern.",
54        Stipple: wxdragon_sys::WXD_BRUSHSTYLE_STIPPLE, "Stippled brush.",
55        StippleMaskOpaque: wxdragon_sys::WXD_BRUSHSTYLE_STIPPLE_MASK_OPAQUE, "Stippled brush with opaque mask.",
56        StippleMask: wxdragon_sys::WXD_BRUSHSTYLE_STIPPLE_MASK, "Stippled brush with mask."
57    },
58    default_variant: Solid
59);
60
61/// Polygon fill modes
62#[derive(Debug, Clone, Copy, PartialEq, Eq)]
63pub enum PolygonFillMode {
64    /// Odd-even fill rule
65    OddEven,
66    /// Winding fill rule
67    Winding,
68}
69
70impl PolygonFillMode {
71    /// Convert to the raw FFI value
72    pub fn to_raw(&self) -> i32 {
73        match self {
74            PolygonFillMode::OddEven => wxdragon_sys::WXD_ODDEVEN_RULE as i32,
75            PolygonFillMode::Winding => wxdragon_sys::WXD_WINDING_RULE as i32,
76        }
77    }
78}
79
80/// Flood fill modes
81#[derive(Debug, Clone, Copy, PartialEq, Eq)]
82pub enum FloodFillMode {
83    /// Fill surface with same color
84    Surface,
85    /// Fill until border color
86    Border,
87}
88
89impl FloodFillMode {
90    /// Convert to the raw FFI value
91    pub fn to_raw(&self) -> i32 {
92        match self {
93            FloodFillMode::Surface => wxdragon_sys::WXD_FLOOD_SURFACE as i32,
94            FloodFillMode::Border => wxdragon_sys::WXD_FLOOD_BORDER as i32,
95        }
96    }
97}
98
99/// Logical drawing functions
100#[derive(Debug, Clone, Copy, PartialEq, Eq)]
101pub enum LogicalFunction {
102    Clear,
103    Xor,
104    Invert,
105    OrReverse,
106    AndReverse,
107    Copy,
108    And,
109    AndInvert,
110    NoOp,
111    Nor,
112    Equiv,
113    SrcInvert,
114    OrInvert,
115    Nand,
116    Or,
117    Set,
118}
119
120impl LogicalFunction {
121    /// Convert to the raw FFI value
122    pub fn to_raw(&self) -> i32 {
123        match self {
124            LogicalFunction::Clear => wxdragon_sys::WXD_CLEAR as i32,
125            LogicalFunction::Xor => wxdragon_sys::WXD_XOR as i32,
126            LogicalFunction::Invert => wxdragon_sys::WXD_INVERT as i32,
127            LogicalFunction::OrReverse => wxdragon_sys::WXD_OR_REVERSE as i32,
128            LogicalFunction::AndReverse => wxdragon_sys::WXD_AND_REVERSE as i32,
129            LogicalFunction::Copy => wxdragon_sys::WXD_COPY as i32,
130            LogicalFunction::And => wxdragon_sys::WXD_AND as i32,
131            LogicalFunction::AndInvert => wxdragon_sys::WXD_AND_INVERT as i32,
132            LogicalFunction::NoOp => wxdragon_sys::WXD_NO_OP as i32,
133            LogicalFunction::Nor => wxdragon_sys::WXD_NOR as i32,
134            LogicalFunction::Equiv => wxdragon_sys::WXD_EQUIV as i32,
135            LogicalFunction::SrcInvert => wxdragon_sys::WXD_SRC_INVERT as i32,
136            LogicalFunction::OrInvert => wxdragon_sys::WXD_OR_INVERT as i32,
137            LogicalFunction::Nand => wxdragon_sys::WXD_NAND as i32,
138            LogicalFunction::Or => wxdragon_sys::WXD_OR as i32,
139            LogicalFunction::Set => wxdragon_sys::WXD_SET as i32,
140        }
141    }
142
143    /// Convert from raw FFI value
144    pub fn from_raw(value: i32) -> Self {
145        match value {
146            _ if value == wxdragon_sys::WXD_CLEAR as i32 => LogicalFunction::Clear,
147            _ if value == wxdragon_sys::WXD_XOR as i32 => LogicalFunction::Xor,
148            _ if value == wxdragon_sys::WXD_INVERT as i32 => LogicalFunction::Invert,
149            _ if value == wxdragon_sys::WXD_OR_REVERSE as i32 => LogicalFunction::OrReverse,
150            _ if value == wxdragon_sys::WXD_AND_REVERSE as i32 => LogicalFunction::AndReverse,
151            _ if value == wxdragon_sys::WXD_COPY as i32 => LogicalFunction::Copy,
152            _ if value == wxdragon_sys::WXD_AND as i32 => LogicalFunction::And,
153            _ if value == wxdragon_sys::WXD_AND_INVERT as i32 => LogicalFunction::AndInvert,
154            _ if value == wxdragon_sys::WXD_NO_OP as i32 => LogicalFunction::NoOp,
155            _ if value == wxdragon_sys::WXD_NOR as i32 => LogicalFunction::Nor,
156            _ if value == wxdragon_sys::WXD_EQUIV as i32 => LogicalFunction::Equiv,
157            _ if value == wxdragon_sys::WXD_SRC_INVERT as i32 => LogicalFunction::SrcInvert,
158            _ if value == wxdragon_sys::WXD_OR_INVERT as i32 => LogicalFunction::OrInvert,
159            _ if value == wxdragon_sys::WXD_NAND as i32 => LogicalFunction::Nand,
160            _ if value == wxdragon_sys::WXD_OR as i32 => LogicalFunction::Or,
161            _ if value == wxdragon_sys::WXD_SET as i32 => LogicalFunction::Set,
162            _ => LogicalFunction::Copy,
163        }
164    }
165}
166
167/// Mapping modes for coordinate systems
168#[derive(Debug, Clone, Copy, PartialEq, Eq)]
169pub enum MapMode {
170    Text,
171    Lometric,
172    Twips,
173    Metric,
174}
175
176impl MapMode {
177    /// Convert to the raw FFI value
178    pub fn to_raw(&self) -> i32 {
179        match self {
180            MapMode::Text => wxdragon_sys::WXD_MM_TEXT as i32,
181            MapMode::Lometric => wxdragon_sys::WXD_MM_LOMETRIC as i32,
182            MapMode::Twips => wxdragon_sys::WXD_MM_TWIPS as i32,
183            MapMode::Metric => wxdragon_sys::WXD_MM_METRIC as i32,
184        }
185    }
186
187    /// Convert from raw FFI value
188    pub fn from_raw(value: i32) -> Self {
189        match value {
190            _ if value == wxdragon_sys::WXD_MM_TEXT as i32 => MapMode::Text,
191            _ if value == wxdragon_sys::WXD_MM_LOMETRIC as i32 => MapMode::Lometric,
192            _ if value == wxdragon_sys::WXD_MM_TWIPS as i32 => MapMode::Twips,
193            _ if value == wxdragon_sys::WXD_MM_METRIC as i32 => MapMode::Metric,
194            _ => MapMode::Text,
195        }
196    }
197}
198
199/// Text alignment constants
200#[derive(Debug, Clone, Copy, PartialEq, Eq)]
201pub struct TextAlignment {
202    bits: i32,
203}
204
205impl TextAlignment {
206    pub const INVALID: Self = Self {
207        bits: wxdragon_sys::WXD_ALIGN_INVALID as i32,
208    };
209    pub const LEFT: Self = Self {
210        bits: wxdragon_sys::WXD_ALIGN_LEFT as i32,
211    };
212    pub const TOP: Self = Self {
213        bits: wxdragon_sys::WXD_ALIGN_TOP as i32,
214    };
215    pub const RIGHT: Self = Self {
216        bits: wxdragon_sys::WXD_ALIGN_RIGHT as i32,
217    };
218    pub const BOTTOM: Self = Self {
219        bits: wxdragon_sys::WXD_ALIGN_BOTTOM as i32,
220    };
221    pub const CENTER_HORIZONTAL: Self = Self {
222        bits: wxdragon_sys::WXD_ALIGN_CENTRE_HORIZONTAL as i32,
223    };
224    pub const CENTER_VERTICAL: Self = Self {
225        bits: wxdragon_sys::WXD_ALIGN_CENTRE_VERTICAL as i32,
226    };
227    pub const CENTER: Self = Self {
228        bits: wxdragon_sys::WXD_ALIGN_CENTRE as i32,
229    };
230
231    pub const fn bits(&self) -> i32 {
232        self.bits
233    }
234
235    pub const fn from_bits(bits: i32) -> Self {
236        Self { bits }
237    }
238}
239
240impl std::ops::BitOr for TextAlignment {
241    type Output = Self;
242    fn bitor(self, rhs: Self) -> Self::Output {
243        Self {
244            bits: self.bits | rhs.bits,
245        }
246    }
247}
248
249impl std::ops::BitOrAssign for TextAlignment {
250    fn bitor_assign(&mut self, rhs: Self) {
251        self.bits |= rhs.bits;
252    }
253}
254
255/// Gradient direction constants
256#[derive(Debug, Clone, Copy, PartialEq, Eq)]
257pub enum GradientDirection {
258    North,
259    South,
260    East,
261    West,
262}
263
264impl GradientDirection {
265    /// Convert to the raw FFI value
266    pub fn to_raw(&self) -> i32 {
267        match self {
268            GradientDirection::North => wxdragon_sys::WXD_NORTH as i32,
269            GradientDirection::South => wxdragon_sys::WXD_SOUTH as i32,
270            GradientDirection::East => wxdragon_sys::WXD_EAST as i32,
271            GradientDirection::West => wxdragon_sys::WXD_WEST as i32,
272        }
273    }
274}
275
276/// Point structure for drawing operations
277#[derive(Debug, Clone, Copy, PartialEq, Eq)]
278pub struct Point {
279    pub x: i32,
280    pub y: i32,
281}
282
283impl Point {
284    pub fn new(x: i32, y: i32) -> Self {
285        Self { x, y }
286    }
287}
288
289impl From<Point> for wxdragon_sys::wxd_Point {
290    fn from(point: Point) -> Self {
291        wxdragon_sys::wxd_Point { x: point.x, y: point.y }
292    }
293}
294
295impl From<wxdragon_sys::wxd_Point> for Point {
296    fn from(point: wxdragon_sys::wxd_Point) -> Self {
297        Point { x: point.x, y: point.y }
298    }
299}
300
301/// Rectangle structure for drawing operations
302#[derive(Debug, Clone, Copy, PartialEq, Eq)]
303pub struct Rect {
304    pub x: i32,
305    pub y: i32,
306    pub width: i32,
307    pub height: i32,
308}
309
310impl Rect {
311    pub fn new(x: i32, y: i32, width: i32, height: i32) -> Self {
312        Self { x, y, width, height }
313    }
314}
315
316impl From<Rect> for wxdragon_sys::wxd_Rect {
317    fn from(rect: Rect) -> Self {
318        wxdragon_sys::wxd_Rect {
319            x: rect.x,
320            y: rect.y,
321            width: rect.width,
322            height: rect.height,
323        }
324    }
325}
326
327impl From<wxdragon_sys::wxd_Rect> for Rect {
328    fn from(rect: wxdragon_sys::wxd_Rect) -> Self {
329        Rect {
330            x: rect.x,
331            y: rect.y,
332            width: rect.width,
333            height: rect.height,
334        }
335    }
336}
337
338pub mod auto_buffered_paint_dc;
339pub mod client_dc;
340pub mod memory_dc;
341pub mod paint_dc;
342pub mod screen_dc;
343pub mod window_dc;
344
345pub use auto_buffered_paint_dc::AutoBufferedPaintDC;
346pub use client_dc::ClientDC;
347pub use memory_dc::MemoryDC;
348pub use paint_dc::PaintDC;
349pub use screen_dc::ScreenDC;
350pub use window_dc::WindowDC;
351
352// Re-export for convenience
353pub use crate::bitmap::Bitmap;
354pub use crate::color::Colour;
355pub use crate::font::Font;
356
357/// Configuration for a blit operation
358#[derive(Debug, Clone, Copy)]
359pub struct BlitConfig {
360    pub dest_x: i32,
361    pub dest_y: i32,
362    pub width: i32,
363    pub height: i32,
364    pub src_x: i32,
365    pub src_y: i32,
366    pub logical_func: LogicalFunction,
367    pub use_mask: bool,
368    pub src_mask_x: i32,
369    pub src_mask_y: i32,
370}
371
372impl BlitConfig {
373    pub fn new(dest_x: i32, dest_y: i32, width: i32, height: i32, src_x: i32, src_y: i32) -> Self {
374        Self {
375            dest_x,
376            dest_y,
377            width,
378            height,
379            src_x,
380            src_y,
381            logical_func: LogicalFunction::Copy,
382            use_mask: false,
383            src_mask_x: -1,
384            src_mask_y: -1,
385        }
386    }
387
388    pub fn with_logical_func(mut self, logical_func: LogicalFunction) -> Self {
389        self.logical_func = logical_func;
390        self
391    }
392
393    pub fn with_mask(mut self, src_mask_x: i32, src_mask_y: i32) -> Self {
394        self.use_mask = true;
395        self.src_mask_x = src_mask_x;
396        self.src_mask_y = src_mask_y;
397        self
398    }
399}
400
401/// Configuration for a stretch blit operation
402#[derive(Debug, Clone, Copy)]
403pub struct StretchBlitConfig {
404    pub dest_x: i32,
405    pub dest_y: i32,
406    pub dest_width: i32,
407    pub dest_height: i32,
408    pub src_x: i32,
409    pub src_y: i32,
410    pub src_width: i32,
411    pub src_height: i32,
412    pub logical_func: LogicalFunction,
413    pub use_mask: bool,
414    pub src_mask_x: i32,
415    pub src_mask_y: i32,
416}
417
418impl StretchBlitConfig {
419    pub fn with_logical_func(mut self, logical_func: LogicalFunction) -> Self {
420        self.logical_func = logical_func;
421        self
422    }
423
424    pub fn with_mask(mut self, src_mask_x: i32, src_mask_y: i32) -> Self {
425        self.use_mask = true;
426        self.src_mask_x = src_mask_x;
427        self.src_mask_y = src_mask_y;
428        self
429    }
430}
431
432/// Common trait implemented by all device context types
433pub trait DeviceContext {
434    /// Get a pointer to the underlying DC
435    fn dc_ptr(&self) -> *mut wxdragon_sys::wxd_DC_t;
436
437    /// Clear the device context
438    fn clear(&self) {
439        unsafe {
440            wxdragon_sys::wxd_DC_Clear(self.dc_ptr());
441        }
442    }
443
444    /// Set the background color of the device context
445    fn set_background(&self, colour: Colour) {
446        unsafe {
447            wxdragon_sys::wxd_DC_SetBackground(self.dc_ptr(), colour.into());
448        }
449    }
450
451    /// Set the background mode of the device context
452    fn set_background_mode(&self, mode: BackgroundMode) {
453        unsafe {
454            wxdragon_sys::wxd_DC_SetBackgroundMode(self.dc_ptr(), mode.to_raw());
455        }
456    }
457
458    /// Set the text background color
459    fn set_text_background(&self, colour: Colour) {
460        unsafe {
461            wxdragon_sys::wxd_DC_SetTextBackground(self.dc_ptr(), colour.into());
462        }
463    }
464
465    /// Set the text foreground color
466    fn set_text_foreground(&self, colour: Colour) {
467        unsafe {
468            wxdragon_sys::wxd_DC_SetTextForeground(self.dc_ptr(), colour.into());
469        }
470    }
471
472    /// Set the font for text drawing
473    fn set_font(&self, font: &Font) {
474        unsafe {
475            wxdragon_sys::wxd_DC_SetFont(self.dc_ptr(), font.as_ptr());
476        }
477    }
478
479    /// Set the pen for drawing outlines
480    fn set_pen(&self, colour: Colour, width: i32, style: PenStyle) {
481        unsafe {
482            wxdragon_sys::wxd_DC_SetPen(self.dc_ptr(), colour.into(), width, style.bits() as i32);
483        }
484    }
485
486    /// Set the brush for filling shapes
487    fn set_brush(&self, colour: Colour, style: BrushStyle) {
488        unsafe {
489            wxdragon_sys::wxd_DC_SetBrush(self.dc_ptr(), colour.into(), style.bits() as i32);
490        }
491    }
492
493    /// Draw a point at the specified coordinates
494    fn draw_point(&self, x: i32, y: i32) {
495        unsafe {
496            wxdragon_sys::wxd_DC_DrawPoint(self.dc_ptr(), x, y);
497        }
498    }
499
500    /// Draw a line from (x1, y1) to (x2, y2)
501    fn draw_line(&self, x1: i32, y1: i32, x2: i32, y2: i32) {
502        unsafe {
503            wxdragon_sys::wxd_DC_DrawLine(self.dc_ptr(), x1, y1, x2, y2);
504        }
505    }
506
507    /// Draw a rectangle with the specified dimensions
508    fn draw_rectangle(&self, x: i32, y: i32, width: i32, height: i32) {
509        unsafe {
510            wxdragon_sys::wxd_DC_DrawRectangle(self.dc_ptr(), x, y, width, height);
511        }
512    }
513
514    /// Draw a circle with the specified center and radius
515    fn draw_circle(&self, x: i32, y: i32, radius: i32) {
516        unsafe {
517            wxdragon_sys::wxd_DC_DrawCircle(self.dc_ptr(), x, y, radius);
518        }
519    }
520
521    /// Draw an ellipse inside the specified rectangle
522    fn draw_ellipse(&self, x: i32, y: i32, width: i32, height: i32) {
523        unsafe {
524            wxdragon_sys::wxd_DC_DrawEllipse(self.dc_ptr(), x, y, width, height);
525        }
526    }
527
528    /// Draw a rounded rectangle with the specified dimensions and corner radius
529    fn draw_rounded_rectangle(&self, x: i32, y: i32, width: i32, height: i32, radius: f64) {
530        unsafe {
531            wxdragon_sys::wxd_DC_DrawRoundedRectangle(self.dc_ptr(), x, y, width, height, radius);
532        }
533    }
534
535    /// Draw text at the specified position
536    fn draw_text(&self, text: &str, x: i32, y: i32) {
537        use std::ffi::CString;
538        if let Ok(c_text) = CString::new(text) {
539            unsafe {
540                wxdragon_sys::wxd_DC_DrawText(self.dc_ptr(), c_text.as_ptr(), x, y);
541            }
542        }
543    }
544
545    /// Draw a bitmap at the specified position
546    fn draw_bitmap(&self, bitmap: &Bitmap, x: i32, y: i32, transparent: bool) {
547        unsafe { wxdragon_sys::wxd_DC_DrawBitmap(self.dc_ptr(), bitmap.as_const_ptr(), x, y, transparent) };
548    }
549
550    /// Get the size of the device context
551    fn get_size(&self) -> (i32, i32) {
552        unsafe {
553            let size = wxdragon_sys::wxd_DC_GetSize(self.dc_ptr());
554            (size.width, size.height)
555        }
556    }
557
558    /// Get the text extent (width and height) for the specified string
559    fn get_text_extent(&self, text: &str) -> (i32, i32) {
560        use std::ffi::CString;
561        if let Ok(c_text) = CString::new(text) {
562            let mut width = 0;
563            let mut height = 0;
564            unsafe { wxdragon_sys::wxd_DC_GetTextExtent(self.dc_ptr(), c_text.as_ptr(), &mut width, &mut height) };
565            (width, height)
566        } else {
567            (0, 0)
568        }
569    }
570
571    /// Set a clipping region to restrict drawing operations
572    fn set_clipping_region(&self, x: i32, y: i32, width: i32, height: i32) {
573        unsafe {
574            wxdragon_sys::wxd_DC_SetClippingRegion(self.dc_ptr(), x, y, width, height);
575        }
576    }
577
578    /// Remove the current clipping region
579    fn destroy_clipping_region(&self) {
580        unsafe { wxdragon_sys::wxd_DC_DestroyClippingRegion(self.dc_ptr()) };
581    }
582
583    /// Draw a polygon using the specified points
584    fn draw_polygon(&self, points: &[Point], x_offset: i32, y_offset: i32, fill_mode: PolygonFillMode) {
585        if points.is_empty() {
586            return;
587        }
588
589        let mut ffi_points: Vec<wxdragon_sys::wxd_Point> = points.iter().map(|p| (*p).into()).collect();
590
591        unsafe {
592            wxdragon_sys::wxd_DC_DrawPolygon(
593                self.dc_ptr(),
594                ffi_points.len() as i32,
595                ffi_points.as_mut_ptr(),
596                x_offset,
597                y_offset,
598                fill_mode.to_raw(),
599            )
600        };
601    }
602
603    /// Draw an elliptic arc
604    fn draw_elliptic_arc(&self, x: i32, y: i32, width: i32, height: i32, start_angle: f64, end_angle: f64) {
605        unsafe { wxdragon_sys::wxd_DC_DrawEllipticArc(self.dc_ptr(), x, y, width, height, start_angle, end_angle) };
606    }
607
608    /// Draw multiple connected lines
609    fn draw_lines(&self, points: &[Point], x_offset: i32, y_offset: i32) {
610        if points.is_empty() {
611            return;
612        }
613
614        let mut ffi_points: Vec<wxdragon_sys::wxd_Point> = points.iter().map(|p| (*p).into()).collect();
615
616        unsafe {
617            wxdragon_sys::wxd_DC_DrawLines(
618                self.dc_ptr(),
619                ffi_points.len() as i32,
620                ffi_points.as_mut_ptr(),
621                x_offset,
622                y_offset,
623            )
624        };
625    }
626
627    /// Draw an arc from (x1, y1) to (x2, y2) with center at (xc, yc)
628    fn draw_arc(&self, x1: i32, y1: i32, x2: i32, y2: i32, xc: i32, yc: i32) {
629        unsafe {
630            wxdragon_sys::wxd_DC_DrawArc(self.dc_ptr(), x1, y1, x2, y2, xc, yc);
631        }
632    }
633
634    /// Draw a smooth spline through the given points
635    fn draw_spline(&self, points: &[Point]) {
636        if points.is_empty() {
637            return;
638        }
639
640        let mut ffi_points: Vec<wxdragon_sys::wxd_Point> = points.iter().map(|p| (*p).into()).collect();
641
642        unsafe { wxdragon_sys::wxd_DC_DrawSpline(self.dc_ptr(), ffi_points.len() as i32, ffi_points.as_mut_ptr()) };
643    }
644
645    /// Draw rotated text at the specified position
646    fn draw_rotated_text(&self, text: &str, x: i32, y: i32, angle: f64) {
647        use std::ffi::CString;
648        if let Ok(c_text) = CString::new(text) {
649            unsafe { wxdragon_sys::wxd_DC_DrawRotatedText(self.dc_ptr(), c_text.as_ptr(), x, y, angle) };
650        }
651    }
652
653    /// Draw text with alignment within a rectangle
654    fn draw_label(&self, text: &str, rect: Rect, alignment: TextAlignment, index_accel: i32) {
655        use std::ffi::CString;
656        if let Ok(c_text) = CString::new(text) {
657            unsafe { wxdragon_sys::wxd_DC_DrawLabel(self.dc_ptr(), c_text.as_ptr(), rect.into(), alignment.bits(), index_accel) };
658        }
659    }
660
661    /// Copy a portion of one DC to another using configuration struct
662    fn blit(&self, source: &dyn DeviceContext, config: BlitConfig) -> bool {
663        unsafe {
664            wxdragon_sys::wxd_DC_Blit(
665                self.dc_ptr(),
666                config.dest_x,
667                config.dest_y,
668                config.width,
669                config.height,
670                source.dc_ptr(),
671                config.src_x,
672                config.src_y,
673                config.logical_func.to_raw(),
674                config.use_mask,
675                config.src_mask_x,
676                config.src_mask_y,
677            )
678        }
679    }
680
681    /// Copy and stretch a portion of one DC to another using configuration struct
682    fn stretch_blit(&self, source: &dyn DeviceContext, config: StretchBlitConfig) -> bool {
683        unsafe {
684            wxdragon_sys::wxd_DC_StretchBlit(
685                self.dc_ptr(),
686                config.dest_x,
687                config.dest_y,
688                config.dest_width,
689                config.dest_height,
690                source.dc_ptr(),
691                config.src_x,
692                config.src_y,
693                config.src_width,
694                config.src_height,
695                config.logical_func.to_raw(),
696                config.use_mask,
697                config.src_mask_x,
698                config.src_mask_y,
699            )
700        }
701    }
702
703    /// Set clipping region from a set of points
704    fn set_clipping_region_from_points(&self, points: &[Point]) {
705        if points.is_empty() {
706            return;
707        }
708
709        let mut ffi_points: Vec<wxdragon_sys::wxd_Point> = points.iter().map(|p| (*p).into()).collect();
710
711        unsafe {
712            wxdragon_sys::wxd_DC_SetClippingRegionFromPoints(self.dc_ptr(), ffi_points.len() as i32, ffi_points.as_mut_ptr())
713        };
714    }
715
716    /// Get the clipping box coordinates
717    fn get_clipping_box(&self) -> Rect {
718        let mut x = 0;
719        let mut y = 0;
720        let mut width = 0;
721        let mut height = 0;
722
723        unsafe { wxdragon_sys::wxd_DC_GetClippingBox(self.dc_ptr(), &mut x, &mut y, &mut width, &mut height) };
724
725        Rect::new(x, y, width, height)
726    }
727
728    /// Set the device origin
729    fn set_device_origin(&self, x: i32, y: i32) {
730        unsafe { wxdragon_sys::wxd_DC_SetDeviceOrigin(self.dc_ptr(), x, y) };
731    }
732
733    /// Set the logical origin
734    fn set_logical_origin(&self, x: i32, y: i32) {
735        unsafe { wxdragon_sys::wxd_DC_SetLogicalOrigin(self.dc_ptr(), x, y) };
736    }
737
738    /// Set the user scale factors
739    fn set_user_scale(&self, x_scale: f64, y_scale: f64) {
740        unsafe { wxdragon_sys::wxd_DC_SetUserScale(self.dc_ptr(), x_scale, y_scale) };
741    }
742
743    /// Set the logical scale factors
744    fn set_logical_scale(&self, x_scale: f64, y_scale: f64) {
745        unsafe { wxdragon_sys::wxd_DC_SetLogicalScale(self.dc_ptr(), x_scale, y_scale) };
746    }
747
748    /// Set the mapping mode
749    fn set_map_mode(&self, mode: MapMode) {
750        unsafe { wxdragon_sys::wxd_DC_SetMapMode(self.dc_ptr(), mode.to_raw()) };
751    }
752
753    /// Get the device origin
754    fn get_device_origin(&self) -> Point {
755        let origin = unsafe { wxdragon_sys::wxd_DC_GetDeviceOrigin(self.dc_ptr()) };
756        origin.into()
757    }
758
759    /// Get the logical origin
760    fn get_logical_origin(&self) -> Point {
761        let origin = unsafe { wxdragon_sys::wxd_DC_GetLogicalOrigin(self.dc_ptr()) };
762        origin.into()
763    }
764
765    /// Get the user scale factors
766    fn get_user_scale(&self) -> (f64, f64) {
767        let mut x_scale = 0.0;
768        let mut y_scale = 0.0;
769
770        unsafe { wxdragon_sys::wxd_DC_GetUserScale(self.dc_ptr(), &mut x_scale, &mut y_scale) };
771
772        (x_scale, y_scale)
773    }
774
775    /// Get the logical scale factors
776    fn get_logical_scale(&self) -> (f64, f64) {
777        let mut x_scale = 0.0;
778        let mut y_scale = 0.0;
779
780        unsafe { wxdragon_sys::wxd_DC_GetLogicalScale(self.dc_ptr(), &mut x_scale, &mut y_scale) };
781
782        (x_scale, y_scale)
783    }
784
785    /// Get the current mapping mode
786    fn get_map_mode(&self) -> MapMode {
787        let mode = unsafe { wxdragon_sys::wxd_DC_GetMapMode(self.dc_ptr()) };
788        MapMode::from_raw(mode)
789    }
790
791    /// Convert device coordinates to logical coordinates (X)
792    fn device_to_logical_x(&self, x: i32) -> i32 {
793        unsafe { wxdragon_sys::wxd_DC_DeviceToLogicalX(self.dc_ptr(), x) }
794    }
795
796    /// Convert device coordinates to logical coordinates (Y)
797    fn device_to_logical_y(&self, y: i32) -> i32 {
798        unsafe { wxdragon_sys::wxd_DC_DeviceToLogicalY(self.dc_ptr(), y) }
799    }
800
801    /// Convert logical coordinates to device coordinates (X)
802    fn logical_to_device_x(&self, x: i32) -> i32 {
803        unsafe { wxdragon_sys::wxd_DC_LogicalToDeviceX(self.dc_ptr(), x) }
804    }
805
806    /// Convert logical coordinates to device coordinates (Y)
807    fn logical_to_device_y(&self, y: i32) -> i32 {
808        unsafe { wxdragon_sys::wxd_DC_LogicalToDeviceY(self.dc_ptr(), y) }
809    }
810
811    /// Get the size in millimeters
812    fn get_size_mm(&self) -> (i32, i32) {
813        let size = unsafe { wxdragon_sys::wxd_DC_GetSizeMM(self.dc_ptr()) };
814        (size.width, size.height)
815    }
816
817    /// Get full text extent including descent and external leading
818    fn get_full_text_extent(&self, text: &str, font: Option<&Font>) -> (i32, i32, i32, i32) {
819        use std::ffi::CString;
820        if let Ok(c_text) = CString::new(text) {
821            let mut width = 0;
822            let mut height = 0;
823            let mut descent = 0;
824            let mut external_leading = 0;
825
826            unsafe {
827                wxdragon_sys::wxd_DC_GetFullTextExtent(
828                    self.dc_ptr(),
829                    c_text.as_ptr(),
830                    &mut width,
831                    &mut height,
832                    &mut descent,
833                    &mut external_leading,
834                    font.map(|f| f.as_ptr()).unwrap_or(std::ptr::null_mut()),
835                );
836            }
837            (width, height, descent, external_leading)
838        } else {
839            (0, 0, 0, 0)
840        }
841    }
842
843    /// Get text extent for multi-line text
844    fn get_multi_line_text_extent(&self, text: &str, font: Option<&Font>) -> (i32, i32, i32) {
845        use std::ffi::CString;
846        if let Ok(c_text) = CString::new(text) {
847            let mut width = 0;
848            let mut height = 0;
849            let mut height_line = 0;
850
851            unsafe {
852                wxdragon_sys::wxd_DC_GetMultiLineTextExtent(
853                    self.dc_ptr(),
854                    c_text.as_ptr(),
855                    &mut width,
856                    &mut height,
857                    &mut height_line,
858                    font.map(|f| f.as_ptr()).unwrap_or(std::ptr::null_mut()),
859                );
860            }
861            (width, height, height_line)
862        } else {
863            (0, 0, 0)
864        }
865    }
866
867    /// Get the character height for the current font
868    fn get_char_height(&self) -> i32 {
869        unsafe { wxdragon_sys::wxd_DC_GetCharHeight(self.dc_ptr()) }
870    }
871
872    /// Get the character width for the current font
873    fn get_char_width(&self) -> i32 {
874        unsafe { wxdragon_sys::wxd_DC_GetCharWidth(self.dc_ptr()) }
875    }
876
877    /// Get the current background color
878    fn get_background(&self) -> Colour {
879        let colour = unsafe { wxdragon_sys::wxd_DC_GetBackground(self.dc_ptr()) };
880        Colour::new(colour.r, colour.g, colour.b, colour.a)
881    }
882
883    /// Get the current background mode
884    fn get_background_mode(&self) -> BackgroundMode {
885        let mode = unsafe { wxdragon_sys::wxd_DC_GetBackgroundMode(self.dc_ptr()) };
886        if mode == wxdragon_sys::WXD_TRANSPARENT as i32 {
887            BackgroundMode::Transparent
888        } else {
889            BackgroundMode::Solid
890        }
891    }
892
893    /// Get the current text background color
894    fn get_text_background(&self) -> Colour {
895        let colour = unsafe { wxdragon_sys::wxd_DC_GetTextBackground(self.dc_ptr()) };
896        Colour::new(colour.r, colour.g, colour.b, colour.a)
897    }
898
899    /// Get the current text foreground color
900    fn get_text_foreground(&self) -> Colour {
901        let colour = unsafe { wxdragon_sys::wxd_DC_GetTextForeground(self.dc_ptr()) };
902        Colour::new(colour.r, colour.g, colour.b, colour.a)
903    }
904
905    /// Get the pixels per inch (DPI)
906    fn get_ppi(&self) -> (i32, i32) {
907        let ppi = unsafe { wxdragon_sys::wxd_DC_GetPPI(self.dc_ptr()) };
908        (ppi.width, ppi.height)
909    }
910
911    /// Get the content scale factor (for high-DPI displays)
912    fn get_content_scale_factor(&self) -> f64 {
913        unsafe { wxdragon_sys::wxd_DC_GetContentScaleFactor(self.dc_ptr()) }
914    }
915
916    /// Fill a rectangle with a linear gradient
917    fn gradient_fill_linear(&self, rect: Rect, initial_colour: Colour, dest_colour: Colour, direction: GradientDirection) {
918        unsafe {
919            wxdragon_sys::wxd_DC_GradientFillLinear(
920                self.dc_ptr(),
921                rect.into(),
922                initial_colour.into(),
923                dest_colour.into(),
924                direction.to_raw(),
925            )
926        };
927    }
928
929    /// Fill a rectangle with a concentric gradient
930    fn gradient_fill_concentric(&self, rect: Rect, initial_colour: Colour, dest_colour: Colour, circle_center: Point) {
931        unsafe {
932            wxdragon_sys::wxd_DC_GradientFillConcentric(
933                self.dc_ptr(),
934                rect.into(),
935                initial_colour.into(),
936                dest_colour.into(),
937                circle_center.into(),
938            )
939        };
940    }
941
942    /// Flood fill starting from a point
943    fn flood_fill(&self, x: i32, y: i32, colour: Colour, style: FloodFillMode) -> bool {
944        unsafe { wxdragon_sys::wxd_DC_FloodFill(self.dc_ptr(), x, y, colour.into(), style.to_raw()) }
945    }
946
947    /// Set the logical function for drawing operations
948    fn set_logical_function(&self, function: LogicalFunction) {
949        unsafe { wxdragon_sys::wxd_DC_SetLogicalFunction(self.dc_ptr(), function.to_raw()) };
950    }
951
952    /// Get the current logical function
953    fn get_logical_function(&self) -> LogicalFunction {
954        let function = unsafe { wxdragon_sys::wxd_DC_GetLogicalFunction(self.dc_ptr()) };
955        LogicalFunction::from_raw(function)
956    }
957}