Coverage Report

Created: 2025-07-01 06:46

/src/FreeRDP/libfreerdp/codec/rfx_encode.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * FreeRDP: A Remote Desktop Protocol Implementation
3
 * RemoteFX Codec Library - Encode
4
 *
5
 * Copyright 2011 Vic Lee
6
 * Copyright 2011 Norbert Federa <norbert.federa@thincast.com>
7
 *
8
 * Licensed under the Apache License, Version 2.0 (the "License");
9
 * you may not use this file except in compliance with the License.
10
 * You may obtain a copy of the License at
11
 *
12
 *     http://www.apache.org/licenses/LICENSE-2.0
13
 *
14
 * Unless required by applicable law or agreed to in writing, software
15
 * distributed under the License is distributed on an "AS IS" BASIS,
16
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
 * See the License for the specific language governing permissions and
18
 * limitations under the License.
19
 */
20
21
#include <freerdp/config.h>
22
23
#include <stdio.h>
24
#include <stdlib.h>
25
#include <string.h>
26
27
#include <winpr/crt.h>
28
#include <winpr/collections.h>
29
30
#include <freerdp/primitives.h>
31
32
#include "rfx_types.h"
33
#include "rfx_rlgr.h"
34
#include "rfx_differential.h"
35
#include "rfx_quantization.h"
36
#include "rfx_dwt.h"
37
38
#include "rfx_encode.h"
39
40
static void rfx_encode_format_rgb(const BYTE* WINPR_RESTRICT rgb_data, uint32_t width,
41
                                  uint32_t height, uint32_t rowstride, UINT32 pixel_format,
42
                                  const BYTE* WINPR_RESTRICT palette, INT16* WINPR_RESTRICT r_buf,
43
                                  INT16* WINPR_RESTRICT g_buf, INT16* WINPR_RESTRICT b_buf)
