Skip to main content

ui/
clipimg.rs

1// clipimg -- (K1) images on the clipboard                            [src/ui/ui_clipimg.s]
2//
3//   clip_set_image(sf) -> 0 / -1        sf (FMT_ARGB straight, FMT_PARGB or FMT_XRGB) as
4//                                       CLIP_IMAGE: PNG (deflate level 1) + a 32-bit BMP
5//                                       (BITMAPV5HEADER, BI_BITFIELDS, straight alpha)
6//   clip_get_image() -> Ok(sf) / Err    the clipboard image (CLIP_IMAGE: PNG, else the DIB /
7//                                       BMP) decoded into a new FMT_ARGB surface (straight,
8//                                       like paint.net layers); ERR_FAIL (-1) = none
9//   clip_bmp_from_sf(sf) -> BMP file    the BMP of clip_set_image
10//
11// A CLIP_IMAGE clipboard record (the asm's CLIPIMG: PNG and BMP pointers) travels as bytes:
12// u64 PNG length (LE), the PNG, u64 BMP length, the BMP (clipimg_record / clipimg_parse);
13// plat::clip_set(CLIP_IMAGE, ..) takes this record.
14
15use alloc::vec::Vec;
16
17use super::consts::*;
18use super::Ui;
19use gfx::{Surface, FMT_ARGB, FMT_PARGB, FMT_XRGB};
20
21const V5_SIZE: usize = 124;
22const HDR_SIZE: usize = 14 + V5_SIZE;
23const PNG_CLIP_LEVEL: u32 = 1;
24
25/// the CLIP_IMAGE record: u64 PNG length, PNG, u64 BMP length, BMP
26pub fn clipimg_record(png: Option<&[u8]>, bmp: Option<&[u8]>) -> Vec<u8> {
27    let mut r = Vec::new();
28    for part in [png, bmp] {
29        let p = part.unwrap_or(&[]);
30        r.extend_from_slice(&(p.len() as u64).to_le_bytes());
31        r.extend_from_slice(p);
32    }
33    r
34}
35
36/// the parts of a CLIP_IMAGE record (an empty part = none); None when it is malformed
37pub fn clipimg_parse(r: &[u8]) -> Option<(Option<&[u8]>, Option<&[u8]>)> {
38    let mut parts = [None, None];
39    let mut o = 0usize;
40    for p in parts.iter_mut() {
41        let n = u64::from_le_bytes(r.get(o..o + 8)?.try_into().ok()?) as usize;
42        o += 8;
43        let d = r.get(o..o.checked_add(n)?)?;
44        o += n;
45        *p = if n == 0 { None } else { Some(d) };
46    }
47    Some((parts[0], parts[1]))
48}
49
50/// clip_bmp_from_sf(sf) -> the BMP file of clip_set_image (None: an empty / huge surface or
51/// another format)
52pub fn clip_bmp_from_sf(sf: &Surface) -> Option<Vec<u8>> {
53    let (w, h) = (sf.w as u32, sf.h as u32);
54    if w.wrapping_sub(1) >= 65535 || h.wrapping_sub(1) >= 65535 {
55        return None;
56    }
57    if sf.fmt != FMT_XRGB && sf.fmt != FMT_PARGB && sf.fmt != FMT_ARGB {
58        return None;
59    }
60    let row = w as usize * 4;
61    let size = row * h as usize + HDR_SIZE;
62    if size > u32::MAX as usize {
63        return None;
64    }
65    let mut b = Vec::new();
66    b.try_reserve_exact(size).ok()?;
67    b.resize(size, 0);
68    let put32 = |b: &mut [u8], o: usize, v: u32| b[o..o + 4].copy_from_slice(&v.to_le_bytes());
69    b[0] = b'B';
70    b[1] = b'M';
71    put32(&mut b, 2, size as u32);
72    put32(&mut b, 10, HDR_SIZE as u32);
73    put32(&mut b, 14, V5_SIZE as u32);
74    put32(&mut b, 18, w);
75    put32(&mut b, 22, h); // bottom-up
76    b[26] = 1;
77    b[28] = 32;
78    put32(&mut b, 30, 3); // BI_BITFIELDS
79    put32(&mut b, 34, (size - HDR_SIZE) as u32);
80    put32(&mut b, 38, 2835);
81    put32(&mut b, 42, 2835);
82    put32(&mut b, 54, 0x00FF_0000);
83    put32(&mut b, 58, 0x0000_FF00);
84    put32(&mut b, 62, 0x0000_00FF);
85    put32(&mut b, 66, 0xFF00_0000);
86    put32(&mut b, 70, 0x7352_4742); // LCS_sRGB
87    put32(&mut b, 122, 4); // LCS_GM_IMAGES
88    // rows bottom-up
89    for y in 0..h as usize {
90        let d = HDR_SIZE + (h as usize - y - 1) * row;
91        let s = y * sf.stride as usize;
92        let (dst, src) = (&mut b[d..d + row], &sf.px[s..s + row]);
93        if sf.fmt == FMT_PARGB {
94            gfx::straight::unpremul_row(dst, src);
95        } else {
96            dst.copy_from_slice(src);
97            if sf.fmt == FMT_XRGB {
98                for x in 0..w as usize {
99                    dst[x * 4 + 3] = 255; // XRGB: alpha 255
100                }
101            }
102        }
103    }
104    Some(b)
105}
106
107impl Ui {
108    /// ui_clip_set_image(sf) -> 0 / -1: sf as CLIP_IMAGE (PNG level 1 + BMP)
109    pub fn clip_set_image(&mut self, sf: &Surface) -> i32 {
110        let png = img_png::enc::encode_classic(sf, PNG_CLIP_LEVEL);
111        let bmp = clip_bmp_from_sf(sf);
112        let Some(png) = png else { return -1 }; // no PNG = no memory: nothing is set
113        let rec = clipimg_record(Some(&png), bmp.as_deref());
114        self.clip_set(CLIP_IMAGE, &rec)
115    }
116
117    /// ui_clip_get_image -> the clipboard image in a new FMT_ARGB surface, or ERR_* (-1 =
118    /// no image)
119    pub fn clip_get_image(&mut self) -> Result<Surface, i32> {
120        let Some(data) = self.clip_get(CLIP_IMAGE) else { return Err(-1) };
121        let dec = img_core::ImgDec { flags: img_core::IMGD_STRAIGHT, frame: 0, hint: 0, maxpx: 0, maxmem: 0, cancel: None };
122        img_core::io::decode_ex(&data, Some(&dec), None)
123    }
124}