/** * FreeRDP: A Remote Desktop Protocol Implementation * GDI Line Functions * * Copyright 2010-2011 Marc-Andre Moreau * Copyright 2016 Armin Novak * Copyright 2016 Thincast Technologies GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include "drawing.h" #include "clipping.h" #include "line.h" /** * Draw a line from the current position to the given position.\n * @msdn{dd145029} * @param hdc device context * @param nXEnd ending x position * @param nYEnd ending y position * @return nonzero if successful, 0 otherwise */ static BOOL gdi_rop_color(UINT32 rop, BYTE* pixelPtr, UINT32 pen, UINT32 format) { UINT32 pixel = ReadColor(pixelPtr, format); switch(rop) { case 1: /* LineTo_BLACK */ pixel = GetColor(format, 0, 0, 0, 0xFF); break; case 2: /* LineTo_NOTMERGEPEN */ pixel = ~(pixel | pen); break; case 3: /* LineTo_MASKNOTPEN */ pixel &= ~pen; break; case 4: /* LineTo_NOTCOPYPEN */ pixel = ~pen; break; case 5: /* LineTo_MASKPENNOT */ pixel = pen & ~pixel; break; case 6: /* LineTo_NOT */ pixel = ~pixel; break; case 7: /* LineTo_XORPEN */ pixel = pixel ^ pen; break; case 8: /* LineTo_NOTMASKPEN */ pixel = ~(pixel & pen); break; case 9: /* LineTo_MASKPEN */ pixel &= pen; break; case 10: /* LineTo_NOTXORPEN */ pixel = ~(pixel ^ pen); break; case 11: /* LineTo_NOP */ break; case 12: /* LineTo_MERGENOTPEN */ pixel |= ~pen; break; case 13: /* LineTo_COPYPEN */ pixel = pen; break; case 14: /* LineTo_MERGEPENNOT */ pixel = pixel | ~pen; break; case 15: /* LineTo_MERGEPEN */ pixel = pixel | pen; break; case 16: /* LineTo_WHITE */ pixel = GetColor(format, 0, 0, 0, 0); break; default: return FALSE; } WriteColor(pixelPtr, format, pixel); return TRUE; } BOOL gdi_LineTo(HGDI_DC hdc, UINT32 nXEnd, UINT32 nYEnd) { UINT32 x, y; UINT32 x1, y1; UINT32 x2, y2; INT32 e, e2; INT32 dx, dy; INT32 sx, sy; INT32 bx1, by1; INT32 bx2, by2; HGDI_BITMAP bmp; UINT32 pen; UINT32 rop2 = gdi_GetROP2(hdc); x1 = hdc->pen->posX; y1 = hdc->pen->posY; x2 = nXEnd; y2 = nYEnd; dx = (x1 > x2) ? x1 - x2 : x2 - x1; dy = (y1 > y2) ? y1 - y2 : y2 - y1; sx = (x1 < x2) ? 1 : -1; sy = (y1 < y2) ? 1 : -1; e = dx - dy; x = x1; y = y1; bmp = (HGDI_BITMAP) hdc->selectedObject; if (hdc->clip->null) { bx1 = (x1 < x2) ? x1 : x2; by1 = (y1 < y2) ? y1 : y2; bx2 = (x1 > x2) ? x1 : x2; by2 = (y1 > y2) ? y1 : y2; } else { bx1 = hdc->clip->x; by1 = hdc->clip->y; bx2 = bx1 + hdc->clip->w - 1; by2 = by1 + hdc->clip->h - 1; } bx1 = MAX(bx1, 0); by1 = MAX(by1, 0); bx2 = MIN(bx2, bmp->width - 1); by2 = MIN(by2, bmp->height - 1); if (!gdi_InvalidateRegion(hdc, bx1, by1, bx2 - bx1 + 1, by2 - by1 + 1)) return FALSE; pen = gdi_GetPenColor(hdc->pen, bmp->format); while (1) { if (!(x == x2 && y == y2)) { if ((x >= bx1 && x <= bx2) && (y >= by1 && y <= by2)) { BYTE* pixel = gdi_GetPointer(bmp, x, y); gdi_rop_color(rop2, pixel, pen, bmp->format); } } else { break; } e2 = 2 * e; if (e2 > -dy) { e -= dy; x += sx; } if (e2 < dx) { e += dx; y += sy; } } return TRUE; } /** * Draw one or more straight lines * @param hdc device context * @param lppt array of points * @param cCount number of points * @return nonzero on success, 0 otherwise */ BOOL gdi_PolylineTo(HGDI_DC hdc, GDI_POINT *lppt, DWORD cCount) { DWORD i; for (i = 0; i < cCount; i++) { if (!gdi_LineTo(hdc, lppt[i].x, lppt[i].y)) return FALSE; if (!gdi_MoveToEx(hdc, lppt[i].x, lppt[i].y, NULL)) return FALSE; } return TRUE; } /** * Draw one or more straight lines * @param hdc device context * @param lppt array of points * @param cPoints number of points * @return nonzero on success, 0 otherwise */ BOOL gdi_Polyline(HGDI_DC hdc, GDI_POINT *lppt, UINT32 cPoints) { if (cPoints > 0) { UINT32 i; GDI_POINT pt; if (!gdi_MoveToEx(hdc, lppt[0].x, lppt[0].y, &pt)) return FALSE; for (i = 0; i < cPoints; i++) { if (!gdi_LineTo(hdc, lppt[i].x, lppt[i].y)) return FALSE; if (!gdi_MoveToEx(hdc, lppt[i].x, lppt[i].y, NULL)) return FALSE; } if (!gdi_MoveToEx(hdc, pt.x, pt.y, NULL)) return FALSE; } return TRUE; } /** * Draw multiple series of connected line segments * @param hdc device context * @param lppt array of points * @param lpdwPolyPoints array of numbers of points per series * @param cCount count of entries in lpdwPolyPoints * @return nonzero on success, 0 otherwise */ BOOL gdi_PolyPolyline(HGDI_DC hdc, GDI_POINT *lppt, UINT32 *lpdwPolyPoints, DWORD cCount) { UINT32 cPoints; DWORD i, j = 0; for (i = 0; i < cCount; i++) { cPoints = lpdwPolyPoints[i]; if (!gdi_Polyline(hdc, &lppt[j], cPoints)) return FALSE; j += cPoints; } return TRUE; } /** * Move pen from the current device context to a new position. * @param hdc device context * @param X x position * @param Y y position * @return nonzero on success, 0 otherwise */ BOOL gdi_MoveToEx(HGDI_DC hdc, UINT32 X, UINT32 Y, HGDI_POINT lpPoint) { if (lpPoint != NULL) { lpPoint->x = hdc->pen->posX; lpPoint->y = hdc->pen->posY; } hdc->pen->posX = X; hdc->pen->posY = Y; return TRUE; }