44
0
{
45
0
  const BYTE* src = NULL;
46
0
  INT16 r = 0;
47
0
  INT16 g = 0;
48
0
  INT16 b = 0;
49
0
  INT16* r_last = NULL;
50
0
  INT16* g_last = NULL;
51
0
  INT16* b_last = NULL;
52
0
  uint32_t x_exceed = 64 - width;
53
0
  uint32_t y_exceed = 64 - height;
54
55
0
  for (uint32_t y = 0; y < height; y++)
56
0
  {
57
0
    src = rgb_data + 1ULL * y * rowstride;
58
59
0
    switch (pixel_format)
60
0
    {
61
0
      case PIXEL_FORMAT_BGRX32:
62
0
      case PIXEL_FORMAT_BGRA32:
63
0
        for (uint32_t x = 0; x < width; x++)
64
0
        {
65
0
          *b_buf++ = (INT16)(*src++);
66
0
          *g_buf++ = (INT16)(*src++);
67
0
          *r_buf++ = (INT16)(*src++);
68
0
          src++;
69
0
        }
70
71
0
        break;
72
73
0
      case PIXEL_FORMAT_XBGR32:
74
0
      case PIXEL_FORMAT_ABGR32:
75
0
        for (size_t x = 0; x < width; x++)
76
0
        {
77
0
          src++;
78
0
          *b_buf++ = (INT16)(*src++);
79
0
          *g_buf++ = (INT16)(*src++);
80
0
          *r_buf++ = (INT16)(*src++);
81
0
        }
82
83
0
        break;
84
85
0
      case PIXEL_FORMAT_RGBX32:
86
0
      case PIXEL_FORMAT_RGBA32:
87
0
        for (size_t x = 0; x < width; x++)
88
0
        {
89
0
          *r_buf++ = (INT16)(*src++);
90
0
          *g_buf++ = (INT16)(*src++);
91
0
          *b_buf++ = (INT16)(*src++);
92
0
          src++;
93
0
        }
94
95
0
        break;
96
97
0
      case PIXEL_FORMAT_XRGB32:
98
0
      case PIXEL_FORMAT_ARGB32:
99
0
        for (size_t x = 0; x < width; x++)
100
0
        {
101
0
          src++;
102
0
          *r_buf++ = (INT16)(*src++);
103
0
          *g_buf++ = (INT16)(*src++);
104
0
          *b_buf++ = (INT16)(*src++);
105
0
        }
106
107
0
        break;
108
109
0
      case PIXEL_FORMAT_BGR24:
110
0
        for (size_t x = 0; x < width; x++)
111
0
        {
112
0
          *b_buf++ = (INT16)(*src++);
113
0
          *g_buf++ = (INT16)(*src++);
114
0
          *r_buf++ = (INT16)(*src++);
115
0
        }
116
117
0
        break;
118
119
0
      case PIXEL_FORMAT_RGB24:
120
0
        for (size_t x = 0; x < width; x++)
121
0
        {
122
0
          *r_buf++ = (INT16)(*src++);
123
0
          *g_buf++ = (INT16)(*src++);
124
0
          *b_buf++ = (INT16)(*src++);
125
0
        }
126
127
0
        break;
128
129
0
      case PIXEL_FORMAT_BGR16:
130
0
        for (size_t x = 0; x < width; x++)
131
0
        {
132
0
          *b_buf++ = (INT16)(((*(src + 1)) & 0xF8) | ((*(src + 1)) >> 5));
133
0
          *g_buf++ = (INT16)((((*(src + 1)) & 0x07) << 5) | (((*src) & 0xE0) >> 3));
134
0
          *r_buf++ = (INT16)((((*src) & 0x1F) << 3) | (((*src) >> 2) & 0x07));
135
0
          src += 2;
136
0
        }
137
138
0
        break;
139
140
0
      case PIXEL_FORMAT_RGB16:
141
0
        for (size_t x = 0; x < width; x++)
142
0
        {
143
0
          *r_buf++ = (INT16)(((*(src + 1)) & 0xF8) | ((*(src + 1)) >> 5));
144
0
          *g_buf++ = (INT16)((((*(src + 1)) & 0x07) << 5) | (((*src) & 0xE0) >> 3));
145
0
          *b_buf++ = (INT16)((((*src) & 0x1F) << 3) | (((*src) >> 2) & 0x07));
146
0
          src += 2;
147
0
        }
148
149
0
        break;
150
151
0
      case PIXEL_FORMAT_RGB8:
152
0
        if (!palette)
153
0
          break;
154
155
0
        for (size_t x = 0; x < width; x++)
156
0
        {
157
0
          BYTE idx = 0;
158
0
          const size_t shift = (7 - (x % 8));
159
0
          idx = ((*src) >> shift) & 1;
160
0
          idx |= (((*(src + 1)) >> shift) & 1) << 1;
161
0
          idx |= (((*(src + 2)) >> shift) & 1) << 2;
162
0
          idx |= (((*(src + 3)) >> shift) & 1) << 3;
163
0
          idx *= 3;
164
0
          *r_buf++ = (INT16)palette[idx];
165
0
          *g_buf++ = (INT16)palette[idx + 1];
166
0
          *b_buf++ = (INT16)palette[idx + 2];
167
168
0
          if (shift == 0)
169
0
            src += 4;
170
0
        }
171
172
0
        break;
173
174
0
      case PIXEL_FORMAT_A4:
175
0
        if (!palette)
176
0
          break;
177
178
0
        for (size_t x = 0; x < width; x++)
179
0
        {
180
0
          int idx = (*src) * 3;
181
0
          *r_buf++ = (INT16)palette[idx];
182
0
          *g_buf++ = (INT16)palette[idx + 1];
183
0
          *b_buf++ = (INT16)palette[idx + 2];
184
0
          src++;
185
0
        }
186
187
0
        break;
188
189
0
      default:
190
0
        break;
191
0
    }
192
193
    /* Fill the horizontal region outside of 64x64 tile size with the right-most pixel for best
194
     * quality */
195
0
    if (x_exceed > 0)
196
0
    {
197
0
      r = *(r_buf - 1);
198
0
      g = *(g_buf - 1);
199
0
      b = *(b_buf - 1);
200
201
0
      for (size_t x = 0; x < x_exceed; x++)
202
0
      {
203
0
        *r_buf++ = r;
204
0
        *g_buf++ = g;
205
0
        *b_buf++ = b;
206
0
      }
207
0
    }
208
0
  }
209
210
  /* Fill the vertical region outside of 64x64 tile size with the last line. */
211
0
  if (y_exceed > 0)
212
0
  {
213
0
    r_last = r_buf - 64;
214
0
    g_last = g_buf - 64;
215
0
    b_last = b_buf - 64;
216
217
0
    while (y_exceed > 0)
218
0
    {
219
0
      CopyMemory(r_buf, r_last, 64 * sizeof(INT16));
220
0
      CopyMemory(g_buf, g_last, 64 * sizeof(INT16));
221
0
      CopyMemory(b_buf, b_last, 64 * sizeof(INT16));
222
0
      r_buf += 64;
223
0
      g_buf += 64;
224
0
      b_buf += 64;
225
0
      y_exceed--;
226
0
    }
227
0
  }
228
0
}
229
230
/* rfx_encode_rgb_to_ycbcr code now resides in the primitives library. */
231
232
static void rfx_encode_component(RFX_CONTEXT* WINPR_RESTRICT context,
233
                                 const UINT32* WINPR_RESTRICT quantization_values,
234
                                 INT16* WINPR_RESTRICT data, BYTE* WINPR_RESTRICT buffer,
235
                                 uint32_t buffer_size, uint32_t* WINPR_RESTRICT size)
