Coverage Report

Created: 2025-07-01 06:46

/src/FreeRDP/libfreerdp/gdi/line.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * FreeRDP: A Remote Desktop Protocol Implementation
3
 * GDI Line Functions
4
 *
5
 * Copyright 2010-2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6
 * Copyright 2016 Armin Novak <armin.novak@thincast.com>
7
 * Copyright 2016 Thincast Technologies GmbH
8
 *
9
 * Licensed under the Apache License, Version 2.0 (the "License");
10
 * you may not use this file except in compliance with the License.
11
 * You may obtain a copy of the License at
12
 *
13
 *     http://www.apache.org/licenses/LICENSE-2.0
14
 *
15
 * Unless required by applicable law or agreed to in writing, software
16
 * distributed under the License is distributed on an "AS IS" BASIS,
17
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18
 * See the License for the specific language governing permissions and
19
 * limitations under the License.
20
 */
21
22
#include <freerdp/config.h>
23
24
#include <stdio.h>
25
#include <string.h>
26
#include <stdlib.h>
27
28
#include <winpr/assert.h>
29
30
#include <freerdp/freerdp.h>
31
#include <freerdp/gdi/gdi.h>
32
#include <freerdp/gdi/pen.h>
33
#include <freerdp/gdi/bitmap.h>
34
#include <freerdp/gdi/region.h>
35
36
#include "drawing.h"
37
#include "clipping.h"
38
#include "line.h"
39
40
static BOOL gdi_rop_color(INT32 rop, BYTE* pixelPtr, UINT32 pen, UINT32 format)
41
0
{
42
0
  WINPR_ASSERT(pixelPtr);
43
0
  const UINT32 srcPixel = FreeRDPReadColor(pixelPtr, format);
44
0
  UINT32 dstPixel = 0;
45
46
0
  switch (rop)
47
0
  {
48
0
    case GDI_R2_BLACK: /* LineTo_BLACK */
49
0
      dstPixel = FreeRDPGetColor(format, 0, 0, 0, 0xFF);
50
0
      break;
51
52
0
    case GDI_R2_NOTMERGEPEN: /* LineTo_NOTMERGEPEN */
53
0
      dstPixel = ~(srcPixel | pen);
54
0
      break;
55
56
0
    case GDI_R2_MASKNOTPEN: /* LineTo_MASKNOTPEN */
57
0
      dstPixel = srcPixel & ~pen;
58
0
      break;
59
60
0
    case GDI_R2_NOTCOPYPEN: /* LineTo_NOTCOPYPEN */
61
0
      dstPixel = ~pen;
62
0
      break;
63
64
0
    case GDI_R2_MASKPENNOT: /* LineTo_MASKPENNOT */
65
0
      dstPixel = pen & ~srcPixel;
66
0
      break;
67
68
0
    case GDI_R2_NOT: /* LineTo_NOT */
69
0
      dstPixel = ~srcPixel;
70
0
      break;
71
72
0
    case GDI_R2_XORPEN: /* LineTo_XORPEN */
73
0
      dstPixel = srcPixel ^ pen;
74
0
      break;
75
76
0
    case GDI_R2_NOTMASKPEN: /* LineTo_NOTMASKPEN */
77
0
      dstPixel = ~(srcPixel & pen);
78
0
      break;
79
80
0
    case GDI_R2_MASKPEN: /* LineTo_MASKPEN */
81
0
      dstPixel = srcPixel & pen;
82
0
      break;
83
84
0
    case GDI_R2_NOTXORPEN: /* LineTo_NOTXORPEN */
85
0
      dstPixel = ~(srcPixel ^ pen);
86
0
      break;
87
88
0
    case GDI_R2_NOP: /* LineTo_NOP */
89
0
      dstPixel = srcPixel;
90
0
      break;
91
92
0
    case GDI_R2_MERGENOTPEN: /* LineTo_MERGENOTPEN */
93
0
      dstPixel = srcPixel | ~pen;
94
0
      break;
95
96
0
    case GDI_R2_COPYPEN: /* LineTo_COPYPEN */
97
0
      dstPixel = pen;
98
0
      break;
99
100
0
    case GDI_R2_MERGEPENNOT: /* LineTo_MERGEPENNOT */
101
0
      dstPixel = srcPixel | ~pen;
102
0
      break;
103
104
0
    case GDI_R2_MERGEPEN: /* LineTo_MERGEPEN */
105
0
      dstPixel = srcPixel | pen;
106
0
      break;
107
108
0
    case GDI_R2_WHITE: /* LineTo_WHITE */
109
0
      dstPixel = FreeRDPGetColor(format, 0xFF, 0xFF, 0xFF, 0xFF);
110
0
      break;
111
112
0
    default:
113
0
      return FALSE;
114
0
  }
115
116
0
  return FreeRDPWriteColor(pixelPtr, format, dstPixel);
117
0
}
118
119
BOOL gdi_LineTo(HGDI_DC hdc, INT32 nXEnd, INT32 nYEnd)
120
0
{
121
0
  INT32 e2 = 0;
122
0
  UINT32 pen = 0;
123
124
0
  WINPR_ASSERT(hdc);
125
0
  const INT32 rop2 = gdi_GetROP2(hdc);
126
127
0
  const INT32 x1 = hdc->pen->posX;
128
0
  const INT32 y1 = hdc->pen->posY;
129
0
  const INT32 x2 = WINPR_ASSERTING_INT_CAST(int32_t, nXEnd);
130
0
  const INT32 y2 = WINPR_ASSERTING_INT_CAST(int32_t, nYEnd);
131
0
  const INT32 dx = (x1 > x2) ? x1 - x2 : x2 - x1;
132
0
  const INT32 dy = (y1 > y2) ? y1 - y2 : y2 - y1;
133
0
  const INT32 sx = (x1 < x2) ? 1 : -1;
134
0
  const INT32 sy = (y1 < y2) ? 1 : -1;
135
0
  INT32 e = dx - dy;
136
0
  INT32 x = x1;
137
0
  INT32 y = y1;
138
139
0
  WINPR_ASSERT(hdc->clip);
140
0
  INT32 bx1 = 0;
141
0
  INT32 by1 = 0;
142
0
  INT32 bx2 = 0;
143
0
  INT32 by2 = 0;
144
0
  if (hdc->clip->null)
145
0
  {
146
0
    bx1 = (x1 < x2) ? x1 : x2;
147
0
    by1 = (y1 < y2) ? y1 : y2;
148
0
    bx2 = (x1 > x2) ? x1 : x2;
149
0
    by2 = (y1 > y2) ? y1 : y2;
150
0
  }
151
0
  else
152
0
  {
153
0
    bx1 = hdc->clip->x;
154
0
    by1 = hdc->clip->y;
155
0
    bx2 = bx1 + hdc->clip->w - 1;
156
0
    by2 = by1 + hdc->clip->h - 1;
157
0
  }
158
159
0
  HGDI_BITMAP bmp = (HGDI_BITMAP)hdc->selectedObject;
160
0
  WINPR_ASSERT(bmp);
161
162
0
  bx1 = MAX(bx1, 0);
163
0
  by1 = MAX(by1, 0);
164
0
  bx2 = MIN(bx2, bmp->width - 1);
165
0
  by2 = MIN(by2, bmp->height - 1);
166
167
0
  if (!gdi_InvalidateRegion(hdc, bx1, by1, bx2 - bx1 + 1, by2 - by1 + 1))
168
0
    return FALSE;
169
170
0
  pen = gdi_GetPenColor(hdc->pen, bmp->format);
171
172
0
  while (1)
173
0
  {
174
0
    if (!(x == x2 && y == y2))
175
0
    {
176
0
      if ((x >= bx1 && x <= bx2) && (y >= by1 && y <= by2))
177
0
      {
178
0
        BYTE* pixel = gdi_GetPointer(bmp, WINPR_ASSERTING_INT_CAST(uint32_t, x),
179
0
                                     WINPR_ASSERTING_INT_CAST(uint32_t, y));
180
0
        WINPR_ASSERT(pixel);
181
0
        gdi_rop_color(rop2, pixel, pen, bmp->format);
182
0
      }
183
0
    }
184
0
    else
185
0
    {
186
0
      break;
187
0
    }
188
189
0
    e2 = 2 * e;
190
191
0
    if (e2 > -dy)
192
0
    {
193
0
      e -= dy;
194
0
      x += sx;
195
0
    }
196
197
0
    if (e2 < dx)
198
0
    {
199
0
      e += dx;
200
0
      y += sy;
201
0
    }
202
0
  }
203
204
0
  return TRUE;
205
0
}
206
207
/**
208
 * Draw one or more straight lines
209
 * @param hdc device context
210
 * @param lppt array of points
211
 * @param cCount number of points
212
 * @return nonzero on success, 0 otherwise
213
 */
