mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-30 16:22:00 +00:00
cebe610723
Although not needed right now (checkerboarding backgrounds get a slice anyway due to being a different scroll root), this will be important for the upcoming work to make backdrop filter roots implicit. This allows WR to know when slicing up a content slice if the prim is relevant to the backdrop root. Differential Revision: https://phabricator.services.mozilla.com/D146145
184 lines
7.2 KiB
C++
184 lines
7.2 KiB
C++
/* -*- 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
|