236
0
{
237
0
  INT16* dwt_buffer = BufferPool_Take(context->priv->BufferPool, -1); /* dwt_buffer */
238
0
  PROFILER_ENTER(context->priv->prof_rfx_encode_component)
239
0
  PROFILER_ENTER(context->priv->prof_rfx_dwt_2d_encode)
240
0
  context->dwt_2d_encode(data, dwt_buffer);
241
0
  PROFILER_EXIT(context->priv->prof_rfx_dwt_2d_encode)
242
0
  PROFILER_ENTER(context->priv->prof_rfx_quantization_encode)
243
0
  context->quantization_encode(data, quantization_values);
244
0
  PROFILER_EXIT(context->priv->prof_rfx_quantization_encode)
245
0
  PROFILER_ENTER(context->priv->prof_rfx_differential_encode)
246
0
  rfx_differential_encode(data + 4032, 64);
247
0
  PROFILER_EXIT(context->priv->prof_rfx_differential_encode)
248
0
  PROFILER_ENTER(context->priv->prof_rfx_rlgr_encode)
249
0
  const int rc = context->rlgr_encode(context->mode, data, 4096, buffer, buffer_size);
250
0
  PROFILER_EXIT(context->priv->prof_rfx_rlgr_encode)
251
0
  PROFILER_EXIT(context->priv->prof_rfx_encode_component)
252
0
  BufferPool_Return(context->priv->BufferPool, dwt_buffer);
253
254
0
  *size = WINPR_ASSERTING_INT_CAST(uint32_t, rc);
255
0
}
256
257
void rfx_encode_rgb(RFX_CONTEXT* WINPR_RESTRICT context, RFX_TILE* WINPR_RESTRICT tile)
258
0
{
259
0
  union
260
0
  {
261
0
    const INT16** cpv;
262
0
    INT16** pv;
263
0
  } cnv;
264
0
  BYTE* pBuffer = NULL;
265
0
  INT16* pSrcDst[3];
266
0
  uint32_t YLen = 0;
267
0
  uint32_t CbLen = 0;
268
0
  uint32_t CrLen = 0;
269
0
  UINT32* YQuant = NULL;
270
0
  UINT32* CbQuant = NULL;
271
0
  UINT32* CrQuant = NULL;
272
0
  primitives_t* prims = primitives_get();
273
0
  static const prim_size_t roi_64x64 = { 64, 64 };
274
275
0
  if (!(pBuffer = (BYTE*)BufferPool_Take(context->priv->BufferPool, -1)))
276
0
    return;
277
278
0
  YLen = CbLen = CrLen = 0;
279
0
  YQuant = context->quants + (10ULL * tile->quantIdxY);
280
0
  CbQuant = context->quants + (10ULL * tile->quantIdxCb);
281
0
  CrQuant = context->quants + (10ULL * tile->quantIdxCr);
282
0
  pSrcDst[0] = (INT16*)((&pBuffer[((8192ULL + 32ULL) * 0ULL) + 16ULL])); /* y_r_buffer */
283
0
  pSrcDst[1] = (INT16*)((&pBuffer[((8192ULL + 32ULL) * 1ULL) + 16ULL])); /* cb_g_buffer */
284
0
  pSrcDst[2] = (INT16*)((&pBuffer[((8192ULL + 32ULL) * 2ULL) + 16ULL])); /* cr_b_buffer */
285
0
  PROFILER_ENTER(context->priv->prof_rfx_encode_rgb)
286
0
  PROFILER_ENTER(context->priv->prof_rfx_encode_format_rgb)
287
0
  rfx_encode_format_rgb(tile->data, tile->width, tile->height, tile->scanline,
288
0
                        context->pixel_format, context->palette, pSrcDst[0], pSrcDst[1],
289
0
                        pSrcDst[2]);
290
0
  PROFILER_EXIT(context->priv->prof_rfx_encode_format_rgb)
291
0
  PROFILER_ENTER(context->priv->prof_rfx_rgb_to_ycbcr)
292
293
0
  cnv.pv = pSrcDst;
294
0
  prims->RGBToYCbCr_16s16s_P3P3(cnv.cpv, 64 * sizeof(INT16), pSrcDst, 64 * sizeof(INT16),
295
0
                                &roi_64x64);
296
0
  PROFILER_EXIT(context->priv->prof_rfx_rgb_to_ycbcr)
297
  /**
298
   * We need to clear the buffers as the RLGR encoder expects it to be initialized to zero.
299
   * This allows simplifying and improving the performance of the encoding process.
300
   */
301
0
  ZeroMemory(tile->YData, 4096);
302
0
  ZeroMemory(tile->CbData, 4096);
303
0
  ZeroMemory(tile->CrData, 4096);
304
0
  rfx_encode_component(context, YQuant, pSrcDst[0], tile->YData, 4096, &YLen);
305
0
  rfx_encode_component(context, CbQuant, pSrcDst[1], tile->CbData, 4096, &CbLen);
306
0
  rfx_encode_component(context, CrQuant, pSrcDst[2], tile->CrData, 4096, &CrLen);
307
0
  tile->YLen = WINPR_ASSERTING_INT_CAST(UINT16, YLen);
308
0
  tile->CbLen = WINPR_ASSERTING_INT_CAST(UINT16, CbLen);
309
0
  tile->CrLen = WINPR_ASSERTING_INT_CAST(UINT16, CrLen);
310
0
  PROFILER_EXIT(context->priv->prof_rfx_encode_rgb)
311
0
  BufferPool_Return(context->priv->BufferPool, pBuffer);
312
0
}