use crate::sys;
use std::cell::UnsafeCell;
use std::ffi::c_void;
use std::ptr::NonNull;
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
#[repr(transparent)]
pub struct TextureId(u64);
impl TextureId {
#[inline]
pub const fn new(id: u64) -> Self {
Self(id)
}
#[inline]
pub const fn id(self) -> u64 {
self.0
}
#[inline]
pub const fn null() -> Self {
Self(0)
}
#[inline]
pub const fn is_null(self) -> bool {
self.0 == 0
}
}
impl From<u64> for TextureId {
#[inline]
fn from(id: u64) -> Self {
TextureId(id)
}
}
impl<T> From<*const T> for TextureId {
#[inline]
fn from(ptr: *const T) -> Self {
TextureId(ptr as usize as u64)
}
}
impl<T> From<*mut T> for TextureId {
#[inline]
fn from(ptr: *mut T) -> Self {
TextureId(ptr as usize as u64)
}
}
impl From<TextureId> for *const c_void {
#[inline]
fn from(id: TextureId) -> Self {
debug_assert!(
id.0 <= (usize::MAX as u64),
"TextureId value {} exceeds pointer width on this target",
id.0
);
id.0 as usize as *const c_void
}
}
impl From<TextureId> for *mut c_void {
#[inline]
fn from(id: TextureId) -> Self {
debug_assert!(
id.0 <= (usize::MAX as u64),
"TextureId value {} exceeds pointer width on this target",
id.0
);
id.0 as usize as *mut c_void
}
}
impl From<usize> for TextureId {
#[inline]
fn from(id: usize) -> Self {
TextureId(id as u64)
}
}
impl From<TextureId> for usize {
#[inline]
fn from(id: TextureId) -> Self {
debug_assert!(
id.0 <= (usize::MAX as u64),
"TextureId value {} exceeds usize width on this target",
id.0
);
id.0 as usize
}
}
impl Default for TextureId {
#[inline]
fn default() -> Self {
Self::null()
}
}
pub type RawTextureId = sys::ImTextureID;
impl From<TextureId> for RawTextureId {
#[inline]
fn from(id: TextureId) -> Self {
id.id() as sys::ImTextureID
}
}
#[derive(Copy, Clone, Debug)]
#[repr(transparent)]
pub struct TextureRef(sys::ImTextureRef);
const _: [(); std::mem::size_of::<sys::ImTextureRef>()] = [(); std::mem::size_of::<TextureRef>()];
const _: [(); std::mem::align_of::<sys::ImTextureRef>()] = [(); std::mem::align_of::<TextureRef>()];
impl TextureRef {
#[inline]
pub fn from_raw(raw: sys::ImTextureRef) -> Self {
Self(raw)
}
#[inline]
pub fn raw(self) -> sys::ImTextureRef {
self.0
}
}
impl From<TextureId> for TextureRef {
#[inline]
fn from(id: TextureId) -> Self {
TextureRef(sys::ImTextureRef {
_TexData: std::ptr::null_mut(),
_TexID: id.id() as sys::ImTextureID,
})
}
}
impl From<u64> for TextureRef {
#[inline]
fn from(id: u64) -> Self {
TextureRef::from(TextureId::from(id))
}
}
impl From<&TextureData> for TextureRef {
#[inline]
fn from(td: &TextureData) -> Self {
TextureRef(sys::ImTextureRef {
_TexData: std::ptr::null_mut(),
_TexID: td.tex_id().id() as sys::ImTextureID,
})
}
}
impl From<&mut TextureData> for TextureRef {
#[inline]
fn from(td: &mut TextureData) -> Self {
TextureRef(sys::ImTextureRef {
_TexData: td.as_raw_mut(),
_TexID: 0,
})
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[repr(i32)]
pub enum TextureFormat {
RGBA32 = sys::ImTextureFormat_RGBA32 as i32,
Alpha8 = sys::ImTextureFormat_Alpha8 as i32,
}
impl From<sys::ImTextureFormat> for TextureFormat {
fn from(format: sys::ImTextureFormat) -> Self {
match format {
sys::ImTextureFormat_RGBA32 => TextureFormat::RGBA32,
sys::ImTextureFormat_Alpha8 => TextureFormat::Alpha8,
_ => TextureFormat::RGBA32, }
}
}
impl From<TextureFormat> for sys::ImTextureFormat {
fn from(format: TextureFormat) -> Self {
format as sys::ImTextureFormat
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[repr(i32)]
pub enum TextureStatus {
OK = sys::ImTextureStatus_OK as i32,
Destroyed = sys::ImTextureStatus_Destroyed as i32,
WantCreate = sys::ImTextureStatus_WantCreate as i32,
WantUpdates = sys::ImTextureStatus_WantUpdates as i32,
WantDestroy = sys::ImTextureStatus_WantDestroy as i32,
}
impl From<sys::ImTextureStatus> for TextureStatus {
fn from(status: sys::ImTextureStatus) -> Self {
match status {
sys::ImTextureStatus_OK => TextureStatus::OK,
sys::ImTextureStatus_Destroyed => TextureStatus::Destroyed,
sys::ImTextureStatus_WantCreate => TextureStatus::WantCreate,
sys::ImTextureStatus_WantUpdates => TextureStatus::WantUpdates,
sys::ImTextureStatus_WantDestroy => TextureStatus::WantDestroy,
_ => TextureStatus::Destroyed, }
}
}
impl From<TextureStatus> for sys::ImTextureStatus {
fn from(status: TextureStatus) -> Self {
status as sys::ImTextureStatus
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct TextureRect {
pub x: u16,
pub y: u16,
pub w: u16,
pub h: u16,
}
impl From<sys::ImTextureRect> for TextureRect {
fn from(rect: sys::ImTextureRect) -> Self {
Self {
x: rect.x,
y: rect.y,
w: rect.w,
h: rect.h,
}
}
}
impl From<TextureRect> for sys::ImTextureRect {
fn from(rect: TextureRect) -> Self {
Self {
x: rect.x,
y: rect.y,
w: rect.w,
h: rect.h,
}
}
}
pub struct OwnedTextureData {
raw: NonNull<sys::ImTextureData>,
}
impl OwnedTextureData {
pub fn new() -> Self {
let raw = unsafe { sys::ImTextureData_ImTextureData() };
let raw = NonNull::new(raw).expect("ImTextureData_ImTextureData() returned null");
Self { raw }
}
pub fn into_raw(self) -> *mut sys::ImTextureData {
let raw = self.raw.as_ptr();
std::mem::forget(self);
raw
}
pub unsafe fn from_raw_owned(raw: *mut sys::ImTextureData) -> Self {
let raw = NonNull::new(raw).expect("raw ImTextureData pointer was null");
Self { raw }
}
}
impl Drop for OwnedTextureData {
fn drop(&mut self) {
unsafe { sys::ImTextureData_destroy(self.raw.as_ptr()) }
}
}
impl std::ops::Deref for OwnedTextureData {
type Target = TextureData;
fn deref(&self) -> &Self::Target {
unsafe { &*(self.raw.as_ptr() as *const TextureData) }
}
}
impl std::ops::DerefMut for OwnedTextureData {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { &mut *(self.raw.as_ptr() as *mut TextureData) }
}
}
impl AsRef<TextureData> for OwnedTextureData {
fn as_ref(&self) -> &TextureData {
self
}
}
impl AsMut<TextureData> for OwnedTextureData {
fn as_mut(&mut self) -> &mut TextureData {
self
}
}
#[repr(transparent)]
pub struct TextureData {
raw: UnsafeCell<sys::ImTextureData>,
}
const _: [(); std::mem::size_of::<sys::ImTextureData>()] = [(); std::mem::size_of::<TextureData>()];
const _: [(); std::mem::align_of::<sys::ImTextureData>()] =
[(); std::mem::align_of::<TextureData>()];
impl TextureData {
#[inline]
fn inner(&self) -> &sys::ImTextureData {
unsafe { &*self.raw.get() }
}
#[inline]
fn inner_mut(&mut self) -> &mut sys::ImTextureData {
unsafe { &mut *self.raw.get() }
}
pub fn new() -> OwnedTextureData {
OwnedTextureData::new()
}
pub(crate) unsafe fn from_raw<'a>(raw: *mut sys::ImTextureData) -> &'a mut Self {
unsafe { &mut *(raw as *mut Self) }
}
pub(crate) unsafe fn from_raw_ref<'a>(raw: *const sys::ImTextureData) -> &'a Self {
unsafe { &*(raw as *const Self) }
}
pub fn as_raw(&self) -> *const sys::ImTextureData {
self.raw.get() as *const _
}
pub fn as_raw_mut(&mut self) -> *mut sys::ImTextureData {
self.raw.get()
}
pub fn unique_id(&self) -> i32 {
self.inner().UniqueID
}
pub fn status(&self) -> TextureStatus {
TextureStatus::from(self.inner().Status)
}
pub fn set_status(&mut self, status: TextureStatus) {
unsafe {
if status == TextureStatus::Destroyed {
sys::ImTextureData_SetTexID(self.as_raw_mut(), 0 as sys::ImTextureID);
(*self.as_raw_mut()).BackendUserData = std::ptr::null_mut();
}
sys::ImTextureData_SetStatus(self.as_raw_mut(), status.into());
}
}
pub fn backend_user_data(&self) -> *mut c_void {
self.inner().BackendUserData
}
pub fn set_backend_user_data(&mut self, data: *mut c_void) {
self.inner_mut().BackendUserData = data;
}
pub fn tex_id(&self) -> TextureId {
TextureId::from(self.inner().TexID)
}
pub fn set_tex_id(&mut self, tex_id: TextureId) {
unsafe {
sys::ImTextureData_SetTexID(self.as_raw_mut(), tex_id.id() as sys::ImTextureID);
}
}
#[inline]
pub fn texture_ref(&mut self) -> TextureRef {
unsafe { TextureRef::from_raw(sys::ImTextureData_GetTexRef(self.as_raw_mut())) }
}
pub fn format(&self) -> TextureFormat {
TextureFormat::from(self.inner().Format)
}
pub fn width(&self) -> i32 {
self.inner().Width
}
pub fn height(&self) -> i32 {
self.inner().Height
}
pub fn bytes_per_pixel(&self) -> i32 {
self.inner().BytesPerPixel
}
pub fn unused_frames(&self) -> i32 {
self.inner().UnusedFrames
}
pub fn ref_count(&self) -> u16 {
self.inner().RefCount
}
pub fn use_colors(&self) -> bool {
self.inner().UseColors
}
pub fn want_destroy_next_frame(&self) -> bool {
self.inner().WantDestroyNextFrame
}
pub fn pixels(&self) -> Option<&[u8]> {
let raw = self.inner();
if raw.Pixels.is_null() {
None
} else {
let width = raw.Width;
let height = raw.Height;
let bytes_per_pixel = raw.BytesPerPixel;
if width <= 0 || height <= 0 || bytes_per_pixel <= 0 {
return None;
}
let size = (width as usize)
.checked_mul(height as usize)?
.checked_mul(bytes_per_pixel as usize)?;
unsafe { Some(std::slice::from_raw_parts(raw.Pixels as *const u8, size)) }
}
}
pub fn used_rect(&self) -> TextureRect {
TextureRect::from(self.inner().UsedRect)
}
pub fn update_rect(&self) -> TextureRect {
TextureRect::from(self.inner().UpdateRect)
}
pub fn updates(&self) -> impl Iterator<Item = TextureRect> + '_ {
let vec = &self.inner().Updates;
let count = if vec.Data.is_null() {
0
} else {
usize::try_from(vec.Size).unwrap_or(0)
};
let data = vec.Data as *const sys::ImTextureRect;
(0..count).map(move |i| unsafe { TextureRect::from(*data.add(i)) })
}
pub fn pixels_at(&self, x: i32, y: i32) -> Option<&[u8]> {
let raw = self.inner();
let width = raw.Width;
let height = raw.Height;
let bytes_per_pixel = raw.BytesPerPixel;
if raw.Pixels.is_null()
|| width <= 0
|| height <= 0
|| bytes_per_pixel <= 0
|| x < 0
|| y < 0
|| x >= width
|| y >= height
{
None
} else {
let width_usize = width as usize;
let x_usize = x as usize;
let y_usize = y as usize;
let bpp_usize = bytes_per_pixel as usize;
let total_size = width_usize
.checked_mul(height as usize)?
.checked_mul(bpp_usize)?;
let offset_px = y_usize.checked_mul(width_usize)?.checked_add(x_usize)?;
let offset_bytes = offset_px.checked_mul(bpp_usize)?;
let remaining_size = total_size.checked_sub(offset_bytes)?;
unsafe {
let ptr = (raw.Pixels as *const u8).add(offset_bytes);
Some(std::slice::from_raw_parts(ptr, remaining_size))
}
}
}
pub fn pitch(&self) -> i32 {
self.width() * self.bytes_per_pixel()
}
pub fn create(&mut self, format: TextureFormat, width: i32, height: i32) {
unsafe {
sys::ImTextureData_Create(self.as_raw_mut(), format.into(), width, height);
}
}
pub fn destroy_pixels(&mut self) {
unsafe {
sys::ImTextureData_DestroyPixels(self.as_raw_mut());
}
}
pub fn set_data(&mut self, data: &[u8]) {
unsafe {
let raw = self.as_raw_mut();
let needed = (*raw)
.Width
.saturating_mul((*raw).Height)
.saturating_mul((*raw).BytesPerPixel);
if needed <= 0 {
return;
}
if (*raw).Pixels.is_null() {
sys::ImTextureData_Create(
self.as_raw_mut(),
(*raw).Format,
(*raw).Width,
(*raw).Height,
);
}
let copy_bytes = std::cmp::min(needed as usize, data.len());
if copy_bytes == 0 {
return;
}
std::ptr::copy_nonoverlapping(data.as_ptr(), (*raw).Pixels as *mut u8, copy_bytes);
(*raw).UpdateRect = sys::ImTextureRect {
x: 0u16,
y: 0u16,
w: (*raw).Width.clamp(0, u16::MAX as i32) as u16,
h: (*raw).Height.clamp(0, u16::MAX as i32) as u16,
};
sys::ImTextureData_SetStatus(raw, sys::ImTextureStatus_WantUpdates);
}
}
pub fn set_width(&mut self, width: u32) {
let width = width.min(i32::MAX as u32) as i32;
self.inner_mut().Width = width;
}
pub fn set_height(&mut self, height: u32) {
let height = height.min(i32::MAX as u32) as i32;
self.inner_mut().Height = height;
}
pub fn set_format(&mut self, format: TextureFormat) {
self.inner_mut().Format = format.into();
}
}
pub fn get_format_bytes_per_pixel(format: TextureFormat) -> i32 {
unsafe { sys::igImTextureDataGetFormatBytesPerPixel(format.into()) }
}
pub fn create_texture_ref(texture_id: u64) -> sys::ImTextureRef {
sys::ImTextureRef {
_TexData: std::ptr::null_mut(),
_TexID: texture_id,
}
}
pub fn get_status_name(status: TextureStatus) -> &'static str {
unsafe {
let ptr = sys::igImTextureDataGetStatusName(status.into());
if ptr.is_null() {
"Unknown"
} else {
std::ffi::CStr::from_ptr(ptr).to_str().unwrap_or("Invalid")
}
}
}
pub fn get_format_name(format: TextureFormat) -> &'static str {
unsafe {
let ptr = sys::igImTextureDataGetFormatName(format.into());
if ptr.is_null() {
"Unknown"
} else {
std::ffi::CStr::from_ptr(ptr).to_str().unwrap_or("Invalid")
}
}
}