Skip to content

Commit

Permalink
Add stub bmp support
Browse files Browse the repository at this point in the history
  • Loading branch information
lilith committed Aug 19, 2024
1 parent dab6411 commit bafacb7
Show file tree
Hide file tree
Showing 9 changed files with 350 additions and 12 deletions.
146 changes: 146 additions & 0 deletions imageflow_core/src/codecs/bmp.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
use std;
use crate::for_other_imageflow_crates::preludes::external_without_std::*;
use crate::ffi;
use crate::{Context, CError, Result, JsonResponse};
use crate::ffi::BitmapBgra;
use imageflow_types::collections::AddRemoveSet;
use crate::io::{IoProxy, ZuneIoProxyReader};
use uuid::Uuid;
use imageflow_types::{DecoderCommand, ImageInfo, IoDirection, PixelLayout};
use super::*;
use std::any::Any;
use crate::gif::Frame;
use std::rc::Rc;
use crate::io::IoProxyProxy;
use crate::io::IoProxyRef;
use crate::graphics::bitmaps::{BitmapKey, ColorSpace, BitmapCompositing};

use zune_bmp;

pub struct BmpDecoder{
decoder: zune_bmp::BmpDecoder<zune_bmp::zune_core::bytestream::ZCursor<ZuneIoProxyReader>>,
}

impl BmpDecoder {
pub fn create(c: &Context, io: IoProxy, io_id: i32) -> Result<BmpDecoder> {
let reader = zune_bmp::zune_core::bytestream::ZCursor::new(ZuneIoProxyReader::wrap_or_buffer(io).map_err(|e| FlowError::from_decoder(e).at(here!()))?);

let mut decoder = zune_bmp::BmpDecoder::new(reader);
decoder.decode_headers().map_err(|e| FlowError::from(e).at(here!()))?;

let (w,h) = decoder.dimensions().expect("Dimensions not found");

Ok(BmpDecoder{
decoder
})
}

fn has_alpha(&self) -> bool {
let colorspace = self.decoder.colorspace().expect("Colorspace not found");
colorspace.has_alpha()
}

fn decodes_into(&self) -> imageflow_types::PixelFormat {
let colorspace = self.decoder.colorspace().expect("Colorspace not found");
if colorspace.has_alpha() {
imageflow_types::PixelFormat::Bgra32
} else {
imageflow_types::PixelFormat::Bgr32
}
}
}
impl Decoder for BmpDecoder{
fn initialize(&mut self, c: &Context) -> Result<()> {
Ok(())
}

fn get_unscaled_image_info(&mut self, c: &Context) -> Result<ImageInfo> {
let (w,h) = self.decoder.dimensions().expect("Dimensions not found");
let colorspace = self.decoder.colorspace().expect("Colorspace not found");
let decodes_into = self.decodes_into();
Ok(ImageInfo{
preferred_mime_type: "image/bmp".to_string(),
preferred_extension: "bmp".to_string(),
image_width: w as i32,
image_height: h as i32,
frame_decodes_into: decodes_into
})
}

fn get_scaled_image_info(&mut self, c: &Context) -> Result<ImageInfo> {
self.get_unscaled_image_info(c)
}

fn get_exif_rotation_flag(&mut self, c: &Context) -> Result<Option<i32>> {
Ok(None)
}

fn tell_decoder(&mut self, c: &Context, tell: DecoderCommand) -> Result<()> {
Ok(())
}

fn read_frame(&mut self, c: &Context) -> Result<BitmapKey> {
let (w,h) = self.decoder.dimensions().expect("Dimensions not found");
let colorspace = self.decoder.colorspace().expect("Colorspace not found");
let fmt = self.decodes_into();

let mut bitmaps = c.borrow_bitmaps_mut()
.map_err(|e| e.at(here!()))?;

let canvas_key = bitmaps.create_bitmap_u8(
w as u32,h as u32,fmt.pixel_layout(),
false, fmt.alpha_meaningful(),
ColorSpace::StandardRGB,
BitmapCompositing::ReplaceSelf)
.map_err(|e| e.at(here!()))?;


let mut canvas = unsafe { bitmaps.try_borrow_mut(canvas_key)
.map_err(|e| e.at(here!()))?
.get_window_u8().unwrap()
.to_bitmap_bgra().map_err(|e| e.at(here!()))?
};

let bytes = self.decoder.decode().map_err(|e| FlowError::from(e).at(here!()))?;
let input_stride = colorspace.num_components() * w;
let output_stride = canvas.stride() as usize;
let input_alpha = colorspace.has_alpha();

unsafe {
let mut canvas = canvas.pixels_slice_mut().unwrap();
for y in 0..h {
let input_row = &bytes[y * input_stride..(y + 1) * input_stride];
let output_row = &mut canvas[y * output_stride..(y + 1) * output_stride];

if input_alpha {
for (input_chunk, output_chunk) in input_row.chunks_exact(4).zip(output_row.chunks_exact_mut(4)) {

// SAFETY: The access is guaranteed to be within bounds because the chunks are of size 4.
*output_chunk.get_unchecked_mut(0) = *input_chunk.get_unchecked(2);
*output_chunk.get_unchecked_mut(1) = *input_chunk.get_unchecked(1);
*output_chunk.get_unchecked_mut(2) = *input_chunk.get_unchecked(0);
*output_chunk.get_unchecked_mut(3) = *input_chunk.get_unchecked(3);
}
} else {
for (input_chunk, output_chunk) in input_row.chunks_exact(3).zip(output_row.chunks_exact_mut(4)) {
// SAFETY: The access is guaranteed to be within bounds because the chunks are of size 3.
*output_chunk.get_unchecked_mut(0) = *input_chunk.get_unchecked(0);
*output_chunk.get_unchecked_mut(1) = *input_chunk.get_unchecked(1);
*output_chunk.get_unchecked_mut(2) = *input_chunk.get_unchecked(2);
*output_chunk.get_unchecked_mut(3) = 255;
}
}
}
}

Ok(canvas_key)
}

fn has_more_frames(&mut self) -> Result<bool> {
Ok(false)
}

fn as_any(&self) -> &dyn Any {
self as &dyn Any
}
}
32 changes: 23 additions & 9 deletions imageflow_core/src/codecs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ mod mozjpeg_decoder_helpers;
mod jpeg_decoder;
mod webp;
mod color_transform_cache;
mod bmp;

