mirror of
https://github.com/ApfelTeeSaft/reactos.git
synced 2026-08-26 19:33:31 +00:00
[MSPAINT] Support dithering drawing (#9175)
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]>
This commit is contained in:
@@ -3,12 +3,24 @@
|
||||
* 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)
|
||||
{
|
||||
@@ -19,6 +31,26 @@ Line(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2, COLORREF color, int thickness)
|
||||
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)
|
||||
{
|
||||
@@ -34,6 +66,34 @@ Rect(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2, COLORREF fg, COLORREF bg, int
|
||||
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)
|
||||
{
|
||||
@@ -49,6 +109,34 @@ Ellp(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2, COLORREF fg, COLORREF bg, int
|
||||
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)
|
||||
{
|
||||
@@ -64,6 +152,34 @@ RRect(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2, COLORREF fg, COLORREF bg, in
|
||||
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)
|
||||
{
|
||||
@@ -89,6 +205,46 @@ Poly(HDC hdc, POINT * lpPoints, int nCount, COLORREF fg, COLORREF bg, int thic
|
||||
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)
|
||||
{
|
||||
@@ -103,29 +259,65 @@ Bezier(HDC hdc, POINT p1, POINT p2, POINT p3, POINT p4, COLORREF color, int thic
|
||||
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 oldBrush = (HBRUSH) SelectObject(hdc, CreateSolidBrush(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);
|
||||
DeleteObject(SelectObject(hdc, oldBrush));
|
||||
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)));
|
||||
HBRUSH hbr = ::CreateSolidBrush(color);
|
||||
|
||||
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, hbr);
|
||||
::FillRect(hdc, &rc, hBrush);
|
||||
}
|
||||
|
||||
::DeleteObject(hbr);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -149,6 +341,30 @@ Replace(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2, COLORREF fg, COLORREF bg, L
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
@@ -163,70 +379,102 @@ Airbrush(HDC hdc, LONG x, LONG y, COLORREF color, LONG r)
|
||||
}
|
||||
|
||||
void
|
||||
Brush(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2, COLORREF color, LONG style, INT thickness)
|
||||
Airbrush(HDC hdc, LONG x, LONG y, HBRUSH hBrush, LONG r)
|
||||
{
|
||||
HPEN oldPen = (HPEN) SelectObject(hdc, CreatePen(PS_SOLID, 1, color));
|
||||
HBRUSH oldBrush = (HBRUSH) SelectObject(hdc, CreateSolidBrush(color));
|
||||
|
||||
if (thickness <= 1)
|
||||
for (LONG dy = -r; dy <= r; dy++)
|
||||
{
|
||||
Line(hdc, x1, y1, x2, y2, color, thickness);
|
||||
}
|
||||
else
|
||||
{
|
||||
LONG a, b = max(1, max(labs(x2 - x1), labs(y2 - y1)));
|
||||
switch ((BrushStyle)style)
|
||||
for (LONG dx = -r; dx <= r; dx++)
|
||||
{
|
||||
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),
|
||||
(y1 * (b - a) + y2 * a) / b + (thickness / 2));
|
||||
}
|
||||
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),
|
||||
(y1 * (b - a) + y2 * a) / b + (thickness / 2));
|
||||
}
|
||||
break;
|
||||
|
||||
case BrushStyleForeSlash:
|
||||
case BrushStyleBackSlash:
|
||||
if ((dx * dx + dy * dy <= r * r) && (rand() % r == 0))
|
||||
{
|
||||
POINT offsetTop, offsetBottom;
|
||||
if ((BrushStyle)style == BrushStyleForeSlash)
|
||||
{
|
||||
offsetTop = { (thickness - 1) / 2, -(thickness - 1) / 2 };
|
||||
offsetBottom = { -thickness / 2, thickness / 2 };
|
||||
}
|
||||
else
|
||||
{
|
||||
offsetTop = { -thickness / 2, -thickness / 2 };
|
||||
offsetBottom = { (thickness - 1) / 2, (thickness - 1) / 2 };
|
||||
}
|
||||
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;
|
||||
RECT rc = { x + dx, y + dy, x + dx + 1, y + dy + 1 };
|
||||
FillRect(hdc, &rc, hBrush);
|
||||
}
|
||||
}
|
||||
}
|
||||
DeleteObject(SelectObject(hdc, oldBrush));
|
||||
DeleteObject(SelectObject(hdc, oldPen));
|
||||
}
|
||||
|
||||
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
|
||||
@@ -283,6 +531,35 @@ Text(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2, COLORREF fg, COLORREF bg, LPCW
|
||||
::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,
|
||||
@@ -369,3 +646,69 @@ void DrawXorRect(HDC hdc, const RECT *prc)
|
||||
::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;
|
||||
}
|
||||
|
||||
@@ -3,35 +3,48 @@
|
||||
* 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]>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
void Line(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2, COLORREF color, int thickness);
|
||||
void Line(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2, HBRUSH hBrush, INT thickness);
|
||||
|
||||
void Rect(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2, COLORREF fg, COLORREF bg, int thickness, int style);
|
||||
void Rect(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2, HBRUSH hFgBrush, HBRUSH hBgBrush, INT thickness, INT style);
|
||||
|
||||
void Ellp(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2, COLORREF fg, COLORREF bg, int thickness, int style);
|
||||
void Ellp(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2, HBRUSH hFgBrush, HBRUSH hBgBrush, INT thickness, INT style);
|
||||
|
||||
void RRect(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2, COLORREF fg, COLORREF bg, int thickness, int style);
|
||||
void RRect(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2, HBRUSH hFgBrush, HBRUSH hBgBrush, INT thickness, INT style);
|
||||
|
||||
void Poly(HDC hdc, POINT *lpPoints, int nCount, COLORREF fg, COLORREF bg, int thickness, int style, BOOL closed, BOOL inverted);
|
||||
void Poly(HDC hdc, POINT *lpPoints, INT nCount, HBRUSH hFgBrush, HBRUSH hBgBrush, INT thickness, INT style, BOOL closed, BOOL inverted);
|
||||
|
||||
void Bezier(HDC hdc, POINT p1, POINT p2, POINT p3, POINT p4, COLORREF color, int thickness);
|
||||
void Bezier(HDC hdc, POINT p1, POINT p2, POINT p3, POINT p4, HBRUSH hBrush, INT thickness);
|
||||
|
||||
void Fill(HDC hdc, LONG x, LONG y, COLORREF color);
|
||||
void Fill(HDC hdc, LONG x, LONG y, HBRUSH hBrush);
|
||||
|
||||
void Erase(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2, COLORREF color, LONG radius);
|
||||
void Erase(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2, HBRUSH hBrush, LONG radius);
|
||||
|
||||
void Replace(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2, COLORREF fg, COLORREF bg, LONG radius);
|
||||
void Replace(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2, COLORREF fg, HBRUSH hBgBrush, LONG radius);
|
||||
|
||||
void Airbrush(HDC hdc, LONG x, LONG y, COLORREF color, LONG r);
|
||||
void Airbrush(HDC hdc, LONG x, LONG y, HBRUSH hBrush, LONG r);
|
||||
|
||||
void Brush(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2, COLORREF color, LONG style, INT thickness);
|
||||
void Brush(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2, HBRUSH hBrush, LONG style, INT thickness);
|
||||
|
||||
void RectSel(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2);
|
||||
|
||||
void Text(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2, COLORREF fg, COLORREF bg, LPCWSTR lpchText, HFONT font, LONG style);
|
||||
void Text(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2, HBRUSH hFgBrush, HBRUSH hBgBrush, LPCWSTR lpchText, HFONT font, LONG style);
|
||||
|
||||
BOOL
|
||||
ColorKeyedMaskBlt(HDC hdcDest, int nXDest, int nYDest, int nWidth, int nHeight,
|
||||
@@ -39,3 +52,5 @@ ColorKeyedMaskBlt(HDC hdcDest, int nXDest, int nYDest, int nWidth, int nHeight,
|
||||
HBITMAP hbmMask, COLORREF keyColor);
|
||||
|
||||
void DrawXorRect(HDC hdc, const RECT *prc);
|
||||
|
||||
HBRUSH CreateDitherBrush(COLORREF color, COLORREF monoColor0, COLORREF monoColor1);
|
||||
|
||||
@@ -560,7 +560,7 @@ struct FreeSelTool : SelectionBaseTool
|
||||
if (!selectionModel.m_bShow && m_bDrawing)
|
||||
{
|
||||
/* Draw the freehand selection inverted/xored */
|
||||
Poly(hdc, s_pPoints, (INT)s_cPoints, 0, 0, 2, 0, FALSE, TRUE);
|
||||
Poly(hdc, s_pPoints, (INT)s_cPoints, (COLORREF)0, (COLORREF)0, 2, 0, FALSE, TRUE);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -587,9 +587,9 @@ struct RubberTool : SmoothDrawTool
|
||||
void OnDraw(HDC hdc, BOOL bLeftButton, POINT pt0, POINT pt1) override
|
||||
{
|
||||
if (bLeftButton)
|
||||
Erase(hdc, pt0.x, pt0.y, pt1.x, pt1.y, m_bg, toolsModel.GetRubberRadius());
|
||||
Erase(hdc, pt0.x, pt0.y, pt1.x, pt1.y, toolsModel.GetBgBrush(), toolsModel.GetRubberRadius());
|
||||
else
|
||||
Replace(hdc, pt0.x, pt0.y, pt1.x, pt1.y, m_fg, m_bg, toolsModel.GetRubberRadius());
|
||||
Replace(hdc, pt0.x, pt0.y, pt1.x, pt1.y, m_fg, toolsModel.GetBgBrush(), toolsModel.GetRubberRadius());
|
||||
}
|
||||
|
||||
void OnSpecialTweak(BOOL bMinus) override
|
||||
@@ -604,7 +604,8 @@ struct FillTool : ToolBase
|
||||
void OnButtonDown(BOOL bLeftButton, LONG x, LONG y, BOOL bDoubleClick) override
|
||||
{
|
||||
imageModel.PushImageForUndo();
|
||||
Fill(m_hdc, x, y, bLeftButton ? m_fg : m_bg);
|
||||
HBRUSH hBrush = (bLeftButton ? toolsModel.GetFgBrush() : toolsModel.GetBgBrush());
|
||||
Fill(m_hdc, x, y, hBrush);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -709,8 +710,8 @@ struct PenTool : SmoothDrawTool
|
||||
{
|
||||
void OnDraw(HDC hdc, BOOL bLeftButton, POINT pt0, POINT pt1) override
|
||||
{
|
||||
COLORREF rgb = bLeftButton ? m_fg : m_bg;
|
||||
Line(hdc, pt0.x, pt0.y, pt1.x, pt1.y, rgb, toolsModel.GetPenWidth());
|
||||
HBRUSH hBrush = (bLeftButton ? toolsModel.GetFgBrush() : toolsModel.GetBgBrush());
|
||||
Line(hdc, pt0.x, pt0.y, pt1.x, pt1.y, hBrush, toolsModel.GetPenWidth());
|
||||
}
|
||||
|
||||
void OnSpecialTweak(BOOL bMinus) override
|
||||
@@ -724,8 +725,8 @@ struct BrushTool : SmoothDrawTool
|
||||
{
|
||||
void OnDraw(HDC hdc, BOOL bLeftButton, POINT pt0, POINT pt1) override
|
||||
{
|
||||
COLORREF rgb = bLeftButton ? m_fg : m_bg;
|
||||
Brush(hdc, pt0.x, pt0.y, pt1.x, pt1.y, rgb, toolsModel.GetBrushStyle(),
|
||||
HBRUSH hBrush = (bLeftButton ? toolsModel.GetFgBrush() : toolsModel.GetBgBrush());
|
||||
Brush(hdc, pt0.x, pt0.y, pt1.x, pt1.y, hBrush, toolsModel.GetBrushStyle(),
|
||||
toolsModel.GetBrushWidth());
|
||||
}
|
||||
|
||||
@@ -754,8 +755,8 @@ struct AirBrushTool : SmoothDrawTool
|
||||
|
||||
void OnDraw(HDC hdc, BOOL bLeftButton, POINT pt0, POINT pt1) override
|
||||
{
|
||||
COLORREF rgb = bLeftButton ? m_fg : m_bg;
|
||||
Airbrush(hdc, pt1.x, pt1.y, rgb, toolsModel.GetAirBrushRadius());
|
||||
HBRUSH hBrush = (bLeftButton ? toolsModel.GetFgBrush() : toolsModel.GetBgBrush());
|
||||
Airbrush(hdc, pt1.x, pt1.y, hBrush, toolsModel.GetAirBrushRadius());
|
||||
}
|
||||
|
||||
void OnSpecialTweak(BOOL bMinus) override
|
||||
@@ -811,7 +812,7 @@ struct TextTool : ToolBase
|
||||
|
||||
// Draw the text
|
||||
INT style = (toolsModel.IsBackgroundTransparent() ? 0 : 1);
|
||||
Text(hdc, rc.left, rc.top, rc.right, rc.bottom, m_fg, m_bg, szText,
|
||||
Text(hdc, rc.left, rc.top, rc.right, rc.bottom, toolsModel.GetFgBrush(), toolsModel.GetBgBrush(), szText,
|
||||
textEditWindow.GetFont(), style);
|
||||
}
|
||||
|
||||
@@ -902,8 +903,8 @@ struct LineTool : TwoPointDrawTool
|
||||
return;
|
||||
if (GetAsyncKeyState(VK_SHIFT) < 0)
|
||||
roundTo8Directions(g_ptStart.x, g_ptStart.y, g_ptEnd.x, g_ptEnd.y);
|
||||
COLORREF rgb = m_bLeftButton ? m_fg : m_bg;
|
||||
Line(hdc, g_ptStart.x, g_ptStart.y, g_ptEnd.x, g_ptEnd.y, rgb, toolsModel.GetLineWidth());
|
||||
HBRUSH hBrush = (m_bLeftButton ? toolsModel.GetFgBrush() : toolsModel.GetBgBrush());
|
||||
Line(hdc, g_ptStart.x, g_ptStart.y, g_ptEnd.x, g_ptEnd.y, hBrush, toolsModel.GetLineWidth());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -914,18 +915,17 @@ struct BezierTool : ToolBase
|
||||
|
||||
void OnDrawOverlayOnImage(HDC hdc)
|
||||
{
|
||||
COLORREF rgb = (m_bLeftButton ? m_fg : m_bg);
|
||||
HBRUSH hBrush = (m_bLeftButton ? toolsModel.GetFgBrush() : toolsModel.GetBgBrush());
|
||||
switch (s_cPoints)
|
||||
{
|
||||
case 2:
|
||||
Line(hdc, s_pPoints[0].x, s_pPoints[0].y, s_pPoints[1].x, s_pPoints[1].y, rgb,
|
||||
toolsModel.GetLineWidth());
|
||||
Line(hdc, s_pPoints[0].x, s_pPoints[0].y, s_pPoints[1].x, s_pPoints[1].y, hBrush, toolsModel.GetLineWidth());
|
||||
break;
|
||||
case 3:
|
||||
Bezier(hdc, s_pPoints[0], s_pPoints[2], s_pPoints[2], s_pPoints[1], rgb, toolsModel.GetLineWidth());
|
||||
Bezier(hdc, s_pPoints[0], s_pPoints[2], s_pPoints[2], s_pPoints[1], hBrush, toolsModel.GetLineWidth());
|
||||
break;
|
||||
case 4:
|
||||
Bezier(hdc, s_pPoints[0], s_pPoints[2], s_pPoints[3], s_pPoints[1], rgb, toolsModel.GetLineWidth());
|
||||
Bezier(hdc, s_pPoints[0], s_pPoints[2], s_pPoints[3], s_pPoints[1], hBrush, toolsModel.GetLineWidth());
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -994,9 +994,9 @@ struct RectTool : TwoPointDrawTool
|
||||
if (GetAsyncKeyState(VK_SHIFT) < 0)
|
||||
regularize(g_ptStart.x, g_ptStart.y, g_ptEnd.x, g_ptEnd.y);
|
||||
if (m_bLeftButton)
|
||||
Rect(hdc, g_ptStart.x, g_ptStart.y, g_ptEnd.x, g_ptEnd.y, m_fg, m_bg, toolsModel.GetLineWidth(), toolsModel.GetShapeStyle());
|
||||
Rect(hdc, g_ptStart.x, g_ptStart.y, g_ptEnd.x, g_ptEnd.y, toolsModel.GetFgBrush(), toolsModel.GetBgBrush(), toolsModel.GetLineWidth(), toolsModel.GetShapeStyle());
|
||||
else
|
||||
Rect(hdc, g_ptStart.x, g_ptStart.y, g_ptEnd.x, g_ptEnd.y, m_bg, m_fg, toolsModel.GetLineWidth(), toolsModel.GetShapeStyle());
|
||||
Rect(hdc, g_ptStart.x, g_ptStart.y, g_ptEnd.x, g_ptEnd.y, toolsModel.GetBgBrush(), toolsModel.GetFgBrush(), toolsModel.GetLineWidth(), toolsModel.GetShapeStyle());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1012,9 +1012,9 @@ struct ShapeTool : ToolBase
|
||||
return;
|
||||
|
||||
if (m_bLeftButton)
|
||||
Poly(hdc, s_pPoints, (INT)s_cPoints, m_fg, m_bg, toolsModel.GetLineWidth(), toolsModel.GetShapeStyle(), m_bClosed, FALSE);
|
||||
Poly(hdc, s_pPoints, (INT)s_cPoints, toolsModel.GetFgBrush(), toolsModel.GetBgBrush(), toolsModel.GetLineWidth(), toolsModel.GetShapeStyle(), m_bClosed, FALSE);
|
||||
else
|
||||
Poly(hdc, s_pPoints, (INT)s_cPoints, m_bg, m_fg, toolsModel.GetLineWidth(), toolsModel.GetShapeStyle(), m_bClosed, FALSE);
|
||||
Poly(hdc, s_pPoints, (INT)s_cPoints, toolsModel.GetBgBrush(), toolsModel.GetFgBrush(), toolsModel.GetLineWidth(), toolsModel.GetShapeStyle(), m_bClosed, FALSE);
|
||||
}
|
||||
|
||||
void OnButtonDown(BOOL bLeftButton, LONG x, LONG y, BOOL bDoubleClick) override
|
||||
@@ -1105,9 +1105,9 @@ struct EllipseTool : TwoPointDrawTool
|
||||
if (GetAsyncKeyState(VK_SHIFT) < 0)
|
||||
regularize(g_ptStart.x, g_ptStart.y, g_ptEnd.x, g_ptEnd.y);
|
||||
if (m_bLeftButton)
|
||||
Ellp(hdc, g_ptStart.x, g_ptStart.y, g_ptEnd.x, g_ptEnd.y, m_fg, m_bg, toolsModel.GetLineWidth(), toolsModel.GetShapeStyle());
|
||||
Ellp(hdc, g_ptStart.x, g_ptStart.y, g_ptEnd.x, g_ptEnd.y, toolsModel.GetFgBrush(), toolsModel.GetBgBrush(), toolsModel.GetLineWidth(), toolsModel.GetShapeStyle());
|
||||
else
|
||||
Ellp(hdc, g_ptStart.x, g_ptStart.y, g_ptEnd.x, g_ptEnd.y, m_bg, m_fg, toolsModel.GetLineWidth(), toolsModel.GetShapeStyle());
|
||||
Ellp(hdc, g_ptStart.x, g_ptStart.y, g_ptEnd.x, g_ptEnd.y, toolsModel.GetBgBrush(), toolsModel.GetFgBrush(), toolsModel.GetLineWidth(), toolsModel.GetShapeStyle());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1121,9 +1121,9 @@ struct RRectTool : TwoPointDrawTool
|
||||
if (GetAsyncKeyState(VK_SHIFT) < 0)
|
||||
regularize(g_ptStart.x, g_ptStart.y, g_ptEnd.x, g_ptEnd.y);
|
||||
if (m_bLeftButton)
|
||||
RRect(hdc, g_ptStart.x, g_ptStart.y, g_ptEnd.x, g_ptEnd.y, m_fg, m_bg, toolsModel.GetLineWidth(), toolsModel.GetShapeStyle());
|
||||
RRect(hdc, g_ptStart.x, g_ptStart.y, g_ptEnd.x, g_ptEnd.y, toolsModel.GetFgBrush(), toolsModel.GetBgBrush(), toolsModel.GetLineWidth(), toolsModel.GetShapeStyle());
|
||||
else
|
||||
RRect(hdc, g_ptStart.x, g_ptStart.y, g_ptEnd.x, g_ptEnd.y, m_bg, m_fg, toolsModel.GetLineWidth(), toolsModel.GetShapeStyle());
|
||||
RRect(hdc, g_ptStart.x, g_ptStart.y, g_ptEnd.x, g_ptEnd.y, toolsModel.GetBgBrush(), toolsModel.GetFgBrush(), toolsModel.GetLineWidth(), toolsModel.GetShapeStyle());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -109,24 +109,21 @@ LRESULT CPaletteWindow::OnPaint(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& b
|
||||
rc.top = Y_MARGIN + (CXY_BIGBOX * 5 / 8) - (CXY_SELECTEDBOX / 2);
|
||||
rc.right = rc.left + CXY_SELECTEDBOX;
|
||||
rc.bottom = rc.top + CXY_SELECTEDBOX;
|
||||
HBRUSH hbrBg = paletteModel.CreateBgBrush();
|
||||
drawColorBox(hMemDC, &rc, hbrBg, BDR_RAISEDINNER);
|
||||
DeleteObject(hbrBg);
|
||||
drawColorBox(hMemDC, &rc, toolsModel.GetBgBrush(), BDR_RAISEDINNER);
|
||||
|
||||
/* Draw the black box (overlapping the white box), at 3/8 position */
|
||||
rc.left = X_MARGIN + (CXY_BIGBOX * 3 / 8) - (CXY_SELECTEDBOX / 2);
|
||||
rc.top = Y_MARGIN + (CXY_BIGBOX * 3 / 8) - (CXY_SELECTEDBOX / 2);
|
||||
rc.right = rc.left + CXY_SELECTEDBOX;
|
||||
rc.bottom = rc.top + CXY_SELECTEDBOX;
|
||||
HBRUSH hbrFg = paletteModel.CreateFgBrush();
|
||||
drawColorBox(hMemDC, &rc, hbrFg, BDR_RAISEDINNER);
|
||||
DeleteObject(hbrFg);
|
||||
drawColorBox(hMemDC, &rc, toolsModel.GetFgBrush(), BDR_RAISEDINNER);
|
||||
|
||||
/* Draw the normal color boxes */
|
||||
const PAL_TYPE palette = paletteModel.SelectedPalette();
|
||||
for (INT i = 0; i < COLOR_COUNT; i++)
|
||||
{
|
||||
getColorBoxRect(&rc, rcClient, i);
|
||||
HBRUSH hbr = paletteModel.CreateColorBrush(paletteModel.GetColor(i));
|
||||
HBRUSH hbr = toolsModel.CreateBrush(palette, paletteModel.GetColor(i));
|
||||
drawColorBox(hMemDC, &rc, hbr, BDR_SUNKENOUTER);
|
||||
DeleteObject(hbr);
|
||||
}
|
||||
|
||||
@@ -120,78 +120,3 @@ void PaletteModel::NotifyPaletteChanged()
|
||||
if (paletteWindow.IsWindow())
|
||||
paletteWindow.Invalidate(FALSE);
|
||||
}
|
||||
|
||||
HBRUSH
|
||||
PaletteModel::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(BITMAPINFOHEADER);
|
||||
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;
|
||||
}
|
||||
|
||||
HBRUSH PaletteModel::CreateColorBrush(COLORREF color)
|
||||
{
|
||||
if (m_nSelectedPalette == PAL_MONOCHROME)
|
||||
return CreateDitherBrush(color, RGB(0, 0, 0), RGB(255, 255, 255));
|
||||
else
|
||||
return CreateSolidBrush(color);
|
||||
}
|
||||
|
||||
@@ -29,7 +29,6 @@ private:
|
||||
|
||||
void NotifyColorChanged();
|
||||
void NotifyPaletteChanged();
|
||||
static HBRUSH CreateDitherBrush(COLORREF color, COLORREF monoColor0, COLORREF monoColor1);
|
||||
|
||||
public:
|
||||
PaletteModel();
|
||||
@@ -41,7 +40,4 @@ public:
|
||||
void SetFgColor(COLORREF newColor);
|
||||
COLORREF GetBgColor() const;
|
||||
void SetBgColor(COLORREF newColor);
|
||||
HBRUSH CreateColorBrush(COLORREF color);
|
||||
HBRUSH CreateFgBrush() { return CreateColorBrush(m_fgColor); }
|
||||
HBRUSH CreateBgBrush() { return CreateColorBrush(m_bgColor); }
|
||||
};
|
||||
|
||||
@@ -11,6 +11,30 @@ ToolsModel toolsModel;
|
||||
|
||||
/* FUNCTIONS ********************************************************/
|
||||
|
||||
ToolsModel::VirtualBrush::~VirtualBrush()
|
||||
{
|
||||
if (m_hBrush)
|
||||
::DeleteObject(m_hBrush);
|
||||
}
|
||||
|
||||
HBRUSH ToolsModel::VirtualBrush::GetBrush(PAL_TYPE palette, COLORREF rgbColor)
|
||||
{
|
||||
if (m_hBrush &&
|
||||
m_palette == palette &&
|
||||
m_rgbColor == rgbColor)
|
||||
{
|
||||
return m_hBrush;
|
||||
}
|
||||
|
||||
if (m_hBrush)
|
||||
::DeleteObject(m_hBrush);
|
||||
|
||||
m_hBrush = ToolsModel::CreateBrush(palette, rgbColor);
|
||||
m_palette = palette;
|
||||
m_rgbColor = rgbColor;
|
||||
return m_hBrush;
|
||||
}
|
||||
|
||||
ToolsModel::ToolsModel()
|
||||
{
|
||||
m_lineWidth = m_penWidth = 1;
|
||||
@@ -321,3 +345,21 @@ void ToolsModel::selectAll()
|
||||
OnMouseMove(TRUE, imageModel.GetWidth(), imageModel.GetHeight());
|
||||
OnButtonUp(TRUE, imageModel.GetWidth(), imageModel.GetHeight());
|
||||
}
|
||||
|
||||
HBRUSH ToolsModel::CreateBrush(PAL_TYPE palette, COLORREF color)
|
||||
{
|
||||
if (palette == PAL_MONOCHROME)
|
||||
return CreateDitherBrush(color, RGB(0, 0, 0), RGB(255, 255, 255));
|
||||
else
|
||||
return ::CreateSolidBrush(color);
|
||||
}
|
||||
|
||||
HBRUSH ToolsModel::GetFgBrush()
|
||||
{
|
||||
return m_fgBrush.GetBrush(paletteModel.SelectedPalette(), paletteModel.GetFgColor());
|
||||
}
|
||||
|
||||
HBRUSH ToolsModel::GetBgBrush()
|
||||
{
|
||||
return m_bgBrush.GetBrush(paletteModel.SelectedPalette(), paletteModel.GetBgColor());
|
||||
}
|
||||
|
||||
@@ -67,6 +67,19 @@ struct ToolBase
|
||||
class ToolsModel
|
||||
{
|
||||
private:
|
||||
class VirtualBrush
|
||||
{
|
||||
protected:
|
||||
COLORREF m_rgbColor = RGB(0, 0, 0);
|
||||
PAL_TYPE m_palette = PAL_MODERN;
|
||||
HBRUSH m_hBrush = NULL;
|
||||
|
||||
public:
|
||||
~VirtualBrush();
|
||||
|
||||
HBRUSH GetBrush(PAL_TYPE palette, COLORREF rgbColor);
|
||||
};
|
||||
|
||||
int m_lineWidth;
|
||||
INT m_penWidth;
|
||||
INT m_brushWidth;
|
||||
@@ -79,6 +92,8 @@ private:
|
||||
BOOL m_transpBg;
|
||||
int m_zoom;
|
||||
ToolBase *m_pToolObject;
|
||||
VirtualBrush m_fgBrush;
|
||||
VirtualBrush m_bgBrush;
|
||||
|
||||
ToolBase *GetOrCreateTool(TOOLTYPE nTool);
|
||||
void SendSetCursor();
|
||||
@@ -144,6 +159,10 @@ public:
|
||||
void SpecialTweak(BOOL bMinus);
|
||||
|
||||
void DrawWithMouseTool(POINT pt, WPARAM wParam);
|
||||
|
||||
HBRUSH GetFgBrush();
|
||||
HBRUSH GetBgBrush();
|
||||
static HBRUSH CreateBrush(PAL_TYPE palette, COLORREF color);
|
||||
};
|
||||
|
||||
extern ToolsModel toolsModel;
|
||||
|
||||
Reference in New Issue
Block a user