FreeRDP
Loading...
Searching...
No Matches
prim_copy.c
1/* FreeRDP: A Remote Desktop Protocol Client
2 * Copy operations.
3 * vi:ts=4 sw=4:
4 *
5 * (c) Copyright 2012 Hewlett-Packard Development Company, L.P.
6 * Licensed under the Apache License, Version 2.0 (the "License"); you may
7 * not use this file except in compliance with the License. You may obtain
8 * a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
12 * or implied. See the License for the specific language governing
13 * permissions and limitations under the License.
14 */
15
16#include <freerdp/config.h>
17
18#include <string.h>
19#include <freerdp/types.h>
20#include <freerdp/primitives.h>
21#include <freerdp/log.h>
22
23#include "prim_internal.h"
24#include "prim_copy.h"
25#include "../codec/color.h"
26
27#include <freerdp/codec/color.h>
28
29static primitives_t* generic = nullptr;
30
31/* ------------------------------------------------------------------------- */
32/*static inline BOOL memory_regions_overlap_1d(*/
33static BOOL memory_regions_overlap_1d(const BYTE* p1, const BYTE* p2, size_t bytes)
34{
35 const ULONG_PTR p1m = (const ULONG_PTR)p1;
36 const ULONG_PTR p2m = (const ULONG_PTR)p2;
37
38 if (p1m <= p2m)
39 {
40 if (p1m + bytes > p2m)
41 return TRUE;
42 }
43 else
44 {
45 if (p2m + bytes > p1m)
46 return TRUE;
47 }
48
49 /* else */
50 return FALSE;
51}
52
53/* ------------------------------------------------------------------------- */
54/*static inline BOOL memory_regions_overlap_2d( */
55static BOOL memory_regions_overlap_2d(const BYTE* p1, int p1Step, int p1Size, const BYTE* p2,
56 int p2Step, int p2Size, int width, int height)
57{
58 ULONG_PTR p1m = (ULONG_PTR)p1;
59 ULONG_PTR p2m = (ULONG_PTR)p2;
60
61 if (p1m <= p2m)
62 {
63 ULONG_PTR p1mEnd = p1m +
64 1ull * (WINPR_ASSERTING_INT_CAST(uint32_t, height - 1)) *
65 WINPR_ASSERTING_INT_CAST(uint32_t, p1Step) +
66 1ull * WINPR_ASSERTING_INT_CAST(uint32_t, width* p1Size);
67
68 if (p1mEnd > p2m)
69 return TRUE;
70 }
71 else
72 {
73 ULONG_PTR p2mEnd = p2m +
74 1ull * (WINPR_ASSERTING_INT_CAST(uintptr_t, height - 1)) *
75 WINPR_ASSERTING_INT_CAST(uintptr_t, p2Step) +
76 1ull * WINPR_ASSERTING_INT_CAST(uintptr_t, width* p2Size);
77
78 if (p2mEnd > p1m)
79 return TRUE;
80 }
81
82 /* else */
83 return FALSE;
84}
85
86/* ------------------------------------------------------------------------- */
87static pstatus_t general_copy_8u(const BYTE* WINPR_RESTRICT pSrc, BYTE* WINPR_RESTRICT pDst,
88 INT32 len)
89{
90 if (memory_regions_overlap_1d(pSrc, pDst, (size_t)len))
91 {
92 memmove((void*)pDst, (const void*)pSrc, (size_t)len);
93 }
94 else
95 {
96 memcpy((void*)pDst, (const void*)pSrc, (size_t)len);
97 }
98
99 return PRIMITIVES_SUCCESS;
100}
101
102/* ------------------------------------------------------------------------- */
103/* Copy a block of pixels from one buffer to another.
104 * The addresses are assumed to have been already offset to the upper-left
105 * corners of the source and destination region of interest.
106 */
107static pstatus_t general_copy_8u_AC4r(const BYTE* WINPR_RESTRICT pSrc, INT32 srcStep,
108 BYTE* WINPR_RESTRICT pDst, INT32 dstStep, INT32 width,
109 INT32 height)
110{
111 const BYTE* src = pSrc;
112 BYTE* dst = pDst;
113 const size_t rowbytes = WINPR_ASSERTING_INT_CAST(size_t, width) * sizeof(UINT32);
114
115 if ((width == 0) || (height == 0))
116 return PRIMITIVES_SUCCESS;
117
118 if (memory_regions_overlap_2d(pSrc, srcStep, sizeof(UINT32), pDst, dstStep, sizeof(UINT32),
119 width, height))
120 {
121 do
122 {
123 const pstatus_t rc =
124 generic->copy(src, dst, WINPR_ASSERTING_INT_CAST(int32_t, rowbytes));
125 if (rc != PRIMITIVES_SUCCESS)
126 return rc;
127
128 src += srcStep;
129 dst += dstStep;
130 } while (--height);
131 }
132 else
133 {
134 /* TODO: do it in one operation when the rowdata is adjacent. */
135 do
136 {
137 /* If we find a replacement for memcpy that is consistently
138 * faster, this could be replaced with that.
139 */
140 memcpy(dst, src, rowbytes);
141 src += srcStep;
142 dst += dstStep;
143 } while (--height);
144 }
145
146 return PRIMITIVES_SUCCESS;
147}
148
149static inline pstatus_t generic_image_copy_bgr24_bgrx32(BYTE* WINPR_RESTRICT pDstData,
150 UINT32 nDstStep, UINT32 nXDst, UINT32 nYDst,
151 UINT32 nWidth, UINT32 nHeight,
152 const BYTE* WINPR_RESTRICT pSrcData,
153 UINT32 nSrcStep, UINT32 nXSrc, UINT32 nYSrc,
154 int64_t srcVMultiplier, int64_t srcVOffset,
155 int64_t dstVMultiplier, int64_t dstVOffset)
156{
157
158 const int64_t srcByte = 3;
159 const int64_t dstByte = 4;
160
161 const UINT32 width = nWidth - nWidth % 8;
162
163 for (int64_t y = 0; y < nHeight; y++)
164 {
165 const BYTE* WINPR_RESTRICT srcLine =
166 &pSrcData[srcVMultiplier * (y + nYSrc) * nSrcStep + srcVOffset];
167 BYTE* WINPR_RESTRICT dstLine =
168 &pDstData[dstVMultiplier * (y + nYDst) * nDstStep + dstVOffset];
169
170 int64_t x = 0;
171 WINPR_PRAGMA_UNROLL_LOOP
172 for (; x < width; x++)
173 {
174 dstLine[(x + nXDst) * dstByte + 0] = srcLine[(x + nXSrc) * srcByte + 0];
175 dstLine[(x + nXDst) * dstByte + 1] = srcLine[(x + nXSrc) * srcByte + 1];
176 dstLine[(x + nXDst) * dstByte + 2] = srcLine[(x + nXSrc) * srcByte + 2];
177 }
178
179 for (; x < nWidth; x++)
180 {
181 dstLine[(x + nXDst) * dstByte + 0] = srcLine[(x + nXSrc) * srcByte + 0];
182 dstLine[(x + nXDst) * dstByte + 1] = srcLine[(x + nXSrc) * srcByte + 1];
183 dstLine[(x + nXDst) * dstByte + 2] = srcLine[(x + nXSrc) * srcByte + 2];
184 }
185 }
186
187 return PRIMITIVES_SUCCESS;
188}
189
190static inline pstatus_t
191generic_image_copy_bgrx32_bgrx32(BYTE* WINPR_RESTRICT pDstData, UINT32 nDstStep, UINT32 nXDst,
192 UINT32 nYDst, UINT32 nWidth, UINT32 nHeight,
193 const BYTE* WINPR_RESTRICT pSrcData, UINT32 nSrcStep, UINT32 nXSrc,
194 UINT32 nYSrc, int64_t srcVMultiplier, int64_t srcVOffset,
195 int64_t dstVMultiplier, int64_t dstVOffset)
196{
197
198 const int64_t srcByte = 4;
199 const int64_t dstByte = 4;
200
201 const UINT32 width = nWidth - nWidth % 8;
202
203 for (int64_t y = 0; y < nHeight; y++)
204 {
205 const BYTE* WINPR_RESTRICT srcLine =
206 &pSrcData[srcVMultiplier * (y + nYSrc) * nSrcStep + srcVOffset];
207 BYTE* WINPR_RESTRICT dstLine =
208 &pDstData[dstVMultiplier * (y + nYDst) * nDstStep + dstVOffset];
209
210 int64_t x = 0;
211 WINPR_PRAGMA_UNROLL_LOOP
212 for (; x < width; x++)
213 {
214 dstLine[(x + nXDst) * dstByte + 0] = srcLine[(x + nXSrc) * srcByte + 0];
215 dstLine[(x + nXDst) * dstByte + 1] = srcLine[(x + nXSrc) * srcByte + 1];
216 dstLine[(x + nXDst) * dstByte + 2] = srcLine[(x + nXSrc) * srcByte + 2];
217 }
218 for (; x < nWidth; x++)
219 {
220 dstLine[(x + nXDst) * dstByte + 0] = srcLine[(x + nXSrc) * srcByte + 0];
221 dstLine[(x + nXDst) * dstByte + 1] = srcLine[(x + nXSrc) * srcByte + 1];
222 dstLine[(x + nXDst) * dstByte + 2] = srcLine[(x + nXSrc) * srcByte + 2];
223 }
224 }
225
226 return PRIMITIVES_SUCCESS;
227}
228
229pstatus_t generic_image_copy_no_overlap_convert(
230 BYTE* WINPR_RESTRICT pDstData, DWORD DstFormat, UINT32 nDstStep, UINT32 nXDst, UINT32 nYDst,
231 UINT32 nWidth, UINT32 nHeight, const BYTE* WINPR_RESTRICT pSrcData, DWORD SrcFormat,
232 UINT32 nSrcStep, UINT32 nXSrc, UINT32 nYSrc, const gdiPalette* WINPR_RESTRICT palette,
233 int64_t srcVMultiplier, int64_t srcVOffset, int64_t dstVMultiplier, int64_t dstVOffset)
234{
235 const int64_t srcByte = FreeRDPGetBytesPerPixel(SrcFormat);
236 const int64_t dstByte = FreeRDPGetBytesPerPixel(DstFormat);
237
238 const UINT32 width = nWidth - nWidth % 8;
239 for (int64_t y = 0; y < nHeight; y++)
240 {
241 const BYTE* WINPR_RESTRICT srcLine =
242 &pSrcData[srcVMultiplier * (y + nYSrc) * nSrcStep + srcVOffset];
243 BYTE* WINPR_RESTRICT dstLine =
244 &pDstData[dstVMultiplier * (y + nYDst) * nDstStep + dstVOffset];
245
246 int64_t x = 0;
247 // WINPR_PRAGMA_UNROLL_LOOP
248 for (; x < width; x++)
249 {
250 const UINT32 color = FreeRDPReadColor_int(&srcLine[(x + nXSrc) * srcByte], SrcFormat);
251 const UINT32 dstColor = FreeRDPConvertColor(color, SrcFormat, DstFormat, palette);
252 if (!FreeRDPWriteColor_int(&dstLine[(x + nXDst) * dstByte], DstFormat, dstColor))
253 return -1;
254 }
255 for (; x < nWidth; x++)
256 {
257 const UINT32 color = FreeRDPReadColor_int(&srcLine[(x + nXSrc) * srcByte], SrcFormat);
258 const UINT32 dstColor = FreeRDPConvertColor(color, SrcFormat, DstFormat, palette);
259 if (!FreeRDPWriteColor_int(&dstLine[(x + nXDst) * dstByte], DstFormat, dstColor))
260 return -1;
261 }
262 }
263 return PRIMITIVES_SUCCESS;
264}
265
266pstatus_t generic_image_copy_no_overlap_memcpy(
267 BYTE* WINPR_RESTRICT pDstData, DWORD DstFormat, UINT32 nDstStep, UINT32 nXDst, UINT32 nYDst,
268 UINT32 nWidth, UINT32 nHeight, const BYTE* WINPR_RESTRICT pSrcData, DWORD SrcFormat,
269 UINT32 nSrcStep, UINT32 nXSrc, UINT32 nYSrc,
270 WINPR_ATTR_UNUSED const gdiPalette* WINPR_RESTRICT palette, int64_t srcVMultiplier,
271 int64_t srcVOffset, int64_t dstVMultiplier, int64_t dstVOffset, WINPR_ATTR_UNUSED UINT32 flags)
272{
273 const int64_t dstByte = FreeRDPGetBytesPerPixel(DstFormat);
274 const int64_t srcByte = FreeRDPGetBytesPerPixel(SrcFormat);
275 const int64_t copyDstWidth = nWidth * dstByte;
276 const int64_t xSrcOffset = nXSrc * srcByte;
277 const int64_t xDstOffset = nXDst * dstByte;
278
279 for (int64_t y = 0; y < nHeight; y++)
280 {
281 const BYTE* WINPR_RESTRICT srcLine =
282 &pSrcData[srcVMultiplier * (y + nYSrc) * nSrcStep + srcVOffset];
283 BYTE* WINPR_RESTRICT dstLine =
284 &pDstData[dstVMultiplier * (y + nYDst) * nDstStep + dstVOffset];
285 memcpy(&dstLine[xDstOffset], &srcLine[xSrcOffset],
286 WINPR_ASSERTING_INT_CAST(size_t, copyDstWidth));
287 }
288
289 return PRIMITIVES_SUCCESS;
290}
291
292static inline pstatus_t generic_image_copy_no_overlap_dst_alpha(
293 BYTE* WINPR_RESTRICT pDstData, DWORD DstFormat, UINT32 nDstStep, UINT32 nXDst, UINT32 nYDst,
294 UINT32 nWidth, UINT32 nHeight, const BYTE* WINPR_RESTRICT pSrcData, DWORD SrcFormat,
295 UINT32 nSrcStep, UINT32 nXSrc, UINT32 nYSrc, const gdiPalette* WINPR_RESTRICT palette,
296 int64_t srcVMultiplier, int64_t srcVOffset, int64_t dstVMultiplier, int64_t dstVOffset)
297{
298 WINPR_ASSERT(pDstData);
299 WINPR_ASSERT(pSrcData);
300
301 switch (SrcFormat)
302 {
303 case PIXEL_FORMAT_BGR24:
304 switch (DstFormat)
305 {
306 case PIXEL_FORMAT_BGRX32:
307 case PIXEL_FORMAT_BGRA32:
308 return generic_image_copy_bgr24_bgrx32(
309 pDstData, nDstStep, nXDst, nYDst, nWidth, nHeight, pSrcData, nSrcStep,
310 nXSrc, nYSrc, srcVMultiplier, srcVOffset, dstVMultiplier, dstVOffset);
311 default:
312 break;
313 }
314 break;
315 case PIXEL_FORMAT_BGRX32:
316 case PIXEL_FORMAT_BGRA32:
317 switch (DstFormat)
318 {
319 case PIXEL_FORMAT_BGRX32:
320 case PIXEL_FORMAT_BGRA32:
321 return generic_image_copy_bgrx32_bgrx32(
322 pDstData, nDstStep, nXDst, nYDst, nWidth, nHeight, pSrcData, nSrcStep,
323 nXSrc, nYSrc, srcVMultiplier, srcVOffset, dstVMultiplier, dstVOffset);
324 default:
325 break;
326 }
327 break;
328 case PIXEL_FORMAT_RGBX32:
329 case PIXEL_FORMAT_RGBA32:
330 switch (DstFormat)
331 {
332 case PIXEL_FORMAT_RGBX32:
333 case PIXEL_FORMAT_RGBA32:
334 return generic_image_copy_bgrx32_bgrx32(
335 pDstData, nDstStep, nXDst, nYDst, nWidth, nHeight, pSrcData, nSrcStep,
336 nXSrc, nYSrc, srcVMultiplier, srcVOffset, dstVMultiplier, dstVOffset);
337 case PIXEL_FORMAT_RGB24:
338 return generic_image_copy_bgr24_bgrx32(
339 pDstData, nDstStep, nXDst, nYDst, nWidth, nHeight, pSrcData, nSrcStep,
340 nXSrc, nYSrc, srcVMultiplier, srcVOffset, dstVMultiplier, dstVOffset);
341 default:
342 break;
343 }
344 break;
345 default:
346 break;
347 }
348
349 return generic_image_copy_no_overlap_convert(
350 pDstData, DstFormat, nDstStep, nXDst, nYDst, nWidth, nHeight, pSrcData, SrcFormat, nSrcStep,
351 nXSrc, nYSrc, palette, srcVMultiplier, srcVOffset, dstVMultiplier, dstVOffset);
352}
353
354static inline pstatus_t generic_image_copy_no_overlap_no_alpha(
355 BYTE* WINPR_RESTRICT pDstData, DWORD DstFormat, UINT32 nDstStep, UINT32 nXDst, UINT32 nYDst,
356 UINT32 nWidth, UINT32 nHeight, const BYTE* WINPR_RESTRICT pSrcData, DWORD SrcFormat,
357 UINT32 nSrcStep, UINT32 nXSrc, UINT32 nYSrc, const gdiPalette* WINPR_RESTRICT palette,
358 int64_t srcVMultiplier, int64_t srcVOffset, int64_t dstVMultiplier, int64_t dstVOffset,
359 UINT32 flags)
360{
361 if (FreeRDPAreColorFormatsEqualNoAlpha(SrcFormat, DstFormat))
362 return generic_image_copy_no_overlap_memcpy(pDstData, DstFormat, nDstStep, nXDst, nYDst,
363 nWidth, nHeight, pSrcData, SrcFormat, nSrcStep,
364 nXSrc, nYSrc, palette, srcVMultiplier,
365 srcVOffset, dstVMultiplier, dstVOffset, flags);
366 else
367 return generic_image_copy_no_overlap_convert(pDstData, DstFormat, nDstStep, nXDst, nYDst,
368 nWidth, nHeight, pSrcData, SrcFormat, nSrcStep,
369 nXSrc, nYSrc, palette, srcVMultiplier,
370 srcVOffset, dstVMultiplier, dstVOffset);
371}
372
373static pstatus_t generic_image_copy_no_overlap(BYTE* WINPR_RESTRICT pDstData, DWORD DstFormat,
374 UINT32 nDstStep, UINT32 nXDst, UINT32 nYDst,
375 UINT32 nWidth, UINT32 nHeight,
376 const BYTE* WINPR_RESTRICT pSrcData, DWORD SrcFormat,
377 UINT32 nSrcStep, UINT32 nXSrc, UINT32 nYSrc,
378 const gdiPalette* WINPR_RESTRICT palette,
379 UINT32 flags)
380{
381 const BOOL vSrcVFlip = (flags & FREERDP_FLIP_VERTICAL) != 0;
382 int64_t srcVOffset = 0;
383 int64_t srcVMultiplier = 1;
384 int64_t dstVOffset = 0;
385 int64_t dstVMultiplier = 1;
386
387 if ((nWidth == 0) || (nHeight == 0))
388 return PRIMITIVES_SUCCESS;
389
390 if ((nHeight > INT32_MAX) || (nWidth > INT32_MAX))
391 return -1;
392
393 if (!pDstData || !pSrcData)
394 return -1;
395
396 if (nDstStep == 0)
397 nDstStep = nWidth * FreeRDPGetBytesPerPixel(DstFormat);
398
399 if (nSrcStep == 0)
400 nSrcStep = nWidth * FreeRDPGetBytesPerPixel(SrcFormat);
401
402 if (vSrcVFlip)
403 {
404 srcVOffset = (nHeight - 1ll) * nSrcStep;
405 srcVMultiplier = -1;
406 }
407
408 if (((flags & FREERDP_KEEP_DST_ALPHA) != 0) && FreeRDPColorHasAlpha(DstFormat))
409 return generic_image_copy_no_overlap_dst_alpha(
410 pDstData, DstFormat, nDstStep, nXDst, nYDst, nWidth, nHeight, pSrcData, SrcFormat,
411 nSrcStep, nXSrc, nYSrc, palette, srcVMultiplier, srcVOffset, dstVMultiplier,
412 dstVOffset);
413 else
414 return generic_image_copy_no_overlap_no_alpha(
415 pDstData, DstFormat, nDstStep, nXDst, nYDst, nWidth, nHeight, pSrcData, SrcFormat,
416 nSrcStep, nXSrc, nYSrc, palette, srcVMultiplier, srcVOffset, dstVMultiplier, dstVOffset,
417 flags);
418
419 return PRIMITIVES_SUCCESS;
420}
421
422/* ------------------------------------------------------------------------- */
423void primitives_init_copy(primitives_t* WINPR_RESTRICT prims)
424{
425 /* Start with the default. */
426 prims->copy_8u = general_copy_8u;
427 prims->copy_8u_AC4r = general_copy_8u_AC4r;
428 prims->copy = WINPR_FUNC_PTR_CAST(prims->copy_8u, fn_copy_t);
429 prims->copy_no_overlap = generic_image_copy_no_overlap;
430}
431
432void primitives_init_copy_opt(primitives_t* WINPR_RESTRICT prims)
433{
434 primitives_init_copy(prims);
435 primitives_init_copy_sse41(prims);
436#if defined(WITH_AVX2)
437 primitives_init_copy_avx2(prims);
438#endif
439}