Skip to content

Commit

Permalink
[Skia] Implement inset shadows
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=269714

Reviewed by Carlos Garcia Campos.

Move the drop shadow filter creation into a helper method, and refactor
all callers of GraphicsContextSkia::createFillPaint() to manually create
and apply a drop shadow. The helper method returns nullptr if there is
no shadow, or if the shadow is effectively hidden, following Cairo's
optimization.

Add a selector enum for inset and outset shadows, used by the helper
method aforementioned, and handle the case for both inset and outset
shadows. The outset shadow case is just what was there before.

The inset shadow case is a combination of two image filters. One creates
a black shadow with the drop shadow parameters, and uses it as a mask to
apply a color blend. The color blend is what uses the actual drop shadow
color, and blends it with the SrcIn mode, which effectively inverts the
drop shadow based on the pixel opacity.

The only place in code that interprets GraphicsContext.dropShadow() as
an inset shadow is inside GraphicsContext::fillRectWithRoundedHole(),
which is implemented in this commit using SkCanvas::drawDRRect().

* Source/WebCore/platform/graphics/skia/FontCascadeSkia.cpp:
(WebCore::FontCascade::drawGlyphs):
* Source/WebCore/platform/graphics/skia/GraphicsContextSkia.cpp:
(WebCore::GraphicsContextSkia::drawNativeImageInternal):
(WebCore::GraphicsContextSkia::fillPath):
(WebCore::GraphicsContextSkia::createDropShadowFilter const):
(WebCore::GraphicsContextSkia::createFillPaint const):
(WebCore::GraphicsContextSkia::fillRect):
(WebCore::GraphicsContextSkia::fillRoundedRectImpl):
(WebCore::GraphicsContextSkia::fillRectWithRoundedHole):
* Source/WebCore/platform/graphics/skia/GraphicsContextSkia.h:

Canonical link: https://commits.webkit.org/275093@main
  • Loading branch information
GeorgesStavracas authored and carlosgcampos committed Feb 21, 2024
1 parent 8b4eddd commit bcffee0
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 23 deletions.
4 changes: 3 additions & 1 deletion Source/WebCore/platform/graphics/skia/FontCascadeSkia.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ void FontCascade::drawGlyphs(GraphicsContext& graphicsContext, const Font& font,
}
auto blob = builder.make();
auto* canvas = graphicsContext.platformContext();
canvas->drawTextBlob(blob, SkFloatToScalar(position.x()), SkFloatToScalar(position.y()), static_cast<GraphicsContextSkia*>(&graphicsContext)->createFillPaint());
SkPaint paint = static_cast<GraphicsContextSkia*>(&graphicsContext)->createFillPaint();
paint.setImageFilter(static_cast<GraphicsContextSkia*>(&graphicsContext)->createDropShadowFilterIfNeeded(GraphicsContextSkia::ShadowStyle::Outset));
canvas->drawTextBlob(blob, SkFloatToScalar(position.x()), SkFloatToScalar(position.y()), paint);
}

bool FontCascade::canReturnFallbackFontsForComplexText()
Expand Down
89 changes: 67 additions & 22 deletions Source/WebCore/platform/graphics/skia/GraphicsContextSkia.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include "ImageBuffer.h"
#include "IntRect.h"
#include "NotImplemented.h"
#include <skia/core/SkColorFilter.h>
#include <skia/core/SkImage.h>
#include <skia/core/SkPath.h>
#include <skia/core/SkPathEffect.h>
Expand Down Expand Up @@ -230,6 +231,7 @@ void GraphicsContextSkia::drawNativeImageInternal(NativeImage& nativeImage, cons
auto normalizedDestRect = normalizeRect(destRect);
SkPaint paint = createFillPaint();
paint.setBlendMode(toSkiaBlendMode(options.compositeOperator(), options.blendMode()));
paint.setImageFilter(createDropShadowFilterIfNeeded(ShadowStyle::Outset));
canvas().drawImageRect(image, normalizedSrcRect, normalizedDestRect, toSkSamplingOptions(m_state.imageInterpolationQuality()), &paint, { });
}

