22#include <freerdp/config.h>
25#include <winpr/wtypes.h>
26#include <winpr/assert.h>
27#include <winpr/cast.h>
28#include <winpr/print.h>
30#include <freerdp/primitives.h>
31#include <freerdp/log.h>
32#include <freerdp/codec/bitmap.h>
33#include <freerdp/codec/planar.h>
35#define TAG FREERDP_TAG("codec")
37#define PLANAR_ALIGN(val, align) \
38 ((val) % (align) == 0) ? (val) : ((val) + (align) - (val) % (align))
54 RDP6_RLE_SEGMENT* segments;
70struct S_BITMAP_PLANAR_CONTEXT
77 BOOL AllowRunLengthEncoding;
78 BOOL AllowColorSubsampling;
79 BOOL AllowDynamicColorFidelity;
81 UINT32 ColorLossLevel;
87 BYTE* deltaPlanesBuffer;
90 BYTE* rlePlanesBuffer;
99static inline BYTE PLANAR_CONTROL_BYTE(UINT32 nRunLength, UINT32 cRawBytes)
101 return WINPR_ASSERTING_INT_CAST(UINT8, ((nRunLength & 0x0F) | ((cRawBytes & 0x0F) << 4)));
104static inline BYTE PLANAR_CONTROL_BYTE_RUN_LENGTH(UINT32 controlByte)
106 return (controlByte & 0x0F);
108static inline BYTE PLANAR_CONTROL_BYTE_RAW_BYTES(UINT32 controlByte)
110 return ((controlByte >> 4) & 0x0F);
113static inline UINT32 planar_invert_format(BITMAP_PLANAR_CONTEXT* WINPR_RESTRICT planar, BOOL alpha,
116 WINPR_ASSERT(planar);
117 if (planar->bgr && alpha)
121 case PIXEL_FORMAT_ARGB32:
122 DstFormat = PIXEL_FORMAT_ABGR32;
124 case PIXEL_FORMAT_XRGB32:
125 DstFormat = PIXEL_FORMAT_XBGR32;
127 case PIXEL_FORMAT_ABGR32:
128 DstFormat = PIXEL_FORMAT_ARGB32;
130 case PIXEL_FORMAT_XBGR32:
131 DstFormat = PIXEL_FORMAT_XRGB32;
133 case PIXEL_FORMAT_BGRA32:
134 DstFormat = PIXEL_FORMAT_RGBA32;
136 case PIXEL_FORMAT_BGRX32:
137 DstFormat = PIXEL_FORMAT_RGBX32;
139 case PIXEL_FORMAT_RGBA32:
140 DstFormat = PIXEL_FORMAT_BGRA32;
142 case PIXEL_FORMAT_RGBX32:
143 DstFormat = PIXEL_FORMAT_BGRX32;
145 case PIXEL_FORMAT_RGB24:
146 DstFormat = PIXEL_FORMAT_BGR24;
148 case PIXEL_FORMAT_BGR24:
149 DstFormat = PIXEL_FORMAT_RGB24;
151 case PIXEL_FORMAT_RGB16:
152 DstFormat = PIXEL_FORMAT_BGR16;
154 case PIXEL_FORMAT_BGR16:
155 DstFormat = PIXEL_FORMAT_RGB16;
157 case PIXEL_FORMAT_ARGB15:
158 DstFormat = PIXEL_FORMAT_ABGR15;
160 case PIXEL_FORMAT_RGB15:
161 DstFormat = PIXEL_FORMAT_BGR15;
163 case PIXEL_FORMAT_ABGR15:
164 DstFormat = PIXEL_FORMAT_ARGB15;
166 case PIXEL_FORMAT_BGR15:
167 DstFormat = PIXEL_FORMAT_RGB15;
176static inline BOOL freerdp_bitmap_planar_compress_plane_rle(
const BYTE* WINPR_RESTRICT inPlane,
177 UINT32 width, UINT32 height,
178 BYTE* WINPR_RESTRICT outPlane,
179 UINT32* WINPR_RESTRICT dstSize);
180static inline BYTE* freerdp_bitmap_planar_delta_encode_plane(
const BYTE* WINPR_RESTRICT inPlane,
181 UINT32 width, UINT32 height,
182 BYTE* WINPR_RESTRICT outPlane);
184static inline INT32 planar_skip_plane_rle(
const BYTE* WINPR_RESTRICT pSrcData, UINT32 SrcSize,
185 UINT32 nWidth, UINT32 nHeight)
189 WINPR_ASSERT(pSrcData);
191 for (UINT32 y = 0; y < nHeight; y++)
193 for (UINT32 x = 0; x < nWidth;)
197 WLog_ERR(TAG,
"planar plane used %" PRIu32
" exceeds SrcSize %" PRIu32, used,
202 const uint8_t controlByte = pSrcData[used++];
203 uint32_t nRunLength = PLANAR_CONTROL_BYTE_RUN_LENGTH(controlByte);
204 uint32_t cRawBytes = PLANAR_CONTROL_BYTE_RAW_BYTES(controlByte);
208 nRunLength = cRawBytes + 16;
211 else if (nRunLength == 2)
213 nRunLength = cRawBytes + 32;
223 WLog_ERR(TAG,
"planar plane x %" PRIu32
" exceeds width %" PRIu32, x, nWidth);
229 WLog_ERR(TAG,
"planar plane used %" PRIu32
" exceeds SrcSize %" PRId32, used,
236 if (used > INT32_MAX)
238 WLog_ERR(TAG,
"planar plane used %" PRIu32
" exceeds SrcSize %" PRIu32, used, SrcSize);
244static inline UINT8 clamp(INT16 val)
249static inline INT32 planar_decompress_plane_rle_only(
const BYTE* WINPR_RESTRICT pSrcData,
250 UINT32 SrcSize, BYTE* WINPR_RESTRICT pDstData,
251 UINT32 nWidth, UINT32 nHeight)
253 BYTE* previousScanline =
nullptr;
254 const BYTE* srcp = pSrcData;
256 WINPR_ASSERT(nHeight <= INT32_MAX);
257 WINPR_ASSERT(nWidth <= INT32_MAX);
259 for (UINT32 y = 0; y < nHeight; y++)
261 BYTE* dstp = &pDstData[1ULL * (y)*nWidth];
263 BYTE* currentScanline = dstp;
265 for (UINT32 x = 0; x < nWidth;)
267 BYTE controlByte = *srcp;
270 if ((srcp - pSrcData) > SrcSize * 1ll)
272 WLog_ERR(TAG,
"error reading input buffer");
276 UINT32 nRunLength = PLANAR_CONTROL_BYTE_RUN_LENGTH(controlByte);
277 UINT32 cRawBytes = PLANAR_CONTROL_BYTE_RAW_BYTES(controlByte);
281 nRunLength = cRawBytes + 16;
284 else if (nRunLength == 2)
286 nRunLength = cRawBytes + 32;
290 if (((dstp + (cRawBytes + nRunLength)) - currentScanline) > nWidth * 1ll)
292 WLog_ERR(TAG,
"too many pixels in scanline");
296 if (!previousScanline)
299 while (cRawBytes > 0)
303 *dstp = clamp(pixel);
309 while (nRunLength > 0)
311 *dstp = clamp(pixel);
320 while (cRawBytes > 0)
322 UINT8 deltaValue = *srcp;
327 deltaValue = deltaValue >> 1;
328 deltaValue = deltaValue + 1;
329 pixel = WINPR_ASSERTING_INT_CAST(int16_t, -1 * (int16_t)deltaValue);
333 deltaValue = deltaValue >> 1;
334 pixel = WINPR_ASSERTING_INT_CAST(INT16, deltaValue);
338 WINPR_ASSERTING_INT_CAST(int16_t, previousScanline[x] + pixel);
339 *dstp = clamp(delta);
345 while (nRunLength > 0)
347 const INT16 deltaValue =
348 WINPR_ASSERTING_INT_CAST(int16_t, previousScanline[x] + pixel);
349 *dstp = clamp(deltaValue);
357 previousScanline = currentScanline;
360 return (INT32)(srcp - pSrcData);
363static inline INT32 planar_decompress_plane_rle(
const BYTE* WINPR_RESTRICT pSrcData, UINT32 SrcSize,
364 BYTE* WINPR_RESTRICT pDstData, UINT32 nDstStep,
365 UINT32 nXDst, UINT32 nYDst, UINT32 nWidth,
366 UINT32 nHeight, UINT32 nChannel, BOOL vFlip)
371 BYTE* previousScanline =
nullptr;
372 const BYTE* srcp = pSrcData;
374 WINPR_ASSERT(nHeight <= INT32_MAX);
375 WINPR_ASSERT(nWidth <= INT32_MAX);
376 WINPR_ASSERT(nDstStep <= INT32_MAX);
380 beg = (INT32)nHeight - 1;
387 end = (INT32)nHeight;
391 for (INT32 y = beg; y != end; y += inc)
393 const intptr_t off = ((1LL * nYDst + y) * nDstStep) + (4LL * nXDst) + nChannel * 1LL;
394 BYTE* dstp = &pDstData[off];
395 BYTE* currentScanline = dstp;
398 for (INT32 x = 0; x < (INT32)nWidth;)
400 const BYTE controlByte = *srcp;
403 if ((srcp - pSrcData) > SrcSize * 1ll)
405 WLog_ERR(TAG,
"error reading input buffer");
409 UINT32 nRunLength = PLANAR_CONTROL_BYTE_RUN_LENGTH(controlByte);
410 UINT32 cRawBytes = PLANAR_CONTROL_BYTE_RAW_BYTES(controlByte);
414 nRunLength = cRawBytes + 16;
417 else if (nRunLength == 2)
419 nRunLength = cRawBytes + 32;
423 if (((dstp + (cRawBytes + nRunLength)) - currentScanline) > nWidth * 4ll)
425 WLog_ERR(TAG,
"too many pixels in scanline");
429 if (!previousScanline)
432 while (cRawBytes > 0)
436 *dstp = WINPR_ASSERTING_INT_CAST(BYTE, pixel);
442 while (nRunLength > 0)
444 *dstp = WINPR_ASSERTING_INT_CAST(BYTE, pixel);
453 while (cRawBytes > 0)
455 BYTE deltaValue = *srcp;
460 deltaValue = deltaValue >> 1;
461 deltaValue = deltaValue + 1;
462 pixel = WINPR_ASSERTING_INT_CAST(int16_t, -deltaValue);
466 deltaValue = deltaValue >> 1;
471 WINPR_ASSERTING_INT_CAST(int16_t, previousScanline[4LL * x] + pixel);
472 *dstp = clamp(delta);
478 while (nRunLength > 0)
480 const INT16 deltaValue =
481 WINPR_ASSERTING_INT_CAST(int16_t, pixel + previousScanline[4LL * x]);
482 *dstp = clamp(deltaValue);
490 previousScanline = currentScanline;
493 return (INT32)(srcp - pSrcData);
496static inline INT32 planar_set_plane(BYTE bValue, BYTE* pDstData, UINT32 nDstStep, UINT32 nXDst,
497 UINT32 nYDst, UINT32 nWidth, UINT32 nHeight, UINT32 nChannel,
501 INT32 end = (INT32)nHeight;
504 WINPR_ASSERT(nHeight <= INT32_MAX);
505 WINPR_ASSERT(nWidth <= INT32_MAX);
506 WINPR_ASSERT(nDstStep <= INT32_MAX);
510 beg = (INT32)nHeight - 1;
515 for (INT32 y = beg; y != end; y += inc)
517 const intptr_t off = ((1LL * nYDst + y) * nDstStep) + (4LL * nXDst) + nChannel * 1LL;
518 BYTE* dstp = &pDstData[off];
520 for (INT32 x = 0; x < (INT32)nWidth; ++x)
530static inline BOOL writeLine(BYTE** WINPR_RESTRICT ppRgba, UINT32 DstFormat, UINT32 width,
531 const BYTE** WINPR_RESTRICT ppR,
const BYTE** WINPR_RESTRICT ppG,
532 const BYTE** WINPR_RESTRICT ppB,
const BYTE** WINPR_RESTRICT ppA)
534 WINPR_ASSERT(ppRgba);
541 case PIXEL_FORMAT_BGRA32:
542 for (UINT32 x = 0; x < width; x++)
544 *(*ppRgba)++ = *(*ppB)++;
545 *(*ppRgba)++ = *(*ppG)++;
546 *(*ppRgba)++ = *(*ppR)++;
547 *(*ppRgba)++ = *(*ppA)++;
552 case PIXEL_FORMAT_BGRX32:
553 for (UINT32 x = 0; x < width; x++)
555 *(*ppRgba)++ = *(*ppB)++;
556 *(*ppRgba)++ = *(*ppG)++;
557 *(*ppRgba)++ = *(*ppR)++;
566 for (UINT32 x = 0; x < width; x++)
568 BYTE alpha = *(*ppA)++;
570 FreeRDPGetColor(DstFormat, *(*ppR)++, *(*ppG)++, *(*ppB)++, alpha);
571 FreeRDPWriteColor(*ppRgba, DstFormat, color);
572 *ppRgba += FreeRDPGetBytesPerPixel(DstFormat);
577 const BYTE alpha = 0xFF;
579 for (UINT32 x = 0; x < width; x++)
582 FreeRDPGetColor(DstFormat, *(*ppR)++, *(*ppG)++, *(*ppB)++, alpha);
583 FreeRDPWriteColor(*ppRgba, DstFormat, color);
584 *ppRgba += FreeRDPGetBytesPerPixel(DstFormat);
592static inline BOOL planar_decompress_planes_raw(
const BYTE* WINPR_RESTRICT pSrcData[4],
593 BYTE* WINPR_RESTRICT pDstData, UINT32 DstFormat,
594 UINT32 nDstStep, UINT32 nXDst, UINT32 nYDst,
595 UINT32 nWidth, UINT32 nHeight, BOOL vFlip,
601 const BYTE* pR = pSrcData[0];
602 const BYTE* pG = pSrcData[1];
603 const BYTE* pB = pSrcData[2];
604 const BYTE* pA = pSrcData[3];
605 const UINT32 bpp = FreeRDPGetBytesPerPixel(DstFormat);
609 beg = WINPR_ASSERTING_INT_CAST(int32_t, nHeight - 1);
616 end = WINPR_ASSERTING_INT_CAST(int32_t, nHeight);
620 if (nYDst + nHeight > totalHeight)
623 "planar plane destination Y %" PRIu32
" + height %" PRIu32
624 " exceeds totalHeight %" PRIu32,
625 nYDst, nHeight, totalHeight);
629 if ((nXDst + nWidth) * bpp > nDstStep)
632 "planar plane destination (X %" PRIu32
" + width %" PRIu32
") * bpp %" PRIu32
633 " exceeds stride %" PRIu32,
634 nXDst, nWidth, bpp, nDstStep);
638 for (INT32 y = beg; y != end; y += inc)
640 BYTE* pRGB =
nullptr;
642 if (y > WINPR_ASSERTING_INT_CAST(INT64, nHeight))
644 WLog_ERR(TAG,
"planar plane destination Y %" PRId32
" exceeds height %" PRIu32, y,
649 const intptr_t off = ((1LL * nYDst + y) * nDstStep) + (1LL * nXDst * bpp);
650 pRGB = &pDstData[off];
652 if (!writeLine(&pRGB, DstFormat, nWidth, &pR, &pG, &pB, &pA))
659static BOOL planar_subsample_expand(
const BYTE* WINPR_RESTRICT plane,
size_t planeLength,
660 UINT32 nWidth, UINT32 nHeight, UINT32 nPlaneWidth,
661 UINT32 nPlaneHeight, BYTE* WINPR_RESTRICT deltaPlane)
664 WINPR_UNUSED(planeLength);
667 WINPR_ASSERT(deltaPlane);
669 if (nWidth > nPlaneWidth * 2)
671 WLog_ERR(TAG,
"planar subsample width %" PRIu32
" > PlaneWidth %" PRIu32
" * 2", nWidth,
676 if (nHeight > nPlaneHeight * 2)
678 WLog_ERR(TAG,
"planar subsample height %" PRIu32
" > PlaneHeight %" PRIu32
" * 2", nHeight,
683 for (
size_t y = 0; y < nHeight; y++)
685 const BYTE* src = plane + y / 2 * nPlaneWidth;
687 for (UINT32 x = 0; x < nWidth; x++)
689 deltaPlane[pos++] = src[x / 2];
696#if !defined(WITHOUT_FREERDP_3x_DEPRECATED)
697BOOL planar_decompress(BITMAP_PLANAR_CONTEXT* WINPR_RESTRICT planar,
698 const BYTE* WINPR_RESTRICT pSrcData, UINT32 SrcSize, UINT32 nSrcWidth,
699 UINT32 nSrcHeight, BYTE* WINPR_RESTRICT pDstData, UINT32 DstFormat,
700 UINT32 nDstStep, UINT32 nXDst, UINT32 nYDst, UINT32 nDstWidth,
701 UINT32 nDstHeight, BOOL vFlip)
703 return freerdp_bitmap_decompress_planar(planar, pSrcData, SrcSize, nSrcWidth, nSrcHeight,
704 pDstData, DstFormat, nDstStep, nXDst, nYDst, nDstWidth,
709BOOL freerdp_bitmap_decompress_planar(BITMAP_PLANAR_CONTEXT* WINPR_RESTRICT planar,
710 const BYTE* WINPR_RESTRICT pSrcData, UINT32 SrcSize,
711 UINT32 nSrcWidth, UINT32 nSrcHeight,
712 BYTE* WINPR_RESTRICT pDstData, UINT32 DstFormat,
713 UINT32 nDstStep, UINT32 nXDst, UINT32 nYDst, UINT32 nDstWidth,
714 UINT32 nDstHeight, BOOL vFlip)
716 BOOL useAlpha = FALSE;
718 INT32 rleSizes[4] = { 0, 0, 0, 0 };
719 UINT32 rawSizes[4] = WINPR_C_ARRAY_INIT;
720 UINT32 rawWidths[4] = WINPR_C_ARRAY_INIT;
721 UINT32 rawHeights[4] = WINPR_C_ARRAY_INIT;
722 const BYTE* planes[4] = WINPR_C_ARRAY_INIT;
723 const UINT32 w = MIN(nSrcWidth, nDstWidth);
724 const UINT32 h = MIN(nSrcHeight, nDstHeight);
727 WINPR_ASSERT(planar);
730 if (planar->maxWidth < nSrcWidth)
732 if (planar->maxHeight < nSrcHeight)
735 const UINT32 bpp = FreeRDPGetBytesPerPixel(DstFormat);
737 nDstStep = nDstWidth * bpp;
739 const BYTE* srcp = pSrcData;
743 WLog_ERR(TAG,
"Invalid argument pSrcData=nullptr");
749 WLog_ERR(TAG,
"Invalid argument pDstData=nullptr");
753 const BYTE FormatHeader = *srcp++;
754 const BYTE cll = (FormatHeader & PLANAR_FORMAT_HEADER_CLL_MASK);
755 const BYTE cs = (FormatHeader & PLANAR_FORMAT_HEADER_CS) != 0;
756 const BYTE rle = (FormatHeader & PLANAR_FORMAT_HEADER_RLE) != 0;
757 const BYTE alpha = !(FormatHeader & PLANAR_FORMAT_HEADER_NA);
759 DstFormat = planar_invert_format(planar, alpha, DstFormat);
762 useAlpha = FreeRDPColorHasAlpha(DstFormat);
769 WLog_ERR(TAG,
"Chroma subsampling requires YCoCg and does not work with RGB data");
773 const UINT32 subWidth = (nSrcWidth / 2) + (nSrcWidth % 2);
774 const UINT32 subHeight = (nSrcHeight / 2) + (nSrcHeight % 2);
775 const UINT32 planeSize = nSrcWidth * nSrcHeight;
776 const UINT32 subSize = subWidth * subHeight;
780 rawSizes[0] = planeSize;
781 rawWidths[0] = nSrcWidth;
782 rawHeights[0] = nSrcHeight;
783 rawSizes[1] = planeSize;
784 rawWidths[1] = nSrcWidth;
785 rawHeights[1] = nSrcHeight;
786 rawSizes[2] = planeSize;
787 rawWidths[2] = nSrcWidth;
788 rawHeights[2] = nSrcHeight;
789 rawSizes[3] = planeSize;
790 rawWidths[3] = nSrcWidth;
791 rawHeights[3] = nSrcHeight;
795 rawSizes[0] = planeSize;
796 rawWidths[0] = nSrcWidth;
797 rawHeights[0] = nSrcHeight;
798 rawSizes[1] = subSize;
799 rawWidths[1] = subWidth;
800 rawHeights[1] = subHeight;
801 rawSizes[2] = subSize;
802 rawWidths[2] = subWidth;
803 rawHeights[2] = subHeight;
804 rawSizes[3] = planeSize;
805 rawWidths[3] = nSrcWidth;
806 rawHeights[3] = nSrcHeight;
809 const size_t diff = WINPR_ASSERTING_INT_CAST(
size_t, (intptr_t)(srcp - pSrcData));
812 WLog_ERR(TAG,
"Size mismatch %" PRIu32
" < %" PRIuz, SrcSize, diff);
819 UINT32 base = planeSize * 3;
821 base = planeSize + planeSize / 2;
825 if ((SrcSize - diff) < (planeSize + base))
827 WLog_ERR(TAG,
"Alpha plane size mismatch %" PRIuz
" < %" PRIu32, SrcSize - diff,
833 planes[0] = planes[3] + rawSizes[3];
834 planes[1] = planes[0] + rawSizes[0];
835 planes[2] = planes[1] + rawSizes[1];
837 if ((planes[2] + rawSizes[2]) > &pSrcData[SrcSize])
839 WLog_ERR(TAG,
"plane size mismatch %p + %" PRIu32
" > %p",
840 WINPR_CXX_COMPAT_CAST(
const void*, planes[2]), rawSizes[2],
841 WINPR_CXX_COMPAT_CAST(
const void*, &pSrcData[SrcSize]));
847 if ((SrcSize - diff) < base)
849 WLog_ERR(TAG,
"plane size mismatch %" PRIuz
" < %" PRIu32, SrcSize - diff, base);
854 planes[1] = planes[0] + rawSizes[0];
855 planes[2] = planes[1] + rawSizes[1];
857 if ((planes[2] + rawSizes[2]) > &pSrcData[SrcSize])
859 WLog_ERR(TAG,
"plane size mismatch %p + %" PRIu32
" > %p",
860 WINPR_CXX_COMPAT_CAST(
const void*, planes[2]), rawSizes[2],
861 WINPR_CXX_COMPAT_CAST(
const void*, &pSrcData[SrcSize]));
871 rleSizes[3] = planar_skip_plane_rle(planes[3], (UINT32)(SrcSize - diff), rawWidths[3],
877 planes[0] = planes[3] + rleSizes[3];
882 const size_t diff0 = WINPR_ASSERTING_INT_CAST(
size_t, (intptr_t)(planes[0] - pSrcData));
885 WLog_ERR(TAG,
"Size mismatch %" PRIu32
" < %" PRIuz, SrcSize, diff0);
888 rleSizes[0] = planar_skip_plane_rle(planes[0], (UINT32)(SrcSize - diff0), rawWidths[0],
894 planes[1] = planes[0] + rleSizes[0];
896 const size_t diff1 = WINPR_ASSERTING_INT_CAST(
size_t, (intptr_t)(planes[1] - pSrcData));
899 WLog_ERR(TAG,
"Size mismatch %" PRIu32
" < %" PRIuz, SrcSize, diff1);
902 rleSizes[1] = planar_skip_plane_rle(planes[1], (UINT32)(SrcSize - diff1), rawWidths[1],
908 planes[2] = planes[1] + rleSizes[1];
909 const size_t diff2 = WINPR_ASSERTING_INT_CAST(
size_t, (intptr_t)(planes[2] - pSrcData));
912 WLog_ERR(TAG,
"Size mismatch %" PRIu32
" < %" PRIuz, SrcSize, diff);
915 rleSizes[2] = planar_skip_plane_rle(planes[2], (UINT32)(SrcSize - diff2), rawWidths[2],
924 UINT32 TempFormat = 0;
925 BYTE* pTempData = pDstData;
926 UINT32 nTempStep = nDstStep;
927 UINT32 nTotalHeight = nYDst + nDstHeight;
930 TempFormat = PIXEL_FORMAT_BGRA32;
932 TempFormat = PIXEL_FORMAT_BGRX32;
934 TempFormat = planar_invert_format(planar, alpha, TempFormat);
936 if ((TempFormat != DstFormat) || (nSrcWidth != nDstWidth) || (nSrcHeight != nDstHeight))
938 pTempData = planar->pTempData;
939 nTempStep = planar->nTempStep;
940 nTotalHeight = planar->maxHeight;
945 if (!planar_decompress_planes_raw(planes, pTempData, TempFormat, nTempStep, nXDst,
946 nYDst, nSrcWidth, nSrcHeight, vFlip, nTotalHeight))
950 srcp += rawSizes[0] + rawSizes[1] + rawSizes[2] + rawSizes[3];
952 srcp += rawSizes[0] + rawSizes[1] + rawSizes[2];
954 if ((SrcSize - (srcp - pSrcData)) == 1)
959 if (nYDst + nSrcHeight > nTotalHeight)
962 "planar plane destination Y %" PRIu32
" + height %" PRIu32
963 " exceeds totalHeight %" PRIu32,
964 nYDst, nSrcHeight, nTotalHeight);
968 if ((nXDst + nSrcWidth) * bpp > nDstStep)
971 "planar plane destination (X %" PRIu32
" + width %" PRIu32
972 ") * bpp %" PRIu32
" exceeds stride %" PRIu32,
973 nXDst, nSrcWidth, bpp, nDstStep);
977 status = planar_decompress_plane_rle(
978 planes[0], WINPR_ASSERTING_INT_CAST(uint32_t, rleSizes[0]), pTempData, nTempStep,
979 nXDst, nYDst, nSrcWidth, nSrcHeight, 2, vFlip);
984 status = planar_decompress_plane_rle(
985 planes[1], WINPR_ASSERTING_INT_CAST(uint32_t, rleSizes[1]), pTempData, nTempStep,
986 nXDst, nYDst, nSrcWidth, nSrcHeight, 1, vFlip);
991 status = planar_decompress_plane_rle(
992 planes[2], WINPR_ASSERTING_INT_CAST(uint32_t, rleSizes[2]), pTempData, nTempStep,
993 nXDst, nYDst, nSrcWidth, nSrcHeight, 0, vFlip);
998 srcp += rleSizes[0] + rleSizes[1] + rleSizes[2];
1002 status = planar_decompress_plane_rle(
1003 planes[3], WINPR_ASSERTING_INT_CAST(uint32_t, rleSizes[3]), pTempData,
1004 nTempStep, nXDst, nYDst, nSrcWidth, nSrcHeight, 3, vFlip);
1007 status = planar_set_plane(0xFF, pTempData, nTempStep, nXDst, nYDst, nSrcWidth,
1008 nSrcHeight, 3, vFlip);
1014 srcp += rleSizes[3];
1017 if (pTempData != pDstData)
1019 if (!freerdp_image_copy_no_overlap(pDstData, DstFormat, nDstStep, nXDst, nYDst, w, h,
1020 pTempData, TempFormat, nTempStep, nXDst, nYDst,
1021 nullptr, FREERDP_FLIP_NONE))
1023 WLog_ERR(TAG,
"planar image copy failed");
1030 UINT32 TempFormat = 0;
1031 BYTE* pTempData = planar->pTempData;
1032 UINT32 nTempStep = planar->nTempStep;
1033 UINT32 nTotalHeight = planar->maxHeight;
1034 BYTE* dst = &pDstData[nXDst * FreeRDPGetBytesPerPixel(DstFormat) + nYDst * nDstStep];
1037 TempFormat = PIXEL_FORMAT_BGRA32;
1039 TempFormat = PIXEL_FORMAT_BGRX32;
1046 BYTE* rleBuffer[4] = WINPR_C_ARRAY_INIT;
1048 if (!planar->rlePlanesBuffer)
1051 rleBuffer[3] = planar->rlePlanesBuffer;
1052 rleBuffer[0] = rleBuffer[3] + planeSize;
1053 rleBuffer[1] = rleBuffer[0] + planeSize;
1054 rleBuffer[2] = rleBuffer[1] + planeSize;
1057 status = planar_decompress_plane_rle_only(
1058 planes[3], WINPR_ASSERTING_INT_CAST(uint32_t, rleSizes[3]), rleBuffer[3],
1059 rawWidths[3], rawHeights[3]);
1066 srcp += rleSizes[3];
1068 status = planar_decompress_plane_rle_only(
1069 planes[0], WINPR_ASSERTING_INT_CAST(uint32_t, rleSizes[0]), rleBuffer[0],
1070 rawWidths[0], rawHeights[0]);
1075 status = planar_decompress_plane_rle_only(
1076 planes[1], WINPR_ASSERTING_INT_CAST(uint32_t, rleSizes[1]), rleBuffer[1],
1077 rawWidths[1], rawHeights[1]);
1082 status = planar_decompress_plane_rle_only(
1083 planes[2], WINPR_ASSERTING_INT_CAST(uint32_t, rleSizes[2]), rleBuffer[2],
1084 rawWidths[2], rawHeights[2]);
1089 planes[0] = rleBuffer[0];
1090 planes[1] = rleBuffer[1];
1091 planes[2] = rleBuffer[2];
1092 planes[3] = rleBuffer[3];
1101 if (!planar_subsample_expand(planes[1], rawSizes[1], nSrcWidth, nSrcHeight,
1102 rawWidths[1], rawHeights[1], planar->deltaPlanes[0]))
1105 planes[1] = planar->deltaPlanes[0];
1106 rawSizes[1] = planeSize;
1107 rawWidths[1] = nSrcWidth;
1108 rawHeights[1] = nSrcHeight;
1110 if (!planar_subsample_expand(planes[2], rawSizes[2], nSrcWidth, nSrcHeight,
1111 rawWidths[2], rawHeights[2], planar->deltaPlanes[1]))
1114 planes[2] = planar->deltaPlanes[1];
1115 rawSizes[2] = planeSize;
1116 rawWidths[2] = nSrcWidth;
1117 rawHeights[2] = nSrcHeight;
1120 if (!planar_decompress_planes_raw(planes, pTempData, TempFormat, nTempStep, nXDst,
1121 nYDst, nSrcWidth, nSrcHeight, vFlip, nTotalHeight))
1125 srcp += rawSizes[0] + rawSizes[1] + rawSizes[2] + rawSizes[3];
1127 srcp += rawSizes[0] + rawSizes[1] + rawSizes[2];
1129 if ((SrcSize - (srcp - pSrcData)) == 1)
1133 WINPR_ASSERT(prims->YCoCgToRGB_8u_AC4R);
1134 int rc = prims->YCoCgToRGB_8u_AC4R(
1135 pTempData, WINPR_ASSERTING_INT_CAST(int32_t, nTempStep), dst, DstFormat,
1136 WINPR_ASSERTING_INT_CAST(int32_t, nDstStep), w, h, cll, useAlpha);
1137 if (rc != PRIMITIVES_SUCCESS)
1139 WLog_ERR(TAG,
"YCoCgToRGB_8u_AC4R failed with %d", rc);
1148static inline BOOL freerdp_split_color_planes(BITMAP_PLANAR_CONTEXT* WINPR_RESTRICT planar,
1149 const BYTE* WINPR_RESTRICT data, UINT32 format,
1150 UINT32 width, UINT32 height, UINT32 scanline,
1151 BYTE* WINPR_RESTRICT planes[4])
1153 WINPR_ASSERT(planar);
1155 if ((width > INT32_MAX) || (height > INT32_MAX) || (scanline > INT32_MAX))
1159 scanline = width * FreeRDPGetBytesPerPixel(format);
1161 if (planar->topdown)
1164 for (UINT32 i = 0; i < height; i++)
1166 const BYTE* pixel = &data[1ULL * scanline * i];
1168 for (UINT32 j = 0; j < width; j++)
1170 const UINT32 color = FreeRDPReadColor(pixel, format);
1171 pixel += FreeRDPGetBytesPerPixel(format);
1172 FreeRDPSplitColor(color, format, &planes[1][k], &planes[2][k], &planes[3][k],
1173 &planes[0][k],
nullptr);
1182 for (INT64 i = (INT64)height - 1; i >= 0; i--)
1184 const BYTE* pixel = &data[1ULL * scanline * (UINT32)i];
1186 for (UINT32 j = 0; j < width; j++)
1188 const UINT32 color = FreeRDPReadColor(pixel, format);
1189 pixel += FreeRDPGetBytesPerPixel(format);
1190 FreeRDPSplitColor(color, format, &planes[1][k], &planes[2][k], &planes[3][k],
1191 &planes[0][k],
nullptr);
1199static inline UINT32 freerdp_bitmap_planar_write_rle_bytes(
const BYTE* WINPR_RESTRICT pInBuffer,
1200 UINT32 cRawBytes, UINT32 nRunLength,
1201 BYTE* WINPR_RESTRICT pOutBuffer,
1202 UINT32 outBufferSize)
1204 const BYTE* pInput = pInBuffer;
1205 BYTE* pOutput = pOutBuffer;
1206 BYTE controlByte = 0;
1207 UINT32 nBytesToWrite = 0;
1209 if (!cRawBytes && !nRunLength)
1214 cRawBytes += nRunLength;
1222 if (nRunLength > 15)
1224 if (nRunLength < 18)
1226 controlByte = PLANAR_CONTROL_BYTE(13, cRawBytes);
1232 controlByte = PLANAR_CONTROL_BYTE(15, cRawBytes);
1239 controlByte = PLANAR_CONTROL_BYTE(nRunLength, cRawBytes);
1246 controlByte = PLANAR_CONTROL_BYTE(0, 15);
1250 if (outBufferSize < 1)
1254 *pOutput = controlByte;
1256 nBytesToWrite = (controlByte >> 4);
1260 if (outBufferSize < nBytesToWrite)
1263 outBufferSize -= nBytesToWrite;
1264 CopyMemory(pOutput, pInput, nBytesToWrite);
1265 pOutput += nBytesToWrite;
1266 pInput += nBytesToWrite;
1272 if (nRunLength > 47)
1274 if (nRunLength < 50)
1276 controlByte = PLANAR_CONTROL_BYTE(2, 13);
1281 controlByte = PLANAR_CONTROL_BYTE(2, 15);
1285 else if (nRunLength > 31)
1287 controlByte = PLANAR_CONTROL_BYTE(2, (nRunLength - 32));
1290 else if (nRunLength > 15)
1292 controlByte = PLANAR_CONTROL_BYTE(1, (nRunLength - 16));
1297 controlByte = PLANAR_CONTROL_BYTE(nRunLength, 0);
1301 if (outBufferSize < 1)
1305 *pOutput = controlByte;
1309 const intptr_t diff = (pOutput - pOutBuffer);
1310 if ((diff < 0) || (diff > UINT32_MAX))
1312 return (UINT32)diff;
1315static inline UINT32 freerdp_bitmap_planar_encode_rle_bytes(
const BYTE* WINPR_RESTRICT pInBuffer,
1316 UINT32 inBufferSize,
1317 BYTE* WINPR_RESTRICT pOutBuffer,
1318 UINT32 outBufferSize)
1321 const BYTE* pInput = pInBuffer;
1322 BYTE* pOutput = pOutBuffer;
1323 const BYTE* pBytes =
nullptr;
1324 UINT32 cRawBytes = 0;
1325 UINT32 nRunLength = 0;
1326 UINT32 nBytesWritten = 0;
1327 UINT32 nTotalBytesWritten = 0;
1337 const UINT32 bSymbolMatch = (symbol == *pInput) != 0;
1342 if (nRunLength && !bSymbolMatch)
1346 cRawBytes += nRunLength;
1351 pBytes = pInput - (cRawBytes + nRunLength + 1);
1352 nBytesWritten = freerdp_bitmap_planar_write_rle_bytes(pBytes, cRawBytes, nRunLength,
1353 pOutput, outBufferSize);
1356 if (!nBytesWritten || (nBytesWritten > outBufferSize))
1359 nTotalBytesWritten += nBytesWritten;
1360 outBufferSize -= nBytesWritten;
1361 pOutput += nBytesWritten;
1366 nRunLength += bSymbolMatch;
1367 cRawBytes += (!bSymbolMatch) != 0;
1368 }
while (outBufferSize);
1370 if (cRawBytes || nRunLength)
1372 pBytes = pInput - (cRawBytes + nRunLength);
1373 nBytesWritten = freerdp_bitmap_planar_write_rle_bytes(pBytes, cRawBytes, nRunLength,
1374 pOutput, outBufferSize);
1379 nTotalBytesWritten += nBytesWritten;
1385 return nTotalBytesWritten;
1388BOOL freerdp_bitmap_planar_compress_plane_rle(
const BYTE* WINPR_RESTRICT inPlane, UINT32 width,
1389 UINT32 height, BYTE* WINPR_RESTRICT outPlane,
1390 UINT32* WINPR_RESTRICT dstSize)
1396 const BYTE* pInput = inPlane;
1397 BYTE* pOutput = outPlane;
1398 UINT32 outBufferSize = *dstSize;
1399 UINT32 nTotalBytesWritten = 0;
1401 while (outBufferSize > 0)
1403 const UINT32 nBytesWritten =
1404 freerdp_bitmap_planar_encode_rle_bytes(pInput, width, pOutput, outBufferSize);
1406 if ((!nBytesWritten) || (nBytesWritten > outBufferSize))
1409 outBufferSize -= nBytesWritten;
1410 nTotalBytesWritten += nBytesWritten;
1411 pOutput += nBytesWritten;
1415 if (index >= height)
1419 *dstSize = nTotalBytesWritten;
1423static inline BOOL freerdp_bitmap_planar_compress_planes_rle(BYTE* WINPR_RESTRICT inPlanes[4],
1424 UINT32 width, UINT32 height,
1425 BYTE* WINPR_RESTRICT outPlanes,
1426 UINT32* WINPR_RESTRICT dstSizes,
1429 UINT32 outPlanesSize = width * height * 4;
1438 dstSizes[0] = outPlanesSize;
1440 if (!freerdp_bitmap_planar_compress_plane_rle(inPlanes[0], width, height, outPlanes,
1444 outPlanes += dstSizes[0];
1445 outPlanesSize -= dstSizes[0];
1449 dstSizes[1] = outPlanesSize;
1451 if (!freerdp_bitmap_planar_compress_plane_rle(inPlanes[1], width, height, outPlanes,
1455 outPlanes += dstSizes[1];
1456 outPlanesSize -= dstSizes[1];
1458 dstSizes[2] = outPlanesSize;
1460 if (!freerdp_bitmap_planar_compress_plane_rle(inPlanes[2], width, height, outPlanes,
1464 outPlanes += dstSizes[2];
1465 outPlanesSize -= dstSizes[2];
1467 dstSizes[3] = outPlanesSize;
1469 return (freerdp_bitmap_planar_compress_plane_rle(inPlanes[3], width, height, outPlanes,
1473BYTE* freerdp_bitmap_planar_delta_encode_plane(
const BYTE* WINPR_RESTRICT inPlane, UINT32 width,
1474 UINT32 height, BYTE* WINPR_RESTRICT outPlane)
1478 if (width * height == 0)
1481 outPlane = (BYTE*)calloc(height, width);
1487 CopyMemory(outPlane, inPlane, width);
1489 for (UINT32 y = 1; y < height; y++)
1491 const size_t off = 1ull * width * y;
1492 BYTE* outPtr = &outPlane[off];
1493 const BYTE* srcPtr = &inPlane[off];
1494 const BYTE* prevLinePtr = &inPlane[off - width];
1495 for (UINT32 x = 0; x < width; x++)
1497 const int delta = (int)srcPtr[x] - (
int)prevLinePtr[x];
1498 const int s2c1i = (delta >= 0) ? delta : (INT_MAX + delta) + 1;
1499 const int8_t s2c1 = WINPR_CXX_COMPAT_CAST(int8_t, s2c1i);
1500 const uint32_t s2c =
1501 (s2c1 >= 0) ? ((UINT32)s2c1 << 1) : (((UINT32)(~(s2c1) + 1) << 1) - 1);
1502 outPtr[x] = (BYTE)s2c;
1509static inline BOOL freerdp_bitmap_planar_delta_encode_planes(BYTE* WINPR_RESTRICT inPlanes[4],
1510 UINT32 width, UINT32 height,
1511 BYTE* WINPR_RESTRICT outPlanes[4])
1513 for (UINT32 i = 0; i < 4; i++)
1516 freerdp_bitmap_planar_delta_encode_plane(inPlanes[i], width, height, outPlanes[i]);
1525BYTE* freerdp_bitmap_compress_planar(BITMAP_PLANAR_CONTEXT* WINPR_RESTRICT context,
1526 const BYTE* WINPR_RESTRICT data, UINT32 format, UINT32 width,
1527 UINT32 height, UINT32 scanline, BYTE* WINPR_RESTRICT dstData,
1528 UINT32* WINPR_RESTRICT pDstSize)
1531 BYTE* dstp =
nullptr;
1532 UINT32 dstSizes[4] = WINPR_C_ARRAY_INIT;
1533 BYTE FormatHeader = 0;
1535 if (!context || !context->rlePlanesBuffer)
1538 if (context->AllowSkipAlpha)
1539 FormatHeader |= PLANAR_FORMAT_HEADER_NA;
1541 const UINT32 planeSize = width * height;
1543 if (!context->AllowSkipAlpha)
1544 format = planar_invert_format(context, TRUE, format);
1546 if (!freerdp_split_color_planes(context, data, format, width, height, scanline,
1550 if (context->AllowRunLengthEncoding)
1552 if (!freerdp_bitmap_planar_delta_encode_planes(context->planes, width, height,
1553 context->deltaPlanes))
1556 if (!freerdp_bitmap_planar_compress_planes_rle(context->deltaPlanes, width, height,
1557 context->rlePlanesBuffer, dstSizes,
1558 context->AllowSkipAlpha))
1562 uint32_t offset = 0;
1563 FormatHeader |= PLANAR_FORMAT_HEADER_RLE;
1564 context->rlePlanes[0] = &context->rlePlanesBuffer[offset];
1565 offset += dstSizes[0];
1566 context->rlePlanes[1] = &context->rlePlanesBuffer[offset];
1567 offset += dstSizes[1];
1568 context->rlePlanes[2] = &context->rlePlanesBuffer[offset];
1569 offset += dstSizes[2];
1570 context->rlePlanes[3] = &context->rlePlanesBuffer[offset];
1572#if defined(WITH_DEBUG_CODECS)
1574 "R: [%" PRIu32
"/%" PRIu32
"] G: [%" PRIu32
"/%" PRIu32
"] B: [%" PRIu32
1576 dstSizes[1], planeSize, dstSizes[2], planeSize, dstSizes[3], planeSize);
1581 if (FormatHeader & PLANAR_FORMAT_HEADER_RLE)
1583 if (!context->AllowRunLengthEncoding)
1586 if (context->rlePlanes[0] ==
nullptr)
1589 if (context->rlePlanes[1] ==
nullptr)
1592 if (context->rlePlanes[2] ==
nullptr)
1595 if (context->rlePlanes[3] ==
nullptr)
1603 if (!(FormatHeader & PLANAR_FORMAT_HEADER_NA))
1605 if (FormatHeader & PLANAR_FORMAT_HEADER_RLE)
1606 size += dstSizes[0];
1611 if (FormatHeader & PLANAR_FORMAT_HEADER_RLE)
1612 size += (dstSizes[1] + dstSizes[2] + dstSizes[3]);
1614 size += (planeSize * 3);
1616 if (!(FormatHeader & PLANAR_FORMAT_HEADER_RLE))
1619 dstData = malloc(size);
1628 *dstp = FormatHeader;
1633 if (!(FormatHeader & PLANAR_FORMAT_HEADER_NA))
1635 if (FormatHeader & PLANAR_FORMAT_HEADER_RLE)
1637 CopyMemory(dstp, context->rlePlanes[0], dstSizes[0]);
1638 dstp += dstSizes[0];
1642 CopyMemory(dstp, context->planes[0], planeSize);
1649 if (FormatHeader & PLANAR_FORMAT_HEADER_RLE)
1651 CopyMemory(dstp, context->rlePlanes[1], dstSizes[1]);
1652 dstp += dstSizes[1];
1656 CopyMemory(dstp, context->planes[1], planeSize);
1662 if (FormatHeader & PLANAR_FORMAT_HEADER_RLE)
1664 CopyMemory(dstp, context->rlePlanes[2], dstSizes[2]);
1665 dstp += dstSizes[2];
1669 CopyMemory(dstp, context->planes[2], planeSize);
1675 if (FormatHeader & PLANAR_FORMAT_HEADER_RLE)
1677 CopyMemory(dstp, context->rlePlanes[3], dstSizes[3]);
1678 dstp += dstSizes[3];
1682 CopyMemory(dstp, context->planes[3], planeSize);
1688 if (!(FormatHeader & PLANAR_FORMAT_HEADER_RLE))
1694 const intptr_t diff = (dstp - dstData);
1695 if ((diff < 0) || (diff > UINT32_MAX))
1700 size = (UINT32)diff;
1705BOOL freerdp_bitmap_planar_context_reset(BITMAP_PLANAR_CONTEXT* WINPR_RESTRICT context,
1706 UINT32 width, UINT32 height)
1711 context->bgr = FALSE;
1712 context->maxWidth = PLANAR_ALIGN(width, 4);
1713 context->maxHeight = PLANAR_ALIGN(height, 4);
1715 const UINT64 tmp = (UINT64)context->maxWidth * context->maxHeight;
1716 if (tmp > UINT32_MAX)
1718 context->maxPlaneSize = (UINT32)tmp;
1721 if (context->maxWidth > UINT32_MAX / 4)
1723 context->nTempStep = context->maxWidth * 4;
1725 memset((
void*)context->planes, 0,
sizeof(context->planes));
1726 memset((
void*)context->rlePlanes, 0,
sizeof(context->rlePlanes));
1727 memset((
void*)context->deltaPlanes, 0,
sizeof(context->deltaPlanes));
1729 if (context->maxPlaneSize > 0)
1731 void* tmp = winpr_aligned_recalloc(context->planesBuffer, context->maxPlaneSize, 4, 32);
1734 context->planesBuffer = tmp;
1736 tmp = winpr_aligned_recalloc(context->pTempData, context->maxPlaneSize, 6, 32);
1739 context->pTempData = tmp;
1741 tmp = winpr_aligned_recalloc(context->deltaPlanesBuffer, context->maxPlaneSize, 4, 32);
1744 context->deltaPlanesBuffer = tmp;
1746 tmp = winpr_aligned_recalloc(context->rlePlanesBuffer, context->maxPlaneSize, 4, 32);
1749 context->rlePlanesBuffer = tmp;
1751 context->planes[0] = &context->planesBuffer[0ULL * context->maxPlaneSize];
1752 context->planes[1] = &context->planesBuffer[1ULL * context->maxPlaneSize];
1753 context->planes[2] = &context->planesBuffer[2ULL * context->maxPlaneSize];
1754 context->planes[3] = &context->planesBuffer[3ULL * context->maxPlaneSize];
1755 context->deltaPlanes[0] = &context->deltaPlanesBuffer[0ULL * context->maxPlaneSize];
1756 context->deltaPlanes[1] = &context->deltaPlanesBuffer[1ULL * context->maxPlaneSize];
1757 context->deltaPlanes[2] = &context->deltaPlanesBuffer[2ULL * context->maxPlaneSize];
1758 context->deltaPlanes[3] = &context->deltaPlanesBuffer[3ULL * context->maxPlaneSize];
1763BITMAP_PLANAR_CONTEXT* freerdp_bitmap_planar_context_new(DWORD flags, UINT32 maxWidth,
1766 BITMAP_PLANAR_CONTEXT* context =
1767 (BITMAP_PLANAR_CONTEXT*)winpr_aligned_calloc(1,
sizeof(BITMAP_PLANAR_CONTEXT), 32);
1772 if (flags & PLANAR_FORMAT_HEADER_NA)
1773 context->AllowSkipAlpha = TRUE;
1775 if (flags & PLANAR_FORMAT_HEADER_RLE)
1776 context->AllowRunLengthEncoding = TRUE;
1778 if (flags & PLANAR_FORMAT_HEADER_CS)
1779 context->AllowColorSubsampling = TRUE;
1781 context->ColorLossLevel = flags & PLANAR_FORMAT_HEADER_CLL_MASK;
1783 if (context->ColorLossLevel)
1784 context->AllowDynamicColorFidelity = TRUE;
1786 if (!freerdp_bitmap_planar_context_reset(context, maxWidth, maxHeight))
1788 WINPR_PRAGMA_DIAG_PUSH
1789 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
1790 freerdp_bitmap_planar_context_free(context);
1791 WINPR_PRAGMA_DIAG_POP
1798void freerdp_bitmap_planar_context_free(BITMAP_PLANAR_CONTEXT* context)
1803 winpr_aligned_free(context->pTempData);
1804 winpr_aligned_free(context->planesBuffer);
1805 winpr_aligned_free(context->deltaPlanesBuffer);
1806 winpr_aligned_free(context->rlePlanesBuffer);
1807 winpr_aligned_free(context);
1810void freerdp_planar_switch_bgr(BITMAP_PLANAR_CONTEXT* WINPR_RESTRICT planar, BOOL bgr)
1812 WINPR_ASSERT(planar);
1816void freerdp_planar_topdown_image(BITMAP_PLANAR_CONTEXT* WINPR_RESTRICT planar, BOOL topdown)
1818 WINPR_ASSERT(planar);
1819 planar->topdown = topdown;