use crate::io::IoProxyRef;
use crate::codecs::color_transform_cache::ColorTransformCache;
use crate::codecs::NamedEncoders::LibPngRsEncoder;
Expand Down Expand Up @@ -64,15 +66,6 @@ enum CodecKind{
Decoder(Box<dyn Decoder>)
}

#[derive(PartialEq, Copy, Clone)]
pub enum NamedDecoders{
MozJpegRsDecoder,
WICJpegDecoder,
ImageRsJpegDecoder,
LibPngRsDecoder,
GifRsDecoder,
WebPDecoder,
}
impl NamedDecoders{
pub fn works_for_magic_bytes(&self, bytes: &[u8]) -> bool{
match self{
Expand All @@ -87,6 +80,9 @@ impl NamedDecoders{
},
NamedDecoders::WebPDecoder => {
bytes.starts_with(b"RIFF") && bytes[8..12].starts_with(b"WEBP")
},
NamedDecoders::BmpRsDecoder => {
bytes.starts_with(b"BM")
}
}
}
Expand All @@ -98,13 +94,27 @@ impl NamedDecoders{
NamedDecoders::GifRsDecoder => Ok(Box::new(gif::GifDecoder::create(c, io, io_id)?)),
NamedDecoders::ImageRsJpegDecoder => Ok(Box::new(jpeg_decoder::JpegDecoder::create(c, io, io_id)?)),
NamedDecoders::WebPDecoder => Ok(Box::new(webp::WebPDecoder::create(c, io, io_id)?)),
NamedDecoders::BmpRsDecoder => Ok(Box::new(bmp::BmpDecoder::create(c, io, io_id)?)),
NamedDecoders::WICJpegDecoder => {
panic!("WIC Jpeg Decoder not implemented"); //TODO, use actual error for this
}
}
}

}


