From 3bf953bcc33f787c606b2f103868cf7550a995cc Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 2 Jun 2023 00:47:22 +0100 Subject: [PATCH] Factor out CustomPaint() function from dark mode code No real changes yet, just make it possible to use the function customizing the default WM_PAINT handle from other places too. --- include/wx/msw/private/custompaint.h | 82 +++++++++++++++++++++++++++ src/msw/darkmode.cpp | 83 +++++++++++++--------------- 2 files changed, 120 insertions(+), 45 deletions(-) create mode 100644 include/wx/msw/private/custompaint.h diff --git a/include/wx/msw/private/custompaint.h b/include/wx/msw/private/custompaint.h new file mode 100644 index 0000000000..e8ef9a3d7c --- /dev/null +++ b/include/wx/msw/private/custompaint.h @@ -0,0 +1,82 @@ +/////////////////////////////////////////////////////////////////////////////// +// Name: wx/msw/private/custompaint.h +// Purpose: Helper function for customizing the standard WM_PAINT handling. +// Author: Vadim Zeitlin +// Created: 2023-06-02 +// Copyright: (c) 2023 Vadim Zeitlin +// Licence: wxWindows licence +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _WX_MSW_PRIVATE_CUSTOMPAINT_H_ +#define _WX_MSW_PRIVATE_CUSTOMPAINT_H_ + +#include "wx/dcmemory.h" +#include "wx/image.h" + +#include "wx/msw/dc.h" + +namespace wxMSWImpl +{ + +// This function can be used as CustomPaint() callback to post process the +// bitmap by applying the specific functor to each of its pixels. +template +wxBitmap +PostPaintEachPixel(const wxBitmap& bmp, FuncProcessPixel processPixel) +{ +#if wxUSE_IMAGE + wxImage image = bmp.ConvertToImage(); + + unsigned char *data = image.GetData(); + unsigned char *alpha = image.GetAlpha(); + unsigned char alphaOpaque = wxALPHA_OPAQUE; + const int len = image.GetWidth()*image.GetHeight(); + for ( int i = 0; i < len; ++i, data += 3 ) + { + processPixel(data[0], data[1], data[2], alpha ? *alpha++ : alphaOpaque); + } + + return wxBitmap(image); +#else // !wxUSE_IMAGE + return bmp; +#endif // wxUSE_IMAGE/!wxUSE_IMAGE +} + +// This function uses the default WM_PAINT handler to paint the window contents +// into a bitmap and then the provided function to tweak the pixels of this +// bitmap. +// +// The first argument is a functor (typically a lambda) to paint the on the +// given HDC and the second one is another functor called to post process the +// bitmap before actually drawing it. +// +// It can only be called from WM_PAINT handler for a native control and assumes +// that this control handles WPARAM argument of WM_PAINT as HDC to paint on. +template +void +CustomPaint(HWND hwnd, FuncDefPaint defPaint, FuncPostProcess postProcess) +{ + const RECT rc = wxGetClientRect(hwnd); + const wxSize size{rc.right - rc.left, rc.bottom - rc.top}; + + // Don't bother doing anything with the empty windows. + if ( size == wxSize() ) + return; + + // Ask the control to paint itself on the given bitmap. + wxBitmap bmp(size); + { + wxMemoryDC mdc(bmp); + + defPaint(hwnd, (WPARAM)GetHdcOf(mdc)); + } + + PAINTSTRUCT ps; + wxDCTemp dc(::BeginPaint(hwnd, &ps), size); + dc.DrawBitmap(postProcess(bmp), 0, 0); + ::EndPaint(hwnd, &ps); +} + +} // namespace wxMSWImpl + +#endif // _WX_MSW_PRIVATE_CUSTOMPAINT_H_ diff --git a/src/msw/darkmode.cpp b/src/msw/darkmode.cpp index eec84f5edf..de0fbb80cb 100644 --- a/src/msw/darkmode.cpp +++ b/src/msw/darkmode.cpp @@ -48,6 +48,7 @@ #include "wx/msw/dc.h" #include "wx/msw/uxtheme.h" +#include "wx/msw/private/custompaint.h" #include "wx/msw/private/darkmode.h" #include @@ -431,32 +432,35 @@ HBRUSH GetBackgroundBrush() return brush ? GetHbrushOf(*brush) : 0; } +#if wxUSE_IMAGE + +static void +InvertBitmapPixel(unsigned char& r, unsigned char& g, unsigned char& b, + unsigned char& WXUNUSED(a)) +{ + wxImage::RGBValue rgb(r, g, b); + wxImage::HSVValue hsv = wxImage::RGBtoHSV(rgb); + + // There is no really good way to convert normal colours to dark mode, + // but try to do a bit better than just inverting the value because + // this results in colours which are much too dark. + hsv.value = sqrt(1.0 - hsv.value*hsv.value); + + rgb = wxImage::HSVtoRGB(hsv); + + r = rgb.red; + g = rgb.green; + b = rgb.blue; +} + +#endif // wxUSE_IMAGE + wxBitmap InvertBitmap(const wxBitmap& bmp) { #if wxUSE_IMAGE - wxImage image = bmp.ConvertToImage(); - - unsigned char *data = image.GetData(); - const int len = image.GetWidth()*image.GetHeight(); - for ( int i = 0; i < len; ++i, data += 3 ) - { - wxImage::RGBValue rgb(data[0], data[1], data[2]); - wxImage::HSVValue hsv = wxImage::RGBtoHSV(rgb); - - // There is no really good way to convert normal colours to dark mode, - // but try to do a bit better than just inverting the value because - // this results in colours which are much too dark. - hsv.value = sqrt(1.0 - hsv.value*hsv.value); - - rgb = wxImage::HSVtoRGB(hsv); - data[0] = rgb.red; - data[1] = rgb.green; - data[2] = rgb.blue; - } - - return wxBitmap(image); + return wxMSWImpl::PostPaintEachPixel(bmp, InvertBitmapPixel); #else // !wxUSE_IMAGE - return wxBitmap(); + return bmp; #endif // wxUSE_IMAGE/!wxUSE_IMAGE } @@ -466,29 +470,18 @@ bool PaintIfNecessary(HWND hwnd, WXWNDPROC defWndProc) if ( !wxMSWImpl::ShouldUseDarkMode() ) return false; - const RECT rc = wxGetClientRect(hwnd); - const wxSize size{rc.right - rc.left, rc.bottom - rc.top}; - - // Don't bother doing anything with the empty windows. - if ( size == wxSize() ) - return false; - - // Ask the control to paint itself on the given bitmap. - wxBitmap bmp(size); - { - wxMemoryDC mdc(bmp); - - WPARAM wparam = (WPARAM)GetHdcOf(mdc); - if ( defWndProc ) - ::CallWindowProc(defWndProc, hwnd, WM_PAINT, wparam, 0); - else - ::DefWindowProc(hwnd, WM_PAINT, wparam, 0); - } - - PAINTSTRUCT ps; - wxDCTemp dc(::BeginPaint(hwnd, &ps), size); - dc.DrawBitmap(InvertBitmap(bmp), 0, 0); - ::EndPaint(hwnd, &ps); + wxMSWImpl::CustomPaint + ( + hwnd, + [defWndProc](HWND hwnd, WPARAM wParam) + { + if ( defWndProc ) + ::CallWindowProc(defWndProc, hwnd, WM_PAINT, wParam, 0); + else + ::DefWindowProc(hwnd, WM_PAINT, wParam, 0); + }, + InvertBitmap + ); return true; #else // !wxUSE_IMAGE