1use crate::sys;
6
7type Result = std::result::Result<(), Box<dyn std::error::Error>>;
8
9pub struct Context {
11 ctx: *mut sys::NVGcontext,
12}
13
14impl Context {
15 pub fn create(fs_ctx: sys::FsContext) -> Option<Self> {
17 let uninit = std::mem::MaybeUninit::<sys::NVGparams>::zeroed();
18 let mut params = unsafe { uninit.assume_init() };
19 params.userPtr = fs_ctx;
20 params.edgeAntiAlias = 1;
21
22 let ctx = unsafe { sys::nvgCreateInternal(&mut params) };
23 if ctx.is_null() {
24 None
25 } else {
26 Some(Self { ctx })
27 }
28 }
29
30 pub fn draw_frame<F: Fn(&Frame) -> Result>(&self, width: usize, height: usize, f: F) {
32 unsafe {
33 sys::nvgBeginFrame(self.ctx, width as f32, height as f32, 1.0);
34 }
35
36 let frame = Frame { ctx: self.ctx };
37
38 match f(&frame) {
39 Ok(()) => unsafe {
40 sys::nvgEndFrame(self.ctx);
41 },
42 Err(_) => unsafe {
43 sys::nvgCancelFrame(self.ctx);
44 },
45 }
46 }
47
48 pub fn create_font(
72 &self,
73 name: &str,
74 filename: &str,
75 ) -> std::result::Result<Font, Box<dyn std::error::Error>> {
76 let name = std::ffi::CString::new(name).unwrap();
77 let filename = std::ffi::CString::new(filename).unwrap();
78 let handle = unsafe { sys::nvgCreateFont(self.ctx, name.as_ptr(), filename.as_ptr()) };
79 match handle {
80 -1 => Err(Box::new(std::io::Error::new(
81 std::io::ErrorKind::Other,
82 "unable to load font",
83 ))),
84 _ => Ok(Font { handle }),
85 }
86 }
87
88 pub fn create_image(
91 &self,
92 filename: &str,
93 ) -> std::result::Result<Image, Box<dyn std::error::Error>> {
94 let filename = std::ffi::CString::new(filename).unwrap();
95 let handle = unsafe { sys::nvgCreateImage(self.ctx, filename.as_ptr(), 0) };
96 match handle {
97 -1 => Err(Box::new(std::io::Error::new(
98 std::io::ErrorKind::Other,
99 "unable to load image",
100 ))),
101 _ => Ok(Image {
102 ctx: self.ctx,
103 handle,
104 }),
105 }
106 }
107}
108
109impl Drop for Context {
110 fn drop(&mut self) {
111 unsafe {
112 sys::nvgDeleteInternal(self.ctx);
113 }
114 }
115}
116
117pub struct Frame {
119 ctx: *mut sys::NVGcontext,
120}
121
122impl Frame {
123 pub fn draw_path<F: Fn(&Path) -> Result>(&self, style: &Style, f: F) -> Result {
125 unsafe {
126 sys::nvgBeginPath(self.ctx);
129 }
130
131 if let Some(stroke) = &style.stroke {
132 match stroke {
133 PaintOrColor::Paint(p) => unsafe {
134 sys::nvgStrokePaint(self.ctx, &p.0);
135 },
136 PaintOrColor::Color(c) => unsafe {
137 sys::nvgStrokeColor(self.ctx, &c.0);
138 },
139 }
140 }
141 if let Some(fill) = &style.fill {
142 match fill {
143 PaintOrColor::Paint(p) => unsafe {
144 sys::nvgFillPaint(self.ctx, &p.0);
145 },
146 PaintOrColor::Color(c) => unsafe {
147 sys::nvgFillColor(self.ctx, &c.0);
148 },
149 }
150 }
151
152 let path = Path { ctx: self.ctx };
153 let r = f(&path);
154
155 if style.stroke.is_some() {
156 unsafe {
157 sys::nvgStroke(self.ctx);
158 }
159 }
160 if style.fill.is_some() {
161 unsafe {
162 sys::nvgFill(self.ctx);
163 }
164 }
165
166 r
173 }
174}
175
176pub struct Path {
178 ctx: *mut sys::NVGcontext,
179}
180
181impl Path {
182 pub fn move_to(&self, x: f32, y: f32) {
184 unsafe {
185 sys::nvgMoveTo(self.ctx, x, y);
186 }
187 }
188
189 pub fn line_to(&self, x: f32, y: f32) {
191 unsafe {
192 sys::nvgLineTo(self.ctx, x, y);
193 }
194 }
195
196 pub fn bezier_to(&self, c1x: f32, c1y: f32, c2x: f32, c2y: f32, x: f32, y: f32) {
198 unsafe {
199 sys::nvgBezierTo(self.ctx, c1x, c1y, c2x, c2y, x, y);
200 }
201 }
202
203 pub fn quad_to(&self, cx: f32, cy: f32, x: f32, y: f32) {
206 unsafe {
207 sys::nvgQuadTo(self.ctx, cx, cy, x, y);
208 }
209 }
210
211 pub fn arc_to(&self, x1: f32, y1: f32, x2: f32, y2: f32, radius: f32) {
213 unsafe {
214 sys::nvgArcTo(self.ctx, x1, y1, x2, y2, radius);
215 }
216 }
217
218 pub fn close_path(&self) {
220 unsafe {
221 sys::nvgClosePath(self.ctx);
222 }
223 }
224
225 pub fn arc(&self, cx: f32, cy: f32, r: f32, a0: f32, a1: f32, dir: Direction) {
229 unsafe {
230 sys::nvgArc(self.ctx, cx, cy, r, a0, a1, dir.to_sys() as _);
231 }
232 }
233
234 #[allow(clippy::too_many_arguments)]
237 pub fn elliptical_arc(
238 &self,
239 cx: f32,
240 cy: f32,
241 rx: f32,
242 ry: f32,
243 a0: f32,
244 a1: f32,
245 dir: Direction,
246 ) {
247 unsafe {
248 sys::nvgEllipticalArc(self.ctx, cx, cy, rx, ry, a0, a1, dir.to_sys() as _);
249 }
250 }
251
252 pub fn rect(&self, x: f32, y: f32, w: f32, h: f32) {
254 unsafe {
255 sys::nvgRect(self.ctx, x, y, w, h);
256 }
257 }
258
259 #[allow(clippy::many_single_char_names)]
261 pub fn rounded_rect(&self, x: f32, y: f32, w: f32, h: f32, r: f32) {
262 unsafe {
263 sys::nvgRoundedRect(self.ctx, x, y, w, h, r);
264 }
265 }
266
267 #[allow(clippy::too_many_arguments)]
269 #[allow(clippy::many_single_char_names)]
270 pub fn rounded_rect_varying(
271 &self,
272 x: f32,
273 y: f32,
274 w: f32,
275 h: f32,
276 rad_top_left: f32,
277 rad_top_right: f32,
278 rad_bottom_right: f32,
279 rad_bottom_left: f32,
280 ) {
281 unsafe {
282 sys::nvgRoundedRectVarying(
283 self.ctx,
284 x,
285 y,
286 w,
287 h,
288 rad_top_left,
289 rad_top_right,
290 rad_bottom_right,
291 rad_bottom_left,
292 );
293 }
294 }
295
296 pub fn ellipse(&self, cx: f32, cy: f32, rx: f32, ry: f32) {
298 unsafe {
299 sys::nvgEllipse(self.ctx, cx, cy, rx, ry);
300 }
301 }
302
303 pub fn circle(&self, cx: f32, cy: f32, r: f32) {
305 unsafe {
306 sys::nvgCircle(self.ctx, cx, cy, r);
307 }
308 }
309
310 }
312
313#[derive(Debug, Clone, Copy)]
315pub enum Direction {
316 Clockwise,
318 CounterClockwise,
320}
321
322impl Direction {
323 fn to_sys(self) -> sys::NVGwinding {
324 match self {
325 Direction::Clockwise => sys::NVGwinding_NVG_CW,
326 Direction::CounterClockwise => sys::NVGwinding_NVG_CCW,
327 }
328 }
329}
330
331#[doc(hidden)]
332pub enum PaintOrColor {
333 Paint(Paint),
334 Color(Color),
335}
336
337impl From<Paint> for PaintOrColor {
338 fn from(p: Paint) -> PaintOrColor {
339 PaintOrColor::Paint(p)
340 }
341}
342
343impl From<Color> for PaintOrColor {
344 fn from(c: Color) -> PaintOrColor {
345 PaintOrColor::Color(c)
346 }
347}
348
349#[derive(Default)]
351pub struct Style {
352 stroke: Option<PaintOrColor>,
353 fill: Option<PaintOrColor>,
354}
355
356impl Style {
357 pub fn stroke<T: Into<PaintOrColor>>(mut self, stroke: T) -> Self {
359 self.stroke = Some(stroke.into());
360 self
361 }
362
363 pub fn fill<T: Into<PaintOrColor>>(mut self, fill: T) -> Self {
365 self.fill = Some(fill.into());
366 self
367 }
368}
369
370pub struct Color(sys::NVGcolor);
372
373impl Color {
374 pub fn from_rgb(r: u8, g: u8, b: u8) -> Self {
376 Self(unsafe { sys::nvgRGB(r, g, b) })
377 }
378
379 pub fn from_rgbf(r: f32, g: f32, b: f32) -> Self {
381 Self(unsafe { sys::nvgRGBf(r, g, b) })
382 }
383
384 pub fn from_rgba(r: u8, g: u8, b: u8, a: u8) -> Self {
386 Self(unsafe { sys::nvgRGBA(r, g, b, a) })
387 }
388
389 pub fn from_rgbaf(r: f32, g: f32, b: f32, a: f32) -> Self {
391 Self(unsafe { sys::nvgRGBAf(r, g, b, a) })
392 }
393
394 pub fn from_hsv(h: f32, s: f32, l: f32) -> Self {
397 Self(unsafe { sys::nvgHSL(h, s, l) })
398 }
399
400 pub fn from_hsva(h: f32, s: f32, l: f32, a: u8) -> Self {
403 Self(unsafe { sys::nvgHSLA(h, s, l, a) })
404 }
405}
406
407pub struct Paint(sys::NVGpaint);
410
411impl Paint {
412 pub fn from_image(
416 image: &Image,
417 x: f32,
418 y: f32,
419 w: f32,
420 h: f32,
421 angle: f32,
422 alpha: f32,
423 ) -> Paint {
424 Paint(unsafe { sys::nvgImagePattern(image.ctx, x, y, w, h, angle, image.handle, alpha) })
425 }
426}
427
428pub struct Font {
430 handle: std::os::raw::c_int,
431}
432
433pub struct Image {
435 ctx: *mut sys::NVGcontext,
436 handle: std::os::raw::c_int,
437}
438
439impl Image {
440 pub fn size(&self) -> (usize, usize) {
442 let mut w = 0;
443 let mut h = 0;
444 unsafe {
445 sys::nvgImageSize(self.ctx, self.handle, &mut w, &mut h);
446 }
447 (w as usize, h as usize)
448 }
449}
450
451impl Drop for Image {
452 fn drop(&mut self) {
453 unsafe {
454 sys::nvgDeleteImage(self.ctx, self.handle);
455 }
456 }
457}