1use ironrdp_core::ReadCursor;
23use ironrdp_pdu::pointer::{ColorPointerAttribute, LargePointerAttribute, PointerAttribute};
24use thiserror::Error;
25
26use crate::color_conversion::rdp_16bit_to_rgb;
27
28#[derive(Debug, Error)]
29pub enum PointerError {
30 #[error("invalid pointer xorMask size. Expected: {expected}, actual: {actual}")]
31 InvalidXorMaskSize { expected: usize, actual: usize },
32 #[error("invalid pointer andMask size. Expected: {expected}, actual: {actual}")]
33 InvalidAndMaskSize { expected: usize, actual: usize },
34 #[error("not supported pointer bpp: {bpp}")]
35 NotSupportedBpp { bpp: u16 },
36 #[error(transparent)]
37 Pdu(#[from] ironrdp_pdu::PduError),
38}
39
40#[derive(Debug)]
42pub struct DecodedPointer {
43 pub width: u16,
44 pub height: u16,
45 pub hotspot_x: u16,
46 pub hotspot_y: u16,
47 pub bitmap_data: Vec<u8>,
48}
49
50#[derive(Clone, Copy, Debug)]
52pub enum PointerBitmapTarget {
53 Software,
60 Accelerated,
63}
64
65impl PointerBitmapTarget {
66 fn should_premultiply_alpha(self) -> bool {
67 match self {
68 Self::Software => true,
69 Self::Accelerated => false,
70 }
71 }
72
73 fn should_invert_pixels_using_check_pattern(self) -> bool {
74 match self {
75 Self::Software => false,
76 Self::Accelerated => true,
77 }
78 }
79}
80
81impl DecodedPointer {
82 pub fn new_invisible() -> Self {
83 Self {
84 width: 0,
85 height: 0,
86 bitmap_data: Vec::new(),
87 hotspot_x: 0,
88 hotspot_y: 0,
89 }
90 }
91
92 pub fn decode_pointer_attribute(
93 src: &PointerAttribute<'_>,
94 target: PointerBitmapTarget,
95 ) -> Result<Self, PointerError> {
96 Self::decode_pointer(
97 PointerData {
98 width: src.color_pointer.width,
99 height: src.color_pointer.height,
100 xor_bpp: src.xor_bpp,
101 xor_mask: src.color_pointer.xor_mask,
102 and_mask: src.color_pointer.and_mask,
103 hot_spot_x: src.color_pointer.hot_spot.x,
104 hot_spot_y: src.color_pointer.hot_spot.y,
105 },
106 target,
107 )
108 }
109
110 pub fn decode_color_pointer_attribute(
111 src: &ColorPointerAttribute<'_>,
112 target: PointerBitmapTarget,
113 ) -> Result<Self, PointerError> {
114 Self::decode_pointer(
115 PointerData {
116 width: src.width,
117 height: src.height,
118 xor_bpp: 24,
119 xor_mask: src.xor_mask,
120 and_mask: src.and_mask,
121 hot_spot_x: src.hot_spot.x,
122 hot_spot_y: src.hot_spot.y,
123 },
124 target,
125 )
126 }
127
128 pub fn decode_large_pointer_attribute(
129 src: &LargePointerAttribute<'_>,
130 target: PointerBitmapTarget,
131 ) -> Result<Self, PointerError> {
132 Self::decode_pointer(
133 PointerData {
134 width: src.width,
135 height: src.height,
136 xor_bpp: src.xor_bpp,
137 xor_mask: src.xor_mask,
138 and_mask: src.and_mask,
139 hot_spot_x: src.hot_spot.x,
140 hot_spot_y: src.hot_spot.y,
141 },
142 target,
143 )
144 }
145
146 fn decode_pointer(data: PointerData<'_>, target: PointerBitmapTarget) -> Result<Self, PointerError> {
147 const SUPPORTED_COLOR_BPP: [u16; 4] = [1, 16, 24, 32];
148
149 if data.width == 0 || data.height == 0 {
150 return Ok(Self::new_invisible());
151 }
152
153 if !SUPPORTED_COLOR_BPP.contains(&data.xor_bpp) {
154 return Err(PointerError::NotSupportedBpp { bpp: data.xor_bpp });
157 }
158
159 let flip_vertical = data.xor_bpp != 1;
160
161 let and_stride = Stride::from_bits(data.width.into());
162 let xor_stride = Stride::from_bits(usize::from(data.width) * usize::from(data.xor_bpp));
163
164 if data.xor_mask.len() != xor_stride.length * usize::from(data.height) {
165 return Err(PointerError::InvalidXorMaskSize {
166 expected: xor_stride.length * usize::from(data.height),
167 actual: data.xor_mask.len(),
168 });
169 }
170
171 let default_and_mask = vec![0x00; and_stride.length * usize::from(data.height)];
172 let mut and_mask = data.and_mask;
173 if and_mask.is_empty() {
174 and_mask = &default_and_mask;
175 } else if and_mask.len() != and_stride.length * usize::from(data.height) {
176 return Err(PointerError::InvalidAndMaskSize {
177 expected: and_stride.length * usize::from(data.height),
178 actual: data.and_mask.len(),
179 });
180 }
181
182 let mut bitmap_data = Vec::new();
183
184 for row_idx in 0..data.height {
185 let (mut xor_stride_cursor, mut and_stride_cursor) = if flip_vertical {
187 let xor_stride_cursor =
188 ReadCursor::new(&data.xor_mask[usize::from(data.height - row_idx - 1) * xor_stride.length..]);
189 let and_stride_cursor =
190 ReadCursor::new(&and_mask[usize::from(data.height - row_idx - 1) * and_stride.length..]);
191 (xor_stride_cursor, and_stride_cursor)
192 } else {
193 let xor_stride_cursor = ReadCursor::new(&data.xor_mask[usize::from(row_idx) * xor_stride.length..]);
194 let and_stride_cursor = ReadCursor::new(&and_mask[usize::from(row_idx) * and_stride.length..]);
195 (xor_stride_cursor, and_stride_cursor)
196 };
197
198 let mut color_reader = ColorStrideReader::new(data.xor_bpp, xor_stride);
199 let mut bitmask_reader = BitmaskStrideReader::new(and_stride);
200
201 let compute_inverted_pixel = if target.should_invert_pixels_using_check_pattern() {
202 |row_idx: u16, col_idx: u16| -> [u8; 4] {
203 if (row_idx + col_idx) % 2 == 0 {
205 [0xff, 0xff, 0xff, 0xff]
206 } else {
207 [0x00, 0x00, 0x00, 0xff]
208 }
209 }
210 } else {
211 |_, _| [0xFF, 0xFF, 0xFF, 0x00]
212 };
213
214 for col_idx in 0..data.width {
215 let and_bit = bitmask_reader.next_bit(&mut and_stride_cursor);
216 let color = color_reader.next_pixel(&mut xor_stride_cursor);
217
218 if and_bit == 1 && color == [0, 0, 0, 0xff] {
219 bitmap_data.extend_from_slice(&[0, 0, 0, 0]);
222 } else if and_bit == 1 && color == [0xff, 0xff, 0xff, 0xff] {
223 bitmap_data.extend_from_slice(&compute_inverted_pixel(row_idx, col_idx));
225 } else if target.should_premultiply_alpha() {
226 let with_premultiplied_alpha = [
228 ((color[0] as u16 * color[0] as u16) >> 8) as u8,
229 ((color[1] as u16 * color[1] as u16) >> 8) as u8,
230 ((color[2] as u16 * color[2] as u16) >> 8) as u8,
231 color[3],
232 ];
233 bitmap_data.extend_from_slice(&with_premultiplied_alpha);
234 } else {
235 bitmap_data.extend_from_slice(&color);
236 }
237 }
238 }
239
240 Ok(Self {
241 width: data.width,
242 height: data.height,
243 bitmap_data,
244 hotspot_x: data.hot_spot_x,
245 hotspot_y: data.hot_spot_y,
246 })
247 }
248}
249
250#[derive(Clone, Copy)]
251struct Stride {
252 length: usize,
253 data_bytes: usize,
254 padding: usize,
255}
256
257impl Stride {
258 fn from_bits(bits: usize) -> Stride {
259 let length = bit_stride_size_align_u16(bits);
260 let data_bytes = bit_stride_size_align_u8(bits);
261 Stride {
262 length,
263 data_bytes,
264 padding: length - data_bytes,
265 }
266 }
267}
268
269struct BitmaskStrideReader {
270 current_byte: u8,
271 read_bits: usize,
272 read_stide_bytes: usize,
273 stride_data_bytes: usize,
274 stride_padding: usize,
275}
276
277impl BitmaskStrideReader {
278 fn new(stride: Stride) -> Self {
279 Self {
280 current_byte: 0,
281 read_bits: 8,
282 read_stide_bytes: 0,
283 stride_data_bytes: stride.data_bytes,
284 stride_padding: stride.padding,
285 }
286 }
287
288 fn next_bit(&mut self, cursor: &mut ReadCursor<'_>) -> u8 {
289 if self.read_bits == 8 {
290 self.read_bits = 0;
291
292 if self.read_stide_bytes == self.stride_data_bytes {
293 self.read_stide_bytes = 0;
294 cursor.read_slice(self.stride_padding);
295 }
296
297 self.current_byte = cursor.read_u8();
298 }
299
300 let bit = (self.current_byte >> (7 - self.read_bits)) & 1;
301 self.read_bits += 1;
302 bit
303 }
304}
305
306enum ColorStrideReader {
307 Color {
308 bpp: u16,
309 read_stide_bytes: usize,
310 stride_data_bytes: usize,
311 stride_padding: usize,
312 },
313 Bitmask(BitmaskStrideReader),
314}
315
316impl ColorStrideReader {
317 fn new(bpp: u16, stride: Stride) -> Self {
318 match bpp {
319 1 => Self::Bitmask(BitmaskStrideReader::new(stride)),
320 bpp => Self::Color {
321 bpp,
322 read_stide_bytes: 0,
323 stride_data_bytes: stride.data_bytes,
324 stride_padding: stride.padding,
325 },
326 }
327 }
328
329 fn next_pixel(&mut self, cursor: &mut ReadCursor<'_>) -> [u8; 4] {
330 match self {
331 ColorStrideReader::Color {
332 bpp,
333 read_stide_bytes,
334 stride_data_bytes,
335 stride_padding,
336 } => {
337 if read_stide_bytes == stride_data_bytes {
338 *read_stide_bytes = 0;
339 cursor.read_slice(*stride_padding);
340 }
341
342 match bpp {
343 16 => {
344 *read_stide_bytes += 2;
345 let color_16bit = cursor.read_u16();
346 let [r, g, b] = rdp_16bit_to_rgb(color_16bit);
347 [r, g, b, 0xff]
348 }
349 24 => {
350 *read_stide_bytes += 3;
351
352 let color_24bit = cursor.read_array::<3>();
353 [color_24bit[2], color_24bit[1], color_24bit[0], 0xff]
354 }
355 32 => {
356 *read_stide_bytes += 4;
357 let color_32bit = cursor.read_array::<4>();
358 [color_32bit[2], color_32bit[1], color_32bit[0], color_32bit[3]]
359 }
360 _ => panic!("BUG: should be validated in the calling code"),
361 }
362 }
363 ColorStrideReader::Bitmask(bitask) => {
364 if bitask.next_bit(cursor) == 1 {
365 [0xff, 0xff, 0xff, 0xff]
366 } else {
367 [0, 0, 0, 0xff]
368 }
369 }
370 }
371 }
372}
373
374fn bit_stride_size_align_u8(size_bits: usize) -> usize {
375 (size_bits + 7) / 8
376}
377
378fn bit_stride_size_align_u16(size_bits: usize) -> usize {
379 ((size_bits + 15) / 16) * 2
380}
381
382struct PointerData<'a> {
384 width: u16,
385 height: u16,
386 xor_bpp: u16,
387 xor_mask: &'a [u8],
388 and_mask: &'a [u8],
389 hot_spot_x: u16,
390 hot_spot_y: u16,
391}