FreeRDP
Loading...
Searching...
No Matches
gdi/gfx.c
1
22#include <freerdp/config.h>
23
24#include "../core/update.h"
25
26#include <winpr/assert.h>
27#include <winpr/cast.h>
28
29#include <freerdp/api.h>
30#include <freerdp/log.h>
31#include <freerdp/gdi/gfx.h>
32#include <freerdp/gdi/region.h>
33#include <freerdp/utils/gfx.h>
34#include <math.h>
35
36#define TAG FREERDP_TAG("gdi")
37
38/* Maximum number of rectangles kept in gdiGfxSurface::invalidRegion before it is replaced by its
39 * bounding box. */
40#define GDI_GFX_MAX_INVALID_RECTS 256
41
42WINPR_ATTR_NODISCARD
43static BOOL is_rect_valid(const RECTANGLE_16* rect, size_t width, size_t height)
44{
45 if (!rect)
46 return FALSE;
47 if ((rect->left > rect->right) || (rect->right > width))
48 return FALSE;
49 if ((rect->top > rect->bottom) || (rect->bottom > height))
50 return FALSE;
51 return TRUE;
52}
53
54WINPR_ATTR_NODISCARD
55static BOOL is_within_surface(const gdiGfxSurface* surface, const RDPGFX_SURFACE_COMMAND* cmd)
56{
57 RECTANGLE_16 rect;
58 if (!surface || !cmd)
59 return FALSE;
60 rect.left = (UINT16)MIN(UINT16_MAX, cmd->left);
61 rect.top = (UINT16)MIN(UINT16_MAX, cmd->top);
62 rect.right = (UINT16)MIN(UINT16_MAX, cmd->right);
63 rect.bottom = (UINT16)MIN(UINT16_MAX, cmd->bottom);
64 if (!is_rect_valid(&rect, surface->width, surface->height))
65 {
66 WLog_ERR(TAG,
67 "Command rect %" PRIu32 "x%" PRIu32 "-%" PRIu32 "x%" PRIu32
68 " not within bounds of %" PRIu32 "x%" PRIu32,
69 rect.left, rect.top, cmd->width, cmd->height, surface->width, surface->height);
70 return FALSE;
71 }
72 if (rect.left > surface->width)
73 return FALSE;
74 if (rect.right > surface->width)
75 return FALSE;
76 if (rect.top > surface->height)
77 return FALSE;
78 if (rect.bottom > surface->height)
79 return FALSE;
80
81 return TRUE;
82}
83
95WINPR_ATTR_NODISCARD
96static BOOL gdi_surface_invalidate_rect(gdiGfxSurface* surface, const RECTANGLE_16* rect)
97{
98 WINPR_ASSERT(surface);
99
100 REGION16* region = &surface->invalidRegion;
101 if (!region16_union_rect(region, region, rect))
102 return FALSE;
103
104 if (region16_n_rects(region) <= GDI_GFX_MAX_INVALID_RECTS)
105 return TRUE;
106
107 const RECTANGLE_16 extents = *region16_extents(region);
108 region16_clear(region);
109 return region16_union_rect(region, region, &extents);
110}
111
112WINPR_ATTR_NODISCARD
113static DWORD gfx_align_scanline(DWORD widthInBytes, DWORD alignment)
114{
115 const UINT32 align = alignment;
116 const UINT32 pad = align - (widthInBytes % alignment);
117 UINT32 scanline = widthInBytes;
118
119 if (align != pad)
120 scanline += pad;
121
122 return scanline;
123}
124
130WINPR_ATTR_NODISCARD
131static UINT gdi_ResetGraphics(RdpgfxClientContext* context,
132 const RDPGFX_RESET_GRAPHICS_PDU* resetGraphics)
133{
134 UINT rc = ERROR_INTERNAL_ERROR;
135 UINT16 count = 0;
136 UINT16* pSurfaceIds = nullptr;
137
138 WINPR_ASSERT(context);
139 WINPR_ASSERT(resetGraphics);
140
141 rdpGdi* gdi = (rdpGdi*)context->custom;
142 WINPR_ASSERT(gdi);
143
144 rdpUpdate* update = gdi->context->update;
145 WINPR_ASSERT(update);
146
147 rdpSettings* settings = gdi->context->settings;
148 WINPR_ASSERT(settings);
149 EnterCriticalSection(&context->mux);
150 const UINT32 DesktopWidth = resetGraphics->width;
151 const UINT32 DesktopHeight = resetGraphics->height;
152
153 WLog_DBG(TAG, "new size: %" PRIu32 "x%" PRIu32, DesktopWidth, DesktopHeight);
154
155 if (!freerdp_settings_set_uint32(settings, FreeRDP_DesktopWidth, DesktopWidth))
156 goto fail;
157 if (!freerdp_settings_set_uint32(settings, FreeRDP_DesktopHeight, DesktopHeight))
158 goto fail;
159
160 if (update)
161 {
162 WINPR_ASSERT(update->DesktopResize);
163
164 /* Keep the lock order of BeginPaint/EndPaint: update lock first, client locks second */
165 rdp_update_lock(update);
166 const BOOL resized = update->DesktopResize(gdi->context);
167 rdp_update_unlock(update);
168 if (!resized)
169 goto fail;
170 }
171
172 WINPR_ASSERT(context->GetSurfaceIds);
173 const UINT res = context->GetSurfaceIds(context, &pSurfaceIds, &count);
174 if (res != CHANNEL_RC_OK)
175 {
176 rc = res;
177 goto fail;
178 }
179
180 for (UINT32 index = 0; index < count; index++)
181 {
182 WINPR_ASSERT(context->GetSurfaceData);
183 gdiGfxSurface* surface =
184 (gdiGfxSurface*)context->GetSurfaceData(context, pSurfaceIds[index]);
185
186 if (!surface)
187 continue;
188
189 memset(surface->data, 0xFF, (size_t)surface->scanline * surface->height);
190 region16_clear(&surface->invalidRegion);
191 }
192
193 free(pSurfaceIds);
194
195 if (!freerdp_settings_get_bool(gdi->context->settings, FreeRDP_DeactivateClientDecoding))
196 {
197 const UINT32 width = (UINT32)MAX(0, gdi->width);
198 const UINT32 height = (UINT32)MAX(0, gdi->height);
199
200 if (!freerdp_client_codecs_reset(
201 context->codecs, freerdp_settings_get_codecs_flags(settings), width, height))
202 {
203 goto fail;
204 }
205 }
206
207 rc = CHANNEL_RC_OK;
208fail:
209 LeaveCriticalSection(&context->mux);
210 return rc;
211}
212
213WINPR_ATTR_NODISCARD
214static UINT gdi_OutputUpdate(rdpGdi* gdi, gdiGfxSurface* surface)
215{
216 UINT rc = ERROR_INTERNAL_ERROR;
217 UINT32 surfaceX = 0;
218 UINT32 surfaceY = 0;
219 RECTANGLE_16 surfaceRect;
220 const RECTANGLE_16* rects = nullptr;
221 UINT32 nbRects = 0;
222 rdpUpdate* update = nullptr;
223
224 WINPR_ASSERT(gdi);
225 WINPR_ASSERT(gdi->context);
226 WINPR_ASSERT(surface);
227
228 update = gdi->context->update;
229 WINPR_ASSERT(update);
230
231 if (gdi->suppressOutput)
232 return CHANNEL_RC_OK;
233
234 surfaceX = surface->outputOriginX;
235 surfaceY = surface->outputOriginY;
236 surfaceRect.left = 0;
237 surfaceRect.top = 0;
238 surfaceRect.right = (UINT16)MIN(UINT16_MAX, surface->mappedWidth);
239 surfaceRect.bottom = (UINT16)MIN(UINT16_MAX, surface->mappedHeight);
240 if (!region16_intersect_rect(&(surface->invalidRegion), &(surface->invalidRegion),
241 &surfaceRect))
242 goto fail;
243
244 const double sx = surface->outputTargetWidth / (double)surface->mappedWidth;
245 const double sy = surface->outputTargetHeight / (double)surface->mappedHeight;
246
247 rects = region16_rects(&surface->invalidRegion, &nbRects);
248 if (!rects || (nbRects == 0))
249 {
250 rc = CHANNEL_RC_OK;
251 goto fail;
252 }
253
254 if (!update_begin_paint(update))
255 goto fail;
256
257 for (UINT32 i = 0; i < nbRects; i++)
258 {
259 const UINT32 nXSrc = rects[i].left;
260 const UINT32 nYSrc = rects[i].top;
261 const double xDst = surfaceX + nXSrc * sx;
262 const double yDst = surfaceY + nYSrc * sy;
263
264 /* A surface can be larger than the output for a while, e.g. right after the desktop
265 * shrank and before the server sent surfaces of the new size: a part outside the
266 * output has nothing to show. */
267 const UINT32 outWidth = (UINT32)MAX(0, gdi->width);
268 const UINT32 outHeight = (UINT32)MAX(0, gdi->height);
269 if ((xDst >= outWidth) || (yDst >= outHeight))
270 continue;
271
272 const UINT32 nXDst = (UINT32)xDst;
273 const UINT32 nYDst = (UINT32)yDst;
274 UINT32 swidth = rects[i].right - rects[i].left;
275 UINT32 sheight = rects[i].bottom - rects[i].top;
276 UINT32 dwidth = (UINT32)(swidth * sx);
277 UINT32 dheight = (UINT32)(sheight * sy);
278
279 /* Clip at the output's edge by cropping the source as well: shrinking only the
280 * destination would turn the clip into a scale, which squeezes the picture and fails
281 * outright in a build without a scaler (no swscale, no cairo), and that failure ends
282 * the graphics channel. */
283 if (dwidth > outWidth - nXDst)
284 {
285 dwidth = outWidth - nXDst;
286 swidth = MAX(1, MIN(swidth, (UINT32)(dwidth / sx)));
287 }
288 if (dheight > outHeight - nYDst)
289 {
290 dheight = outHeight - nYDst;
291 sheight = MAX(1, MIN(sheight, (UINT32)(dheight / sy)));
292 }
293
294 if (!freerdp_image_scale(gdi->primary_buffer, gdi->dstFormat, gdi->stride, nXDst, nYDst,
295 dwidth, dheight, surface->data, surface->format, surface->scanline,
296 nXSrc, nYSrc, swidth, sheight))
297 {
298 rc = CHANNEL_RC_NULL_DATA;
299 goto clear;
300 }
301
302 if (!gdi_InvalidateRegion(gdi->primary->hdc, (INT32)nXDst, (INT32)nYDst, (INT32)dwidth,
303 (INT32)dheight))
304 goto clear;
305 }
306
307 rc = CHANNEL_RC_OK;
308clear:
309
310 if (!update_end_paint(update))
311 rc = ERROR_INTERNAL_ERROR;
312
313fail:
314 region16_clear(&(surface->invalidRegion));
315 return rc;
316}
317
318WINPR_ATTR_NODISCARD
319static UINT gdi_WindowUpdate(RdpgfxClientContext* context, gdiGfxSurface* surface)
320{
321 WINPR_ASSERT(context);
322 WINPR_ASSERT(surface);
323 return IFCALLRESULT(CHANNEL_RC_OK, context->UpdateWindowFromSurface, context, surface);
324}
325
326WINPR_ATTR_NODISCARD
327static UINT gdi_UpdateSurfaces(RdpgfxClientContext* context)
328{
329 UINT16 count = 0;
330 UINT16* pSurfaceIds = nullptr;
331
332 WINPR_ASSERT(context);
333
334 rdpGdi* gdi = (rdpGdi*)context->custom;
335 WINPR_ASSERT(gdi);
336
337 EnterCriticalSection(&context->mux);
338
339 WINPR_ASSERT(context->GetSurfaceIds);
340 UINT status = context->GetSurfaceIds(context, &pSurfaceIds, &count);
341 if (status != CHANNEL_RC_OK)
342 goto fail;
343
344 for (UINT32 index = 0; index < count; index++)
345 {
346 WINPR_ASSERT(context->GetSurfaceData);
347 gdiGfxSurface* surface =
348 (gdiGfxSurface*)context->GetSurfaceData(context, pSurfaceIds[index]);
349
350 if (!surface)
351 continue;
352
353 /* Already handled in UpdateSurfaceArea callbacks */
354 if (context->UpdateSurfaceArea)
355 {
356 if (surface->handleInUpdateSurfaceArea)
357 continue;
358 }
359
360 if (surface->outputMapped)
361 status = gdi_OutputUpdate(gdi, surface);
362 else if (surface->windowMapped)
363 status = gdi_WindowUpdate(context, surface);
364
365 if (status != CHANNEL_RC_OK)
366 break;
367 }
368
369fail:
370 free(pSurfaceIds);
371 LeaveCriticalSection(&context->mux);
372 return status;
373}
374
380WINPR_ATTR_NODISCARD
381static UINT gdi_StartFrame(RdpgfxClientContext* context, const RDPGFX_START_FRAME_PDU* startFrame)
382{
383 rdpGdi* gdi = nullptr;
384
385 WINPR_ASSERT(context);
386 WINPR_ASSERT(startFrame);
387
388 gdi = (rdpGdi*)context->custom;
389 WINPR_ASSERT(gdi);
390 gdi->inGfxFrame = TRUE;
391 gdi->frameId = startFrame->frameId;
392 return CHANNEL_RC_OK;
393}
394
395WINPR_ATTR_NODISCARD
396static UINT gdi_call_update_surfaces(RdpgfxClientContext* context)
397{
398 WINPR_ASSERT(context);
399 return IFCALLRESULT(CHANNEL_RC_OK, context->UpdateSurfaces, context);
400}
401
407WINPR_ATTR_NODISCARD
408static UINT gdi_EndFrame(RdpgfxClientContext* context,
409 WINPR_ATTR_UNUSED const RDPGFX_END_FRAME_PDU* endFrame)
410{
411 WINPR_ASSERT(context);
412 WINPR_ASSERT(endFrame);
413
414 rdpGdi* gdi = (rdpGdi*)context->custom;
415 WINPR_ASSERT(gdi);
416 const UINT status = gdi_call_update_surfaces(context);
417 gdi->inGfxFrame = FALSE;
418 return status;
419}
420
421WINPR_ATTR_NODISCARD
422static UINT gdi_interFrameUpdate(rdpGdi* gdi, RdpgfxClientContext* context)
423{
424 WINPR_ASSERT(gdi);
425 UINT status = CHANNEL_RC_OK;
426 if (!gdi->inGfxFrame)
427 status = gdi_call_update_surfaces(context);
428 return status;
429}
430
436WINPR_ATTR_NODISCARD
437static UINT gdi_SurfaceCommand_Uncompressed(rdpGdi* gdi, RdpgfxClientContext* context,
438 const RDPGFX_SURFACE_COMMAND* cmd)
439{
440 UINT status = CHANNEL_RC_OK;
441 gdiGfxSurface* surface = nullptr;
442 RECTANGLE_16 invalidRect;
443 DWORD bpp = 0;
444 size_t size = 0;
445 WINPR_ASSERT(gdi);
446 WINPR_ASSERT(context);
447 WINPR_ASSERT(cmd);
448
449 WINPR_ASSERT(context->GetSurfaceData);
450 surface =
451 (gdiGfxSurface*)context->GetSurfaceData(context, (UINT16)MIN(UINT16_MAX, cmd->surfaceId));
452
453 if (!surface)
454 {
455 WLog_ERR(TAG, "unable to retrieve surfaceData for surfaceId=%" PRIu32 "", cmd->surfaceId);
456 return ERROR_NOT_FOUND;
457 }
458
459 if (!is_within_surface(surface, cmd))
460 return ERROR_INVALID_DATA;
461
462 bpp = FreeRDPGetBytesPerPixel(cmd->format);
463 size = 1ull * bpp * cmd->width * cmd->height;
464 if (cmd->length < size)
465 {
466 WLog_ERR(TAG, "Not enough data, got %" PRIu32 ", expected %" PRIuz, cmd->length, size);
467 return ERROR_INVALID_DATA;
468 }
469
470 if (!freerdp_image_copy_no_overlap(surface->data, surface->format, surface->scanline, cmd->left,
471 cmd->top, cmd->width, cmd->height, cmd->data, cmd->format, 0,
472 0, 0, nullptr, FREERDP_FLIP_NONE))
473 return ERROR_INTERNAL_ERROR;
474
475 invalidRect.left = (UINT16)MIN(UINT16_MAX, cmd->left);
476 invalidRect.top = (UINT16)MIN(UINT16_MAX, cmd->top);
477 invalidRect.right = (UINT16)MIN(UINT16_MAX, cmd->right);
478 invalidRect.bottom = (UINT16)MIN(UINT16_MAX, cmd->bottom);
479 if (!gdi_surface_invalidate_rect(surface, &invalidRect))
480 goto fail;
481 status = IFCALLRESULT(CHANNEL_RC_OK, context->UpdateSurfaceArea, context, surface->surfaceId, 1,
482 &invalidRect);
483
484 if (status != CHANNEL_RC_OK)
485 goto fail;
486
487 status = gdi_interFrameUpdate(gdi, context);
488
489fail:
490 return status;
491}
492
493#if defined(WITH_GFX_AV1)
494WINPR_ATTR_NODISCARD
495static UINT gdi_SurfaceCommand_AV1(rdpGdi* gdi, RdpgfxClientContext* context,
496 const RDPGFX_SURFACE_COMMAND* cmd)
497{
498 INT32 rc = 0;
499 UINT status = CHANNEL_RC_OK;
500 gdiGfxSurface* surface = nullptr;
501 RDPGFX_H264_METABLOCK* meta = nullptr;
502 RDPGFX_AVC420_BITMAP_STREAM* bs = nullptr;
503 WINPR_ASSERT(gdi);
504 WINPR_ASSERT(context);
505 WINPR_ASSERT(cmd);
506
507 WINPR_ASSERT(context->GetSurfaceData);
508 surface =
509 (gdiGfxSurface*)context->GetSurfaceData(context, (UINT16)MIN(UINT16_MAX, cmd->surfaceId));
510
511 if (!surface)
512 {
513 WLog_ERR(TAG, "unable to retrieve surfaceData for surfaceId=%" PRIu32 "", cmd->surfaceId);
514 return ERROR_NOT_FOUND;
515 }
516
517 if (!surface->av1)
518 {
519 surface->av1 = freerdp_av1_context_new(FALSE);
520
521 if (!surface->av1)
522 {
523 WLog_ERR(TAG, "unable to create av1 context");
524 return ERROR_NOT_ENOUGH_MEMORY;
525 }
526
527 if (!freerdp_av1_context_reset(surface->av1, surface->width, surface->height))
528 return ERROR_INTERNAL_ERROR;
529 }
530
531 if (!surface->av1)
532 return ERROR_NOT_SUPPORTED;
533
534 if (!is_within_surface(surface, cmd))
535 return ERROR_INVALID_DATA;
536
537 bs = (RDPGFX_AVC420_BITMAP_STREAM*)cmd->extra;
538
539 if (!bs)
540 return ERROR_INTERNAL_ERROR;
541
542 meta = &(bs->meta);
543 rc = freerdp_av1_decompress(surface->av1, bs->data, bs->length, surface->data, surface->format,
544 surface->scanline, surface->width, surface->height,
545 meta->regionRects, meta->numRegionRects);
546
547 if (rc < 0)
548 {
549 WLog_WARN(TAG, "freerdp_av1_decompress failure: %" PRId32 ", ignoring update.", rc);
550 return CHANNEL_RC_OK;
551 }
552
553 for (UINT32 i = 0; i < meta->numRegionRects; i++)
554 {
555 if (!gdi_surface_invalidate_rect(surface, &(meta->regionRects[i])))
556 goto fail;
557 }
558
559 status = IFCALLRESULT(CHANNEL_RC_OK, context->UpdateSurfaceArea, context, surface->surfaceId,
560 meta->numRegionRects, meta->regionRects);
561
562 if (status != CHANNEL_RC_OK)
563 goto fail;
564
565 status = gdi_interFrameUpdate(gdi, context);
566
567fail:
568 return status;
569}
570#endif
571
572#if defined(WITH_GFX_AV2)
573WINPR_ATTR_NODISCARD
574static UINT gdi_SurfaceCommand_AV2(rdpGdi* gdi, RdpgfxClientContext* context,
575 const RDPGFX_SURFACE_COMMAND* cmd)
576{
577 INT32 rc = 0;
578 UINT status = CHANNEL_RC_OK;
579 gdiGfxSurface* surface = nullptr;
580 RDPGFX_H264_METABLOCK* meta = nullptr;
581 RDPGFX_AVC420_BITMAP_STREAM* bs = nullptr;
582 WINPR_ASSERT(gdi);
583 WINPR_ASSERT(context);
584 WINPR_ASSERT(cmd);
585
586 WINPR_ASSERT(context->GetSurfaceData);
587 surface =
588 (gdiGfxSurface*)context->GetSurfaceData(context, (UINT16)MIN(UINT16_MAX, cmd->surfaceId));
589
590 if (!surface)
591 {
592 WLog_ERR(TAG, "unable to retrieve surfaceData for surfaceId=%" PRIu32 "", cmd->surfaceId);
593 return ERROR_NOT_FOUND;
594 }
595
596 if (!surface->av2)
597 {
598 surface->av2 = freerdp_av2_context_new(FALSE);
599
600 if (!surface->av2)
601 {
602 WLog_ERR(TAG, "unable to create av2 context");
603 return ERROR_NOT_ENOUGH_MEMORY;
604 }
605
606 if (!freerdp_av2_context_reset(surface->av2, surface->width, surface->height))
607 return ERROR_INTERNAL_ERROR;
608 }
609
610 if (!surface->av2)
611 return ERROR_NOT_SUPPORTED;
612
613 if (!is_within_surface(surface, cmd))
614 return ERROR_INVALID_DATA;
615
616 bs = (RDPGFX_AVC420_BITMAP_STREAM*)cmd->extra;
617
618 if (!bs)
619 return ERROR_INTERNAL_ERROR;
620
621 meta = &(bs->meta);
622 rc = freerdp_av2_decompress(surface->av2, bs->data, bs->length, surface->data, surface->format,
623 surface->scanline, surface->width, surface->height,
624 meta->regionRects, meta->numRegionRects);
625
626 if (rc < 0)
627 {
628 WLog_WARN(TAG, "freerdp_av2_decompress failure: %" PRId32 ", ignoring update.", rc);
629 return CHANNEL_RC_OK;
630 }
631
632 for (UINT32 i = 0; i < meta->numRegionRects; i++)
633 {
634 if (!region16_union_rect(&(surface->invalidRegion), &(surface->invalidRegion),
635 &(meta->regionRects[i])))
636 goto fail;
637 }
638
639 status = IFCALLRESULT(CHANNEL_RC_OK, context->UpdateSurfaceArea, context, surface->surfaceId,
640 meta->numRegionRects, meta->regionRects);
641
642 if (status != CHANNEL_RC_OK)
643 goto fail;
644
645 status = gdi_interFrameUpdate(gdi, context);
646
647fail:
648 return status;
649}
650#endif
651
657WINPR_ATTR_NODISCARD
658static UINT gdi_SurfaceCommand_RemoteFX(rdpGdi* gdi, RdpgfxClientContext* context,
659 const RDPGFX_SURFACE_COMMAND* cmd)
660{
661 UINT status = ERROR_INTERNAL_ERROR;
662 gdiGfxSurface* surface = nullptr;
663 REGION16 invalidRegion;
664 const RECTANGLE_16* rects = nullptr;
665 UINT32 nrRects = 0;
666 WINPR_ASSERT(gdi);
667 WINPR_ASSERT(context);
668 WINPR_ASSERT(cmd);
669
670 WINPR_ASSERT(context->GetSurfaceData);
671 surface =
672 (gdiGfxSurface*)context->GetSurfaceData(context, (UINT16)MIN(UINT16_MAX, cmd->surfaceId));
673
674 if (!surface)
675 {
676 WLog_ERR(TAG, "unable to retrieve surfaceData for surfaceId=%" PRIu32 "", cmd->surfaceId);
677 return ERROR_NOT_FOUND;
678 }
679
680 if (!is_within_surface(surface, cmd))
681 return ERROR_INVALID_DATA;
682
683 WINPR_ASSERT(surface->codecs);
684 rfx_context_set_pixel_format(surface->codecs->rfx, cmd->format);
685 region16_init(&invalidRegion);
686
687 if (!rfx_process_message(surface->codecs->rfx, cmd->data, cmd->length, cmd->left, cmd->top,
688 surface->data, surface->format, surface->scanline, surface->height,
689 &invalidRegion))
690 {
691 WLog_ERR(TAG, "Failed to process RemoteFX message");
692 goto fail;
693 }
694
695 rects = region16_rects(&invalidRegion, &nrRects);
696 status = IFCALLRESULT(CHANNEL_RC_OK, context->UpdateSurfaceArea, context, surface->surfaceId,
697 nrRects, rects);
698
699 if (status != CHANNEL_RC_OK)
700 goto fail;
701
702 for (UINT32 x = 0; x < nrRects; x++)
703 {
704 if (!gdi_surface_invalidate_rect(surface, &rects[x]))
705 goto fail;
706 }
707
708 status = gdi_interFrameUpdate(gdi, context);
709
710fail:
711 region16_uninit(&invalidRegion);
712 return status;
713}
714
720WINPR_ATTR_NODISCARD
721static UINT gdi_SurfaceCommand_ClearCodec(rdpGdi* gdi, RdpgfxClientContext* context,
722 const RDPGFX_SURFACE_COMMAND* cmd)
723{
724 INT32 rc = 0;
725 UINT status = CHANNEL_RC_OK;
726 gdiGfxSurface* surface = nullptr;
727 RECTANGLE_16 invalidRect;
728 WINPR_ASSERT(gdi);
729 WINPR_ASSERT(context);
730 WINPR_ASSERT(cmd);
731
732 WINPR_ASSERT(context->GetSurfaceData);
733 surface =
734 (gdiGfxSurface*)context->GetSurfaceData(context, (UINT16)MIN(UINT16_MAX, cmd->surfaceId));
735
736 if (!surface)
737 {
738 WLog_ERR(TAG, "unable to retrieve surfaceData for surfaceId=%" PRIu32 "", cmd->surfaceId);
739 return ERROR_NOT_FOUND;
740 }
741
742 if (!is_within_surface(surface, cmd))
743 return ERROR_INVALID_DATA;
744
745 WINPR_ASSERT(surface->codecs);
746 rc = clear_decompress(surface->codecs->clear, cmd->data, cmd->length, cmd->width, cmd->height,
747 surface->data, surface->format, surface->scanline, cmd->left, cmd->top,
748 surface->width, surface->height, &gdi->palette);
749
750 if (rc < 0)
751 {
752 WLog_ERR(TAG, "clear_decompress failure: %" PRId32 "", rc);
753 return ERROR_INTERNAL_ERROR;
754 }
755
756 invalidRect.left = (UINT16)MIN(UINT16_MAX, cmd->left);
757 invalidRect.top = (UINT16)MIN(UINT16_MAX, cmd->top);
758 invalidRect.right = (UINT16)MIN(UINT16_MAX, cmd->right);
759 invalidRect.bottom = (UINT16)MIN(UINT16_MAX, cmd->bottom);
760 if (!gdi_surface_invalidate_rect(surface, &invalidRect))
761 goto fail;
762 status = IFCALLRESULT(CHANNEL_RC_OK, context->UpdateSurfaceArea, context, surface->surfaceId, 1,
763 &invalidRect);
764
765 if (status != CHANNEL_RC_OK)
766 goto fail;
767
768 status = gdi_interFrameUpdate(gdi, context);
769
770fail:
771 return status;
772}
773
779WINPR_ATTR_NODISCARD
780static UINT gdi_SurfaceCommand_Planar(rdpGdi* gdi, RdpgfxClientContext* context,
781 const RDPGFX_SURFACE_COMMAND* cmd)
782{
783 UINT status = CHANNEL_RC_OK;
784 BYTE* DstData = nullptr;
785 gdiGfxSurface* surface = nullptr;
786 RECTANGLE_16 invalidRect;
787 WINPR_ASSERT(gdi);
788 WINPR_ASSERT(context);
789 WINPR_ASSERT(cmd);
790
791 WINPR_ASSERT(context->GetSurfaceData);
792 surface =
793 (gdiGfxSurface*)context->GetSurfaceData(context, (UINT16)MIN(UINT16_MAX, cmd->surfaceId));
794
795 if (!surface)
796 {
797 WLog_ERR(TAG, "unable to retrieve surfaceData for surfaceId=%" PRIu32 "", cmd->surfaceId);
798 return ERROR_NOT_FOUND;
799 }
800
801 DstData = surface->data;
802
803 if (!is_within_surface(surface, cmd))
804 return ERROR_INVALID_DATA;
805
806 if (!freerdp_bitmap_decompress_planar(surface->codecs->planar, cmd->data, cmd->length,
807 cmd->width, cmd->height, DstData, surface->format,
808 surface->scanline, cmd->left, cmd->top, cmd->width,
809 cmd->height, FALSE))
810 return ERROR_INTERNAL_ERROR;
811
812 invalidRect.left = (UINT16)MIN(UINT16_MAX, cmd->left);
813 invalidRect.top = (UINT16)MIN(UINT16_MAX, cmd->top);
814 invalidRect.right = (UINT16)MIN(UINT16_MAX, cmd->right);
815 invalidRect.bottom = (UINT16)MIN(UINT16_MAX, cmd->bottom);
816 if (!gdi_surface_invalidate_rect(surface, &invalidRect))
817 goto fail;
818 status = IFCALLRESULT(CHANNEL_RC_OK, context->UpdateSurfaceArea, context, surface->surfaceId, 1,
819 &invalidRect);
820
821 if (status != CHANNEL_RC_OK)
822 goto fail;
823
824 status = gdi_interFrameUpdate(gdi, context);
825
826fail:
827 return status;
828}
829
835WINPR_ATTR_NODISCARD
836static UINT gdi_SurfaceCommand_AVC420(rdpGdi* gdi, RdpgfxClientContext* context,
837 const RDPGFX_SURFACE_COMMAND* cmd)
838{
839#ifdef WITH_GFX_H264
840 INT32 rc = 0;
841 UINT status = CHANNEL_RC_OK;
842 gdiGfxSurface* surface = nullptr;
843 RDPGFX_H264_METABLOCK* meta = nullptr;
844 RDPGFX_AVC420_BITMAP_STREAM* bs = nullptr;
845 WINPR_ASSERT(gdi);
846 WINPR_ASSERT(context);
847 WINPR_ASSERT(cmd);
848
849 WINPR_ASSERT(context->GetSurfaceData);
850 surface =
851 (gdiGfxSurface*)context->GetSurfaceData(context, (UINT16)MIN(UINT16_MAX, cmd->surfaceId));
852
853 if (!surface)
854 {
855 WLog_ERR(TAG, "unable to retrieve surfaceData for surfaceId=%" PRIu32 "", cmd->surfaceId);
856 return ERROR_NOT_FOUND;
857 }
858
859 if (!surface->h264)
860 {
861 surface->h264 = h264_context_new(FALSE);
862
863 if (!surface->h264)
864 {
865 WLog_ERR(TAG, "unable to create h264 context");
866 return ERROR_NOT_ENOUGH_MEMORY;
867 }
868
869 if (!h264_context_set_option(surface->h264, H264_CONTEXT_OPTION_HW_ACCEL,
870 (UINT32)freerdp_settings_get_bool(gdi->context->settings,
871 FreeRDP_SoftwareGdi) == 0))
872 return ERROR_INTERNAL_ERROR;
873
874 if (!h264_context_reset(surface->h264, surface->width, surface->height))
875 return ERROR_INTERNAL_ERROR;
876 }
877
878 if (!surface->h264)
879 return ERROR_NOT_SUPPORTED;
880
881 if (!is_within_surface(surface, cmd))
882 return ERROR_INVALID_DATA;
883
884 bs = (RDPGFX_AVC420_BITMAP_STREAM*)cmd->extra;
885
886 if (!bs)
887 return ERROR_INTERNAL_ERROR;
888
889 meta = &(bs->meta);
890 rc = avc420_decompress(surface->h264, bs->data, bs->length, surface->data, surface->format,
891 surface->scanline, surface->width, surface->height, meta->regionRects,
892 meta->numRegionRects);
893
894 if (rc < 0)
895 {
896 WLog_WARN(TAG, "avc420_decompress failure: %" PRId32 ", ignoring update.", rc);
897 return CHANNEL_RC_OK;
898 }
899
900 for (UINT32 i = 0; i < meta->numRegionRects; i++)
901 {
902 if (!gdi_surface_invalidate_rect(surface, &(meta->regionRects[i])))
903 goto fail;
904 }
905
906 status = IFCALLRESULT(CHANNEL_RC_OK, context->UpdateSurfaceArea, context, surface->surfaceId,
907 meta->numRegionRects, meta->regionRects);
908
909 if (status != CHANNEL_RC_OK)
910 goto fail;
911
912 status = gdi_interFrameUpdate(gdi, context);
913
914fail:
915 return status;
916#else
917 return ERROR_NOT_SUPPORTED;
918#endif
919}
920
926WINPR_ATTR_NODISCARD
927static UINT gdi_SurfaceCommand_AVC444(rdpGdi* gdi, RdpgfxClientContext* context,
928 const RDPGFX_SURFACE_COMMAND* cmd)
929{
930#ifdef WITH_GFX_H264
931 INT32 rc = 0;
932 UINT status = CHANNEL_RC_OK;
933 gdiGfxSurface* surface = nullptr;
934 RDPGFX_AVC444_BITMAP_STREAM* bs = nullptr;
935 RDPGFX_AVC420_BITMAP_STREAM* avc1 = nullptr;
936 RDPGFX_H264_METABLOCK* meta1 = nullptr;
937 RDPGFX_AVC420_BITMAP_STREAM* avc2 = nullptr;
938 RDPGFX_H264_METABLOCK* meta2 = nullptr;
939 WINPR_ASSERT(gdi);
940 WINPR_ASSERT(context);
941 WINPR_ASSERT(cmd);
942
943 WINPR_ASSERT(context->GetSurfaceData);
944 surface =
945 (gdiGfxSurface*)context->GetSurfaceData(context, (UINT16)MIN(UINT16_MAX, cmd->surfaceId));
946
947 if (!surface)
948 {
949 WLog_ERR(TAG, "unable to retrieve surfaceData for surfaceId=%" PRIu32 "", cmd->surfaceId);
950 return ERROR_NOT_FOUND;
951 }
952
953 if (!surface->h264)
954 {
955 surface->h264 = h264_context_new(FALSE);
956
957 if (!surface->h264)
958 {
959 WLog_ERR(TAG, "unable to create h264 context");
960 return ERROR_NOT_ENOUGH_MEMORY;
961 }
962
963 if (!h264_context_set_option(surface->h264, H264_CONTEXT_OPTION_HW_ACCEL,
964 (UINT32)freerdp_settings_get_bool(gdi->context->settings,
965 FreeRDP_SoftwareGdi) == 0))
966 return ERROR_INTERNAL_ERROR;
967 if (!h264_context_reset(surface->h264, surface->width, surface->height))
968 return ERROR_INTERNAL_ERROR;
969 }
970
971 if (!surface->h264)
972 return ERROR_NOT_SUPPORTED;
973
974 if (!is_within_surface(surface, cmd))
975 return ERROR_INVALID_DATA;
976
977 bs = (RDPGFX_AVC444_BITMAP_STREAM*)cmd->extra;
978
979 if (!bs)
980 return ERROR_INTERNAL_ERROR;
981
982 avc1 = &bs->bitstream[0];
983 avc2 = &bs->bitstream[1];
984 meta1 = &avc1->meta;
985 meta2 = &avc2->meta;
986 rc = avc444_decompress(surface->h264, bs->LC, meta1->regionRects, meta1->numRegionRects,
987 avc1->data, avc1->length, meta2->regionRects, meta2->numRegionRects,
988 avc2->data, avc2->length, surface->data, surface->format,
989 surface->scanline, surface->width, surface->height, cmd->codecId);
990
991 if (rc < 0)
992 {
993 WLog_WARN(TAG, "avc444_decompress failure: %" PRId32 ", ignoring update.", rc);
994 return CHANNEL_RC_OK;
995 }
996
997 for (UINT32 i = 0; i < meta1->numRegionRects; i++)
998 {
999 if (!gdi_surface_invalidate_rect(surface, &(meta1->regionRects[i])))
1000 goto fail;
1001 }
1002
1003 status = IFCALLRESULT(CHANNEL_RC_OK, context->UpdateSurfaceArea, context, surface->surfaceId,
1004 meta1->numRegionRects, meta1->regionRects);
1005
1006 if (status != CHANNEL_RC_OK)
1007 goto fail;
1008
1009 for (UINT32 i = 0; i < meta2->numRegionRects; i++)
1010 {
1011 if (!gdi_surface_invalidate_rect(surface, &(meta2->regionRects[i])))
1012 goto fail;
1013 }
1014
1015 status = IFCALLRESULT(CHANNEL_RC_OK, context->UpdateSurfaceArea, context, surface->surfaceId,
1016 meta2->numRegionRects, meta2->regionRects);
1017
1018 if (status != CHANNEL_RC_OK)
1019 goto fail;
1020
1021 status = gdi_interFrameUpdate(gdi, context);
1022
1023fail:
1024 return status;
1025#else
1026 return ERROR_NOT_SUPPORTED;
1027#endif
1028}
1029
1030WINPR_ATTR_NODISCARD
1031static BOOL gdi_apply_alpha(BYTE* data, UINT32 format, UINT32 stride, RECTANGLE_16* rect,
1032 UINT32 startOffsetX, UINT32 count, BYTE a)
1033{
1034 UINT32 written = 0;
1035 BOOL first = TRUE;
1036 const UINT32 bpp = FreeRDPGetBytesPerPixel(format);
1037 WINPR_ASSERT(rect);
1038
1039 for (size_t y = rect->top; y < rect->bottom; y++)
1040 {
1041 BYTE* line = &data[y * stride];
1042
1043 for (size_t x = first ? rect->left + startOffsetX : rect->left; x < rect->right; x++)
1044 {
1045 BYTE r = 0;
1046 BYTE g = 0;
1047 BYTE b = 0;
1048
1049 if (written == count)
1050 return TRUE;
1051
1052 BYTE* src = &line[x * bpp];
1053 UINT32 color = FreeRDPReadColor(src, format);
1054 FreeRDPSplitColor(color, format, &r, &g, &b, nullptr, nullptr);
1055 color = FreeRDPGetColor(format, r, g, b, a);
1056 FreeRDPWriteColor(src, format, color);
1057 written++;
1058 }
1059
1060 first = FALSE;
1061 }
1062
1063 return TRUE;
1064}
1070WINPR_ATTR_NODISCARD
1071static UINT gdi_SurfaceCommand_Alpha(rdpGdi* gdi, RdpgfxClientContext* context,
1072 const RDPGFX_SURFACE_COMMAND* cmd)
1073{
1074 UINT status = CHANNEL_RC_OK;
1075 UINT16 alphaSig = 0;
1076 UINT16 compressed = 0;
1077 RECTANGLE_16 invalidRect;
1078 wStream buffer = WINPR_C_ARRAY_INIT;
1079 WINPR_ASSERT(gdi);
1080 WINPR_ASSERT(context);
1081 WINPR_ASSERT(cmd);
1082
1083 wStream* s = Stream_StaticConstInit(&buffer, cmd->data, cmd->length);
1084
1085 if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
1086 return ERROR_INVALID_DATA;
1087
1088 WINPR_ASSERT(context->GetSurfaceData);
1089 gdiGfxSurface* surface =
1090 (gdiGfxSurface*)context->GetSurfaceData(context, (UINT16)MIN(UINT16_MAX, cmd->surfaceId));
1091
1092 if (!surface)
1093 {
1094 WLog_ERR(TAG, "unable to retrieve surfaceData for surfaceId=%" PRIu32 "", cmd->surfaceId);
1095 return ERROR_NOT_FOUND;
1096 }
1097
1098 if (!is_within_surface(surface, cmd))
1099 return ERROR_INVALID_DATA;
1100
1101 Stream_Read_UINT16(s, alphaSig);
1102 Stream_Read_UINT16(s, compressed);
1103
1104 if (alphaSig != 0x414C)
1105 return ERROR_INVALID_DATA;
1106
1107 if (compressed == 0)
1108 {
1109 if (!Stream_CheckAndLogRequiredLengthOfSize(TAG, s, cmd->height, cmd->width))
1110 return ERROR_INVALID_DATA;
1111
1112 for (size_t y = cmd->top; y < cmd->top + cmd->height; y++)
1113 {
1114 BYTE* line = &surface->data[y * surface->scanline];
1115
1116 for (size_t x = cmd->left; x < cmd->left + cmd->width; x++)
1117 {
1118 UINT32 color = 0;
1119 BYTE r = 0;
1120 BYTE g = 0;
1121 BYTE b = 0;
1122 BYTE a = 0;
1123 BYTE* src = &line[x * FreeRDPGetBytesPerPixel(surface->format)];
1124 Stream_Read_UINT8(s, a);
1125 color = FreeRDPReadColor(src, surface->format);
1126 FreeRDPSplitColor(color, surface->format, &r, &g, &b, nullptr, nullptr);
1127 color = FreeRDPGetColor(surface->format, r, g, b, a);
1128 FreeRDPWriteColor(src, surface->format, color);
1129 }
1130 }
1131 }
1132 else
1133 {
1134 UINT32 startOffsetX = 0;
1135 RECTANGLE_16 rect = WINPR_C_ARRAY_INIT;
1136 rect.left = (UINT16)MIN(UINT16_MAX, cmd->left);
1137 rect.top = (UINT16)MIN(UINT16_MAX, cmd->top);
1138 rect.right = (UINT16)MIN(UINT16_MAX, cmd->left + cmd->width);
1139 rect.bottom = (UINT16)MIN(UINT16_MAX, cmd->top + cmd->height);
1140
1141 while (rect.top < rect.bottom)
1142 {
1143 UINT32 count = 0;
1144 BYTE a = 0;
1145
1146 if (!Stream_CheckAndLogRequiredLength(TAG, s, 2))
1147 return ERROR_INVALID_DATA;
1148
1149 Stream_Read_UINT8(s, a);
1150 Stream_Read_UINT8(s, count);
1151
1152 if (count >= 0xFF)
1153 {
1154 if (!Stream_CheckAndLogRequiredLength(TAG, s, 2))
1155 return ERROR_INVALID_DATA;
1156
1157 Stream_Read_UINT16(s, count);
1158
1159 if (count >= 0xFFFF)
1160 {
1161 if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
1162 return ERROR_INVALID_DATA;
1163
1164 Stream_Read_UINT32(s, count);
1165 }
1166 }
1167
1168 if (!gdi_apply_alpha(surface->data, surface->format, surface->scanline, &rect,
1169 startOffsetX, count, a))
1170 return ERROR_INTERNAL_ERROR;
1171
1172 startOffsetX += count;
1173
1174 while (startOffsetX >= cmd->width)
1175 {
1176 startOffsetX -= cmd->width;
1177 rect.top++;
1178 }
1179 }
1180 }
1181
1182 invalidRect.left = (UINT16)MIN(UINT16_MAX, cmd->left);
1183 invalidRect.top = (UINT16)MIN(UINT16_MAX, cmd->top);
1184 invalidRect.right = (UINT16)MIN(UINT16_MAX, cmd->right);
1185 invalidRect.bottom = (UINT16)MIN(UINT16_MAX, cmd->bottom);
1186 if (!gdi_surface_invalidate_rect(surface, &invalidRect))
1187 goto fail;
1188 status = IFCALLRESULT(CHANNEL_RC_OK, context->UpdateSurfaceArea, context, surface->surfaceId, 1,
1189 &invalidRect);
1190
1191 if (status != CHANNEL_RC_OK)
1192 goto fail;
1193
1194 status = gdi_interFrameUpdate(gdi, context);
1195
1196fail:
1197 return status;
1198}
1199
1200#if defined(WITH_GFX_FRAME_DUMP)
1201static void dump_cmd(const RDPGFX_SURFACE_COMMAND* cmd, UINT32 frameId)
1202{
1203 static UINT64 xxx = 0;
1204 const char* path = "/tmp/dump/";
1205 WINPR_ASSERT(cmd);
1206 char fname[1024] = WINPR_C_ARRAY_INIT;
1207
1208 if (_snprintf(fname, sizeof(fname), "%s/%08" PRIx64 ".raw", path, xxx++) < 0)
1209 return;
1210
1211 FILE* fp = fopen(fname, "w");
1212 if (!fp)
1213 return;
1214 (void)fprintf(fp, "frameid: %" PRIu32 "\n", frameId);
1215 (void)fprintf(fp, "surfaceId: %" PRIu32 "\n", cmd->surfaceId);
1216 (void)fprintf(fp, "codecId: %" PRIu32 "\n", cmd->codecId);
1217 (void)fprintf(fp, "contextId: %" PRIu32 "\n", cmd->contextId);
1218 (void)fprintf(fp, "format: %" PRIu32 "\n", cmd->format);
1219 (void)fprintf(fp, "left: %" PRIu32 "\n", cmd->left);
1220 (void)fprintf(fp, "top: %" PRIu32 "\n", cmd->top);
1221 (void)fprintf(fp, "right: %" PRIu32 "\n", cmd->right);
1222 (void)fprintf(fp, "bottom: %" PRIu32 "\n", cmd->bottom);
1223 (void)fprintf(fp, "width: %" PRIu32 "\n", cmd->width);
1224 (void)fprintf(fp, "height: %" PRIu32 "\n", cmd->height);
1225 (void)fprintf(fp, "length: %" PRIu32 "\n", cmd->length);
1226
1227 char* bdata = crypto_base64_encode_ex(cmd->data, cmd->length, FALSE);
1228 (void)fprintf(fp, "data: %s\n", bdata);
1229 free(bdata);
1230 fclose(fp);
1231}
1232#endif
1233
1239WINPR_ATTR_NODISCARD
1240static UINT gdi_SurfaceCommand_Progressive(rdpGdi* gdi, RdpgfxClientContext* context,
1241 const RDPGFX_SURFACE_COMMAND* cmd)
1242{
1243 INT32 rc = 0;
1244 UINT status = ERROR_INTERNAL_ERROR;
1245 gdiGfxSurface* surface = nullptr;
1246 REGION16 invalidRegion;
1247 const RECTANGLE_16* rects = nullptr;
1248 UINT32 nrRects = 0;
1254 WINPR_ASSERT(gdi);
1255 WINPR_ASSERT(context);
1256 WINPR_ASSERT(cmd);
1257 const UINT16 surfaceId = (UINT16)MIN(UINT16_MAX, cmd->surfaceId);
1258
1259 WINPR_ASSERT(context->GetSurfaceData);
1260 surface = (gdiGfxSurface*)context->GetSurfaceData(context, surfaceId);
1261
1262 if (!surface)
1263 {
1264 WLog_ERR(TAG, "unable to retrieve surfaceData for surfaceId=%" PRIu32 "", cmd->surfaceId);
1265 return ERROR_NOT_FOUND;
1266 }
1267
1268 if (!is_within_surface(surface, cmd))
1269 return ERROR_INVALID_DATA;
1270
1271 WINPR_ASSERT(surface->codecs);
1272 rc = progressive_create_surface_context(surface->codecs->progressive, surfaceId, surface->width,
1273 surface->height);
1274
1275 if (rc < 0)
1276 {
1277 WLog_ERR(TAG, "progressive_create_surface_context failure: %" PRId32 "", rc);
1278 return ERROR_INTERNAL_ERROR;
1279 }
1280
1281 region16_init(&invalidRegion);
1282
1283 rc = progressive_decompress(surface->codecs->progressive, cmd->data, cmd->length, surface->data,
1284 surface->format, surface->scanline, cmd->left, cmd->top,
1285 &invalidRegion, surfaceId, gdi->frameId);
1286
1287 if (rc < 0)
1288 {
1289 WLog_ERR(TAG, "progressive_decompress failure: %" PRId32 "", rc);
1290 goto fail;
1291 }
1292
1293 rects = region16_rects(&invalidRegion, &nrRects);
1294 status = IFCALLRESULT(CHANNEL_RC_OK, context->UpdateSurfaceArea, context, surface->surfaceId,
1295 nrRects, rects);
1296
1297 if (status != CHANNEL_RC_OK)
1298 goto fail;
1299
1300 status = ERROR_INTERNAL_ERROR;
1301 for (UINT32 x = 0; x < nrRects; x++)
1302 {
1303 if (!gdi_surface_invalidate_rect(surface, &rects[x]))
1304 goto fail;
1305 }
1306
1307 status = gdi_interFrameUpdate(gdi, context);
1308
1309fail:
1310
1311 region16_uninit(&invalidRegion);
1312 return status;
1313}
1314
1320WINPR_ATTR_NODISCARD
1321static UINT gdi_SurfaceCommand(RdpgfxClientContext* context, const RDPGFX_SURFACE_COMMAND* cmd)
1322{
1323 UINT status = CHANNEL_RC_OK;
1324 rdpGdi* gdi = nullptr;
1325
1326 if (!context || !cmd)
1327 return ERROR_INVALID_PARAMETER;
1328
1329 gdi = (rdpGdi*)context->custom;
1330
1331 EnterCriticalSection(&context->mux);
1332 const UINT16 codecId = WINPR_ASSERTING_INT_CAST(UINT16, cmd->codecId);
1333 WLog_Print(gdi->log, WLOG_TRACE,
1334 "surfaceId=%" PRIu32 ", codec=%s [%" PRIu32 "], contextId=%" PRIu32 ", format=%s, "
1335 "left=%" PRIu32 ", top=%" PRIu32 ", right=%" PRIu32 ", bottom=%" PRIu32
1336 ", width=%" PRIu32 ", height=%" PRIu32 " "
1337 "length=%" PRIu32 ", data=%p, extra=%p",
1338 cmd->surfaceId, rdpgfx_get_codec_id_string(codecId), cmd->codecId, cmd->contextId,
1339 FreeRDPGetColorFormatName(cmd->format), cmd->left, cmd->top, cmd->right, cmd->bottom,
1340 cmd->width, cmd->height, cmd->length, (void*)cmd->data, (void*)cmd->extra);
1341#if defined(WITH_GFX_FRAME_DUMP)
1342 dump_cmd(cmd, gdi->frameId);
1343#endif
1344
1345 switch (codecId)
1346 {
1347 case RDPGFX_CODECID_UNCOMPRESSED:
1348 status = gdi_SurfaceCommand_Uncompressed(gdi, context, cmd);
1349 break;
1350
1351#if defined(WITH_GFX_AV1) || defined(WITH_GFX_AV2)
1352 case RDPGFX_CODECID_AV1:
1353 /* the codec ID is shared, the confirmed caps version tells AV1 from AV2 */
1354 switch (context->CapsVersion)
1355 {
1356#if defined(WITH_GFX_AV1)
1357 case RDPGFX_CAPVERSION_FRDP_1:
1358 status = gdi_SurfaceCommand_AV1(gdi, context, cmd);
1359 break;
1360#endif
1361#if defined(WITH_GFX_AV2)
1362 case RDPGFX_CAPVERSION_FRDP_2:
1363 status = gdi_SurfaceCommand_AV2(gdi, context, cmd);
1364 break;
1365#endif
1366 default:
1367 WLog_ERR(TAG,
1368 "SurfaceCommand %s [0x%08" PRIX16 "] not valid with caps 0x%08" PRIX32,
1369 rdpgfx_get_codec_id_string(codecId), codecId, context->CapsVersion);
1370 status = ERROR_INVALID_DATA;
1371 break;
1372 }
1373 break;
1374#endif
1375
1376 case RDPGFX_CODECID_CAVIDEO:
1377 status = gdi_SurfaceCommand_RemoteFX(gdi, context, cmd);
1378 break;
1379
1380 case RDPGFX_CODECID_CLEARCODEC:
1381 status = gdi_SurfaceCommand_ClearCodec(gdi, context, cmd);
1382 break;
1383
1384 case RDPGFX_CODECID_PLANAR:
1385 status = gdi_SurfaceCommand_Planar(gdi, context, cmd);
1386 break;
1387
1388 case RDPGFX_CODECID_AVC420:
1389 status = gdi_SurfaceCommand_AVC420(gdi, context, cmd);
1390 break;
1391
1392 case RDPGFX_CODECID_AVC444v2:
1393 case RDPGFX_CODECID_AVC444:
1394 status = gdi_SurfaceCommand_AVC444(gdi, context, cmd);
1395 break;
1396
1397 case RDPGFX_CODECID_ALPHA:
1398 status = gdi_SurfaceCommand_Alpha(gdi, context, cmd);
1399 break;
1400
1401 case RDPGFX_CODECID_CAPROGRESSIVE:
1402 status = gdi_SurfaceCommand_Progressive(gdi, context, cmd);
1403 break;
1404
1405 case RDPGFX_CODECID_CAPROGRESSIVE_V2:
1406 WLog_WARN(TAG, "SurfaceCommand %s [0x%08" PRIX16 "] not implemented",
1407 rdpgfx_get_codec_id_string(codecId), codecId);
1408 break;
1409
1410 default:
1411 WLog_WARN(TAG, "Invalid SurfaceCommand %s [0x%08" PRIX16 "]",
1412 rdpgfx_get_codec_id_string(codecId), codecId);
1413 break;
1414 }
1415
1416 LeaveCriticalSection(&context->mux);
1417 return status;
1418}
1419
1425WINPR_ATTR_NODISCARD
1426static UINT
1427gdi_DeleteEncodingContext(RdpgfxClientContext* context,
1428 const RDPGFX_DELETE_ENCODING_CONTEXT_PDU* deleteEncodingContext)
1429{
1430 WINPR_ASSERT(context);
1431 WINPR_ASSERT(deleteEncodingContext);
1432 WINPR_UNUSED(context);
1433 WINPR_UNUSED(deleteEncodingContext);
1434 return CHANNEL_RC_OK;
1435}
1436
1442WINPR_ATTR_NODISCARD
1443static UINT gdi_CreateSurface(RdpgfxClientContext* context,
1444 const RDPGFX_CREATE_SURFACE_PDU* createSurface)
1445{
1446 UINT rc = ERROR_INTERNAL_ERROR;
1447 gdiGfxSurface* surface = nullptr;
1448 rdpGdi* gdi = nullptr;
1449 WINPR_ASSERT(context);
1450 WINPR_ASSERT(createSurface);
1451 gdi = (rdpGdi*)context->custom;
1452 WINPR_ASSERT(gdi);
1453 WINPR_ASSERT(gdi->context);
1454 EnterCriticalSection(&context->mux);
1455 surface = (gdiGfxSurface*)calloc(1, sizeof(gdiGfxSurface));
1456
1457 if (!surface)
1458 goto fail;
1459
1460 if (!freerdp_settings_get_bool(gdi->context->settings, FreeRDP_DeactivateClientDecoding))
1461 {
1462 WINPR_ASSERT(context->codecs);
1463 surface->codecs = context->codecs;
1464
1465 if (!surface->codecs)
1466 {
1467 free(surface);
1468 goto fail;
1469 }
1470 }
1471
1472 surface->surfaceId = createSurface->surfaceId;
1473 surface->width = gfx_align_scanline(createSurface->width, 16);
1474 surface->height = gfx_align_scanline(createSurface->height, 16);
1475 surface->mappedWidth = createSurface->width;
1476 surface->mappedHeight = createSurface->height;
1477 surface->outputTargetWidth = createSurface->width;
1478 surface->outputTargetHeight = createSurface->height;
1479
1480 const BOOL rails =
1481 freerdp_settings_get_bool(gdi->context->settings, FreeRDP_RemoteApplicationMode);
1482 switch (createSurface->pixelFormat)
1483 {
1484 case GFX_PIXEL_FORMAT_ARGB_8888:
1485 surface->format = PIXEL_FORMAT_BGRA32;
1486 break;
1487
1488 case GFX_PIXEL_FORMAT_XRGB_8888:
1489 surface->format = rails ? PIXEL_FORMAT_BGRA32 : PIXEL_FORMAT_BGRX32;
1490 break;
1491
1492 default:
1493 free(surface);
1494 goto fail;
1495 }
1496
1497 if (surface->width > UINT32_MAX / 4 - 2 * 16)
1498 {
1499 free(surface);
1500 goto fail;
1501 }
1502
1503 /* Align to multiples of 16, but also add additional 16 bytes so SIMD implementations do not
1504 * accidentally read out of bound */
1505 surface->scanline = gfx_align_scanline(surface->width * 4UL, 16) + 16ul;
1506 surface->data = (BYTE*)winpr_aligned_calloc(surface->scanline, surface->height, 16);
1507
1508 if (!surface->data)
1509 {
1510 free(surface);
1511 goto fail;
1512 }
1513
1514 for (UINT64 y = 0; y < surface->height; y++)
1515 memset(&surface->data[surface->scanline * y], 0xFF, surface->scanline);
1516 region16_init(&surface->invalidRegion);
1517
1518 WINPR_ASSERT(context->SetSurfaceData);
1519 rc = context->SetSurfaceData(context, surface->surfaceId, (void*)surface);
1520fail:
1521 LeaveCriticalSection(&context->mux);
1522 return rc;
1523}
1524
1530WINPR_ATTR_NODISCARD
1531static UINT gdi_DeleteSurface(RdpgfxClientContext* context,
1532 const RDPGFX_DELETE_SURFACE_PDU* deleteSurface)
1533{
1534 UINT rc = CHANNEL_RC_OK;
1535 UINT res = ERROR_INTERNAL_ERROR;
1536 rdpCodecs* codecs = nullptr;
1537 gdiGfxSurface* surface = nullptr;
1538 EnterCriticalSection(&context->mux);
1539
1540 WINPR_ASSERT(context->GetSurfaceData);
1541 surface = (gdiGfxSurface*)context->GetSurfaceData(context, deleteSurface->surfaceId);
1542
1543 if (surface)
1544 {
1545 if (surface->windowMapped)
1546 rc = IFCALLRESULT(CHANNEL_RC_OK, context->UnmapWindowForSurface, context,
1547 surface->windowId);
1548
1549#ifdef WITH_GFX_H264
1550 h264_context_free(surface->h264);
1551#endif
1552#if defined(WITH_GFX_AV1)
1553 freerdp_av1_context_free(surface->av1);
1554#endif
1555#if defined(WITH_GFX_AV2)
1556 freerdp_av2_context_free(surface->av2);
1557#endif
1558 region16_uninit(&surface->invalidRegion);
1559 codecs = surface->codecs;
1560 winpr_aligned_free(surface->data);
1561 free(surface);
1562 }
1563
1564 WINPR_ASSERT(context->SetSurfaceData);
1565 res = context->SetSurfaceData(context, deleteSurface->surfaceId, nullptr);
1566 if (res)
1567 rc = res;
1568
1569 if (codecs && codecs->progressive)
1570 progressive_delete_surface_context(codecs->progressive, deleteSurface->surfaceId);
1571
1572 LeaveCriticalSection(&context->mux);
1573 return rc;
1574}
1575
1576WINPR_ATTR_NODISCARD
1577static BOOL intersect_rect(const RECTANGLE_16* rect, const gdiGfxSurface* surface,
1578 RECTANGLE_16* prect)
1579{
1580 WINPR_ASSERT(rect);
1581 WINPR_ASSERT(surface);
1582 WINPR_ASSERT(prect);
1583
1584 if (rect->left > rect->right)
1585 return FALSE;
1586 if (rect->left > surface->width)
1587 return FALSE;
1588 if (rect->top > rect->bottom)
1589 return FALSE;
1590 if (rect->top > surface->height)
1591 return FALSE;
1592 prect->left = rect->left;
1593 prect->top = rect->top;
1594
1595 prect->right = MIN(rect->right, WINPR_ASSERTING_INT_CAST(UINT16, surface->width));
1596 prect->bottom = MIN(rect->bottom, WINPR_ASSERTING_INT_CAST(UINT16, surface->height));
1597 return TRUE;
1598}
1599
1605WINPR_ATTR_NODISCARD
1606static UINT gdi_SolidFill(RdpgfxClientContext* context, const RDPGFX_SOLID_FILL_PDU* solidFill)
1607{
1608 UINT status = ERROR_INTERNAL_ERROR;
1609 BYTE a = 0xff;
1610 RECTANGLE_16 invalidRect = WINPR_C_ARRAY_INIT;
1611 rdpGdi* gdi = (rdpGdi*)context->custom;
1612
1613 EnterCriticalSection(&context->mux);
1614
1615 WINPR_ASSERT(context->GetSurfaceData);
1616 gdiGfxSurface* surface = (gdiGfxSurface*)context->GetSurfaceData(context, solidFill->surfaceId);
1617
1618 if (!surface)
1619 goto fail;
1620
1621 {
1622 const BYTE b = solidFill->fillPixel.B;
1623 const BYTE g = solidFill->fillPixel.G;
1624 const BYTE r = solidFill->fillPixel.R;
1625
1626 /* [MS-RDPEGFX] 3.3.5.4 Processing an RDPGFX_SOLIDFILL_PDU message
1627 * https://learn.microsoft.com/en-us/windows/win32/gdi/binary-raster-operations
1628 *
1629 * this sounds like the alpha value is always ignored.
1630 */
1631 const UINT32 color = FreeRDPGetColor(surface->format, r, g, b, a);
1632 for (UINT16 index = 0; index < solidFill->fillRectCount; index++)
1633 {
1634 const RECTANGLE_16* rect = &(solidFill->fillRects[index]);
1635
1636 if (!intersect_rect(rect, surface, &invalidRect))
1637 goto fail;
1638
1639 const UINT32 nWidth = invalidRect.right - invalidRect.left;
1640 const UINT32 nHeight = invalidRect.bottom - invalidRect.top;
1641
1642 if (!freerdp_image_fill(surface->data, surface->format, surface->scanline,
1643 invalidRect.left, invalidRect.top, nWidth, nHeight, color))
1644 goto fail;
1645
1646 if (!gdi_surface_invalidate_rect(surface, &invalidRect))
1647 goto fail;
1648 }
1649 }
1650
1651 status = IFCALLRESULT(CHANNEL_RC_OK, context->UpdateSurfaceArea, context, surface->surfaceId,
1652 solidFill->fillRectCount, solidFill->fillRects);
1653
1654 if (status != CHANNEL_RC_OK)
1655 goto fail;
1656
1657 LeaveCriticalSection(&context->mux);
1658
1659 return gdi_interFrameUpdate(gdi, context);
1660fail:
1661 LeaveCriticalSection(&context->mux);
1662 return status;
1663}
1664
1670WINPR_ATTR_NODISCARD
1671static UINT gdi_SurfaceToSurface(RdpgfxClientContext* context,
1672 const RDPGFX_SURFACE_TO_SURFACE_PDU* surfaceToSurface)
1673{
1674 UINT status = ERROR_INTERNAL_ERROR;
1675 BOOL sameSurface = 0;
1676 const RECTANGLE_16* rectSrc = nullptr;
1677 RECTANGLE_16 invalidRect;
1678 gdiGfxSurface* surfaceSrc = nullptr;
1679 gdiGfxSurface* surfaceDst = nullptr;
1680 rdpGdi* gdi = (rdpGdi*)context->custom;
1681 EnterCriticalSection(&context->mux);
1682 rectSrc = &(surfaceToSurface->rectSrc);
1683
1684 WINPR_ASSERT(context->GetSurfaceData);
1685 surfaceSrc = (gdiGfxSurface*)context->GetSurfaceData(context, surfaceToSurface->surfaceIdSrc);
1686 sameSurface = (surfaceToSurface->surfaceIdSrc == surfaceToSurface->surfaceIdDest);
1687
1688 if (!sameSurface)
1689 surfaceDst =
1690 (gdiGfxSurface*)context->GetSurfaceData(context, surfaceToSurface->surfaceIdDest);
1691 else
1692 surfaceDst = surfaceSrc;
1693
1694 if (!surfaceSrc || !surfaceDst)
1695 goto fail;
1696
1697 if (!is_rect_valid(rectSrc, surfaceSrc->width, surfaceSrc->height))
1698 goto fail;
1699
1700 {
1701 const UINT32 nWidth = rectSrc->right - rectSrc->left;
1702 const UINT32 nHeight = rectSrc->bottom - rectSrc->top;
1703
1704 for (UINT16 index = 0; index < surfaceToSurface->destPtsCount; index++)
1705 {
1706 const RDPGFX_POINT16* destPt = &surfaceToSurface->destPts[index];
1707 const RECTANGLE_16 rect = { destPt->x, destPt->y,
1708 (UINT16)MIN(UINT16_MAX, destPt->x + nWidth),
1709 (UINT16)MIN(UINT16_MAX, destPt->y + nHeight) };
1710 if (!is_rect_valid(&rect, surfaceDst->width, surfaceDst->height))
1711 goto fail;
1712
1713 const UINT32 rwidth = rect.right - rect.left;
1714 const UINT32 rheight = rect.bottom - rect.top;
1715 if (!freerdp_image_copy(surfaceDst->data, surfaceDst->format, surfaceDst->scanline,
1716 destPt->x, destPt->y, rwidth, rheight, surfaceSrc->data,
1717 surfaceSrc->format, surfaceSrc->scanline, rectSrc->left,
1718 rectSrc->top, nullptr, FREERDP_FLIP_NONE))
1719 goto fail;
1720
1721 invalidRect = rect;
1722 if (!gdi_surface_invalidate_rect(surfaceDst, &invalidRect))
1723 goto fail;
1724 status = IFCALLRESULT(CHANNEL_RC_OK, context->UpdateSurfaceArea, context,
1725 surfaceDst->surfaceId, 1, &invalidRect);
1726
1727 if (status != CHANNEL_RC_OK)
1728 goto fail;
1729 }
1730 }
1731
1732 LeaveCriticalSection(&context->mux);
1733
1734 return gdi_interFrameUpdate(gdi, context);
1735fail:
1736 LeaveCriticalSection(&context->mux);
1737 return status;
1738}
1739
1740static void gdi_GfxCacheEntryFree(gdiGfxCacheEntry* entry)
1741{
1742 if (!entry)
1743 return;
1744 free(entry->data);
1745 free(entry);
1746}
1747
1748WINPR_ATTR_MALLOC(gdi_GfxCacheEntryFree, 1)
1749static gdiGfxCacheEntry* gdi_GfxCacheEntryNew(UINT64 cacheKey, UINT32 width, UINT32 height,
1750 UINT32 format)
1751{
1752 gdiGfxCacheEntry* cacheEntry = (gdiGfxCacheEntry*)calloc(1, sizeof(gdiGfxCacheEntry));
1753 if (!cacheEntry)
1754 goto fail;
1755
1756 cacheEntry->cacheKey = cacheKey;
1757 cacheEntry->width = width;
1758 cacheEntry->height = height;
1759 cacheEntry->format = format;
1760
1761 const UINT32 bpp = MAX(4, FreeRDPGetBytesPerPixel(format));
1762 cacheEntry->scanline = gfx_align_scanline(cacheEntry->width * bpp, 16);
1763
1764 if ((cacheEntry->width > 0) && (cacheEntry->height > 0))
1765 {
1766 cacheEntry->data = (BYTE*)calloc(cacheEntry->height, cacheEntry->scanline);
1767
1768 if (!cacheEntry->data)
1769 goto fail;
1770 }
1771 return cacheEntry;
1772fail:
1773 gdi_GfxCacheEntryFree(cacheEntry);
1774 return nullptr;
1775}
1776
1782WINPR_ATTR_NODISCARD
1783static UINT gdi_SurfaceToCache(RdpgfxClientContext* context,
1784 const RDPGFX_SURFACE_TO_CACHE_PDU* surfaceToCache)
1785{
1786 gdiGfxCacheEntry* cacheEntry = nullptr;
1787 UINT rc = ERROR_INTERNAL_ERROR;
1788 EnterCriticalSection(&context->mux);
1789 const RECTANGLE_16* rect = &(surfaceToCache->rectSrc);
1790
1791 WINPR_ASSERT(context->GetSurfaceData);
1792 gdiGfxSurface* surface =
1793 (gdiGfxSurface*)context->GetSurfaceData(context, surfaceToCache->surfaceId);
1794
1795 if (!surface)
1796 goto fail;
1797
1798 if (!is_rect_valid(rect, surface->width, surface->height))
1799 goto fail;
1800
1801 cacheEntry = gdi_GfxCacheEntryNew(surfaceToCache->cacheKey, (UINT32)(rect->right - rect->left),
1802 (UINT32)(rect->bottom - rect->top), surface->format);
1803
1804 if (!cacheEntry)
1805 goto fail;
1806
1807 if (!cacheEntry->data)
1808 goto fail;
1809
1810 if (!freerdp_image_copy_no_overlap(cacheEntry->data, cacheEntry->format, cacheEntry->scanline,
1811 0, 0, cacheEntry->width, cacheEntry->height, surface->data,
1812 surface->format, surface->scanline, rect->left, rect->top,
1813 nullptr, FREERDP_FLIP_NONE))
1814 goto fail;
1815
1816 {
1817 RDPGFX_EVICT_CACHE_ENTRY_PDU evict = { surfaceToCache->cacheSlot };
1818 WINPR_ASSERT(context->EvictCacheEntry);
1819 rc = context->EvictCacheEntry(context, &evict);
1820 if (rc != CHANNEL_RC_OK)
1821 goto fail;
1822 }
1823
1824 WINPR_ASSERT(context->SetCacheSlotData);
1825 rc = context->SetCacheSlotData(context, surfaceToCache->cacheSlot, (void*)cacheEntry);
1826fail:
1827 if (rc != CHANNEL_RC_OK)
1828 gdi_GfxCacheEntryFree(cacheEntry);
1829 LeaveCriticalSection(&context->mux);
1830 return rc;
1831}
1832
1838WINPR_ATTR_NODISCARD
1839static UINT gdi_CacheToSurface(RdpgfxClientContext* context,
1840 const RDPGFX_CACHE_TO_SURFACE_PDU* cacheToSurface)
1841{
1842 UINT status = ERROR_INTERNAL_ERROR;
1843 gdiGfxSurface* surface = nullptr;
1844 gdiGfxCacheEntry* cacheEntry = nullptr;
1845 RECTANGLE_16 invalidRect;
1846 rdpGdi* gdi = (rdpGdi*)context->custom;
1847
1848 EnterCriticalSection(&context->mux);
1849
1850 WINPR_ASSERT(context->GetSurfaceData);
1851 surface = (gdiGfxSurface*)context->GetSurfaceData(context, cacheToSurface->surfaceId);
1852
1853 WINPR_ASSERT(context->GetCacheSlotData);
1854 cacheEntry = (gdiGfxCacheEntry*)context->GetCacheSlotData(context, cacheToSurface->cacheSlot);
1855
1856 if (!surface || !cacheEntry)
1857 goto fail;
1858
1859 for (UINT16 index = 0; index < cacheToSurface->destPtsCount; index++)
1860 {
1861 const RDPGFX_POINT16* destPt = &cacheToSurface->destPts[index];
1862 const RECTANGLE_16 rect = { destPt->x, destPt->y,
1863 (UINT16)MIN(UINT16_MAX, destPt->x + cacheEntry->width),
1864 (UINT16)MIN(UINT16_MAX, destPt->y + cacheEntry->height) };
1865
1866 if (rectangle_is_empty(&rect))
1867 continue;
1868
1869 if (!is_rect_valid(&rect, surface->width, surface->height))
1870 goto fail;
1871
1872 const UINT32 w = rect.right - rect.left;
1873 const UINT32 h = rect.bottom - rect.top;
1874 if (!freerdp_image_copy_no_overlap(surface->data, surface->format, surface->scanline,
1875 destPt->x, destPt->y, w, h, cacheEntry->data,
1876 cacheEntry->format, cacheEntry->scanline, 0, 0, nullptr,
1877 FREERDP_FLIP_NONE))
1878 goto fail;
1879
1880 invalidRect = rect;
1881 if (!gdi_surface_invalidate_rect(surface, &invalidRect))
1882 goto fail;
1883 status = IFCALLRESULT(CHANNEL_RC_OK, context->UpdateSurfaceArea, context,
1884 surface->surfaceId, 1, &invalidRect);
1885
1886 if (status != CHANNEL_RC_OK)
1887 goto fail;
1888 }
1889
1890 LeaveCriticalSection(&context->mux);
1891
1892 return gdi_interFrameUpdate(gdi, context);
1893
1894fail:
1895 LeaveCriticalSection(&context->mux);
1896 return status;
1897}
1898
1904WINPR_ATTR_NODISCARD
1905static UINT gdi_CacheImportReply(RdpgfxClientContext* context,
1906 const RDPGFX_CACHE_IMPORT_REPLY_PDU* cacheImportReply)
1907{
1908 UINT16 count = 0;
1909 const UINT16* slots = nullptr;
1910 UINT error = CHANNEL_RC_OK;
1911
1912 slots = cacheImportReply->cacheSlots;
1913 count = cacheImportReply->importedEntriesCount;
1914
1915 for (UINT16 index = 0; index < count; index++)
1916 {
1917 UINT16 cacheSlot = slots[index];
1918
1919 if (cacheSlot == 0)
1920 continue;
1921
1922 WINPR_ASSERT(context->GetCacheSlotData);
1923 gdiGfxCacheEntry* cacheEntry =
1924 (gdiGfxCacheEntry*)context->GetCacheSlotData(context, cacheSlot);
1925
1926 if (cacheEntry)
1927 continue;
1928
1929 cacheEntry = gdi_GfxCacheEntryNew(cacheSlot, 0, 0, PIXEL_FORMAT_BGRX32);
1930
1931 if (!cacheEntry)
1932 return ERROR_INTERNAL_ERROR;
1933
1934 WINPR_ASSERT(context->SetCacheSlotData);
1935 error = context->SetCacheSlotData(context, cacheSlot, (void*)cacheEntry);
1936
1937 if (error)
1938 {
1939 WLog_ERR(TAG, "CacheImportReply: SetCacheSlotData failed with error %" PRIu32 "",
1940 error);
1941 gdi_GfxCacheEntryFree(cacheEntry);
1942 break;
1943 }
1944 }
1945
1946 return error;
1947}
1948
1949WINPR_ATTR_NODISCARD
1950static UINT gdi_ImportCacheEntry(RdpgfxClientContext* context, UINT16 cacheSlot,
1951 const PERSISTENT_CACHE_ENTRY* importCacheEntry)
1952{
1953 UINT error = ERROR_INTERNAL_ERROR;
1954 gdiGfxCacheEntry* cacheEntry = nullptr;
1955
1956 if (cacheSlot == 0)
1957 return CHANNEL_RC_OK;
1958
1959 cacheEntry = gdi_GfxCacheEntryNew(importCacheEntry->key64, importCacheEntry->width,
1960 importCacheEntry->height, PIXEL_FORMAT_BGRX32);
1961
1962 if (!cacheEntry)
1963 goto fail;
1964
1965 if (!freerdp_image_copy_no_overlap(cacheEntry->data, cacheEntry->format, cacheEntry->scanline,
1966 0, 0, cacheEntry->width, cacheEntry->height,
1967 importCacheEntry->data, PIXEL_FORMAT_BGRX32, 0, 0, 0,
1968 nullptr, FREERDP_FLIP_NONE))
1969 goto fail;
1970
1971 {
1972 RDPGFX_EVICT_CACHE_ENTRY_PDU evict = { cacheSlot };
1973 WINPR_ASSERT(context->EvictCacheEntry);
1974 error = context->EvictCacheEntry(context, &evict);
1975 }
1976 if (error != CHANNEL_RC_OK)
1977 goto fail;
1978
1979 WINPR_ASSERT(context->SetCacheSlotData);
1980 error = context->SetCacheSlotData(context, cacheSlot, (void*)cacheEntry);
1981
1982fail:
1983 if (error)
1984 {
1985 gdi_GfxCacheEntryFree(cacheEntry);
1986 WLog_ERR(TAG, "ImportCacheEntry: SetCacheSlotData failed with error %" PRIu32 "", error);
1987 }
1988
1989 return error;
1990}
1991
1992WINPR_ATTR_NODISCARD
1993static UINT gdi_ExportCacheEntry(RdpgfxClientContext* context, UINT16 cacheSlot,
1994 PERSISTENT_CACHE_ENTRY* exportCacheEntry)
1995{
1996 WINPR_ASSERT(context->GetCacheSlotData);
1997 gdiGfxCacheEntry* cacheEntry = (gdiGfxCacheEntry*)context->GetCacheSlotData(context, cacheSlot);
1998
1999 if (cacheEntry)
2000 {
2001 exportCacheEntry->key64 = cacheEntry->cacheKey;
2002 exportCacheEntry->width = (UINT16)MIN(UINT16_MAX, cacheEntry->width);
2003 exportCacheEntry->height = (UINT16)MIN(UINT16_MAX, cacheEntry->height);
2004 const UINT64 size = 4ull * cacheEntry->width * cacheEntry->height;
2005 if (size > UINT32_MAX)
2006 return ERROR_NOT_FOUND;
2007
2008 exportCacheEntry->size = WINPR_ASSERTING_INT_CAST(UINT32, size);
2009 exportCacheEntry->flags = 0;
2010 exportCacheEntry->data = cacheEntry->data;
2011 return CHANNEL_RC_OK;
2012 }
2013
2014 return ERROR_NOT_FOUND;
2015}
2016
2022WINPR_ATTR_NODISCARD
2023static UINT gdi_EvictCacheEntry(RdpgfxClientContext* context,
2024 const RDPGFX_EVICT_CACHE_ENTRY_PDU* evictCacheEntry)
2025{
2026 gdiGfxCacheEntry* cacheEntry = nullptr;
2027 UINT rc = ERROR_NOT_FOUND;
2028
2029 WINPR_ASSERT(context);
2030 WINPR_ASSERT(evictCacheEntry);
2031
2032 EnterCriticalSection(&context->mux);
2033
2034 WINPR_ASSERT(context->GetCacheSlotData);
2035 cacheEntry = (gdiGfxCacheEntry*)context->GetCacheSlotData(context, evictCacheEntry->cacheSlot);
2036
2037 gdi_GfxCacheEntryFree(cacheEntry);
2038
2039 WINPR_ASSERT(context->SetCacheSlotData);
2040 rc = context->SetCacheSlotData(context, evictCacheEntry->cacheSlot, nullptr);
2041 LeaveCriticalSection(&context->mux);
2042 return rc;
2043}
2044
2050WINPR_ATTR_NODISCARD
2051static UINT gdi_MapSurfaceToOutput(RdpgfxClientContext* context,
2052 const RDPGFX_MAP_SURFACE_TO_OUTPUT_PDU* surfaceToOutput)
2053{
2054 UINT rc = ERROR_INTERNAL_ERROR;
2055 gdiGfxSurface* surface = nullptr;
2056 EnterCriticalSection(&context->mux);
2057
2058 WINPR_ASSERT(context->GetSurfaceData);
2059 surface = (gdiGfxSurface*)context->GetSurfaceData(context, surfaceToOutput->surfaceId);
2060
2061 if (!surface)
2062 goto fail;
2063
2064 if (surface->windowMapped)
2065 {
2066 WLog_WARN(TAG, "surface already windowMapped when trying to set outputMapped");
2067 goto fail;
2068 }
2069
2070 surface->outputMapped = TRUE;
2071 surface->outputOriginX = surfaceToOutput->outputOriginX;
2072 surface->outputOriginY = surfaceToOutput->outputOriginY;
2073 surface->outputTargetWidth = surface->mappedWidth;
2074 surface->outputTargetHeight = surface->mappedHeight;
2075 region16_clear(&surface->invalidRegion);
2076 rc = CHANNEL_RC_OK;
2077fail:
2078 LeaveCriticalSection(&context->mux);
2079 return rc;
2080}
2081
2082WINPR_ATTR_NODISCARD
2083static UINT
2084gdi_MapSurfaceToScaledOutput(RdpgfxClientContext* context,
2085 const RDPGFX_MAP_SURFACE_TO_SCALED_OUTPUT_PDU* surfaceToOutput)
2086{
2087 UINT rc = ERROR_INTERNAL_ERROR;
2088 gdiGfxSurface* surface = nullptr;
2089 EnterCriticalSection(&context->mux);
2090
2091 WINPR_ASSERT(context->GetSurfaceData);
2092 surface = (gdiGfxSurface*)context->GetSurfaceData(context, surfaceToOutput->surfaceId);
2093
2094 if (!surface)
2095 goto fail;
2096
2097 if (surface->windowMapped)
2098 {
2099 WLog_WARN(TAG, "surface already windowMapped when trying to set outputMapped");
2100 goto fail;
2101 }
2102
2103 surface->outputMapped = TRUE;
2104 surface->outputOriginX = surfaceToOutput->outputOriginX;
2105 surface->outputOriginY = surfaceToOutput->outputOriginY;
2106 surface->outputTargetWidth = surfaceToOutput->targetWidth;
2107 surface->outputTargetHeight = surfaceToOutput->targetHeight;
2108 region16_clear(&surface->invalidRegion);
2109 rc = CHANNEL_RC_OK;
2110fail:
2111 LeaveCriticalSection(&context->mux);
2112 return rc;
2113}
2114
2120WINPR_ATTR_NODISCARD
2121static UINT gdi_MapSurfaceToWindow(RdpgfxClientContext* context,
2122 const RDPGFX_MAP_SURFACE_TO_WINDOW_PDU* surfaceToWindow)
2123{
2124 UINT rc = ERROR_INTERNAL_ERROR;
2125 gdiGfxSurface* surface = nullptr;
2126 EnterCriticalSection(&context->mux);
2127
2128 WINPR_ASSERT(context->GetSurfaceData);
2129 surface = (gdiGfxSurface*)context->GetSurfaceData(context, surfaceToWindow->surfaceId);
2130
2131 if (!surface)
2132 goto fail;
2133
2134 if (surface->outputMapped)
2135 {
2136 WLog_WARN(TAG, "surface already outputMapped when trying to set windowMapped");
2137 goto fail;
2138 }
2139
2140 if (surface->windowMapped)
2141 {
2142 if (surface->windowId != surfaceToWindow->windowId)
2143 {
2144 WLog_WARN(TAG, "surface windowId mismatch, has %" PRIu64 ", expected %" PRIu64,
2145 surface->windowId, surfaceToWindow->windowId);
2146 goto fail;
2147 }
2148 }
2149 surface->windowMapped = TRUE;
2150
2151 surface->windowId = surfaceToWindow->windowId;
2152 surface->mappedWidth = surfaceToWindow->mappedWidth;
2153 if (surface->mappedWidth > surface->width)
2154 {
2155 WLog_WARN(TAG, "surface mappedWidth[%" PRIu32 "] > width[%" PRIu32 "]",
2156 surface->mappedWidth, surface->width);
2157 goto fail;
2158 }
2159 surface->mappedHeight = surfaceToWindow->mappedHeight;
2160 if (surface->mappedHeight > surface->height)
2161 {
2162 WLog_WARN(TAG, "surface mappedHeight[%" PRIu32 "] > height[%" PRIu32 "]",
2163 surface->mappedHeight, surface->height);
2164 goto fail;
2165 }
2166 surface->outputTargetWidth = surfaceToWindow->mappedWidth;
2167 surface->outputTargetHeight = surfaceToWindow->mappedHeight;
2168 rc = IFCALLRESULT(CHANNEL_RC_OK, context->MapWindowForSurface, context,
2169 surfaceToWindow->surfaceId, surfaceToWindow->windowId);
2170fail:
2171 LeaveCriticalSection(&context->mux);
2172 return rc;
2173}
2174
2175WINPR_ATTR_NODISCARD
2176static UINT
2177gdi_MapSurfaceToScaledWindow(RdpgfxClientContext* context,
2178 const RDPGFX_MAP_SURFACE_TO_SCALED_WINDOW_PDU* surfaceToWindow)
2179{
2180 UINT rc = ERROR_INTERNAL_ERROR;
2181 gdiGfxSurface* surface = nullptr;
2182 EnterCriticalSection(&context->mux);
2183
2184 WINPR_ASSERT(context->GetSurfaceData);
2185 surface = (gdiGfxSurface*)context->GetSurfaceData(context, surfaceToWindow->surfaceId);
2186
2187 if (!surface)
2188 goto fail;
2189
2190 if (surface->outputMapped)
2191 {
2192 WLog_WARN(TAG, "surface already outputMapped when trying to set windowMapped");
2193 goto fail;
2194 }
2195
2196 if (surface->windowMapped)
2197 {
2198 if (surface->windowId != surfaceToWindow->windowId)
2199 {
2200 WLog_WARN(TAG, "surface windowId mismatch, has %" PRIu64 ", expected %" PRIu64,
2201 surface->windowId, surfaceToWindow->windowId);
2202 goto fail;
2203 }
2204 }
2205 surface->windowMapped = TRUE;
2206
2207 surface->windowId = surfaceToWindow->windowId;
2208 surface->mappedWidth = surfaceToWindow->mappedWidth;
2209 if (surface->mappedWidth > surface->width)
2210 {
2211 WLog_WARN(TAG, "surface mappedWidth[%" PRIu32 "] > width[%" PRIu32 "]",
2212 surface->mappedWidth, surface->width);
2213 goto fail;
2214 }
2215
2216 surface->mappedHeight = surfaceToWindow->mappedHeight;
2217 if (surface->mappedHeight > surface->height)
2218 {
2219 WLog_WARN(TAG, "surface mappedHeight[%" PRIu32 "] > height[%" PRIu32 "]",
2220 surface->mappedHeight, surface->height);
2221 goto fail;
2222 }
2223 surface->outputTargetWidth = surfaceToWindow->targetWidth;
2224 surface->outputTargetHeight = surfaceToWindow->targetHeight;
2225 rc = IFCALLRESULT(CHANNEL_RC_OK, context->MapWindowForSurface, context,
2226 surfaceToWindow->surfaceId, surfaceToWindow->windowId);
2227fail:
2228 LeaveCriticalSection(&context->mux);
2229 return rc;
2230}
2231
2232BOOL gdi_graphics_pipeline_init(rdpGdi* gdi, RdpgfxClientContext* gfx)
2233{
2234 return gdi_graphics_pipeline_init_ex(gdi, gfx, nullptr, nullptr, nullptr);
2235}
2236
2237BOOL gdi_graphics_pipeline_init_ex(rdpGdi* gdi, RdpgfxClientContext* gfx,
2238 pcRdpgfxMapWindowForSurface map,
2239 pcRdpgfxUnmapWindowForSurface unmap,
2240 pcRdpgfxUpdateSurfaceArea update)
2241{
2242 if (!gdi || !gfx || !gdi->context || !gdi->context->settings)
2243 return FALSE;
2244
2245 rdpContext* context = gdi->context;
2246 rdpSettings* settings = context->settings;
2247
2248 gdi->gfx = gfx;
2249 gfx->custom = (void*)gdi;
2250 gfx->ResetGraphics = gdi_ResetGraphics;
2251 gfx->StartFrame = gdi_StartFrame;
2252 gfx->EndFrame = gdi_EndFrame;
2253 gfx->SurfaceCommand = gdi_SurfaceCommand;
2254 gfx->DeleteEncodingContext = gdi_DeleteEncodingContext;
2255 gfx->CreateSurface = gdi_CreateSurface;
2256 gfx->DeleteSurface = gdi_DeleteSurface;
2257 gfx->SolidFill = gdi_SolidFill;
2258 gfx->SurfaceToSurface = gdi_SurfaceToSurface;
2259 gfx->SurfaceToCache = gdi_SurfaceToCache;
2260 gfx->CacheToSurface = gdi_CacheToSurface;
2261 gfx->CacheImportReply = gdi_CacheImportReply;
2262 gfx->ImportCacheEntry = gdi_ImportCacheEntry;
2263 gfx->ExportCacheEntry = gdi_ExportCacheEntry;
2264 gfx->EvictCacheEntry = gdi_EvictCacheEntry;
2265 gfx->MapSurfaceToOutput = gdi_MapSurfaceToOutput;
2266 gfx->MapSurfaceToWindow = gdi_MapSurfaceToWindow;
2267 gfx->MapSurfaceToScaledOutput = gdi_MapSurfaceToScaledOutput;
2268 gfx->MapSurfaceToScaledWindow = gdi_MapSurfaceToScaledWindow;
2269 gfx->UpdateSurfaces = gdi_UpdateSurfaces;
2270 gfx->MapWindowForSurface = map;
2271 gfx->UnmapWindowForSurface = unmap;
2272 gfx->UpdateSurfaceArea = update;
2273
2274 if (!freerdp_settings_get_bool(settings, FreeRDP_DeactivateClientDecoding))
2275 {
2276 const UINT32 w = freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth);
2277 const UINT32 h = freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight);
2278 const UINT32 flags = freerdp_settings_get_uint32(settings, FreeRDP_ThreadingFlags);
2279
2280 gfx->codecs = freerdp_client_codecs_new(flags);
2281 if (!gfx->codecs)
2282 return FALSE;
2283 if (!freerdp_client_codecs_prepare(gfx->codecs, FREERDP_CODEC_ALL, w, h))
2284 return FALSE;
2285 }
2286 InitializeCriticalSection(&gfx->mux);
2287 PROFILER_CREATE(gfx->SurfaceProfiler, "GFX-PROFILER")
2288
2289
2295 gdi->graphicsReset = TRUE;
2296 if (freerdp_settings_get_bool(settings, FreeRDP_DeactivateClientDecoding))
2297 {
2298 gfx->UpdateSurfaceArea = nullptr;
2299 gfx->UpdateSurfaces = nullptr;
2300 gfx->SurfaceCommand = nullptr;
2301 }
2302
2303 return TRUE;
2304}
2305
2306void gdi_graphics_pipeline_uninit(rdpGdi* gdi, RdpgfxClientContext* gfx)
2307{
2308 if (gdi)
2309 gdi->gfx = nullptr;
2310
2311 if (!gfx)
2312 return;
2313
2314 gfx->custom = nullptr;
2315 freerdp_client_codecs_free(gfx->codecs);
2316 gfx->codecs = nullptr;
2317 DeleteCriticalSection(&gfx->mux);
2318 PROFILER_PRINT_HEADER
2319 PROFILER_PRINT(gfx->SurfaceProfiler)
2320 PROFILER_PRINT_FOOTER
2321 PROFILER_FREE(gfx->SurfaceProfiler)
2322}
WINPR_ATTR_NODISCARD FREERDP_API UINT32 freerdp_settings_get_codecs_flags(const rdpSettings *settings)
helper function to get a mask of supported codec flags.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_set_uint32(rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id, UINT32 val)
Sets a UINT32 settings value.
WINPR_ATTR_NODISCARD FREERDP_API UINT32 freerdp_settings_get_uint32(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id)
Returns a UINT32 settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.
Definition persistent.h:70