diff options
Diffstat (limited to 'win32/PlatWin.cxx')
-rw-r--r-- | win32/PlatWin.cxx | 156 |
1 files changed, 150 insertions, 6 deletions
diff --git a/win32/PlatWin.cxx b/win32/PlatWin.cxx index ae819bb69..dacea8522 100644 --- a/win32/PlatWin.cxx +++ b/win32/PlatWin.cxx @@ -459,7 +459,9 @@ class SurfaceGDI : public Surface { int codePage = 0; - void BrushColour(ColourDesired back) noexcept; + void PenColour(ColourAlpha fore, XYPOSITION widthStroke) noexcept; + + void BrushColour(ColourAlpha back) noexcept; void SetFont(const Font *font_) noexcept; void Clear() noexcept; @@ -487,17 +489,21 @@ public: void MoveTo(int x_, int y_) override; void LineTo(int x_, int y_) override; void Polygon(Point *pts, size_t npts, ColourDesired fore, ColourDesired back) override; + void Polygon(const Point *pts, size_t npts, FillStroke fillStroke) override; void RectangleDraw(PRectangle rc, ColourDesired fore, ColourDesired back) override; + void RectangleDraw(PRectangle rc, FillStroke fillStroke) override; void FillRectangle(PRectangle rc, ColourDesired back) override; void FillRectangle(PRectangle rc, Fill fill) override; void FillRectangle(PRectangle rc, Surface &surfacePattern) override; void RoundedRectangle(PRectangle rc, ColourDesired fore, ColourDesired back) override; + void RoundedRectangle(PRectangle rc, FillStroke fillStroke) override; void AlphaRectangle(PRectangle rc, int cornerSize, ColourDesired fill, int alphaFill, ColourDesired outline, int alphaOutline, int flags) override; void AlphaRectangle(PRectangle rc, XYPOSITION cornerSize, FillStroke fillStroke) override; void GradientRectangle(PRectangle rc, const std::vector<ColourStop> &stops, GradientOptions options) override; void DrawRGBAImage(PRectangle rc, int width, int height, const unsigned char *pixelsImage) override; void Ellipse(PRectangle rc, ColourDesired fore, ColourDesired back) override; + void Ellipse(PRectangle rc, FillStroke fillStroke) override; void Copy(PRectangle rc, Point from, Surface &surfaceSource) override; std::unique_ptr<IScreenLineLayout> Layout(const IScreenLine *screenLine) override; @@ -625,14 +631,36 @@ void SurfaceGDI::PenColour(ColourDesired fore) { penOld = SelectPen(hdc, pen); } -void SurfaceGDI::BrushColour(ColourDesired back) noexcept { +void SurfaceGDI::PenColour(ColourAlpha fore, XYPOSITION widthStroke) noexcept { + if (pen) { + ::SelectObject(hdc, penOld); + ::DeleteObject(pen); + pen = {}; + penOld = {}; + } + const DWORD penWidth = std::lround(widthStroke); + const COLORREF penColour = fore.GetColour().AsInteger(); + if (widthStroke > 1) { + const LOGBRUSH brushParameters{ BS_SOLID, penColour, 0 }; + pen = ::ExtCreatePen(PS_GEOMETRIC | PS_ENDCAP_ROUND | PS_JOIN_MITER, + penWidth, + &brushParameters, + 0, + nullptr); + } else { + pen = ::CreatePen(PS_INSIDEFRAME, penWidth, penColour); + } + penOld = SelectPen(hdc, pen); +} + +void SurfaceGDI::BrushColour(ColourAlpha back) noexcept { if (brush) { ::SelectObject(hdc, brushOld); ::DeleteObject(brush); brush = {}; brushOld = {}; } - brush = ::CreateSolidBrush(back.AsInteger()); + brush = ::CreateSolidBrush(back.GetColour().AsInteger()); brushOld = SelectBrush(hdc, brush); } @@ -675,6 +703,14 @@ void SurfaceGDI::Polygon(Point *pts, size_t npts, ColourDesired fore, ColourDesi ::Polygon(hdc, outline.data(), static_cast<int>(npts)); } +void SurfaceGDI::Polygon(const Point *pts, size_t npts, FillStroke fillStroke) { + PenColour(fillStroke.stroke.colour.GetColour(), fillStroke.stroke.width); + BrushColour(fillStroke.fill.colour.GetColour()); + std::vector<POINT> outline; + std::transform(pts, pts + npts, std::back_inserter(outline), POINTFromPoint); + ::Polygon(hdc, outline.data(), static_cast<int>(npts)); +} + void SurfaceGDI::RectangleDraw(PRectangle rc, ColourDesired fore, ColourDesired back) { PenColour(fore); BrushColour(back); @@ -682,6 +718,11 @@ void SurfaceGDI::RectangleDraw(PRectangle rc, ColourDesired fore, ColourDesired ::Rectangle(hdc, rcw.left, rcw.top, rcw.right, rcw.bottom); } +void SurfaceGDI::RectangleDraw(PRectangle rc, FillStroke fillStroke) { + FillRectangle(rc, fillStroke.stroke.colour); + FillRectangle(rc.Inset(fillStroke.stroke.width), fillStroke.fill.colour); +} + void SurfaceGDI::FillRectangle(PRectangle rc, ColourDesired back) { // Using ExtTextOut rather than a FillRect ensures that no dithering occurs. // There is no need to allocate a brush either. @@ -698,9 +739,7 @@ void SurfaceGDI::FillRectangle(PRectangle rc, Fill fill) { ::SetBkColor(hdc, fill.colour.GetColour().AsInteger()); ::ExtTextOut(hdc, rcw.left, rcw.top, ETO_OPAQUE, &rcw, TEXT(""), 0, nullptr); } else { - const ColourDesired fillOpaque = fill.colour.GetColour(); - const int alpha = fill.colour.GetAlpha(); - AlphaRectangle(rc, 0, fillOpaque, alpha, fillOpaque, alpha, 0); + AlphaRectangle(rc, 0, FillStroke(fill.colour)); } } @@ -726,6 +765,16 @@ void SurfaceGDI::RoundedRectangle(PRectangle rc, ColourDesired fore, ColourDesir 8, 8); } +void SurfaceGDI::RoundedRectangle(PRectangle rc, FillStroke fillStroke) { + PenColour(fillStroke.stroke.colour, fillStroke.stroke.width); + BrushColour(fillStroke.fill.colour); + const RECT rcw = RectFromPRectangle(rc); + ::RoundRect(hdc, + rcw.left + 1, rcw.top, + rcw.right - 1, rcw.bottom, + 8, 8); +} + namespace { constexpr DWORD dwordFromBGRA(byte b, byte g, byte r, byte a) noexcept { @@ -1020,6 +1069,13 @@ void SurfaceGDI::Ellipse(PRectangle rc, ColourDesired fore, ColourDesired back) ::Ellipse(hdc, rcw.left, rcw.top, rcw.right, rcw.bottom); } +void SurfaceGDI::Ellipse(PRectangle rc, FillStroke fillStroke) { + PenColour(fillStroke.stroke.colour, fillStroke.stroke.width); + BrushColour(fillStroke.fill.colour); + const RECT rcw = RectFromPRectangle(rc); + ::Ellipse(hdc, rcw.left, rcw.top, rcw.right, rcw.bottom); +} + void SurfaceGDI::Copy(PRectangle rc, Point from, Surface &surfaceSource) { ::BitBlt(hdc, static_cast<int>(rc.left), static_cast<int>(rc.top), @@ -1355,18 +1411,23 @@ public: int DeviceHeightFont(int points) override; void MoveTo(int x_, int y_) override; void LineTo(int x_, int y_) override; + ID2D1PathGeometry *Geometry(const Point *pts, size_t npts, D2D1_FIGURE_BEGIN figureBegin) noexcept; void Polygon(Point *pts, size_t npts, ColourDesired fore, ColourDesired back) override; + void Polygon(const Point *pts, size_t npts, FillStroke fillStroke) override; void RectangleDraw(PRectangle rc, ColourDesired fore, ColourDesired back) override; + void RectangleDraw(PRectangle rc, FillStroke fillStroke) override; void FillRectangle(PRectangle rc, ColourDesired back) override; void FillRectangle(PRectangle rc, Fill fill) override; void FillRectangle(PRectangle rc, Surface &surfacePattern) override; void RoundedRectangle(PRectangle rc, ColourDesired fore, ColourDesired back) override; + void RoundedRectangle(PRectangle rc, FillStroke fillStroke) override; void AlphaRectangle(PRectangle rc, int cornerSize, ColourDesired fill, int alphaFill, ColourDesired outline, int alphaOutline, int flags) override; void AlphaRectangle(PRectangle rc, XYPOSITION cornerSize, FillStroke fillStroke) override; void GradientRectangle(PRectangle rc, const std::vector<ColourStop> &stops, GradientOptions options) override; void DrawRGBAImage(PRectangle rc, int width, int height, const unsigned char *pixelsImage) override; void Ellipse(PRectangle rc, ColourDesired fore, ColourDesired back) override; + void Ellipse(PRectangle rc, FillStroke fillStroke) override; void Copy(PRectangle rc, Point from, Surface &surfaceSource) override; std::unique_ptr<IScreenLineLayout> Layout(const IScreenLine *screenLine) override; @@ -1610,6 +1671,26 @@ void SurfaceD2D::LineTo(int x_, int y_) { } } +ID2D1PathGeometry *SurfaceD2D::Geometry(const Point *pts, size_t npts, D2D1_FIGURE_BEGIN figureBegin) noexcept { + ID2D1PathGeometry *geometry = nullptr; + HRESULT hr = pD2DFactory->CreatePathGeometry(&geometry); + if (SUCCEEDED(hr) && geometry) { + ID2D1GeometrySink *sink = nullptr; + hr = geometry->Open(&sink); + if (SUCCEEDED(hr) && sink) { + sink->BeginFigure(D2D1::Point2F(pts[0].x, pts[0].y), figureBegin); + for (size_t i = 1; i < npts; i++) { + sink->AddLine(D2D1::Point2F(pts[i].x, pts[i].y)); + } + sink->EndFigure((figureBegin == D2D1_FIGURE_BEGIN_FILLED) ? + D2D1_FIGURE_END_CLOSED : D2D1_FIGURE_END_OPEN); + sink->Close(); + ReleaseUnknown(sink); + } + } + return geometry; +} + void SurfaceD2D::Polygon(Point *pts, size_t npts, ColourDesired fore, ColourDesired back) { PLATFORM_ASSERT(pRenderTarget && (npts > 2)); if (pRenderTarget) { @@ -1639,6 +1720,21 @@ void SurfaceD2D::Polygon(Point *pts, size_t npts, ColourDesired fore, ColourDesi } } +void SurfaceD2D::Polygon(const Point *pts, size_t npts, FillStroke fillStroke) { + PLATFORM_ASSERT(pRenderTarget && (npts > 2)); + if (pRenderTarget) { + ID2D1PathGeometry *geometry = Geometry(pts, npts, D2D1_FIGURE_BEGIN_FILLED); + PLATFORM_ASSERT(geometry); + if (geometry) { + D2DPenColourAlpha(fillStroke.fill.colour); + pRenderTarget->FillGeometry(geometry, pBrush); + D2DPenColourAlpha(fillStroke.stroke.colour); + pRenderTarget->DrawGeometry(geometry, pBrush, fillStroke.stroke.width); + ReleaseUnknown(geometry); + } + } +} + void SurfaceD2D::RectangleDraw(PRectangle rc, ColourDesired fore, ColourDesired back) { if (pRenderTarget) { const D2D1_RECT_F rectangle1 = D2D1::RectF(std::round(rc.left) + 0.5f, rc.top+0.5f, std::round(rc.right) - 0.5f, rc.bottom-0.5f); @@ -1649,6 +1745,20 @@ void SurfaceD2D::RectangleDraw(PRectangle rc, ColourDesired fore, ColourDesired } } +void SurfaceD2D::RectangleDraw(PRectangle rc, FillStroke fillStroke) { + if (!pRenderTarget) + return; + const D2D1_RECT_F rect = RectangleFromPRectangle(rc); + const D2D1_RECT_F rectFill = RectangleInset(rect, fillStroke.stroke.width); + const float halfStroke = fillStroke.stroke.width / 2.0f; + const D2D1_RECT_F rectOutline = RectangleInset(rect, halfStroke); + + D2DPenColourAlpha(fillStroke.fill.colour); + pRenderTarget->FillRectangle(&rectFill, pBrush); + D2DPenColourAlpha(fillStroke.stroke.colour); + pRenderTarget->DrawRectangle(&rectOutline, pBrush, fillStroke.stroke.width); +} + void SurfaceD2D::FillRectangle(PRectangle rc, ColourDesired back) { if (pRenderTarget) { D2DPenColour(back); @@ -1703,6 +1813,22 @@ void SurfaceD2D::RoundedRectangle(PRectangle rc, ColourDesired fore, ColourDesir } } +void SurfaceD2D::RoundedRectangle(PRectangle rc, FillStroke fillStroke) { + if (pRenderTarget) { + D2D1_ROUNDED_RECT roundedRectFill = { + D2D1::RectF(rc.left+1.0f, rc.top+1.0f, rc.right-1.0f, rc.bottom-1.0f), + 4, 4}; + D2DPenColourAlpha(fillStroke.fill.colour); + pRenderTarget->FillRoundedRectangle(roundedRectFill, pBrush); + + D2D1_ROUNDED_RECT roundedRect = { + D2D1::RectF(rc.left + 0.5f, rc.top+0.5f, rc.right - 0.5f, rc.bottom-0.5f), + 4, 4}; + D2DPenColourAlpha(fillStroke.stroke.colour); + pRenderTarget->DrawRoundedRectangle(roundedRect, pBrush, fillStroke.stroke.width); + } +} + void SurfaceD2D::AlphaRectangle(PRectangle rc, int cornerSize, ColourDesired fill, int alphaFill, ColourDesired outline, int alphaOutline, int /* flags*/ ) { if (pRenderTarget) { @@ -1837,6 +1963,24 @@ void SurfaceD2D::Ellipse(PRectangle rc, ColourDesired fore, ColourDesired back) } } +void SurfaceD2D::Ellipse(PRectangle rc, FillStroke fillStroke) { + if (!pRenderTarget) + return; + const D2D1_POINT_2F centre = D2D1::Point2F((rc.left + rc.right) / 2.0f, (rc.top + rc.bottom) / 2.0f); + + const FLOAT radiusFill = rc.Width() / 2.0f - fillStroke.stroke.width; + const D2D1_ELLIPSE ellipseFill = { centre, radiusFill, radiusFill }; + + D2DPenColourAlpha(fillStroke.fill.colour); + pRenderTarget->FillEllipse(ellipseFill, pBrush); + + const FLOAT radiusOutline = rc.Width() / 2.0f - fillStroke.stroke.width / 2.0f; + const D2D1_ELLIPSE ellipseOutline = { centre, radiusOutline, radiusOutline }; + + D2DPenColourAlpha(fillStroke.stroke.colour); + pRenderTarget->DrawEllipse(ellipseOutline, pBrush, fillStroke.stroke.width); +} + void SurfaceD2D::Copy(PRectangle rc, Point from, Surface &surfaceSource) { SurfaceD2D &surfOther = dynamic_cast<SurfaceD2D &>(surfaceSource); ID2D1Bitmap *pBitmap = nullptr; |