1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
//! Drawing primitives that burn annotations into image pixels.
//!
//! Everything in this module **mutates the image it draws on**. That is the
//! point: an inspection record with the defect location, ROI, and markers in
//! the pixels themselves is self-contained — it opens in any image viewer,
//! survives being copied around, and needs no sidecar file. The intended
//! pattern is to run algorithms on the original and draw on a clone:
//!
//! ```
//! use fovea::Size;
//! use fovea::draw::{draw_crosshair, draw_rect};
//! use fovea::image::Image;
//! use fovea::pixel::Mono8;
//!
//! let source: Image<Mono8> = Image::zero(64, 64);
//! // ... run detection on `source` ...
//!
//! let mut annotated = source.clone();
//! draw_rect(&mut annotated, (8, 8), Size::new(24, 16), Mono8::new(255), false);
//! draw_crosshair(&mut annotated, (20, 16), 5, Mono8::new(255));
//! ```
//!
//! ## Shapes and free functions
//!
//! Each shape exists twice: as a struct implementing [`Drawable`], for when
//! the shape needs to be stored, cloned, or built up before drawing, and as
//! a one-shot free function wrapping it. Neither form duplicates logic.
//!
//! | Shape | Free function | Rasterisation |
//! |---|---|---|
//! | [`Line`] | [`draw_line`] | Bresenham's line algorithm |
//! | [`Rect`] | [`draw_rect`] | Row/column spans (outline or filled) |
//! | [`Circle`] | [`draw_circle`] | Midpoint circle (outline or filled) |
//! | [`Polyline`] | [`draw_polyline`] | Sequential Bresenham segments |
//! | [`Crosshair`] | [`draw_crosshair`] | Two perpendicular axis-aligned lines |
//!
//! ## Signed coordinates, silent clipping
//!
//! Drawing positions are `(i32, i32)` — signed, unlike the unsigned
//! [`Coordinate`](crate::Coordinate) used for ROI offsets — because a shape
//! is routinely centred on a feature near the image edge and legitimately
//! extends past it. Every primitive clips: the in-bounds portion is drawn,
//! out-of-bounds pixels are skipped, no error is returned and no panic
//! occurs. This is the universal convention for 2D rasterisation.
//!
//! Clipping bounds the cost as well as the writes: the walks skip the
//! invisible portion of a shape, so a segment or circle whose ideal extent
//! is billions of pixels costs what its visible portion costs, not what
//! the ideal shape would.
//!
//! ## Crisp, single-pixel rendering
//!
//! All primitives write hard single-pixel strokes: each touched pixel is set
//! to exactly `color`, untouched pixels keep their value. There is no
//! anti-aliasing and no alpha blending — deliberate for annotation burn-in,
//! where hard edges survive JPEG compression without smearing and the only
//! requirement on the pixel type is `Copy`. Thick strokes, anti-aliased
//! variants, and text rendering are out of scope for now.
//!
//! ## Extension by addition
//!
//! Custom markers — arrows, calipers, target diamonds — are implemented by
//! writing a type with [`Drawable`], not by modifying this module. See the
//! trait documentation for an example.
//!
//! [`Drawable`]: crate::draw::Drawable
//! [`Line`]: crate::draw::Line
//! [`Rect`]: crate::draw::Rect
//! [`Circle`]: crate::draw::Circle
//! [`Polyline`]: crate::draw::Polyline
//! [`Crosshair`]: crate::draw::Crosshair
//! [`draw_line`]: crate::draw::draw_line
//! [`draw_rect`]: crate::draw::draw_rect
//! [`draw_circle`]: crate::draw::draw_circle
//! [`draw_polyline`]: crate::draw::draw_polyline
//! [`draw_crosshair`]: crate::draw::draw_crosshair
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
use crateImageViewMut;
/// A shape that renders itself into any mutable image of pixel type `P`.
///
/// This is the extension point of the [`draw`](self) module: the built-in
/// shapes implement it, and user-defined shapes implement it to become
/// drawable everywhere the built-ins are — no library modification needed.
///
/// # Clipping
///
/// Implementations must silently clip pixels that fall outside the image
/// bounds. Drawing a shape that is fully or partially outside the image is
/// defined behaviour — the in-bounds portion is drawn, out-of-bounds pixels
/// are skipped. No error is returned; no panic occurs.
///
/// # Minimal bounds
///
/// `P: Copy` is the tightest bound that permits writing a pixel value into
/// an image location, so any pixel type — including user-defined ones — can
/// be drawn onto.
///
/// # Object safety
///
/// This trait is intentionally not object-safe: `draw_into` takes
/// `impl ImageViewMut`, making the method generic, so a `Line<P>` drawn into
/// an owned image, a borrowed buffer, or an ROI view monomorphises to direct
/// pixel writes with no dynamic dispatch.
///
/// # Examples
///
/// A custom shape — an X marker — built from two [`Line`]s:
///
/// ```
/// use fovea::draw::{Drawable, Line};
/// use fovea::image::{Image, ImageView, ImageViewMut};
/// use fovea::pixel::Mono8;
///
/// struct XMarker {
/// center: (i32, i32),
/// arm: i32,
/// color: Mono8,
/// }
///
/// impl Drawable<Mono8> for XMarker {
/// fn draw_into(&self, image: &mut impl ImageViewMut<Pixel = Mono8>) {
/// let (cx, cy) = self.center;
/// let a = self.arm;
/// Line { from: (cx - a, cy - a).into(), to: (cx + a, cy + a).into(), color: self.color }
/// .draw_into(image);
/// Line { from: (cx - a, cy + a).into(), to: (cx + a, cy - a).into(), color: self.color }
/// .draw_into(image);
/// }
/// }
///
/// let mut image: Image<Mono8> = Image::zero(9, 9);
/// XMarker { center: (4, 4), arm: 3, color: Mono8::new(255) }.draw_into(&mut image);
/// assert_eq!(image.pixel_at(4, 4), Mono8::new(255));
/// assert_eq!(image.pixel_at(1, 1), Mono8::new(255));
/// assert_eq!(image.pixel_at(1, 7), Mono8::new(255));
/// ```
/// Writes `color` at signed `(x, y)`, skipping out-of-bounds positions.
/// Writes the horizontal run from `x0` to `x1` (either order, inclusive) on
/// row `y`, clipped to the image bounds.
/// Writes the vertical run from `y0` to `y1` (either order, inclusive) in
/// column `x`, clipped to the image bounds.