diff options
Diffstat (limited to 'widget/ThemeDrawing.cpp')
-rw-r--r-- | widget/ThemeDrawing.cpp | 183 |
1 files changed, 183 insertions, 0 deletions
diff --git a/widget/ThemeDrawing.cpp b/widget/ThemeDrawing.cpp new file mode 100644 index 0000000000..93e317a41b --- /dev/null +++ b/widget/ThemeDrawing.cpp @@ -0,0 +1,183 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ThemeDrawing.h" + +namespace mozilla::widget { + +/*static*/ +void ThemeDrawing::FillRect(DrawTarget& aDt, const LayoutDeviceRect& aRect, + const sRGBColor& aColor) { + aDt.FillRect(aRect.ToUnknownRect(), gfx::ColorPattern(ToDeviceColor(aColor))); +} + +/*static*/ +void ThemeDrawing::FillRect(WebRenderBackendData& aWrData, + const LayoutDeviceRect& aRect, + const sRGBColor& aColor) { + const bool kBackfaceIsVisible = true; + auto dest = wr::ToLayoutRect(aRect); + aWrData.mBuilder.PushRect(dest, dest, kBackfaceIsVisible, false, false, + wr::ToColorF(ToDeviceColor(aColor))); +} + +/*static*/ +LayoutDeviceIntCoord ThemeDrawing::SnapBorderWidth(const CSSCoord& aCssWidth, + const DPIRatio& aDpiRatio) { + if (aCssWidth == 0.0f) { + return 0; + } + return std::max(LayoutDeviceIntCoord(1), (aCssWidth * aDpiRatio).Truncated()); +} + +/*static*/ +void ThemeDrawing::PaintArrow(DrawTarget& aDrawTarget, + const LayoutDeviceRect& aRect, + const float aArrowPolygonX[], + const float aArrowPolygonY[], + const float aArrowPolygonSize, + const int32_t aArrowNumPoints, + const sRGBColor aFillColor) { + const float scale = ScaleToFillRect(aRect, aArrowPolygonSize); + + auto center = aRect.Center().ToUnknownPoint(); + + RefPtr<gfx::PathBuilder> builder = aDrawTarget.CreatePathBuilder(); + gfx::Point p = + center + gfx::Point(aArrowPolygonX[0] * scale, aArrowPolygonY[0] * scale); + builder->MoveTo(p); + for (int32_t i = 1; i < aArrowNumPoints; i++) { + p = center + + gfx::Point(aArrowPolygonX[i] * scale, aArrowPolygonY[i] * scale); + builder->LineTo(p); + } + RefPtr<gfx::Path> path = builder->Finish(); + + aDrawTarget.Fill(path, gfx::ColorPattern(ToDeviceColor(aFillColor))); +} + +void ThemeDrawing::PaintRoundedRectWithRadius( + WebRenderBackendData& aWrData, const LayoutDeviceRect& aRect, + const LayoutDeviceRect& aClipRect, const sRGBColor& aBackgroundColor, + const sRGBColor& aBorderColor, const CSSCoord& aBorderWidth, + const CSSCoord& aRadius, const DPIRatio& aDpiRatio) { + const bool kBackfaceIsVisible = true; + const LayoutDeviceCoord borderWidth(SnapBorderWidth(aBorderWidth, aDpiRatio)); + const LayoutDeviceCoord radius(aRadius * aDpiRatio); + const wr::LayoutRect dest = wr::ToLayoutRect(aRect); + const wr::LayoutRect clip = wr::ToLayoutRect(aClipRect); + + // Push the background. + if (aBackgroundColor.a != 0.0f) { + auto backgroundColor = wr::ToColorF(ToDeviceColor(aBackgroundColor)); + wr::LayoutRect backgroundRect = [&] { + LayoutDeviceRect bg = aRect; + bg.Deflate(borderWidth); + return wr::ToLayoutRect(bg); + }(); + if (radius == 0.0f) { + aWrData.mBuilder.PushRect(backgroundRect, clip, kBackfaceIsVisible, false, + false, backgroundColor); + } else { + // NOTE(emilio): This follows DisplayListBuilder::PushRoundedRect and + // draws the rounded fill as an extra thick rounded border instead of a + // rectangle that's clipped to a rounded clip. Refer to that method for a + // justification. See bug 1694269. + LayoutDeviceCoord backgroundRadius = + std::max(0.0f, float(radius) - float(borderWidth)); + wr::BorderSide side = {backgroundColor, wr::BorderStyle::Solid}; + const wr::BorderSide sides[4] = {side, side, side, side}; + float h = backgroundRect.width() * 0.6f; + float v = backgroundRect.height() * 0.6f; + wr::LayoutSideOffsets widths = {v, h, v, h}; + wr::BorderRadius radii = {{backgroundRadius, backgroundRadius}, + {backgroundRadius, backgroundRadius}, + {backgroundRadius, backgroundRadius}, + {backgroundRadius, backgroundRadius}}; + aWrData.mBuilder.PushBorder(backgroundRect, clip, kBackfaceIsVisible, + widths, {sides, 4}, radii); + } + } + + if (borderWidth != 0.0f && aBorderColor.a != 0.0f) { + // Push the border. + const auto borderColor = ToDeviceColor(aBorderColor); + const auto side = wr::ToBorderSide(borderColor, StyleBorderStyle::Solid); + const wr::BorderSide sides[4] = {side, side, side, side}; + const LayoutDeviceSize sideRadius(radius, radius); + const auto widths = + wr::ToBorderWidths(borderWidth, borderWidth, borderWidth, borderWidth); + const auto wrRadius = + wr::ToBorderRadius(sideRadius, sideRadius, sideRadius, sideRadius); + aWrData.mBuilder.PushBorder(dest, clip, kBackfaceIsVisible, widths, + {sides, 4}, wrRadius); + } +} + +void ThemeDrawing::PaintRoundedRectWithRadius( + DrawTarget& aDrawTarget, const LayoutDeviceRect& aRect, + const LayoutDeviceRect& aClipRect, const sRGBColor& aBackgroundColor, + const sRGBColor& aBorderColor, const CSSCoord& aBorderWidth, + const CSSCoord& aRadius, const DPIRatio& aDpiRatio) { + const LayoutDeviceCoord borderWidth(SnapBorderWidth(aBorderWidth, aDpiRatio)); + const bool needsClip = !(aRect == aClipRect); + if (needsClip) { + aDrawTarget.PushClipRect(aClipRect.ToUnknownRect()); + } + + LayoutDeviceRect rect(aRect); + // Deflate the rect by half the border width, so that the middle of the + // stroke fills exactly the area we want to fill and not more. + rect.Deflate(borderWidth * 0.5f); + + LayoutDeviceCoord radius(aRadius * aDpiRatio - borderWidth * 0.5f); + // Fix up the radius if it's too large with the rect we're going to paint. + { + LayoutDeviceCoord min = std::min(rect.width, rect.height); + if (radius * 2.0f > min) { + radius = min * 0.5f; + } + } + + Maybe<gfx::ColorPattern> backgroundPattern; + if (aBackgroundColor.a != 0.0f) { + backgroundPattern.emplace(ToDeviceColor(aBackgroundColor)); + } + Maybe<gfx::ColorPattern> borderPattern; + if (borderWidth != 0.0f && aBorderColor.a != 0.0f) { + borderPattern.emplace(ToDeviceColor(aBorderColor)); + } + + if (borderPattern || backgroundPattern) { + if (radius != 0.0f) { + gfx::RectCornerRadii radii(radius, radius, radius, radius); + RefPtr<gfx::Path> roundedRect = + MakePathForRoundedRect(aDrawTarget, rect.ToUnknownRect(), radii); + + if (backgroundPattern) { + aDrawTarget.Fill(roundedRect, *backgroundPattern); + } + if (borderPattern) { + aDrawTarget.Stroke(roundedRect, *borderPattern, + gfx::StrokeOptions(borderWidth)); + } + } else { + if (backgroundPattern) { + aDrawTarget.FillRect(rect.ToUnknownRect(), *backgroundPattern); + } + if (borderPattern) { + aDrawTarget.StrokeRect(rect.ToUnknownRect(), *borderPattern, + gfx::StrokeOptions(borderWidth)); + } + } + } + + if (needsClip) { + aDrawTarget.PopClip(); + } +} + +} // namespace mozilla::widget |