1use crate::sys::{self, DisplayPixelFormat};
2use crate::{SCREEN_HEIGHT, SCREEN_WIDTH};
3use core::{ffi::c_void, ptr};
4
5const BYTES_PER_PIXEL: usize = 4;
7
8const NUM_PIXELS: usize = (SCREEN_WIDTH * SCREEN_HEIGHT) as usize;
9
10#[repr(C, packed)]
11#[derive(Clone, Copy)]
12struct BmpHeader {
13 pub file_type: [u8; 2],
14 pub file_size: u32,
15 pub reserved_1: u16,
16 pub reserved_2: u16,
17 pub image_data_start: u32,
18 pub dib_header_size: u32,
19 pub image_width: u32,
20 pub image_height: u32,
21 pub color_planes: u16,
22 pub bpp: u16,
23 pub compression: u32,
24 pub image_data_len: u32,
25 pub print_resolution_x: u32,
26 pub print_resolution_y: u32,
27 pub palette_color_count: u32,
28 pub important_colors: u32,
29}
30
31impl BmpHeader {
32 const BYTES: usize = core::mem::size_of::<Self>();
33
34 fn to_bytes(self) -> [u8; Self::BYTES] {
35 unsafe { core::mem::transmute(self) }
36 }
37}
38
39fn rgba_to_bgra(rgba: u32) -> u32 {
40 core::intrinsics::bswap(rgba << 8 | rgba >> 24)
43}
44
45fn rgb565_to_bgra(rgb565: u16) -> u32 {
46 let rgb565 = rgb565 as u32;
47
48 (((rgb565 & 0x1f) << 16) * 0x100 / 0x20)
50 | (((rgb565 & 0x7e0) << 3) * 0x100 / 0x40)
51 | (((rgb565 & 0xf800) >> 11) * 0x100 / 0x20)
52 | 0xff00_0000
53}
54
55fn rgba5551_to_bgra(rgba5551: u16) -> u32 {
56 let rgba5551 = rgba5551 as u32;
57
58 (((rgba5551 & 0x1f) << 16) * 0x100 / 0x20)
60 | (((rgba5551 & 0x3e0) << 3) * 0x100 / 0x20)
61 | (((rgba5551 & 0x7c00) >> 10) * 0x100 / 0x20)
62 | (((rgba5551 & 0x8000) >> 15) * 0xff00_0000)
63}
64
65fn rgba4444_to_bgra(rgba4444: u16) -> u32 {
66 let rgba4444 = rgba4444 as u32;
67
68 (((rgba4444 & 0x000f) << 16) * 0x100 / 0x10)
70 | (((rgba4444 & 0x00f0) << 4) * 0x100 / 0x10)
71 | (((rgba4444 & 0x0f00) >> 8) * 0x100 / 0x10)
72 | (((rgba4444 & 0xf000) << 12) * 0x100 / 0x10)
73}
74
75pub fn screenshot_argb_be() -> alloc::vec::Vec<u32> {
77 let mut screenshot_buffer = alloc::vec![0; NUM_PIXELS];
78 let mut buffer_width: usize = 0;
79 let mut pixel_format = DisplayPixelFormat::Psm5650;
80 let mut top_addr: *mut c_void = ptr::null_mut();
81
82 unsafe {
83 sys::sceDisplayGetFrameBuf(
84 &mut top_addr,
85 &mut buffer_width,
86 &mut pixel_format,
87 sys::DisplaySetBufSync::Immediate,
88 );
89 }
90
91 if top_addr as u32 & 0x80000000 != 0 {
95 top_addr = (top_addr as u32 | 0xA0000000) as _;
97 } else {
98 top_addr = (top_addr as u32 | 0x40000000) as _;
100 }
101
102 for x in 0..SCREEN_WIDTH {
103 for y in 0..SCREEN_HEIGHT {
104 let bgra = match pixel_format {
106 sys::DisplayPixelFormat::Psm8888 => {
107 let rgba = unsafe {
108 *(top_addr as *mut u32).add(x as usize + y as usize * buffer_width)
109 };
110
111 rgba_to_bgra(rgba)
112 }
113
114 sys::DisplayPixelFormat::Psm5650 => {
115 let rgb565 = unsafe {
116 *(top_addr as *mut u16).add(x as usize + y as usize * buffer_width)
117 };
118
119 rgb565_to_bgra(rgb565)
120 }
121
122 sys::DisplayPixelFormat::Psm5551 => {
123 let rgba5551 = unsafe {
124 *(top_addr as *mut u16).add(x as usize + y as usize * buffer_width)
125 };
126
127 rgba5551_to_bgra(rgba5551)
128 }
129
130 sys::DisplayPixelFormat::Psm4444 => {
131 let rgba4444 = unsafe {
132 *(top_addr as *mut u16).add(x as usize + y as usize * buffer_width)
133 };
134
135 rgba4444_to_bgra(rgba4444)
136 }
137 };
138
139 let y_inv = SCREEN_HEIGHT - y - 1;
141 screenshot_buffer[x as usize + y_inv as usize * SCREEN_WIDTH as usize] = bgra;
142 }
143 }
144
145 screenshot_buffer
146}
147
148pub fn screenshot_bmp() -> alloc::vec::Vec<u8> {
150 let mut screenshot_buffer = alloc::vec![0; BmpHeader::BYTES + NUM_PIXELS * BYTES_PER_PIXEL];
151
152 let payload = screenshot_argb_be();
153
154 let bmp_header = BmpHeader {
155 file_type: *b"BM",
156 file_size: BmpHeader::BYTES as u32 + payload.len() as u32 * 4,
157 reserved_1: 0,
158 reserved_2: 0,
159 image_data_start: BmpHeader::BYTES as u32,
160 dib_header_size: 40,
161 image_width: SCREEN_WIDTH,
162 image_height: SCREEN_HEIGHT,
163 color_planes: 1,
164 bpp: 32,
165 compression: 0,
166 image_data_len: payload.len() as u32 * 4,
167 print_resolution_x: 2835, print_resolution_y: 2835, palette_color_count: 0,
170 important_colors: 0,
171 };
172
173 screenshot_buffer[0..BmpHeader::BYTES].copy_from_slice(&bmp_header.to_bytes());
174
175 unsafe {
176 core::ptr::copy_nonoverlapping(
177 &payload[0] as *const _ as _,
178 &mut screenshot_buffer[BmpHeader::BYTES] as *mut u8,
179 NUM_PIXELS * BYTES_PER_PIXEL,
180 );
181 }
182
183 screenshot_buffer
184}