214
BOOL gdi_PolylineTo(HGDI_DC hdc, GDI_POINT* lppt, DWORD cCount)
215
0
{
216
0
  WINPR_ASSERT(hdc);
217
0
  WINPR_ASSERT(lppt || (cCount == 0));
218
219
0
  for (DWORD i = 0; i < cCount; i++)
220
0
  {
221
0
    if (!gdi_LineTo(hdc, lppt[i].x, lppt[i].y))
222
0
      return FALSE;
223
224
0
    if (!gdi_MoveToEx(hdc, lppt[i].x, lppt[i].y, NULL))
225
0
      return FALSE;
226
0
  }
227
228
0
  return TRUE;
229
0
}
230
231
/**
232
 * Draw one or more straight lines
233
 * @param hdc device context
234
 * @param lppt array of points
235
 * @param cPoints number of points
236
 * @return nonzero on success, 0 otherwise
237
 */
238
BOOL gdi_Polyline(HGDI_DC hdc, GDI_POINT* lppt, UINT32 cPoints)
239
0
{
240
0
  WINPR_ASSERT(hdc);
241
0
  WINPR_ASSERT(lppt || (cPoints == 0));
242
243
0
  if (cPoints > 0)
244
0
  {
245
0
    GDI_POINT pt = { 0 };
246
247
0
    if (!gdi_MoveToEx(hdc, lppt[0].x, lppt[0].y, &pt))
248
0
      return FALSE;
249
250
0
    for (UINT32 i = 0; i < cPoints; i++)
251
0
    {
252
0
      if (!gdi_LineTo(hdc, lppt[i].x, lppt[i].y))
253
0
        return FALSE;
254
255
0
      if (!gdi_MoveToEx(hdc, lppt[i].x, lppt[i].y, NULL))
256
0
        return FALSE;
257
0
    }
258
259
0
    if (!gdi_MoveToEx(hdc, pt.x, pt.y, NULL))
260
0
      return FALSE;
261
0
  }
262
263
0
  return TRUE;
264
0
}
265
266
/**
267
 * Draw multiple series of connected line segments
268
 * @param hdc device context
269
 * @param lppt array of points
270
 * @param lpdwPolyPoints array of numbers of points per series
271
 * @param cCount count of entries in lpdwPolyPoints
272
 * @return nonzero on success, 0 otherwise
273
 */
