use std::mem::size_of;
use std::slice;
use crate::internal::{RawCast, RawWrapper};
use crate::math::MintVec2;
use crate::render::renderer::TextureId;
use crate::sys;
#[repr(C)]
pub struct DrawData {
valid: bool,
cmd_lists_count: i32,
pub total_idx_count: i32,
pub total_vtx_count: i32,
cmd_lists: *mut *mut DrawList,
pub display_pos: [f32; 2],
pub display_size: [f32; 2],
pub framebuffer_scale: [f32; 2],
#[cfg(feature = "docking")]
owner_viewport: *mut sys::ImGuiViewport,
}
unsafe impl RawCast<sys::ImDrawData> for DrawData {}
impl DrawData {
#[inline]
pub fn draw_lists(&self) -> DrawListIterator<'_> {
unsafe {
DrawListIterator {
iter: self.cmd_lists().iter(),
}
}
}
#[inline]
pub fn draw_lists_count(&self) -> usize {
self.cmd_lists_count.try_into().unwrap()
}
#[inline]
pub(crate) unsafe fn cmd_lists(&self) -> &[*const DrawList] {
slice::from_raw_parts(
self.cmd_lists as *const *const DrawList,
self.cmd_lists_count as usize,
)
}
#[doc(alias = "DeIndexAllBuffers")]
pub fn deindex_all_buffers(&mut self) {
unsafe {
sys::ImDrawData_DeIndexAllBuffers(self.raw_mut());
}
}
#[doc(alias = "ScaleClipRects")]
pub fn scale_clip_rects(&mut self, fb_scale: MintVec2) {
unsafe {
sys::ImDrawData_ScaleClipRects(self.raw_mut(), fb_scale.into());
}
}
}
pub struct DrawListIterator<'a> {
iter: std::slice::Iter<'a, *const DrawList>,
}
impl<'a> Iterator for DrawListIterator<'a> {
type Item = &'a DrawList;
fn next(&mut self) -> Option<Self::Item> {
self.iter.next().map(|&ptr| unsafe { &*ptr })
}
}
#[test]
#[cfg(test)]
fn test_drawdata_memory_layout() {
use std::mem;
assert_eq!(
mem::size_of::<DrawData>(),
mem::size_of::<sys::ImDrawData>()
);
assert_eq!(
mem::align_of::<DrawData>(),
mem::align_of::<sys::ImDrawData>()
);
use sys::ImDrawData;
macro_rules! assert_field_offset {
($l:ident, $r:ident) => {
assert_eq!(
memoffset::offset_of!(DrawData, $l),
memoffset::offset_of!(ImDrawData, $r)
);
};
}
assert_field_offset!(valid, Valid);
assert_field_offset!(cmd_lists, CmdLists);
assert_field_offset!(cmd_lists_count, CmdListsCount);
assert_field_offset!(total_idx_count, TotalIdxCount);
assert_field_offset!(total_vtx_count, TotalVtxCount);
assert_field_offset!(display_pos, DisplayPos);
assert_field_offset!(display_size, DisplaySize);
assert_field_offset!(framebuffer_scale, FramebufferScale);
}
#[repr(transparent)]
pub struct DrawList(sys::ImDrawList);
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 cmd_buffer(&self) -> &[sys::ImDrawCmd] {
slice::from_raw_parts(
self.0.CmdBuffer.Data as *const sys::ImDrawCmd,
self.0.CmdBuffer.Size as usize,
)
}
#[inline]
pub fn idx_buffer(&self) -> &[DrawIdx] {
unsafe {
slice::from_raw_parts(
self.0.IdxBuffer.Data as *const DrawIdx,
self.0.IdxBuffer.Size as usize,
)
}
}
#[inline]
pub fn vtx_buffer(&self) -> &[DrawVert] {
unsafe {
slice::from_raw_parts(
self.0.VtxBuffer.Data as *const DrawVert,
self.0.VtxBuffer.Size as usize,
)
}
}
pub unsafe fn transmute_vtx_buffer<VTy: Copy>(&self) -> &[VTy] {
assert_eq!(
core::mem::size_of::<VTy>(),
core::mem::size_of::<DrawVert>(),
);
assert!(core::mem::align_of::<VTy>() <= core::mem::align_of::<DrawVert>());
slice::from_raw_parts(self.0.VtxBuffer.Data.cast(), self.0.VtxBuffer.Size as usize)
}
#[inline]
pub fn commands(&self) -> DrawCmdIterator<'_> {
unsafe {
DrawCmdIterator {
iter: self.cmd_buffer().iter(),
}
}
}
}
pub struct DrawCmdIterator<'a> {
iter: std::slice::Iter<'a, sys::ImDrawCmd>,
}
impl<'a> Iterator for DrawCmdIterator<'a> {
type Item = DrawCmd;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.iter.next().map(|cmd| {
let cmd_params = DrawCmdParams {
clip_rect: cmd.ClipRect.into(),
texture_id: TextureId::from(cmd.TextureId),
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,
},
}
})
}
}
pub type DrawIdx = sys::ImDrawIdx;
#[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,
}
pub enum DrawCmd {
Elements {
count: usize,
cmd_params: DrawCmdParams,
},
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: [u8; 4],
}
#[test]
#[cfg(test)]
fn test_drawvert_memory_layout() {
use std::mem;
assert_eq!(
mem::size_of::<DrawVert>(),
mem::size_of::<sys::ImDrawVert>()
);
assert_eq!(
mem::align_of::<DrawVert>(),
mem::align_of::<sys::ImDrawVert>()
);
use sys::ImDrawVert;
macro_rules! assert_field_offset {
($l:ident, $r:ident) => {
assert_eq!(
memoffset::offset_of!(DrawVert, $l),
memoffset::offset_of!(ImDrawVert, $r)
);
};
}
assert_field_offset!(pos, pos);
assert_field_offset!(uv, uv);
assert_field_offset!(col, col);
}
pub struct OwnedDrawData {
draw_data: *mut sys::ImDrawData,
}
impl OwnedDrawData {
#[inline]
pub fn draw_data(&self) -> Option<&DrawData> {
if !self.draw_data.is_null() {
Some(unsafe { DrawData::from_raw(&*self.draw_data) })
} else {
None
}
}
#[cfg(feature = "docking")]
unsafe fn copy_docking_properties(source: &sys::ImDrawData, dest: *mut sys::ImDrawData) {
(*dest).OwnerViewport = source.OwnerViewport;
}
#[cfg(not(feature = "docking"))]
unsafe fn copy_docking_properties(_source: &sys::ImDrawData, _dest: *mut sys::ImDrawData) {}
}
impl Default for OwnedDrawData {
#[inline]
fn default() -> Self {
Self {
draw_data: std::ptr::null_mut(),
}
}
}
impl From<&DrawData> for OwnedDrawData {
fn from(value: &DrawData) -> Self {
OwnedDrawData {
draw_data: unsafe {
let other_ptr = value.raw();
let result = sys::ImDrawData_ImDrawData();
(*result).Valid = other_ptr.Valid;
(*result).TotalIdxCount = other_ptr.TotalIdxCount;
(*result).TotalVtxCount = other_ptr.TotalVtxCount;
(*result).DisplayPos = other_ptr.DisplayPos;
(*result).DisplaySize = other_ptr.DisplaySize;
(*result).FramebufferScale = other_ptr.FramebufferScale;
(*result).CmdListsCount = other_ptr.CmdListsCount;
(*result).CmdLists = sys::igMemAlloc(
size_of::<*mut sys::ImDrawList>() * other_ptr.CmdListsCount as usize,
) as *mut *mut sys::ImDrawList;
OwnedDrawData::copy_docking_properties(other_ptr, result);
let mut current_draw_list = (*result).CmdLists;
for i in 0..other_ptr.CmdListsCount as usize {
*current_draw_list = sys::ImDrawList_CloneOutput(*other_ptr.CmdLists.add(i));
current_draw_list = current_draw_list.add(1);
}
result
},
}
}
}
impl Drop for OwnedDrawData {
fn drop(&mut self) {
unsafe {
if !self.draw_data.is_null() {
if !(*self.draw_data).CmdLists.is_null() {
for i in 0..(*self.draw_data).CmdListsCount as usize {
let ptr = *(*self.draw_data).CmdLists.add(i);
if !ptr.is_null() {
sys::ImDrawList_destroy(ptr);
}
}
sys::igMemFree((*self.draw_data).CmdLists as *mut std::ffi::c_void);
}
sys::ImDrawData_destroy(self.draw_data);
self.draw_data = std::ptr::null_mut();
}
}
}
}
#[test]
#[cfg(test)]
fn test_owneddrawdata_default() {
let default = OwnedDrawData::default();
assert!(default.draw_data().is_none());
}
#[test]
#[cfg(test)]
fn test_owneddrawdata_from_drawdata() {
let (_guard, _ctx) = crate::test::test_ctx();
let mut draw_list = sys::ImDrawList::default();
let mut draw_lists_raw = [std::ptr::addr_of_mut!(draw_list)];
let draw_data_raw = sys::ImDrawData {
Valid: true,
CmdListsCount: 1,
CmdLists: draw_lists_raw.as_mut_ptr(),
TotalIdxCount: 123,
TotalVtxCount: 456,
DisplayPos: sys::ImVec2 { x: 123.0, y: 456.0 },
DisplaySize: sys::ImVec2 { x: 789.0, y: 012.0 },
FramebufferScale: sys::ImVec2 { x: 3.0, y: 7.0 },
#[cfg(feature = "docking")]
OwnerViewport: unsafe { std::ptr::null_mut::<sys::ImGuiViewport>().offset(123) },
};
let draw_data = unsafe { DrawData::from_raw(&draw_data_raw) };
let owned_draw_data: OwnedDrawData = draw_data.into();
let inner_draw_data = owned_draw_data.draw_data();
assert!(inner_draw_data.is_some());
let owned_draw_data_raw = unsafe { inner_draw_data.unwrap().raw() };
assert_eq!(draw_data_raw.Valid, owned_draw_data_raw.Valid);
assert_eq!(
draw_data_raw.CmdListsCount,
owned_draw_data_raw.CmdListsCount
);
assert!(!draw_data_raw.CmdLists.is_null());
assert_eq!(
draw_data_raw.TotalIdxCount,
owned_draw_data_raw.TotalIdxCount
);
assert_eq!(
draw_data_raw.TotalVtxCount,
owned_draw_data_raw.TotalVtxCount
);
assert_eq!(draw_data_raw.DisplayPos, owned_draw_data_raw.DisplayPos);
assert_eq!(draw_data_raw.DisplaySize, owned_draw_data_raw.DisplaySize);
assert_eq!(
draw_data_raw.FramebufferScale,
owned_draw_data_raw.FramebufferScale
);
#[cfg(feature = "docking")]
assert_eq!(
draw_data_raw.OwnerViewport,
owned_draw_data_raw.OwnerViewport
);
}
#[test]
#[cfg(test)]
fn test_owneddrawdata_drop() {
let (_guard, _ctx) = crate::test::test_ctx();
let initial_allocation_count = unsafe { (*sys::igGetIO()).MetricsActiveAllocations };
let mut draw_list = sys::ImDrawList::default();
let mut draw_lists_raw = [std::ptr::addr_of_mut!(draw_list)];
let draw_data_raw = sys::ImDrawData {
Valid: true,
CmdListsCount: 1,
CmdLists: draw_lists_raw.as_mut_ptr(),
TotalIdxCount: 0,
TotalVtxCount: 0,
DisplayPos: sys::ImVec2 { x: 0.0, y: 0.0 },
DisplaySize: sys::ImVec2 { x: 800.0, y: 600.0 },
FramebufferScale: sys::ImVec2 { x: 1.0, y: 1.0 },
#[cfg(feature = "docking")]
OwnerViewport: std::ptr::null_mut(),
};
let draw_data = unsafe { DrawData::from_raw(&draw_data_raw) };
{
let _owned_draw_data: OwnedDrawData = draw_data.into();
let cloned_allocation_count = unsafe { (*sys::igGetIO()).MetricsActiveAllocations };
assert!(cloned_allocation_count > initial_allocation_count);
}
let final_allocation_count = unsafe { (*sys::igGetIO()).MetricsActiveAllocations };
assert_eq!(initial_allocation_count, final_allocation_count);
}