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

Issue:
[BUG] `DrawingArea::fill` doesn't fill the right and bottom edge in some backends
**Describe the bug**
`DrawingArea::fill` doesn't fill the right and bottom edge in some backends and leaves transparent edges in a generated image. As far as I have checked, [plotters-svg](https://github.com/plotters-rs/plotters-svg) and [plotters-canvas](https://github.com/plotters-rs/plotters-canvas) have this issue. `BitMapBackend` is fine about this.

For CanvasBackend, it's quick to see #165's attached image. It continues in the latest code too.

For SVGBackend, The repro code below prints this result. `width` and `height` in the `rect` element should be `"100"` here.

```svg
<svg width="100" height="100" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<rect x="0" y="0" width="99" height="99" opacity="1" fill="#0000FF" stroke="none"/>
</svg>
```

Other backends with sub-pixel coordinate might have the same issue. Can be related to #128.

**To Reproduce**
```rust
use plotters::prelude::*;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut content = String::new();
    {
        let area = SVGBackend::with_string(&mut content, (100, 100)).into_drawing_area();
        area.fill(&BLUE)?;
    }
    println!("{content}");
    Ok(())
}
```

**Version Information**
`plotters`: current master (a72bfbced3523b17adc974ec7459f6c1c6f2d986)
`plotters-svg`: current master (https://github.com/plotters-rs/plotters-svg/commit/47289e89a1078f330fcb6a3886b0ae34dc3f69e2)
`plotters-canvas`: current master (https://github.com/plotters-rs/plotters-canvas/commit/111fce55a9ccc2999864358f991a28df6261f921)


Discussion / hints:
`plotters-cairo` has the same issue. Adding the code below to the example in `gtk-demo` generated the red edge.

```rust
        // ↓ Added
        root.draw(&Rectangle::new(
            [(0, 0), (500, 500)],
            ShapeStyle {
                color: RGBAColor(255, 0, 0, 1.0),
                filled: true,
                stroke_width: 0,
            },
        ))
        .unwrap();
        // ↑ Added

        root.fill(&WHITE).unwrap();
```

![cairo](https://user-images.githubusercontent.com/7472874/174310769-e07c60a1-0071-464b-a5ea-f5aad9446d30.png)
You need to update dependencies to see https://github.com/plotters-rs/plotters-cairo/pull/20 works.

`examples/gtk-demo/Cargo.toml`
```diff
-plotters-cairo = "0.3.1"
-cairo-rs = "^0.9"
-gtk = "^0.9"
-gio = "^0.9"
+plotters-cairo = { git = "https://github.com/shinmili/plotters-cairo.git", branch = "fix-rect-width-height" }
+cairo-rs = "^0.15"
+gtk = "^0.15"
+gio = "^0.15"
```

`examples/gtk-demo/src/main.rs`
```diff
     let application = gtk::Application::new(
         Some("io.github.plotters-rs.plotters-gtk-test"),
         Default::default(),
-    )
-    .expect("Initialization failed...");
+    );
 
     application.connect_activate(|app| {
         build_ui(app);
     });
 
-    application.run(&args().collect::<Vec<_>>());
+    application.run_with_args(&args().collect::<Vec<_>>());
 }
```

 (When should this patch be merged? After publishing plotters-cairo to crates.io?)
Honestly, I don't know if we should fix the backends that don't work or whether we should rather fix plotters call to the backend. I think in this case the call to the backend is wrong. If the canvas is 100x100, the call would draw a rectangle with (0, 0) and (99, 99). And if the canvas was 0x0, the call would even draw a rectangle with (0, 0) and (-1, -1). That looks odd to me and is not how most backends work, as you pointed out in the issue.

https://github.com/plotters-rs/plotters/blob/a72bfbced3523b17adc974ec7459f6c1c6f2d986/src/drawing/area.rs#L283-L293

The question is should we fix the backends that don't work or should we fix the backend that work? I'd say SVG, canvas and cairo were rightfully printing too few pixels, due to the -1. And doing a +1 on in each backend would only harm the rectangle drawing because that should be correct like it is.
IMHO the real problem is that there is no serious vector graphics backend abstraction (as discussed in #128), and implementing `DrawingBackend` for SVG, canvas, or cairo backend has been done by, like, *adjusting* those vector graphics libraries to raster graphics model. I think the ideal solution would be reached by pushing #128 forward.

Looking around some codes and docs, I understood most parts of them assumes `DrawingBackend` as a *framebuffer* or *raster graphics* model. In that context, doing -1 to select the right-bottom end of a canvas is quite natural, just like doing `arr.len() - 1` to index the last element of an array. That also explains why `BitMapBackend` just works fine; it doesn't need to fill the gaps between vector and raster. Not only this issue, but #165 too!

After a new trait for a vector graphics backend is introduced (or `DrawingBackend` is modified to support both raster and vector), vector graphics backends implement it, and plotters starts calling the new trait method, everyone will be finally happy. But it's not started yet and I guess it will take some time. I hope my patches would do until that day. They are a quick and effective fix that work with the current `DrawingBackend`, as long as it keeps its raster graphics model.

Or if we start #128 now, I'd like to help you, and would like to scrap those workaround patches now!
@shinmili I agree and will support you with improving the internal code for vector graphics backends. IMO vector graphics should be the "default" and raster graphics should have to adjust, not the other way around. I just don't have a lot time right now due to exams.

But there's one thing about the bitmap backend, I'm not sure whether it's actually correct:
In this example, we have a 6x6 canvas. First we plot a 5x5, then a 1x1 and then a 0x0 rectangle. However, the bitmap backend seems to increase their size by one pixel. You're right that in an array the last index is one less than the length, but the first index is also 0 (and 0 regarding images naturally means 0 size = invisible). IMO the SVG backend is correct here: 0x0 should not be visible and the 1x1 is just one pixel.

```rust
use plotters::prelude::*;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    //let root = BitMapBackend::new("plots/0.png", (6, 6)).into_drawing_area();
    let root = SVGBackend::new("plots/0.svg", (6, 6)).into_drawing_area();

    root.draw(&Rectangle::new(
        [(0, 0), (5, 5)],
        ShapeStyle {
            color: RGBAColor(255, 0, 0, 1.0),
            filled: true,
            stroke_width: 0,
        },
    ))?;

    root.draw(&Rectangle::new(
        [(0, 0), (1, 1)],
        ShapeStyle {
            color: RGBAColor(0, 255, 0, 1.0),
            filled: true,
            stroke_width: 0,
        },
    ))?;

    root.draw(&Rectangle::new(
        [(0, 0), (0, 0)],
        ShapeStyle {
            color: RGBAColor(0, 0, 255, 1.0),
            filled: true,
            stroke_width: 0,
        },
    ))?;

    root.present()?;

    Ok(())
}
```

![plotters_backends](https://user-images.githubusercontent.com/22240637/174888332-547c6a6d-4893-4f8b-b311-e595929715a0.png)
@AaronErhardt OK, I had a confusion over how raster graphics should be, and now have changed my mind on what to fix: `plotters` and `plotters-bitmap` are the ones that should be fixed even in the current abstraction.

For some reason, I had been thinking raster graphics APIs should take coordinate ranges inclusively, and calling a maybe-out-of-bounds coordinate a "right-bottom corner" sounded really odd to me. In the context of your example, if btimap got fixed, `(6, 6)` would refer to an out-of-bounds pixel, but also be specified as a right-bottom corner coordinate to fill the whole 6x6 canvas. That's what sounded odd.

I stopped confusing after reviewing [Win32 GDI API's `Rectangle` docs](https://docs.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-rectangle) (last visited more than a decade ago):

> ## Remarks
> The rectangle that is drawn excludes the bottom and right edges.

So that's how most graphics libraries work, even in raster graphics. It's just fine to use `(6, 6)`. I'll close the PRs and make ones for `plotters` and `plotters-bitmap`.

Thanks for your fruitful comments!
We really appreciate your contributions. Please bear in mind that plotters and plotters-bitmap are now hosted on the same GitHub repo.

Thanks!

Fix patch:
diff --git a/.github/workflows/plotters-core.yml b/.github/workflows/plotters-core.yml
index 73a0908a..323ba3c1 100644
--- a/.github/workflows/plotters-core.yml
+++ b/.github/workflows/plotters-core.yml
@@ -63,7 +63,7 @@ jobs:
     - uses: actions-rs/cargo@v1
       with:
           command: test
-          args: --verbose --no-default-features --lib
+          args: --verbose --no-default-features --features=svg_backend --lib
   test_all_features:
     runs-on: ubuntu-latest
     steps:
diff --git a/plotters-bitmap/src/bitmap.rs b/plotters-bitmap/src/bitmap.rs
index f11e17aa..cd69d087 100644
--- a/plotters-bitmap/src/bitmap.rs
+++ b/plotters-bitmap/src/bitmap.rs
@@ -252,12 +252,12 @@ impl<'a, P: PixelFormat> DrawingBackend for BitMapBackend<'a, P> {
         if (from.0 == to.0 || from.1 == to.1) && style.stroke_width() == 1 {
             if alpha >= 1.0 {
                 if from.1 == to.1 {
-                    P::fill_rect_fast(self, from, to, r, g, b);
+                    P::fill_rect_fast(self, from, (to.0 + 1, to.1 + 1), r, g, b);
                 } else {
                     P::fill_vertical_line_fast(self, from.0, (from.1, to.1), r, g, b);
                 }
             } else {
-                P::blend_rect_fast(self, from, to, r, g, b, alpha);
+                P::blend_rect_fast(self, from, (to.0 + 1, to.1 + 1), r, g, b, alpha);
             }
             return Ok(());
         }
diff --git a/plotters-bitmap/src/bitmap_pixel/bgrx.rs b/plotters-bitmap/src/bitmap_pixel/bgrx.rs
index baae6381..16bcd25e 100644
--- a/plotters-bitmap/src/bitmap_pixel/bgrx.rs
+++ b/plotters-bitmap/src/bitmap_pixel/bgrx.rs
@@ -46,13 +46,13 @@ impl PixelFormat for BGRXPixel {
             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),
+            upper_left.0.max(bottom_right.0).min(w as i32),
+            upper_left.1.max(bottom_right.1).min(h as i32),
         );
 
         // 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 {
+        if x0 >= x1 || y0 >= y1 {
             return;
         }
 
@@ -79,9 +79,9 @@ impl PixelFormat for BGRXPixel {
         const N: u64 = 0xff00_ff00_ff00_ff00;
         const M: u64 = 0x00ff_00ff_00ff_00ff;
 
-        for y in y0..=y1 {
+        for y in y0..y1 {
             let start = (y * w as i32 + x0) as usize;
-            let count = (x1 - x0 + 1) as usize;
+            let count = (x1 - x0) as usize;
 
             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) };
@@ -135,13 +135,13 @@ impl PixelFormat for BGRXPixel {
             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),
+            upper_left.0.max(bottom_right.0).min(w as i32),
+            upper_left.1.max(bottom_right.1).min(h as i32),
         );
 
         // 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 {
+        if x0 >= x1 || y0 >= y1 {
             return;
         }
 
@@ -149,12 +149,12 @@ impl PixelFormat for BGRXPixel {
 
         if r == g && g == b {
             // If r == g == b, then we can use memset
-            if x0 != 0 || x1 != w as i32 - 1 {
+            if x0 != 0 || x1 != w as i32 {
                 // If it's not the entire row is filled, we can only do
                 // memset per row
-                for y in y0..=y1 {
+                for y in y0..y1 {
                     let start = (y * w as i32 + x0) as usize;
-                    let count = (x1 - x0 + 1) as usize;
+                    let count = (x1 - x0) as usize;
                     dst[(start * Self::PIXEL_SIZE)..((start + count) * Self::PIXEL_SIZE)]
                         .iter_mut()
                         .for_each(|e| *e = r);
@@ -162,19 +162,19 @@ impl PixelFormat for BGRXPixel {
             } 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]
+                    ..(y1 * w as i32) as usize * Self::PIXEL_SIZE]
                     .iter_mut()
                     .for_each(|e| *e = r);
             }
         } else {
-            let count = (x1 - x0 + 1) as usize;
+            let count = (x1 - x0) as usize;
             if count < 8 {
-                for y in y0..=y1 {
+                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) {
+                    for _ in 0..count {
                         *iter.next().unwrap() = b;
                         *iter.next().unwrap() = g;
                         *iter.next().unwrap() = r;
@@ -182,7 +182,7 @@ impl PixelFormat for BGRXPixel {
                     }
                 }
             } else {
-                for y in y0..=y1 {
+                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; 8];
                     let slice =
diff --git a/plotters-bitmap/src/bitmap_pixel/rgb.rs b/plotters-bitmap/src/bitmap_pixel/rgb.rs
index 06ab6d24..e8b88216 100644
--- a/plotters-bitmap/src/bitmap_pixel/rgb.rs
+++ b/plotters-bitmap/src/bitmap_pixel/rgb.rs
@@ -50,13 +50,13 @@ impl PixelFormat for RGBPixel {
             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),
+            upper_left.0.max(bottom_right.0).min(w as i32),
+            upper_left.1.max(bottom_right.1).min(h as i32),
         );
 
         // 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 {
+        if x0 >= x1 || y0 >= y1 {
             return;
         }
 
@@ -87,9 +87,9 @@ impl PixelFormat for RGBPixel {
         const N: u64 = 0xff00_ff00_ff00_ff00;
         const M: u64 = 0x00ff_00ff_00ff_00ff;
 
-        for y in y0..=y1 {
+        for y in y0..y1 {
             let start = (y * w as i32 + x0) as usize;
-            let count = (x1 - x0 + 1) as usize;
+            let count = (x1 - 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) };
@@ -150,13 +150,13 @@ impl PixelFormat for RGBPixel {
             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),
+            upper_left.0.max(bottom_right.0).min(w as i32),
+            upper_left.1.max(bottom_right.1).min(h as i32),
         );
 
         // 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 {
+        if x0 >= x1 || y0 >= y1 {
             return;
         }
 
@@ -164,12 +164,12 @@ impl PixelFormat for RGBPixel {
 
         if r == g && g == b {
             // If r == g == b, then we can use memset
-            if x0 != 0 || x1 != w as i32 - 1 {
+            if x0 != 0 || x1 != w as i32 {
                 // If it's not the entire row is filled, we can only do
                 // memset per row
-                for y in y0..=y1 {
+                for y in y0..y1 {
                     let start = (y * w as i32 + x0) as usize;
-                    let count = (x1 - x0 + 1) as usize;
+                    let count = (x1 - x0) as usize;
                     dst[(start * Self::PIXEL_SIZE)..((start + count) * Self::PIXEL_SIZE)]
                         .iter_mut()
                         .for_each(|e| *e = r);
@@ -177,26 +177,26 @@ impl PixelFormat for RGBPixel {
             } 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]
+                    ..(y1 * w as i32) as usize * Self::PIXEL_SIZE]
                     .iter_mut()
                     .for_each(|e| *e = r);
             }
         } else {
-            let count = (x1 - x0 + 1) as usize;
+            let count = (x1 - x0) as usize;
             if count < 8 {
-                for y in y0..=y1 {
+                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) {
+                    for _ in 0..count {
                         *iter.next().unwrap() = r;
                         *iter.next().unwrap() = g;
                         *iter.next().unwrap() = b;
                     }
                 }
             } else {
-                for y in y0..=y1 {
+                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 =
diff --git a/plotters/src/drawing/area.rs b/plotters/src/drawing/area.rs
index 071d75ee..5121f00b 100644
--- a/plotters/src/drawing/area.rs
+++ b/plotters/src/drawing/area.rs
@@ -289,7 +289,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 - 1, self.rect.y1 - 1),
+                (self.rect.x1, self.rect.y1),
                 &color.to_backend_color(),
                 true,
             )
@@ -549,7 +549,7 @@ mod drawing_area_tests {
                 assert_eq!(c, WHITE.to_rgba());
                 assert_eq!(f, true);
                 assert_eq!(u, (0, 0));
-                assert_eq!(d, (1023, 767));
+                assert_eq!(d, (1024, 768));
             });
 
             m.drop_check(|b| {
@@ -577,8 +577,8 @@ mod drawing_area_tests {
                         assert_eq!(
                             d,
                             (
-                                300 + 300 * row as i32 + 2.min(row + 1) as i32 - 1,
-                                300 + 300 * col as i32 - 1
+                                300 + 300 * row as i32 + 2.min(row + 1) as i32,
+                                300 + 300 * col as i32
                             )
                         );
                     });
@@ -606,14 +606,14 @@ mod drawing_area_tests {
                 assert_eq!(c, RED.to_rgba());
                 assert_eq!(f, true);
                 assert_eq!(u, (0, 0));
-                assert_eq!(d, (345 - 1, 768 - 1));
+                assert_eq!(d, (345, 768));
             });
 
             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 - 1, 768 - 1));
+                assert_eq!(d, (1024, 768));
             });
 
             m.drop_check(|b| {
@@ -634,14 +634,14 @@ mod drawing_area_tests {
                 assert_eq!(c, RED.to_rgba());
                 assert_eq!(f, true);
                 assert_eq!(u, (0, 0));
-                assert_eq!(d, (1024 - 1, 345 - 1));
+                assert_eq!(d, (1024, 345));
             });
 
             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 - 1, 768 - 1));
+                assert_eq!(d, (1024, 768));
             });
 
             m.drop_check(|b| {
@@ -678,10 +678,8 @@ 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) - 1,
-                                get_bp(768, nyb, row + 1) - 1,
-                            );
+                            let expected_d =
+                                (get_bp(1024, nxb, col + 1), get_bp(768, nyb, row + 1));
                             let expected_color =
                                 colors[(row * (nxb + 1) + col) as usize % colors.len()];
 
@@ -724,7 +722,7 @@ mod drawing_area_tests {
                 assert_eq!(f, true);
                 assert_eq!(u.0, 0);
                 assert!(u.1 > 0);
-                assert_eq!(d, (1024 - 1, 768 - 1));
+                assert_eq!(d, (1024, 768));
             });
             m.drop_check(|b| {
                 assert_eq!(b.num_draw_text_call, 1);
@@ -747,7 +745,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 - 1, 768 - 2 - 1));
+                assert_eq!(d, (1024 - 4, 768 - 2));
             });
 
             m.drop_check(|b| {
@@ -802,22 +800,22 @@ mod drawing_area_tests {
                     0 => {
                         assert_eq!(c, RED.to_rgba());
                         assert_eq!(u, (0, 0));
-                        assert_eq!(d, (300 - 1, 600 - 1));
+                        assert_eq!(d, (300, 600));
                     }
                     1 => {
                         assert_eq!(c, BLUE.to_rgba());
                         assert_eq!(u, (300, 0));
-                        assert_eq!(d, (1000 - 1, 600 - 1));
+                        assert_eq!(d, (1000, 600));
                     }
                     2 => {
                         assert_eq!(c, GREEN.to_rgba());
                         assert_eq!(u, (0, 600));
-                        assert_eq!(d, (300 - 1, 1200 - 1));
+                        assert_eq!(d, (300, 1200));
                     }
                     3 => {
                         assert_eq!(c, WHITE.to_rgba());
                         assert_eq!(u, (300, 600));
-                        assert_eq!(d, (1000 - 1, 1200 - 1));
+                        assert_eq!(d, (1000, 1200));
                     }
                     _ => panic!("Too many draw rect"),
                 }
@@ -845,7 +843,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 - 1, 700 - 1), d);
+                assert_eq!((300, 700), d);
             });
 
             m.drop_check(|b| {


Test patch:
diff --git a/plotters-bitmap/src/bitmap/test.rs b/plotters-bitmap/src/bitmap/test.rs
index 6796a95c..7bbe7a48 100644
--- a/plotters-bitmap/src/bitmap/test.rs
+++ b/plotters-bitmap/src/bitmap/test.rs
@@ -46,7 +46,7 @@ fn test_bitmap_backend_fill_half() {
         for y in 0..10 {
             assert_eq!(
                 buffer[(y * 10 + x) as usize * 3 + 0],
-                if x <= 5 { 255 } else { 0 }
+                if x < 5 { 255 } else { 0 }
             );
             assert_eq!(buffer[(y * 10 + x) as usize * 3 + 1], 0);
             assert_eq!(buffer[(y * 10 + x) as usize * 3 + 2], 0);
@@ -67,7 +67,7 @@ fn test_bitmap_backend_fill_half() {
         for y in 0..10 {
             assert_eq!(
                 buffer[(y * 10 + x) as usize * 3 + 0],
-                if y <= 5 { 255 } else { 0 }
+                if y < 5 { 255 } else { 0 }
             );
             assert_eq!(buffer[(y * 10 + x) as usize * 3 + 1], 0);
             assert_eq!(buffer[(y * 10 + x) as usize * 3 + 2], 0);
@@ -95,7 +95,7 @@ fn test_bitmap_backend_blend() {
 
     for x in 0..10 {
         for y in 0..10 {
-            let (r, g, b) = if x <= 5 {
+            let (r, g, b) = if x < 5 {
                 (205, 225, 245)
             } else {
                 (255, 255, 255)
@@ -157,6 +157,22 @@ fn test_draw_rect_out_of_range() {
     }
 }
 
+#[cfg(test)]
+#[test]
+fn test_draw_rect_exclude_bottom_right() {
+    use plotters::prelude::*;
+    let mut buffer = vec![0; 100 * 100 * 3];
+
+    {
+        let mut back = BitMapBackend::with_buffer(&mut buffer, (100, 100));
+
+        back.draw_rect((0, 0), (0, 0), &RED.to_rgba(), true)
+            .unwrap();
+    }
+
+    assert_eq!(&buffer[0..3], &[0, 0, 0]);
+}
+
 #[cfg(test)]
 #[test]
 fn test_draw_line_out_of_range() {
@@ -203,7 +219,7 @@ fn test_bitmap_blend_large() {
 
         for x in 0..1000 {
             for y in 0..1000 {
-                let expected_value = if x <= 100 && y <= 100 {
+                let expected_value = if x < 100 && y < 100 {
                     let (r, g, b) = fill_color.to_rgba().rgb();
                     (
                         if r > 0 { 139 } else { 12 },