#[derive(PartialEq, Copy, Clone)]
pub enum NamedDecoders{
MozJpegRsDecoder,
WICJpegDecoder,
ImageRsJpegDecoder,
LibPngRsDecoder,
GifRsDecoder,
BmpRsDecoder,
WebPDecoder,
}

#[derive(PartialEq, Copy, Clone)]
pub enum NamedEncoders{
GifEncoder,
Expand All @@ -125,6 +135,7 @@ impl Default for EnabledCodecs {
&[NamedDecoders::MozJpegRsDecoder,
NamedDecoders::LibPngRsDecoder,
NamedDecoders::GifRsDecoder,
NamedDecoders::BmpRsDecoder,
NamedDecoders::WebPDecoder]),
encoders: smallvec::SmallVec::from_slice(
&[NamedEncoders::GifEncoder,
Expand All @@ -146,6 +157,9 @@ impl EnabledCodecs{
pub fn disable_decoder(&mut self, decoder: NamedDecoders){
self.decoders.retain( |item| item != &decoder);
}
pub fn enable_decoder(&mut self, decoder: NamedDecoders){
self.decoders.push(decoder);
}
pub fn create_decoder_for_magic_bytes(&self, bytes: &[u8], c: &Context, io: IoProxy, io_id: i32) -> Result<Box<dyn Decoder>>{
for &decoder in self.decoders.iter(){
if decoder.works_for_magic_bytes(bytes){
Expand Down
3 changes: 3 additions & 0 deletions imageflow_core/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,9 @@ impl Context {
if let Some(encode) = s.max_encode_size{
self.security.max_encode_size = Some(encode);
}
if let Some(allowed) = s.allow_decoding{
self.enabled_codecs = allowed;
}
}

/// For executing an operation graph (assumes you have already configured the context with IO sources/destinations as needed)
Expand Down
15 changes: 15 additions & 0 deletions imageflow_core/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use num::FromPrimitive;
use crate::ffi;
use std::ffi::CStr;
use std::ptr;
use zune_bmp::BmpDecoderErrors;
use imageflow_riapi::sizing::LayoutError;
use crate::flow::definitions::FrameEstimate;

Expand Down Expand Up @@ -314,6 +315,20 @@ impl From<::gif::DecodingError> for FlowError{
}
}
}
impl From<::zune_bmp::BmpDecoderErrors> for FlowError{
fn from(f: zune_bmp::BmpDecoderErrors) -> Self {
match f {
BmpDecoderErrors::InvalidMagicBytes => FlowError::without_location(ErrorKind::ImageDecodingError, "Invalid BMP magic bytes".to_owned()),
BmpDecoderErrors::TooSmallBuffer(a, b) => FlowError::without_location(ErrorKind::ImageDecodingError, format!("BMP buffer too small: {:?} {:?}", a, b)),
BmpDecoderErrors::GenericStatic(s) => FlowError::without_location(ErrorKind::ImageDecodingError, format!("BMP decoding error: {:?}", s)),
BmpDecoderErrors::Generic(s) => FlowError::without_location(ErrorKind::ImageDecodingError, format!("BMP decoding error: {:?}", s)),
BmpDecoderErrors::TooLargeDimensions(s, a, b) => FlowError::without_location(ErrorKind::ImageDecodingError, format!("BMP dimensions too large: {:?} {:?} {:?}", s, a, b)),
BmpDecoderErrors::OverFlowOccurred => FlowError::without_location(ErrorKind::ImageDecodingError, "BMP overflow error".to_owned()),
BmpDecoderErrors::IoErrors(e) => FlowError::without_location(ErrorKind::ImageDecodingError, format!("BMP decoding error: {:?}", e)),
e => FlowError::without_location(ErrorKind::ImageDecodingError, format!("BMP decoding error: {:?}", e))
}
}
}

impl From<::gif::EncodingError> for FlowError{
fn from(f: ::gif::EncodingError) -> Self {
Expand Down
55 changes: 53 additions & 2 deletions imageflow_core/src/io.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std;
use std::ops::Range;
use crate::for_other_imageflow_crates::preludes::external_without_std::*;
use crate::{ffi, FlowError};
use crate::{Context, Result, JsonResponse, ErrorKind};
Expand Down Expand Up @@ -53,6 +54,15 @@ impl IoBackend{
_ => None
}
}

#[inline(always)]
pub fn get_read_bytes(&self) -> Option<&[u8]>{
match self{
IoBackend::ReadSlice(w) => Some(w.get_ref()),
IoBackend::ReadVec(w) => Some(w.get_ref()),
_ => None
}
}
pub fn get_seek(&mut self) -> Option<&mut dyn Seek>{
match self{
IoBackend::ReadSlice(w) => Some(w),
Expand All @@ -67,8 +77,7 @@ impl IoBackend{
/// Implements Read/Write
pub struct IoProxy{
io_id: i32,
path: Option<PathBuf>,
backend: IoBackend,
path: Option<PathBuf>, backend: IoBackend,
}


Expand All @@ -79,6 +88,48 @@ impl io::Read for IoProxy{
self.backend.get_read().expect("cannot read from writer").read(buf)
}
}

pub struct ZuneIoProxyReader{
io: Option<IoProxy>,
buffer: Option<Vec<u8>>
}

impl ZuneIoProxyReader{
pub fn wrap_or_buffer(mut io: IoProxy) -> io::Result<Self>{
if io.backend.get_read_bytes().is_none(){
// read entire file into memory
let mut buffer = Vec::new();
let bytes_read = io.backend.get_read().expect("cannot read from writer")
.read_to_end(&mut buffer)?;
// Drop io
Ok(ZuneIoProxyReader{
io: None,
buffer: Some(buffer)
})
} else {
Ok(ZuneIoProxyReader{
io: Some(io),
buffer: None
})
}
}
#[inline(always)]
fn get_bytes(&self) -> &[u8]{
if let Some(ref io) = self.io{
io.backend.get_read_bytes().expect("cannot read from writer")
} else {
self.buffer.as_ref().expect("no buffer")
}
}
}
//impl zune_bmp::zune_core::bytestream::ZByteReaderTrait for ZuneIoProxyReader{
impl AsRef<[u8]> for ZuneIoProxyReader{
fn as_ref(&self) -> &[u8] {
self.get_bytes()
}
}


impl io::Seek for IoProxy{
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
self.backend.get_seek().expect("cannot read from writer").seek(pos)
Expand Down
19 changes: 19 additions & 0 deletions imageflow_core/tests/test_decoders.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use zune_bmp::zune_core::bytestream::ZCursor;

// #[test]
// pub fn test_rgba32_bmp_channel_order(){
// let bytes32 = imageflow_http_helpers::fetch_bytes("https://raw.githubusercontent.com/etemesi254/zune-image/dev/test-images/bmp/rgba32-1.bmp").unwrap();
// let bytes24 = imageflow_http_helpers::fetch_bytes("https://raw.githubusercontent.com/etemesi254/zune-image/dev/test-images/bmp/rgb24.bmp").unwrap();
//
// let mut decoder32 = zune_bmp::BmpDecoder::new(ZCursor::new(&bytes32));
// let decoded_bytes = decoder32.decode().unwrap();
// // We know the first pixel is more red than blue, so RGB order means byte 0 is greater than byte 2
// assert!(decoded_bytes[0] > decoded_bytes[2]);
// // The above passes
//
// let mut decoder24 = zune_bmp::BmpDecoder::new(ZCursor::new(&bytes24));
// let decoded_bytes = decoder24.decode().unwrap();
// // We know the first pixel is more red than blue, so RGB order means byte 0 is greater than byte 2
// assert!(decoded_bytes[0] > decoded_bytes[2]);
// // But this one fails.
// }
Loading

0 comments on commit bafacb7

Please sign in to comment.