mirror of
https://github.com/tauri-apps/plugins-workspace.git
synced 2026-04-21 11:26:15 +02:00
230 lines
6.5 KiB
Rust
230 lines
6.5 KiB
Rust
#[cfg(feature = "dbus")]
|
|
use dbus::arg::messageitem::{MessageItem, MessageItemArray};
|
|
pub use image::DynamicImage;
|
|
|
|
use std::cmp::Ordering;
|
|
use std::convert::TryFrom;
|
|
use std::error::Error;
|
|
use std::fmt;
|
|
use std::path::Path;
|
|
|
|
use super::miniver::Version;
|
|
|
|
mod constants {
|
|
pub const IMAGE_DATA: &str = "image-data";
|
|
pub const IMAGE_DATA_1_1: &str = "image_data";
|
|
pub const IMAGE_DATA_1_0: &str = "icon_data";
|
|
}
|
|
|
|
/// Image representation for images. Send via `Notification::image_data()`
|
|
#[derive(PartialEq, Eq, Debug, Clone, Hash)]
|
|
pub struct Image {
|
|
width: i32,
|
|
height: i32,
|
|
rowstride: i32,
|
|
alpha: bool,
|
|
bits_per_sample: i32,
|
|
channels: i32,
|
|
data: Vec<u8>,
|
|
}
|
|
|
|
impl Image {
|
|
fn from_raw_data(
|
|
width: i32,
|
|
height: i32,
|
|
data: Vec<u8>,
|
|
channels: i32,
|
|
bits_per_sample: i32,
|
|
alpha: bool,
|
|
) -> Result<Self, ImageError> {
|
|
const MAX_SIZE: i32 = 0x0fff_ffff;
|
|
if width > MAX_SIZE || height > MAX_SIZE {
|
|
return Err(ImageError::TooBig);
|
|
}
|
|
|
|
if data.len() != (width * height * channels) as usize {
|
|
Err(ImageError::WrongDataSize)
|
|
} else {
|
|
Ok(Self {
|
|
width,
|
|
height,
|
|
bits_per_sample,
|
|
channels,
|
|
data,
|
|
rowstride: width * channels,
|
|
alpha,
|
|
})
|
|
}
|
|
}
|
|
|
|
/// Creates an image from a raw vector of bytes
|
|
pub fn from_rgb(width: i32, height: i32, data: Vec<u8>) -> Result<Self, ImageError> {
|
|
let channels = 3i32;
|
|
let bits_per_sample = 8;
|
|
Self::from_raw_data(width, height, data, channels, bits_per_sample, false)
|
|
}
|
|
|
|
/// Creates an image from a raw vector of bytes with alpha
|
|
pub fn from_rgba(width: i32, height: i32, data: Vec<u8>) -> Result<Self, ImageError> {
|
|
let channels = 4i32;
|
|
let bits_per_sample = 8;
|
|
Self::from_raw_data(width, height, data, channels, bits_per_sample, true)
|
|
}
|
|
|
|
/// Attempts to open the given path as image
|
|
pub fn open<T: AsRef<Path> + Sized>(path: T) -> Result<Self, ImageError> {
|
|
let dyn_img = image::open(&path).map_err(ImageError::CantOpen)?;
|
|
Image::try_from(dyn_img)
|
|
}
|
|
|
|
#[cfg(all(feature = "images", feature = "zbus"))]
|
|
pub(crate) fn to_tuple(&self) -> (i32, i32, i32, bool, i32, i32, Vec<u8>) {
|
|
(
|
|
self.width,
|
|
self.height,
|
|
self.rowstride,
|
|
self.alpha,
|
|
self.bits_per_sample,
|
|
self.channels,
|
|
self.data.clone(),
|
|
)
|
|
}
|
|
}
|
|
|
|
impl TryFrom<DynamicImage> for Image {
|
|
type Error = ImageError;
|
|
|
|
fn try_from(dyn_img: DynamicImage) -> Result<Self, Self::Error> {
|
|
match dyn_img {
|
|
DynamicImage::ImageRgb8(img) => Self::try_from(img),
|
|
DynamicImage::ImageRgba8(img) => Self::try_from(img),
|
|
_ => Err(ImageError::CantConvert),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl TryFrom<image::RgbImage> for Image {
|
|
type Error = ImageError;
|
|
|
|
fn try_from(img: image::RgbImage) -> Result<Self, Self::Error> {
|
|
let (width, height) = img.dimensions();
|
|
let image_data = img.into_raw();
|
|
Image::from_rgb(width as i32, height as i32, image_data)
|
|
}
|
|
}
|
|
|
|
impl TryFrom<image::RgbaImage> for Image {
|
|
type Error = ImageError;
|
|
|
|
fn try_from(img: image::RgbaImage) -> Result<Self, Self::Error> {
|
|
let (width, height) = img.dimensions();
|
|
let image_data = img.into_raw();
|
|
Image::from_rgba(width as i32, height as i32, image_data)
|
|
}
|
|
}
|
|
|
|
/// Errors that can occur when creating an Image
|
|
#[derive(Debug)]
|
|
pub enum ImageError {
|
|
/// The given image is too big. DBus only has 32 bits for width / height
|
|
TooBig,
|
|
/// The given bytes don't match the width, height and channel count
|
|
WrongDataSize,
|
|
/// Can't open given path
|
|
CantOpen(image::ImageError),
|
|
/// Can't convert from given input
|
|
CantConvert,
|
|
}
|
|
|
|
impl Error for ImageError {
|
|
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
|
use ImageError::*;
|
|
match self {
|
|
TooBig | WrongDataSize | CantConvert => None,
|
|
CantOpen(e) => Some(e),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for ImageError {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
use ImageError::*;
|
|
match self {
|
|
TooBig => writeln!(
|
|
f,
|
|
"The given image is too big. DBus only has 32 bits for width / height"
|
|
),
|
|
WrongDataSize => writeln!(
|
|
f,
|
|
"The given bytes don't match the width, height and channel count"
|
|
),
|
|
CantOpen(e) => writeln!(f, "Can't open given path {}", e),
|
|
CantConvert => writeln!(f, "Can't convert from given input"),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// matching image data key for each spec version
|
|
#[cfg(feature = "dbus")]
|
|
pub(crate) fn image_spec(version: Version) -> String {
|
|
match version.cmp(&Version::new(1, 1)) {
|
|
Ordering::Less => constants::IMAGE_DATA_1_0.to_owned(),
|
|
Ordering::Equal => constants::IMAGE_DATA_1_1.to_owned(),
|
|
Ordering::Greater => constants::IMAGE_DATA.to_owned(),
|
|
}
|
|
}
|
|
|
|
/// matching image data key for each spec version
|
|
#[cfg(feature = "zbus")]
|
|
pub(crate) fn image_spec_str(version: Version) -> &'static str {
|
|
match version.cmp(&Version::new(1, 1)) {
|
|
Ordering::Less => constants::IMAGE_DATA_1_0,
|
|
Ordering::Equal => constants::IMAGE_DATA_1_1,
|
|
Ordering::Greater => constants::IMAGE_DATA,
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "dbus")]
|
|
pub struct ImageMessage(Image);
|
|
|
|
#[cfg(feature = "dbus")]
|
|
impl From<Image> for ImageMessage {
|
|
fn from(hint: Image) -> Self {
|
|
ImageMessage(hint)
|
|
}
|
|
}
|
|
|
|
impl From<image::ImageError> for ImageError {
|
|
fn from(image_error: image::ImageError) -> Self {
|
|
ImageError::CantOpen(image_error)
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "dbus")]
|
|
impl std::ops::Deref for ImageMessage {
|
|
type Target = Image;
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
&self.0
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "dbus")]
|
|
impl From<ImageMessage> for MessageItem {
|
|
fn from(img_msg: ImageMessage) -> Self {
|
|
let img = img_msg.0;
|
|
|
|
let bytes = img.data.into_iter().map(MessageItem::Byte).collect();
|
|
|
|
MessageItem::Struct(vec![
|
|
MessageItem::Int32(img.width),
|
|
MessageItem::Int32(img.height),
|
|
MessageItem::Int32(img.rowstride),
|
|
MessageItem::Bool(img.alpha),
|
|
MessageItem::Int32(img.bits_per_sample),
|
|
MessageItem::Int32(img.channels),
|
|
MessageItem::Array(MessageItemArray::new(bytes, "ay".into()).unwrap()),
|
|
])
|
|
}
|
|
}
|