mirror of
https://github.com/ApfelTeeSaft/reactos.git
synced 2026-08-27 03:43:36 +00:00
Support drawing with dithering on monochrome palette mode. JIRA issue: CORE-15990 - Enhance drawing routines for dithering in drawing.cpp. - Add VirtualBrush class to the tools model in order to virtualize the drawing colors. - Move PaletteModel::CreateDitherBrush to drawing.cpp ::CreateDitherBrush. Co-authored-by: Hermès BÉLUSCA - MAÏTO <[email protected]>
715 lines
22 KiB
C++
715 lines
22 KiB
C++
/*
|
|
* PROJECT: PAINT for ReactOS
|
|
* LICENSE: LGPL-2.0-or-later (https://spdx.org/licenses/LGPL-2.0-or-later)
|
|
* PURPOSE: The drawing functions used by the tools
|
|
* COPYRIGHT: Copyright 2015 Benedikt Freisen <[email protected]>
|
|
* Copyright 2026 Katayama Hirofumi MZ <[email protected]>
|
|
*/
|
|
|
|
#include "precomp.h"
|
|
|
|
/* FUNCTIONS ********************************************************/
|
|
|
|
// WidenPath requires a geometric pen
|
|
static HPEN
|
|
CreateGeometricPen(COLORREF rgbColor, INT thickness)
|
|
{
|
|
LOGBRUSH logbrush;
|
|
logbrush.lbStyle = BS_SOLID;
|
|
logbrush.lbColor = rgbColor;
|
|
logbrush.lbHatch = 0;
|
|
return ExtCreatePen(PS_GEOMETRIC | PS_SOLID | PS_ENDCAP_ROUND | PS_JOIN_ROUND, thickness, &logbrush, 0, NULL);
|
|
}
|
|
|
|
void
|
|
Line(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2, COLORREF color, int thickness)
|
|
{
|
|
HPEN oldPen = (HPEN) SelectObject(hdc, CreatePen(PS_SOLID, thickness, color));
|
|
MoveToEx(hdc, x1, y1, NULL);
|
|
LineTo(hdc, x2, y2);
|
|
SetPixelV(hdc, x2, y2, color);
|
|
DeleteObject(SelectObject(hdc, oldPen));
|
|
}
|
|
|
|
void
|
|
Line(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2, HBRUSH hBrush, INT thickness)
|
|
{
|
|
HGDIOBJ oldPen = SelectObject(hdc, CreateGeometricPen(0, thickness));
|
|
BeginPath(hdc);
|
|
MoveToEx(hdc, x1, y1, NULL);
|
|
LineTo(hdc, x2, y2);
|
|
EndPath(hdc);
|
|
SetPolyFillMode(hdc, WINDING);
|
|
WidenPath(hdc);
|
|
DeleteObject(SelectObject(hdc, oldPen));
|
|
|
|
HRGN hRgn = PathToRegion(hdc);
|
|
FillRgn(hdc, hRgn, hBrush);
|
|
DeleteObject(hRgn);
|
|
|
|
RECT rc = { x2, y2, x2 + 1, y2 + 1 };
|
|
FillRect(hdc, &rc, hBrush);
|
|
}
|
|
|
|
void
|
|
Rect(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2, COLORREF fg, COLORREF bg, int thickness, int style)
|
|
{
|
|
HBRUSH oldBrush;
|
|
LOGBRUSH logbrush;
|
|
HPEN oldPen = (HPEN) SelectObject(hdc, CreatePen(PS_SOLID, thickness, fg));
|
|
logbrush.lbStyle = (style == 0) ? BS_HOLLOW : BS_SOLID;
|
|
logbrush.lbColor = (style == 2) ? fg : bg;
|
|
logbrush.lbHatch = 0;
|
|
oldBrush = (HBRUSH) SelectObject(hdc, CreateBrushIndirect(&logbrush));
|
|
Rectangle(hdc, x1, y1, x2, y2);
|
|
DeleteObject(SelectObject(hdc, oldBrush));
|
|
DeleteObject(SelectObject(hdc, oldPen));
|
|
}
|
|
|
|
void
|
|
Rect(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2, HBRUSH hFgBrush, HBRUSH hBgBrush, INT thickness, INT style)
|
|
{
|
|
HGDIOBJ hPenOld;
|
|
if (style != 0)
|
|
{
|
|
HGDIOBJ hBrushOld = SelectObject(hdc, (style == 2) ? hFgBrush : hBgBrush);
|
|
hPenOld = SelectObject(hdc, GetStockObject(NULL_PEN));
|
|
Rectangle(hdc, x1, y1, x2, y2);
|
|
SelectObject(hdc, hBrushOld);
|
|
SelectObject(hdc, hPenOld);
|
|
}
|
|
|
|
HPEN hPen = CreateGeometricPen(0, thickness);
|
|
hPenOld = SelectObject(hdc, hPen);
|
|
BeginPath(hdc);
|
|
Rectangle(hdc, x1, y1, x2, y2);
|
|
EndPath(hdc);
|
|
SetPolyFillMode(hdc, WINDING);
|
|
WidenPath(hdc);
|
|
SelectObject(hdc, hPenOld);
|
|
DeleteObject(hPen);
|
|
|
|
HRGN hRgn = PathToRegion(hdc);
|
|
FillRgn(hdc, hRgn, hFgBrush);
|
|
DeleteObject(hRgn);
|
|
}
|
|
|
|
void
|
|
Ellp(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2, COLORREF fg, COLORREF bg, int thickness, int style)
|
|
{
|
|
HBRUSH oldBrush;
|
|
LOGBRUSH logbrush;
|
|
HPEN oldPen = (HPEN) SelectObject(hdc, CreatePen(PS_SOLID, thickness, fg));
|
|
logbrush.lbStyle = (style == 0) ? BS_HOLLOW : BS_SOLID;
|
|
logbrush.lbColor = (style == 2) ? fg : bg;
|
|
logbrush.lbHatch = 0;
|
|
oldBrush = (HBRUSH) SelectObject(hdc, CreateBrushIndirect(&logbrush));
|
|
Ellipse(hdc, x1, y1, x2, y2);
|
|
DeleteObject(SelectObject(hdc, oldBrush));
|
|
DeleteObject(SelectObject(hdc, oldPen));
|
|
}
|
|
|
|
void
|
|
Ellp(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2, HBRUSH hFgBrush, HBRUSH hBgBrush, INT thickness, INT style)
|
|
{
|
|
HGDIOBJ hPenOld;
|
|
if (style != 0)
|
|
{
|
|
HGDIOBJ hBrushOld = SelectObject(hdc, (style == 2) ? hFgBrush : hBgBrush);
|
|
hPenOld = SelectObject(hdc, GetStockObject(NULL_PEN));
|
|
Ellipse(hdc, x1, y1, x2, y2);
|
|
SelectObject(hdc, hBrushOld);
|
|
SelectObject(hdc, hPenOld);
|
|
}
|
|
|
|
HPEN hPen = CreateGeometricPen(0, thickness);
|
|
hPenOld = SelectObject(hdc, hPen);
|
|
BeginPath(hdc);
|
|
Ellipse(hdc, x1, y1, x2, y2);
|
|
EndPath(hdc);
|
|
SetPolyFillMode(hdc, WINDING);
|
|
WidenPath(hdc);
|
|
SelectObject(hdc, hPenOld);
|
|
DeleteObject(hPen);
|
|
|
|
HRGN hRgn = PathToRegion(hdc);
|
|
FillRgn(hdc, hRgn, hFgBrush);
|
|
DeleteObject(hRgn);
|
|
}
|
|
|
|
void
|
|
RRect(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2, COLORREF fg, COLORREF bg, int thickness, int style)
|
|
{
|
|
LOGBRUSH logbrush;
|
|
HBRUSH oldBrush;
|
|
HPEN oldPen = (HPEN) SelectObject(hdc, CreatePen(PS_SOLID, thickness, fg));
|
|
logbrush.lbStyle = (style == 0) ? BS_HOLLOW : BS_SOLID;
|
|
logbrush.lbColor = (style == 2) ? fg : bg;
|
|
logbrush.lbHatch = 0;
|
|
oldBrush = (HBRUSH) SelectObject(hdc, CreateBrushIndirect(&logbrush));
|
|
RoundRect(hdc, x1, y1, x2, y2, 16, 16);
|
|
DeleteObject(SelectObject(hdc, oldBrush));
|
|
DeleteObject(SelectObject(hdc, oldPen));
|
|
}
|
|
|
|
void
|
|
RRect(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2, HBRUSH hFgBrush, HBRUSH hBgBrush, INT thickness, INT style)
|
|
{
|
|
HGDIOBJ hPenOld;
|
|
if (style != 0)
|
|
{
|
|
HGDIOBJ hBrushOld = SelectObject(hdc, (style == 2) ? hFgBrush : hBgBrush);
|
|
hPenOld = SelectObject(hdc, GetStockObject(NULL_PEN));
|
|
RoundRect(hdc, x1, y1, x2, y2, 16, 16);
|
|
SelectObject(hdc, hBrushOld);
|
|
SelectObject(hdc, hPenOld);
|
|
}
|
|
|
|
HPEN hPen = CreateGeometricPen(0, thickness);
|
|
hPenOld = SelectObject(hdc, hPen);
|
|
BeginPath(hdc);
|
|
RoundRect(hdc, x1, y1, x2, y2, 16, 16);
|
|
EndPath(hdc);
|
|
SetPolyFillMode(hdc, WINDING);
|
|
WidenPath(hdc);
|
|
SelectObject(hdc, hPenOld);
|
|
DeleteObject(hPen);
|
|
|
|
HRGN hRgn = PathToRegion(hdc);
|
|
FillRgn(hdc, hRgn, hFgBrush);
|
|
DeleteObject(hRgn);
|
|
}
|
|
|
|
void
|
|
Poly(HDC hdc, POINT * lpPoints, int nCount, COLORREF fg, COLORREF bg, int thickness, int style, BOOL closed, BOOL inverted)
|
|
{
|
|
LOGBRUSH logbrush;
|
|
HBRUSH oldBrush;
|
|
HPEN oldPen = (HPEN) SelectObject(hdc, CreatePen(PS_SOLID, thickness, fg));
|
|
UINT oldRop = GetROP2(hdc);
|
|
|
|
if (inverted)
|
|
SetROP2(hdc, R2_NOTXORPEN);
|
|
|
|
logbrush.lbStyle = (style == 0) ? BS_HOLLOW : BS_SOLID;
|
|
logbrush.lbColor = (style == 2) ? fg : bg;
|
|
logbrush.lbHatch = 0;
|
|
oldBrush = (HBRUSH) SelectObject(hdc, CreateBrushIndirect(&logbrush));
|
|
if (closed)
|
|
Polygon(hdc, lpPoints, nCount);
|
|
else
|
|
Polyline(hdc, lpPoints, nCount);
|
|
DeleteObject(SelectObject(hdc, oldBrush));
|
|
DeleteObject(SelectObject(hdc, oldPen));
|
|
|
|
SetROP2(hdc, oldRop);
|
|
}
|
|
|
|
void
|
|
Poly(HDC hdc, POINT *lpPoints, INT nCount, HBRUSH hFgBrush, HBRUSH hBgBrush, INT thickness, INT style, BOOL closed, BOOL inverted)
|
|
{
|
|
UINT oldRop = GetROP2(hdc);
|
|
if (inverted)
|
|
SetROP2(hdc, R2_NOTXORPEN);
|
|
|
|
HGDIOBJ hPenOld;
|
|
if (style != 0)
|
|
{
|
|
HGDIOBJ hBrushOld = SelectObject(hdc, (style == 2) ? hFgBrush : hBgBrush);
|
|
hPenOld = SelectObject(hdc, GetStockObject(NULL_PEN));
|
|
if (closed)
|
|
Polygon(hdc, lpPoints, nCount);
|
|
else
|
|
Polyline(hdc, lpPoints, nCount);
|
|
SelectObject(hdc, hBrushOld);
|
|
SelectObject(hdc, hPenOld);
|
|
}
|
|
|
|
HPEN hPen = CreateGeometricPen(0, thickness);
|
|
hPenOld = SelectObject(hdc, hPen);
|
|
BeginPath(hdc);
|
|
if (closed)
|
|
Polygon(hdc, lpPoints, nCount);
|
|
else
|
|
Polyline(hdc, lpPoints, nCount);
|
|
EndPath(hdc);
|
|
SetPolyFillMode(hdc, WINDING);
|
|
WidenPath(hdc);
|
|
SelectObject(hdc, hPenOld);
|
|
DeleteObject(hPen);
|
|
|
|
HRGN hRgn = PathToRegion(hdc);
|
|
FillRgn(hdc, hRgn, hFgBrush);
|
|
DeleteObject(hRgn);
|
|
|
|
SetROP2(hdc, oldRop);
|
|
}
|
|
|
|
void
|
|
Bezier(HDC hdc, POINT p1, POINT p2, POINT p3, POINT p4, COLORREF color, int thickness)
|
|
{
|
|
HPEN oldPen;
|
|
POINT fourPoints[4];
|
|
fourPoints[0] = p1;
|
|
fourPoints[1] = p2;
|
|
fourPoints[2] = p3;
|
|
fourPoints[3] = p4;
|
|
oldPen = (HPEN) SelectObject(hdc, CreatePen(PS_SOLID, thickness, color));
|
|
PolyBezier(hdc, fourPoints, 4);
|
|
DeleteObject(SelectObject(hdc, oldPen));
|
|
}
|
|
|
|
void
|
|
Bezier(HDC hdc, POINT p1, POINT p2, POINT p3, POINT p4, HBRUSH hBrush, INT thickness)
|
|
{
|
|
HPEN oldPen;
|
|
POINT fourPoints[4];
|
|
fourPoints[0] = p1;
|
|
fourPoints[1] = p2;
|
|
fourPoints[2] = p3;
|
|
fourPoints[3] = p4;
|
|
oldPen = (HPEN) SelectObject(hdc, CreateGeometricPen(0, thickness));
|
|
BeginPath(hdc);
|
|
PolyBezier(hdc, fourPoints, 4);
|
|
EndPath(hdc);
|
|
SetPolyFillMode(hdc, WINDING);
|
|
WidenPath(hdc);
|
|
|
|
HRGN hRgn = PathToRegion(hdc);
|
|
FillRgn(hdc, hRgn, hBrush);
|
|
DeleteObject(hRgn);
|
|
|
|
DeleteObject(SelectObject(hdc, oldPen));
|
|
}
|
|
|
|
void
|
|
Fill(HDC hdc, LONG x, LONG y, COLORREF color)
|
|
{
|
|
HBRUSH hBrush = CreateSolidBrush(color);
|
|
Fill(hdc, x, y, hBrush);
|
|
DeleteObject(hBrush);
|
|
}
|
|
|
|
void
|
|
Fill(HDC hdc, LONG x, LONG y, HBRUSH hBrush)
|
|
{
|
|
HBRUSH oldBrush = (HBRUSH)SelectObject(hdc, hBrush);
|
|
ExtFloodFill(hdc, x, y, GetPixel(hdc, x, y), FLOODFILLSURFACE);
|
|
SelectObject(hdc, oldBrush);
|
|
}
|
|
|
|
void
|
|
Erase(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2, COLORREF color, LONG radius)
|
|
{
|
|
HBRUSH hBrush = ::CreateSolidBrush(color);
|
|
Erase(hdc, x1, y1, x2, y2, hBrush, radius);
|
|
::DeleteObject(hBrush);
|
|
}
|
|
|
|
void
|
|
Erase(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2, HBRUSH hBrush, LONG radius)
|
|
{
|
|
LONG b = max(1, max(labs(x2 - x1), labs(y2 - y1)));
|
|
|
|
for (LONG a = 0; a <= b; a++)
|
|
{
|
|
LONG cx = (x1 * (b - a) + x2 * a) / b;
|
|
LONG cy = (y1 * (b - a) + y2 * a) / b;
|
|
RECT rc = { cx - radius, cy - radius, cx + radius, cy + radius };
|
|
::FillRect(hdc, &rc, hBrush);
|
|
}
|
|
}
|
|
|
|
void
|
|
Replace(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2, COLORREF fg, COLORREF bg, LONG radius)
|
|
{
|
|
LONG b = max(1, max(labs(x2 - x1), labs(y2 - y1)));
|
|
|
|
for (LONG a = 0; a <= b; a++)
|
|
{
|
|
LONG cx = (x1 * (b - a) + x2 * a) / b;
|
|
LONG cy = (y1 * (b - a) + y2 * a) / b;
|
|
RECT rc = { cx - radius, cy - radius, cx + radius, cy + radius };
|
|
for (LONG y = rc.top; y < rc.bottom; ++y)
|
|
{
|
|
for (LONG x = rc.left; x < rc.right; ++x)
|
|
{
|
|
if (::GetPixel(hdc, x, y) == fg)
|
|
::SetPixelV(hdc, x, y, bg);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
Replace(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2, COLORREF fg, HBRUSH hBgBrush, LONG radius)
|
|
{
|
|
LONG b = max(1, max(labs(x2 - x1), labs(y2 - y1)));
|
|
|
|
for (LONG a = 0; a <= b; a++)
|
|
{
|
|
LONG cx = (x1 * (b - a) + x2 * a) / b;
|
|
LONG cy = (y1 * (b - a) + y2 * a) / b;
|
|
RECT rc = { cx - radius, cy - radius, cx + radius, cy + radius };
|
|
for (LONG y = rc.top; y < rc.bottom; ++y)
|
|
{
|
|
for (LONG x = rc.left; x < rc.right; ++x)
|
|
{
|
|
if (::GetPixel(hdc, x, y) == fg)
|
|
{
|
|
RECT rc = { x, y, x + 1, y + 1 };
|
|
FillRect(hdc, &rc, hBgBrush);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
Airbrush(HDC hdc, LONG x, LONG y, COLORREF color, LONG r)
|
|
{
|
|
for (LONG dy = -r; dy <= r; dy++)
|
|
{
|
|
for (LONG dx = -r; dx <= r; dx++)
|
|
{
|
|
if ((dx * dx + dy * dy <= r * r) && (rand() % r == 0))
|
|
::SetPixelV(hdc, x + dx, y + dy, color);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
Airbrush(HDC hdc, LONG x, LONG y, HBRUSH hBrush, LONG r)
|
|
{
|
|
for (LONG dy = -r; dy <= r; dy++)
|
|
{
|
|
for (LONG dx = -r; dx <= r; dx++)
|
|
{
|
|
if ((dx * dx + dy * dy <= r * r) && (rand() % r == 0))
|
|
{
|
|
RECT rc = { x + dx, y + dy, x + dx + 1, y + dy + 1 };
|
|
FillRect(hdc, &rc, hBrush);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
BrushInternal(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2, LONG style, INT thickness)
|
|
{
|
|
LONG a, b = max(1, max(labs(x2 - x1), labs(y2 - y1)));
|
|
switch ((BrushStyle)style)
|
|
{
|
|
case BrushStyleRound:
|
|
for (a = 0; a <= b; a++)
|
|
{
|
|
Ellipse(hdc,
|
|
(x1 * (b - a) + x2 * a) / b - (thickness / 2),
|
|
(y1 * (b - a) + y2 * a) / b - (thickness / 2),
|
|
(x1 * (b - a) + x2 * a) / b + (thickness / 2) + 1,
|
|
(y1 * (b - a) + y2 * a) / b + (thickness / 2) + 1);
|
|
}
|
|
break;
|
|
|
|
case BrushStyleSquare:
|
|
for (a = 0; a <= b; a++)
|
|
{
|
|
Rectangle(hdc,
|
|
(x1 * (b - a) + x2 * a) / b - (thickness / 2),
|
|
(y1 * (b - a) + y2 * a) / b - (thickness / 2),
|
|
(x1 * (b - a) + x2 * a) / b + (thickness / 2) + 1,
|
|
(y1 * (b - a) + y2 * a) / b + (thickness / 2) + 1);
|
|
}
|
|
break;
|
|
|
|
case BrushStyleForeSlash:
|
|
case BrushStyleBackSlash:
|
|
{
|
|
POINT offsetTop, offsetBottom;
|
|
if ((BrushStyle)style == BrushStyleForeSlash)
|
|
{
|
|
offsetTop = { (thickness - 1) / 2 + 1, -(thickness - 1) / 2 };
|
|
offsetBottom = { -thickness / 2 , thickness / 2 + 1 };
|
|
}
|
|
else
|
|
{
|
|
offsetTop = { -thickness / 2 , -thickness / 2 };
|
|
offsetBottom = { (thickness - 1) / 2 + 1, (thickness - 1) / 2 + 1 };
|
|
}
|
|
if (x1 == x2 && y1 == y2)
|
|
{
|
|
++x2;
|
|
}
|
|
POINT points[4] =
|
|
{
|
|
{ x1 + offsetTop.x, y1 + offsetTop.y },
|
|
{ x1 + offsetBottom.x, y1 + offsetBottom.y },
|
|
{ x2 + offsetBottom.x, y2 + offsetBottom.y },
|
|
{ x2 + offsetTop.x, y2 + offsetTop.y },
|
|
};
|
|
Polygon(hdc, points, _countof(points));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
Brush(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2, COLORREF color, LONG style, INT thickness)
|
|
{
|
|
HBRUSH hBrush = CreateSolidBrush(color);
|
|
Brush(hdc, x1, y1, x2, y2, hBrush, style, thickness);
|
|
DeleteObject(hBrush);
|
|
}
|
|
|
|
void
|
|
Brush(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2, HBRUSH hBrush, LONG style, INT thickness)
|
|
{
|
|
if (thickness <= 1)
|
|
{
|
|
Line(hdc, x1, y1, x2, y2, hBrush, thickness);
|
|
return;
|
|
}
|
|
|
|
HGDIOBJ oldPen = SelectObject(hdc, GetStockObject(NULL_PEN)); // 1px off
|
|
HGDIOBJ oldBrush = SelectObject(hdc, hBrush);
|
|
BrushInternal(hdc, x1, y1, x2, y2, style, thickness);
|
|
SelectObject(hdc, oldBrush);
|
|
SelectObject(hdc, oldPen);
|
|
}
|
|
|
|
void
|
|
RectSel(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2)
|
|
{
|
|
HBRUSH oldBrush;
|
|
LOGBRUSH logbrush;
|
|
HPEN oldPen = (HPEN) SelectObject(hdc, CreatePen(PS_DOT, 1, GetSysColor(COLOR_HIGHLIGHT)));
|
|
UINT oldRop = GetROP2(hdc);
|
|
|
|
SetROP2(hdc, R2_NOTXORPEN);
|
|
|
|
logbrush.lbStyle = BS_HOLLOW;
|
|
logbrush.lbColor = 0;
|
|
logbrush.lbHatch = 0;
|
|
oldBrush = (HBRUSH) SelectObject(hdc, CreateBrushIndirect(&logbrush));
|
|
Rectangle(hdc, x1, y1, x2, y2);
|
|
DeleteObject(SelectObject(hdc, oldBrush));
|
|
DeleteObject(SelectObject(hdc, oldPen));
|
|
|
|
SetROP2(hdc, oldRop);
|
|
}
|
|
|
|
void
|
|
Text(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2, COLORREF fg, COLORREF bg, LPCWSTR lpchText, HFONT font, LONG style)
|
|
{
|
|
INT iSaveDC = ::SaveDC(hdc); // We will modify the clipping region. Save now.
|
|
|
|
CRect rc = { x1, y1, x2, y2 };
|
|
|
|
if (style == 0) // Transparent
|
|
{
|
|
::SetBkMode(hdc, TRANSPARENT);
|
|
}
|
|
else // Opaque
|
|
{
|
|
::SetBkMode(hdc, OPAQUE);
|
|
::SetBkColor(hdc, bg);
|
|
|
|
HBRUSH hbr = ::CreateSolidBrush(bg);
|
|
::FillRect(hdc, &rc, hbr); // Fill the background
|
|
::DeleteObject(hbr);
|
|
}
|
|
|
|
IntersectClipRect(hdc, rc.left, rc.top, rc.right, rc.bottom);
|
|
|
|
HGDIOBJ hFontOld = ::SelectObject(hdc, font);
|
|
::SetTextColor(hdc, fg);
|
|
const UINT uFormat = DT_LEFT | DT_TOP | DT_EDITCONTROL | DT_NOPREFIX | DT_NOCLIP |
|
|
DT_EXPANDTABS | DT_WORDBREAK;
|
|
::DrawTextW(hdc, lpchText, -1, &rc, uFormat);
|
|
::SelectObject(hdc, hFontOld);
|
|
|
|
::RestoreDC(hdc, iSaveDC); // Restore
|
|
}
|
|
|
|
void
|
|
Text(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2, HBRUSH hFgBrush, HBRUSH hBgBrush, LPCWSTR lpchText, HFONT font, LONG style)
|
|
{
|
|
INT iSaveDC = ::SaveDC(hdc); // We will modify the clipping region. Save now.
|
|
|
|
CRect rc = { x1, y1, x2, y2 };
|
|
|
|
::SetBkMode(hdc, TRANSPARENT);
|
|
if (style != 0) // Not Transparent
|
|
::FillRect(hdc, &rc, hBgBrush); // Fill the background
|
|
|
|
IntersectClipRect(hdc, rc.left, rc.top, rc.right, rc.bottom);
|
|
|
|
HGDIOBJ hFontOld = ::SelectObject(hdc, font);
|
|
const UINT uFormat = DT_LEFT | DT_TOP | DT_EDITCONTROL | DT_NOPREFIX | DT_NOCLIP |
|
|
DT_EXPANDTABS | DT_WORDBREAK;
|
|
::BeginPath(hdc);
|
|
::DrawTextW(hdc, lpchText, -1, &rc, uFormat);
|
|
::EndPath(hdc);
|
|
|
|
HRGN hRgn = PathToRegion(hdc);
|
|
FillRgn(hdc, hRgn, hFgBrush);
|
|
DeleteObject(hRgn);
|
|
|
|
::SelectObject(hdc, hFontOld);
|
|
|
|
::RestoreDC(hdc, iSaveDC); // Restore
|
|
}
|
|
|
|
BOOL
|
|
ColorKeyedMaskBlt(HDC hdcDest, int nXDest, int nYDest, int nWidth, int nHeight,
|
|
HDC hdcSrc, int nXSrc, int nYSrc, int nSrcWidth, int nSrcHeight,
|
|
HBITMAP hbmMask, COLORREF keyColor)
|
|
{
|
|
HDC hTempDC1, hTempDC2;
|
|
HBITMAP hbmTempColor, hbmTempMask;
|
|
HGDIOBJ hbmOld1, hbmOld2;
|
|
|
|
if (hbmMask == NULL)
|
|
{
|
|
if (keyColor == CLR_INVALID)
|
|
{
|
|
::StretchBlt(hdcDest, nXDest, nYDest, nWidth, nHeight,
|
|
hdcSrc, nXSrc, nYSrc, nSrcWidth, nSrcHeight, SRCCOPY);
|
|
}
|
|
else
|
|
{
|
|
::GdiTransparentBlt(hdcDest, nXDest, nYDest, nWidth, nHeight,
|
|
hdcSrc, nXSrc, nYSrc, nSrcWidth, nSrcHeight, keyColor);
|
|
}
|
|
return TRUE;
|
|
}
|
|
else if (nWidth == nSrcWidth && nHeight == nSrcHeight && keyColor == CLR_INVALID)
|
|
{
|
|
::MaskBlt(hdcDest, nXDest, nYDest, nWidth, nHeight,
|
|
hdcSrc, nXSrc, nYSrc, hbmMask, 0, 0, MAKEROP4(SRCCOPY, 0xAA0029));
|
|
return TRUE;
|
|
}
|
|
|
|
hTempDC1 = ::CreateCompatibleDC(hdcDest);
|
|
hTempDC2 = ::CreateCompatibleDC(hdcDest);
|
|
hbmTempMask = ::CreateBitmap(nWidth, nHeight, 1, 1, NULL);
|
|
hbmTempColor = CreateColorDIB(nWidth, nHeight, RGB(255, 255, 255));
|
|
|
|
// hbmTempMask <-- hbmMask (stretched)
|
|
hbmOld1 = ::SelectObject(hTempDC1, hbmMask);
|
|
hbmOld2 = ::SelectObject(hTempDC2, hbmTempMask);
|
|
::StretchBlt(hTempDC2, 0, 0, nWidth, nHeight, hTempDC1, 0, 0, nSrcWidth, nSrcHeight, SRCCOPY);
|
|
::SelectObject(hTempDC2, hbmOld2);
|
|
::SelectObject(hTempDC1, hbmOld1);
|
|
|
|
hbmOld1 = ::SelectObject(hTempDC1, hbmTempColor);
|
|
if (keyColor == CLR_INVALID)
|
|
{
|
|
// hbmTempColor <-- hdcSrc (stretched)
|
|
::StretchBlt(hTempDC1, 0, 0, nWidth, nHeight,
|
|
hdcSrc, nXSrc, nYSrc, nSrcWidth, nSrcHeight, SRCCOPY);
|
|
|
|
// hdcDest <-- hbmTempColor (masked)
|
|
::MaskBlt(hdcDest, nXDest, nYDest, nWidth, nHeight, hTempDC1, 0, 0,
|
|
hbmTempMask, 0, 0, MAKEROP4(SRCCOPY, 0xAA0029));
|
|
}
|
|
else
|
|
{
|
|
// hbmTempColor <-- hdcDest
|
|
::BitBlt(hTempDC1, 0, 0, nWidth, nHeight, hdcDest, nXDest, nYDest, SRCCOPY);
|
|
|
|
// hbmTempColor <-- hdcSrc (color key)
|
|
::GdiTransparentBlt(hTempDC1, 0, 0, nWidth, nHeight,
|
|
hdcSrc, nXSrc, nYSrc, nSrcWidth, nSrcHeight, keyColor);
|
|
|
|
// hdcDest <-- hbmTempColor (masked)
|
|
::MaskBlt(hdcDest, nXDest, nYDest, nWidth, nHeight, hTempDC1, 0, 0,
|
|
hbmTempMask, 0, 0, MAKEROP4(SRCCOPY, 0xAA0029));
|
|
}
|
|
::SelectObject(hTempDC1, hbmOld1);
|
|
|
|
::DeleteObject(hbmTempColor);
|
|
::DeleteObject(hbmTempMask);
|
|
::DeleteDC(hTempDC2);
|
|
::DeleteDC(hTempDC1);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void DrawXorRect(HDC hdc, const RECT *prc)
|
|
{
|
|
HGDIOBJ oldPen = ::SelectObject(hdc, ::CreatePen(PS_SOLID, 0, RGB(0, 0, 0)));
|
|
HGDIOBJ oldBrush = ::SelectObject(hdc, ::GetStockObject(NULL_BRUSH));
|
|
INT oldRop2 = SetROP2(hdc, R2_NOTXORPEN);
|
|
::Rectangle(hdc, prc->left, prc->top, prc->right, prc->bottom);
|
|
::SetROP2(hdc, oldRop2);
|
|
::SelectObject(hdc, oldBrush);
|
|
::DeleteObject(::SelectObject(hdc, oldPen));
|
|
}
|
|
|
|
HBRUSH CreateDitherBrush(COLORREF color, COLORREF monoColor0, COLORREF monoColor1)
|
|
{
|
|
// 8x8 Bayer ordered dithering matrix (0 to 63)
|
|
static const BYTE s_bayerMatrix[8][8] =
|
|
{
|
|
{ 0, 32, 8, 40, 2, 34, 10, 42 },
|
|
{ 48, 16, 56, 24, 50, 18, 58, 26 },
|
|
{ 12, 44, 4, 36, 14, 46, 6, 38 },
|
|
{ 60, 28, 52, 20, 62, 30, 54, 22 },
|
|
{ 3, 35, 11, 43, 1, 33, 9, 41 },
|
|
{ 51, 19, 59, 27, 49, 17, 57, 25 },
|
|
{ 15, 47, 7, 39, 13, 45, 5, 37 },
|
|
{ 63, 31, 55, 23, 61, 29, 53, 21 },
|
|
};
|
|
INT sum = GetRValue(color) + GetGValue(color) + GetBValue(color);
|
|
INT brightness = sum / 3;
|
|
if (brightness < 0)
|
|
brightness = 0;
|
|
if (brightness >= 255)
|
|
brightness = 256; // White out
|
|
|
|
BITMAPINFO bmi = {};
|
|
bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader);
|
|
bmi.bmiHeader.biWidth = 8;
|
|
bmi.bmiHeader.biHeight = -8; // Top-down
|
|
bmi.bmiHeader.biPlanes = 1;
|
|
bmi.bmiHeader.biBitCount = 24;
|
|
|
|
const BYTE b0 = GetBValue(monoColor0), g0 = GetGValue(monoColor0), r0 = GetRValue(monoColor0);
|
|
const BYTE b1 = GetBValue(monoColor1), g1 = GetGValue(monoColor1), r1 = GetRValue(monoColor1);
|
|
|
|
BYTE pixels[8 * 8 * 3];
|
|
for (INT y = 0; y < 8; ++y)
|
|
{
|
|
INT index = y * (3 * CHAR_BIT);
|
|
for (INT x = 0; x < 8; ++x)
|
|
{
|
|
const INT threshold = s_bayerMatrix[y][x] * 255 / 63;
|
|
if (brightness > threshold)
|
|
{
|
|
pixels[index++] = b1; // Blue
|
|
pixels[index++] = g1; // Green
|
|
pixels[index++] = r1; // Red
|
|
}
|
|
else
|
|
{
|
|
pixels[index++] = b0; // Blue
|
|
pixels[index++] = g0; // Green
|
|
pixels[index++] = r0; // Red
|
|
}
|
|
}
|
|
}
|
|
|
|
HDC hdc = GetDC(NULL);
|
|
HBITMAP hBitmap = CreateDIBitmap(hdc, &bmi.bmiHeader, CBM_INIT, pixels, &bmi, DIB_RGB_COLORS);
|
|
ReleaseDC(NULL, hdc);
|
|
|
|
if (!hBitmap)
|
|
return NULL;
|
|
|
|
HBRUSH hBrush = CreatePatternBrush(hBitmap);
|
|
DeleteObject(hBitmap);
|
|
|
|
return hBrush;
|
|
}
|