Browse Source

Minor features and bugfixes.

Kestrel 1 month ago
parent
commit
1a1afd5fbf
4 changed files with 83 additions and 17 deletions
  1. 18 1
      src/lib.rs
  2. 48 6
      src/ops.rs
  3. 12 6
      src/ops/helpers.rs
  4. 5 4
      tests/common/mod.rs

+ 18 - 1
src/lib.rs

@@ -8,7 +8,7 @@ pub mod colour;
 pub mod formats;
 
 pub mod prelude {
-    pub use super::ops::Paintable;
+    pub use super::ops::{AlphaPaintable, Readable, RgbaPaintable, Writable};
     pub use super::{BitmapAccess, BitmapMutAccess};
 }
 
@@ -122,6 +122,9 @@ impl<Format: PixelFormat> std::fmt::Debug for Bitmap<Format> {
     }
 }
 
+pub type RgbaBitmap = Bitmap<formats::Rgba32>;
+pub type Alphamap = Bitmap<formats::A8>;
+
 /// Borrowed bitmap with static pixel format information.
 #[derive(Clone)]
 pub struct BitmapRef<'l, Format: PixelFormat> {
@@ -129,6 +132,20 @@ pub struct BitmapRef<'l, Format: PixelFormat> {
     _ghost: std::marker::PhantomData<Format>,
 }
 
+impl<'l, Format: PixelFormat> BitmapRef<'l, Format> {
+    pub fn new(data: &'l [u8], width: usize, height: usize, stride: usize) -> Self {
+        Self {
+            data: data::BorrowedBitmapData {
+                data,
+                width,
+                height,
+                stride,
+            },
+            _ghost: Default::default(),
+        }
+    }
+}
+
 impl<'l, Format: PixelFormat> BitmapAccess<Format> for BitmapRef<'l, Format> {
     fn data(&self) -> &[u8] {
         &self.data.data

+ 48 - 6
src/ops.rs

@@ -16,8 +16,45 @@ pub trait Writable<PF: PixelFormat>: Readable<PF> {
     fn set_pixel(&mut self, x: usize, y: usize, to: Self::ReadResult);
 }
 
+pub trait AlphaPaintable: BitmapMutAccess<A8> + Writable<A8, ReadResult = u8> {
+    fn fill(&mut self, with: u8) {
+        self.data_mut().fill(with);
+    }
+
+    fn copy_from(
+        &mut self,
+        amap: &impl BitmapAccess<A8>,
+        amap_region: &math::PixelBox,
+        dst: &math::PixelPoint,
+    ) {
+        let Some((write_region, read_region)) = helpers::clip_to(
+            self.size(),
+            *dst,
+            amap.size(),
+            amap_region.min,
+            amap_region.size(),
+        ) else {
+            return;
+        };
+
+        let row_width = write_region.width() as usize * A8::PIXEL_WIDTH;
+        for y in 0..write_region.height() {
+            let write_offset = ((y + write_region.min.y) * self.row_stride() as i32) as usize;
+            let read_offset = ((y + read_region.min.y) * amap.row_stride() as i32) as usize;
+
+            let write_offset = write_offset + write_region.min.x as usize;
+            let read_offset = read_offset + read_region.min.x as usize;
+
+            self.data_mut()[write_offset..(write_offset + row_width)]
+                .copy_from_slice(&amap.data()[read_offset..(read_offset + row_width)]);
+        }
+    }
+}
+
+impl<T: BitmapMutAccess<A8>> AlphaPaintable for T {}
+
 /// RGBA32-specific painting struct.
-pub trait Paintable:
+pub trait RgbaPaintable:
     BitmapAccess<Rgba32> + BitmapMutAccess<Rgba32> + Writable<Rgba32, ReadResult = Colour>
 {
     /// Fill the entire image with a given colour.
@@ -54,9 +91,13 @@ pub trait Paintable:
         col: Colour,
         mode: BlendMode,
     ) {
-        let Some((write_box, read_box)) =
-            helpers::clip_to(self.size(), *dst, amap.size(), amap_region.min, amap_region.size())
-        else {
+        let Some((write_box, read_box)) = helpers::clip_to(
+            self.size(),
+            *dst,
+            amap.size(),
+            amap_region.min,
+            amap_region.size(),
+        ) else {
             return;
         };
 
@@ -67,7 +108,8 @@ pub trait Paintable:
                 let wx = (write_box.min.x + x) as usize;
                 let wy = (write_box.min.y + y) as usize;
                 let source = col.with_a(amap.get_pixel(rx, ry));
-                self.set_pixel(wx, wy, mode.blend(&self.get_pixel(wx, wy), &source));
+                let blended = mode.blend(&source, &self.get_pixel(wx, wy));
+                self.set_pixel(wx, wy, blended);
             }
         }
     }
@@ -118,4 +160,4 @@ pub trait Paintable:
     }
 }
 
-impl<T: BitmapMutAccess<Rgba32>> Paintable for T {}
+impl<T: BitmapMutAccess<Rgba32>> RgbaPaintable for T {}

+ 12 - 6
src/ops/helpers.rs

@@ -9,11 +9,19 @@ pub fn clip_to(
 ) -> Option<(math::PixelBox, math::PixelBox)> {
     let left = (-pt1.x).max(-pt2.x).max(0);
     let top = (-pt1.y).max(-pt2.y).max(0);
-    let right = ((sz.width + pt1.x) - img1_size.width as i32).max((sz.width + pt2.x) - img2_size.width as i32).max(0);
-    let bottom = ((sz.height + pt1.y) - img2_size.height as i32).max((sz.height + pt2.y) - img2_size.height as i32).max(0);
+    let right = ((sz.width + pt1.x) - img1_size.width as i32)
+        .max((sz.width + pt2.x) - img2_size.width as i32)
+        .max(0);
+    let bottom = ((sz.height + pt1.y) - img1_size.height as i32)
+        .max((sz.height + pt2.y) - img2_size.height as i32)
+        .max(0);
 
     let adj = math::PixelSideOffsets {
-        left, right, top, bottom, ..Default::default()
+        left,
+        right,
+        top,
+        bottom,
+        ..Default::default()
     };
 
     Some((
@@ -23,6 +31,4 @@ pub fn clip_to(
 }
 
 #[test]
-fn clip_test() {
-    
-}
+fn clip_test() {}

+ 5 - 4
tests/common/mod.rs

@@ -1,6 +1,4 @@
-use kahlo::{
-    Bitmap, BitmapAccess
-};
+use kahlo::{Bitmap, BitmapAccess};
 
 pub fn load_test_resource(name: &'static str) -> Bitmap<kahlo::formats::Rgba32> {
     let mut path = std::path::PathBuf::new();
@@ -17,7 +15,10 @@ pub fn load_test_resource(name: &'static str) -> Bitmap<kahlo::formats::Rgba32>
     Bitmap::new_from_vec(img.into_vec(), w as usize, h as usize, w as usize * 4)
 }
 
-pub fn save_test_result_rgba32(name: &'static str, bitmap: &impl BitmapAccess<kahlo::formats::Rgba32>) {
+pub fn save_test_result_rgba32(
+    name: &'static str,
+    bitmap: &impl BitmapAccess<kahlo::formats::Rgba32>,
+) {
     let mut path = std::path::PathBuf::new();
     path.push(env!("CARGO_TARGET_TMPDIR"));
     path.push(name);