[svg] Implement distant light sources for specular lighting

https://www.w3.org/TR/SVG11/filters.html#feDistantLightElement
(specular lighting only in this CL)

Tests filters-specular-01-f and filters-light-02-f should be passing
now. Note that our filters-light-02 result matches the behavior of
Chrome and Firefox, but is different than the official W3C png. We
currently think the test is somewhat poorly written, so we are
comfortable matching the output of Chrome and Firefox.

Bug: skia:10841
Change-Id: Id9517c8594a3826e5d98685d1e7d1d1b3d3cdf51
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/402582
Reviewed-by: Florin Malita <fmalita@chromium.org>
Commit-Queue: Tyler Denniston <tdenniston@google.com>
This commit is contained in:
Tyler Denniston 2021-04-29 11:20:06 -04:00 committed by Skia Commit-Bot
parent 0d174586c4
commit f208f81e1d
2 changed files with 41 additions and 2 deletions

View File

@ -11,6 +11,7 @@
#include "modules/svg/include/SkSVGFe.h"
#include "modules/svg/include/SkSVGTypes.h"
class SkSVGFeDistantLight;
class SkSVGFePointLight;
class SkSVGFeLighting : public SkSVGFe {
@ -33,6 +34,10 @@ protected:
sk_sp<SkImageFilter> onMakeImageFilter(const SkSVGRenderContext&,
const SkSVGFilterContext&) const final;
virtual sk_sp<SkImageFilter> makeDistantLight(const SkSVGRenderContext&,
const SkSVGFilterContext&,
const SkSVGFeDistantLight*) const = 0;
virtual sk_sp<SkImageFilter> makePointLight(const SkSVGRenderContext&,
const SkSVGFilterContext&,
const SkSVGFePointLight*) const = 0;
@ -61,6 +66,10 @@ public:
protected:
bool parseAndSetAttribute(const char*, const char*) override;
sk_sp<SkImageFilter> makeDistantLight(const SkSVGRenderContext&,
const SkSVGFilterContext&,
const SkSVGFeDistantLight*) const final;
sk_sp<SkImageFilter> makePointLight(const SkSVGRenderContext&,
const SkSVGFilterContext&,
const SkSVGFePointLight*) const final;

View File

@ -39,9 +39,12 @@ sk_sp<SkImageFilter> SkSVGFeLighting::onMakeImageFilter(const SkSVGRenderContext
const SkSVGFilterContext& fctx) const {
for (const auto& child : fChildren) {
switch (child->tag()) {
case SkSVGTag::kFeDistantLight:
return this->makeDistantLight(
ctx, fctx, static_cast<const SkSVGFeDistantLight*>(child.get()));
case SkSVGTag::kFePointLight:
return this->makePointLight(ctx, fctx,
static_cast<const SkSVGFePointLight*>(child.get()));
return this->makePointLight(
ctx, fctx, static_cast<const SkSVGFePointLight*>(child.get()));
default:
// Ignore unknown children, such as <desc> elements
break;
@ -88,6 +91,33 @@ bool SkSVGFeSpecularLighting::parseAndSetAttribute(const char* n, const char* v)
SkSVGAttributeParser::parse<SkSVGNumberType>("specularExponent", n, v));
}
sk_sp<SkImageFilter> SkSVGFeSpecularLighting::makeDistantLight(
const SkSVGRenderContext& ctx,
const SkSVGFilterContext& fctx,
const SkSVGFeDistantLight* light) const {
const auto computeDirection = [](float azimuth, float elevation) -> SkPoint3 {
// Computing direction from azimuth+elevation is two 3D rotations:
// - Rotate [1,0,0] about y axis first (elevation)
// - Rotate result about z axis (azimuth)
// Which is just the first column vector in the 3x3 matrix Rz*Ry.
const float azimuthRad = SkDegreesToRadians(azimuth);
const float elevationRad = SkDegreesToRadians(elevation);
const float sinAzimuth = sinf(azimuthRad), cosAzimuth = cosf(azimuthRad);
const float sinElevation = sinf(elevationRad), cosElevation = cosf(elevationRad);
return SkPoint3::Make(cosAzimuth * cosElevation, sinAzimuth * cosElevation, sinElevation);
};
const SkPoint3 dir = computeDirection(light->getAzimuth(), light->getElevation());
return SkImageFilters::DistantLitSpecular(
this->resolveXYZ(ctx, fctx, dir.fX, dir.fY, dir.fZ),
this->resolveLightingColor(ctx),
this->getSurfaceScale(),
fSpecularConstant,
fSpecularExponent,
fctx.resolveInput(ctx, this->getIn(), this->resolveColorspace(ctx, fctx)),
this->resolveFilterSubregion(ctx, fctx));
}
sk_sp<SkImageFilter> SkSVGFeSpecularLighting::makePointLight(const SkSVGRenderContext& ctx,
const SkSVGFilterContext& fctx,
const SkSVGFePointLight* light) const {