1#[derive(Clone, Copy, Debug, PartialEq, Eq)]
9pub enum LineCap {
10 Butt = 0,
11 Round = 1,
12 Square = 2,
13}
14
15impl LineCap {
16 pub fn from_i32(v: i32) -> Option<Self> {
17 match v {
18 0 => Some(Self::Butt),
19 1 => Some(Self::Round),
20 2 => Some(Self::Square),
21 _ => None,
22 }
23 }
24}
25
26#[derive(Clone, Copy, Debug, PartialEq, Eq)]
28pub enum LineJoin {
29 Miter = 0,
30 Round = 1,
31 Bevel = 2,
32}
33
34impl LineJoin {
35 pub fn from_i32(v: i32) -> Option<Self> {
36 match v {
37 0 => Some(Self::Miter),
38 1 => Some(Self::Round),
39 2 => Some(Self::Bevel),
40 _ => None,
41 }
42 }
43}
44
45#[derive(Clone, Copy, Debug, PartialEq, Eq)]
47pub enum FillRule {
48 NonZeroWinding,
49 EvenOdd,
50}
51
52#[derive(Clone, Debug)]
54pub struct DashPattern {
55 pub array: Vec<f64>,
56 pub offset: f64,
57}
58
59impl DashPattern {
60 pub fn solid() -> Self {
61 Self {
62 array: Vec::new(),
63 offset: 0.0,
64 }
65 }
66}
67
68impl Default for DashPattern {
69 fn default() -> Self {
70 Self::solid()
71 }
72}
73
74#[derive(Clone, Debug)]
76pub struct DeviceColor {
77 pub r: f64,
78 pub g: f64,
79 pub b: f64,
80 pub native_cmyk: Option<(f64, f64, f64, f64)>,
84 pub process_cmyk: Option<(f64, f64, f64, f64)>,
90}
91
92impl DeviceColor {
93 pub fn from_gray(gray: f64) -> Self {
94 Self {
95 r: gray,
96 g: gray,
97 b: gray,
98 native_cmyk: None,
99 process_cmyk: None,
100 }
101 }
102
103 pub fn from_rgb(r: f64, g: f64, b: f64) -> Self {
104 Self {
105 r,
106 g,
107 b,
108 native_cmyk: None,
109 process_cmyk: None,
110 }
111 }
112
113 pub fn from_cmyk(c: f64, m: f64, y: f64, k: f64) -> Self {
114 Self {
115 r: 1.0 - (c + k).min(1.0),
116 g: 1.0 - (m + k).min(1.0),
117 b: 1.0 - (y + k).min(1.0),
118 native_cmyk: Some((c, m, y, k)),
119 process_cmyk: None,
120 }
121 }
122
123 pub fn from_cmyk_icc(c: f64, m: f64, y: f64, k: f64, icc: &mut crate::icc::IccCache) -> Self {
126 if let Some((r, g, b)) = icc.convert_cmyk(c, m, y, k) {
127 Self {
128 r,
129 g,
130 b,
131 native_cmyk: Some((c, m, y, k)),
132 process_cmyk: None,
133 }
134 } else {
135 Self::from_cmyk(c, m, y, k)
136 }
137 }
138
139 pub fn from_hsb(h: f64, s: f64, b: f64) -> Self {
140 if s == 0.0 {
141 return Self::from_gray(b);
142 }
143 if b == 0.0 {
144 return Self::from_gray(0.0);
145 }
146
147 let mut hue = h * 6.0;
148 if hue >= 6.0 {
149 hue = 0.0;
150 }
151
152 let sector = hue as i32;
153 let frac = hue - sector as f64;
154
155 let p = b * (1.0 - s);
156 let q = b * (1.0 - s * frac);
157 let t = b * (1.0 - s * (1.0 - frac));
158
159 let (r, g, bl) = match sector {
160 0 => (b, t, p),
161 1 => (q, b, p),
162 2 => (p, b, t),
163 3 => (p, q, b),
164 4 => (t, p, b),
165 _ => (b, p, q),
166 };
167
168 Self {
169 r,
170 g,
171 b: bl,
172 native_cmyk: None,
173 process_cmyk: None,
174 }
175 }
176
177 pub fn to_gray(&self) -> f64 {
179 0.3 * self.r + 0.59 * self.g + 0.11 * self.b
180 }
181
182 pub fn to_cmyk(&self) -> (f64, f64, f64, f64) {
184 if let Some(cmyk) = self.native_cmyk {
185 return cmyk;
186 }
187 let c = 1.0 - self.r;
188 let m = 1.0 - self.g;
189 let y = 1.0 - self.b;
190 let k = c.min(m).min(y);
191 (
192 (c - k).clamp(0.0, 1.0),
193 (m - k).clamp(0.0, 1.0),
194 (y - k).clamp(0.0, 1.0),
195 k.clamp(0.0, 1.0),
196 )
197 }
198
199 pub fn to_hsb(&self) -> (f64, f64, f64) {
201 let max_val = self.r.max(self.g).max(self.b);
202 let min_val = self.r.min(self.g).min(self.b);
203 let diff = max_val - min_val;
204
205 let brightness = max_val;
206 let saturation = if max_val == 0.0 { 0.0 } else { diff / max_val };
207
208 let hue = if diff == 0.0 {
209 0.0
210 } else if max_val == self.r {
211 let mut h = (self.g - self.b) / diff;
212 if h < 0.0 {
213 h += 6.0;
214 }
215 h / 6.0
216 } else if max_val == self.g {
217 ((self.b - self.r) / diff + 2.0) / 6.0
218 } else {
219 ((self.r - self.g) / diff + 4.0) / 6.0
220 };
221
222 (hue, saturation, brightness)
223 }
224
225 pub fn black() -> Self {
227 Self {
228 r: 0.0,
229 g: 0.0,
230 b: 0.0,
231 native_cmyk: None,
232 process_cmyk: None,
233 }
234 }
235
236 fn srgb_gamma(u: f64) -> f64 {
238 if u <= 0.0031308 {
239 12.92 * u
240 } else {
241 1.055 * u.powf(1.0 / 2.4) - 0.055
242 }
243 }
244
245 fn from_xyz(x: f64, y: f64, z: f64) -> Self {
247 let lr = 3.2404542 * x + (-1.5371385) * y + (-0.4985314) * z;
249 let lg = (-0.9692660) * x + 1.8760108 * y + 0.0415560 * z;
250 let lb = 0.0556434 * x + (-0.2040259) * y + 1.0572252 * z;
251
252 Self {
253 r: Self::srgb_gamma(lr.max(0.0)).clamp(0.0, 1.0),
254 g: Self::srgb_gamma(lg.max(0.0)).clamp(0.0, 1.0),
255 b: Self::srgb_gamma(lb.max(0.0)).clamp(0.0, 1.0),
256 native_cmyk: None,
257 process_cmyk: None,
258 }
259 }
260
261 fn adapt_xyz_to_d65(x: f64, y: f64, z: f64, src_wp: &[f64; 3]) -> [f64; 3] {
263 const D65: [f64; 3] = [0.95047, 1.0, 1.08883];
265
266 if (src_wp[0] - D65[0]).abs() < 1e-3
268 && (src_wp[1] - D65[1]).abs() < 1e-3
269 && (src_wp[2] - D65[2]).abs() < 1e-3
270 {
271 return [x, y, z];
272 }
273
274 const M: [f64; 9] = [
276 0.8951, -0.7502, 0.0389, 0.2664, 1.7135, -0.0685, -0.1614, 0.0367, 1.0296,
277 ];
278 const M_INV: [f64; 9] = [
280 0.9869929, 0.4323053, -0.0085287, -0.1470543, 0.5183603, 0.0400428, 0.1599627,
281 0.0492912, 0.9684867,
282 ];
283
284 let lms_src = Self::apply_matrix_3x3(&M, src_wp);
286 let lms_d65 = Self::apply_matrix_3x3(&M, &D65);
287
288 let s0 = if lms_src[0].abs() > 1e-10 {
290 lms_d65[0] / lms_src[0]
291 } else {
292 1.0
293 };
294 let s1 = if lms_src[1].abs() > 1e-10 {
295 lms_d65[1] / lms_src[1]
296 } else {
297 1.0
298 };
299 let s2 = if lms_src[2].abs() > 1e-10 {
300 lms_d65[2] / lms_src[2]
301 } else {
302 1.0
303 };
304
305 let lms = Self::apply_matrix_3x3(&M, &[x, y, z]);
307 let scaled = [lms[0] * s0, lms[1] * s1, lms[2] * s2];
308 Self::apply_matrix_3x3(&M_INV, &scaled)
309 }
310
311 fn apply_matrix_3x3(mat: &[f64; 9], v: &[f64; 3]) -> [f64; 3] {
313 [
314 mat[0] * v[0] + mat[3] * v[1] + mat[6] * v[2],
315 mat[1] * v[0] + mat[4] * v[1] + mat[7] * v[2],
316 mat[2] * v[0] + mat[5] * v[1] + mat[8] * v[2],
317 ]
318 }
319
320 fn decode_lookup(table: &[f64], value: f64) -> f64 {
322 let n = table.len();
323 if n < 2 {
324 return table.first().copied().unwrap_or(value);
325 }
326 let idx = value * (n - 1) as f64;
327 let i0 = (idx as usize).min(n - 2);
328 let frac = idx - i0 as f64;
329 table[i0] + (table[i0 + 1] - table[i0]) * frac
330 }
331
332 pub fn from_lab(l_star: f64, a_star: f64, b_star: f64, range: &[f64; 4]) -> Self {
343 const D65: [f64; 3] = [0.95047, 1.0, 1.08883];
345
346 let l_star = l_star.clamp(0.0, 100.0);
347 let a_star = a_star.clamp(range[0], range[1]);
348 let b_star = b_star.clamp(range[2], range[3]);
349
350 let fy = (l_star + 16.0) / 116.0;
351 let fx = a_star / 500.0 + fy;
352 let fz = fy - b_star / 200.0;
353
354 let x = D65[0] * Self::lab_f_inv(fx);
355 let y = D65[1] * Self::lab_f_inv(fy);
356 let z = D65[2] * Self::lab_f_inv(fz);
357
358 Self::from_xyz(x, y, z)
359 }
360
361 fn lab_f_inv(t: f64) -> f64 {
362 if t > 6.0 / 29.0 {
363 t * t * t
364 } else {
365 3.0 * (6.0 / 29.0) * (6.0 / 29.0) * (t - 4.0 / 29.0)
366 }
367 }
368
369 pub fn from_cie_abc(a: f64, b: f64, c: f64, params: &CieAbcParams) -> Self {
371 let mut a = a.clamp(params.range_abc[0], params.range_abc[1]);
372 let mut b = b.clamp(params.range_abc[2], params.range_abc[3]);
373 let mut c = c.clamp(params.range_abc[4], params.range_abc[5]);
374
375 if let Some(ref tables) = params.decode_abc {
376 let ra = params.range_abc[1] - params.range_abc[0];
377 let rb = params.range_abc[3] - params.range_abc[2];
378 let rc = params.range_abc[5] - params.range_abc[4];
379 let na = if ra > 0.0 {
380 (a - params.range_abc[0]) / ra
381 } else {
382 0.0
383 };
384 let nb = if rb > 0.0 {
385 (b - params.range_abc[2]) / rb
386 } else {
387 0.0
388 };
389 let nc = if rc > 0.0 {
390 (c - params.range_abc[4]) / rc
391 } else {
392 0.0
393 };
394 a = Self::decode_lookup(&tables[0], na);
395 b = Self::decode_lookup(&tables[1], nb);
396 c = Self::decode_lookup(&tables[2], nc);
397 }
398
399 let lmn = Self::apply_matrix_3x3(¶ms.matrix_abc, &[a, b, c]);
400
401 let mut l = lmn[0].clamp(params.range_lmn[0], params.range_lmn[1]);
402 let mut m = lmn[1].clamp(params.range_lmn[2], params.range_lmn[3]);
403 let mut n = lmn[2].clamp(params.range_lmn[4], params.range_lmn[5]);
404
405 if let Some(ref tables) = params.decode_lmn {
406 let rl = params.range_lmn[1] - params.range_lmn[0];
407 let rm = params.range_lmn[3] - params.range_lmn[2];
408 let rn = params.range_lmn[5] - params.range_lmn[4];
409 let nl = if rl > 0.0 {
410 (l - params.range_lmn[0]) / rl
411 } else {
412 0.0
413 };
414 let nm = if rm > 0.0 {
415 (m - params.range_lmn[2]) / rm
416 } else {
417 0.0
418 };
419 let nn = if rn > 0.0 {
420 (n - params.range_lmn[4]) / rn
421 } else {
422 0.0
423 };
424 l = Self::decode_lookup(&tables[0], nl);
425 m = Self::decode_lookup(&tables[1], nm);
426 n = Self::decode_lookup(&tables[2], nn);
427 }
428
429 let xyz = Self::apply_matrix_3x3(¶ms.matrix_lmn, &[l, m, n]);
430
431 let xyz = Self::adapt_xyz_to_d65(xyz[0], xyz[1], xyz[2], ¶ms.white_point);
433 Self::from_xyz(xyz[0], xyz[1], xyz[2])
434 }
435
436 pub fn from_cie_a(a: f64, params: &CieAParams) -> Self {
438 let mut a = a.clamp(params.range_a[0], params.range_a[1]);
439
440 if let Some(ref table) = params.decode_a {
441 let ra = params.range_a[1] - params.range_a[0];
442 let na = if ra > 0.0 {
443 (a - params.range_a[0]) / ra
444 } else {
445 0.0
446 };
447 a = Self::decode_lookup(table, na);
448 }
449
450 let lmn = [
451 params.matrix_a[0] * a,
452 params.matrix_a[1] * a,
453 params.matrix_a[2] * a,
454 ];
455
456 let mut l = lmn[0].clamp(params.range_lmn[0], params.range_lmn[1]);
457 let mut m = lmn[1].clamp(params.range_lmn[2], params.range_lmn[3]);
458 let mut n = lmn[2].clamp(params.range_lmn[4], params.range_lmn[5]);
459
460 if let Some(ref tables) = params.decode_lmn {
461 let rl = params.range_lmn[1] - params.range_lmn[0];
462 let rm = params.range_lmn[3] - params.range_lmn[2];
463 let rn = params.range_lmn[5] - params.range_lmn[4];
464 let nl = if rl > 0.0 {
465 (l - params.range_lmn[0]) / rl
466 } else {
467 0.0
468 };
469 let nm = if rm > 0.0 {
470 (m - params.range_lmn[2]) / rm
471 } else {
472 0.0
473 };
474 let nn = if rn > 0.0 {
475 (n - params.range_lmn[4]) / rn
476 } else {
477 0.0
478 };
479 l = Self::decode_lookup(&tables[0], nl);
480 m = Self::decode_lookup(&tables[1], nm);
481 n = Self::decode_lookup(&tables[2], nn);
482 }
483
484 let xyz = Self::apply_matrix_3x3(¶ms.matrix_lmn, &[l, m, n]);
485
486 let xyz = Self::adapt_xyz_to_d65(xyz[0], xyz[1], xyz[2], ¶ms.white_point);
488 Self::from_xyz(xyz[0], xyz[1], xyz[2])
489 }
490
491 pub fn from_cie_def(d: f64, e: f64, f: f64, params: &CieDefParams) -> Self {
493 let (m1, m2, m3) = (params.m1, params.m2, params.m3);
494 if m1 < 2 || m2 < 2 || m3 < 2 {
495 return Self::from_gray(0.0);
496 }
497
498 let d_range = params.range_def[1] - params.range_def[0];
499 let e_range = params.range_def[3] - params.range_def[2];
500 let f_range = params.range_def[5] - params.range_def[4];
501
502 let di = if d_range > 0.0 {
503 ((d - params.range_def[0]) / d_range * (m1 - 1) as f64).clamp(0.0, (m1 - 1) as f64)
504 } else {
505 0.0
506 };
507 let ei = if e_range > 0.0 {
508 ((e - params.range_def[2]) / e_range * (m2 - 1) as f64).clamp(0.0, (m2 - 1) as f64)
509 } else {
510 0.0
511 };
512 let fi = if f_range > 0.0 {
513 ((f - params.range_def[4]) / f_range * (m3 - 1) as f64).clamp(0.0, (m3 - 1) as f64)
514 } else {
515 0.0
516 };
517
518 let di0 = (di as usize).min(m1 - 2);
519 let ei0 = (ei as usize).min(m2 - 2);
520 let fi0 = (fi as usize).min(m3 - 2);
521 let di1 = di0 + 1;
522 let ei1 = ei0 + 1;
523 let fi1 = fi0 + 1;
524 let dd = di - di0 as f64;
525 let de = ei - ei0 as f64;
526 let df = fi - fi0 as f64;
527
528 let stride_e = m3;
529 let stride_d = m2 * m3;
530
531 let mut abc = [0.0f64; 3];
532 for (ch, table) in [¶ms.a_table, ¶ms.b_table, ¶ms.c_table]
533 .iter()
534 .enumerate()
535 {
536 let c000 = table[di0 * stride_d + ei0 * stride_e + fi0];
537 let c001 = table[di0 * stride_d + ei0 * stride_e + fi1];
538 let c010 = table[di0 * stride_d + ei1 * stride_e + fi0];
539 let c011 = table[di0 * stride_d + ei1 * stride_e + fi1];
540 let c100 = table[di1 * stride_d + ei0 * stride_e + fi0];
541 let c101 = table[di1 * stride_d + ei0 * stride_e + fi1];
542 let c110 = table[di1 * stride_d + ei1 * stride_e + fi0];
543 let c111 = table[di1 * stride_d + ei1 * stride_e + fi1];
544
545 let c00 = c000 * (1.0 - df) + c001 * df;
546 let c01 = c010 * (1.0 - df) + c011 * df;
547 let c10 = c100 * (1.0 - df) + c101 * df;
548 let c11 = c110 * (1.0 - df) + c111 * df;
549
550 let c0 = c00 * (1.0 - de) + c01 * de;
551 let c1 = c10 * (1.0 - de) + c11 * de;
552
553 abc[ch] = c0 * (1.0 - dd) + c1 * dd;
554 }
555
556 Self::from_cie_abc(abc[0], abc[1], abc[2], ¶ms.abc_params)
557 }
558
559 pub fn from_cie_defg(d: f64, e: f64, f: f64, g: f64, params: &CieDefgParams) -> Self {
561 let (m1, m2, m3, m4) = (params.m1, params.m2, params.m3, params.m4);
562 if m1 == 0 || m2 == 0 || m3 == 0 || m4 == 0 {
563 return Self::from_gray(0.0);
564 }
565
566 let d_range = params.range_defg[1] - params.range_defg[0];
567 let e_range = params.range_defg[3] - params.range_defg[2];
568 let f_range = params.range_defg[5] - params.range_defg[4];
569 let g_range = params.range_defg[7] - params.range_defg[6];
570
571 let di = if d_range > 0.0 {
572 ((d - params.range_defg[0]) / d_range * (m1 - 1) as f64 + 0.5) as usize
573 } else {
574 0
575 }
576 .min(m1 - 1);
577 let ei = if e_range > 0.0 {
578 ((e - params.range_defg[2]) / e_range * (m2 - 1) as f64 + 0.5) as usize
579 } else {
580 0
581 }
582 .min(m2 - 1);
583 let fi = if f_range > 0.0 {
584 ((f - params.range_defg[4]) / f_range * (m3 - 1) as f64 + 0.5) as usize
585 } else {
586 0
587 }
588 .min(m3 - 1);
589 let gi = if g_range > 0.0 {
590 ((g - params.range_defg[6]) / g_range * (m4 - 1) as f64 + 0.5) as usize
591 } else {
592 0
593 }
594 .min(m4 - 1);
595
596 let idx = di * m2 * m3 * m4 + ei * m3 * m4 + fi * m4 + gi;
597 if idx >= params.a_table.len() {
598 return Self::from_gray(0.0);
599 }
600
601 Self::from_cie_abc(
602 params.a_table[idx],
603 params.b_table[idx],
604 params.c_table[idx],
605 ¶ms.abc_params,
606 )
607 }
608}
609
610#[derive(Clone, Debug)]
612pub struct CieAbcParams {
613 pub range_abc: [f64; 6],
614 pub decode_abc: Option<[Vec<f64>; 3]>,
615 pub matrix_abc: [f64; 9],
616 pub range_lmn: [f64; 6],
617 pub decode_lmn: Option<[Vec<f64>; 3]>,
618 pub matrix_lmn: [f64; 9],
619 pub white_point: [f64; 3],
620}
621
622impl Default for CieAbcParams {
623 fn default() -> Self {
624 Self {
625 range_abc: [0.0, 1.0, 0.0, 1.0, 0.0, 1.0],
626 decode_abc: None,
627 matrix_abc: [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0],
628 range_lmn: [0.0, 1.0, 0.0, 1.0, 0.0, 1.0],
629 decode_lmn: None,
630 matrix_lmn: [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0],
631 white_point: [0.9505, 1.0, 1.089],
632 }
633 }
634}
635
636#[derive(Clone, Debug)]
638pub struct CieAParams {
639 pub range_a: [f64; 2],
640 pub decode_a: Option<Vec<f64>>,
641 pub matrix_a: [f64; 3],
642 pub range_lmn: [f64; 6],
643 pub decode_lmn: Option<[Vec<f64>; 3]>,
644 pub matrix_lmn: [f64; 9],
645 pub white_point: [f64; 3],
646}
647
648impl Default for CieAParams {
649 fn default() -> Self {
650 Self {
651 range_a: [0.0, 1.0],
652 decode_a: None,
653 matrix_a: [1.0, 1.0, 1.0],
654 range_lmn: [0.0, 1.0, 0.0, 1.0, 0.0, 1.0],
655 decode_lmn: None,
656 matrix_lmn: [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0],
657 white_point: [0.9505, 1.0, 1.089],
658 }
659 }
660}
661
662#[derive(Clone, Debug)]
664pub struct CieDefParams {
665 pub range_def: [f64; 6],
666 pub m1: usize,
667 pub m2: usize,
668 pub m3: usize,
669 pub a_table: Vec<f64>,
670 pub b_table: Vec<f64>,
671 pub c_table: Vec<f64>,
672 pub abc_params: CieAbcParams,
673}
674
675#[derive(Clone, Debug)]
677pub struct CieDefgParams {
678 pub range_defg: [f64; 8],
679 pub m1: usize,
680 pub m2: usize,
681 pub m3: usize,
682 pub m4: usize,
683 pub a_table: Vec<f64>,
684 pub b_table: Vec<f64>,
685 pub c_table: Vec<f64>,
686 pub abc_params: CieAbcParams,
687}
688
689#[cfg(test)]
690mod tests {
691 use super::*;
692
693 #[test]
694 fn test_color_from_gray() {
695 let c = DeviceColor::from_gray(0.5);
696 assert!((c.r - 0.5).abs() < 1e-10);
697 assert!((c.g - 0.5).abs() < 1e-10);
698 assert!((c.b - 0.5).abs() < 1e-10);
699 }
700
701 #[test]
702 fn test_color_from_cmyk() {
703 let c = DeviceColor::from_cmyk(1.0, 0.0, 0.0, 0.0);
704 assert!((c.r - 0.0).abs() < 1e-10);
705 assert!((c.g - 1.0).abs() < 1e-10);
706 assert!((c.b - 1.0).abs() < 1e-10);
707 }
708
709 #[test]
710 fn test_color_from_hsb() {
711 let c = DeviceColor::from_hsb(0.0, 1.0, 1.0);
712 assert!((c.r - 1.0).abs() < 1e-10);
713 assert!((c.g - 0.0).abs() < 1e-10);
714 assert!((c.b - 0.0).abs() < 1e-10);
715
716 let c = DeviceColor::from_hsb(1.0 / 3.0, 1.0, 1.0);
717 assert!((c.r - 0.0).abs() < 1e-10);
718 assert!((c.g - 1.0).abs() < 1e-10);
719 assert!((c.b - 0.0).abs() < 1e-10);
720 }
721
722 #[test]
723 fn test_color_gray_roundtrip() {
724 let c = DeviceColor::from_gray(0.5);
725 let gray = c.to_gray();
726 assert!((gray - 0.5).abs() < 1e-10);
727 }
728
729 #[test]
730 fn test_color_hsb_roundtrip() {
731 let c = DeviceColor::from_hsb(0.6, 0.8, 0.9);
732 let (h, s, b) = c.to_hsb();
733 assert!((h - 0.6).abs() < 0.01);
734 assert!((s - 0.8).abs() < 0.01);
735 assert!((b - 0.9).abs() < 0.01);
736 }
737
738 #[test]
739 fn test_linecap_from_i32() {
740 assert_eq!(LineCap::from_i32(0), Some(LineCap::Butt));
741 assert_eq!(LineCap::from_i32(1), Some(LineCap::Round));
742 assert_eq!(LineCap::from_i32(2), Some(LineCap::Square));
743 assert_eq!(LineCap::from_i32(3), None);
744 }
745
746 #[test]
747 fn test_linejoin_from_i32() {
748 assert_eq!(LineJoin::from_i32(0), Some(LineJoin::Miter));
749 assert_eq!(LineJoin::from_i32(1), Some(LineJoin::Round));
750 assert_eq!(LineJoin::from_i32(2), Some(LineJoin::Bevel));
751 assert_eq!(LineJoin::from_i32(3), None);
752 }
753}