1use png::{AdaptiveFilterType, BitDepth, ColorType, Compression, Encoder};
15use std::io::Write;
16use win_wrap::{
17 common::HWND,
18 ext::ToBytesExt,
19 graphic::{
20 bit_blt, create_compatible_bitmap, create_compatible_dc, delete_dc, delete_object, get_dc,
21 get_di_bits, release_dc, select_object, BITMAPFILEHEADER, BITMAPINFOHEADER, BI_RGB,
22 DIB_RGB_COLORS, RGBQUAD, SRCCOPY,
23 },
24};
25
26pub enum ImageCompressionFormat {
30 BMP,
31 PNG,
32}
33
34pub fn snapshot(
43 h_wnd: Option<HWND>,
44 left: i32,
45 top: i32,
46 with: i32,
47 height: i32,
48) -> Option<(Vec<Vec<RGBQUAD>>, BITMAPINFOHEADER, Option<Vec<RGBQUAD>>)> {
49 let h_dc = get_dc(h_wnd);
50 if h_dc.is_invalid() {
51 return None;
52 }
53 let h_mem_dc = create_compatible_dc(Some(h_dc));
54 if h_mem_dc.is_invalid() {
55 release_dc(h_wnd, h_dc);
56 return None;
57 }
58 let h_bm = create_compatible_bitmap(h_dc, with, height);
59 if h_bm.is_invalid() {
60 delete_dc(h_mem_dc);
61 release_dc(h_wnd, h_dc);
62 }
63 let h_old_obj = select_object(h_mem_dc, h_bm.into());
64 let res = bit_blt(h_mem_dc, 0, 0, with, height, Some(h_dc), left, top, SRCCOPY);
65 let data = if res {
66 let (pixels, mut bm_header, color_table) =
67 get_di_bits(h_dc, h_bm, 0, height as u32, DIB_RGB_COLORS);
68 bm_header.biCompression = BI_RGB.0;
69 Some((pixels, bm_header, color_table))
70 } else {
71 None
72 };
73 if !h_old_obj.is_invalid() {
74 select_object(h_mem_dc, h_old_obj);
75 }
76 delete_object(h_bm.into());
77 delete_dc(h_mem_dc);
78 release_dc(h_wnd, h_dc);
79 data
80}
81
82pub fn snapshot_bytes(
92 h_wnd: Option<HWND>,
93 left: i32,
94 top: i32,
95 width: i32,
96 height: i32,
97 format: ImageCompressionFormat,
98) -> Option<Vec<u8>> {
99 let Some((mut pixels, bm_header, color_table)) = snapshot(h_wnd, left, top, width, height)
100 else {
101 return None;
102 };
103 match format {
104 ImageCompressionFormat::BMP => {
105 let offset = bm_header.biSize
106 + size_of::<BITMAPFILEHEADER>() as u32
107 + bm_header.biClrUsed * size_of::<RGBQUAD>() as u32;
108 let header = BITMAPFILEHEADER {
109 bfType: 0x4d42,
110 bfSize: bm_header.biSizeImage + offset,
111 bfReserved1: 0,
112 bfReserved2: 0,
113 bfOffBits: offset,
114 };
115 let mut v = Vec::<u8>::with_capacity((bm_header.biSize + offset) as usize);
116 v.write(header.to_bytes()).unwrap();
117 v.write(bm_header.to_bytes()).unwrap();
118 if let Some(color_table) = color_table {
119 for i in color_table.iter() {
120 v.write(i.to_bytes()).unwrap();
121 }
122 }
123 for i in pixels.iter() {
124 for j in i.iter() {
125 v.write(j.to_bytes()).unwrap();
126 }
127 }
128 Some(v)
129 }
130 ImageCompressionFormat::PNG => {
131 let mut v2 = Vec::with_capacity((bm_header.biSize + bm_header.biSizeImage) as usize);
132 let mut encoder =
133 Encoder::new(&mut v2, bm_header.biWidth as u32, bm_header.biHeight as u32);
134 encoder.set_color(ColorType::Rgba);
135 encoder.set_depth(BitDepth::Eight);
136 encoder.set_compression(Compression::Best);
137 encoder.set_adaptive_filter(AdaptiveFilterType::Adaptive);
138 if let Ok(mut w) = encoder.write_header() {
139 let mut buf = Vec::with_capacity(bm_header.biSizeImage as usize);
140 if bm_header.biHeight > 0 {
141 pixels.reverse();
143 }
144 for i in pixels.iter() {
145 for j in i.iter() {
146 buf.write(j.to_bytes()).unwrap();
147 }
148 }
149 w.write_image_data(&buf).unwrap();
150 w.finish().unwrap();
151 }
152 Some(v2)
153 }
154 }
155}
156
157#[cfg(test)]
158mod test_screen {
159 use std::{fs::OpenOptions, io::Write};
160
161 use win_wrap::common::get_desktop_window;
162
163 use crate::screen::{snapshot_bytes, ImageCompressionFormat};
164
165 #[test]
166 fn main() {
167 let Some(data) = snapshot_bytes(
168 Some(get_desktop_window()),
169 20,
170 20,
171 1300,
172 620,
173 ImageCompressionFormat::PNG,
174 ) else {
175 return;
176 };
177 OpenOptions::new()
178 .create(true)
179 .write(true)
180 .open("screen.png")
181 .unwrap()
182 .write(&data)
183 .unwrap();
184 }
185}