use crate::internal::{RawCast, RawWrapper};
use crate::sys;
use crate::texture::TextureId;
use std::marker::PhantomData;
use std::rc::Rc;
use std::slice;
use std::sync::atomic::{AtomicBool, Ordering};
static TEXTURE_DATA_BORROWED: AtomicBool = AtomicBool::new(false);
pub(crate) fn assert_texture_data_not_borrowed() {
if TEXTURE_DATA_BORROWED.load(Ordering::Acquire) {
panic!(
"TextureData is already mutably borrowed; \
do not mix DrawData::textures()/PlatformIo::textures() with DrawData::texture()/PlatformIo::texture() calls"
);
}
}
#[repr(C)]
pub struct DrawData {
valid: bool,
cmd_lists_count: i32,
pub total_idx_count: i32,
pub total_vtx_count: i32,
cmd_lists: crate::internal::ImVector<*mut sys::ImDrawList>,
pub display_pos: [f32; 2],
pub display_size: [f32; 2],
pub framebuffer_scale: [f32; 2],
owner_viewport: *mut sys::ImGuiViewport,
textures: *mut crate::internal::ImVector<*mut sys::ImTextureData>,
}
const _: [(); std::mem::size_of::<sys::ImDrawData>()] = [(); std::mem::size_of::<DrawData>()];
const _: [(); std::mem::align_of::<sys::ImDrawData>()] = [(); std::mem::align_of::<DrawData>()];
unsafe impl RawCast<sys::ImDrawData> for DrawData {}
impl RawWrapper for DrawData {
type Raw = sys::ImDrawData;
unsafe fn raw(&self) -> &Self::Raw {
unsafe { <Self as RawCast<Self::Raw>>::raw(self) }
}
unsafe fn raw_mut(&mut self) -> &mut Self::Raw {
unsafe { <Self as RawCast<Self::Raw>>::raw_mut(self) }
}
}
impl DrawData {
#[inline]
pub fn valid(&self) -> bool {
self.valid
}
#[inline]
pub fn draw_lists(&self) -> DrawListIterator<'_> {
unsafe {
DrawListIterator {
iter: self.cmd_lists().iter(),
}
}
}
#[inline]
pub fn draw_lists_count(&self) -> usize {
unsafe { self.cmd_lists().len() }
}
pub fn textures(&self) -> TextureIterator<'_> {
unsafe {
if self.textures.is_null() {
TextureIterator::new(std::ptr::null(), std::ptr::null())
} else {
let vector = &*self.textures;
if vector.size <= 0 || vector.data.is_null() {
TextureIterator::new(std::ptr::null(), std::ptr::null())
} else {
TextureIterator::new(vector.data, vector.data.add(vector.size as usize))
}
}
}
}
pub fn textures_count(&self) -> usize {
unsafe {
if self.textures.is_null() {
0
} else {
let vector = &*self.textures;
if vector.size <= 0 || vector.data.is_null() {
0
} else {
vector.size as usize
}
}
}
}
pub fn texture(&self, index: usize) -> Option<&crate::texture::TextureData> {
unsafe {
assert_texture_data_not_borrowed();
if self.textures.is_null() {
return None;
}
let vector = &*self.textures;
let size = usize::try_from(vector.size).ok()?;
if size == 0 || vector.data.is_null() {
return None;
}
if index >= size {
return None;
}
let texture_ptr = *vector.data.add(index);
if texture_ptr.is_null() {
return None;
}
Some(crate::texture::TextureData::from_raw_ref(
texture_ptr as *const _,
))
}
}
pub fn texture_mut(&mut self, index: usize) -> Option<&mut crate::texture::TextureData> {
unsafe {
if self.textures.is_null() {
return None;
}
let vector = &*self.textures;
let size = usize::try_from(vector.size).ok()?;
if size == 0 || vector.data.is_null() {
return None;
}
if index >= size {
return None;
}
let texture_ptr = *vector.data.add(index);
if texture_ptr.is_null() {
return None;
}
Some(crate::texture::TextureData::from_raw(texture_ptr))
}
}
#[inline]
pub fn display_pos(&self) -> [f32; 2] {
self.display_pos
}
#[inline]
pub fn display_size(&self) -> [f32; 2] {
self.display_size
}
#[inline]
pub fn framebuffer_scale(&self) -> [f32; 2] {
self.framebuffer_scale
}
#[inline]
pub(crate) unsafe fn cmd_lists(&self) -> &[*mut sys::ImDrawList] {
unsafe {
if self.cmd_lists_count <= 0 || self.cmd_lists.data.is_null() {
return &[];
}
let len = match usize::try_from(self.cmd_lists_count) {
Ok(len) => len,
Err(_) => return &[],
};
slice::from_raw_parts(self.cmd_lists.data, len)
}
}
#[doc(alias = "DeIndexAllBuffers")]
pub fn deindex_all_buffers(&mut self) {
unsafe {
sys::ImDrawData_DeIndexAllBuffers(RawWrapper::raw_mut(self));
}
}
#[doc(alias = "ScaleClipRects")]
pub fn scale_clip_rects(&mut self, fb_scale: [f32; 2]) {
unsafe {
let scale = sys::ImVec2 {
x: fb_scale[0],
y: fb_scale[1],
};
sys::ImDrawData_ScaleClipRects(RawWrapper::raw_mut(self), scale);
}
}
}
pub struct DrawListIterator<'a> {
iter: std::slice::Iter<'a, *mut sys::ImDrawList>,
}
impl<'a> Iterator for DrawListIterator<'a> {
type Item = &'a DrawList;
fn next(&mut self) -> Option<Self::Item> {
self.iter.next().and_then(|&ptr| {
if ptr.is_null() {
None
} else {
Some(unsafe { DrawList::from_raw(ptr.cast_const()) })
}
})
}
}
impl<'a> ExactSizeIterator for DrawListIterator<'a> {
fn len(&self) -> usize {
self.iter.len()
}
}
#[repr(transparent)]
pub struct DrawList(sys::ImDrawList);
const _: [(); std::mem::size_of::<sys::ImDrawList>()] = [(); std::mem::size_of::<DrawList>()];
const _: [(); std::mem::align_of::<sys::ImDrawList>()] = [(); std::mem::align_of::<DrawList>()];
impl RawWrapper for DrawList {
type Raw = sys::ImDrawList;
#[inline]
unsafe fn raw(&self) -> &sys::ImDrawList {
&self.0
}
#[inline]
unsafe fn raw_mut(&mut self) -> &mut sys::ImDrawList {
&mut self.0
}
}
impl DrawList {
#[inline]
pub(crate) unsafe fn from_raw<'a>(raw: *const sys::ImDrawList) -> &'a Self {
unsafe { &*(raw as *const Self) }
}
#[inline]
pub(crate) unsafe fn cmd_buffer(&self) -> &[sys::ImDrawCmd] {
unsafe {
let cmd_buffer = &self.0.CmdBuffer;
if cmd_buffer.Size <= 0 || cmd_buffer.Data.is_null() {
return &[];
}
let len = match usize::try_from(cmd_buffer.Size) {
Ok(len) => len,
Err(_) => return &[],
};
slice::from_raw_parts(cmd_buffer.Data, len)
}
}
pub fn commands(&self) -> DrawCmdIterator<'_> {
unsafe {
DrawCmdIterator {
iter: self.cmd_buffer().iter(),
}
}
}
pub fn vtx_buffer(&self) -> &[DrawVert] {
unsafe {
let vtx_buffer = &self.0.VtxBuffer;
if vtx_buffer.Size <= 0 || vtx_buffer.Data.is_null() {
return &[];
}
let len = match usize::try_from(vtx_buffer.Size) {
Ok(len) => len,
Err(_) => return &[],
};
slice::from_raw_parts(vtx_buffer.Data as *const DrawVert, len)
}
}
pub fn idx_buffer(&self) -> &[DrawIdx] {
unsafe {
let idx_buffer = &self.0.IdxBuffer;
if idx_buffer.Size <= 0 || idx_buffer.Data.is_null() {
return &[];
}
let len = match usize::try_from(idx_buffer.Size) {
Ok(len) => len,
Err(_) => return &[],
};
slice::from_raw_parts(idx_buffer.Data, len)
}
}
}
pub struct DrawCmdIterator<'a> {
iter: slice::Iter<'a, sys::ImDrawCmd>,
}
impl<'a> Iterator for DrawCmdIterator<'a> {
type Item = DrawCmd;
fn next(&mut self) -> Option<Self::Item> {
self.iter.next().map(|cmd| {
let cmd_params = DrawCmdParams {
clip_rect: [
cmd.ClipRect.x,
cmd.ClipRect.y,
cmd.ClipRect.z,
cmd.ClipRect.w,
],
texture_id: TextureId::from(cmd.TexRef._TexID),
vtx_offset: cmd.VtxOffset as usize,
idx_offset: cmd.IdxOffset as usize,
};
match cmd.UserCallback {
Some(raw_callback) if raw_callback as usize == (-1isize) as usize => {
DrawCmd::ResetRenderState
}
Some(raw_callback) => DrawCmd::RawCallback {
callback: raw_callback,
raw_cmd: cmd,
},
None => DrawCmd::Elements {
count: cmd.ElemCount as usize,
cmd_params,
raw_cmd: cmd as *const sys::ImDrawCmd,
},
}
})
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct DrawCmdParams {
pub clip_rect: [f32; 4],
pub texture_id: TextureId,
pub vtx_offset: usize,
pub idx_offset: usize,
}
#[derive(Clone, Debug)]
pub enum DrawCmd {
Elements {
count: usize,
cmd_params: DrawCmdParams,
raw_cmd: *const sys::ImDrawCmd,
},
ResetRenderState,
RawCallback {
callback: unsafe extern "C" fn(*const sys::ImDrawList, cmd: *const sys::ImDrawCmd),
raw_cmd: *const sys::ImDrawCmd,
},
}
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct DrawVert {
pub pos: [f32; 2],
pub uv: [f32; 2],
pub col: u32,
}
const _: [(); std::mem::size_of::<sys::ImDrawVert>()] = [(); std::mem::size_of::<DrawVert>()];
const _: [(); std::mem::align_of::<sys::ImDrawVert>()] = [(); std::mem::align_of::<DrawVert>()];
impl DrawVert {
pub fn new(pos: [f32; 2], uv: [f32; 2], col: u32) -> Self {
Self { pos, uv, col }
}
pub fn from_rgba(pos: [f32; 2], uv: [f32; 2], rgba: [u8; 4]) -> Self {
let col = ((rgba[3] as u32) << 24)
| ((rgba[2] as u32) << 16)
| ((rgba[1] as u32) << 8)
| (rgba[0] as u32);
Self { pos, uv, col }
}
pub fn rgba(&self) -> [u8; 4] {
[
(self.col & 0xFF) as u8,
((self.col >> 8) & 0xFF) as u8,
((self.col >> 16) & 0xFF) as u8,
((self.col >> 24) & 0xFF) as u8,
]
}
}
pub type DrawIdx = u16;
const _: [(); std::mem::size_of::<sys::ImDrawIdx>()] = [(); std::mem::size_of::<DrawIdx>()];
const _: [(); std::mem::align_of::<sys::ImDrawIdx>()] = [(); std::mem::align_of::<DrawIdx>()];
pub struct OwnedDrawData {
draw_data: *mut sys::ImDrawData,
_no_send_sync: PhantomData<Rc<()>>,
}
impl OwnedDrawData {
#[inline]
pub fn draw_data(&self) -> Option<&DrawData> {
if !self.draw_data.is_null() {
Some(unsafe { &*(self.draw_data as *const DrawData) })
} else {
None
}
}
}
impl Default for OwnedDrawData {
#[inline]
fn default() -> Self {
Self {
draw_data: std::ptr::null_mut(),
_no_send_sync: PhantomData,
}
}
}
impl From<&DrawData> for OwnedDrawData {
fn from(value: &DrawData) -> Self {
unsafe {
let result = sys::ImDrawData_ImDrawData();
if result.is_null() {
panic!("Failed to allocate ImDrawData for OwnedDrawData");
}
let source_ptr = RawWrapper::raw(value);
(*result).Valid = source_ptr.Valid;
(*result).TotalIdxCount = source_ptr.TotalIdxCount;
(*result).TotalVtxCount = source_ptr.TotalVtxCount;
(*result).DisplayPos = source_ptr.DisplayPos;
(*result).DisplaySize = source_ptr.DisplaySize;
(*result).FramebufferScale = source_ptr.FramebufferScale;
(*result).OwnerViewport = source_ptr.OwnerViewport;
(*result).CmdListsCount = 0;
if source_ptr.CmdListsCount > 0 && !source_ptr.CmdLists.Data.is_null() {
for i in 0..(source_ptr.CmdListsCount as usize) {
let src_list = *source_ptr.CmdLists.Data.add(i);
if !src_list.is_null() {
let cloned = sys::ImDrawList_CloneOutput(src_list);
if !cloned.is_null() {
sys::ImDrawData_AddDrawList(result, cloned);
}
}
}
}
(*result).Textures = source_ptr.Textures;
OwnedDrawData {
draw_data: result,
_no_send_sync: PhantomData,
}
}
}
}
impl Drop for OwnedDrawData {
fn drop(&mut self) {
unsafe {
if !self.draw_data.is_null() {
if !(*self.draw_data).CmdLists.Data.is_null() {
for i in 0..(*self.draw_data).CmdListsCount as usize {
let ptr = *(*self.draw_data).CmdLists.Data.add(i);
if !ptr.is_null() {
sys::ImDrawList_destroy(ptr);
}
}
}
sys::ImDrawData_destroy(self.draw_data);
self.draw_data = std::ptr::null_mut();
}
}
}
}
pub struct TextureIterator<'a> {
ptr: *const *mut sys::ImTextureData,
end: *const *mut sys::ImTextureData,
_phantom: std::marker::PhantomData<&'a crate::texture::TextureData>,
}
impl<'a> TextureIterator<'a> {
pub(crate) unsafe fn new(
ptr: *const *mut sys::ImTextureData,
end: *const *mut sys::ImTextureData,
) -> Self {
Self {
ptr,
end,
_phantom: std::marker::PhantomData,
}
}
}
impl<'a> Iterator for TextureIterator<'a> {
type Item = TextureDataMut<'a>;
fn next(&mut self) -> Option<Self::Item> {
while self.ptr < self.end {
let texture_ptr = unsafe { *self.ptr };
self.ptr = unsafe { self.ptr.add(1) };
if texture_ptr.is_null() {
continue;
}
if TEXTURE_DATA_BORROWED
.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
.is_err()
{
panic!(
"TextureData is already mutably borrowed; \
do not hold items from DrawData::textures()/PlatformIo::textures() across iterations"
);
}
return Some(TextureDataMut {
raw: texture_ptr,
_phantom: PhantomData,
});
}
None
}
}
impl<'a> std::iter::FusedIterator for TextureIterator<'a> {}
pub struct TextureDataMut<'a> {
raw: *mut sys::ImTextureData,
_phantom: PhantomData<&'a mut crate::texture::TextureData>,
}
impl Drop for TextureDataMut<'_> {
fn drop(&mut self) {
TEXTURE_DATA_BORROWED.store(false, Ordering::Release);
}
}
impl std::ops::Deref for TextureDataMut<'_> {
type Target = crate::texture::TextureData;
fn deref(&self) -> &Self::Target {
unsafe { &*(self.raw as *const crate::texture::TextureData) }
}
}
impl std::ops::DerefMut for TextureDataMut<'_> {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { &mut *(self.raw as *mut crate::texture::TextureData) }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn draw_data_textures_empty_is_safe() {
let mut textures_vec: crate::internal::ImVector<*mut sys::ImTextureData> =
crate::internal::ImVector::default();
let draw_data = DrawData {
valid: false,
cmd_lists_count: 0,
total_idx_count: 0,
total_vtx_count: 0,
cmd_lists: crate::internal::ImVector::default(),
display_pos: [0.0, 0.0],
display_size: [0.0, 0.0],
framebuffer_scale: [1.0, 1.0],
owner_viewport: std::ptr::null_mut(),
textures: &mut textures_vec,
};
assert_eq!(draw_data.textures().count(), 0);
assert_eq!(draw_data.textures_count(), 0);
let mut textures_vec: crate::internal::ImVector<*mut sys::ImTextureData> =
crate::internal::ImVector {
size: 1,
data: std::ptr::null_mut(),
..crate::internal::ImVector::default()
};
let draw_data = DrawData {
valid: false,
cmd_lists_count: 0,
total_idx_count: 0,
total_vtx_count: 0,
cmd_lists: crate::internal::ImVector::default(),
display_pos: [0.0, 0.0],
display_size: [0.0, 0.0],
framebuffer_scale: [1.0, 1.0],
owner_viewport: std::ptr::null_mut(),
textures: &mut textures_vec,
};
assert_eq!(draw_data.textures().count(), 0);
assert_eq!(draw_data.textures_count(), 0);
assert!(draw_data.texture(0).is_none());
}
}