use super::Color;
use crate::sensor::SensorData;
use carla_sys::carla_rust::sensor::data::FfiImage;
use cxx::SharedPtr;
use derivative::Derivative;
use ndarray::ArrayView2;
use static_assertions::assert_impl_all;
use std::slice;
#[cfg_attr(carla_version_0916, doc = "")]
#[cfg_attr(
carla_version_0916,
doc = " [`carla.Image`]: https://carla.readthedocs.io/en/0.9.16/python_api/#carla.Image"
)]
#[cfg_attr(carla_version_0915, doc = "")]
#[cfg_attr(
carla_version_0915,
doc = " [`carla.Image`]: https://carla.readthedocs.io/en/0.9.15/python_api/#carla.Image"
)]
#[cfg_attr(carla_version_0914, doc = "")]
#[cfg_attr(
carla_version_0914,
doc = " [`carla.Image`]: https://carla.readthedocs.io/en/0.9.14/python_api/#carla.Image"
)]
#[derive(Clone, Derivative)]
#[derivative(Debug)]
#[repr(transparent)]
pub struct Image {
#[derivative(Debug = "ignore")]
inner: SharedPtr<FfiImage>,
}
impl Image {
#[cfg_attr(
carla_version_0916,
doc = " See [carla.Image.height](https://carla.readthedocs.io/en/0.9.16/python_api/#carla.Image.height)"
)]
#[cfg_attr(
carla_version_0915,
doc = " See [carla.Image.height](https://carla.readthedocs.io/en/0.9.15/python_api/#carla.Image.height)"
)]
#[cfg_attr(
carla_version_0914,
doc = " See [carla.Image.height](https://carla.readthedocs.io/en/0.9.14/python_api/#carla.Image.height)"
)]
#[cfg_attr(
any(carla_version_0916, carla_version_0915, carla_version_0914),
doc = " in the Python API."
)]
pub fn height(&self) -> usize {
self.inner.GetHeight()
}
#[cfg_attr(
carla_version_0916,
doc = " See [carla.Image.width](https://carla.readthedocs.io/en/0.9.16/python_api/#carla.Image.width)"
)]
#[cfg_attr(
carla_version_0915,
doc = " See [carla.Image.width](https://carla.readthedocs.io/en/0.9.15/python_api/#carla.Image.width)"
)]
#[cfg_attr(
carla_version_0914,
doc = " See [carla.Image.width](https://carla.readthedocs.io/en/0.9.14/python_api/#carla.Image.width)"
)]
#[cfg_attr(
any(carla_version_0916, carla_version_0915, carla_version_0914),
doc = " in the Python API."
)]
pub fn width(&self) -> usize {
self.inner.GetWidth()
}
pub fn len(&self) -> usize {
self.inner.size()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[cfg_attr(
carla_version_0916,
doc = " See [carla.Image.fov](https://carla.readthedocs.io/en/0.9.16/python_api/#carla.Image.fov)"
)]
#[cfg_attr(
carla_version_0915,
doc = " See [carla.Image.fov](https://carla.readthedocs.io/en/0.9.15/python_api/#carla.Image.fov)"
)]
#[cfg_attr(
carla_version_0914,
doc = " See [carla.Image.fov](https://carla.readthedocs.io/en/0.9.14/python_api/#carla.Image.fov)"
)]
#[cfg_attr(
any(carla_version_0916, carla_version_0915, carla_version_0914),
doc = " in the Python API."
)]
pub fn fov_angle(&self) -> f32 {
self.inner.GetFOVAngle()
}
pub fn as_slice(&self) -> &[Color] {
let ptr = self.inner.data();
let len = self.len();
debug_assert!(!ptr.is_null(), "Image data pointer is null");
debug_assert!(
(ptr as usize).is_multiple_of(std::mem::align_of::<Color>()),
"Image data pointer not properly aligned"
);
unsafe { slice::from_raw_parts(ptr, len) }
}
pub fn as_raw_bytes(&self) -> &[u8] {
let ptr = self.inner.data() as *const u8;
let byte_len = self.len() * std::mem::size_of::<Color>();
debug_assert!(!ptr.is_null(), "Image data pointer is null");
unsafe { slice::from_raw_parts(ptr, byte_len) }
}
pub fn as_array(&self) -> ArrayView2<'_, Color> {
let width = self.width();
let height = self.height();
let len = self.len();
assert!(
width * height == len,
"Image dimensions mismatch: {}x{} = {} but data length is {}",
width,
height,
width * height,
len
);
ArrayView2::from_shape((width, height), self.as_slice())
.expect("Failed to create array view with validated dimensions")
}
pub fn get(&self, index: usize) -> Option<&Color> {
if index < self.inner.size() {
Some(self.inner.at(index))
} else {
None
}
}
#[cfg_attr(
carla_version_0916,
doc = " See [carla.Image.save_to_disk](https://carla.readthedocs.io/en/0.9.16/python_api/#carla.Image.save_to_disk)"
)]
#[cfg_attr(
carla_version_0915,
doc = " See [carla.Image.save_to_disk](https://carla.readthedocs.io/en/0.9.15/python_api/#carla.Image.save_to_disk)"
)]
#[cfg_attr(
carla_version_0914,
doc = " See [carla.Image.save_to_disk](https://carla.readthedocs.io/en/0.9.14/python_api/#carla.Image.save_to_disk)"
)]
#[cfg_attr(
any(carla_version_0916, carla_version_0915, carla_version_0914),
doc = " in the Python API."
)]
pub fn save_to_disk(&self, path: &str) -> std::io::Result<()> {
use image::{ImageBuffer, Rgba};
let width = self.width() as u32;
let height = self.height() as u32;
let pixels = self.as_slice();
let rgba_data: Vec<u8> = pixels
.iter()
.flat_map(|color| {
[color.r, color.g, color.b, color.a]
})
.collect();
let img_buffer = ImageBuffer::<Rgba<u8>, Vec<u8>>::from_raw(width, height, rgba_data)
.ok_or_else(|| {
std::io::Error::new(
std::io::ErrorKind::InvalidData,
"Failed to create image buffer from pixel data",
)
})?;
img_buffer
.save(path)
.map_err(|e| std::io::Error::other(format!("Failed to save image: {}", e)))
}
pub(crate) fn from_cxx(ptr: SharedPtr<FfiImage>) -> Option<Self> {
if ptr.is_null() {
None
} else {
Some(Self { inner: ptr })
}
}
}
impl TryFrom<SensorData> for Image {
type Error = SensorData;
fn try_from(value: SensorData) -> Result<Self, Self::Error> {
let ptr = value.inner.to_image();
Self::from_cxx(ptr).ok_or(value)
}
}
assert_impl_all!(Image: Send, Sync);