user17014151
user17014151

Reputation: 53

How do I convert a custom image implementation of a bitmap slice to PNG?

I work at a project written in Rust that basically mocks a printer. I get printer input, and try to convert it to human readable data (strings, images). So, for reproducing the QR code, I am converting the printer input to BitMap Slices (I created a struct that implements GenericImageView).

BitmapImageSlice {
      height,
      width,
      buffer: data
    }
impl GenericImageView for BitmapImageSlice {
  type Pixel = Rgb<u8>;
  type InnerImageView = BitmapImageSlice;

  fn dimensions(&self) -> (u32, u32) {
    (self.width as u32, self.height as u32)
  }

  fn bounds(&self) -> (u32, u32, u32, u32) {
    ( 0, 0, self.width as u32, self.height as u32)
  }

  fn get_pixel(&self, x: u32, y: u32) -> Self::Pixel {
    let byte = self.buffer[x as usize];
    let bit_position = 7-y;
    let bit = 1 << bit_position;
    if (byte & bit as u8) > 0{
      Rgb([0, 0, 0])
    }
    else {
      Rgb([255, 255, 255])
    }
  }


  fn inner(&self) -> &Self::InnerImageView {
    self
  }
}

My question is, how can I convert BitMapImageSlice values to an image encoded in PNG?

Upvotes: 2

Views: 1019

Answers (1)

E_net4
E_net4

Reputation: 29983

There is a key problem when trying to make your own implementation of a generic image through the image crate: there is no support for a 1 bit per pixel color type, and even with an adaptation layer, it will not be compatible with the APIs available.

  • The method write_to only exists for DynamicImage, which is defined as an enum of built-in implementations. It cannot be extended to consider orphan implementations.
  • save_buffer_with_format (as proposed here) expects a buffer of pixel samples in accordance to a supported color type.
  • Even the bare image encoder signature declared in the trait ImageEncoder for writing encoded content also expects a buffer of pixels following a supported color type.

As such, it is more straightforward here to use a supported image type. Convert the bitmap to the L8 color type and use the existing functions from there.

impl BitmapImageSlice {
    
    pub fn to_image(&self) -> ImageBuffer<Luma<u8>, Vec<u8>> {
        // NOTE: this depends on the BitmapImageSlice data layout,
        // adjust vector construction accordingly
        let data: Vec<u8> = self.buffer.iter()
            .flat_map(|b| [
                b >> 7,
                (b >> 6) & 1,
                (b >> 5) & 1,
                (b >> 4) & 1,
                (b >> 3) & 1,
                (b >> 2) & 1,
                (b >> 1) & 1,
                b & 1,
            ])
            .map(|p| p * 0xFF)
            .collect();
        
        ImageBuffer::from_vec(self.width, self.height, data).unwrap()
    }
}

fn save(bitmap: &BitmapImageSlice) -> image::error::ImageResult<()> {
    let img = bitmap.to_image();
    image::save_buffer_with_format(
        "out.png",
        img.as_raw(),
        bitmap.width,
        bitmap.height,
        ColorType::L8,
        ImageFormat::Png,
    )?;
    Ok(())
}

Playground

Upvotes: 3

Related Questions