Flutter Engine
The Flutter Engine
SkLightingImageFilter.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2012 The Android Open Source Project
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
9
13#include "include/core/SkM44.h"
16#include "include/core/SkRect.h"
29#include "src/core/SkRectPriv.h"
31
32#include <optional>
33#include <utility>
34
35struct SkISize;
36
37namespace {
38// The 3D points/vectors used for lighting don't have a great analog for the rest of the image
39// filtering system, and don't have any representation for ParameterSpace and LayerSpace. The
40// SVG spec is also vague on how to handle managing them. Using the principle of least-surprise,
41// the X and Y coordinates are treated as ParameterSpace<SkPoint|Vector> and the Z will be
42// scaled by the average of the X and Y scale factors when tranforming to layer space. For uniform
43// scaling transforms, this has the desirable behavior of uniformly scaling the Z axis as well.
44struct ZValue {
45 ZValue() : fZ(0.f) {}
46 ZValue(float z) : fZ(z) {}
47 operator float() const { return fZ; }
48
49 float fZ;
50};
51} // anonymous namespace
52
53namespace skif {
54template<>
55class LayerSpace<ZValue> {
56public:
57 LayerSpace() = default;
58 explicit LayerSpace(ZValue z) : fData(z) {}
59
60 float val() const { return fData.fZ; }
61
63 // See comment on ZValue for rationale.
65 skif::ParameterSpace<skif::Vector>({ZValue(z), ZValue(z)}));
66 return LayerSpace<ZValue>(SkScalarAve(z2d.x(), z2d.y()));
67 }
68
69private:
70 ZValue fData;
71};
72} // namespace skif
73
74namespace {
75
76struct Light {
77 enum class Type {
78 kDistant,
79 kPoint,
80 kSpot,
81 kLast = kSpot
82 };
83
84 Type fType;
85 SkColor fLightColor; // All lights
86
87 // Location and direction are decomposed into typed XY and Z for how they are transformed from
88 // parameter space to layer space.
89 skif::ParameterSpace<SkPoint> fLocationXY; // Spotlight and point lights only
90 skif::ParameterSpace<ZValue> fLocationZ; // ""
91
92 skif::ParameterSpace<skif::Vector> fDirectionXY; // Spotlight and distant lights only
93 skif::ParameterSpace<ZValue> fDirectionZ; // ""
94
95 // Spotlight only (and unchanged by layer matrix)
96 float fFalloffExponent;
97 float fCosCutoffAngle;
98
99 static Light Point(SkColor color, const SkPoint3& location) {
100 return {Type::kPoint,
101 color,
102 skif::ParameterSpace<SkPoint>({location.fX, location.fY}),
104 /*directionXY=*/{},
105 /*directionZ=*/{},
106 /*falloffExponent=*/0.f,
107 /*cutoffAngle=*/0.f};
108 }
109
110 static Light Distant(SkColor color, const SkPoint3& direction) {
111 return {Type::kDistant,
112 color,
113 /*locationXY=*/{},
114 /*locationZ=*/{},
115 skif::ParameterSpace<skif::Vector>({direction.fX, direction.fY}),
117 /*falloffExponent=*/0.f,
118 /*cutoffAngle=*/0.f};
119 }
120
121 static Light Spot(SkColor color, const SkPoint3& location, const SkPoint3& direction,
122 float falloffExponent, float cosCutoffAngle) {
123 return {Type::kSpot,
124 color,
125 skif::ParameterSpace<SkPoint>({location.fX, location.fY}),
127 skif::ParameterSpace<skif::Vector>({direction.fX, direction.fY}),
129 falloffExponent,
130 cosCutoffAngle};
131 }
132};
133
134struct Material {
135 enum class Type {
136 kDiffuse,
137 kSpecular,
138 kLast = kSpecular
139 };
140
141 Type fType;
142 // The base scale factor applied to alpha image to go from [0-1] to [0-depth] before computing
143 // surface normals.
144 skif::ParameterSpace<ZValue> fSurfaceDepth;
145
146 // Non-geometric
147 float fK; // Reflectance coefficient
148 float fShininess; // Specular only
149
150 static Material Diffuse(float k, float surfaceDepth) {
151 return {Type::kDiffuse, skif::ParameterSpace<ZValue>(surfaceDepth), k, 0.f};
152 }
153
154 static Material Specular(float k, float shininess, float surfaceDepth) {
155 return {Type::kSpecular, skif::ParameterSpace<ZValue>(surfaceDepth), k, shininess};
156 }
157};
158
159class SkLightingImageFilter final : public SkImageFilter_Base {
160public:
161 SkLightingImageFilter(const Light& light, const Material& material, sk_sp<SkImageFilter> input)
162 : SkImageFilter_Base(&input, 1)
163 , fLight(light)
164 , fMaterial(material) {}
165
166 SkRect computeFastBounds(const SkRect& src) const override;
167
168protected:
169 void flatten(SkWriteBuffer&) const override;
170
171private:
173 SK_FLATTENABLE_HOOKS(SkLightingImageFilter)
174 static Light LegacyDeserializeLight(SkReadBuffer& buffer);
175 static sk_sp<SkFlattenable> LegacyDiffuseCreateProc(SkReadBuffer& buffer);
176 static sk_sp<SkFlattenable> LegacySpecularCreateProc(SkReadBuffer& buffer);
177
178 bool onAffectsTransparentBlack() const override { return true; }
179
180 skif::FilterResult onFilterImage(const skif::Context&) const override;
181
183 const skif::Mapping& mapping,
184 const skif::LayerSpace<SkIRect>& desiredOutput,
185 std::optional<skif::LayerSpace<SkIRect>> contentBounds) const override;
186
187 std::optional<skif::LayerSpace<SkIRect>> onGetOutputLayerBounds(
188 const skif::Mapping& mapping,
189 std::optional<skif::LayerSpace<SkIRect>> contentBounds) const override;
190
191 skif::LayerSpace<SkIRect> requiredInput(const skif::LayerSpace<SkIRect>& desiredOutput) const {
192 // We request 1px of padding so that the visible normal map can do a regular Sobel kernel
193 // eval. The Sobel kernel is always applied in layer pixels
194 skif::LayerSpace<SkIRect> requiredInput = desiredOutput;
195 requiredInput.outset(skif::LayerSpace<SkISize>({1, 1}));
196 return requiredInput;
197 }
198
199 Light fLight;
200 Material fMaterial;
201};
202
203// Creates a shader that performs a Sobel filter on the alpha channel of the input image, using
204// 'edgeBounds' to decide how to modify the kernel weights.
205sk_sp<SkShader> make_normal_shader(sk_sp<SkShader> alphaMap,
206 const skif::LayerSpace<SkIRect>& edgeBounds,
207 skif::LayerSpace<ZValue> surfaceDepth) {
208 const SkRuntimeEffect* normalEffect =
210
212 builder.child("alphaMap") = std::move(alphaMap);
213 builder.uniform("edgeBounds") = SkRect::Make(SkIRect(edgeBounds)).makeInset(0.5f, 0.5f);
214 builder.uniform("negSurfaceDepth") = -surfaceDepth.val();
215
216 return builder.makeShader();
217}
218
219sk_sp<SkShader> make_lighting_shader(sk_sp<SkShader> normalMap,
220 Light::Type lightType,
221 SkColor lightColor,
222 skif::LayerSpace<SkPoint> locationXY,
223 skif::LayerSpace<ZValue> locationZ,
225 skif::LayerSpace<ZValue> directionZ,
226 float falloffExponent,
227 float cosCutoffAngle,
228 Material::Type matType,
229 skif::LayerSpace<ZValue> surfaceDepth,
230 float k,
231 float shininess) {
232
233 const SkRuntimeEffect* lightingEffect =
234 GetKnownRuntimeEffect(SkKnownRuntimeEffects::StableKey::kLighting);
235
237 builder.child("normalMap") = std::move(normalMap);
238
239 builder.uniform("materialAndLightType") =
240 SkV4{surfaceDepth.val(),
241 shininess,
242 matType == Material::Type::kDiffuse ? 0.f : 1.f,
243 lightType == Light::Type::kPoint ?
244 0.f : (lightType == Light::Type::kDistant ? -1.f : 1.f)};
245 builder.uniform("lightPosAndSpotFalloff") =
246 SkV4{locationXY.x(), locationXY.y(), locationZ.val(), falloffExponent};
247
248 // Pre-normalize the light direction, but this can be (0,0,0) for point lights, which won't use
249 // the uniform anyways. Avoid a division by 0 to keep ASAN happy or in the event that a spot/dir
250 // light have bad user input.
251 SkV3 dir{directionXY.x(), directionXY.y(), directionZ.val()};
252 float invDirLen = dir.length();
253 invDirLen = invDirLen ? 1.0f / invDirLen : 0.f;
254 builder.uniform("lightDirAndSpotCutoff") =
255 SkV4{invDirLen*dir.x, invDirLen*dir.y, invDirLen*dir.z, cosCutoffAngle};
256
257 // Historically, the Skia lighting image filter did not apply any color space transformation to
258 // the light's color. The SVG spec for the lighting effects does not stipulate how to interpret
259 // the color for a light. Overall, it does not have a principled physically based approach, but
260 // the closest way to interpret it, is:
261 // - the material's K is a uniformly distributed reflectance coefficient
262 // - lighting *should* be calculated in a linear color space, which is the default for SVG
263 // filters. Chromium manages these color transformations using SkImageFilters::ColorFilter
264 // so it's not necessarily reflected in the Context's color space.
265 // - it's unspecified in the SVG spec if the light color should be transformed to linear or
266 // interpreted as linear already. Regardless, if there was any transformation that needed to
267 // occur, Blink took care of it in the past so adding color space management to the light
268 // color would be a breaking change.
269 // - so for now, leave the color un-modified and apply K up front since no color space
270 // transforms need to be performed on the original light color.
271 const float colorScale = k / 255.f;
272 builder.uniform("lightColor") = SkV3{SkColorGetR(lightColor) * colorScale,
273 SkColorGetG(lightColor) * colorScale,
274 SkColorGetB(lightColor) * colorScale};
275
276 return builder.makeShader();
277}
278
279sk_sp<SkImageFilter> make_lighting(const Light& light,
280 const Material& material,
282 const SkImageFilters::CropRect& cropRect) {
283 // According to the spec, ks and kd can be any non-negative number:
284 // http://www.w3.org/TR/SVG/filters.html#feSpecularLightingElement
285 if (!SkIsFinite(material.fK, material.fShininess, ZValue(material.fSurfaceDepth)) ||
286 material.fK < 0.f) {
287 return nullptr;
288 }
289
290 // Ensure light values are finite, and the cosine should be between -1 and 1
291 if (!SkPoint(light.fLocationXY).isFinite() || !skif::Vector(light.fDirectionXY).isFinite() ||
292 !SkIsFinite(light.fFalloffExponent, light.fCosCutoffAngle,
293 ZValue(light.fLocationZ), ZValue(light.fDirectionZ)) ||
294 light.fCosCutoffAngle < -1.f || light.fCosCutoffAngle > 1.f) {
295 return nullptr;
296 }
297
298 // If a crop rect is provided, it clamps both the input (to better match the SVG's normal
299 // boundary condition spec) and the output (because otherwise it has infinite bounds).
300 sk_sp<SkImageFilter> filter = std::move(input);
301 if (cropRect) {
302 filter = SkImageFilters::Crop(*cropRect, std::move(filter));
303 }
304 filter = sk_sp<SkImageFilter>(
305 new SkLightingImageFilter(light, material, std::move(filter)));
306 if (cropRect) {
307 filter = SkImageFilters::Crop(*cropRect, std::move(filter));
308 }
309 return filter;
310}
311
312} // anonymous namespace
313
315 const SkPoint3& direction, SkColor lightColor, SkScalar surfaceScale, SkScalar kd,
316 sk_sp<SkImageFilter> input, const CropRect& cropRect) {
317 return make_lighting(Light::Distant(lightColor, direction),
318 Material::Diffuse(kd, surfaceScale),
319 std::move(input), cropRect);
320}
321
323 const SkPoint3& location, SkColor lightColor, SkScalar surfaceScale, SkScalar kd,
324 sk_sp<SkImageFilter> input, const CropRect& cropRect) {
325 return make_lighting(Light::Point(lightColor, location),
326 Material::Diffuse(kd, surfaceScale),
327 std::move(input), cropRect);
328}
329
331 const SkPoint3& location, const SkPoint3& target, SkScalar falloffExponent,
332 SkScalar cutoffAngle, SkColor lightColor, SkScalar surfaceScale, SkScalar kd,
333 sk_sp<SkImageFilter> input, const CropRect& cropRect) {
334 SkPoint3 dir = target - location;
335 float cosCutoffAngle = SkScalarCos(SkDegreesToRadians(cutoffAngle));
336 return make_lighting(Light::Spot(lightColor, location, dir, falloffExponent, cosCutoffAngle),
337 Material::Diffuse(kd, surfaceScale),
338 std::move(input), cropRect);
339}
340
342 const SkPoint3& direction, SkColor lightColor, SkScalar surfaceScale, SkScalar ks,
343 SkScalar shininess, sk_sp<SkImageFilter> input, const CropRect& cropRect) {
344 return make_lighting(Light::Distant(lightColor, direction),
345 Material::Specular(ks, shininess, surfaceScale),
346 std::move(input), cropRect);
347}
348
350 const SkPoint3& location, SkColor lightColor, SkScalar surfaceScale, SkScalar ks,
351 SkScalar shininess, sk_sp<SkImageFilter> input, const CropRect& cropRect) {
352 return make_lighting(Light::Point(lightColor, location),
353 Material::Specular(ks, shininess, surfaceScale),
354 std::move(input), cropRect);
355}
356
358 const SkPoint3& location, const SkPoint3& target, SkScalar falloffExponent,
359 SkScalar cutoffAngle, SkColor lightColor, SkScalar surfaceScale, SkScalar ks,
360 SkScalar shininess, sk_sp<SkImageFilter> input, const CropRect& cropRect) {
361 SkPoint3 dir = target - location;
362 float cosCutoffAngle = SkScalarCos(SkDegreesToRadians(cutoffAngle));
363 return make_lighting(Light::Spot(lightColor, location, dir, falloffExponent, cosCutoffAngle),
364 Material::Specular(ks, shininess, surfaceScale),
365 std::move(input), cropRect);
366}
367
369 SK_REGISTER_FLATTENABLE(SkLightingImageFilter);
370 // TODO (michaelludwig): Remove after grace period for SKPs to stop using old name
371 SkFlattenable::Register("SkDiffuseLightingImageFilter",
372 SkLightingImageFilter::LegacyDiffuseCreateProc);
373 SkFlattenable::Register("SkSpecularLightingImageFilter",
374 SkLightingImageFilter::LegacySpecularCreateProc);
375}
376
377///////////////////////////////////////////////////////////////////////////////
378
379sk_sp<SkFlattenable> SkLightingImageFilter::CreateProc(SkReadBuffer& buffer) {
381
382 Light light;
383 light.fType = buffer.read32LE(Light::Type::kLast);
384 light.fLightColor = buffer.readColor();
385
386 SkPoint3 lightPos, lightDir;
387 buffer.readPoint3(&lightPos);
388 light.fLocationXY = skif::ParameterSpace<SkPoint>({lightPos.fX, lightPos.fY});
389 light.fLocationZ = skif::ParameterSpace<ZValue>(lightPos.fZ);
390
391 buffer.readPoint3(&lightDir);
392 light.fDirectionXY = skif::ParameterSpace<skif::Vector>({lightDir.fX, lightDir.fY});
393 light.fDirectionZ = skif::ParameterSpace<ZValue>(lightDir.fZ);
394
395 light.fFalloffExponent = buffer.readScalar();
396 light.fCosCutoffAngle = buffer.readScalar();
397
398 Material material;
399 material.fType = buffer.read32LE(Material::Type::kLast);
400 material.fSurfaceDepth = skif::ParameterSpace<ZValue>(buffer.readScalar());
401 material.fK = buffer.readScalar();
402 material.fShininess = buffer.readScalar();
403
404 if (!buffer.isValid()) {
405 return nullptr;
406 }
407
408 return make_lighting(light, material, common.getInput(0), common.cropRect());
409}
410
411Light SkLightingImageFilter::LegacyDeserializeLight(SkReadBuffer& buffer) {
412 // Light::Type has the same order as the legacy SkImageFilterLight::LightType enum
413 Light::Type lightType = buffer.read32LE(Light::Type::kLast);
414 if (!buffer.isValid()) {
415 return {};
416 }
417
418 // Legacy lights stored just the RGB, but as floats (notably *not* normalized to [0-1])
419 SkColor lightColor = SkColorSetARGB(/*a (ignored)=*/255,
420 /*r=*/ (U8CPU) buffer.readScalar(),
421 /*g=*/ (U8CPU) buffer.readScalar(),
422 /*b=*/ (U8CPU) buffer.readScalar());
423 // Legacy lights only serialized fields specific to that type
424 switch (lightType) {
425 case Light::Type::kDistant: {
426 SkPoint3 dir = {buffer.readScalar(), buffer.readScalar(), buffer.readScalar()};
427 return Light::Distant(lightColor, dir);
428 }
429 case Light::Type::kPoint: {
430 SkPoint3 loc = {buffer.readScalar(), buffer.readScalar(), buffer.readScalar()};
431 return Light::Point(lightColor, loc);
432 }
433 case Light::Type::kSpot: {
434 SkPoint3 loc = {buffer.readScalar(), buffer.readScalar(), buffer.readScalar()};
435 SkPoint3 target = {buffer.readScalar(), buffer.readScalar(), buffer.readScalar()};
436 float falloffExponent = buffer.readScalar();
437 float cosOuterConeAngle = buffer.readScalar();
438 buffer.readScalar(); // skip cosInnerConeAngle, derived from outer cone angle
439 buffer.readScalar(); // skip coneScale, which is a constant
440 buffer.readScalar(); // skip S, which is normalize(target - loc)
441 buffer.readScalar(); // ""
442 buffer.readScalar(); // ""
443 return Light::Spot(lightColor, loc, target - loc, falloffExponent, cosOuterConeAngle);
444 }
445 }
446
447 SkUNREACHABLE; // Validation by read32LE() should avoid this
448}
449
450sk_sp<SkFlattenable> SkLightingImageFilter::LegacyDiffuseCreateProc(SkReadBuffer& buffer) {
452
453 Light light = LegacyDeserializeLight(buffer);
454
455 // Legacy implementations used (scale/255) when filtering, but serialized (fScale*255) so the
456 // buffer held the original unmodified surface scale.
457 float surfaceScale = buffer.readScalar();
458 float kd = buffer.readScalar();
459 Material material = Material::Diffuse(kd, surfaceScale);
460
461 return make_lighting(light, material, common.getInput(0), common.cropRect());
462}
463
464sk_sp<SkFlattenable> SkLightingImageFilter::LegacySpecularCreateProc(SkReadBuffer& buffer) {
466
467 Light light = LegacyDeserializeLight(buffer);
468
469 // Legacy implementations used (scale/255) when filtering, but serialized (fScale*255) so the
470 // buffer held the original unmodified surface scale.
471 float surfaceScale = buffer.readScalar();
472 float ks = buffer.readScalar();
473 float shininess = buffer.readScalar();
474 Material material = Material::Specular(ks, shininess, surfaceScale);
475
476 return make_lighting(light, material, common.getInput(0), common.cropRect());
477}
478
479void SkLightingImageFilter::flatten(SkWriteBuffer& buffer) const {
480 this->SkImageFilter_Base::flatten(buffer);
481
482 // Light
483 buffer.writeInt((int) fLight.fType);
484 buffer.writeColor(fLight.fLightColor);
485
486 buffer.writePoint(SkPoint(fLight.fLocationXY));
487 buffer.writeScalar(ZValue(fLight.fLocationZ));
488
489 skif::Vector dirXY{fLight.fDirectionXY};
490 buffer.writePoint(SkPoint{dirXY.fX, dirXY.fY});
491 buffer.writeScalar(ZValue(fLight.fDirectionZ));
492
493 buffer.writeScalar(fLight.fFalloffExponent);
494 buffer.writeScalar(fLight.fCosCutoffAngle);
495
496 // Material
497 buffer.writeInt((int) fMaterial.fType);
498 buffer.writeScalar(ZValue(fMaterial.fSurfaceDepth));
499 buffer.writeScalar(fMaterial.fK);
500 buffer.writeScalar(fMaterial.fShininess);
501}
502
503///////////////////////////////////////////////////////////////////////////////
504
505skif::FilterResult SkLightingImageFilter::onFilterImage(const skif::Context& ctx) const {
506 using ShaderFlags = skif::FilterResult::ShaderFlags;
507
508 auto mapZToLayer = [&ctx](skif::ParameterSpace<ZValue> z) {
509 return skif::LayerSpace<ZValue>::Map(ctx.mapping(), z);
510 };
511
512 // Map lighting and material parameters into layer space
513 skif::LayerSpace<ZValue> surfaceDepth = mapZToLayer(fMaterial.fSurfaceDepth);
514 skif::LayerSpace<SkPoint> lightLocationXY = ctx.mapping().paramToLayer(fLight.fLocationXY);
515 skif::LayerSpace<ZValue> lightLocationZ = mapZToLayer(fLight.fLocationZ);
516 skif::LayerSpace<skif::Vector> lightDirXY = ctx.mapping().paramToLayer(fLight.fDirectionXY);
517 skif::LayerSpace<ZValue> lightDirZ = mapZToLayer(fLight.fDirectionZ);
518
519 // The normal map is determined by a 3x3 kernel, so we request a 1px outset of what should be
520 // filled by the lighting equation. Ideally this means there are no boundary conditions visible.
521 // If the required input is incomplete, the lighting filter handles the boundaries in two ways:
522 // - When the actual child output's edge matches the desired output's edge, it uses clamped
523 // tiling at the desired output. This approximates the modified Sobel kernel's specified in
524 // https://drafts.fxtf.org/filter-effects/#feDiffuseLightingElement. NOTE: It's identical to
525 // the interior kernel and near equal on the 4 edges (only weights are biased differently).
526 // The four corners' convolution sums with clamped tiling are not equal, but should not be
527 // objectionable since the normals produced are reasonable and still further processed by the
528 // lighting equation. The increased complexity is not worth it for just 4 pixels of output.
529 // - However, when the desired output is far larger than the produced image, we process the
530 // child output with the default decal tiling that the Skia image filter pipeline relies on.
531 // This creates a visual bevel at the image boundary but avoids producing streaked normals if
532 // the clamped tiling was used in all scenarios.
533 skif::LayerSpace<SkIRect> requiredInput = this->requiredInput(ctx.desiredOutput());
534 skif::FilterResult childOutput =
535 this->getChildOutput(0, ctx.withNewDesiredOutput(requiredInput));
536
537 skif::LayerSpace<SkIRect> clampRect = requiredInput; // effectively no clamping of normals
538 if (!childOutput.layerBounds().contains(requiredInput)) {
539 // Adjust clampRect edges to desiredOutput if the actual child output matched the lighting
540 // output size (typical SVG case). Otherwise leave coordinates alone to use decal tiling
541 // automatically for the pixels outside the child image but inside the desired output.
542 auto edgeClamp = [](int actualEdgeValue, int requestedEdgeValue, int outputEdge) {
543 return actualEdgeValue == outputEdge ? outputEdge : requestedEdgeValue;
544 };
545 auto inputRect = childOutput.layerBounds();
546 auto clampTo = ctx.desiredOutput();
547 clampRect = skif::LayerSpace<SkIRect>({
548 edgeClamp(inputRect.left(), requiredInput.left(), clampTo.left()),
549 edgeClamp(inputRect.top(), requiredInput.top(), clampTo.top()),
550 edgeClamp(inputRect.right(), requiredInput.right(), clampTo.right()),
551 edgeClamp(inputRect.bottom(), requiredInput.bottom(), clampTo.bottom())});
552 }
553
555 builder.add(childOutput, /*sampleBounds=*/clampRect, ShaderFlags::kSampledRepeatedly);
556 return builder.eval([&](SkSpan<sk_sp<SkShader>> input) {
557 // TODO: Once shaders are deferred in FilterResult, it will likely make sense to have an
558 // internal normal map filter that uses this shader, and then have the lighting effects as
559 // a separate filter. It's common for multiple lights to use the same input (producing the
560 // same normal map) before being merged together. With a separate normal image filter, its
561 // output would be automatically cached, and the lighting equation shader would be deferred
562 // to the merge's draw operation, making for a maximum of 2 renderpasses instead of N+1.
563 sk_sp<SkShader> normals = make_normal_shader(std::move(input[0]), clampRect, surfaceDepth);
564 return make_lighting_shader(std::move(normals),
565 // Light in layer space
566 fLight.fType,
567 fLight.fLightColor,
568 lightLocationXY,
569 lightLocationZ,
570 lightDirXY,
571 lightDirZ,
572 fLight.fFalloffExponent,
573 fLight.fCosCutoffAngle,
574 // Material in layer space
575 fMaterial.fType,
576 surfaceDepth,
577 fMaterial.fK,
578 fMaterial.fShininess);
579 });
580}
581
582skif::LayerSpace<SkIRect> SkLightingImageFilter::onGetInputLayerBounds(
583 const skif::Mapping& mapping,
584 const skif::LayerSpace<SkIRect>& desiredOutput,
585 std::optional<skif::LayerSpace<SkIRect>> contentBounds) const {
586 skif::LayerSpace<SkIRect> requiredInput = this->requiredInput(desiredOutput);
587 return this->getChildInputLayerBounds(0, mapping, requiredInput, contentBounds);
588}
589
590std::optional<skif::LayerSpace<SkIRect>> SkLightingImageFilter::onGetOutputLayerBounds(
591 const skif::Mapping& mapping,
592 std::optional<skif::LayerSpace<SkIRect>> contentBounds) const {
593 // The lighting equation is defined on the entire plane, even if the input image that defines
594 // the normal map is bounded. It just is evaluated at a constant normal vector, which can still
595 // produce non-constant color since the direction to the eye and light change per pixel.
597}
598
599SkRect SkLightingImageFilter::computeFastBounds(const SkRect& src) const {
601}
static SkM44 normals(SkM44 m)
Definition: 3DSlide.cpp:78
#define SkUNREACHABLE
Definition: SkAssert.h:135
unsigned U8CPU
Definition: SkCPUTypes.h:18
#define SkColorGetR(color)
Definition: SkColor.h:65
#define SkColorGetG(color)
Definition: SkColor.h:69
uint32_t SkColor
Definition: SkColor.h:37
static constexpr SkColor SkColorSetARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b)
Definition: SkColor.h:49
#define SkColorGetB(color)
Definition: SkColor.h:73
#define SK_FLATTENABLE_HOOKS(type)
#define SK_REGISTER_FLATTENABLE(type)
static bool SkIsFinite(T x, Pack... values)
#define SK_IMAGEFILTER_UNFLATTEN_COMMON(localVar, expectedCount)
void SkRegisterLightingImageFilterFlattenables()
sk_sp< T > sk_ref_sp(T *obj)
Definition: SkRefCnt.h:381
#define SkDegreesToRadians(degrees)
Definition: SkScalar.h:77
#define SkScalarAve(a, b)
Definition: SkScalar.h:74
#define SkScalarCos(radians)
Definition: SkScalar.h:46
static void Register(const char name[], Factory)
virtual skif::LayerSpace< SkIRect > onGetInputLayerBounds(const skif::Mapping &mapping, const skif::LayerSpace< SkIRect > &desiredOutput, std::optional< skif::LayerSpace< SkIRect > > contentBounds) const =0
virtual bool onAffectsTransparentBlack() const
void flatten(SkWriteBuffer &) const override
virtual std::optional< skif::LayerSpace< SkIRect > > onGetOutputLayerBounds(const skif::Mapping &mapping, std::optional< skif::LayerSpace< SkIRect > > contentBounds) const =0
virtual skif::FilterResult onFilterImage(const skif::Context &context) const =0
virtual SkRect computeFastBounds(const SkRect &bounds) const
static sk_sp< SkImageFilter > PointLitDiffuse(const SkPoint3 &location, SkColor lightColor, SkScalar surfaceScale, SkScalar kd, sk_sp< SkImageFilter > input, const CropRect &cropRect={})
static sk_sp< SkImageFilter > DistantLitSpecular(const SkPoint3 &direction, SkColor lightColor, SkScalar surfaceScale, SkScalar ks, SkScalar shininess, sk_sp< SkImageFilter > input, const CropRect &cropRect={})
static sk_sp< SkImageFilter > DistantLitDiffuse(const SkPoint3 &direction, SkColor lightColor, SkScalar surfaceScale, SkScalar kd, sk_sp< SkImageFilter > input, const CropRect &cropRect={})
static sk_sp< SkImageFilter > Crop(const SkRect &rect, SkTileMode tileMode, sk_sp< SkImageFilter > input)
static sk_sp< SkImageFilter > PointLitSpecular(const SkPoint3 &location, SkColor lightColor, SkScalar surfaceScale, SkScalar ks, SkScalar shininess, sk_sp< SkImageFilter > input, const CropRect &cropRect={})
static sk_sp< SkImageFilter > SpotLitDiffuse(const SkPoint3 &location, const SkPoint3 &target, SkScalar falloffExponent, SkScalar cutoffAngle, SkColor lightColor, SkScalar surfaceScale, SkScalar kd, sk_sp< SkImageFilter > input, const CropRect &cropRect={})
static sk_sp< SkImageFilter > SpotLitSpecular(const SkPoint3 &location, const SkPoint3 &target, SkScalar falloffExponent, SkScalar cutoffAngle, SkColor lightColor, SkScalar surfaceScale, SkScalar ks, SkScalar shininess, sk_sp< SkImageFilter > input, const CropRect &cropRect={})
static SkRect MakeLargeS32()
Definition: SkRectPriv.h:33
const LayerSpace< SkIRect > & desiredOutput() const
Context withNewDesiredOutput(const LayerSpace< SkIRect > &desiredOutput) const
const Mapping & mapping() const
LayerSpace< SkIRect > layerBounds() const
void outset(const LayerSpace< SkISize > &delta)
bool contains(const LayerSpace< SkIRect > &r) const
static LayerSpace< ZValue > Map(const Mapping &mapping, ParameterSpace< ZValue > z)
LayerSpace< T > paramToLayer(const ParameterSpace< T > &paramGeometry) const
DlColor color
@ kNormal
Default priority level.
Definition: embedder.h:262
float SkScalar
Definition: extension.cpp:12
uint32_t * target
const SkRuntimeEffect * GetKnownRuntimeEffect(StableKey stableKey)
Definition: common.py:1
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace Enable an endless trace buffer The default is a ring buffer This is useful when very old events need to viewed For during application launch Memory usage will continue to grow indefinitely however Start app with an specific route defined on the framework flutter assets dir
Definition: switches.h:145
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace buffer
Definition: switches.h:126
@ kPoint
Draws a point at each input vertex.
TPoint< Scalar > Point
Definition: point.h:322
Definition: SkDevice.h:63
Definition: SkRect.h:32
Definition: SkSize.h:16
SkScalar fX
Definition: SkPoint3.h:16
SkScalar fZ
Definition: SkPoint3.h:16
SkScalar fY
Definition: SkPoint3.h:16
float fX
x-axis value
Definition: SkPoint_impl.h:164
bool isFinite() const
Definition: SkPoint_impl.h:412
static SkRect Make(const SkISize &size)
Definition: SkRect.h:669
SkRect makeInset(float dx, float dy) const
Definition: SkRect.h:987
Definition: SkM44.h:56
Definition: SkM44.h:98
bool isFinite() const