From d6ac48ec5dd93f010eb1307ce676bb2a68461ea6 Mon Sep 17 00:00:00 2001 From: Katayama Hirofumi MZ Date: Tue, 7 Jul 2026 12:44:29 +0900 Subject: [PATCH] [MSPAINT] Support dithering drawing (#9175) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- base/applications/mspaint/drawing.cpp | 469 ++++++++++++++++++--- base/applications/mspaint/drawing.h | 15 + base/applications/mspaint/mouse.cpp | 52 +-- base/applications/mspaint/palette.cpp | 11 +- base/applications/mspaint/palettemodel.cpp | 75 ---- base/applications/mspaint/palettemodel.h | 4 - base/applications/mspaint/toolsmodel.cpp | 42 ++ base/applications/mspaint/toolsmodel.h | 19 + 8 files changed, 512 insertions(+), 175 deletions(-) diff --git a/base/applications/mspaint/drawing.cpp b/base/applications/mspaint/drawing.cpp index 70d9f203780..b80b46a5b8b 100644 --- a/base/applications/mspaint/drawing.cpp +++ b/base/applications/mspaint/drawing.cpp @@ -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 + * Copyright 2026 Katayama Hirofumi MZ */ #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; +} diff --git a/base/applications/mspaint/drawing.h b/base/applications/mspaint/drawing.h index 676bb578bb7..2598fe086e5 100644 --- a/base/applications/mspaint/drawing.h +++ b/base/applications/mspaint/drawing.h @@ -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 + * Copyright 2026 Katayama Hirofumi MZ */ #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); diff --git a/base/applications/mspaint/mouse.cpp b/base/applications/mspaint/mouse.cpp index 115e8474f9e..e747fbce864 100644 --- a/base/applications/mspaint/mouse.cpp +++ b/base/applications/mspaint/mouse.cpp @@ -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()); } }; diff --git a/base/applications/mspaint/palette.cpp b/base/applications/mspaint/palette.cpp index 01bd1677791..b1f9dca4aa9 100644 --- a/base/applications/mspaint/palette.cpp +++ b/base/applications/mspaint/palette.cpp @@ -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); } diff --git a/base/applications/mspaint/palettemodel.cpp b/base/applications/mspaint/palettemodel.cpp index aedfe9d5cfb..68639121903 100644 --- a/base/applications/mspaint/palettemodel.cpp +++ b/base/applications/mspaint/palettemodel.cpp @@ -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); -} diff --git a/base/applications/mspaint/palettemodel.h b/base/applications/mspaint/palettemodel.h index 8731630df98..74230d85b80 100644 --- a/base/applications/mspaint/palettemodel.h +++ b/base/applications/mspaint/palettemodel.h @@ -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); } }; diff --git a/base/applications/mspaint/toolsmodel.cpp b/base/applications/mspaint/toolsmodel.cpp index 93fa233c685..c7cc731ed75 100644 --- a/base/applications/mspaint/toolsmodel.cpp +++ b/base/applications/mspaint/toolsmodel.cpp @@ -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()); +} diff --git a/base/applications/mspaint/toolsmodel.h b/base/applications/mspaint/toolsmodel.h index 2d4813866c6..d39f454d83a 100644 --- a/base/applications/mspaint/toolsmodel.h +++ b/base/applications/mspaint/toolsmodel.h @@ -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;