Expand Down Expand Up @@ -264,16 +266,19 @@ void GraphicsContextSkia::fillPath(const Path& path)
if (path.isEmpty())
return;

SkPaint paint = createFillPaint();
paint.setImageFilter(createDropShadowFilterIfNeeded(ShadowStyle::Outset));

auto fillRule = toSkiaFillType(state().fillRule());
auto& skiaPath= *path.platformPath();
if (skiaPath.getFillType() == fillRule) {
canvas().drawPath(skiaPath, createFillPaint());
canvas().drawPath(skiaPath, paint);
return;
}

auto skiaPathCopy = skiaPath;
skiaPathCopy.setFillType(fillRule);
canvas().drawPath(skiaPathCopy, createFillPaint());
canvas().drawPath(skiaPathCopy, paint);
}

void GraphicsContextSkia::strokePath(const Path& path)
Expand All @@ -284,6 +289,42 @@ void GraphicsContextSkia::strokePath(const Path& path)
canvas().drawPath(*path.platformPath(), createStrokePaint());
}

sk_sp<SkImageFilter> GraphicsContextSkia::createDropShadowFilterIfNeeded(ShadowStyle shadowStyle) const
{
// FIXME: this does not handle state.shadowsIgnoreTransforms()

if (!hasDropShadow())
return nullptr;

const auto& shadow = dropShadow();
ASSERT(shadow);

const FloatSize& offset = shadow->offset;
auto shadowColor = shadow->color;

if (!shadowColor.isVisible() || (!offset.width() && !offset.height() && !shadow->radius))
return nullptr;

const auto& state = this->state();

const auto sigma = shadow->radius / 2.0;
auto globalAlpha = state.alpha();
if (globalAlpha < 1)
shadowColor = shadowColor.colorWithAlphaMultipliedBy(globalAlpha);
auto [r, g, b, a] = shadowColor.toColorTypeLossy<SRGBA<uint8_t>>().resolved();

switch (shadowStyle) {
case ShadowStyle::Outset:
return SkImageFilters::DropShadow(offset.width(), offset.height(), sigma, sigma, SkColorSetARGB(a, r, g, b), nullptr);
case ShadowStyle::Inset: {
auto dropShadow = SkImageFilters::DropShadowOnly(offset.width(), offset.height(), sigma, sigma, SK_ColorBLACK, nullptr);
return SkImageFilters::ColorFilter(SkColorFilters::Blend(SkColorSetARGB(a, r, g, b), SkBlendMode::kSrcIn), dropShadow);
}
}

return nullptr;
}

SkPaint GraphicsContextSkia::createFillPaint(std::optional<Color> fillColor) const
{
const auto& state = this->state();
Expand All @@ -306,21 +347,6 @@ SkPaint GraphicsContextSkia::createFillPaint(std::optional<Color> fillColor) con
paint.setColor(SkColorSetARGB(a, r, g, b));
}

// Outset shadow
// FIXME: Don't add the effect if the shadow is inset
if (hasDropShadow()) {
const auto shadow = dropShadow();
ASSERT(shadow);

const auto sigma = shadow->radius / 2.0;
auto globalAlpha = state.alpha();
auto shadowColor = shadow->color;
if (globalAlpha < 1)
shadowColor = shadowColor.colorWithAlphaMultipliedBy(globalAlpha);
auto [r, g, b, a] = shadowColor.toColorTypeLossy<SRGBA<uint8_t>>().resolved();
paint.setImageFilter(SkImageFilters::DropShadow(shadow->offset.width(), shadow->offset.height(), sigma, sigma, SkColorSetARGB(a, r, g, b), nullptr));
}

return paint;
}

