Repository: plotters-rs/plotters (language: rust)

Issue:
[Feature Request] Different Pixel Format for BitMapBackend?
- What is the feature ?
We should support different pixel format for bitmap backend.

- Why do you need the feature ?

If we want to use Plotters to manipulate a framebuffer, the most commonly used frame buffer pixel format is BGRX. Currently we have to draw the RGB buffer and convert the pixel format. 

- What is the use case ?

See above

- Additional Information

#66 This PR is actually trying to address this. But we definitely should have some more clean and nice way to do this inplace



Discussion / hints:

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 47231188..1100f872 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,10 @@
 # Changelog
 
 ## Plotters latest (?)
+### Added
+
+- BitMapBackend now is able to support different pixel format natively. Check our new minifb demo for details.
+- Incremental Rendering by saving the previous chart context into a state and restore it on a different drawing area.
 
 ### Improved
 
diff --git a/examples/minifb-demo/Cargo.toml b/examples/minifb-demo/Cargo.toml
new file mode 100644
index 00000000..25971547
--- /dev/null
+++ b/examples/minifb-demo/Cargo.toml
@@ -0,0 +1,11 @@
+[package]
+name = "minifb-demo"
+version = "0.1.0"
+authors = ["Hao Hou <haohou302@gmail.com>"]
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+minifb = "0.13.0"
+plotters = { path = "../.." , default_features = false}
diff --git a/examples/minifb-demo/src/main.rs b/examples/minifb-demo/src/main.rs
new file mode 100644
index 00000000..5db09400
--- /dev/null
+++ b/examples/minifb-demo/src/main.rs
@@ -0,0 +1,148 @@
+use minifb::{Key, KeyRepeat, Window, WindowOptions};
+use plotters::drawing::bitmap_pixel::BGRXPixel;
+use plotters::prelude::*;
+use std::collections::VecDeque;
+use std::error::Error;
+use std::time::SystemTime;
+const W: usize = 480;
+const H: usize = 320;
+
+const SAMPLE_RATE: f64 = 10_000.0;
+const FREAME_RATE: f64 = 30.0;
+
+fn get_window_title(fx: f64, fy: f64, iphase: f64) -> String {
+    format!(
+        "x={:.1}Hz, y={:.1}Hz, phase={:.1} +/-=Adjust y 9/0=Adjust x <Esc>=Exit",
+        fx, fy, iphase
+    )
+}
+
+fn main() -> Result<(), Box<dyn Error>> {
+    let mut buf = vec![0u8; W * H * 4];
+
+    let mut fx: f64 = 1.0;
+    let mut fy: f64 = 1.1;
+    let mut xphase: f64 = 0.0;
+    let mut yphase: f64 = 0.1;
+
+    let mut window = Window::new(
+        &get_window_title(fx, fy, yphase - xphase),
+        W,
+        H,
+        WindowOptions::default(),
+    )?;
+    let root =
+        BitMapBackend::<BGRXPixel>::with_buffer_and_format(&mut buf[..], (W as u32, H as u32))?
+            .into_drawing_area();
+    root.fill(&BLACK)?;
+
+    let mut chart = ChartBuilder::on(&root)
+        .margin(10)
+        .set_all_label_area_size(30)
+        .build_ranged(-1.2..1.2, -1.2..1.2)?;
+
+    chart
+        .configure_mesh()
+        .label_style(("sans-serif", 15).into_font().color(&GREEN))
+        .axis_style(&GREEN)
+        .draw()?;
+
+    let cs = chart.into_chart_state();
+    drop(root);
+
+    let mut data = VecDeque::new();
+    let start_ts = SystemTime::now();
+    let mut last_flushed = 0.0;
+
+    while window.is_open() && !window.is_key_down(Key::Escape) {
+        let epoch = SystemTime::now()
+            .duration_since(start_ts)
+            .unwrap()
+            .as_secs_f64();
+
+        if let Some((ts, _, _)) = data.back() {
+            if epoch - ts < 1.0 / SAMPLE_RATE {
+                std::thread::sleep(std::time::Duration::from_secs_f64(epoch - ts));
+                continue;
+            }
+            let mut ts = *ts;
+            while ts < epoch {
+                ts += 1.0 / SAMPLE_RATE;
+                let phase_x: f64 = 2.0 * ts * std::f64::consts::PI * fx + xphase;
+                let phase_y: f64 = 2.0 * ts * std::f64::consts::PI * fy + yphase;
+                data.push_back((ts, phase_x.sin(), phase_y.sin()));
+            }
+        }
+
+        let phase_x = 2.0 * epoch * std::f64::consts::PI * fx + xphase;
+        let phase_y = 2.0 * epoch * std::f64::consts::PI * fy + yphase;
+        data.push_back((epoch, phase_x.sin(), phase_y.sin()));
+
+        if epoch - last_flushed > 1.0 / FREAME_RATE {
+            let root = BitMapBackend::<BGRXPixel>::with_buffer_and_format(
+                &mut buf[..],
+                (W as u32, H as u32),
+            )?
+            .into_drawing_area();
+            let mut chart = cs.clone().restore(&root);
+            chart.plotting_area().fill(&BLACK)?;
+
+            chart
+                .configure_mesh()
+                .line_style_1(&GREEN.mix(0.2))
+                .line_style_2(&TRANSPARENT)
+                .draw()?;
+
+            chart.draw_series(data.iter().zip(data.iter().skip(1)).map(
+                |(&(e, x0, y0), &(_, x1, y1))| {
+                    PathElement::new(
+                        vec![(x0, y0), (x1, y1)],
+                        &GREEN.mix(((e - epoch) * 20.0).exp()),
+                    )
+                },
+            ))?;
+
+            drop(root);
+            drop(chart);
+
+            if let Some(keys) = window.get_keys_pressed(KeyRepeat::Yes) {
+                for key in keys {
+                    let old_fx = fx;
+                    let old_fy = fy;
+                    match key {
+                        Key::Equal => {
+                            fy += 0.1;
+                        }
+                        Key::Minus => {
+                            fy -= 0.1;
+                        }
+                        Key::Key0 => {
+                            fx += 0.1;
+                        }
+                        Key::Key9 => {
+                            fx -= 0.1;
+                        }
+                        _ => {
+                            continue;
+                        }
+                    }
+                    xphase += 2.0 * epoch * std::f64::consts::PI * (old_fx - fx);
+                    yphase += 2.0 * epoch * std::f64::consts::PI * (old_fy - fy);
+
+                    window.set_title(&get_window_title(fx, fy, yphase - xphase));
+                    break;
+                }
+            }
+            window.update_with_buffer(unsafe { std::mem::transmute(&buf[..]) })?;
+            last_flushed = epoch;
+        }
+
+        while let Some((e, _, _)) = data.front() {
+            if ((e - epoch) * 20.0).exp() > 0.1 {
+                break;
+            }
+            data.pop_front();
+        }
+    }
+    Ok(())
+}
diff --git a/src/chart/builder.rs b/src/chart/builder.rs
index 88a20a41..ee8ef123 100644
--- a/src/chart/builder.rs
+++ b/src/chart/builder.rs
@@ -246,7 +246,7 @@ impl<'a, 'b, DB: DrawingBackend> ChartBuilder<'a, 'b, DB> {
         }
 
         let mut pixel_range = drawing_area.get_pixel_range();
-        pixel_range.1 = pixel_range.1.end..pixel_range.1.start;
+        pixel_range.1 = (pixel_range.1.end - 1)..(pixel_range.1.start - 1);
 
         let mut x_label_area = [None, None];
         let mut y_label_area = [None, None];
@@ -266,8 +266,8 @@ impl<'a, 'b, DB: DrawingBackend> ChartBuilder<'a, 'b, DB> {
             )),
             series_anno: vec![],
             drawing_area_pos: (
-                actual_drawing_area_pos[2] + title_dx,
-                actual_drawing_area_pos[0] + title_dy,
+                actual_drawing_area_pos[2] + title_dx + self.margin[2] as i32,
+                actual_drawing_area_pos[0] + title_dy + self.margin[0] as i32,
             ),
         })
     }
diff --git a/src/chart/context.rs b/src/chart/context.rs
index e46a39ec..3ac102bb 100644
--- a/src/chart/context.rs
+++ b/src/chart/context.rs
@@ -76,6 +76,12 @@ pub struct ChartContext<'a, DB: DrawingBackend, CT: CoordTranslate> {
     pub(super) drawing_area_pos: (i32, i32),
 }
 
