type Error = Box<dyn std::error::Error>;
use crate::color::RGBA;
use crate::error::ImgError;
use crate::error::ImgErrorKind;
use crate::metadata::DataMap;
use crate::util::ImageFormat;
use crate::util::format_check;
use crate::warning::ImgWarnings;
use bin_rs::reader::*;
use std::collections::HashMap;
#[cfg(not(target_family = "wasm"))]
use std::io::BufRead;
#[cfg(not(target_family = "wasm"))]
use std::io::BufReader;
use std::io::Seek;
use std::io::SeekFrom;
use std::io::Write;
#[cfg(not(target_family = "wasm"))]
use std::path::Path;
#[derive(Debug)]
pub enum DrawNextOptions {
Continue,
NextImage,
ClearNext,
WaitTime(usize),
None,
}
pub type Response = Result<Option<CallbackResponse>, Error>;
pub trait DrawCallback: Sync + Send {
fn init(
&mut self,
width: usize,
height: usize,
option: Option<InitOptions>,
) -> Result<Option<CallbackResponse>, Error>;
fn draw(
&mut self,
start_x: usize,
start_y: usize,
width: usize,
height: usize,
data: &[u8],
option: Option<DrawOptions>,
) -> Result<Option<CallbackResponse>, Error>;
fn terminate(
&mut self,
_term: Option<TerminateOptions>,
) -> Result<Option<CallbackResponse>, Error>;
fn next(&mut self, _next: Option<NextOptions>) -> Result<Option<CallbackResponse>, Error>;
fn verbose(
&mut self,
_verbose: &str,
_: Option<VerboseOptions>,
) -> Result<Option<CallbackResponse>, Error>;
fn set_metadata(
&mut self,
key: &str,
value: DataMap,
) -> Result<Option<CallbackResponse>, Error>;
}
pub trait PickCallback: Sync + Send {
fn encode_start(
&mut self,
option: Option<EncoderOptions>,
) -> Result<Option<ImageProfiles>, Error>;
fn encode_pick(
&mut self,
start_x: usize,
start_y: usize,
width: usize,
height: usize,
option: Option<PickOptions>,
) -> Result<Option<Vec<u8>>, Error>;
fn encode_end(&mut self, _: Option<EndOptions>) -> Result<(), Error>;
fn metadata(&mut self) -> Result<Option<HashMap<String, DataMap>>, Error>;
}
#[derive(Debug)]
pub struct EncoderOptions {}
#[derive(Debug)]
pub struct PickOptions {}
#[derive(Debug)]
pub struct EndOptions {}
#[allow(unused)]
#[derive(Debug)]
pub struct InitOptions {
pub loop_count: u32,
pub background: Option<RGBA>,
pub animation: bool,
}
impl InitOptions {
pub fn new() -> Option<Self> {
Some(Self {
loop_count: 1,
background: None,
animation: false,
})
}
}
#[derive(Debug)]
pub struct DrawOptions {}
#[derive(Debug)]
pub struct TerminateOptions {}
#[derive(Debug)]
pub struct VerboseOptions {}
#[derive(Debug)]
pub enum NextOption {
Continue,
Next,
Dispose,
ClearAbort,
Terminate,
}
#[derive(Debug)]
pub enum NextDispose {
None,
Override,
Background,
Previous,
}
#[derive(Debug)]
pub enum NextBlend {
Source,
Override,
}
#[allow(unused)]
#[derive(Debug)]
pub struct ImageRect {
pub start_x: i32,
pub start_y: i32,
pub width: usize,
pub height: usize,
}
#[allow(unused)]
#[derive(Debug)]
pub struct NextOptions {
pub flag: NextOption,
pub await_time: u64,
pub image_rect: Option<ImageRect>,
pub dispose_option: Option<NextDispose>,
pub blend: Option<NextBlend>,
}
impl NextOptions {
pub fn new() -> Self {
NextOptions {
flag: NextOption::Continue,
await_time: 0,
image_rect: None,
dispose_option: None,
blend: None,
}
}
pub fn wait(ms_time: u64) -> Self {
NextOptions {
flag: NextOption::Continue,
await_time: ms_time,
image_rect: None,
dispose_option: None,
blend: None,
}
}
}
#[derive(std::cmp::PartialEq, Debug)]
pub enum ResponseCommand {
Abort,
Continue,
}
#[derive(Debug)]
pub struct CallbackResponse {
pub response: ResponseCommand,
}
impl CallbackResponse {
pub fn abort() -> Self {
Self {
response: ResponseCommand::Abort,
}
}
pub fn cont() -> Self {
Self {
response: ResponseCommand::Continue,
}
}
}
#[derive(Debug)]
pub struct ImageProfiles {
pub width: usize,
pub height: usize,
pub background: Option<RGBA>,
pub metadata: Option<HashMap<String, DataMap>>,
}
pub(crate) const ENCODE_ANIMATION_FRAMES_KEY: &str = "wml2.animation.frames";
pub(crate) const ENCODE_ANIMATION_LOOP_COUNT_KEY: &str = "wml2.animation.loop_count";
pub(crate) fn encode_animation_frame_key(index: usize, field: &str) -> String {
format!("wml2.animation.frame.{index}.{field}")
}
fn encode_animation_dispose(dispose: &Option<NextDispose>) -> u64 {
match dispose {
Some(NextDispose::Background) => 1,
Some(NextDispose::Previous) => 2,
_ => 0,
}
}
fn encode_animation_blend(blend: &Option<NextBlend>) -> u64 {
match blend {
Some(NextBlend::Source) => 1,
_ => 0,
}
}
fn append_animation_metadata(
metadata: &mut HashMap<String, DataMap>,
animation: &[AnimationLayer],
loop_count: u32,
) {
metadata.insert(
ENCODE_ANIMATION_FRAMES_KEY.to_string(),
DataMap::UInt(animation.len() as u64),
);
metadata.insert(
ENCODE_ANIMATION_LOOP_COUNT_KEY.to_string(),
DataMap::UInt(loop_count as u64),
);
for (index, layer) in animation.iter().enumerate() {
metadata.insert(
encode_animation_frame_key(index, "width"),
DataMap::UInt(layer.width as u64),
);
metadata.insert(
encode_animation_frame_key(index, "height"),
DataMap::UInt(layer.height as u64),
);
metadata.insert(
encode_animation_frame_key(index, "start_x"),
DataMap::SInt(layer.start_x as i64),
);
metadata.insert(
encode_animation_frame_key(index, "start_y"),
DataMap::SInt(layer.start_y as i64),
);
metadata.insert(
encode_animation_frame_key(index, "delay_ms"),
DataMap::UInt(layer.control.await_time),
);
metadata.insert(
encode_animation_frame_key(index, "dispose"),
DataMap::UInt(encode_animation_dispose(&layer.control.dispose_option)),
);
metadata.insert(
encode_animation_frame_key(index, "blend"),
DataMap::UInt(encode_animation_blend(&layer.control.blend)),
);
metadata.insert(
encode_animation_frame_key(index, "buffer"),
DataMap::Raw(layer.buffer.clone()),
);
}
}
pub struct AnimationLayer {
pub width: usize,
pub height: usize,
pub start_x: i32,
pub start_y: i32,
pub buffer: Vec<u8>,
pub control: NextOptions,
}
#[allow(unused)]
pub struct ImageBuffer {
pub width: usize,
pub height: usize,
pub background_color: Option<RGBA>,
pub buffer: Option<Vec<u8>>,
pub animation: Option<Vec<AnimationLayer>>,
pub current: Option<usize>,
pub loop_count: Option<u32>,
pub first_wait_time: Option<u64>,
fnverbose: fn(&str) -> Result<Option<CallbackResponse>, Error>,
pub metadata: Option<HashMap<String, DataMap>>,
}
fn default_verbose(_: &str) -> Result<Option<CallbackResponse>, Error> {
Ok(None)
}
fn checked_rgba_len(width: usize, height: usize, context: &str) -> Result<usize, Error> {
width
.checked_mul(height)
.and_then(|pixels| pixels.checked_mul(4))
.ok_or_else(|| {
Box::new(ImgError::new_const(
ImgErrorKind::InvalidParameter,
format!("{context} buffer size overflow"),
)) as Error
})
}
impl ImageBuffer {
pub fn new() -> Self {
Self {
width: 0,
height: 0,
background_color: None,
buffer: None,
animation: None,
current: None,
loop_count: None,
first_wait_time: None,
fnverbose: default_verbose,
metadata: None,
}
}
pub fn from_buffer(width: usize, height: usize, buf: Vec<u8>) -> Self {
Self {
width,
height,
background_color: None,
buffer: Some(buf),
animation: None,
current: None,
loop_count: None,
first_wait_time: None,
fnverbose: default_verbose,
metadata: None,
}
}
pub fn set_animation(&mut self, flag: bool) {
if flag {
self.animation = Some(Vec::new())
} else {
self.animation = None
}
}
pub fn set_verbose(&mut self, verbose: fn(&str) -> Result<Option<CallbackResponse>, Error>) {
self.fnverbose = verbose;
}
}
impl DrawCallback for ImageBuffer {
fn init(
&mut self,
width: usize,
height: usize,
option: Option<InitOptions>,
) -> Result<Option<CallbackResponse>, Error> {
let buffersize = checked_rgba_len(width, height, "image")?;
self.width = width;
self.height = height;
if let Some(option) = option {
self.background_color = option.background;
if option.animation {
self.set_animation(true);
}
self.loop_count = Some(option.loop_count);
}
if let Some(background) = &self.background_color {
self.buffer = Some(
(0..buffersize)
.map(|i| match i % 4 {
0 => background.red,
1 => background.green,
2 => background.blue,
_ => background.alpha,
})
.collect(),
);
} else {
self.buffer = Some((0..buffersize).map(|_| 0).collect());
}
Ok(None)
}
fn draw(
&mut self,
start_x: usize,
start_y: usize,
width: usize,
height: usize,
data: &[u8],
_: Option<DrawOptions>,
) -> Result<Option<CallbackResponse>, Error> {
if self.buffer.is_none() {
return Err(Box::new(ImgError::new_const(
ImgErrorKind::NotInitializedImageBuffer,
"in draw".to_string(),
)));
}
let buffer;
let (w, h, raws);
if self.current.is_none() {
if start_x >= self.width || start_y >= self.height {
return Ok(None);
}
let requested_end_x = start_x.checked_add(width).ok_or_else(|| {
Box::new(ImgError::new_const(
ImgErrorKind::OutboundIndex,
"draw rectangle x overflow".to_string(),
)) as Error
})?;
let requested_end_y = start_y.checked_add(height).ok_or_else(|| {
Box::new(ImgError::new_const(
ImgErrorKind::OutboundIndex,
"draw rectangle y overflow".to_string(),
)) as Error
})?;
w = self.width.min(requested_end_x) - start_x;
h = self.height.min(requested_end_y) - start_y;
raws = self.width;
buffer = self.buffer.as_deref_mut().ok_or_else(|| {
Box::new(ImgError::new_const(
ImgErrorKind::NotInitializedImageBuffer,
"buffer is not initialized".to_string(),
)) as Error
})?;
} else if let Some(animation) = &mut self.animation {
let current = self.current.ok_or_else(|| {
Box::new(ImgError::new_const(
ImgErrorKind::IllegalData,
"animation frame is not selected".to_string(),
)) as Error
})?;
if start_x >= animation[current].width || start_y >= animation[current].height {
return Ok(None);
}
let requested_end_x = start_x.checked_add(width).ok_or_else(|| {
Box::new(ImgError::new_const(
ImgErrorKind::OutboundIndex,
"draw rectangle x overflow".to_string(),
)) as Error
})?;
let requested_end_y = start_y.checked_add(height).ok_or_else(|| {
Box::new(ImgError::new_const(
ImgErrorKind::OutboundIndex,
"draw rectangle y overflow".to_string(),
)) as Error
})?;
w = animation[current].width.min(requested_end_x) - start_x;
h = animation[current].height.min(requested_end_y) - start_y;
raws = animation[current].width;
buffer = &mut animation[current].buffer;
} else {
return Err(Box::new(ImgError::new_const(
ImgErrorKind::NotInitializedImageBuffer,
"in animation".to_string(),
)));
}
for y in 0..h {
let scanline_src = y * width * 4;
let scanline_dest = (start_y + y) * raws * 4;
for x in 0..w {
let offset_src = scanline_src + x * 4;
let offset_dest = scanline_dest + (x + start_x) * 4;
if offset_src + 3 >= data.len() {
return Err(Box::new(ImgError::new_const(
ImgErrorKind::OutboundIndex,
"decoder buffer in draw".to_string(),
)));
}
if offset_dest + 3 >= buffer.len() {
return Err(Box::new(ImgError::new_const(
ImgErrorKind::OutboundIndex,
"image buffer in draw".to_string(),
)));
}
buffer[offset_dest] = data[offset_src];
buffer[offset_dest + 1] = data[offset_src + 1];
buffer[offset_dest + 2] = data[offset_src + 2];
buffer[offset_dest + 3] = data[offset_src + 3];
}
}
Ok(None)
}
fn terminate(
&mut self,
_: Option<TerminateOptions>,
) -> Result<Option<CallbackResponse>, Error> {
Ok(None)
}
fn next(&mut self, opt: Option<NextOptions>) -> Result<Option<CallbackResponse>, Error> {
if self.animation.is_some() {
if let Some(opt) = opt {
if self.current.is_none() {
self.current = Some(0);
self.first_wait_time = Some(opt.await_time);
} else {
self.current = Some(
self.current.ok_or_else(|| {
Box::new(ImgError::new_const(
ImgErrorKind::IllegalData,
"animation frame is not selected".to_string(),
)) as Error
})? + 1,
);
}
let (width, height, start_x, start_y);
if let Some(ref rect) = opt.image_rect {
width = rect.width;
height = rect.height;
start_x = rect.start_x;
start_y = rect.start_y;
} else {
width = self.width;
height = self.height;
start_x = 0;
start_y = 0;
}
let buffersize = checked_rgba_len(width, height, "animation frame")?;
let buffer: Vec<u8> = (0..buffersize).map(|_| 0).collect();
let layer = AnimationLayer {
width,
height,
start_x,
start_y,
buffer,
control: opt,
};
if let Some(animation) = self.animation.as_mut() {
animation.push(layer);
} else {
return Err(Box::new(ImgError::new_const(
ImgErrorKind::NotInitializedImageBuffer,
"animation buffer is not initialized".to_string(),
)));
}
return Ok(Some(CallbackResponse::cont()));
}
}
Ok(Some(CallbackResponse::abort()))
}
fn verbose(
&mut self,
str: &str,
_: Option<VerboseOptions>,
) -> Result<Option<CallbackResponse>, Error> {
(self.fnverbose)(str)
}
fn set_metadata(
&mut self,
key: &str,
value: DataMap,
) -> Result<Option<CallbackResponse>, Error> {
let hashmap = if let Some(ref mut hashmap) = self.metadata {
hashmap
} else {
self.metadata = Some(HashMap::new());
self.metadata.as_mut().ok_or_else(|| {
Box::new(ImgError::new_const(
ImgErrorKind::IllegalData,
"metadata store is not initialized".to_string(),
)) as Error
})?
};
hashmap.insert(key.to_string(), value);
Ok(None)
}
}
impl PickCallback for ImageBuffer {
fn encode_start(&mut self, _: Option<EncoderOptions>) -> Result<Option<ImageProfiles>, Error> {
let mut metadata = self.metadata.clone();
if let Some(animation) = &self.animation {
if !animation.is_empty() {
let hashmap = if let Some(ref mut metadata) = metadata {
metadata
} else {
metadata = Some(HashMap::new());
metadata.as_mut().ok_or_else(|| {
Box::new(ImgError::new_const(
ImgErrorKind::IllegalData,
"metadata store is not initialized".to_string(),
)) as Error
})?
};
append_animation_metadata(hashmap, animation, self.loop_count.unwrap_or(0));
}
}
let init = ImageProfiles {
width: self.width,
height: self.height,
background: self.background_color.clone(),
metadata,
};
Ok(Some(init))
}
fn encode_pick(
&mut self,
start_x: usize,
start_y: usize,
width: usize,
height: usize,
_: Option<PickOptions>,
) -> Result<Option<Vec<u8>>, Error> {
if self.buffer.is_none() {
return Err(Box::new(ImgError::new_const(
ImgErrorKind::NotInitializedImageBuffer,
"in pick".to_string(),
)));
}
let buffersize = checked_rgba_len(width, height, "pick")?;
let mut data = Vec::with_capacity(buffersize);
let buffer = self.buffer.as_ref().ok_or_else(|| {
Box::new(ImgError::new_const(
ImgErrorKind::NotInitializedImageBuffer,
"in pick".to_string(),
)) as Error
})?;
if start_x >= self.width || start_y >= self.height {
return Ok(None);
}
let requested_end_x = start_x.checked_add(width).ok_or_else(|| {
Box::new(ImgError::new_const(
ImgErrorKind::OutboundIndex,
"pick rectangle x overflow".to_string(),
)) as Error
})?;
let requested_end_y = start_y.checked_add(height).ok_or_else(|| {
Box::new(ImgError::new_const(
ImgErrorKind::OutboundIndex,
"pick rectangle y overflow".to_string(),
)) as Error
})?;
let w = self.width.min(requested_end_x) - start_x;
let h = self.height.min(requested_end_y) - start_y;
for y in 0..h {
let scanline_src = (start_y + y) * self.width * 4;
for x in 0..w {
let offset_src = scanline_src + (start_x + x) * 4;
if offset_src + 3 >= buffer.len() {
return Err(Box::new(ImgError::new_const(
ImgErrorKind::OutboundIndex,
"Image buffer in pick".to_string(),
)));
}
data.push(buffer[offset_src]);
data.push(buffer[offset_src + 1]);
data.push(buffer[offset_src + 2]);
data.push(buffer[offset_src + 3]);
}
for _ in w..width {
data.push(0x00);
data.push(0x00);
data.push(0x00);
data.push(0x00);
}
}
for _ in h..height {
for _ in 0..width {
data.push(0x00);
data.push(0x00);
data.push(0x00);
data.push(0x00);
}
}
Ok(Some(data))
}
fn encode_end(&mut self, _: Option<EndOptions>) -> Result<(), Error> {
Ok(())
}
fn metadata(&mut self) -> Result<Option<HashMap<String, DataMap>>, Error> {
if let Some(hashmap) = &self.metadata {
Ok(Some(hashmap.clone()))
} else {
Ok(None)
}
}
}
pub struct DecodeOptions<'a> {
pub debug_flag: usize,
pub drawer: &'a mut dyn DrawCallback,
}
pub struct EncodeOptions<'a> {
pub debug_flag: usize,
pub drawer: &'a mut dyn PickCallback,
pub options: Option<HashMap<String, DataMap>>,
}
pub fn image_from(buffer: &[u8]) -> Result<ImageBuffer, Error> {
image_load(buffer)
}
#[cfg(not(target_family = "wasm"))]
pub fn image_from_file(filename: String) -> Result<ImageBuffer, Error> {
let f = std::fs::File::open(filename)?;
let reader = BufReader::new(f);
let mut image = ImageBuffer::new();
let mut option = DecodeOptions {
debug_flag: 0x00,
drawer: &mut image,
};
let _ = image_reader(reader, &mut option)?;
Ok(image)
}
#[cfg(not(target_family = "wasm"))]
pub fn image_to_file(
filename: String,
image: &mut dyn PickCallback,
format: ImageFormat,
) -> Result<(), Error> {
let f = std::fs::File::create(filename)?;
let mut option = EncodeOptions {
debug_flag: 0x00,
drawer: image,
options: None,
};
image_writer(f, &mut option, format)?;
Ok(())
}
#[cfg(not(target_family = "wasm"))]
fn format_from_output_path(output_file: &str) -> Result<ImageFormat, Error> {
let extension = Path::new(output_file)
.extension()
.and_then(|extension| extension.to_str())
.map(|extension| extension.to_ascii_lowercase());
match extension.as_deref() {
Some("gif") => Ok(ImageFormat::Gif),
Some("png") | Some("apng") => Ok(ImageFormat::Png),
Some("jpg") | Some("jpeg") => Ok(ImageFormat::Jpeg),
Some("bmp") => Ok(ImageFormat::Bmp),
Some("tif") | Some("tiff") => Ok(ImageFormat::Tiff),
Some("webp") => Ok(ImageFormat::Webp),
Some(extension) => Err(Box::new(ImgError::new_const(
ImgErrorKind::NoSupportFormat,
format!("unsupported output extension: {extension}"),
))),
None => Err(Box::new(ImgError::new_const(
ImgErrorKind::InvalidParameter,
"output file has no extension".to_string(),
))),
}
}
#[cfg(not(target_family = "wasm"))]
pub fn convert(
input_file: String,
output_file: String,
options: Option<HashMap<String, DataMap>>,
) -> Result<(), Error> {
let format = format_from_output_path(&output_file)?;
let f = std::fs::File::create(&output_file)?;
let mut image = image_from_file(input_file)?;
let mut encode = EncodeOptions {
debug_flag: 0,
drawer: &mut image,
options,
};
image_writer(f, &mut encode, format)?;
Ok(())
}
pub fn image_load(buffer: &[u8]) -> Result<ImageBuffer, Error> {
let mut ib = ImageBuffer::new();
let mut option = DecodeOptions {
debug_flag: 0,
drawer: &mut ib,
};
let mut reader = BytesReader::new(buffer);
image_decoder(&mut reader, &mut option)?;
Ok(ib)
}
pub fn image_loader(
buffer: &[u8],
option: &mut DecodeOptions,
) -> Result<Option<ImgWarnings>, Error> {
let mut reader = BytesReader::new(buffer);
let r = image_decoder(&mut reader, option)?;
Ok(r)
}
#[cfg(not(target_family = "wasm"))]
pub fn image_reader<R: BufRead + Seek>(
reader: R,
option: &mut DecodeOptions,
) -> Result<Option<ImgWarnings>, Error> {
let mut reader = StreamReader::new(reader);
let r = image_decoder(&mut reader, option)?;
Ok(r)
}
pub fn image_writer<W: Write>(
mut writer: W,
option: &mut EncodeOptions,
format: ImageFormat,
) -> Result<Option<ImgWarnings>, Error> {
let buffer = image_encoder(option, format)?;
writer.write_all(&buffer)?;
writer.flush()?;
Ok(None)
}
pub fn image_decoder<B: BinaryReader>(
reader: &mut B,
option: &mut DecodeOptions,
) -> Result<Option<ImgWarnings>, Error> {
let current = reader.offset()?;
let end = reader.seek(SeekFrom::End(0))?;
reader.seek(SeekFrom::Start(current))?;
let sample_len = usize::try_from((end - current).min(128)).map_err(|_| {
Box::new(ImgError::new_const(
ImgErrorKind::InvalidParameter,
"input sample size overflow".to_string(),
)) as Error
})?;
let buffer = reader.read_bytes_no_move(sample_len)?;
let format = format_check(&buffer);
use crate::util::ImageFormat::*;
match format {
#[cfg(feature = "jpeg")]
Jpeg => {
return crate::jpeg::decoder::decode(reader, option);
}
#[cfg(feature = "bmp")]
Bmp => {
return crate::bmp::decoder::decode(reader, option);
}
#[cfg(feature = "ico")]
Ico => {
return crate::ico::decoder::decode(reader, option);
}
#[cfg(feature = "gif")]
Gif => {
return crate::gif::decoder::decode(reader, option);
}
#[cfg(feature = "png")]
Png => {
return crate::png::decoder::decode(reader, option);
}
#[cfg(feature = "webp")]
Webp => {
return crate::webp::decoder::decode(reader, option);
}
#[cfg(feature = "tiff")]
Tiff => {
return crate::tiff::decoder::decode(reader, option);
}
#[cfg(all(feature = "mag", not(feature = "noretoro")))]
Mag => {
return crate::mag::decoder::decode(reader, option);
}
#[cfg(all(feature = "maki", not(feature = "noretoro")))]
Maki => {
return crate::maki::decoder::decode(reader, option);
}
#[cfg(all(feature = "pi", not(feature = "noretoro")))]
Pi => {
return crate::pi::decoder::decode(reader, option);
}
#[cfg(all(feature = "pic", not(feature = "noretoro")))]
Pic => {
return crate::pic::decoder::decode(reader, option);
}
#[cfg(all(feature = "vsp", not(feature = "noretoro")))]
Vsp => {
return crate::vsp::decoder::decode(reader, option);
}
_ => {}
}
#[cfg(all(feature = "pcd", not(feature = "noretoro")))]
{
let current = reader.seek(std::io::SeekFrom::Current(0))?;
let pcd = (|| -> Result<bool, Error> {
reader.seek(std::io::SeekFrom::Start(0x800))?;
let mut id = [0u8; 7];
reader.read_exact(&mut id)?;
Ok(&id == b"PCD_IPI")
})();
reader.seek(std::io::SeekFrom::Start(current))?;
if matches!(pcd, Ok(true)) {
return crate::pcd::decoder::decode(reader, option);
}
}
Err(Box::new(ImgError::new_const(
ImgErrorKind::NoSupportFormat,
"This buffer can not decode".to_string(),
)))
}
pub fn image_encoder(option: &mut EncodeOptions, format: ImageFormat) -> Result<Vec<u8>, Error> {
use crate::util::ImageFormat::*;
match format {
#[cfg(feature = "gif")]
Gif => {
return crate::gif::encoder::encode(option);
}
#[cfg(feature = "bmp")]
Bmp => {
return crate::bmp::encoder::encode(option);
}
#[cfg(feature = "jpeg")]
Jpeg => {
return crate::jpeg::encoder::encode(option);
}
#[cfg(feature = "png")]
Png => {
return crate::png::encoder::encode(option);
}
#[cfg(feature = "tiff")]
Tiff => {
return crate::tiff::encoder::encode(option);
}
#[cfg(feature = "webp")]
Webp => {
return crate::webp::encoder::encode(option);
}
_ => Err(Box::new(ImgError::new_const(
ImgErrorKind::NoSupportFormat,
"This encoder no impl".to_string(),
))),
}
}
pub fn image_to(
image: &mut ImageBuffer,
format: ImageFormat,
options: Option<HashMap<String, DataMap>>,
) -> Result<Vec<u8>, Error> {
let mut option = EncodeOptions {
debug_flag: 0,
drawer: image,
options,
};
image_encoder(&mut option, format)
}