274
BOOL gdi_PolyPolyline(HGDI_DC hdc, GDI_POINT* lppt, const UINT32* lpdwPolyPoints, DWORD cCount)
275
0
{
276
0
  DWORD j = 0;
277
278
0
  WINPR_ASSERT(hdc);
279
0
  WINPR_ASSERT(lppt || (cCount == 0));
280
0
  WINPR_ASSERT(lpdwPolyPoints || (cCount == 0));
281
282
0
  for (DWORD i = 0; i < cCount; i++)
283
0
  {
284
0
    const UINT32 cPoints = lpdwPolyPoints[i];
285
286
0
    if (!gdi_Polyline(hdc, &lppt[j], cPoints))
287
0
      return FALSE;
288
289
0
    j += cPoints;
290
0
  }
291
292
0
  return TRUE;
293
0
}
294
295
/**
296
 * Move pen from the current device context to a new position.
297
 * @param hdc device context
298
 * @param X x position
299
 * @param Y y position
300
 * @return nonzero on success, 0 otherwise
301
 */
302
303
BOOL gdi_MoveToEx(HGDI_DC hdc, INT32 X, INT32 Y, HGDI_POINT lpPoint)
304
0
{
305
0
  WINPR_ASSERT(hdc);
306
307
0
  if (lpPoint != NULL)
308
0
  {
309
0
    lpPoint->x = hdc->pen->posX;
310
0
    lpPoint->y = hdc->pen->posY;
311
0
  }
312
313
0
  hdc->pen->posX = X;
314
0
  hdc->pen->posY = Y;
315
0
  return TRUE;
316
0
}