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