| /* |
| * Copyright 2020 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "include/core/SkPoint3.h" |
| #include "include/effects/SkImageFilters.h" |
| #include "modules/svg/include/SkSVGAttributeParser.h" |
| #include "modules/svg/include/SkSVGFeLightSource.h" |
| #include "modules/svg/include/SkSVGFeLighting.h" |
| #include "modules/svg/include/SkSVGFilterContext.h" |
| #include "modules/svg/include/SkSVGRenderContext.h" |
| #include "modules/svg/include/SkSVGValue.h" |
| |
| bool SkSVGFeLighting::parseAndSetAttribute(const char* n, const char* v) { |
| return INHERITED::parseAndSetAttribute(n, v) || |
| this->setSurfaceScale( |
| SkSVGAttributeParser::parse<SkSVGNumberType>("surfaceScale", n, v)) || |
| this->setKernelUnitLength(SkSVGAttributeParser::parse<SkSVGFeLighting::KernelUnitLength>( |
| "kernelUnitLength", n, v)); |
| } |
| |
| template <> |
| bool SkSVGAttributeParser::parse<SkSVGFeLighting::KernelUnitLength>( |
| SkSVGFeLighting::KernelUnitLength* kernelUnitLength) { |
| std::vector<SkSVGNumberType> values; |
| if (!this->parse(&values)) { |
| return false; |
| } |
| |
| kernelUnitLength->fDx = values[0]; |
| kernelUnitLength->fDy = values.size() > 1 ? values[1] : values[0]; |
| return true; |
| } |
| |
| sk_sp<SkImageFilter> SkSVGFeLighting::onMakeImageFilter(const SkSVGRenderContext& ctx, |
| 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())); |
| case SkSVGTag::kFeSpotLight: |
| return this->makeSpotLight( |
| ctx, fctx, static_cast<const SkSVGFeSpotLight*>(child.get())); |
| default: |
| // Ignore unknown children, such as <desc> elements |
| break; |
| } |
| } |
| |
| SkDebugf("lighting filter effect needs exactly one light source\n"); |
| return nullptr; |
| } |
| |
| SkColor SkSVGFeLighting::resolveLightingColor(const SkSVGRenderContext& ctx) const { |
| const auto color = this->getLightingColor(); |
| if (!color.isValue()) { |
| // Uninherited presentation attributes should have a concrete value by now. |
| SkDebugf("unhandled: lighting-color has no value\n"); |
| return SK_ColorWHITE; |
| } |
| |
| return ctx.resolveSvgColor(*color); |
| } |
| |
| SkPoint3 SkSVGFeLighting::resolveXYZ(const SkSVGRenderContext& ctx, |
| const SkSVGFilterContext& fctx, |
| SkSVGNumberType x, |
| SkSVGNumberType y, |
| SkSVGNumberType z) const { |
| const auto obbt = ctx.transformForCurrentOBB(fctx.primitiveUnits()); |
| const auto xy = SkV2{x,y} * obbt.scale + obbt.offset; |
| z = SkSVGLengthContext({obbt.scale.x, obbt.scale.y}) |
| .resolve(SkSVGLength(z * 100.f, SkSVGLength::Unit::kPercentage), |
| SkSVGLengthContext::LengthType::kOther); |
| return SkPoint3::Make(xy.x, xy.y, z); |
| } |
| |
| bool SkSVGFeSpecularLighting::parseAndSetAttribute(const char* n, const char* v) { |
| return INHERITED::parseAndSetAttribute(n, v) || |
| this->setSpecularConstant( |
| SkSVGAttributeParser::parse<SkSVGNumberType>("specularConstant", n, v)) || |
| this->setSpecularExponent( |
| SkSVGAttributeParser::parse<SkSVGNumberType>("specularExponent", n, v)); |
| } |
| |
| sk_sp<SkImageFilter> SkSVGFeSpecularLighting::makeDistantLight( |
| const SkSVGRenderContext& ctx, |
| const SkSVGFilterContext& fctx, |
| const SkSVGFeDistantLight* light) const { |
| const SkPoint3 dir = light->computeDirection(); |
| 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 { |
| return SkImageFilters::PointLitSpecular( |
| this->resolveXYZ(ctx, fctx, light->getX(), light->getY(), light->getZ()), |
| this->resolveLightingColor(ctx), |
| this->getSurfaceScale(), |
| fSpecularConstant, |
| fSpecularExponent, |
| fctx.resolveInput(ctx, this->getIn(), this->resolveColorspace(ctx, fctx)), |
| this->resolveFilterSubregion(ctx, fctx)); |
| } |
| |
| sk_sp<SkImageFilter> SkSVGFeSpecularLighting::makeSpotLight(const SkSVGRenderContext& ctx, |
| const SkSVGFilterContext& fctx, |
| const SkSVGFeSpotLight* light) const { |
| const auto& limitingConeAngle = light->getLimitingConeAngle(); |
| const float cutoffAngle = limitingConeAngle.isValid() ? *limitingConeAngle : 180.f; |
| |
| return SkImageFilters::SpotLitSpecular( |
| this->resolveXYZ(ctx, fctx, light->getX(), light->getY(), light->getZ()), |
| this->resolveXYZ( |
| ctx, fctx, light->getPointsAtX(), light->getPointsAtY(), light->getPointsAtZ()), |
| light->getSpecularExponent(), |
| cutoffAngle, |
| this->resolveLightingColor(ctx), |
| this->getSurfaceScale(), |
| fSpecularConstant, |
| fSpecularExponent, |
| fctx.resolveInput(ctx, this->getIn(), this->resolveColorspace(ctx, fctx)), |
| this->resolveFilterSubregion(ctx, fctx)); |
| } |
| |
| bool SkSVGFeDiffuseLighting::parseAndSetAttribute(const char* n, const char* v) { |
| return INHERITED::parseAndSetAttribute(n, v) || |
| this->setDiffuseConstant( |
| SkSVGAttributeParser::parse<SkSVGNumberType>("diffuseConstant", n, v)); |
| } |
| |
| sk_sp<SkImageFilter> SkSVGFeDiffuseLighting::makeDistantLight( |
| const SkSVGRenderContext& ctx, |
| const SkSVGFilterContext& fctx, |
| const SkSVGFeDistantLight* light) const { |
| const SkPoint3 dir = light->computeDirection(); |
| return SkImageFilters::DistantLitDiffuse( |
| this->resolveXYZ(ctx, fctx, dir.fX, dir.fY, dir.fZ), |
| this->resolveLightingColor(ctx), |
| this->getSurfaceScale(), |
| this->getDiffuseConstant(), |
| fctx.resolveInput(ctx, this->getIn(), this->resolveColorspace(ctx, fctx)), |
| this->resolveFilterSubregion(ctx, fctx)); |
| } |
| |
| sk_sp<SkImageFilter> SkSVGFeDiffuseLighting::makePointLight(const SkSVGRenderContext& ctx, |
| const SkSVGFilterContext& fctx, |
| const SkSVGFePointLight* light) const { |
| return SkImageFilters::PointLitDiffuse( |
| this->resolveXYZ(ctx, fctx, light->getX(), light->getY(), light->getZ()), |
| this->resolveLightingColor(ctx), |
| this->getSurfaceScale(), |
| this->getDiffuseConstant(), |
| fctx.resolveInput(ctx, this->getIn(), this->resolveColorspace(ctx, fctx)), |
| this->resolveFilterSubregion(ctx, fctx)); |
| } |
| |
| sk_sp<SkImageFilter> SkSVGFeDiffuseLighting::makeSpotLight(const SkSVGRenderContext& ctx, |
| const SkSVGFilterContext& fctx, |
| const SkSVGFeSpotLight* light) const { |
| const auto& limitingConeAngle = light->getLimitingConeAngle(); |
| const float cutoffAngle = limitingConeAngle.isValid() ? *limitingConeAngle : 180.f; |
| |
| return SkImageFilters::SpotLitDiffuse( |
| this->resolveXYZ(ctx, fctx, light->getX(), light->getY(), light->getZ()), |
| this->resolveXYZ( |
| ctx, fctx, light->getPointsAtX(), light->getPointsAtY(), light->getPointsAtZ()), |
| light->getSpecularExponent(), |
| cutoffAngle, |
| this->resolveLightingColor(ctx), |
| this->getSurfaceScale(), |
| this->getDiffuseConstant(), |
| fctx.resolveInput(ctx, this->getIn(), this->resolveColorspace(ctx, fctx)), |
| this->resolveFilterSubregion(ctx, fctx)); |
| } |