+/// A chart context state - This is the data that is needed to reconstruct the chart context
+/// without actually drawing the chart. This is useful when we want to do realtime rendering and
+/// want to incrementally update the chart.
+///
+/// For each frame, instead of updating the entire backend, we are able to keep the keep the figure
+/// component like axis, labels untouched and make updates only in the plotting drawing area.
 pub struct ChartState<CT: CoordTranslate> {
     drawing_area_pos: (i32, i32),
     drawing_area_size: (u32, u32),
@@ -103,10 +109,16 @@ impl<'a, DB: DrawingBackend, CT: CoordTranslate> From<ChartContext<'a, DB, CT>>
 }
 
 impl<'a, DB: DrawingBackend, CT: CoordTranslate> ChartContext<'a, DB, CT> {
+    /// Convert a chart context into a chart state, by doing so, the chart context is consumed and
+    /// a saved chart state is created for later use.
     pub fn into_chart_state(self) -> ChartState<CT> {
         self.into()
     }
 
+    /// Convert the chart context into a sharable chart state.
+    /// Normally a chart state can not be clone, since the coordinate spec may not be able to be
+    /// cloned. In this case, we can use an `Arc` get the coordinate wrapped thus the state can be
+    /// cloned and shared by multiple chart context
     pub fn into_shared_chart_state(self) -> ChartState<Arc<CT>> {
         ChartState {
             drawing_area_pos: self.drawing_area_pos,
@@ -131,12 +143,17 @@ where
 }
 
 impl<'a, DB: DrawingBackend, CT: CoordTranslate + Clone> ChartContext<'a, DB, CT> {
+    /// Make the chart context, do not consume the chart context and clone the coordinate spec
     pub fn to_chart_state(&self) -> ChartState<CT> {
         self.into()
     }
 }
 
 impl<CT: CoordTranslate> ChartState<CT> {
+    /// Restore the chart context on the given drawing area
+    ///
+    /// - `area`: The given drawing area where we want to restore the chart context
+    /// - **returns** The newly created chart context
     pub fn restore<'a, DB: DrawingBackend>(
         self,
         area: &DrawingArea<DB, Shift>,
@@ -419,23 +436,23 @@ impl<'a, DB: DrawingBackend, X: Ranged, Y: Ranged> ChartContext<'a, DB, RangedCo
         }
 
         if let Some(axis_style) = axis_style {
-            let mut x0 = if orientation.0 > 0 { 0 } else { tw as i32 };
-            let mut y0 = if orientation.1 > 0 { 0 } else { th as i32 };
-            let mut x1 = if orientation.0 >= 0 { 0 } else { tw as i32 };
-            let mut y1 = if orientation.1 >= 0 { 0 } else { th as i32 };
+            let mut x0 = if orientation.0 > 0 { 0 } else { tw as i32 - 1 };
+            let mut y0 = if orientation.1 > 0 { 0 } else { th as i32 - 1 };
+            let mut x1 = if orientation.0 >= 0 { 0 } else { tw as i32 - 1 };
+            let mut y1 = if orientation.1 >= 0 { 0 } else { th as i32 - 1 };
 
             if inward_labels {
                 if orientation.0 == 0 {
                     if y0 == 0 {
-                        y0 = th as i32;
-                        y1 = th as i32;
+                        y0 = th as i32 - 1;
+                        y1 = th as i32 - 1;
                     } else {
                         y0 = 0;
                         y1 = 0;
                     }
                 } else if x0 == 0 {
-                    x0 = tw as i32;
-                    x1 = tw as i32;
+                    x0 = tw as i32 - 1;
+                    x1 = tw as i32 - 1;
                 } else {
                     x0 = 0;
                     x1 = 0;
@@ -583,26 +600,28 @@ impl<'a, DB: DrawingBackend, X: Ranged, Y: Ranged> ChartContext<'a, DB, RangedCo
                 area.draw_text(&t, label_style, (text_x, text_y))?;
 
                 if let Some(style) = axis_style {
+                    let xmax = tw as i32 - 1;
+                    let ymax = th as i32 - 1;
                     let (kx0, ky0, kx1, ky1) = if tick_size > 0 {
                         match orientation {
                             (dx, dy) if dx > 0 && dy == 0 => (0, *p - y0, tick_size, *p - y0),
                             (dx, dy) if dx < 0 && dy == 0 => {
-                                (tw as i32 - tick_size, *p - y0, tw as i32, *p - y0)
+                                (xmax - tick_size, *p - y0, xmax, *p - y0)
                             }
                             (dx, dy) if dx == 0 && dy > 0 => (*p - x0, 0, *p - x0, tick_size),
                             (dx, dy) if dx == 0 && dy < 0 => {
-                                (*p - x0, th as i32 - tick_size, *p - x0, th as i32)
+                                (*p - x0, ymax - tick_size, *p - x0, ymax)
                             }
                             _ => panic!("Bug: Invalid orientation specification"),
                         }
                     } else {
                         match orientation {
                             (dx, dy) if dx > 0 && dy == 0 => {
-                                (tw as i32, *p - y0, tw as i32 + tick_size, *p - y0)
+                                (xmax, *p - y0, xmax + tick_size, *p - y0)
                             }
                             (dx, dy) if dx < 0 && dy == 0 => (0, *p - y0, -tick_size, *p - y0),
                             (dx, dy) if dx == 0 && dy > 0 => {
-                                (*p - x0, th as i32, *p - x0, th as i32 + tick_size)
+                                (*p - x0, ymax, *p - x0, ymax + tick_size)
                             }
                             (dx, dy) if dx == 0 && dy < 0 => (*p - x0, 0, *p - x0, -tick_size),
                             _ => panic!("Bug: Invalid orientation specification"),
diff --git a/src/chart/dual_coord.rs b/src/chart/dual_coord.rs
index 03e5db42..dcc0ce8f 100644
--- a/src/chart/dual_coord.rs
+++ b/src/chart/dual_coord.rs
@@ -19,6 +19,8 @@ pub struct DualCoordChartContext<'a, DB: DrawingBackend, CT1: CoordTranslate, CT
     pub(super) secondary: ChartContext<'a, DB, CT2>,
 }
 
+/// The chart state for a dual coord chart, see the detailed description for `ChartState` for more
+/// information about the purpose of a chart state
 pub struct DualCoordChartState<CT1: CoordTranslate, CT2: CoordTranslate> {
     primary: ChartState<CT1>,
     secondary: ChartState<CT2>,
@@ -27,6 +29,7 @@ pub struct DualCoordChartState<CT1: CoordTranslate, CT2: CoordTranslate> {
 impl<'a, DB: DrawingBackend, CT1: CoordTranslate, CT2: CoordTranslate>
     DualCoordChartContext<'a, DB, CT1, CT2>
 {
+    /// Convert the chart context into a chart state
     pub fn into_chart_state(self) -> DualCoordChartState<CT1, CT2> {
         DualCoordChartState {
             primary: self.primary.into(),
@@ -34,6 +37,7 @@ impl<'a, DB: DrawingBackend, CT1: CoordTranslate, CT2: CoordTranslate>
         }
     }
 
+    /// Convert the chart context into a sharable chart state
     pub fn into_shared_chart_state(self) -> DualCoordChartState<Arc<CT1>, Arc<CT2>> {
         DualCoordChartState {
             primary: self.primary.into_shared_chart_state(),
@@ -48,6 +52,7 @@ where
     CT1: Clone,
     CT2: Clone,
 {
+    /// Copy the coordinate specs and make the chart state
     pub fn to_chart_state(&self) -> DualCoordChartState<CT1, CT2> {
         DualCoordChartState {
             primary: self.primary.to_chart_state(),
@@ -57,6 +62,7 @@ where
 }
 
 impl<CT1: CoordTranslate, CT2: CoordTranslate> DualCoordChartState<CT1, CT2> {
+    /// Restore the chart state on the given drawing area
     pub fn restore<'a, DB: DrawingBackend + 'a>(
         self,
         area: &DrawingArea<DB, Shift>,
diff --git a/src/coord/ranged.rs b/src/coord/ranged.rs
index a72d20c0..2e7e2ab8 100644
--- a/src/coord/ranged.rs
+++ b/src/coord/ranged.rs
@@ -21,7 +21,11 @@ pub trait Ranged {
 
     /// This function provides the on-axis part of its range
     fn axis_pixel_range(&self, limit: (i32, i32)) -> Range<i32> {
-        limit.0..limit.1
+        if limit.0 < limit.1 {
+            limit.0..limit.1
+        } else {
+            (limit.1 + 1)..(limit.0 + 1)
+        }
     }
 }
 
diff --git a/src/drawing/area.rs b/src/drawing/area.rs
index 140b2acd..1cebfe63 100644
--- a/src/drawing/area.rs
+++ b/src/drawing/area.rs
@@ -278,7 +278,7 @@ impl<DB: DrawingBackend, CT: CoordTranslate> DrawingArea<DB, CT> {
         self.backend_ops(|backend| {
             backend.draw_rect(
                 (self.rect.x0, self.rect.y0),
-                (self.rect.x1, self.rect.y1),
+                (self.rect.x1 - 1, self.rect.y1 - 1),
                 color,
                 true,
             )
@@ -541,7 +541,7 @@ mod drawing_area_tests {
                 assert_eq!(c, WHITE.to_rgba());
                 assert_eq!(f, true);
                 assert_eq!(u, (0, 0));
-                assert_eq!(d, (1024, 768));
+                assert_eq!(d, (1023, 767));
             });
 
             m.drop_check(|b| {
@@ -569,8 +569,8 @@ mod drawing_area_tests {
                         assert_eq!(
                             d,
                             (
-                                300 + 300 * row as i32 + 2.min(row + 1) as i32,
-                                300 + 300 * col as i32
+                                300 + 300 * row as i32 + 2.min(row + 1) as i32 - 1,
+                                300 + 300 * col as i32 - 1
                             )
                         );
                     });
@@ -598,14 +598,14 @@ mod drawing_area_tests {
                 assert_eq!(c, RED.to_rgba());
                 assert_eq!(f, true);
                 assert_eq!(u, (0, 0));
-                assert_eq!(d, (345, 768));
+                assert_eq!(d, (345 - 1, 768 - 1));
             });
 
             m.check_draw_rect(|c, _, f, u, d| {
                 assert_eq!(c, BLUE.to_rgba());
                 assert_eq!(f, true);
                 assert_eq!(u, (345, 0));
-                assert_eq!(d, (1024, 768));
+                assert_eq!(d, (1024 - 1, 768 - 1));
             });
 
             m.drop_check(|b| {
@@ -626,14 +626,14 @@ mod drawing_area_tests {
                 assert_eq!(c, RED.to_rgba());
                 assert_eq!(f, true);
                 assert_eq!(u, (0, 0));
-                assert_eq!(d, (1024, 345));
+                assert_eq!(d, (1024 - 1, 345 - 1));
             });
 
             m.check_draw_rect(|c, _, f, u, d| {
                 assert_eq!(c, BLUE.to_rgba());
                 assert_eq!(f, true);
                 assert_eq!(u, (0, 345));
-                assert_eq!(d, (1024, 768));
+                assert_eq!(d, (1024 - 1, 768 - 1));
             });
 
             m.drop_check(|b| {
@@ -670,8 +670,10 @@ mod drawing_area_tests {
                             };
 
                             let expected_u = (get_bp(1024, nxb, col), get_bp(768, nyb, row));
-                            let expected_d =
-                                (get_bp(1024, nxb, col + 1), get_bp(768, nyb, row + 1));
+                            let expected_d = (
+                                get_bp(1024, nxb, col + 1) - 1,
+                                get_bp(768, nyb, row + 1) - 1,
+                            );
                             let expected_color =
                                 colors[(row * (nxb + 1) + col) as usize % colors.len()];
 
@@ -714,7 +716,7 @@ mod drawing_area_tests {
                 assert_eq!(f, true);
                 assert_eq!(u.0, 0);
                 assert!(u.1 > 0);
-                assert_eq!(d, (1024, 768));
+                assert_eq!(d, (1024 - 1, 768 - 1));
             });
             m.drop_check(|b| {
                 assert_eq!(b.num_draw_text_call, 1);
@@ -737,7 +739,7 @@ mod drawing_area_tests {
                 assert_eq!(c, WHITE.to_rgba());
                 assert_eq!(f, true);
                 assert_eq!(u, (3, 1));
-                assert_eq!(d, (1024 - 4, 768 - 2));
+                assert_eq!(d, (1024 - 4 - 1, 768 - 2 - 1));
             });
 
             m.drop_check(|b| {
@@ -796,22 +798,22 @@ mod drawing_area_tests {
                     0 => {
                         assert_eq!(c, RED.to_rgba());
                         assert_eq!(u, (0, 0));
-                        assert_eq!(d, (300, 600));
+                        assert_eq!(d, (300 - 1, 600 - 1));
                     }
                     1 => {
                         assert_eq!(c, BLUE.to_rgba());
                         assert_eq!(u, (300, 0));
-                        assert_eq!(d, (1000, 600));
+                        assert_eq!(d, (1000 - 1, 600 - 1));
                     }
                     2 => {
                         assert_eq!(c, GREEN.to_rgba());
                         assert_eq!(u, (0, 600));
-                        assert_eq!(d, (300, 1200));
+                        assert_eq!(d, (300 - 1, 1200 - 1));
                     }
                     3 => {
                         assert_eq!(c, WHITE.to_rgba());
                         assert_eq!(u, (300, 600));
-                        assert_eq!(d, (1000, 1200));
+                        assert_eq!(d, (1000 - 1, 1200 - 1));
                     }
                     _ => panic!("Too many draw rect"),
                 }
@@ -839,7 +841,7 @@ mod drawing_area_tests {
         let drawing_area = create_mocked_drawing_area(1000, 1200, |m| {
             m.check_draw_rect(move |_, _, _, u, d| {
                 assert_eq!((100, 100), u);
-                assert_eq!((300, 700), d);
+                assert_eq!((300 - 1, 700 - 1), d);
             });
 
             m.drop_check(|b| {
diff --git a/src/drawing/backend.rs b/src/drawing/backend.rs
index c982247a..f90618a2 100644
--- a/src/drawing/backend.rs
+++ b/src/drawing/backend.rs
@@ -219,6 +219,10 @@ pub trait DrawingBackend: Sized {
     ///
     /// - `text`: pos the left upper conner of the bitmap to blit
     /// - `src`: The source of the image
+    ///
+    /// TODO: The default implementation of bitmap blitting assumes that the bitmap is RGB, but
+    /// this may not be the case. But for bitmap backend it's actually ok if we use the bitmap
+    /// element that matches the pixel format, but we need to fix this.
     fn blit_bitmap<'a>(
         &mut self,
         pos: BackendCoord,
diff --git a/src/drawing/backend_impl/bitmap.rs b/src/drawing/backend_impl/bitmap.rs
index 80a838f9..997de514 100644
--- a/src/drawing/backend_impl/bitmap.rs
+++ b/src/drawing/backend_impl/bitmap.rs
@@ -13,10 +13,14 @@ mod image_encoding_support {
 use image_encoding_support::*;
 
 #[derive(Debug)]
+/// Indicates some error occurs within the bitmap backend
 pub enum BitMapBackendError {
+    /// The buffer provided is invalid, for example, wrong pixel buffer size
     InvalidBuffer,
+    /// Some IO error occurs while the bitmap maniuplation
     IOError(std::io::Error),
     #[cfg(all(not(target_arch = "wasm32"), feature = "image"))]
+    /// Image encoding error
     ImageError(ImageError),
 }
 
@@ -28,8 +32,13 @@ impl std::fmt::Display for BitMapBackendError {
 
 impl std::error::Error for BitMapBackendError {}
 
-fn blend(prev: &mut u8, new: u8, a: f64) {
-    *prev = ((f64::from(*prev)) * (1.0 - a) + a * f64::from(new)).min(255.0) as u8;
+#[inline(always)]
+fn blend(prev: &mut u8, new: u8, a: u64) {
+    if new > *prev {
+        *prev += ((new - *prev) as u64 * a / 256) as u8
+    } else {
+        *prev -= ((*prev - new) as u64 * a / 256) as u8
+    }
 }
 
 #[cfg(all(feature = "gif", not(target_arch = "wasm32"), feature = "image"))]
@@ -101,6 +110,7 @@ enum Buffer<'a> {
 }
 
 impl<'a> Buffer<'a> {
+    #[inline(always)]
     fn borrow_buffer(&mut self) -> &mut [u8] {
         match self {
             #[cfg(all(not(target_arch = "wasm32"), feature = "image"))]
@@ -110,126 +120,171 @@ impl<'a> Buffer<'a> {
     }
 }
 
-/// The backend that drawing a bitmap
-pub struct BitMapBackend<'a> {
-    /// The path to the image
-    #[allow(dead_code)]
-    target: Target<'a>,
-    /// The size of the image
-    size: (u32, u32),
-    /// The data buffer of the image
-    buffer: Buffer<'a>,
-    /// Flag indicates if the bitmap has been saved
-    saved: bool,
-}
+/// The trait that describes some details about a particular pixel format
+pub trait PixelFormat: Sized {
+    /// Number of bytes per pixel
+    const PIXEL_SIZE: usize;
 
-impl<'a> BitMapBackend<'a> {
-    const PIXEL_SIZE: usize = 3;
-}
+    /// Number of effective bytes per pixel, e.g. for BGRX pixel format, the size of pixel
+    /// is 4 but the effective size is 3, since the 4th byte isn't used
+    const EFFECTIVE_PIXEL_SIZE: usize;
 
-impl<'a> BitMapBackend<'a> {
-    /// Create a new bitmap backend
-    #[cfg(all(not(target_arch = "wasm32"), feature = "image"))]
-    pub fn new<T: AsRef<Path> + ?Sized>(path: &'a T, (w, h): (u32, u32)) -> Self {
-        Self {
-            target: Target::File(path.as_ref()),
-            size: (w, h),
-            buffer: Buffer::Owned(vec![0; Self::PIXEL_SIZE * (w * h) as usize]),
-            saved: false,
-        }
-    }
+    /// Encoding a pixel and returns the idx-th byte for the pixel
+    fn byte_at(r: u8, g: u8, b: u8, a: u64, idx: usize) -> u8;
 
-    /// Create a new bitmap backend that generate GIF animation
+    /// Decode a pixel at the given location
+    fn decode_pixel(data: &[u8]) -> (u8, u8, u8, u64);
+
+    /// The fast alpha blending algorithm for this pixel format
     ///
-    /// When this is used, the bitmap backend acts similar to a real-time rendering backend.
-    /// When the program finished drawing one frame, use `present` function to flush the frame
-    /// into the GIF file.
+    /// - `target`: The target bitmap backend
+    /// - `upper_left`: The upper-left coord for the rect
+    /// - `bottom_right`: The bottom-right coord for the rect
+    /// - `r`, `g`, `b`, `a`: The blending color and alpha value
+    fn blend_rect_fast(
+        target: &mut BitMapBackend<'_, Self>,
+        upper_left: (i32, i32),
+        bottom_right: (i32, i32),
+        r: u8,
+        g: u8,
+        b: u8,
+        a: f64,
+    );
+
+    /// The fast vertical line filling algorithm
     ///
-    /// - `path`: The path to the GIF file to create
-    /// - `dimension`: The size of the GIF image
-    /// - `speed`: The amount of time for each frame to display
-    #[cfg(all(feature = "gif", not(target_arch = "wasm32"), feature = "image"))]
-    pub fn gif<T: AsRef<Path>>(
-        path: T,
-        (w, h): (u32, u32),
-        frame_delay: u32,
-    ) -> Result<Self, BitMapBackendError> {
-        Ok(Self {
-            target: Target::Gif(Box::new(gif_support::GifFile::new(
-                path,
-                (w, h),
-                frame_delay,
-            )?)),
-            size: (w, h),
-            buffer: Buffer::Owned(vec![0; Self::PIXEL_SIZE * (w * h) as usize]),
-            saved: false,
-        })
+    /// - `target`: The target bitmap backend
+    /// - `x`: the X coordinate for the entire line
+    /// - `ys`: The range of y coord
+    /// - `r`, `g`, `b`: The blending color and alpha value
+    fn fill_vertical_line_fast(
+        target: &mut BitMapBackend<'_, Self>,
+        x: i32,
+        ys: (i32, i32),
+        r: u8,
+        g: u8,
+        b: u8,
+    ) {
+        let (w, h) = target.get_size();
+        let w = w as i32;
+        let h = h as i32;
+
+        // Make sure we are in the range
+        if x < 0 || x >= w {
+            return;
+        }
+
+        let dst = target.get_raw_pixel_buffer();
+        let (mut y0, mut y1) = ys;
+        if y0 > y1 {
+            std::mem::swap(&mut y0, &mut y1);
+        }
+        // And check the y axis isn't out of bound
+        y0 = y0.max(0);
+        y1 = y1.min(h - 1);
+        // This is ok because once y0 > y1, there won't be any iteration anymore
+        for y in y0..=y1 {
+            for idx in 0..Self::EFFECTIVE_PIXEL_SIZE {
+                dst[(y * w + x) as usize * Self::PIXEL_SIZE + idx] = Self::byte_at(r, g, b, 0, idx);
+            }
+        }
     }
 
-    /// Create a new bitmap backend which only lives in-memory
+    /// The fast rectangle filling algorithm
     ///
-    /// When this is used, the bitmap backend will write to a user provided [u8] array (or Vec<u8>).
-    /// Plotters uses RGB pixel format
+    /// - `target`: The target bitmap backend
+    /// - `upper_left`: The upper-left coord for the rect
+    /// - `bottom_right`: The bottom-right coord for the rect
+    /// - `r`, `g`, `b`: The filling color
+    fn fill_rect_fast(
+        target: &mut BitMapBackend<'_, Self>,
+        upper_left: (i32, i32),
+        bottom_right: (i32, i32),
+        r: u8,
+        g: u8,
+        b: u8,
+    );
+
+    #[inline(always)]
+    /// Drawing a single pixel in this format
     ///
-    /// - `buf`: The buffer to operate
-    /// - `dimension`: The size of the image in pixels
-    pub fn with_buffer(buf: &'a mut [u8], (w, h): (u32, u32)) -> Self {
-        if (w * h) as usize * Self::PIXEL_SIZE > buf.len() {
-            // TODO: This doesn't deserve a panic.
-            panic!(
-                "Wrong image size: H = {}, W = {}, BufSize = {}",
-                w,
-                h,
-                buf.len()
-            );
-        }
+    /// - `target`: The target bitmap backend
+    /// - `point`: The coord of the point
+    /// - `r`, `g`, `b`: The filling color
+    /// - `alpha`: The alpha value
+    fn draw_pixel(
+        target: &mut BitMapBackend<'_, Self>,
+        point: (i32, i32),
+        (r, g, b): (u8, u8, u8),
+        alpha: f64,
+    ) {
+        let (x, y) = (point.0 as usize, point.1 as usize);
+        let (w, _) = target.get_size();
+        let buf = target.get_raw_pixel_buffer();
+        let w = w as usize;
+        let base = (y * w + x) * Self::PIXEL_SIZE;
 
-        Self {
-            target: Target::Buffer(PhantomData),
-            size: (w, h),
-            buffer: Buffer::Borrowed(buf),
-            saved: false,
+        if base < buf.len() {
+            unsafe {
+                if alpha >= 1.0 {
+                    for idx in 0..Self::EFFECTIVE_PIXEL_SIZE {
+                        *buf.get_unchecked_mut(base + idx) = Self::byte_at(r, g, b, 0, idx);
+                    }
+                } else {
+                    let alpha = (alpha * 256.0).floor() as u64;
+                    for idx in 0..Self::EFFECTIVE_PIXEL_SIZE {
+                        blend(
+                            buf.get_unchecked_mut(base + idx),
+                            Self::byte_at(r, g, b, 0, idx),
+                            alpha,
+                        );
+                    }
+                }
+            }
         }
     }
 
-    fn get_raw_pixel_buffer(&mut self) -> &mut [u8] {
-        self.buffer.borrow_buffer()
+    /// Indicates if this pixel format can be saved as image.
+    /// Note: Currently we only using RGB pixel format in the image crate, but later we may lift
+    /// this restriction
+    ///
+    /// - `returns`: If the image can be saved as image file
+    fn can_be_saved() -> bool {
+        false
     }
+}
 
-    /// Split a bitmap backend vertically into several sub drawing area which allows
-    /// multi-threading rendering.
-    pub fn split(&mut self, area_size: &[u32]) -> Vec<BitMapBackend> {
-        let (w, h) = self.get_size();
-        let buf = self.get_raw_pixel_buffer();
+/// The marker type that indicates we are currently using a RGB888 pixel format
+pub struct RGBPixel;
 
-        let base_addr = &mut buf[0] as *mut u8;
-        let mut split_points = vec![0];
-        for size in area_size {
-            let next = split_points.last().unwrap() + size;
-            if next >= h {
-                break;
-            }
-            split_points.push(next);
+/// The marker type that indicates we are currently using a BGRX8888 pixel format
+pub struct BGRXPixel;
+
+impl PixelFormat for RGBPixel {
+    const PIXEL_SIZE: usize = 3;
+    const EFFECTIVE_PIXEL_SIZE: usize = 3;
+
+    #[inline(always)]
+    fn byte_at(r: u8, g: u8, b: u8, _a: u64, idx: usize) -> u8 {
+        match idx {
+            0 => r,
+            1 => g,
+            2 => b,
+            _ => 0xff,
         }
-        split_points.push(h);
+    }
 
-        split_points
-            .iter()
-            .zip(split_points.iter().skip(1))
-            .map(|(begin, end)| {
-                let actual_buf = unsafe {
-                    std::slice::from_raw_parts_mut(
-                        base_addr.offset((begin * w) as isize * Self::PIXEL_SIZE as isize),
-                        ((end - begin) * w) as usize * Self::PIXEL_SIZE,
-                    )
-                };
-                Self::with_buffer(actual_buf, (w, end - begin))
-            })
-            .collect()
+    #[inline(always)]
+    fn decode_pixel(data: &[u8]) -> (u8, u8, u8, u64) {
+        (data[0], data[1], data[2], 0x255)
+    }
+
+    fn can_be_saved() -> bool {
+        true
     }
 
     fn blend_rect_fast(
-        &mut self,
+        target: &mut BitMapBackend<'_, Self>,
         upper_left: (i32, i32),
         bottom_right: (i32, i32),
         r: u8,
@@ -237,7 +292,7 @@ impl<'a> BitMapBackend<'a> {
         b: u8,
         a: f64,
     ) {
-        let (w, h) = self.get_size();
+        let (w, h) = target.get_size();
         let a = a.min(1.0).max(0.0);
         if a == 0.0 {
             return;
@@ -258,10 +313,9 @@ impl<'a> BitMapBackend<'a> {
             return;
         }
 
-        let dst = self.get_raw_pixel_buffer();
+        let dst = target.get_raw_pixel_buffer();
 
-        let af = a;
-        let a = (255.9 * a).floor() as u64;
+        let a = (256.0 * a).floor() as u64;
 
         // Since we should always make sure the RGB payload occupies the logic lower bits
         // thus, this type purning should work for both LE and BE CPUs
@@ -273,11 +327,224 @@ impl<'a> BitMapBackend<'a> {
             ])
         };
 
-        let (q1, q2, q3): (u64, u64, u64) = unsafe {
+        let (q1, q2, q3): (u64, u64, u64) = unsafe {
+            std::mem::transmute([
+                g as u16, r as u16, b as u16, g as u16, // QW1
+                r as u16, b as u16, g as u16, r as u16, // QW2
+                b as u16, g as u16, r as u16, b as u16, // QW3
+            ])
+        };
+
+        const N: u64 = 0xff00ff00ff00ff00;
+        const M: u64 = 0x00ff00ff00ff00ff;
+
+        for y in y0..=y1 {
+            let start = (y * w as i32 + x0) as usize;
+            let count = (x1 - x0 + 1) as usize;
+
+            let start_ptr = &mut dst[start * Self::PIXEL_SIZE] as *mut u8 as *mut [u8; 24];
+            let slice = unsafe { std::slice::from_raw_parts_mut(start_ptr, (count - 1) / 8) };
+            for p in slice.iter_mut() {
+                let ptr = p as *mut [u8; 24] as *mut (u64, u64, u64);
+                let (d1, d2, d3) = unsafe { *ptr };
+                let (mut h1, mut h2, mut h3) = ((d1 >> 8) & M, (d2 >> 8) & M, (d3 >> 8) & M);
+                let (mut l1, mut l2, mut l3) = (d1 & M, d2 & M, d3 & M);
+
+                #[cfg(target_endian = "little")]
+                {
+                    h1 = (h1 * (256 - a) + q1 * a) & N;
+                    h2 = (h2 * (256 - a) + q2 * a) & N;
+                    h3 = (h3 * (256 - a) + q3 * a) & N;
+                    l1 = ((l1 * (256 - a) + p1 * a) & N) >> 8;
+                    l2 = ((l2 * (256 - a) + p2 * a) & N) >> 8;
+                    l3 = ((l3 * (256 - a) + p3 * a) & N) >> 8;
+                }
+
+                #[cfg(target_endian = "big")]
+                {
+                    h1 = (h1 * (256 - a) + p1 * a) & N;
+                    h2 = (h2 * (256 - a) + p2 * a) & N;
+                    h3 = (h3 * (256 - a) + p3 * a) & N;
+                    l1 = ((l1 * (256 - a) + q1 * a) & N) >> 8;
+                    l2 = ((l2 * (256 - a) + q2 * a) & N) >> 8;
+                    l3 = ((l3 * (256 - a) + q3 * a) & N) >> 8;
+                }
+
+                unsafe {
+                    *ptr = (h1 | l1, h2 | l2, h3 | l3);
+                }
+            }
+
+            let mut iter = dst[((start + slice.len() * 8) * Self::PIXEL_SIZE)
+                ..((start + count) * Self::PIXEL_SIZE)]
+                .iter_mut();
+            for _ in (slice.len() * 8)..count {
+                blend(iter.next().unwrap(), r, a);
+                blend(iter.next().unwrap(), g, a);
+                blend(iter.next().unwrap(), b, a);
+            }
+        }
+    }
+
+    fn fill_rect_fast(
+        target: &mut BitMapBackend<'_, Self>,
+        upper_left: (i32, i32),
+        bottom_right: (i32, i32),
+        r: u8,
+        g: u8,
+        b: u8,
+    ) {
+        let (w, h) = target.get_size();
+        let (x0, y0) = (
+            upper_left.0.min(bottom_right.0).max(0),
+            upper_left.1.min(bottom_right.1).max(0),
+        );
+        let (x1, y1) = (
+            upper_left.0.max(bottom_right.0).min(w as i32 - 1),
+            upper_left.1.max(bottom_right.1).min(h as i32 - 1),
+        );
+
+        // This may happen when the minimal value is larger than the limit.
+        // Thus we just have something that is completely out-of-range
+        if x0 > x1 || y0 > y1 {
+            return;
+        }
+
+        let dst = target.get_raw_pixel_buffer();
+
+        if r == g && g == b {
+            // If r == g == b, then we can use memset
+            if x0 != 0 || x1 != w as i32 - 1 {
+                // If it's not the entire row is filled, we can only do
+                // memset per row
+                for y in y0..=y1 {
+                    let start = (y * w as i32 + x0) as usize;
+                    let count = (x1 - x0 + 1) as usize;
+                    dst[(start * Self::PIXEL_SIZE)..((start + count) * Self::PIXEL_SIZE)]
+                        .iter_mut()
+                        .for_each(|e| *e = r);
+                }
+            } else {
+                // If the entire memory block is going to be filled, just use single memset
+                dst[Self::PIXEL_SIZE * (y0 * w as i32) as usize
+                    ..((y1 + 1) * w as i32) as usize * Self::PIXEL_SIZE]
+                    .iter_mut()
+                    .for_each(|e| *e = r);
+            }
+        } else {
+            let count = (x1 - x0 + 1) as usize;
+            if count < 8 {
+                for y in y0..=y1 {
+                    let start = (y * w as i32 + x0) as usize;
+                    let mut iter = dst
+                        [(start * Self::PIXEL_SIZE)..((start + count) * Self::PIXEL_SIZE)]
+                        .iter_mut();
+                    for _ in 0..(x1 - x0 + 1) {
+                        *iter.next().unwrap() = r;
+                        *iter.next().unwrap() = g;
+                        *iter.next().unwrap() = b;
+                    }
+                }
+            } else {
+                for y in y0..=y1 {
+                    let start = (y * w as i32 + x0) as usize;
+                    let start_ptr = &mut dst[start * Self::PIXEL_SIZE] as *mut u8 as *mut [u8; 24];
+                    let slice =
+                        unsafe { std::slice::from_raw_parts_mut(start_ptr, (count - 1) / 8) };
+                    for p in slice.iter_mut() {
+                        // In this case, we can actually fill 8 pixels in one iteration with
+                        // only 3 movq instructions.
+                        // TODO: Consider using AVX instructions when possible
+                        let ptr = p as *mut [u8; 24] as *mut u64;
+                        unsafe {
+                            let (d1, d2, d3): (u64, u64, u64) = std::mem::transmute([
+                                r, g, b, r, g, b, r, g, // QW1
+                                b, r, g, b, r, g, b, r, // QW2
+                                g, b, r, g, b, r, g, b, // QW3
+                            ]);
+                            *ptr = d1;
+                            *ptr.offset(1) = d2;
+                            *ptr.offset(2) = d3;
+                        }
+                    }
+
+                    for idx in (slice.len() * 8)..count {
+                        dst[start * Self::PIXEL_SIZE + idx * Self::PIXEL_SIZE] = r;
+                        dst[start * Self::PIXEL_SIZE + idx * Self::PIXEL_SIZE + 1] = g;
+                        dst[start * Self::PIXEL_SIZE + idx * Self::PIXEL_SIZE + 2] = b;
+                    }
+                }
+            }
+        }
+    }
+}
+
+impl PixelFormat for BGRXPixel {
+    const PIXEL_SIZE: usize = 4;
+    const EFFECTIVE_PIXEL_SIZE: usize = 3;
+
+    #[inline(always)]
+    fn byte_at(r: u8, g: u8, b: u8, _a: u64, idx: usize) -> u8 {
+        match idx {
+            0 => b,
+            1 => g,
+            2 => r,
+            _ => 0xff,
+        }
+    }
+
+    #[inline(always)]
+    fn decode_pixel(data: &[u8]) -> (u8, u8, u8, u64) {
+        (data[2], data[1], data[0], 0x255)
+    }
+
+    fn blend_rect_fast(
+        target: &mut BitMapBackend<'_, Self>,
+        upper_left: (i32, i32),
+        bottom_right: (i32, i32),
+        r: u8,
+        g: u8,
+        b: u8,
+        a: f64,
+    ) {
+        let (w, h) = target.get_size();
+        let a = a.min(1.0).max(0.0);
+        if a == 0.0 {
+            return;
+        }
+
+        let (x0, y0) = (
+            upper_left.0.min(bottom_right.0).max(0),
+            upper_left.1.min(bottom_right.1).max(0),
+        );
+        let (x1, y1) = (
+            upper_left.0.max(bottom_right.0).min(w as i32 - 1),
+            upper_left.1.max(bottom_right.1).min(h as i32 - 1),
+        );
+
+        // This may happen when the minimal value is larger than the limit.
+        // Thus we just have something that is completely out-of-range
+        if x0 > x1 || y0 > y1 {
+            return;
+        }
+
+        let dst = target.get_raw_pixel_buffer();
+
+        let a = (256.0 * a).floor() as u64;
+
+        // Since we should always make sure the RGB payload occupies the logic lower bits
+        // thus, this type purning should work for both LE and BE CPUs
+        let p: u64 = unsafe {
+            //0011 0022 0033 0011
+            std::mem::transmute([
+                b as u16, r as u16, b as u16, r as u16, // QW1
+            ])
+        };
+
+        let q: u64 = unsafe {
+            //0022 0011 0033 0022
             std::mem::transmute([
-                g as u16, r as u16, b as u16, g as u16, // QW1
-                r as u16, b as u16, g as u16, r as u16, // QW2
-                b as u16, g as u16, r as u16, b as u16, // QW3
+                g as u16, 0 as u16, g as u16, 0 as u16, // QW1
             ])
         };
 
@@ -288,72 +555,52 @@ impl<'a> BitMapBackend<'a> {
             let start = (y * w as i32 + x0) as usize;
             let count = (x1 - x0 + 1) as usize;
 
-            let start_ptr = &mut dst[start * Self::PIXEL_SIZE] as *mut u8 as *mut [u8; 24];
-            let slice = unsafe { std::slice::from_raw_parts_mut(start_ptr, (count - 1) / 8) };
-            for p in slice.iter_mut() {
-                let ptr = p as *mut [u8; 24] as *mut (u64, u64, u64);
-                let (d1, d2, d3) = unsafe { *ptr };
+            let start_ptr = &mut dst[start * Self::PIXEL_SIZE] as *mut u8 as *mut [u8; 8];
+            let slice = unsafe { std::slice::from_raw_parts_mut(start_ptr, (count - 1) / 2) };
+            for rp in slice.iter_mut() {
+                let ptr = rp as *mut [u8; 8] as *mut u64;
+                let d1 = unsafe { *ptr };
+                let mut h = (d1 >> 8) & M;
+                let mut l = d1 & M;
+
+                #[cfg(target_endian = "little")]
+                {
+                    h = (h * (256 - a) + q * a) & N;
+                    l = ((l * (256 - a) + p * a) & N) >> 8;
+                }
 
-                let (mut h1, mut h2, mut h3) = ((d1 >> 8) & M, (d2 >> 8) & M, (d3 >> 8) & M);
-                let (mut l1, mut l2, mut l3) = (d1 & M, d2 & M, d3 & M);
-                h1 = (h1 * (255 - a) + q1 * a) & N;
-                h2 = (h2 * (255 - a) + q2 * a) & N;
-                h3 = (h3 * (255 - a) + q3 * a) & N;
-                l1 = ((l1 * (255 - a) + p1 * a) & N) >> 8;
-                l2 = ((l2 * (255 - a) + p2 * a) & N) >> 8;
-                l3 = ((l3 * (255 - a) + p3 * a) & N) >> 8;
+                #[cfg(target_endian = "big")]
+                {
+                    h = (h * (256 - a) + p * a) & N;
+                    l = ((l * (256 - a) + q * a) & N) >> 8;
+                }
 
                 unsafe {
-                    *ptr = (h1 | l1, h2 | l2, h3 | l3);
+                    *ptr = h | l;
                 }
             }
 
-            let mut iter = dst[((start + slice.len() * 8) * Self::PIXEL_SIZE)
+            let mut iter = dst[((start + slice.len() * 2) * Self::PIXEL_SIZE)
                 ..((start + count) * Self::PIXEL_SIZE)]
                 .iter_mut();
-            for _ in (slice.len() * 8)..count {
-                blend(iter.next().unwrap(), r, af);
-                blend(iter.next().unwrap(), g, af);
-                blend(iter.next().unwrap(), b, af);
+            for _ in (slice.len() * 2)..count {
+                blend(iter.next().unwrap(), b, a);
+                blend(iter.next().unwrap(), g, a);
+                blend(iter.next().unwrap(), r, a);
+                iter.next();
             }
         }
     }
 
-    fn fill_vertical_line_fast(&mut self, x: i32, ys: (i32, i32), r: u8, g: u8, b: u8) {
-        let (w, h) = self.get_size();
-        let w = w as i32;
-        let h = h as i32;
-
-        // Make sure we are in the range
-        if x < 0 || x >= w {
-            return;
-        }
-
-        let dst = self.get_raw_pixel_buffer();
-        let (mut y0, mut y1) = ys;
-        if y0 > y1 {
-            std::mem::swap(&mut y0, &mut y1);
-        }
-        // And check the y axis isn't out of bound
-        y0 = y0.max(0);
-        y1 = y1.min(h - 1);
-        // This is ok because once y0 > y1, there won't be any iteration anymore
-        for y in y0..=y1 {
-            dst[(y * w + x) as usize * Self::PIXEL_SIZE] = r;
-            dst[(y * w + x) as usize * Self::PIXEL_SIZE + 1] = g;
-            dst[(y * w + x) as usize * Self::PIXEL_SIZE + 2] = b;
-        }
-    }
-
     fn fill_rect_fast(
-        &mut self,
+        target: &mut BitMapBackend<'_, Self>,
         upper_left: (i32, i32),
         bottom_right: (i32, i32),
         r: u8,
         g: u8,
         b: u8,
     ) {
-        let (w, h) = self.get_size();
+        let (w, h) = target.get_size();
         let (x0, y0) = (
             upper_left.0.min(bottom_right.0).max(0),
             upper_left.1.min(bottom_right.1).max(0),
@@ -369,7 +616,7 @@ impl<'a> BitMapBackend<'a> {
             return;
         }
 
-        let dst = self.get_raw_pixel_buffer();
+        let dst = target.get_raw_pixel_buffer();
 
         if r == g && g == b {
             // If r == g == b, then we can use memset
@@ -379,13 +626,13 @@ impl<'a> BitMapBackend<'a> {
                 for y in y0..=y1 {
                     let start = (y * w as i32 + x0) as usize;
                     let count = (x1 - x0 + 1) as usize;
-                    dst[(start * 3)..((start + count) * Self::PIXEL_SIZE)]
+                    dst[(start * Self::PIXEL_SIZE)..((start + count) * Self::PIXEL_SIZE)]
                         .iter_mut()
                         .for_each(|e| *e = r);
                 }
             } else {
                 // If the entire memory block is going to be filled, just use single memset
-                dst[(3 * y0 * w as i32) as usize
+                dst[Self::PIXEL_SIZE * (y0 * w as i32) as usize
                     ..((y1 + 1) * w as i32) as usize * Self::PIXEL_SIZE]
                     .iter_mut()
                     .for_each(|e| *e = r);
@@ -399,38 +646,35 @@ impl<'a> BitMapBackend<'a> {
                         [(start * Self::PIXEL_SIZE)..((start + count) * Self::PIXEL_SIZE)]
                         .iter_mut();
                     for _ in 0..(x1 - x0 + 1) {
-                        *iter.next().unwrap() = r;
-                        *iter.next().unwrap() = g;
                         *iter.next().unwrap() = b;
+                        *iter.next().unwrap() = g;
+                        *iter.next().unwrap() = r;
+                        iter.next();
                     }
                 }
             } else {
                 for y in y0..=y1 {
                     let start = (y * w as i32 + x0) as usize;
-                    let start_ptr = &mut dst[start * Self::PIXEL_SIZE] as *mut u8 as *mut [u8; 24];
+                    let start_ptr = &mut dst[start * Self::PIXEL_SIZE] as *mut u8 as *mut [u8; 8];
                     let slice =
-                        unsafe { std::slice::from_raw_parts_mut(start_ptr, (count - 1) / 8) };
+                        unsafe { std::slice::from_raw_parts_mut(start_ptr, (count - 1) / 2) };
                     for p in slice.iter_mut() {
                         // In this case, we can actually fill 8 pixels in one iteration with
                         // only 3 movq instructions.
                         // TODO: Consider using AVX instructions when possible
-                        let ptr = p as *mut [u8; 24] as *mut u64;
+                        let ptr = p as *mut [u8; 8] as *mut u64;
                         unsafe {
-                            let (d1, d2, d3): (u64, u64, u64) = std::mem::transmute([
-                                r, g, b, r, g, b, r, g, // QW1
-                                b, r, g, b, r, g, b, r, // QW2
-                                g, b, r, g, b, r, g, b, // QW3
+                            let d: u64 = std::mem::transmute([
+                                b, g, r, 0, b, g, r, 0, // QW1
                             ]);
-                            *ptr = d1;
-                            *ptr.offset(1) = d2;
-                            *ptr.offset(2) = d3;
+                            *ptr = d;
                         }
                     }
 
-                    for idx in (slice.len() * 8)..count {
-                        dst[start * 3 + idx * Self::PIXEL_SIZE] = r;
-                        dst[start * 3 + idx * Self::PIXEL_SIZE + 1] = g;
-                        dst[start * 3 + idx * Self::PIXEL_SIZE + 2] = b;
+                    for idx in (slice.len() * 2)..count {
+                        dst[start * Self::PIXEL_SIZE + idx * Self::PIXEL_SIZE] = b;
+                        dst[start * Self::PIXEL_SIZE + idx * Self::PIXEL_SIZE + 1] = g;
+                        dst[start * Self::PIXEL_SIZE + idx * Self::PIXEL_SIZE + 2] = r;
                     }
                 }
             }
@@ -438,7 +682,151 @@ impl<'a> BitMapBackend<'a> {
     }
 }
 
-impl<'a> DrawingBackend for BitMapBackend<'a> {
+/// The backend that drawing a bitmap
+pub struct BitMapBackend<'a, P: PixelFormat = RGBPixel> {
+    /// The path to the image
+    #[allow(dead_code)]
+    target: Target<'a>,
+    /// The size of the image
+    size: (u32, u32),
+    /// The data buffer of the image
+    buffer: Buffer<'a>,
+    /// Flag indicates if the bitmap has been saved
+    saved: bool,
+    _pantomdata: PhantomData<P>,
+}
+
+impl<'a, P: PixelFormat> BitMapBackend<'a, P> {
+    /// The number of bytes per pixel
+    const PIXEL_SIZE: usize = P::PIXEL_SIZE;
+}
+
+impl<'a> BitMapBackend<'a, RGBPixel> {
+    /// Create a new bitmap backend
+    #[cfg(all(not(target_arch = "wasm32"), feature = "image"))]
+    pub fn new<T: AsRef<Path> + ?Sized>(path: &'a T, (w, h): (u32, u32)) -> Self {
+        Self {
+            target: Target::File(path.as_ref()),
+            size: (w, h),
+            buffer: Buffer::Owned(vec![0; Self::PIXEL_SIZE * (w * h) as usize]),
+            saved: false,
+            _pantomdata: PhantomData,
+        }
+    }
+
+    /// Create a new bitmap backend that generate GIF animation
+    ///
+    /// When this is used, the bitmap backend acts similar to a real-time rendering backend.
+    /// When the program finished drawing one frame, use `present` function to flush the frame
+    /// into the GIF file.
+    ///
+    /// - `path`: The path to the GIF file to create
+    /// - `dimension`: The size of the GIF image
+    /// - `speed`: The amount of time for each frame to display
+    #[cfg(all(feature = "gif", not(target_arch = "wasm32"), feature = "image"))]
+    pub fn gif<T: AsRef<Path>>(
+        path: T,
+        (w, h): (u32, u32),
+        frame_delay: u32,
+    ) -> Result<Self, BitMapBackendError> {
+        Ok(Self {
+            target: Target::Gif(Box::new(gif_support::GifFile::new(
+                path,
+                (w, h),
+                frame_delay,
+            )?)),
+            size: (w, h),
+            buffer: Buffer::Owned(vec![0; Self::PIXEL_SIZE * (w * h) as usize]),
+            saved: false,
+            _pantomdata: PhantomData,
+        })
+    }
+
+    /// Create a new bitmap backend which only lives in-memory
+    ///
+    /// When this is used, the bitmap backend will write to a user provided [u8] array (or Vec<u8>)
+    /// in RGB pixel format.
+    ///
+    /// Note: This function provides backward compatibility for those code that assumes Plotters
+    /// uses RGB pixel format and maniuplates the in-memory framebuffer.
+    /// For more pixel format option, use `with_buffer_and_format` instead.
+    ///
+    /// - `buf`: The buffer to operate
+    /// - `dimension`: The size of the image in pixels
+    /// - **returns**: The newly created bitmap backend
+    pub fn with_buffer(buf: &'a mut [u8], (w, h): (u32, u32)) -> Self {
+        Self::with_buffer_and_format(buf, (w, h)).expect("Wrong buffer size")
+    }
+}
+
+impl<'a, P: PixelFormat> BitMapBackend<'a, P> {
+    /// Create a new bitmap backend with a in-memory buffer with specific pixel format.
+    ///
+    /// Note: This can be used as a way to manipulate framebuffer, `mmap` can be used on the top of this
+    /// as well.
+    ///
+    /// - `buf`: The buffer to operate
+    /// - `dimension`: The size of the image in pixels
+    /// - **returns**: The newly created bitmap backend
+    pub fn with_buffer_and_format(
+        buf: &'a mut [u8],
+        (w, h): (u32, u32),
+    ) -> Result<Self, BitMapBackendError> {
+        if (w * h) as usize * Self::PIXEL_SIZE > buf.len() {
+            return Err(BitMapBackendError::InvalidBuffer);
+        }
+
+        Ok(Self {
+            target: Target::Buffer(PhantomData),
+            size: (w, h),
+            buffer: Buffer::Borrowed(buf),
+            saved: false,
+            _pantomdata: PhantomData,
+        })
+    }
+
+    #[inline(always)]
+    fn get_raw_pixel_buffer(&mut self) -> &mut [u8] {
+        self.buffer.borrow_buffer()
+    }
+
+    /// Split a bitmap backend vertically into several sub drawing area which allows
+    /// multi-threading rendering.
+    ///
+    /// - `area_size`: The size of the area
+    /// - **returns**: The splitted backends that can be rendered in parallel
+    pub fn split(&mut self, area_size: &[u32]) -> Vec<BitMapBackend<P>> {
+        let (w, h) = self.get_size();
+        let buf = self.get_raw_pixel_buffer();
+
+        let base_addr = &mut buf[0] as *mut u8;
+        let mut split_points = vec![0];
+        for size in area_size {
+            let next = split_points.last().unwrap() + size;
+            if next >= h {
+                break;
+            }
+            split_points.push(next);
+        }
+        split_points.push(h);
+
+        split_points
+            .iter()
+            .zip(split_points.iter().skip(1))
+            .map(|(begin, end)| {
+                let actual_buf = unsafe {
+                    std::slice::from_raw_parts_mut(
+                        base_addr.offset((begin * w) as isize * Self::PIXEL_SIZE as isize),
+                        ((end - begin) * w) as usize * Self::PIXEL_SIZE,
+                    )
+                };
+                Self::with_buffer_and_format(actual_buf, (w, end - begin)).unwrap()
+            })
+            .collect()
+    }
+}
+
+impl<'a, P: PixelFormat> DrawingBackend for BitMapBackend<'a, P> {
     type ErrorType = BitMapBackendError;
 
     fn get_size(&self) -> (u32, u32) {
@@ -457,6 +845,9 @@ impl<'a> DrawingBackend for BitMapBackend<'a> {
 
     #[cfg(all(not(target_arch = "wasm32"), feature = "image"))]
     fn present(&mut self) -> Result<(), DrawingErrorKind<BitMapBackendError>> {
+        if !P::can_be_saved() {
+            return Ok(());
+        }
         let (w, h) = self.get_size();
         match &mut self.target {
             Target::File(path) => {
@@ -493,30 +884,11 @@ impl<'a> DrawingBackend for BitMapBackend<'a> {
             return Ok(());
         }
 
-        let (w, _) = self.get_size();
         let alpha = color.alpha();
         let rgb = color.rgb();
 
-        let buf = self.get_raw_pixel_buffer();
-
-        let (x, y) = (point.0 as usize, point.1 as usize);
-        let w = w as usize;
-
-        let base = (y * w + x) * Self::PIXEL_SIZE;
+        P::draw_pixel(self, point, rgb, alpha);
 
-        if base < buf.len() {
-            unsafe {
-                if alpha >= 1.0 {
-                    *buf.get_unchecked_mut(base) = rgb.0;
-                    *buf.get_unchecked_mut(base + 1) = rgb.1;
-                    *buf.get_unchecked_mut(base + 2) = rgb.2;
-                } else {
-                    blend(buf.get_unchecked_mut(base), rgb.0, alpha);
-                    blend(buf.get_unchecked_mut(base + 1), rgb.1, alpha);
-                    blend(buf.get_unchecked_mut(base + 2), rgb.2, alpha);
-                }
-            }
-        }
         Ok(())
     }
 
@@ -532,12 +904,12 @@ impl<'a> DrawingBackend for BitMapBackend<'a> {
         if from.0 == to.0 || from.1 == to.1 {
             if alpha >= 1.0 {
                 if from.1 == to.1 {
-                    self.fill_rect_fast(from, to, r, g, b);
+                    P::fill_rect_fast(self, from, to, r, g, b);
                 } else {
-                    self.fill_vertical_line_fast(from.0, (from.1, to.1), r, g, b);
+                    P::fill_vertical_line_fast(self, from.0, (from.1, to.1), r, g, b);
                 }
             } else {
-                self.blend_rect_fast(from, to, r, g, b, alpha);
+                P::blend_rect_fast(self, from, to, r, g, b, alpha);
             }
             return Ok(());
         }
@@ -556,9 +928,9 @@ impl<'a> DrawingBackend for BitMapBackend<'a> {
         let (r, g, b) = style.as_color().rgb();
         if fill {
             if alpha >= 1.0 {
-                self.fill_rect_fast(upper_left, bottom_right, r, g, b);
+                P::fill_rect_fast(self, upper_left, bottom_right, r, g, b);
             } else {
-                self.blend_rect_fast(upper_left, bottom_right, r, g, b, alpha);
+                P::blend_rect_fast(self, upper_left, bottom_right, r, g, b, alpha);
             }
             return Ok(());
         }
@@ -612,7 +984,7 @@ impl<'a> DrawingBackend for BitMapBackend<'a> {
     }
 }
 
-impl Drop for BitMapBackend<'_> {
+impl<P: PixelFormat> Drop for BitMapBackend<'_, P> {
     fn drop(&mut self) {
         if !self.saved {
             self.present().expect("Unable to save the bitmap");
@@ -716,7 +1088,7 @@ fn test_bitmap_backend_blend() {
     for x in 0..10 {
         for y in 0..10 {
             let (r, g, b) = if x <= 5 {
-                (204, 224, 244)
+                (205, 225, 245)
             } else {
                 (255, 255, 255)
             };
@@ -802,3 +1174,175 @@ fn test_draw_line_out_of_range() {
         }
     }
 }
+
+#[cfg(test)]
+#[test]
+fn test_bitmap_blend_large() {
+    use crate::prelude::*;
+    let mut buffer = vec![0; 1000 * 1000 * 3];
+
+    for fill_color in [RED, GREEN, BLUE].iter() {
+        buffer.iter_mut().for_each(|x| *x = 0);
+
+        {
+            let mut back = BitMapBackend::with_buffer(&mut buffer, (1000, 1000));
+
+            back.draw_rect((0, 0), (1000, 1000), &WHITE.mix(0.1), true)
+                .unwrap(); // should be (24, 24, 24)
+            back.draw_rect((0, 0), (100, 100), &fill_color.mix(0.5), true)
+                .unwrap(); // should be (139, 24, 24)
+        }
+
+        for x in 0..1000 {
+            for y in 0..1000 {
+                let expected_value = if x <= 100 && y <= 100 {
+                    let (r, g, b) = fill_color.to_rgba().rgb();
+                    (
+                        if r > 0 { 139 } else { 12 },
+                        if g > 0 { 139 } else { 12 },
+                        if b > 0 { 139 } else { 12 },
+                    )
+                } else {
+                    (24, 24, 24)
+                };
+                assert_eq!(buffer[(y * 1000 + x) as usize * 3 + 0], expected_value.0);
+                assert_eq!(buffer[(y * 1000 + x) as usize * 3 + 1], expected_value.1);
+                assert_eq!(buffer[(y * 1000 + x) as usize * 3 + 2], expected_value.2);
+            }
+        }
+    }
+}
+
+#[cfg(test)]
+#[test]
+fn test_bitmap_bgrx_pixel_format() {
+    use crate::drawing::bitmap_pixel::BGRXPixel;
+    use crate::prelude::*;
+    let mut rgb_buffer = vec![0; 1000 * 1000 * 3];
+    let mut bgrx_buffer = vec![0; 1000 * 1000 * 4];
+
+    {
+        let mut rgb_back = BitMapBackend::with_buffer(&mut rgb_buffer, (1000, 1000));
+        let mut bgrx_back =
+            BitMapBackend::<BGRXPixel>::with_buffer_and_format(&mut bgrx_buffer, (1000, 1000))
+                .unwrap();
+
+        rgb_back
+            .draw_rect((0, 0), (1000, 1000), &BLACK, true)
+            .unwrap();
+        bgrx_back
+            .draw_rect((0, 0), (1000, 1000), &BLACK, true)
+            .unwrap();
+
+        rgb_back
+            .draw_rect(
+                (0, 0),
+                (1000, 1000),
+                &RGBColor(0xaa, 0xbb, 0xcc).mix(0.85),
+                true,
+            )
+            .unwrap();
+        bgrx_back
+            .draw_rect(
+                (0, 0),
+                (1000, 1000),
+                &RGBColor(0xaa, 0xbb, 0xcc).mix(0.85),
+                true,
+            )
+            .unwrap();
+
+        rgb_back
+            .draw_rect((0, 0), (1000, 1000), &RED.mix(0.85), true)
+            .unwrap();
+        bgrx_back
+            .draw_rect((0, 0), (1000, 1000), &RED.mix(0.85), true)
+            .unwrap();
+
+        rgb_back.draw_circle((300, 300), 100, &GREEN, true).unwrap();
+        bgrx_back
+            .draw_circle((300, 300), 100, &GREEN, true)
+            .unwrap();
+
+        rgb_back.draw_rect((10, 10), (50, 50), &BLUE, true).unwrap();
+        bgrx_back
+            .draw_rect((10, 10), (50, 50), &BLUE, true)
+            .unwrap();
+
+        rgb_back
+            .draw_rect((10, 10), (50, 50), &WHITE, true)
+            .unwrap();
+        bgrx_back
+            .draw_rect((10, 10), (50, 50), &WHITE, true)
+            .unwrap();
+
+        rgb_back
+            .draw_rect((10, 10), (15, 50), &YELLOW, true)
+            .unwrap();
+        bgrx_back
+            .draw_rect((10, 10), (15, 50), &YELLOW, true)
+            .unwrap();
+    }
+
+    for x in 0..1000 {
+        for y in 0..1000 {
+            assert!(
+                (rgb_buffer[y * 3000 + x * 3 + 0] as i32
+                    - bgrx_buffer[y * 4000 + x * 4 + 2] as i32)
+                    .abs()
+                    <= 1
+            );
+            assert!(
+                (rgb_buffer[y * 3000 + x * 3 + 1] as i32
+                    - bgrx_buffer[y * 4000 + x * 4 + 1] as i32)
+                    .abs()
+                    <= 1
+            );
+            assert!(
+                (rgb_buffer[y * 3000 + x * 3 + 2] as i32
+                    - bgrx_buffer[y * 4000 + x * 4 + 0] as i32)
+                    .abs()
+                    <= 1
+            );
+        }
+    }
+}
+
+#[cfg(test)]
+#[test]
+fn test_bitmap_blit() {
+    let src_bitmap: Vec<u8> = (0..100)
+        .map(|y| (0..300).map(move |x| ((x * y) % 253) as u8))
+        .flatten()
+        .collect();
+
+    use crate::prelude::*;
+    let mut buffer = vec![0; 1000 * 1000 * 3];
+
+    {
+        let mut back = BitMapBackend::with_buffer(&mut buffer, (1000, 1000));
+        back.blit_bitmap((500, 500), (100, 100), &src_bitmap[..])
+            .unwrap();
+    }
+
+    for y in 0..1000 {
+        for x in 0..1000 {
+            if x >= 500 && x < 600 && y >= 500 && y < 600 {
+                let lx = x - 500;
+                let ly = y - 500;
+                assert_eq!(buffer[y * 3000 + x * 3 + 0] as usize, (ly * lx * 3) % 253);
+                assert_eq!(
+                    buffer[y * 3000 + x * 3 + 1] as usize,
+                    (ly * (lx * 3 + 1)) % 253
+                );
+                assert_eq!(
+                    buffer[y * 3000 + x * 3 + 2] as usize,
+                    (ly * (lx * 3 + 2)) % 253
+                );
+            } else {
+                assert_eq!(buffer[y * 3000 + x * 3 + 0], 0);
+                assert_eq!(buffer[y * 3000 + x * 3 + 1], 0);
+                assert_eq!(buffer[y * 3000 + x * 3 + 2], 0);
+            }
+        }
+    }
+}
diff --git a/src/drawing/backend_impl/mod.rs b/src/drawing/backend_impl/mod.rs
index a2fadc9e..bd188413 100644
--- a/src/drawing/backend_impl/mod.rs
+++ b/src/drawing/backend_impl/mod.rs
@@ -6,6 +6,10 @@ pub use self::svg::{svg_types, SVGBackend};
 mod bitmap;
 pub use bitmap::BitMapBackend;
 
+pub mod bitmap_pixel {
+    pub use super::bitmap::{BGRXPixel, PixelFormat, RGBPixel};
+}
+
 #[cfg(target_arch = "wasm32")]
 mod canvas;
 #[cfg(target_arch = "wasm32")]
diff --git a/src/element/image.rs b/src/element/image.rs
index a867cfc5..f241c611 100644
--- a/src/element/image.rs
+++ b/src/element/image.rs
@@ -3,18 +3,21 @@ use image::{DynamicImage, GenericImageView};
 
 use super::{Drawable, PointCollection};
 use crate::drawing::backend::{BackendCoord, DrawingBackend, DrawingErrorKind};
+use crate::drawing::bitmap_pixel::{PixelFormat, RGBPixel};
 
 use crate::drawing::BitMapBackend;
 use std::borrow::{Borrow, Cow};
+use std::marker::PhantomData;
 
 /// The element that contains a bitmap on it
-pub struct BitMapElement<'a, Coord> {
+pub struct BitMapElement<'a, Coord, P: PixelFormat = RGBPixel> {
     image: Cow<'a, Vec<u8>>,
     size: (u32, u32),
     pos: Coord,
+    phantom: PhantomData<P>,
 }
 
-impl<'a, Coord> BitMapElement<'a, Coord> {
+impl<'a, Coord, P: PixelFormat> BitMapElement<'a, Coord, P> {
     /// Create a new empty bitmap element. This can be use as
     /// the draw and blit pattern.
     ///
@@ -22,20 +25,22 @@ impl<'a, Coord> BitMapElement<'a, Coord> {
     /// - `size`: The size of the bitmap
     pub fn new(pos: Coord, size: (u32, u32)) -> Self {
         Self {
-            image: Cow::Owned(vec![0; (size.0 * size.1 * 3) as usize]),
+            image: Cow::Owned(vec![0; (size.0 * size.1) as usize * P::PIXEL_SIZE]),
             size,
             pos,
+            phantom: PhantomData,
         }
     }
 
     /// Copy the existing bitmap element to another location
     ///
     /// - `pos`: The new location to copy
-    pub fn copy_to<Coord2>(&self, pos: Coord2) -> BitMapElement<Coord2> {
+    pub fn copy_to<Coord2>(&self, pos: Coord2) -> BitMapElement<Coord2, P> {
         BitMapElement {
             image: Cow::Borrowed(self.image.borrow()),
             size: self.size,
             pos,
+            phantom: PhantomData,
         }
     }
 
@@ -48,13 +53,13 @@ impl<'a, Coord> BitMapElement<'a, Coord> {
 
     /// Make the bitmap element as a bitmap backend, so that we can use
     /// plotters drawing functionality on the bitmap element
-    pub fn as_bitmap_backend(&mut self) -> BitMapBackend {
-        BitMapBackend::with_buffer(self.image.to_mut(), self.size)
+    pub fn as_bitmap_backend(&mut self) -> BitMapBackend<P> {
+        BitMapBackend::with_buffer_and_format(self.image.to_mut(), self.size).unwrap()
     }
 }
 
 #[cfg(all(not(target_arch = "wasm32"), feature = "image"))]
-impl<'a, Coord> From<(Coord, DynamicImage)> for BitMapElement<'a, Coord> {
+impl<'a, Coord> From<(Coord, DynamicImage)> for BitMapElement<'a, Coord, RGBPixel> {
     fn from((pos, image): (Coord, DynamicImage)) -> Self {
         let (w, h) = image.dimensions();
         let rgb_image = image.to_rgb().into_raw();
@@ -62,6 +67,23 @@ impl<'a, Coord> From<(Coord, DynamicImage)> for BitMapElement<'a, Coord> {
             pos,
             image: Cow::Owned(rgb_image),
             size: (w, h),
+            phantom: PhantomData,
+        }
+    }
+}
+
+#[cfg(all(not(target_arch = "wasm32"), feature = "image"))]
+impl<'a, Coord> From<(Coord, DynamicImage)>
+    for BitMapElement<'a, Coord, crate::drawing::bitmap_pixel::BGRXPixel>
+{
+    fn from((pos, image): (Coord, DynamicImage)) -> Self {
+        let (w, h) = image.dimensions();
+        let rgb_image = image.to_bgra().into_raw();
+        Self {
+            pos,
+            image: Cow::Owned(rgb_image),
+            size: (w, h),
+            phantom: PhantomData,
         }
     }
 }
@@ -82,6 +104,7 @@ impl<'a, Coord, DB: DrawingBackend> Drawable<DB> for BitMapElement<'a, Coord> {
         _: (u32, u32),
     ) -> Result<(), DrawingErrorKind<DB::ErrorType>> {
         if let Some((x, y)) = points.next() {
+            // TODO: convert the pixel format when needed
             return backend.blit_bitmap((x, y), self.size, self.image.as_ref());
         }
         Ok(())
diff --git a/src/series/line_series.rs b/src/series/line_series.rs
index eee8118f..b0fd0b21 100644
--- a/src/series/line_series.rs
+++ b/src/series/line_series.rs
@@ -44,7 +44,7 @@ mod test {
                 assert_eq!(c, RED.to_rgba());
                 assert_eq!(s, 3);
                 for i in 0..100 {
-                    assert_eq!(path[i], (i as i32 * 2, 200 - i as i32 * 2));
+                    assert_eq!(path[i], (i as i32 * 2, 200 - i as i32 * 2 - 1));
                 }
             });
 
diff --git a/src/style/color.rs b/src/style/color.rs
index 41c38894..de7484dd 100644
--- a/src/style/color.rs
+++ b/src/style/color.rs
@@ -48,10 +48,12 @@ pub trait Color {
 pub struct RGBAColor(pub(super) u8, pub(super) u8, pub(super) u8, pub(super) f64);
 
 impl Color for RGBAColor {
+    #[inline(always)]
     fn rgb(&self) -> (u8, u8, u8) {
         (self.0, self.1, self.2)
     }
 
+    #[inline(always)]
     fn alpha(&self) -> f64 {
         self.3
     }


Test patch: