Skip to content

Commit 0ccec8b

Browse files
committed
Make bitmap backend not relies on image crate until we need to encode image
1 parent 458be85 commit 0ccec8b

File tree

1 file changed

+96
-80
lines changed

1 file changed

+96
-80
lines changed

src/drawing/backend_impl/bitmap.rs

Lines changed: 96 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::drawing::backend::{BackendCoord, BackendStyle, DrawingBackend, DrawingErrorKind};
22
use crate::style::{Color, RGBAColor};
33

4-
use image::{ImageBuffer, ImageError, Rgb, RgbImage};
4+
use image::{ImageBuffer, ImageError, Rgb};
55

66
use std::path::Path;
77

@@ -11,35 +11,6 @@ fn blend(prev: &mut u8, new: u8, a: f64) {
1111
*prev = ((f64::from(*prev)) * (1.0 - a) + a * f64::from(new)).min(255.0) as u8;
1212
}
1313

14-
/// Macro implementation for drawing pixels, since a generic implementation would have been
15-
/// much more unwieldy.
16-
macro_rules! draw_pixel {
17-
($img:expr, $point:expr, $color:expr) => {{
18-
if $point.0 as u32 >= $img.width()
19-
|| $point.0 < 0
20-
|| $point.1 as u32 >= $img.height()
21-
|| $point.1 < 0
22-
{
23-
return Ok(());
24-
}
25-
26-
let alpha = $color.alpha();
27-
let rgb = $color.rgb();
28-
29-
if alpha >= 1.0 {
30-
$img.put_pixel($point.0 as u32, $point.1 as u32, Rgb([rgb.0, rgb.1, rgb.2]));
31-
} else {
32-
let pixel = $img.get_pixel_mut($point.0 as u32, $point.1 as u32);
33-
let new_color = [rgb.0, rgb.1, rgb.2];
34-
35-
pixel.0.iter_mut().zip(&new_color).for_each(|(old, new)| {
36-
*old = (f64::from(*old) * (1.0 - alpha) + f64::from(*new) * alpha).min(255.0) as u8;
37-
});
38-
}
39-
Ok(())
40-
}};
41-
}
42-
4314
#[cfg(feature = "gif")]
4415
mod gif_support {
4516
use super::*;
@@ -76,16 +47,9 @@ mod gif_support {
7647
})
7748
}
7849

79-
pub(super) fn flush_frame(&mut self, img: &mut RgbImage) -> Result<(), ImageError> {
80-
let mut new_img = RgbImage::new(self.width, self.height);
81-
std::mem::swap(&mut new_img, img);
82-
83-
let mut frame = GifFrame::from_rgb_speed(
84-
self.width as u16,
85-
self.height as u16,
86-
&new_img.into_raw(),
87-
10,
88-
);
50+
pub(super) fn flush_frame(&mut self, buffer: &[u8]) -> Result<(), ImageError> {
51+
let mut frame =
52+
GifFrame::from_rgb_speed(self.width as u16, self.height as u16, buffer, 10);
8953

9054
frame.delay = self.delay as u16;
9155

@@ -97,26 +61,48 @@ mod gif_support {
9761
}
9862

9963
enum Target<'a> {
100-
File(&'a Path, RgbImage),
101-
Buffer(BorrowedImage<'a>),
64+
File(&'a Path),
65+
Buffer,
10266
#[cfg(feature = "gif")]
103-
Gif(Box<gif_support::GifFile>, RgbImage),
67+
Gif(Box<gif_support::GifFile>),
68+
}
69+
70+
enum Buffer<'a> {
71+
Owned(Vec<u8>),
72+
Borrowed(&'a mut [u8]),
73+
}
74+
75+
impl<'a> Buffer<'a> {
76+
fn borrow_buffer(&mut self) -> &mut [u8] {
77+
match self {
78+
Buffer::Owned(buf) => &mut buf[..],
79+
Buffer::Borrowed(buf) => *buf,
80+
}
81+
}
10482
}
10583

10684
/// The backend that drawing a bitmap
10785
pub struct BitMapBackend<'a> {
10886
/// The path to the image
10987
target: Target<'a>,
11088

89+
/// The size of the image
90+
size: (u32, u32),
91+
92+
/// The data buffer of the image
93+
buffer: Buffer<'a>,
94+
11195
/// Flag indicates if the bitmap has been saved
11296
saved: bool,
11397
}
11498

11599
impl<'a> BitMapBackend<'a> {
116100
/// Create a new bitmap backend
117-
pub fn new<T: AsRef<Path> + ?Sized>(path: &'a T, dimension: (u32, u32)) -> Self {
101+
pub fn new<T: AsRef<Path> + ?Sized>(path: &'a T, (w, h): (u32, u32)) -> Self {
118102
Self {
119-
target: Target::File(path.as_ref(), RgbImage::new(dimension.0, dimension.1)),
103+
target: Target::File(path.as_ref()),
104+
size: (w, h),
105+
buffer: Buffer::Owned(vec![0; (3 * w * h) as usize]),
120106
saved: false,
121107
}
122108
}
@@ -133,14 +119,17 @@ impl<'a> BitMapBackend<'a> {
133119
#[cfg(feature = "gif")]
134120
pub fn gif<T: AsRef<Path>>(
135121
path: T,
136-
dimension: (u32, u32),
122+
(w, h): (u32, u32),
137123
frame_delay: u32,
138124
) -> Result<Self, ImageError> {
139125
Ok(Self {
140-
target: Target::Gif(
141-
Box::new(gif_support::GifFile::new(path, dimension, frame_delay)?),
142-
RgbImage::new(dimension.0, dimension.1),
143-
),
126+
target: Target::Gif(Box::new(gif_support::GifFile::new(
127+
path,
128+
(w, h),
129+
frame_delay,
130+
)?)),
131+
size: (w, h),
132+
buffer: Buffer::Owned(vec![0; (3 * w * h) as usize]),
144133
saved: false,
145134
})
146135
}
@@ -152,23 +141,27 @@ impl<'a> BitMapBackend<'a> {
152141
///
153142
/// - `buf`: The buffer to operate
154143
/// - `dimension`: The size of the image in pixels
155-
pub fn with_buffer(buf: &'a mut [u8], dimension: (u32, u32)) -> Self {
144+
pub fn with_buffer(buf: &'a mut [u8], (w, h): (u32, u32)) -> Self {
145+
if (w * h * 3) as usize > buf.len() {
146+
// TODO: This doesn't deserve a panic.
147+
panic!(
148+
"Wrong image size: H = {}, W = {}, BufSize = {}",
149+
w,
150+
h,
151+
buf.len()
152+
);
153+
}
154+
156155
Self {
157-
target: Target::Buffer(
158-
BorrowedImage::from_raw(dimension.0, dimension.1, buf)
159-
.expect("Buffer size must match dimensions (w * h * 3)."),
160-
),
156+
target: Target::Buffer,
157+
size: (w, h),
158+
buffer: Buffer::Borrowed(buf),
161159
saved: false,
162160
}
163161
}
164162

165163
fn get_raw_pixel_buffer(&mut self) -> &mut [u8] {
166-
match &mut self.target {
167-
Target::File(_, img) => &mut (**img)[..],
168-
Target::Buffer(img) => &mut (**img)[..],
169-
#[cfg(feature = "gif")]
170-
Target::Gif(_, img) => &mut (**img)[..],
171-
}
164+
self.buffer.borrow_buffer()
172165
}
173166

174167
/// Split a bitmap backend vertically into several sub drawing area which allows
@@ -341,12 +334,7 @@ impl<'a> DrawingBackend for BitMapBackend<'a> {
341334
type ErrorType = ImageError;
342335

343336
fn get_size(&self) -> (u32, u32) {
344-
match &self.target {
345-
Target::Buffer(img) => (img.width(), img.height()),
346-
Target::File(_, img) => (img.width(), img.height()),
347-
#[cfg(feature = "gif")]
348-
Target::Gif(_, img) => (img.width(), img.height()),
349-
}
337+
self.size
350338
}
351339

352340
fn ensure_prepared(&mut self) -> Result<(), DrawingErrorKind<ImageError>> {
@@ -355,19 +343,24 @@ impl<'a> DrawingBackend for BitMapBackend<'a> {
355343
}
356344

357345
fn present(&mut self) -> Result<(), DrawingErrorKind<ImageError>> {
346+
let (w, h) = self.get_size();
358347
match &mut self.target {
359-
Target::File(path, img) => {
360-
img.save(&path)
361-
.map_err(|x| DrawingErrorKind::DrawingError(ImageError::IoError(x)))?;
362-
self.saved = true;
363-
Ok(())
348+
Target::File(path) => {
349+
if let Some(img) = BorrowedImage::from_raw(w, h, self.buffer.borrow_buffer()) {
350+
img.save(&path)
351+
.map_err(|x| DrawingErrorKind::DrawingError(ImageError::IoError(x)))?;
352+
self.saved = true;
353+
Ok(())
354+
} else {
355+
Err(DrawingErrorKind::DrawingError(ImageError::DimensionError))
356+
}
364357
}
365-
Target::Buffer(_) => Ok(()),
358+
Target::Buffer => Ok(()),
366359

367360
#[cfg(feature = "gif")]
368-
Target::Gif(target, img) => {
361+
Target::Gif(target) => {
369362
target
370-
.flush_frame(img)
363+
.flush_frame(self.buffer.borrow_buffer())
371364
.map_err(DrawingErrorKind::DrawingError)?;
372365
self.saved = true;
373366
Ok(())
@@ -380,12 +373,35 @@ impl<'a> DrawingBackend for BitMapBackend<'a> {
380373
point: BackendCoord,
381374
color: &RGBAColor,
382375
) -> Result<(), DrawingErrorKind<ImageError>> {
383-
match &mut self.target {
384-
Target::Buffer(img) => draw_pixel!(img, point, color),
385-
Target::File(_, img) => draw_pixel!(img, point, color),
386-
#[cfg(feature = "gif")]
387-
Target::Gif(_, img) => draw_pixel!(img, point, color),
376+
if point.0 < 0 || point.1 < 0 {
377+
return Ok(());
388378
}
379+
380+
let (w, _) = self.get_size();
381+
let alpha = color.alpha();
382+
let rgb = color.rgb();
383+
384+
let buf = self.get_raw_pixel_buffer();
385+
386+
let (x, y) = (point.0 as usize, point.1 as usize);
387+
let w = w as usize;
388+
389+
let base = (y * w + x) * 3;
390+
391+
if base < buf.len() {
392+
unsafe {
393+
if alpha >= 1.0 {
394+
*buf.get_unchecked_mut(base) = rgb.0;
395+
*buf.get_unchecked_mut(base + 1) = rgb.1;
396+
*buf.get_unchecked_mut(base + 2) = rgb.2;
397+
} else {
398+
blend(buf.get_unchecked_mut(base), rgb.0, alpha);
399+
blend(buf.get_unchecked_mut(base + 1), rgb.1, alpha);
400+
blend(buf.get_unchecked_mut(base + 2), rgb.2, alpha);
401+
}
402+
}
403+
}
404+
Ok(())
389405
}
390406

391407
fn draw_line<S: BackendStyle>(

0 commit comments

Comments
 (0)