1use crate::drawing::backend::{BackendCoord, BackendStyle, DrawingBackend, DrawingErrorKind};
2use crate::style::{Color, RGBAColor};
3use std::marker::PhantomData;
4
5#[cfg(all(not(target_arch = "wasm32"), feature = "image"))]
6mod image_encoding_support {
7 pub(super) use image::{ImageBuffer, ImageError, Rgb};
8 pub(super) use std::path::Path;
9 pub(super) type BorrowedImage<'a> = ImageBuffer<Rgb<u8>, &'a mut [u8]>;
10}
11
12#[cfg(all(not(target_arch = "wasm32"), feature = "image"))]
13use image_encoding_support::*;
14
15#[derive(Debug)]
16pub enum BitMapBackendError {
18 InvalidBuffer,
20 IOError(std::io::Error),
22 #[cfg(all(not(target_arch = "wasm32"), feature = "image"))]
23 ImageError(ImageError),
25}
26
27impl std::fmt::Display for BitMapBackendError {
28 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
29 write!(f, "{:?}", self)
30 }
31}
32
33impl std::error::Error for BitMapBackendError {}
34
35#[inline(always)]
36fn blend(prev: &mut u8, new: u8, a: u64) {
37 if new > *prev {
38 *prev += (u64::from(new - *prev) * a / 256) as u8
39 } else {
40 *prev -= (u64::from(*prev - new) * a / 256) as u8
41 }
42}
43
44#[cfg(all(feature = "gif", not(target_arch = "wasm32"), feature = "image"))]
45mod gif_support {
46 use super::*;
47 use gif::{Encoder as GifEncoder, Frame as GifFrame, Repeat, SetParameter};
48 use std::fs::File;
49
50 pub(super) struct GifFile {
51 encoder: GifEncoder<File>,
52 height: u32,
53 width: u32,
54 delay: u32,
55 }
56
57 impl GifFile {
58 pub(super) fn new<T: AsRef<Path>>(
59 path: T,
60 dim: (u32, u32),
61 delay: u32,
62 ) -> Result<Self, BitMapBackendError> {
63 let mut encoder = GifEncoder::new(
64 File::create(path.as_ref()).map_err(BitMapBackendError::IOError)?,
65 dim.0 as u16,
66 dim.1 as u16,
67 &[],
68 )
69 .map_err(BitMapBackendError::IOError)?;
70
71 encoder
72 .set(Repeat::Infinite)
73 .map_err(BitMapBackendError::IOError)?;
74
75 Ok(Self {
76 encoder,
77 width: dim.0,
78 height: dim.1,
79 delay: (delay + 5) / 10,
80 })
81 }
82
83 pub(super) fn flush_frame(&mut self, buffer: &[u8]) -> Result<(), BitMapBackendError> {
84 let mut frame =
85 GifFrame::from_rgb_speed(self.width as u16, self.height as u16, buffer, 10);
86
87 frame.delay = self.delay as u16;
88
89 self.encoder
90 .write_frame(&frame)
91 .map_err(BitMapBackendError::IOError)?;
92
93 Ok(())
94 }
95 }
96}
97
98enum Target<'a> {
99 #[cfg(all(not(target_arch = "wasm32"), feature = "image"))]
100 File(&'a Path),
101 Buffer(PhantomData<&'a u32>),
102 #[cfg(all(feature = "gif", not(target_arch = "wasm32"), feature = "image"))]
103 Gif(Box<gif_support::GifFile>),
104}
105
106enum Buffer<'a> {
107 #[cfg(all(not(target_arch = "wasm32"), feature = "image"))]
108 Owned(Vec<u8>),
109 Borrowed(&'a mut [u8]),
110}
111
112impl<'a> Buffer<'a> {
113 #[inline(always)]
114 fn borrow_buffer(&mut self) -> &mut [u8] {
115 match self {
116 #[cfg(all(not(target_arch = "wasm32"), feature = "image"))]
117 Buffer::Owned(buf) => &mut buf[..],
118 Buffer::Borrowed(buf) => *buf,
119 }
120 }
121}
122
123pub trait PixelFormat: Sized {
125 const PIXEL_SIZE: usize;
127
128 const EFFECTIVE_PIXEL_SIZE: usize;
131
132 fn byte_at(r: u8, g: u8, b: u8, a: u64, idx: usize) -> u8;
134
135 fn decode_pixel(data: &[u8]) -> (u8, u8, u8, u64);
137
138 fn blend_rect_fast(
145 target: &mut BitMapBackend<'_, Self>,
146 upper_left: (i32, i32),
147 bottom_right: (i32, i32),
148 r: u8,
149 g: u8,
150 b: u8,
151 a: f64,
152 );
153
154 fn fill_vertical_line_fast(
161 target: &mut BitMapBackend<'_, Self>,
162 x: i32,
163 ys: (i32, i32),
164 r: u8,
165 g: u8,
166 b: u8,
167 ) {
168 let (w, h) = target.get_size();
169 let w = w as i32;
170 let h = h as i32;
171
172 if x < 0 || x >= w {
174 return;
175 }
176
177 let dst = target.get_raw_pixel_buffer();
178 let (mut y0, mut y1) = ys;
179 if y0 > y1 {
180 std::mem::swap(&mut y0, &mut y1);
181 }
182 y0 = y0.max(0);
184 y1 = y1.min(h - 1);
185 for y in y0..=y1 {
187 for idx in 0..Self::EFFECTIVE_PIXEL_SIZE {
188 dst[(y * w + x) as usize * Self::PIXEL_SIZE + idx] = Self::byte_at(r, g, b, 0, idx);
189 }
190 }
191 }
192
193 fn fill_rect_fast(
200 target: &mut BitMapBackend<'_, Self>,
201 upper_left: (i32, i32),
202 bottom_right: (i32, i32),
203 r: u8,
204 g: u8,
205 b: u8,
206 );
207
208 #[inline(always)]
209 fn draw_pixel(
216 target: &mut BitMapBackend<'_, Self>,
217 point: (i32, i32),
218 (r, g, b): (u8, u8, u8),
219 alpha: f64,
220 ) {
221 let (x, y) = (point.0 as usize, point.1 as usize);
222 let (w, _) = target.get_size();
223 let buf = target.get_raw_pixel_buffer();
224 let w = w as usize;
225 let base = (y * w + x) * Self::PIXEL_SIZE;
226
227 if base < buf.len() {
228 unsafe {
229 if alpha >= 1.0 - 1.0 / 256.0 {
230 for idx in 0..Self::EFFECTIVE_PIXEL_SIZE {
231 *buf.get_unchecked_mut(base + idx) = Self::byte_at(r, g, b, 0, idx);
232 }
233 } else {
234 if alpha <= 0.0 {
235 return;
236 }
237
238 let alpha = (alpha * 256.0).floor() as u64;
239 for idx in 0..Self::EFFECTIVE_PIXEL_SIZE {
240 blend(
241 buf.get_unchecked_mut(base + idx),
242 Self::byte_at(r, g, b, 0, idx),
243 alpha,
244 );
245 }
246 }
247 }
248 }
249 }
250
251 fn can_be_saved() -> bool {
257 false
258 }
259}
260
261pub struct RGBPixel;
263
264pub struct BGRXPixel;
266
267impl PixelFormat for RGBPixel {
268 const PIXEL_SIZE: usize = 3;
269 const EFFECTIVE_PIXEL_SIZE: usize = 3;
270
271 #[inline(always)]
272 fn byte_at(r: u8, g: u8, b: u8, _a: u64, idx: usize) -> u8 {
273 match idx {
274 0 => r,
275 1 => g,
276 2 => b,
277 _ => 0xff,
278 }
279 }
280
281 #[inline(always)]
282 fn decode_pixel(data: &[u8]) -> (u8, u8, u8, u64) {
283 (data[0], data[1], data[2], 0x255)
284 }
285
286 fn can_be_saved() -> bool {
287 true
288 }
289
290 #[allow(clippy::many_single_char_names, clippy::cast_ptr_alignment)]
291 fn blend_rect_fast(
292 target: &mut BitMapBackend<'_, Self>,
293 upper_left: (i32, i32),
294 bottom_right: (i32, i32),
295 r: u8,
296 g: u8,
297 b: u8,
298 a: f64,
299 ) {
300 let (w, h) = target.get_size();
301 let a = a.min(1.0).max(0.0);
302 if a == 0.0 {
303 return;
304 }
305
306 let (x0, y0) = (
307 upper_left.0.min(bottom_right.0).max(0),
308 upper_left.1.min(bottom_right.1).max(0),
309 );
310 let (x1, y1) = (
311 upper_left.0.max(bottom_right.0).min(w as i32 - 1),
312 upper_left.1.max(bottom_right.1).min(h as i32 - 1),
313 );
314
315 if x0 > x1 || y0 > y1 {
318 return;
319 }
320
321 let dst = target.get_raw_pixel_buffer();
322
323 let a = (256.0 * a).floor() as u64;
324
325 #[rustfmt::skip]
328 let (p1, p2, p3): (u64, u64, u64) = unsafe {
329 std::mem::transmute([
330 u16::from(r), u16::from(b), u16::from(g), u16::from(r), u16::from(b), u16::from(g), u16::from(r), u16::from(b), u16::from(g), u16::from(r), u16::from(b), u16::from(g), ])
334 };
335
336 #[rustfmt::skip]
337 let (q1, q2, q3): (u64, u64, u64) = unsafe {
338 std::mem::transmute([
339 u16::from(g), u16::from(r), u16::from(b), u16::from(g), u16::from(r), u16::from(b), u16::from(g), u16::from(r), u16::from(b), u16::from(g), u16::from(r), u16::from(b), ])
343 };
344
345 const N: u64 = 0xff00_ff00_ff00_ff00;
346 const M: u64 = 0x00ff_00ff_00ff_00ff;
347
348 for y in y0..=y1 {
349 let start = (y * w as i32 + x0) as usize;
350 let count = (x1 - x0 + 1) as usize;
351
352 let start_ptr = &mut dst[start * Self::PIXEL_SIZE] as *mut u8 as *mut [u8; 24];
353 let slice = unsafe { std::slice::from_raw_parts_mut(start_ptr, (count - 1) / 8) };
354 for p in slice.iter_mut() {
355 let ptr = p as *mut [u8; 24] as *mut (u64, u64, u64);
356 let (d1, d2, d3) = unsafe { *ptr };
357 let (mut h1, mut h2, mut h3) = ((d1 >> 8) & M, (d2 >> 8) & M, (d3 >> 8) & M);
358 let (mut l1, mut l2, mut l3) = (d1 & M, d2 & M, d3 & M);
359
360 #[cfg(target_endian = "little")]
361 {
362 h1 = (h1 * (256 - a) + q1 * a) & N;
363 h2 = (h2 * (256 - a) + q2 * a) & N;
364 h3 = (h3 * (256 - a) + q3 * a) & N;
365 l1 = ((l1 * (256 - a) + p1 * a) & N) >> 8;
366 l2 = ((l2 * (256 - a) + p2 * a) & N) >> 8;
367 l3 = ((l3 * (256 - a) + p3 * a) & N) >> 8;
368 }
369
370 #[cfg(target_endian = "big")]
371 {
372 h1 = (h1 * (256 - a) + p1 * a) & N;
373 h2 = (h2 * (256 - a) + p2 * a) & N;
374 h3 = (h3 * (256 - a) + p3 * a) & N;
375 l1 = ((l1 * (256 - a) + q1 * a) & N) >> 8;
376 l2 = ((l2 * (256 - a) + q2 * a) & N) >> 8;
377 l3 = ((l3 * (256 - a) + q3 * a) & N) >> 8;
378 }
379
380 unsafe {
381 *ptr = (h1 | l1, h2 | l2, h3 | l3);
382 }
383 }
384
385 let mut iter = dst[((start + slice.len() * 8) * Self::PIXEL_SIZE)
386 ..((start + count) * Self::PIXEL_SIZE)]
387 .iter_mut();
388 for _ in (slice.len() * 8)..count {
389 blend(iter.next().unwrap(), r, a);
390 blend(iter.next().unwrap(), g, a);
391 blend(iter.next().unwrap(), b, a);
392 }
393 }
394 }
395
396 #[allow(clippy::many_single_char_names, clippy::cast_ptr_alignment)]
397 fn fill_rect_fast(
398 target: &mut BitMapBackend<'_, Self>,
399 upper_left: (i32, i32),
400 bottom_right: (i32, i32),
401 r: u8,
402 g: u8,
403 b: u8,
404 ) {
405 let (w, h) = target.get_size();
406 let (x0, y0) = (
407 upper_left.0.min(bottom_right.0).max(0),
408 upper_left.1.min(bottom_right.1).max(0),
409 );
410 let (x1, y1) = (
411 upper_left.0.max(bottom_right.0).min(w as i32 - 1),
412 upper_left.1.max(bottom_right.1).min(h as i32 - 1),
413 );
414
415 if x0 > x1 || y0 > y1 {
418 return;
419 }
420
421 let dst = target.get_raw_pixel_buffer();
422
423 if r == g && g == b {
424 if x0 != 0 || x1 != w as i32 - 1 {
426 for y in y0..=y1 {
429 let start = (y * w as i32 + x0) as usize;
430 let count = (x1 - x0 + 1) as usize;
431 dst[(start * Self::PIXEL_SIZE)..((start + count) * Self::PIXEL_SIZE)]
432 .iter_mut()
433 .for_each(|e| *e = r);
434 }
435 } else {
436 dst[Self::PIXEL_SIZE * (y0 * w as i32) as usize
438 ..((y1 + 1) * w as i32) as usize * Self::PIXEL_SIZE]
439 .iter_mut()
440 .for_each(|e| *e = r);
441 }
442 } else {
443 let count = (x1 - x0 + 1) as usize;
444 if count < 8 {
445 for y in y0..=y1 {
446 let start = (y * w as i32 + x0) as usize;
447 let mut iter = dst
448 [(start * Self::PIXEL_SIZE)..((start + count) * Self::PIXEL_SIZE)]
449 .iter_mut();
450 for _ in 0..=(x1 - x0) {
451 *iter.next().unwrap() = r;
452 *iter.next().unwrap() = g;
453 *iter.next().unwrap() = b;
454 }
455 }
456 } else {
457 for y in y0..=y1 {
458 let start = (y * w as i32 + x0) as usize;
459 let start_ptr = &mut dst[start * Self::PIXEL_SIZE] as *mut u8 as *mut [u8; 24];
460 let slice =
461 unsafe { std::slice::from_raw_parts_mut(start_ptr, (count - 1) / 8) };
462 for p in slice.iter_mut() {
463 let ptr = p as *mut [u8; 24] as *mut u64;
467 unsafe {
468 let (d1, d2, d3): (u64, u64, u64) = std::mem::transmute([
469 r, g, b, r, g, b, r, g, b, r, g, b, r, g, b, r, g, b, r, g, b, r, g, b, ]);
473 *ptr = d1;
474 *ptr.offset(1) = d2;
475 *ptr.offset(2) = d3;
476 }
477 }
478
479 for idx in (slice.len() * 8)..count {
480 dst[start * Self::PIXEL_SIZE + idx * Self::PIXEL_SIZE] = r;
481 dst[start * Self::PIXEL_SIZE + idx * Self::PIXEL_SIZE + 1] = g;
482 dst[start * Self::PIXEL_SIZE + idx * Self::PIXEL_SIZE + 2] = b;
483 }
484 }
485 }
486 }
487 }
488}
489
490impl PixelFormat for BGRXPixel {
491 const PIXEL_SIZE: usize = 4;
492 const EFFECTIVE_PIXEL_SIZE: usize = 3;
493
494 #[inline(always)]
495 fn byte_at(r: u8, g: u8, b: u8, _a: u64, idx: usize) -> u8 {
496 match idx {
497 0 => b,
498 1 => g,
499 2 => r,
500 _ => 0xff,
501 }
502 }
503
504 #[inline(always)]
505 fn decode_pixel(data: &[u8]) -> (u8, u8, u8, u64) {
506 (data[2], data[1], data[0], 0x255)
507 }
508
509 #[allow(clippy::many_single_char_names, clippy::cast_ptr_alignment)]
510 fn blend_rect_fast(
511 target: &mut BitMapBackend<'_, Self>,
512 upper_left: (i32, i32),
513 bottom_right: (i32, i32),
514 r: u8,
515 g: u8,
516 b: u8,
517 a: f64,
518 ) {
519 let (w, h) = target.get_size();
520 let a = a.min(1.0).max(0.0);
521 if a == 0.0 {
522 return;
523 }
524
525 let (x0, y0) = (
526 upper_left.0.min(bottom_right.0).max(0),
527 upper_left.1.min(bottom_right.1).max(0),
528 );
529 let (x1, y1) = (
530 upper_left.0.max(bottom_right.0).min(w as i32 - 1),
531 upper_left.1.max(bottom_right.1).min(h as i32 - 1),
532 );
533
534 if x0 > x1 || y0 > y1 {
537 return;
538 }
539
540 let dst = target.get_raw_pixel_buffer();
541
542 let a = (256.0 * a).floor() as u64;
543
544 #[rustfmt::skip]
547 let p: u64 = unsafe {
548 std::mem::transmute([
549 u16::from(b), u16::from(r), u16::from(b), u16::from(r), ])
551 };
552
553 #[rustfmt::skip]
554 let q: u64 = unsafe {
555 std::mem::transmute([
556 u16::from(g), 0u16, u16::from(g), 0u16, ])
558 };
559
560 const N: u64 = 0xff00_ff00_ff00_ff00;
561 const M: u64 = 0x00ff_00ff_00ff_00ff;
562
563 for y in y0..=y1 {
564 let start = (y * w as i32 + x0) as usize;
565 let count = (x1 - x0 + 1) as usize;
566
567 let start_ptr = &mut dst[start * Self::PIXEL_SIZE] as *mut u8 as *mut [u8; 8];
568 let slice = unsafe { std::slice::from_raw_parts_mut(start_ptr, (count - 1) / 2) };
569 for rp in slice.iter_mut() {
570 let ptr = rp as *mut [u8; 8] as *mut u64;
571 let d1 = unsafe { *ptr };
572 let mut h = (d1 >> 8) & M;
573 let mut l = d1 & M;
574
575 #[cfg(target_endian = "little")]
576 {
577 h = (h * (256 - a) + q * a) & N;
578 l = ((l * (256 - a) + p * a) & N) >> 8;
579 }
580
581 #[cfg(target_endian = "big")]
582 {
583 h = (h * (256 - a) + p * a) & N;
584 l = ((l * (256 - a) + q * a) & N) >> 8;
585 }
586
587 unsafe {
588 *ptr = h | l;
589 }
590 }
591
592 let mut iter = dst[((start + slice.len() * 2) * Self::PIXEL_SIZE)
593 ..((start + count) * Self::PIXEL_SIZE)]
594 .iter_mut();
595 for _ in (slice.len() * 2)..count {
596 blend(iter.next().unwrap(), b, a);
597 blend(iter.next().unwrap(), g, a);
598 blend(iter.next().unwrap(), r, a);
599 iter.next();
600 }
601 }
602 }
603
604 #[allow(clippy::many_single_char_names, clippy::cast_ptr_alignment)]
605 fn fill_rect_fast(
606 target: &mut BitMapBackend<'_, Self>,
607 upper_left: (i32, i32),
608 bottom_right: (i32, i32),
609 r: u8,
610 g: u8,
611 b: u8,
612 ) {
613 let (w, h) = target.get_size();
614 let (x0, y0) = (
615 upper_left.0.min(bottom_right.0).max(0),
616 upper_left.1.min(bottom_right.1).max(0),
617 );
618 let (x1, y1) = (
619 upper_left.0.max(bottom_right.0).min(w as i32 - 1),
620 upper_left.1.max(bottom_right.1).min(h as i32 - 1),
621 );
622
623 if x0 > x1 || y0 > y1 {
626 return;
627 }
628
629 let dst = target.get_raw_pixel_buffer();
630
631 if r == g && g == b {
632 if x0 != 0 || x1 != w as i32 - 1 {
634 for y in y0..=y1 {
637 let start = (y * w as i32 + x0) as usize;
638 let count = (x1 - x0 + 1) as usize;
639 dst[(start * Self::PIXEL_SIZE)..((start + count) * Self::PIXEL_SIZE)]
640 .iter_mut()
641 .for_each(|e| *e = r);
642 }
643 } else {
644 dst[Self::PIXEL_SIZE * (y0 * w as i32) as usize
646 ..((y1 + 1) * w as i32) as usize * Self::PIXEL_SIZE]
647 .iter_mut()
648 .for_each(|e| *e = r);
649 }
650 } else {
651 let count = (x1 - x0 + 1) as usize;
652 if count < 8 {
653 for y in y0..=y1 {
654 let start = (y * w as i32 + x0) as usize;
655 let mut iter = dst
656 [(start * Self::PIXEL_SIZE)..((start + count) * Self::PIXEL_SIZE)]
657 .iter_mut();
658 for _ in 0..=(x1 - x0) {
659 *iter.next().unwrap() = b;
660 *iter.next().unwrap() = g;
661 *iter.next().unwrap() = r;
662 iter.next();
663 }
664 }
665 } else {
666 for y in y0..=y1 {
667 let start = (y * w as i32 + x0) as usize;
668 let start_ptr = &mut dst[start * Self::PIXEL_SIZE] as *mut u8 as *mut [u8; 8];
669 let slice =
670 unsafe { std::slice::from_raw_parts_mut(start_ptr, (count - 1) / 2) };
671 for p in slice.iter_mut() {
672 let ptr = p as *mut [u8; 8] as *mut u64;
676 unsafe {
677 let d: u64 = std::mem::transmute([
678 b, g, r, 0, b, g, r, 0, ]);
680 *ptr = d;
681 }
682 }
683
684 for idx in (slice.len() * 2)..count {
685 dst[start * Self::PIXEL_SIZE + idx * Self::PIXEL_SIZE] = b;
686 dst[start * Self::PIXEL_SIZE + idx * Self::PIXEL_SIZE + 1] = g;
687 dst[start * Self::PIXEL_SIZE + idx * Self::PIXEL_SIZE + 2] = r;
688 }
689 }
690 }
691 }
692 }
693}
694
695pub struct BitMapBackend<'a, P: PixelFormat = RGBPixel> {
697 #[allow(dead_code)]
699 target: Target<'a>,
700 size: (u32, u32),
702 buffer: Buffer<'a>,
704 saved: bool,
706 _pantomdata: PhantomData<P>,
707}
708
709impl<'a, P: PixelFormat> BitMapBackend<'a, P> {
710 const PIXEL_SIZE: usize = P::PIXEL_SIZE;
712}
713
714impl<'a> BitMapBackend<'a, RGBPixel> {
715 #[cfg(all(not(target_arch = "wasm32"), feature = "image"))]
717 pub fn new<T: AsRef<Path> + ?Sized>(path: &'a T, (w, h): (u32, u32)) -> Self {
718 Self {
719 target: Target::File(path.as_ref()),
720 size: (w, h),
721 buffer: Buffer::Owned(vec![0; Self::PIXEL_SIZE * (w * h) as usize]),
722 saved: false,
723 _pantomdata: PhantomData,
724 }
725 }
726
727 #[cfg(all(feature = "gif", not(target_arch = "wasm32"), feature = "image"))]
737 pub fn gif<T: AsRef<Path>>(
738 path: T,
739 (w, h): (u32, u32),
740 frame_delay: u32,
741 ) -> Result<Self, BitMapBackendError> {
742 Ok(Self {
743 target: Target::Gif(Box::new(gif_support::GifFile::new(
744 path,
745 (w, h),
746 frame_delay,
747 )?)),
748 size: (w, h),
749 buffer: Buffer::Owned(vec![0; Self::PIXEL_SIZE * (w * h) as usize]),
750 saved: false,
751 _pantomdata: PhantomData,
752 })
753 }
754
755 pub fn with_buffer(buf: &'a mut [u8], (w, h): (u32, u32)) -> Self {
768 Self::with_buffer_and_format(buf, (w, h)).expect("Wrong buffer size")
769 }
770}
771
772impl<'a, P: PixelFormat> BitMapBackend<'a, P> {
773 pub fn with_buffer_and_format(
782 buf: &'a mut [u8],
783 (w, h): (u32, u32),
784 ) -> Result<Self, BitMapBackendError> {
785 if (w * h) as usize * Self::PIXEL_SIZE > buf.len() {
786 return Err(BitMapBackendError::InvalidBuffer);
787 }
788
789 Ok(Self {
790 target: Target::Buffer(PhantomData),
791 size: (w, h),
792 buffer: Buffer::Borrowed(buf),
793 saved: false,
794 _pantomdata: PhantomData,
795 })
796 }
797
798 #[inline(always)]
799 fn get_raw_pixel_buffer(&mut self) -> &mut [u8] {
800 self.buffer.borrow_buffer()
801 }
802
803 pub fn split(&mut self, area_size: &[u32]) -> Vec<BitMapBackend<P>> {
809 let (w, h) = self.get_size();
810 let buf = self.get_raw_pixel_buffer();
811
812 let base_addr = &mut buf[0] as *mut u8;
813 let mut split_points = vec![0];
814 for size in area_size {
815 let next = split_points.last().unwrap() + size;
816 if next >= h {
817 break;
818 }
819 split_points.push(next);
820 }
821 split_points.push(h);
822
823 split_points
824 .iter()
825 .zip(split_points.iter().skip(1))
826 .map(|(begin, end)| {
827 let actual_buf = unsafe {
828 std::slice::from_raw_parts_mut(
829 base_addr.offset((begin * w) as isize * Self::PIXEL_SIZE as isize),
830 ((end - begin) * w) as usize * Self::PIXEL_SIZE,
831 )
832 };
833 Self::with_buffer_and_format(actual_buf, (w, end - begin)).unwrap()
834 })
835 .collect()
836 }
837}
838
839impl<'a, P: PixelFormat> DrawingBackend for BitMapBackend<'a, P> {
840 type ErrorType = BitMapBackendError;
841
842 fn get_size(&self) -> (u32, u32) {
843 self.size
844 }
845
846 fn ensure_prepared(&mut self) -> Result<(), DrawingErrorKind<BitMapBackendError>> {
847 self.saved = false;
848 Ok(())
849 }
850
851 #[cfg(any(target_arch = "wasm32", not(feature = "image")))]
852 fn present(&mut self) -> Result<(), DrawingErrorKind<BitMapBackendError>> {
853 Ok(())
854 }
855
856 #[cfg(all(not(target_arch = "wasm32"), feature = "image"))]
857 fn present(&mut self) -> Result<(), DrawingErrorKind<BitMapBackendError>> {
858 if !P::can_be_saved() {
859 return Ok(());
860 }
861 let (w, h) = self.get_size();
862 match &mut self.target {
863 Target::File(path) => {
864 if let Some(img) = BorrowedImage::from_raw(w, h, self.buffer.borrow_buffer()) {
865 img.save(&path).map_err(|x| {
866 DrawingErrorKind::DrawingError(BitMapBackendError::IOError(x))
867 })?;
868 self.saved = true;
869 Ok(())
870 } else {
871 Err(DrawingErrorKind::DrawingError(
872 BitMapBackendError::InvalidBuffer,
873 ))
874 }
875 }
876 Target::Buffer(_) => Ok(()),
877
878 #[cfg(all(feature = "gif", not(target_arch = "wasm32"), feature = "image"))]
879 Target::Gif(target) => {
880 target
881 .flush_frame(self.buffer.borrow_buffer())
882 .map_err(DrawingErrorKind::DrawingError)?;
883 self.saved = true;
884 Ok(())
885 }
886 }
887 }
888
889 fn draw_pixel(
890 &mut self,
891 point: BackendCoord,
892 color: &RGBAColor,
893 ) -> Result<(), DrawingErrorKind<BitMapBackendError>> {
894 if point.0 < 0 || point.1 < 0 {
895 return Ok(());
896 }
897
898 let alpha = color.alpha();
899 let rgb = color.rgb();
900
901 P::draw_pixel(self, point, rgb, alpha);
902
903 Ok(())
904 }
905
906 fn draw_line<S: BackendStyle>(
907 &mut self,
908 from: (i32, i32),
909 to: (i32, i32),
910 style: &S,
911 ) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
912 let alpha = style.as_color().alpha();
913 let (r, g, b) = style.as_color().rgb();
914
915 if from.0 == to.0 || from.1 == to.1 {
916 if alpha >= 1.0 {
917 if from.1 == to.1 {
918 P::fill_rect_fast(self, from, to, r, g, b);
919 } else {
920 P::fill_vertical_line_fast(self, from.0, (from.1, to.1), r, g, b);
921 }
922 } else {
923 P::blend_rect_fast(self, from, to, r, g, b, alpha);
924 }
925 return Ok(());
926 }
927
928 crate::drawing::rasterizer::draw_line(self, from, to, style)
929 }
930
931 fn draw_rect<S: BackendStyle>(
932 &mut self,
933 upper_left: (i32, i32),
934 bottom_right: (i32, i32),
935 style: &S,
936 fill: bool,
937 ) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
938 let alpha = style.as_color().alpha();
939 let (r, g, b) = style.as_color().rgb();
940 if fill {
941 if alpha >= 1.0 {
942 P::fill_rect_fast(self, upper_left, bottom_right, r, g, b);
943 } else {
944 P::blend_rect_fast(self, upper_left, bottom_right, r, g, b, alpha);
945 }
946 return Ok(());
947 }
948 crate::drawing::rasterizer::draw_rect(self, upper_left, bottom_right, style, fill)
949 }
950
951 fn blit_bitmap<'b>(
952 &mut self,
953 pos: BackendCoord,
954 (sw, sh): (u32, u32),
955 src: &'b [u8],
956 ) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
957 let (dw, dh) = self.get_size();
958
959 let (x0, y0) = pos;
960 let (x1, y1) = (x0 + sw as i32, y0 + sh as i32);
961
962 let (x0, y0, x1, y1) = (x0.max(0), y0.max(0), x1.min(dw as i32), y1.min(dh as i32));
963
964 if x0 == x1 || y0 == y1 {
965 return Ok(());
966 }
967
968 let mut chunk_size = (x1 - x0) as usize;
969 let mut num_chunks = (y1 - y0) as usize;
970 let dst_gap = dw as usize - chunk_size;
971 let src_gap = sw as usize - chunk_size;
972
973 let dst_start = Self::PIXEL_SIZE * (y0 as usize * dw as usize + x0 as usize);
974
975 let mut dst = &mut self.get_raw_pixel_buffer()[dst_start..];
976
977 let src_start =
978 Self::PIXEL_SIZE * ((sh as i32 + y0 - y1) * sw as i32 + (sw as i32 + x0 - x1)) as usize;
979 let mut src = &src[src_start..];
980
981 if src_gap == 0 && dst_gap == 0 {
982 chunk_size *= num_chunks;
983 num_chunks = 1;
984 }
985 for i in 0..num_chunks {
986 dst[0..(chunk_size * Self::PIXEL_SIZE)]
987 .copy_from_slice(&src[0..(chunk_size * Self::PIXEL_SIZE)]);
988 if i != num_chunks - 1 {
989 dst = &mut dst[((chunk_size + dst_gap) * Self::PIXEL_SIZE)..];
990 src = &src[((chunk_size + src_gap) * Self::PIXEL_SIZE)..];
991 }
992 }
993
994 Ok(())
995 }
996}
997
998impl<P: PixelFormat> Drop for BitMapBackend<'_, P> {
999 fn drop(&mut self) {
1000 if !self.saved {
1001 self.present().expect("Unable to save the bitmap");
1002 }
1003 }
1004}
1005
1006#[cfg(test)]
1007#[test]
1008fn test_bitmap_backend() {
1009 use crate::prelude::*;
1010 let mut buffer = vec![0; 10 * 10 * 3];
1011
1012 {
1013 let back = BitMapBackend::with_buffer(&mut buffer, (10, 10));
1014
1015 let area = back.into_drawing_area();
1016 area.fill(&WHITE).unwrap();
1017 area.draw(&PathElement::new(vec![(0, 0), (10, 10)], RED.filled()))
1018 .unwrap();
1019 area.present().unwrap();
1020 }
1021
1022 for i in 0..10 {
1023 assert_eq!(buffer[i * 33], 255);
1024 assert_eq!(buffer[i * 33 + 1], 0);
1025 assert_eq!(buffer[i * 33 + 2], 0);
1026 buffer[i * 33] = 255;
1027 buffer[i * 33 + 1] = 255;
1028 buffer[i * 33 + 2] = 255;
1029 }
1030
1031 assert!(buffer.into_iter().all(|x| x == 255));
1032}
1033
1034#[cfg(test)]
1035#[test]
1036fn test_bitmap_backend_fill_half() {
1037 use crate::prelude::*;
1038 let mut buffer = vec![0; 10 * 10 * 3];
1039
1040 {
1041 let back = BitMapBackend::with_buffer(&mut buffer, (10, 10));
1042
1043 let area = back.into_drawing_area();
1044 area.draw(&Rectangle::new([(0, 0), (5, 10)], RED.filled()))
1045 .unwrap();
1046 area.present().unwrap();
1047 }
1048 for x in 0..10 {
1049 for y in 0..10 {
1050 assert_eq!(
1051 buffer[(y * 10 + x) as usize * 3 + 0],
1052 if x <= 5 { 255 } else { 0 }
1053 );
1054 assert_eq!(buffer[(y * 10 + x) as usize * 3 + 1], 0);
1055 assert_eq!(buffer[(y * 10 + x) as usize * 3 + 2], 0);
1056 }
1057 }
1058
1059 let mut buffer = vec![0; 10 * 10 * 3];
1060
1061 {
1062 let back = BitMapBackend::with_buffer(&mut buffer, (10, 10));
1063
1064 let area = back.into_drawing_area();
1065 area.draw(&Rectangle::new([(0, 0), (10, 5)], RED.filled()))
1066 .unwrap();
1067 area.present().unwrap();
1068 }
1069 for x in 0..10 {
1070 for y in 0..10 {
1071 assert_eq!(
1072 buffer[(y * 10 + x) as usize * 3 + 0],
1073 if y <= 5 { 255 } else { 0 }
1074 );
1075 assert_eq!(buffer[(y * 10 + x) as usize * 3 + 1], 0);
1076 assert_eq!(buffer[(y * 10 + x) as usize * 3 + 2], 0);
1077 }
1078 }
1079}
1080
1081#[cfg(test)]
1082#[test]
1083fn test_bitmap_backend_blend() {
1084 use crate::prelude::*;
1085 let mut buffer = vec![255; 10 * 10 * 3];
1086
1087 {
1088 let back = BitMapBackend::with_buffer(&mut buffer, (10, 10));
1089
1090 let area = back.into_drawing_area();
1091 area.draw(&Rectangle::new(
1092 [(0, 0), (5, 10)],
1093 RGBColor(0, 100, 200).mix(0.2).filled(),
1094 ))
1095 .unwrap();
1096 area.present().unwrap();
1097 }
1098
1099 for x in 0..10 {
1100 for y in 0..10 {
1101 let (r, g, b) = if x <= 5 {
1102 (205, 225, 245)
1103 } else {
1104 (255, 255, 255)
1105 };
1106 assert_eq!(buffer[(y * 10 + x) as usize * 3 + 0], r);
1107 assert_eq!(buffer[(y * 10 + x) as usize * 3 + 1], g);
1108 assert_eq!(buffer[(y * 10 + x) as usize * 3 + 2], b);
1109 }
1110 }
1111}
1112
1113#[cfg(test)]
1114#[test]
1115fn test_bitmap_backend_split_and_fill() {
1116 use crate::prelude::*;
1117 let mut buffer = vec![255; 10 * 10 * 3];
1118
1119 {
1120 let mut back = BitMapBackend::with_buffer(&mut buffer, (10, 10));
1121
1122 for (sub_backend, color) in back.split(&[5]).into_iter().zip([&RED, &GREEN].iter()) {
1123 sub_backend.into_drawing_area().fill(*color).unwrap();
1124 }
1125 }
1126
1127 for x in 0..10 {
1128 for y in 0..10 {
1129 let (r, g, b) = if y < 5 { (255, 0, 0) } else { (0, 255, 0) };
1130 assert_eq!(buffer[(y * 10 + x) as usize * 3 + 0], r);
1131 assert_eq!(buffer[(y * 10 + x) as usize * 3 + 1], g);
1132 assert_eq!(buffer[(y * 10 + x) as usize * 3 + 2], b);
1133 }
1134 }
1135}
1136
1137#[cfg(test)]
1138#[test]
1139fn test_draw_rect_out_of_range() {
1140 use crate::prelude::*;
1141 let mut buffer = vec![0; 1099 * 1000 * 3];
1142
1143 {
1144 let mut back = BitMapBackend::with_buffer(&mut buffer, (1000, 1000));
1145
1146 back.draw_line((1100, 0), (1100, 999), &RED.to_rgba())
1147 .unwrap();
1148 back.draw_line((0, 1100), (999, 1100), &RED.to_rgba())
1149 .unwrap();
1150 back.draw_rect((1100, 0), (1100, 999), &RED.to_rgba(), true)
1151 .unwrap();
1152 }
1153
1154 for x in 0..1000 {
1155 for y in 0..1000 {
1156 assert_eq!(buffer[(y * 1000 + x) as usize * 3 + 0], 0);
1157 assert_eq!(buffer[(y * 1000 + x) as usize * 3 + 1], 0);
1158 assert_eq!(buffer[(y * 1000 + x) as usize * 3 + 2], 0);
1159 }
1160 }
1161}
1162
1163#[cfg(test)]
1164#[test]
1165fn test_draw_line_out_of_range() {
1166 use crate::prelude::*;
1167 let mut buffer = vec![0; 1000 * 1000 * 3];
1168
1169 {
1170 let mut back = BitMapBackend::with_buffer(&mut buffer, (1000, 1000));
1171
1172 back.draw_line((-1000, -1000), (2000, 2000), &WHITE.to_rgba())
1173 .unwrap();
1174
1175 back.draw_line((999, -1000), (999, 2000), &WHITE.to_rgba())
1176 .unwrap();
1177 }
1178
1179 for x in 0..1000 {
1180 for y in 0..1000 {
1181 let expected_value = if x == y || x == 999 { 255 } else { 0 };
1182 assert_eq!(buffer[(y * 1000 + x) as usize * 3 + 0], expected_value);
1183 assert_eq!(buffer[(y * 1000 + x) as usize * 3 + 1], expected_value);
1184 assert_eq!(buffer[(y * 1000 + x) as usize * 3 + 2], expected_value);
1185 }
1186 }
1187}
1188
1189#[cfg(test)]
1190#[test]
1191fn test_bitmap_blend_large() {
1192 use crate::prelude::*;
1193 let mut buffer = vec![0; 1000 * 1000 * 3];
1194
1195 for fill_color in [RED, GREEN, BLUE].iter() {
1196 buffer.iter_mut().for_each(|x| *x = 0);
1197
1198 {
1199 let mut back = BitMapBackend::with_buffer(&mut buffer, (1000, 1000));
1200
1201 back.draw_rect((0, 0), (1000, 1000), &WHITE.mix(0.1), true)
1202 .unwrap(); back.draw_rect((0, 0), (100, 100), &fill_color.mix(0.5), true)
1204 .unwrap(); }
1206
1207 for x in 0..1000 {
1208 for y in 0..1000 {
1209 let expected_value = if x <= 100 && y <= 100 {
1210 let (r, g, b) = fill_color.to_rgba().rgb();
1211 (
1212 if r > 0 { 139 } else { 12 },
1213 if g > 0 { 139 } else { 12 },
1214 if b > 0 { 139 } else { 12 },
1215 )
1216 } else {
1217 (24, 24, 24)
1218 };
1219 assert_eq!(buffer[(y * 1000 + x) as usize * 3 + 0], expected_value.0);
1220 assert_eq!(buffer[(y * 1000 + x) as usize * 3 + 1], expected_value.1);
1221 assert_eq!(buffer[(y * 1000 + x) as usize * 3 + 2], expected_value.2);
1222 }
1223 }
1224 }
1225}
1226
1227#[cfg(test)]
1228#[test]
1229fn test_bitmap_bgrx_pixel_format() {
1230 use crate::drawing::bitmap_pixel::BGRXPixel;
1231 use crate::prelude::*;
1232 let mut rgb_buffer = vec![0; 1000 * 1000 * 3];
1233 let mut bgrx_buffer = vec![0; 1000 * 1000 * 4];
1234
1235 {
1236 let mut rgb_back = BitMapBackend::with_buffer(&mut rgb_buffer, (1000, 1000));
1237 let mut bgrx_back =
1238 BitMapBackend::<BGRXPixel>::with_buffer_and_format(&mut bgrx_buffer, (1000, 1000))
1239 .unwrap();
1240
1241 rgb_back
1242 .draw_rect((0, 0), (1000, 1000), &BLACK, true)
1243 .unwrap();
1244 bgrx_back
1245 .draw_rect((0, 0), (1000, 1000), &BLACK, true)
1246 .unwrap();
1247
1248 rgb_back
1249 .draw_rect(
1250 (0, 0),
1251 (1000, 1000),
1252 &RGBColor(0xaa, 0xbb, 0xcc).mix(0.85),
1253 true,
1254 )
1255 .unwrap();
1256 bgrx_back
1257 .draw_rect(
1258 (0, 0),
1259 (1000, 1000),
1260 &RGBColor(0xaa, 0xbb, 0xcc).mix(0.85),
1261 true,
1262 )
1263 .unwrap();
1264
1265 rgb_back
1266 .draw_rect((0, 0), (1000, 1000), &RED.mix(0.85), true)
1267 .unwrap();
1268 bgrx_back
1269 .draw_rect((0, 0), (1000, 1000), &RED.mix(0.85), true)
1270 .unwrap();
1271
1272 rgb_back.draw_circle((300, 300), 100, &GREEN, true).unwrap();
1273 bgrx_back
1274 .draw_circle((300, 300), 100, &GREEN, true)
1275 .unwrap();
1276
1277 rgb_back.draw_rect((10, 10), (50, 50), &BLUE, true).unwrap();
1278 bgrx_back
1279 .draw_rect((10, 10), (50, 50), &BLUE, true)
1280 .unwrap();
1281
1282 rgb_back
1283 .draw_rect((10, 10), (50, 50), &WHITE, true)
1284 .unwrap();
1285 bgrx_back
1286 .draw_rect((10, 10), (50, 50), &WHITE, true)
1287 .unwrap();
1288
1289 rgb_back
1290 .draw_rect((10, 10), (15, 50), &YELLOW, true)
1291 .unwrap();
1292 bgrx_back
1293 .draw_rect((10, 10), (15, 50), &YELLOW, true)
1294 .unwrap();
1295 }
1296
1297 for x in 0..1000 {
1298 for y in 0..1000 {
1299 assert!(
1300 (rgb_buffer[y * 3000 + x * 3 + 0] as i32
1301 - bgrx_buffer[y * 4000 + x * 4 + 2] as i32)
1302 .abs()
1303 <= 1
1304 );
1305 assert!(
1306 (rgb_buffer[y * 3000 + x * 3 + 1] as i32
1307 - bgrx_buffer[y * 4000 + x * 4 + 1] as i32)
1308 .abs()
1309 <= 1
1310 );
1311 assert!(
1312 (rgb_buffer[y * 3000 + x * 3 + 2] as i32
1313 - bgrx_buffer[y * 4000 + x * 4 + 0] as i32)
1314 .abs()
1315 <= 1
1316 );
1317 }
1318 }
1319}
1320
1321#[cfg(test)]
1322#[test]
1323fn test_bitmap_blit() {
1324 let src_bitmap: Vec<u8> = (0..100)
1325 .map(|y| (0..300).map(move |x| ((x * y) % 253) as u8))
1326 .flatten()
1327 .collect();
1328
1329 use crate::prelude::*;
1330 let mut buffer = vec![0; 1000 * 1000 * 3];
1331
1332 {
1333 let mut back = BitMapBackend::with_buffer(&mut buffer, (1000, 1000));
1334 back.blit_bitmap((500, 500), (100, 100), &src_bitmap[..])
1335 .unwrap();
1336 }
1337
1338 for y in 0..1000 {
1339 for x in 0..1000 {
1340 if x >= 500 && x < 600 && y >= 500 && y < 600 {
1341 let lx = x - 500;
1342 let ly = y - 500;
1343 assert_eq!(buffer[y * 3000 + x * 3 + 0] as usize, (ly * lx * 3) % 253);
1344 assert_eq!(
1345 buffer[y * 3000 + x * 3 + 1] as usize,
1346 (ly * (lx * 3 + 1)) % 253
1347 );
1348 assert_eq!(
1349 buffer[y * 3000 + x * 3 + 2] as usize,
1350 (ly * (lx * 3 + 2)) % 253
1351 );
1352 } else {
1353 assert_eq!(buffer[y * 3000 + x * 3 + 0], 0);
1354 assert_eq!(buffer[y * 3000 + x * 3 + 1], 0);
1355 assert_eq!(buffer[y * 3000 + x * 3 + 2], 0);
1356 }
1357 }
1358 }
1359}
1360
1361#[cfg(all(not(target_arch = "wasm32"), feature = "image"))]
1362#[cfg(test)]
1363mod test {
1364 use crate::prelude::*;
1365 use crate::style::text_anchor::{HPos, Pos, VPos};
1366 use image::{ImageBuffer, Rgb};
1367 use std::fs;
1368 use std::path::Path;
1369
1370 static DST_DIR: &str = "target/test/bitmap";
1371
1372 fn checked_save_file(name: &str, content: &[u8], w: u32, h: u32) {
1373 assert!(content.iter().any(|x| *x != 0));
1377 fs::create_dir_all(DST_DIR).unwrap();
1378 let file_name = format!("{}.png", name);
1379 let file_path = Path::new(DST_DIR).join(file_name);
1380 println!("{:?} created", file_path);
1381 let img = ImageBuffer::<Rgb<u8>, &[u8]>::from_raw(w, h, content).unwrap();
1382 img.save(&file_path).unwrap();
1383 }
1384
1385 fn draw_mesh_with_custom_ticks(tick_size: i32, test_name: &str) {
1386 let (width, height) = (500, 500);
1387 let mut buffer = vec![0; (width * height * 3) as usize];
1388 {
1389 let root = BitMapBackend::with_buffer(&mut buffer, (width, height)).into_drawing_area();
1390 root.fill(&WHITE).unwrap();
1391
1392 let mut chart = ChartBuilder::on(&root)
1393 .caption("This is a test", ("sans-serif", 20))
1394 .set_all_label_area_size(40)
1395 .build_ranged(0..10, 0..10)
1396 .unwrap();
1397
1398 chart
1399 .configure_mesh()
1400 .set_all_tick_mark_size(tick_size)
1401 .draw()
1402 .unwrap();
1403 }
1404 checked_save_file(test_name, &buffer, width, height);
1405 }
1406
1407 #[test]
1408 fn test_draw_mesh_no_ticks() {
1409 draw_mesh_with_custom_ticks(0, "test_draw_mesh_no_ticks");
1410 }
1411
1412 #[test]
1413 fn test_draw_mesh_negative_ticks() {
1414 draw_mesh_with_custom_ticks(-10, "test_draw_mesh_negative_ticks");
1415 }
1416
1417 #[test]
1418 fn test_text_draw() {
1419 let (width, height) = (1500, 800);
1420 let mut buffer = vec![0; (width * height * 3) as usize];
1421 {
1422 let root = BitMapBackend::with_buffer(&mut buffer, (width, height)).into_drawing_area();
1423 root.fill(&WHITE).unwrap();
1424 let root = root
1425 .titled("Image Title", ("sans-serif", 60).into_font())
1426 .unwrap();
1427
1428 let mut chart = ChartBuilder::on(&root)
1429 .caption("All anchor point positions", ("sans-serif", 20))
1430 .set_all_label_area_size(40)
1431 .build_ranged(0..100, 0..50)
1432 .unwrap();
1433
1434 chart
1435 .configure_mesh()
1436 .disable_x_mesh()
1437 .disable_y_mesh()
1438 .x_desc("X Axis")
1439 .y_desc("Y Axis")
1440 .draw()
1441 .unwrap();
1442
1443 let ((x1, y1), (x2, y2), (x3, y3)) = ((-30, 30), (0, -30), (30, 30));
1444
1445 for (dy, trans) in [
1446 FontTransform::None,
1447 FontTransform::Rotate90,
1448 FontTransform::Rotate180,
1449 FontTransform::Rotate270,
1450 ]
1451 .iter()
1452 .enumerate()
1453 {
1454 for (dx1, h_pos) in [HPos::Left, HPos::Right, HPos::Center].iter().enumerate() {
1455 for (dx2, v_pos) in [VPos::Top, VPos::Center, VPos::Bottom].iter().enumerate() {
1456 let x = 150_i32 + (dx1 as i32 * 3 + dx2 as i32) * 150;
1457 let y = 120 + dy as i32 * 150;
1458 let draw = |x, y, text| {
1459 root.draw(&Circle::new((x, y), 3, &BLACK.mix(0.5))).unwrap();
1460 let style = TextStyle::from(("sans-serif", 20).into_font())
1461 .pos(Pos::new(*h_pos, *v_pos))
1462 .transform(trans.clone());
1463 root.draw_text(text, &style, (x, y)).unwrap();
1464 };
1465 draw(x + x1, y + y1, "dood");
1466 draw(x + x2, y + y2, "dog");
1467 draw(x + x3, y + y3, "goog");
1468 }
1469 }
1470 }
1471 }
1472 checked_save_file("test_text_draw", &buffer, width, height);
1473 }
1474
1475 #[test]
1476 fn test_text_clipping() {
1477 let (width, height) = (500_i32, 500_i32);
1478 let mut buffer = vec![0; (width * height * 3) as usize];
1479 {
1480 let root = BitMapBackend::with_buffer(&mut buffer, (width as u32, height as u32))
1481 .into_drawing_area();
1482 root.fill(&WHITE).unwrap();
1483
1484 let style = TextStyle::from(("sans-serif", 20).into_font())
1485 .pos(Pos::new(HPos::Center, VPos::Center));
1486 root.draw_text("TOP LEFT", &style, (0, 0)).unwrap();
1487 root.draw_text("TOP CENTER", &style, (width / 2, 0))
1488 .unwrap();
1489 root.draw_text("TOP RIGHT", &style, (width, 0)).unwrap();
1490
1491 root.draw_text("MIDDLE LEFT", &style, (0, height / 2))
1492 .unwrap();
1493 root.draw_text("MIDDLE RIGHT", &style, (width, height / 2))
1494 .unwrap();
1495
1496 root.draw_text("BOTTOM LEFT", &style, (0, height)).unwrap();
1497 root.draw_text("BOTTOM CENTER", &style, (width / 2, height))
1498 .unwrap();
1499 root.draw_text("BOTTOM RIGHT", &style, (width, height))
1500 .unwrap();
1501 }
1502 checked_save_file("test_text_clipping", &buffer, width as u32, height as u32);
1503 }
1504
1505 #[test]
1506 fn test_series_labels() {
1507 let (width, height) = (500, 500);
1508 let mut buffer = vec![0; (width * height * 3) as usize];
1509 {
1510 let root = BitMapBackend::with_buffer(&mut buffer, (width, height)).into_drawing_area();
1511 root.fill(&WHITE).unwrap();
1512
1513 let mut chart = ChartBuilder::on(&root)
1514 .caption("All series label positions", ("sans-serif", 20))
1515 .set_all_label_area_size(40)
1516 .build_ranged(0..50, 0..50)
1517 .unwrap();
1518
1519 chart
1520 .configure_mesh()
1521 .disable_x_mesh()
1522 .disable_y_mesh()
1523 .draw()
1524 .unwrap();
1525
1526 chart
1527 .draw_series(std::iter::once(Circle::new((5, 15), 5, &RED)))
1528 .expect("Drawing error")
1529 .label("Series 1")
1530 .legend(|(x, y)| Circle::new((x, y), 3, RED.filled()));
1531
1532 chart
1533 .draw_series(std::iter::once(Circle::new((5, 15), 10, &BLUE)))
1534 .expect("Drawing error")
1535 .label("Series 2")
1536 .legend(|(x, y)| Circle::new((x, y), 3, BLUE.filled()));
1537
1538 for pos in vec![
1539 SeriesLabelPosition::UpperLeft,
1540 SeriesLabelPosition::MiddleLeft,
1541 SeriesLabelPosition::LowerLeft,
1542 SeriesLabelPosition::UpperMiddle,
1543 SeriesLabelPosition::MiddleMiddle,
1544 SeriesLabelPosition::LowerMiddle,
1545 SeriesLabelPosition::UpperRight,
1546 SeriesLabelPosition::MiddleRight,
1547 SeriesLabelPosition::LowerRight,
1548 SeriesLabelPosition::Coordinate(70, 70),
1549 ]
1550 .into_iter()
1551 {
1552 chart
1553 .configure_series_labels()
1554 .border_style(&BLACK.mix(0.5))
1555 .position(pos)
1556 .draw()
1557 .expect("Drawing error");
1558 }
1559 }
1560 checked_save_file("test_series_labels", &buffer, width, height);
1561 }
1562
1563 #[test]
1564 fn test_draw_pixel_alphas() {
1565 let (width, height) = (100_i32, 100_i32);
1566 let mut buffer = vec![0; (width * height * 3) as usize];
1567 {
1568 let root = BitMapBackend::with_buffer(&mut buffer, (width as u32, height as u32))
1569 .into_drawing_area();
1570 root.fill(&WHITE).unwrap();
1571 for i in -20..20 {
1572 let alpha = i as f64 * 0.1;
1573 root.draw_pixel((50 + i, 50 + i), &BLACK.mix(alpha))
1574 .unwrap();
1575 }
1576 }
1577 checked_save_file(
1578 "test_draw_pixel_alphas",
1579 &buffer,
1580 width as u32,
1581 height as u32,
1582 );
1583 }
1584}