From 02de87171c8b154a925c0da3190bf40b7ddb2750 Mon Sep 17 00:00:00 2001 From: Neil Date: Sat, 6 Jun 2020 16:42:27 +1000 Subject: Backport: Add DIBSection class to simplify bitmap operations on GDI. Backport of changeset 8290:7d93501a4443. --- win32/PlatWin.cxx | 187 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 121 insertions(+), 66 deletions(-) (limited to 'win32') diff --git a/win32/PlatWin.cxx b/win32/PlatWin.cxx index 09cb2f517..56f627e70 100644 --- a/win32/PlatWin.cxx +++ b/win32/PlatWin.cxx @@ -735,14 +735,6 @@ void SurfaceGDI::RoundedRectangle(PRectangle rc, ColourDesired fore, ColourDesir namespace { -// Plot a point into a DWORD buffer symmetrically to all 4 quadrants -void AllFour(DWORD *pixels, int width, int height, int x, int y, DWORD val) noexcept { - pixels[y*width+x] = val; - pixels[y*width+width-1-x] = val; - pixels[(height-1-y)*width+x] = val; - pixels[(height-1-y)*width+width-1-x] = val; -} - constexpr DWORD dwordFromBGRA(byte b, byte g, byte r, byte a) noexcept { return (a << 24) | (r << 16) | (g << 8) | b; } @@ -759,57 +751,138 @@ DWORD dwordMultiplied(ColourDesired colour, unsigned int alpha) noexcept { static_cast(alpha)); } +class DIBSection { + HDC hMemDC {}; + HBITMAP hbmMem {}; + HBITMAP hbmOld {}; + SIZE size {}; + DWORD *pixels = nullptr; +public: + DIBSection(HDC hdc, SIZE size_) noexcept; + // Deleted so DIBSection objects can not be copied. + DIBSection(const DIBSection&) = delete; + DIBSection(DIBSection&&) = delete; + DIBSection &operator=(const DIBSection&) = delete; + DIBSection &operator=(DIBSection&&) = delete; + ~DIBSection() noexcept; + operator bool() const noexcept { + return hMemDC && hbmMem && pixels; + } + DWORD *Pixels() const noexcept { + return pixels; + } + unsigned char *Bytes() const noexcept { + return reinterpret_cast(pixels); + } + HDC DC() const noexcept { + return hMemDC; + } + void SetPixel(LONG x, LONG y, DWORD value) noexcept { + PLATFORM_ASSERT(x >= 0); + PLATFORM_ASSERT(y >= 0); + PLATFORM_ASSERT(x < size.cx); + PLATFORM_ASSERT(y < size.cy); + pixels[y * size.cx + x] = value; + } + void SetSymmetric(LONG x, LONG y, DWORD value) noexcept; +}; + +DIBSection::DIBSection(HDC hdc, SIZE size_) noexcept { + hMemDC = ::CreateCompatibleDC(hdc); + if (!hMemDC) { + return; + } + + size = size_; + + // -size.y makes bitmap start from top + const BITMAPINFO bpih = { {sizeof(BITMAPINFOHEADER), size.cx, -size.cy, 1, 32, BI_RGB, 0, 0, 0, 0, 0}, + {{0, 0, 0, 0}} }; + void *image = nullptr; + hbmMem = CreateDIBSection(hMemDC, &bpih, DIB_RGB_COLORS, &image, {}, 0); + if (!hbmMem || !image) { + return; + } + pixels = static_cast(image); + hbmOld = SelectBitmap(hMemDC, hbmMem); +} + +DIBSection::~DIBSection() noexcept { + if (hbmOld) { + SelectBitmap(hMemDC, hbmOld); + hbmOld = {}; + } + if (hbmMem) { + ::DeleteObject(hbmMem); + hbmMem = {}; + } + if (hMemDC) { + ::DeleteDC(hMemDC); + hMemDC = {}; + } +} + +void DIBSection::SetSymmetric(LONG x, LONG y, DWORD value) noexcept { + // Plot a point symmetrically to all 4 quadrants + const LONG xSymmetric = size.cx - 1 - x; + const LONG ySymmetric = size.cy - 1 - y; + SetPixel(x, y, value); + SetPixel(xSymmetric, y, value); + SetPixel(x, ySymmetric, value); + SetPixel(xSymmetric, ySymmetric, value); +} + +constexpr SIZE SizeOfRect(RECT rc) noexcept { + return { rc.right - rc.left, rc.bottom - rc.top }; +} + +constexpr BLENDFUNCTION mergeAlpha = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA }; + } void SurfaceGDI::AlphaRectangle(PRectangle rc, int cornerSize, ColourDesired fill, int alphaFill, ColourDesired outline, int alphaOutline, int /* flags*/ ) { const RECT rcw = RectFromPRectangle(rc); - if (rc.Width() > 0) { - HDC hMemDC = ::CreateCompatibleDC(hdc); - const int width = rcw.right - rcw.left; - const int height = rcw.bottom - rcw.top; - // Ensure not distorted too much by corners when small - cornerSize = std::min(cornerSize, (std::min(width, height) / 2) - 2); - const BITMAPINFO bpih = {{sizeof(BITMAPINFOHEADER), width, height, 1, 32, BI_RGB, 0, 0, 0, 0, 0}, - {{0, 0, 0, 0}}}; - void *image = nullptr; - HBITMAP hbmMem = CreateDIBSection(hMemDC, &bpih, - DIB_RGB_COLORS, &image, NULL, 0); - - if (hbmMem) { - HBITMAP hbmOld = SelectBitmap(hMemDC, hbmMem); - - const DWORD valEmpty = dwordFromBGRA(0,0,0,0); + const SIZE size = SizeOfRect(rcw); + + if (size.cx > 0) { + + DIBSection section(hdc, size); + + if (section) { + + // Ensure not distorted too much by corners when small + const LONG corner = std::min(cornerSize, (std::min(size.cx, size.cy) / 2) - 2); + + constexpr DWORD valEmpty = dwordFromBGRA(0,0,0,0); const DWORD valFill = dwordMultiplied(fill, alphaFill); const DWORD valOutline = dwordMultiplied(outline, alphaOutline); - DWORD *pixels = static_cast(image); - for (int y=0; y void SurfaceGDI::DrawRGBAImage(PRectangle rc, int width, int height, const unsigned char *pixelsImage) { if (rc.Width() > 0) { - HDC hMemDC = ::CreateCompatibleDC(hdc); if (rc.Width() > width) rc.left += std::floor((rc.Width() - width) / 2); rc.right = rc.left + width; @@ -833,31 +905,14 @@ void SurfaceGDI::DrawRGBAImage(PRectangle rc, int width, int height, const unsig rc.top += std::floor((rc.Height() - height) / 2); rc.bottom = rc.top + height; - const BITMAPINFO bpih = {{sizeof(BITMAPINFOHEADER), width, height, 1, 32, BI_RGB, 0, 0, 0, 0, 0}, - {{0, 0, 0, 0}}}; - void *image = nullptr; - HBITMAP hbmMem = ::CreateDIBSection(hMemDC, &bpih, - DIB_RGB_COLORS, &image, {}, 0); - if (hbmMem) { - HBITMAP hbmOld = SelectBitmap(hMemDC, hbmMem); - - for (int y=height-1; y>=0; y--) { - // Bits flipped vertically - unsigned char *pixel = static_cast(image) + RGBAImage::bytesPerPixel * y * width; - RGBAImage::BGRAFromRGBA(pixel, pixelsImage, width); - pixelsImage += RGBAImage::bytesPerPixel * width; - } - - const BLENDFUNCTION merge = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA }; - + const SIZE size { width, height }; + DIBSection section(hdc, size); + if (section) { + RGBAImage::BGRAFromRGBA(section.Bytes(), pixelsImage, width * height); AlphaBlend(hdc, static_cast(rc.left), static_cast(rc.top), - static_cast(rc.Width()), static_cast(rc.Height()), hMemDC, 0, 0, width, height, merge); - - SelectBitmap(hMemDC, hbmOld); - ::DeleteObject(hbmMem); + static_cast(rc.Width()), static_cast(rc.Height()), section.DC(), + 0, 0, width, height, mergeAlpha); } - ::DeleteDC(hMemDC); - } } -- cgit v1.2.3