Coverage Report

Created: 2026-02-26 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/FreeRDP/libfreerdp/primitives/prim_copy.c
Line
Count
Source
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
29
static primitives_t* generic = NULL;
30
31
/* ------------------------------------------------------------------------- */
32
/*static inline BOOL memory_regions_overlap_1d(*/
33
static BOOL memory_regions_overlap_1d(const BYTE* p1, const BYTE* p2, size_t bytes)
34
0
{
35
0
  const ULONG_PTR p1m = (const ULONG_PTR)p1;
36
0
  const ULONG_PTR p2m = (const ULONG_PTR)p2;
37
38
0
  if (p1m <= p2m)
39
0
  {
40
0
    if (p1m + bytes > p2m)
41
0
      return TRUE;
42
0
  }
43
0
  else
44
0
  {
45
0
    if (p2m + bytes > p1m)
46
0
      return TRUE;
47
0
  }
48
49
  /* else */
50
0
  return FALSE;
51
0
}
52
53
/* ------------------------------------------------------------------------- */
54
/*static inline BOOL memory_regions_overlap_2d( */
55
static 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
0
{
58
0
  ULONG_PTR p1m = (ULONG_PTR)p1;
59
0
  ULONG_PTR p2m = (ULONG_PTR)p2;
60
61
0
  if (p1m <= p2m)
62
0
  {
63
0
    ULONG_PTR p1mEnd = p1m +
64
0
                       1ull * (WINPR_ASSERTING_INT_CAST(uint32_t, height - 1)) *
65
0
                           WINPR_ASSERTING_INT_CAST(uint32_t, p1Step) +
66
0
                       1ull * WINPR_ASSERTING_INT_CAST(uint32_t, width* p1Size);
67
68
0
    if (p1mEnd > p2m)
69
0
      return TRUE;
70
0
  }
71
0
  else
72
0
  {
73
0
    ULONG_PTR p2mEnd = p2m +
74
0
                       1ull * (WINPR_ASSERTING_INT_CAST(uintptr_t, height - 1)) *
75
0
                           WINPR_ASSERTING_INT_CAST(uintptr_t, p2Step) +
76
0
                       1ull * WINPR_ASSERTING_INT_CAST(uintptr_t, width* p2Size);
77
78
0
    if (p2mEnd > p1m)
79
0
      return TRUE;
80
0
  }
81
82
  /* else */
83
0
  return FALSE;
84
0
}
85
86
/* ------------------------------------------------------------------------- */
87
static pstatus_t general_copy_8u(const BYTE* WINPR_RESTRICT pSrc, BYTE* WINPR_RESTRICT pDst,
88
                                 INT32 len)
89
0
{
90
0
  if (memory_regions_overlap_1d(pSrc, pDst, (size_t)len))
91
0
  {
92
0
    memmove((void*)pDst, (const void*)pSrc, (size_t)len);
93
0
  }
94
0
  else
95
0
  {
96
0
    memcpy((void*)pDst, (const void*)pSrc, (size_t)len);
97
0
  }
98
99
0
  return PRIMITIVES_SUCCESS;
100
0
}
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
 */
