diff options
Diffstat (limited to 'win32/PlatWin.cxx')
-rw-r--r-- | win32/PlatWin.cxx | 187 |
1 files changed, 121 insertions, 66 deletions
diff --git a/win32/PlatWin.cxx b/win32/PlatWin.cxx index b2b90fa5e..342900703 100644 --- a/win32/PlatWin.cxx +++ b/win32/PlatWin.cxx @@ -730,14 +730,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; } @@ -754,57 +746,138 @@ DWORD dwordMultiplied(ColourDesired colour, unsigned int alpha) noexcept { static_cast<byte>(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<unsigned char *>(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<DWORD *>(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<LONG>(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<DWORD *>(image); - for (int y=0; y<height; y++) { - for (int x=0; x<width; x++) { - if ((x==0) || (x==width-1) || (y == 0) || (y == height-1)) { - pixels[y*width+x] = valOutline; + // Draw a framed rectangle + for (int y=0; y<size.cy; y++) { + for (int x=0; x<size.cx; x++) { + if ((x==0) || (x==size.cx-1) || (y == 0) || (y == size.cy -1)) { + section.SetPixel(x, y, valOutline); } else { - pixels[y*width+x] = valFill; + section.SetPixel(x, y, valFill); } } } - for (int c=0; c<cornerSize; c++) { - for (int x=0; x<c+1; x++) { - AllFour(pixels, width, height, x, c-x, valEmpty); + + // Make the corners transparent + for (LONG c=0; c<corner; c++) { + for (LONG x=0; x<c+1; x++) { + section.SetSymmetric(x, c - x, valEmpty); } } - for (int x=1; x<cornerSize; x++) { - AllFour(pixels, width, height, x, cornerSize-x, valOutline); - } - - const BLENDFUNCTION merge = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA }; - AlphaBlend(hdc, rcw.left, rcw.top, width, height, hMemDC, 0, 0, width, height, merge); + // Draw the corner frame pieces + for (LONG x=1; x<corner; x++) { + section.SetSymmetric(x, corner - x, valOutline); + } - SelectBitmap(hMemDC, hbmOld); - ::DeleteObject(hbmMem); + AlphaBlend(hdc, rcw.left, rcw.top, size.cx, size.cy, section.DC(), 0, 0, size.cx, size.cy, mergeAlpha); } - ::DeleteDC(hMemDC); } else { BrushColour(outline); FrameRect(hdc, &rcw, brush); @@ -820,7 +893,6 @@ void SurfaceGDI::GradientRectangle(PRectangle rc, const std::vector<ColourStop> 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; @@ -828,31 +900,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<unsigned char *>(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<int>(rc.left), static_cast<int>(rc.top), - static_cast<int>(rc.Width()), static_cast<int>(rc.Height()), hMemDC, 0, 0, width, height, merge); - - SelectBitmap(hMemDC, hbmOld); - ::DeleteObject(hbmMem); + static_cast<int>(rc.Width()), static_cast<int>(rc.Height()), section.DC(), + 0, 0, width, height, mergeAlpha); } - ::DeleteDC(hMemDC); - } } |