Expand Down Expand Up @@ -357,12 +383,16 @@ SkPaint GraphicsContextSkia::createStrokePaint(std::optional<Color> strokeColor)

void GraphicsContextSkia::fillRect(const FloatRect& boundaries)
{
canvas().drawRect(boundaries, createFillPaint());
SkPaint paint = createFillPaint();
paint.setImageFilter(createDropShadowFilterIfNeeded(ShadowStyle::Outset));
canvas().drawRect(boundaries, paint);
}

void GraphicsContextSkia::fillRect(const FloatRect& boundaries, const Color& fillColor)
{
canvas().drawRect(boundaries, createFillPaint(fillColor));
SkPaint paint = createFillPaint(fillColor);
paint.setImageFilter(createDropShadowFilterIfNeeded(ShadowStyle::Outset));
canvas().drawRect(boundaries, paint);
}

void GraphicsContextSkia::fillRect(const FloatRect&, Gradient&, const AffineTransform&)
Expand Down Expand Up @@ -620,14 +650,29 @@ void GraphicsContextSkia::fillRoundedRectImpl(const FloatRoundedRect& rect, cons
{ SkFloatToScalar(radii.bottomLeft().width()), SkFloatToScalar(radii.bottomLeft().height()) },
{ SkFloatToScalar(radii.bottomRight().width()), SkFloatToScalar(radii.bottomRight().height()) } };
skRect.setRectRadii(rect.rect(), skRadii);
canvas().drawRRect(skRect, createFillPaint(color));

SkPaint paint = createFillPaint(color);
paint.setImageFilter(createDropShadowFilterIfNeeded(ShadowStyle::Outset));
canvas().drawRRect(skRect, paint);
}

void GraphicsContextSkia::fillRectWithRoundedHole(const FloatRect&, const FloatRoundedRect&, const Color& color)
void GraphicsContextSkia::fillRectWithRoundedHole(const FloatRect& outerRect, const FloatRoundedRect& innerRRect, const Color& color)
{
if (!color.isValid())
return;
notImplemented();

auto innerSkRect = SkRRect::MakeEmpty();
const auto& radii = innerRRect.radii();
SkVector skRadii[4] = {
{ SkFloatToScalar(radii.topLeft().width()), SkFloatToScalar(radii.topLeft().height()) },
{ SkFloatToScalar(radii.topRight().width()), SkFloatToScalar(radii.topRight().height()) },
{ SkFloatToScalar(radii.bottomLeft().width()), SkFloatToScalar(radii.bottomLeft().height()) },
{ SkFloatToScalar(radii.bottomRight().width()), SkFloatToScalar(radii.bottomRight().height()) } };
innerSkRect.setRectRadii(innerRRect.rect(), skRadii);

SkPaint paint = createFillPaint(color);
paint.setImageFilter(createDropShadowFilterIfNeeded(ShadowStyle::Inset));
canvas().drawDRRect(SkRRect::MakeRect(outerRect), innerSkRect, paint);
}

void GraphicsContextSkia::drawPattern(NativeImage& nativeImage, const FloatRect& destRect, const FloatRect& tileRect, const AffineTransform& patternTransform, const FloatPoint& phase, const FloatSize& spacing, ImagePaintingOptions options)
Expand Down
3 changes: 3 additions & 0 deletions Source/WebCore/platform/graphics/skia/GraphicsContextSkia.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@ class WEBCORE_EXPORT GraphicsContextSkia final : public GraphicsContext {

RenderingMode renderingMode() const final;

enum class ShadowStyle : uint8_t { Outset, Inset };
sk_sp<SkImageFilter> createDropShadowFilterIfNeeded(ShadowStyle) const;

SkPaint createFillPaint(std::optional<Color> fillColor = std::nullopt) const;
SkPaint createStrokeStylePaint() const;
SkPaint createStrokePaint(std::optional<Color> strokeColor = std::nullopt) const;
Expand Down

0 comments on commit bcffee0

Please sign in to comment.