From 455c1fe1b989a3c1a6731e2aa8beb2bdf05fdf2f Mon Sep 17 00:00:00 2001 From: Katayama Hirofumi MZ Date: Sun, 20 Aug 2023 16:46:18 +0900 Subject: [PATCH] [MSPAINT] Speed up for black and white (#5563) Follow-up to #5554. - Speed up ImageModel::PushBlackAndWhite by using GetDIBits and SetDIBits. CORE-19094 --- base/applications/mspaint/dib.cpp | 93 +++++++++++++++++++++++++++ base/applications/mspaint/dib.h | 2 + base/applications/mspaint/history.cpp | 44 +++---------- 3 files changed, 104 insertions(+), 35 deletions(-) diff --git a/base/applications/mspaint/dib.cpp b/base/applications/mspaint/dib.cpp index c12a2f013a8..e770f5494d0 100644 --- a/base/applications/mspaint/dib.cpp +++ b/base/applications/mspaint/dib.cpp @@ -13,6 +13,13 @@ float g_xDpi = 96; float g_yDpi = 96; SYSTEMTIME g_fileTime; +#define WIDTHBYTES(i) (((i) + 31) / 32 * 4) + +struct BITMAPINFOEX : BITMAPINFO +{ + RGBQUAD bmiColorsExtra[256 - 1]; +}; + /* FUNCTIONS ********************************************************/ // Convert DPI (dots per inch) into PPCM (pixels per centimeter) @@ -519,3 +526,89 @@ HBITMAP BitmapFromHEMF(HENHMETAFILE hEMF) return hbm; } + +BOOL IsBitmapBlackAndWhite(HBITMAP hbm) +{ + BITMAP bm; + if (!::GetObjectW(hbm, sizeof(bm), &bm)) + return FALSE; + + if (bm.bmBitsPixel == 1) + return TRUE; + + BITMAPINFOEX bmi; + ZeroMemory(&bmi, sizeof(bmi)); + bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader); + bmi.bmiHeader.biWidth = bm.bmWidth; + bmi.bmiHeader.biHeight = bm.bmHeight; + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = 24; + + DWORD widthbytes = WIDTHBYTES(24 * bm.bmWidth); + DWORD cbBits = widthbytes * bm.bmHeight; + LPBYTE pbBits = new BYTE[cbBits]; + + HDC hdc = ::CreateCompatibleDC(NULL); + ::GetDIBits(hdc, hbm, 0, bm.bmHeight, pbBits, &bmi, DIB_RGB_COLORS); + ::DeleteDC(hdc); + + BOOL bBlackAndWhite = TRUE; + for (LONG y = 0; y < bm.bmHeight; ++y) + { + LPBYTE pbLine = &pbBits[widthbytes * y]; + for (LONG x = 0; x < bm.bmWidth; ++x) + { + BYTE Blue = *pbLine++; + BYTE Green = *pbLine++; + BYTE Red = *pbLine++; + COLORREF rgbColor = RGB(Red, Green, Blue); + if (rgbColor != RGB(0, 0, 0) && rgbColor != RGB(255, 255, 255)) + { + bBlackAndWhite = FALSE; + goto Finish; + } + } + } + +Finish: + delete[] pbBits; + + return bBlackAndWhite; +} + +HBITMAP ConvertToBlackAndWhite(HBITMAP hbm) +{ + BITMAP bm; + if (!::GetObject(hbm, sizeof(bm), &bm)) + return NULL; + + BITMAPINFOEX bmi; + ZeroMemory(&bmi, sizeof(bmi)); + bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bmi.bmiHeader.biWidth = bm.bmWidth; + bmi.bmiHeader.biHeight = bm.bmHeight; + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = 1; + bmi.bmiColors[1].rgbBlue = 255; + bmi.bmiColors[1].rgbGreen = 255; + bmi.bmiColors[1].rgbRed = 255; + HDC hdc = ::CreateCompatibleDC(NULL); + LPVOID pvMonoBits; + HBITMAP hMonoBitmap = ::CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, &pvMonoBits, NULL, 0); + if (!hMonoBitmap) + { + ::DeleteDC(hdc); + return NULL; + } + + HBITMAP hNewBitmap = CreateDIBWithProperties(bm.bmWidth, bm.bmHeight); + if (hNewBitmap) + { + ::GetDIBits(hdc, hbm, 0, bm.bmHeight, pvMonoBits, &bmi, DIB_RGB_COLORS); + ::SetDIBits(hdc, hNewBitmap, 0, bm.bmHeight, pvMonoBits, &bmi, DIB_RGB_COLORS); + } + ::DeleteObject(hMonoBitmap); + ::DeleteDC(hdc); + + return hNewBitmap; +} diff --git a/base/applications/mspaint/dib.h b/base/applications/mspaint/dib.h index cefdb6e42b5..bb9d933a14a 100644 --- a/base/applications/mspaint/dib.h +++ b/base/applications/mspaint/dib.h @@ -7,10 +7,12 @@ #pragma once +BOOL IsBitmapBlackAndWhite(HBITMAP hbm); HBITMAP CreateDIBWithProperties(int width, int height); HBITMAP CreateMonoBitmap(int width, int height, BOOL bWhite); HBITMAP CreateColorDIB(int width, int height, COLORREF rgb); HBITMAP CachedBufferDIB(HBITMAP hbm, int minimalWidth, int minimalHeight); +HBITMAP ConvertToBlackAndWhite(HBITMAP hbm); HBITMAP CopyMonoImage(HBITMAP hbm, INT cx = 0, INT cy = 0); diff --git a/base/applications/mspaint/history.cpp b/base/applications/mspaint/history.cpp index 8867a53025a..4451b06f927 100644 --- a/base/applications/mspaint/history.cpp +++ b/base/applications/mspaint/history.cpp @@ -270,44 +270,18 @@ HBITMAP ImageModel::CopyBitmap() BOOL ImageModel::IsBlackAndWhite() { - LONG cxWidth = GetWidth(), cyHeight = GetHeight(); - for (LONG y = 0; y < cyHeight; ++y) - { - for (LONG x = 0; x < cxWidth; ++x) - { - COLORREF rgbColor = ::GetPixel(m_hDrawingDC, x, y); - if (rgbColor != RGB(0, 0, 0) && rgbColor != RGB(255, 255, 255)) - return FALSE; - } - } - return TRUE; + ::SelectObject(m_hDrawingDC, m_hbmOld); // De-select + BOOL bBlackAndWhite = IsBitmapBlackAndWhite(m_hBms[m_currInd]); + m_hbmOld = ::SelectObject(m_hDrawingDC, m_hBms[m_currInd]); // Re-select + return bBlackAndWhite; } void ImageModel::PushBlackAndWhite() { - HBITMAP hNewBitmap = CopyBitmap(); - if (!hNewBitmap) - return; + ::SelectObject(m_hDrawingDC, m_hbmOld); // De-select + HBITMAP hNewBitmap = ConvertToBlackAndWhite(m_hBms[m_currInd]); + m_hbmOld = ::SelectObject(m_hDrawingDC, m_hBms[m_currInd]); // Re-select - HDC hdc2 = ::CreateCompatibleDC(NULL); - HGDIOBJ hbm2Old = ::SelectObject(hdc2, hNewBitmap); - LONG cxWidth = GetWidth(), cyHeight = GetHeight(); - for (LONG y = 0; y < cyHeight; ++y) - { - for (LONG x = 0; x < cxWidth; ++x) - { - COLORREF rgbColor = ::GetPixel(m_hDrawingDC, x, y); - BYTE Red = GetRValue(rgbColor); - BYTE Green = GetGValue(rgbColor); - BYTE Blue = GetBValue(rgbColor); - if ((Red + Green + Blue) / 3 >= 255 / 2) - ::SetPixelV(hdc2, x, y, RGB(255, 255, 255)); // White - else - ::SetPixelV(hdc2, x, y, RGB(0, 0, 0)); // Black - } - } - ::SelectObject(hdc2, hbm2Old); - ::DeleteDC(hdc2); - - PushImageForUndo(hNewBitmap); + if (hNewBitmap) + PushImageForUndo(hNewBitmap); }