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 u8::try_from((u16::from(color[0]) * u16::from(color[0])) >> 8)
264 .expect("(u16 >> 8) fits into u8"),
265 u8::try_from((u16::from(color[1]) * u16::from(color[1])) >> 8)
266 .expect("(u16 >> 8) fits into u8"),
267 u8::try_from((u16::from(color[2]) * u16::from(color[2])) >> 8)
268 .expect("(u16 >> 8) fits into u8"),
269 color[3],
270 ];
271 bitmap_data.extend_from_slice(&with_premultiplied_alpha);
272 } else {
273 bitmap_data.extend_from_slice(&color);
274 }
275 }
276 }
277
278 Ok(Self {
279 width: data.width,
280 height: data.height,
281 bitmap_data,
282 hotspot_x: data.hot_spot_x,
283 hotspot_y: data.hot_spot_y,
284 })
285 }
286}
287
288#[derive(Clone, Copy)]
289struct Stride {
290 length: usize,
291 data_bytes: usize,
292 padding: usize,
293}
294
295impl Stride {
296 fn from_bits(bits: usize) -> Stride {
297 let length = bit_stride_size_align_u16(bits);
298 let data_bytes = bit_stride_size_align_u8(bits);
299 Stride {
300 length,
301 data_bytes,
302 padding: length - data_bytes,
303 }
304 }
305}
306
307struct BitmaskStrideReader {
308 current_byte: u8,
309 read_bits: usize,
310 read_stide_bytes: usize,
311 stride_data_bytes: usize,
312 stride_padding: usize,
313}
314
315impl BitmaskStrideReader {
316 fn new(stride: Stride) -> Self {
317 Self {
318 current_byte: 0,
319 read_bits: 8,
320 read_stide_bytes: 0,
321 stride_data_bytes: stride.data_bytes,
322 stride_padding: stride.padding,
323 }
324 }
325
326 fn next_bit(&mut self, cursor: &mut ReadCursor<'_>) -> u8 {
327 if self.read_bits == 8 {
328 self.read_bits = 0;
329
330 if self.read_stide_bytes == self.stride_data_bytes {
331 self.read_stide_bytes = 0;
332 cursor.read_slice(self.stride_padding);
333 }
334
335 self.current_byte = cursor.read_u8();
336 }
337
338 let bit = (self.current_byte >> (7 - self.read_bits)) & 1;
339 self.read_bits += 1;
340 bit
341 }
342}
343
344enum ColorStrideReader {
345 Color {
346 bpp: u16,
348 read_stide_bytes: usize,
349 stride_data_bytes: usize,
350 stride_padding: usize,
351 },
352 Bitmask(BitmaskStrideReader),
353}
354
355impl ColorStrideReader {
356 fn new(bpp: u16, stride: Stride) -> Result<Self, PointerError> {
357 Ok(match bpp {
358 1 => Self::Bitmask(BitmaskStrideReader::new(stride)),
359 bpp => Self::Color {
360 bpp: {
361 if !SUPPORTED_COLOR_BPP[1..].contains(&bpp) {
363 return Err(PointerError::NotSupportedBpp { bpp });
364 }
365
366 bpp
367 },
368 read_stide_bytes: 0,
369 stride_data_bytes: stride.data_bytes,
370 stride_padding: stride.padding,
371 },
372 })
373 }
374
375 fn next_pixel(&mut self, cursor: &mut ReadCursor<'_>) -> [u8; 4] {
376 match self {
377 ColorStrideReader::Color {
378 bpp,
379 read_stide_bytes,
380 stride_data_bytes,
381 stride_padding,
382 } => {
383 if read_stide_bytes == stride_data_bytes {
384 *read_stide_bytes = 0;
385 cursor.read_slice(*stride_padding);
386 }
387
388 match bpp {
389 16 => {
390 *read_stide_bytes += 2;
391 let color_16bit = cursor.read_u16();
392 let [r, g, b] = rdp_16bit_to_rgb(color_16bit);
393 [r, g, b, 0xff]
394 }
395 24 => {
396 *read_stide_bytes += 3;
397
398 let color_24bit = cursor.read_array::<3>();
399 [color_24bit[2], color_24bit[1], color_24bit[0], 0xff]
400 }
401 32 => {
402 *read_stide_bytes += 4;
403 let color_32bit = cursor.read_array::<4>();
404 [color_32bit[2], color_32bit[1], color_32bit[0], color_32bit[3]]
405 }
406 _ => unreachable!("per the invariant on self.bpp, this path is unreachable"),
407 }
408 }
409 ColorStrideReader::Bitmask(bitask) => {
410 if bitask.next_bit(cursor) == 1 {
411 [0xff, 0xff, 0xff, 0xff]
412 } else {
413 [0, 0, 0, 0xff]
414 }
415 }
416 }
417 }
418}
419
420fn bit_stride_size_align_u8(size_bits: usize) -> usize {
421 size_bits.div_ceil(8)
422}
423
424fn bit_stride_size_align_u16(size_bits: usize) -> usize {
425 size_bits.div_ceil(16) * 2
426}
427
428struct PointerData<'a> {
430 width: u16,
431 height: u16,
432 xor_bpp: u16,
433 xor_mask: &'a [u8],
434 and_mask: &'a [u8],
435 hot_spot_x: u16,
436 hot_spot_y: u16,
437}