107
static 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
0
{
111
0
  const BYTE* src = pSrc;
112
0
  BYTE* dst = pDst;
113
0
  const size_t rowbytes = WINPR_ASSERTING_INT_CAST(size_t, width) * sizeof(UINT32);
114
115
0
  if ((width == 0) || (height == 0))
116
0
    return PRIMITIVES_SUCCESS;
117
118
0
  if (memory_regions_overlap_2d(pSrc, srcStep, sizeof(UINT32), pDst, dstStep, sizeof(UINT32),
119
0
                                width, height))
120
0
  {
121
0
    do
122
0
    {
123
0
      const pstatus_t rc =
124
0
          generic->copy(src, dst, WINPR_ASSERTING_INT_CAST(int32_t, rowbytes));
125
0
      if (rc != PRIMITIVES_SUCCESS)
126
0
        return rc;
127
128
0
      src += srcStep;
129
0
      dst += dstStep;
130
0
    } while (--height);
131
0
  }
132
0
  else
133
0
  {
134
    /* TODO: do it in one operation when the rowdata is adjacent. */
135
0
    do
136
0
    {
137
      /* If we find a replacement for memcpy that is consistently
138
       * faster, this could be replaced with that.
139
       */
140
0
      memcpy(dst, src, rowbytes);
141
0
      src += srcStep;
142
0
      dst += dstStep;
143
0
    } while (--height);
144
0
  }
145
146
0
  return PRIMITIVES_SUCCESS;
147
0
}
148
149
static 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
0
{
157
158
0
  const int64_t srcByte = 3;
159
0
  const int64_t dstByte = 4;
160
161
0
  const UINT32 width = nWidth - nWidth % 8;
162
163
0
  for (int64_t y = 0; y < nHeight; y++)
164
0
  {
165
0
    const BYTE* WINPR_RESTRICT srcLine =
166
0
        &pSrcData[srcVMultiplier * (y + nYSrc) * nSrcStep + srcVOffset];
167
0
    BYTE* WINPR_RESTRICT dstLine =
168
0
        &pDstData[dstVMultiplier * (y + nYDst) * nDstStep + dstVOffset];
169
170
0
    int64_t x = 0;
171
0
    WINPR_PRAGMA_UNROLL_LOOP
172
0
    for (; x < width; x++)
173
0
    {
174
0
      dstLine[(x + nXDst) * dstByte + 0] = srcLine[(x + nXSrc) * srcByte + 0];
175
0
      dstLine[(x + nXDst) * dstByte + 1] = srcLine[(x + nXSrc) * srcByte + 1];
176
0
      dstLine[(x + nXDst) * dstByte + 2] = srcLine[(x + nXSrc) * srcByte + 2];
177
0
    }
178
179
0
    for (; x < nWidth; x++)
180
0
    {
181
0
      dstLine[(x + nXDst) * dstByte + 0] = srcLine[(x + nXSrc) * srcByte + 0];
182
0
      dstLine[(x + nXDst) * dstByte + 1] = srcLine[(x + nXSrc) * srcByte + 1];
183
0
      dstLine[(x + nXDst) * dstByte + 2] = srcLine[(x + nXSrc) * srcByte + 2];
184
0
    }
185
0
  }
186
187
0
  return PRIMITIVES_SUCCESS;
188
0
}
189
190
static inline pstatus_t
191
generic_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
0
{
197
198
0
  const int64_t srcByte = 4;
199
0
  const int64_t dstByte = 4;
200
201
0
  const UINT32 width = nWidth - nWidth % 8;
202
203
0
  for (int64_t y = 0; y < nHeight; y++)
204
0
  {
205
0
    const BYTE* WINPR_RESTRICT srcLine =
206
0
        &pSrcData[srcVMultiplier * (y + nYSrc) * nSrcStep + srcVOffset];
207
0
    BYTE* WINPR_RESTRICT dstLine =
208
0
        &pDstData[dstVMultiplier * (y + nYDst) * nDstStep + dstVOffset];
209
210
0
    int64_t x = 0;
211
0
    WINPR_PRAGMA_UNROLL_LOOP
212
0
    for (; x < width; x++)
213
0
    {
214
0
      dstLine[(x + nXDst) * dstByte + 0] = srcLine[(x + nXSrc) * srcByte + 0];
215
0
      dstLine[(x + nXDst) * dstByte + 1] = srcLine[(x + nXSrc) * srcByte + 1];
216
0
      dstLine[(x + nXDst) * dstByte + 2] = srcLine[(x + nXSrc) * srcByte + 2];
217
0
    }
218
0
    for (; x < nWidth; x++)
219
0
    {
220
0
      dstLine[(x + nXDst) * dstByte + 0] = srcLine[(x + nXSrc) * srcByte + 0];
221
0
      dstLine[(x + nXDst) * dstByte + 1] = srcLine[(x + nXSrc) * srcByte + 1];
222
0
      dstLine[(x + nXDst) * dstByte + 2] = srcLine[(x + nXSrc) * srcByte + 2];
223
0
    }
224
0
  }
225
226
0
  return PRIMITIVES_SUCCESS;
227
0
}
228
229
pstatus_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
5.23k
{
235
5.23k
  const int64_t srcByte = FreeRDPGetBytesPerPixel(SrcFormat);
236
5.23k
  const int64_t dstByte = FreeRDPGetBytesPerPixel(DstFormat);
237
238
5.23k
  const UINT32 width = nWidth - nWidth % 8;
239
279k
  for (int64_t y = 0; y < nHeight; y++)
240
274k
  {
241
274k
    const BYTE* WINPR_RESTRICT srcLine =
242
274k
        &pSrcData[srcVMultiplier * (y + nYSrc) * nSrcStep + srcVOffset];
243
274k
    BYTE* WINPR_RESTRICT dstLine =
244
274k
        &pDstData[dstVMultiplier * (y + nYDst) * nDstStep + dstVOffset];
245
246
274k
    int64_t x = 0;
247
    // WINPR_PRAGMA_UNROLL_LOOP
248
17.7M
    for (; x < width; x++)
249
17.4M
    {
250
17.4M
      const UINT32 color = FreeRDPReadColor_int(&srcLine[(x + nXSrc) * srcByte], SrcFormat);
251
17.4M
      const UINT32 dstColor = FreeRDPConvertColor(color, SrcFormat, DstFormat, palette);
252
17.4M
      if (!FreeRDPWriteColor_int(&dstLine[(x + nXDst) * dstByte], DstFormat, dstColor))
253
643
        return -1;
254
17.4M
    }
255
281k
    for (; x < nWidth; x++)
256
7.61k
    {
257
7.61k
      const UINT32 color = FreeRDPReadColor_int(&srcLine[(x + nXSrc) * srcByte], SrcFormat);
258
7.61k
      const UINT32 dstColor = FreeRDPConvertColor(color, SrcFormat, DstFormat, palette);
259
7.61k
      if (!FreeRDPWriteColor_int(&dstLine[(x + nXDst) * dstByte], DstFormat, dstColor))
260
0
        return -1;
261
7.61k
    }
262
274k
  }
263
4.58k
  return PRIMITIVES_SUCCESS;
264
5.23k
}
265
266
pstatus_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
181
{
273
181
  const int64_t dstByte = FreeRDPGetBytesPerPixel(DstFormat);
274
181
  const int64_t srcByte = FreeRDPGetBytesPerPixel(SrcFormat);
275
181
  const int64_t copyDstWidth = nWidth * dstByte;
276
181
  const int64_t xSrcOffset = nXSrc * srcByte;
277
181
  const int64_t xDstOffset = nXDst * dstByte;
278
279
3.63k
  for (int64_t y = 0; y < nHeight; y++)
280
3.45k
  {
281
3.45k
    const BYTE* WINPR_RESTRICT srcLine =
282
3.45k
        &pSrcData[srcVMultiplier * (y + nYSrc) * nSrcStep + srcVOffset];
283
3.45k
    BYTE* WINPR_RESTRICT dstLine =
284
3.45k
        &pDstData[dstVMultiplier * (y + nYDst) * nDstStep + dstVOffset];
285
3.45k
    memcpy(&dstLine[xDstOffset], &srcLine[xSrcOffset],
286
3.45k
           WINPR_ASSERTING_INT_CAST(size_t, copyDstWidth));
287
3.45k
  }
288
289
181
  return PRIMITIVES_SUCCESS;
290
181
}
291
292
static 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
0
{
298
0
  WINPR_ASSERT(pDstData);
299
0
  WINPR_ASSERT(pSrcData);
300
301
0
  switch (SrcFormat)
302
0
  {
303
0
    case PIXEL_FORMAT_BGR24:
304
0
      switch (DstFormat)
305
0
      {
306
0
        case PIXEL_FORMAT_BGRX32:
307
0
        case PIXEL_FORMAT_BGRA32:
308
0
          return generic_image_copy_bgr24_bgrx32(
309
0
              pDstData, nDstStep, nXDst, nYDst, nWidth, nHeight, pSrcData, nSrcStep,
310
0
              nXSrc, nYSrc, srcVMultiplier, srcVOffset, dstVMultiplier, dstVOffset);
311
0
        default:
312
0
          break;
313
0
      }
314
0
      break;
315
0
    case PIXEL_FORMAT_BGRX32:
316
0
    case PIXEL_FORMAT_BGRA32:
317
0
      switch (DstFormat)
318
0
      {
319
0
        case PIXEL_FORMAT_BGRX32:
320
0
        case PIXEL_FORMAT_BGRA32:
321
0
          return generic_image_copy_bgrx32_bgrx32(
322
0
              pDstData, nDstStep, nXDst, nYDst, nWidth, nHeight, pSrcData, nSrcStep,
323
0
              nXSrc, nYSrc, srcVMultiplier, srcVOffset, dstVMultiplier, dstVOffset);
324
0
        default:
325
0
          break;
326
0
      }
327
0
      break;
328
0
    case PIXEL_FORMAT_RGBX32:
329
0
    case PIXEL_FORMAT_RGBA32:
330
0
      switch (DstFormat)
331
0
      {
332
0
        case PIXEL_FORMAT_RGBX32:
333
0
        case PIXEL_FORMAT_RGBA32:
334
0
          return generic_image_copy_bgrx32_bgrx32(
335
0
              pDstData, nDstStep, nXDst, nYDst, nWidth, nHeight, pSrcData, nSrcStep,
336
0
              nXSrc, nYSrc, srcVMultiplier, srcVOffset, dstVMultiplier, dstVOffset);
337
0
        case PIXEL_FORMAT_RGB24:
338
0
          return generic_image_copy_bgr24_bgrx32(
339
0
              pDstData, nDstStep, nXDst, nYDst, nWidth, nHeight, pSrcData, nSrcStep,
340
0
              nXSrc, nYSrc, srcVMultiplier, srcVOffset, dstVMultiplier, dstVOffset);
341
0
        default:
342
0
          break;
343
0
      }
344
0
      break;
345
0
    default:
346
0
      break;
347
0
  }
348
349
0
  return generic_image_copy_no_overlap_convert(
350
0
      pDstData, DstFormat, nDstStep, nXDst, nYDst, nWidth, nHeight, pSrcData, SrcFormat, nSrcStep,
351
0
      nXSrc, nYSrc, palette, srcVMultiplier, srcVOffset, dstVMultiplier, dstVOffset);
352
0
}
353
354
static 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
5.23k
{
361
5.23k
  if (FreeRDPAreColorFormatsEqualNoAlpha(SrcFormat, DstFormat))
362
0
    return generic_image_copy_no_overlap_memcpy(pDstData, DstFormat, nDstStep, nXDst, nYDst,
363
0
                                                nWidth, nHeight, pSrcData, SrcFormat, nSrcStep,
364
0
                                                nXSrc, nYSrc, palette, srcVMultiplier,
365
0
                                                srcVOffset, dstVMultiplier, dstVOffset, flags);
366
5.23k
  else
367
5.23k
    return generic_image_copy_no_overlap_convert(pDstData, DstFormat, nDstStep, nXDst, nYDst,
368
5.23k
                                                 nWidth, nHeight, pSrcData, SrcFormat, nSrcStep,
369
5.23k
                                                 nXSrc, nYSrc, palette, srcVMultiplier,
370
5.23k
                                                 srcVOffset, dstVMultiplier, dstVOffset);
371
5.23k
}
372
373
static 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
5.23k
{
381
5.23k
  const BOOL vSrcVFlip = (flags & FREERDP_FLIP_VERTICAL) ? TRUE : FALSE;
382
5.23k
  int64_t srcVOffset = 0;
383
5.23k
  int64_t srcVMultiplier = 1;
384
5.23k
  int64_t dstVOffset = 0;
385
5.23k
  int64_t dstVMultiplier = 1;
386
387
5.23k
  if ((nWidth == 0) || (nHeight == 0))
388
0
    return PRIMITIVES_SUCCESS;
389
390
5.23k
  if ((nHeight > INT32_MAX) || (nWidth > INT32_MAX))
391
0
    return -1;
392
393
5.23k
  if (!pDstData || !pSrcData)
394
0
    return -1;
395
396
5.23k
  if (nDstStep == 0)
397
643
    nDstStep = nWidth * FreeRDPGetBytesPerPixel(DstFormat);
398
399
5.23k
  if (nSrcStep == 0)
400
0
    nSrcStep = nWidth * FreeRDPGetBytesPerPixel(SrcFormat);
401
402
5.23k
  if (vSrcVFlip)
403
4.25k
  {
404
4.25k
    srcVOffset = (nHeight - 1ll) * nSrcStep;
405
4.25k
    srcVMultiplier = -1;
406
4.25k
  }
407
408
5.23k
  if (((flags & FREERDP_KEEP_DST_ALPHA) != 0) && FreeRDPColorHasAlpha(DstFormat))
409
0
    return generic_image_copy_no_overlap_dst_alpha(
410
0
        pDstData, DstFormat, nDstStep, nXDst, nYDst, nWidth, nHeight, pSrcData, SrcFormat,
411
0
        nSrcStep, nXSrc, nYSrc, palette, srcVMultiplier, srcVOffset, dstVMultiplier,
412
0
        dstVOffset);
413
5.23k
  else
414
5.23k
    return generic_image_copy_no_overlap_no_alpha(
415
5.23k
        pDstData, DstFormat, nDstStep, nXDst, nYDst, nWidth, nHeight, pSrcData, SrcFormat,
416
5.23k
        nSrcStep, nXSrc, nYSrc, palette, srcVMultiplier, srcVOffset, dstVMultiplier, dstVOffset,
417
5.23k
        flags);
418
419
0
  return PRIMITIVES_SUCCESS;
420
5.23k
}
421
422
/* ------------------------------------------------------------------------- */
423
void primitives_init_copy(primitives_t* WINPR_RESTRICT prims)
424
3
{
425
  /* Start with the default. */
426
3
  prims->copy_8u = general_copy_8u;
427
3
  prims->copy_8u_AC4r = general_copy_8u_AC4r;
428
3
  prims->copy = WINPR_FUNC_PTR_CAST(prims->copy_8u, fn_copy_t);
429
3
  prims->copy_no_overlap = generic_image_copy_no_overlap;
430
3
}
431
432
void primitives_init_copy_opt(primitives_t* WINPR_RESTRICT prims)
433
1
{
434
1
  primitives_init_copy(prims);
435
1
  primitives_init_copy_sse41(prims);
436
1
#if defined(WITH_AVX2)
437
1
  primitives_init_copy_avx2(prims);
438
1
#endif
439
1
}