1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "ThemeDrawing.h"
9 namespace mozilla::widget
{
12 void ThemeDrawing::FillRect(DrawTarget
& aDt
, const LayoutDeviceRect
& aRect
,
13 const sRGBColor
& aColor
) {
14 aDt
.FillRect(aRect
.ToUnknownRect(), gfx::ColorPattern(ToDeviceColor(aColor
)));
18 void ThemeDrawing::FillRect(WebRenderBackendData
& aWrData
,
19 const LayoutDeviceRect
& aRect
,
20 const sRGBColor
& aColor
) {
21 const bool kBackfaceIsVisible
= true;
22 auto dest
= wr::ToLayoutRect(aRect
);
23 aWrData
.mBuilder
.PushRect(dest
, dest
, kBackfaceIsVisible
, false, false,
24 wr::ToColorF(ToDeviceColor(aColor
)));
28 LayoutDeviceIntCoord
ThemeDrawing::SnapBorderWidth(const CSSCoord
& aCssWidth
,
29 const DPIRatio
& aDpiRatio
) {
30 if (aCssWidth
== 0.0f
) {
33 return std::max(LayoutDeviceIntCoord(1), (aCssWidth
* aDpiRatio
).Truncated());
37 void ThemeDrawing::PaintArrow(DrawTarget
& aDrawTarget
,
38 const LayoutDeviceRect
& aRect
,
39 const float aArrowPolygonX
[],
40 const float aArrowPolygonY
[],
41 const float aArrowPolygonSize
,
42 const int32_t aArrowNumPoints
,
43 const sRGBColor aFillColor
) {
44 const float scale
= ScaleToFillRect(aRect
, aArrowPolygonSize
);
46 auto center
= aRect
.Center().ToUnknownPoint();
48 RefPtr
<gfx::PathBuilder
> builder
= aDrawTarget
.CreatePathBuilder();
50 center
+ gfx::Point(aArrowPolygonX
[0] * scale
, aArrowPolygonY
[0] * scale
);
52 for (int32_t i
= 1; i
< aArrowNumPoints
; i
++) {
54 gfx::Point(aArrowPolygonX
[i
] * scale
, aArrowPolygonY
[i
] * scale
);
57 RefPtr
<gfx::Path
> path
= builder
->Finish();
59 aDrawTarget
.Fill(path
, gfx::ColorPattern(ToDeviceColor(aFillColor
)));
62 void ThemeDrawing::PaintRoundedRectWithRadius(
63 WebRenderBackendData
& aWrData
, const LayoutDeviceRect
& aRect
,
64 const LayoutDeviceRect
& aClipRect
, const sRGBColor
& aBackgroundColor
,
65 const sRGBColor
& aBorderColor
, const CSSCoord
& aBorderWidth
,
66 const CSSCoord
& aRadius
, const DPIRatio
& aDpiRatio
) {
67 const bool kBackfaceIsVisible
= true;
68 const LayoutDeviceCoord
borderWidth(SnapBorderWidth(aBorderWidth
, aDpiRatio
));
69 const LayoutDeviceCoord
radius(aRadius
* aDpiRatio
);
70 const wr::LayoutRect dest
= wr::ToLayoutRect(aRect
);
71 const wr::LayoutRect clip
= wr::ToLayoutRect(aClipRect
);
73 // Push the background.
74 if (aBackgroundColor
.a
!= 0.0f
) {
75 auto backgroundColor
= wr::ToColorF(ToDeviceColor(aBackgroundColor
));
76 wr::LayoutRect backgroundRect
= [&] {
77 LayoutDeviceRect bg
= aRect
;
78 bg
.Deflate(borderWidth
);
79 return wr::ToLayoutRect(bg
);
82 aWrData
.mBuilder
.PushRect(backgroundRect
, clip
, kBackfaceIsVisible
, false,
83 false, backgroundColor
);
85 // NOTE(emilio): This follows DisplayListBuilder::PushRoundedRect and
86 // draws the rounded fill as an extra thick rounded border instead of a
87 // rectangle that's clipped to a rounded clip. Refer to that method for a
88 // justification. See bug 1694269.
89 LayoutDeviceCoord backgroundRadius
=
90 std::max(0.0f
, float(radius
) - float(borderWidth
));
91 wr::BorderSide side
= {backgroundColor
, wr::BorderStyle::Solid
};
92 const wr::BorderSide sides
[4] = {side
, side
, side
, side
};
93 float h
= backgroundRect
.width() * 0.6f
;
94 float v
= backgroundRect
.height() * 0.6f
;
95 wr::LayoutSideOffsets widths
= {v
, h
, v
, h
};
96 wr::BorderRadius radii
= {{backgroundRadius
, backgroundRadius
},
97 {backgroundRadius
, backgroundRadius
},
98 {backgroundRadius
, backgroundRadius
},
99 {backgroundRadius
, backgroundRadius
}};
100 aWrData
.mBuilder
.PushBorder(backgroundRect
, clip
, kBackfaceIsVisible
,
101 widths
, {sides
, 4}, radii
);
105 if (borderWidth
!= 0.0f
&& aBorderColor
.a
!= 0.0f
) {
107 const auto borderColor
= ToDeviceColor(aBorderColor
);
108 const auto side
= wr::ToBorderSide(borderColor
, StyleBorderStyle::Solid
);
109 const wr::BorderSide sides
[4] = {side
, side
, side
, side
};
110 const LayoutDeviceSize
sideRadius(radius
, radius
);
112 wr::ToBorderWidths(borderWidth
, borderWidth
, borderWidth
, borderWidth
);
113 const auto wrRadius
=
114 wr::ToBorderRadius(sideRadius
, sideRadius
, sideRadius
, sideRadius
);
115 aWrData
.mBuilder
.PushBorder(dest
, clip
, kBackfaceIsVisible
, widths
,
116 {sides
, 4}, wrRadius
);
120 void ThemeDrawing::PaintRoundedRectWithRadius(
121 DrawTarget
& aDrawTarget
, const LayoutDeviceRect
& aRect
,
122 const LayoutDeviceRect
& aClipRect
, const sRGBColor
& aBackgroundColor
,
123 const sRGBColor
& aBorderColor
, const CSSCoord
& aBorderWidth
,
124 const CSSCoord
& aRadius
, const DPIRatio
& aDpiRatio
) {
125 const LayoutDeviceCoord
borderWidth(SnapBorderWidth(aBorderWidth
, aDpiRatio
));
126 const bool needsClip
= !(aRect
== aClipRect
);
128 aDrawTarget
.PushClipRect(aClipRect
.ToUnknownRect());
131 LayoutDeviceRect
rect(aRect
);
132 // Deflate the rect by half the border width, so that the middle of the
133 // stroke fills exactly the area we want to fill and not more.
134 rect
.Deflate(borderWidth
* 0.5f
);
136 LayoutDeviceCoord
radius(aRadius
* aDpiRatio
- borderWidth
* 0.5f
);
137 // Fix up the radius if it's too large with the rect we're going to paint.
139 LayoutDeviceCoord min
= std::min(rect
.width
, rect
.height
);
140 if (radius
* 2.0f
> min
) {
145 Maybe
<gfx::ColorPattern
> backgroundPattern
;
146 if (aBackgroundColor
.a
!= 0.0f
) {
147 backgroundPattern
.emplace(ToDeviceColor(aBackgroundColor
));
149 Maybe
<gfx::ColorPattern
> borderPattern
;
150 if (borderWidth
!= 0.0f
&& aBorderColor
.a
!= 0.0f
) {
151 borderPattern
.emplace(ToDeviceColor(aBorderColor
));
154 if (borderPattern
|| backgroundPattern
) {
155 if (radius
!= 0.0f
) {
156 gfx::RectCornerRadii
radii(radius
, radius
, radius
, radius
);
157 RefPtr
<gfx::Path
> roundedRect
=
158 MakePathForRoundedRect(aDrawTarget
, rect
.ToUnknownRect(), radii
);
160 if (backgroundPattern
) {
161 aDrawTarget
.Fill(roundedRect
, *backgroundPattern
);
164 aDrawTarget
.Stroke(roundedRect
, *borderPattern
,
165 gfx::StrokeOptions(borderWidth
));
168 if (backgroundPattern
) {
169 aDrawTarget
.FillRect(rect
.ToUnknownRect(), *backgroundPattern
);
172 aDrawTarget
.StrokeRect(rect
.ToUnknownRect(), *borderPattern
,
173 gfx::StrokeOptions(borderWidth
));
179 aDrawTarget
.PopClip();
183 } // namespace mozilla::widget