diff options
Diffstat (limited to 'src/xcursor.rs')
-rw-r--r-- | src/xcursor.rs | 101 |
1 files changed, 101 insertions, 0 deletions
diff --git a/src/xcursor.rs b/src/xcursor.rs new file mode 100644 index 0000000..ff6c3d6 --- /dev/null +++ b/src/xcursor.rs @@ -0,0 +1,101 @@ +#[derive(Debug, Clone)] +pub struct ImageInfo { + pub r#type: u32, + pub subtype: u32, + pub width: u32, + pub height: u32, + pub xhot: u32, + pub yhot: u32, + pub delay: u32, + pub data: Vec<u8>, +} + +pub struct XCursorEncoder { + pub images: Vec<ImageInfo>, +} + +const MAGIC: [u8; 4] = [0x58, 0x63, 0x75, 0x72]; +const IMAGE_HEADER: [u8; 4] = [0x02, 0x00, 0xfd, 0xff]; + +impl XCursorEncoder { + pub fn new(images: Vec<ImageInfo>) -> Self { + XCursorEncoder { images } + } + + fn image_pos(&self, position: usize) -> usize { + let mut val = 12 * self.images.len() + 16; + for n in 0..position { + val += 36 + ((self.images[n].width as usize) * (self.images[n].height as usize) * 4) + } + val + } + + pub fn pack(&mut self) -> Vec<u8> { + fn insert_bytes(data: &mut Vec<u8>, new_data: impl IntoIterator<Item = u8>) { + for int in new_data { + data.push(int); + } + } + fn insert_int(data: &mut Vec<u8>, int: u32) { + data.push((int & 0xff) as u8); + data.push(((int >> 8) & 0xff) as u8); + data.push(((int >> 16) & 0xff) as u8); + data.push(((int >> 24) & 0xff) as u8); + } + + let mut data: Vec<u8> = Vec::new(); + + // File Header + { + // MAGIC string ("Xcur") + insert_bytes(&mut data, MAGIC); + // CARD32 bytes in this header + insert_int(&mut data, 16); + // CARD32 file version + insert_int(&mut data, 1); + // CARD32 number of toc entries + insert_int(&mut data, self.images.len().try_into().unwrap()); + } + + // ntoc entries + { + let mut img_idx: usize = 0; + for img in self.images.clone().into_iter() { + // Some header + insert_bytes(&mut data, IMAGE_HEADER); + // CARD32 type-specific label - size for images + insert_int(&mut data, img.r#type); + // CARD32 absolute byte position of table in file + insert_int(&mut data, self.image_pos(img_idx).try_into().unwrap()); + + img_idx += 1; + } + } + + // images + { + for img in self.images.clone().into_iter() { + // Header Size (36) + insert_int(&mut data, 36); + // Image Type + insert_bytes(&mut data, IMAGE_HEADER); + // Subtype, for nominal size + insert_int(&mut data, img.subtype); + // Version + insert_int(&mut data, 1); + // Image dimensions + insert_int(&mut data, img.width); + insert_int(&mut data, img.height); + // Cursor positioning + insert_int(&mut data, img.xhot); + insert_int(&mut data, img.yhot); + // Milliseconds till next frame + insert_int(&mut data, img.delay); + // Raw image data + insert_bytes(&mut data, img.data); + } + } + + data + } +} |