aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/effects/imagefilters/SkLightingImageFilter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/effects/imagefilters/SkLightingImageFilter.cpp')
-rw-r--r--src/effects/imagefilters/SkLightingImageFilter.cpp2195
1 files changed, 2195 insertions, 0 deletions
diff --git a/src/effects/imagefilters/SkLightingImageFilter.cpp b/src/effects/imagefilters/SkLightingImageFilter.cpp
new file mode 100644
index 0000000000..064c16d48a
--- /dev/null
+++ b/src/effects/imagefilters/SkLightingImageFilter.cpp
@@ -0,0 +1,2195 @@
+/*
+ * Copyright 2012 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkLightingImageFilter.h"
+#include "SkBitmap.h"
+#include "SkColorData.h"
+#include "SkColorSpaceXformer.h"
+#include "SkFlattenablePriv.h"
+#include "SkImageFilterPriv.h"
+#include "SkPoint3.h"
+#include "SkReadBuffer.h"
+#include "SkSpecialImage.h"
+#include "SkTypes.h"
+#include "SkWriteBuffer.h"
+
+#if SK_SUPPORT_GPU
+#include "GrContext.h"
+#include "GrFixedClip.h"
+#include "GrFragmentProcessor.h"
+#include "GrPaint.h"
+#include "GrRenderTargetContext.h"
+#include "GrTexture.h"
+#include "GrTextureProxy.h"
+
+#include "SkGr.h"
+#include "effects/GrTextureDomain.h"
+#include "glsl/GrGLSLFragmentProcessor.h"
+#include "glsl/GrGLSLFragmentShaderBuilder.h"
+#include "glsl/GrGLSLProgramDataManager.h"
+#include "glsl/GrGLSLUniformHandler.h"
+#include "../private/GrGLSL.h"
+
+class GrGLDiffuseLightingEffect;
+class GrGLSpecularLightingEffect;
+
+// For brevity
+typedef GrGLSLProgramDataManager::UniformHandle UniformHandle;
+#endif
+
+const SkScalar gOneThird = SkIntToScalar(1) / 3;
+const SkScalar gTwoThirds = SkIntToScalar(2) / 3;
+const SkScalar gOneHalf = 0.5f;
+const SkScalar gOneQuarter = 0.25f;
+
+#if SK_SUPPORT_GPU
+static void setUniformPoint3(const GrGLSLProgramDataManager& pdman, UniformHandle uni,
+ const SkPoint3& point) {
+ GR_STATIC_ASSERT(sizeof(SkPoint3) == 3 * sizeof(float));
+ pdman.set3fv(uni, 1, &point.fX);
+}
+
+static void setUniformNormal3(const GrGLSLProgramDataManager& pdman, UniformHandle uni,
+ const SkPoint3& point) {
+ setUniformPoint3(pdman, uni, point);
+}
+#endif
+
+// Shift matrix components to the left, as we advance pixels to the right.
+static inline void shiftMatrixLeft(int m[9]) {
+ m[0] = m[1];
+ m[3] = m[4];
+ m[6] = m[7];
+ m[1] = m[2];
+ m[4] = m[5];
+ m[7] = m[8];
+}
+
+static inline void fast_normalize(SkPoint3* vector) {
+ // add a tiny bit so we don't have to worry about divide-by-zero
+ SkScalar magSq = vector->dot(*vector) + SK_ScalarNearlyZero;
+ SkScalar scale = sk_float_rsqrt(magSq);
+ vector->fX *= scale;
+ vector->fY *= scale;
+ vector->fZ *= scale;
+}
+
+static SkPoint3 read_point3(SkReadBuffer& buffer) {
+ SkPoint3 point;
+ point.fX = buffer.readScalar();
+ point.fY = buffer.readScalar();
+ point.fZ = buffer.readScalar();
+ buffer.validate(SkScalarIsFinite(point.fX) &&
+ SkScalarIsFinite(point.fY) &&
+ SkScalarIsFinite(point.fZ));
+ return point;
+};
+
+static void write_point3(const SkPoint3& point, SkWriteBuffer& buffer) {
+ buffer.writeScalar(point.fX);
+ buffer.writeScalar(point.fY);
+ buffer.writeScalar(point.fZ);
+};
+
+class GrGLLight;
+class SkImageFilterLight : public SkRefCnt {
+public:
+ enum LightType {
+ kDistant_LightType,
+ kPoint_LightType,
+ kSpot_LightType,
+
+ kLast_LightType = kSpot_LightType
+ };
+ virtual LightType type() const = 0;
+ const SkPoint3& color() const { return fColor; }
+ virtual GrGLLight* createGLLight() const = 0;
+ virtual bool isEqual(const SkImageFilterLight& other) const {
+ return fColor == other.fColor;
+ }
+ virtual SkImageFilterLight* transform(const SkMatrix& matrix) const = 0;
+
+ virtual sk_sp<SkImageFilterLight> makeColorSpace(SkColorSpaceXformer*) const = 0;
+
+ // Defined below SkLight's subclasses.
+ void flattenLight(SkWriteBuffer& buffer) const;
+ static SkImageFilterLight* UnflattenLight(SkReadBuffer& buffer);
+
+ virtual SkPoint3 surfaceToLight(int x, int y, int z, SkScalar surfaceScale) const = 0;
+ virtual SkPoint3 lightColor(const SkPoint3& surfaceToLight) const = 0;
+
+protected:
+ SkImageFilterLight(SkColor color) {
+ fColor = SkPoint3::Make(SkIntToScalar(SkColorGetR(color)),
+ SkIntToScalar(SkColorGetG(color)),
+ SkIntToScalar(SkColorGetB(color)));
+ }
+ SkImageFilterLight(const SkPoint3& color) : fColor(color) {}
+
+ SkImageFilterLight(SkReadBuffer& buffer) {
+ fColor = read_point3(buffer);
+ }
+
+ virtual void onFlattenLight(SkWriteBuffer& buffer) const = 0;
+
+
+private:
+ typedef SkRefCnt INHERITED;
+ SkPoint3 fColor;
+};
+
+class BaseLightingType {
+public:
+ BaseLightingType() {}
+ virtual ~BaseLightingType() {}
+
+ virtual SkPMColor light(const SkPoint3& normal, const SkPoint3& surfaceTolight,
+ const SkPoint3& lightColor) const= 0;
+};
+
+class DiffuseLightingType : public BaseLightingType {
+public:
+ DiffuseLightingType(SkScalar kd)
+ : fKD(kd) {}
+ SkPMColor light(const SkPoint3& normal, const SkPoint3& surfaceTolight,
+ const SkPoint3& lightColor) const override {
+ SkScalar colorScale = fKD * normal.dot(surfaceTolight);
+ colorScale = SkScalarClampMax(colorScale, SK_Scalar1);
+ SkPoint3 color = lightColor.makeScale(colorScale);
+ return SkPackARGB32(255,
+ SkClampMax(SkScalarRoundToInt(color.fX), 255),
+ SkClampMax(SkScalarRoundToInt(color.fY), 255),
+ SkClampMax(SkScalarRoundToInt(color.fZ), 255));
+ }
+private:
+ SkScalar fKD;
+};
+
+static SkScalar max_component(const SkPoint3& p) {
+ return p.x() > p.y() ? (p.x() > p.z() ? p.x() : p.z()) : (p.y() > p.z() ? p.y() : p.z());
+}
+
+class SpecularLightingType : public BaseLightingType {
+public:
+ SpecularLightingType(SkScalar ks, SkScalar shininess)
+ : fKS(ks), fShininess(shininess) {}
+ SkPMColor light(const SkPoint3& normal, const SkPoint3& surfaceTolight,
+ const SkPoint3& lightColor) const override {
+ SkPoint3 halfDir(surfaceTolight);
+ halfDir.fZ += SK_Scalar1; // eye position is always (0, 0, 1)
+ fast_normalize(&halfDir);
+ SkScalar colorScale = fKS * SkScalarPow(normal.dot(halfDir), fShininess);
+ colorScale = SkScalarClampMax(colorScale, SK_Scalar1);
+ SkPoint3 color = lightColor.makeScale(colorScale);
+ return SkPackARGB32(SkClampMax(SkScalarRoundToInt(max_component(color)), 255),
+ SkClampMax(SkScalarRoundToInt(color.fX), 255),
+ SkClampMax(SkScalarRoundToInt(color.fY), 255),
+ SkClampMax(SkScalarRoundToInt(color.fZ), 255));
+ }
+private:
+ SkScalar fKS;
+ SkScalar fShininess;
+};
+
+static inline SkScalar sobel(int a, int b, int c, int d, int e, int f, SkScalar scale) {
+ return (-a + b - 2 * c + 2 * d -e + f) * scale;
+}
+
+static inline SkPoint3 pointToNormal(SkScalar x, SkScalar y, SkScalar surfaceScale) {
+ SkPoint3 vector = SkPoint3::Make(-x * surfaceScale, -y * surfaceScale, 1);
+ fast_normalize(&vector);
+ return vector;
+}
+
+static inline SkPoint3 topLeftNormal(int m[9], SkScalar surfaceScale) {
+ return pointToNormal(sobel(0, 0, m[4], m[5], m[7], m[8], gTwoThirds),
+ sobel(0, 0, m[4], m[7], m[5], m[8], gTwoThirds),
+ surfaceScale);
+}
+
+static inline SkPoint3 topNormal(int m[9], SkScalar surfaceScale) {
+ return pointToNormal(sobel( 0, 0, m[3], m[5], m[6], m[8], gOneThird),
+ sobel(m[3], m[6], m[4], m[7], m[5], m[8], gOneHalf),
+ surfaceScale);
+}
+
+static inline SkPoint3 topRightNormal(int m[9], SkScalar surfaceScale) {
+ return pointToNormal(sobel( 0, 0, m[3], m[4], m[6], m[7], gTwoThirds),
+ sobel(m[3], m[6], m[4], m[7], 0, 0, gTwoThirds),
+ surfaceScale);
+}
+
+static inline SkPoint3 leftNormal(int m[9], SkScalar surfaceScale) {
+ return pointToNormal(sobel(m[1], m[2], m[4], m[5], m[7], m[8], gOneHalf),
+ sobel( 0, 0, m[1], m[7], m[2], m[8], gOneThird),
+ surfaceScale);
+}
+
+
+static inline SkPoint3 interiorNormal(int m[9], SkScalar surfaceScale) {
+ return pointToNormal(sobel(m[0], m[2], m[3], m[5], m[6], m[8], gOneQuarter),
+ sobel(m[0], m[6], m[1], m[7], m[2], m[8], gOneQuarter),
+ surfaceScale);
+}
+
+static inline SkPoint3 rightNormal(int m[9], SkScalar surfaceScale) {
+ return pointToNormal(sobel(m[0], m[1], m[3], m[4], m[6], m[7], gOneHalf),
+ sobel(m[0], m[6], m[1], m[7], 0, 0, gOneThird),
+ surfaceScale);
+}
+
+static inline SkPoint3 bottomLeftNormal(int m[9], SkScalar surfaceScale) {
+ return pointToNormal(sobel(m[1], m[2], m[4], m[5], 0, 0, gTwoThirds),
+ sobel( 0, 0, m[1], m[4], m[2], m[5], gTwoThirds),
+ surfaceScale);
+}
+
+static inline SkPoint3 bottomNormal(int m[9], SkScalar surfaceScale) {
+ return pointToNormal(sobel(m[0], m[2], m[3], m[5], 0, 0, gOneThird),
+ sobel(m[0], m[3], m[1], m[4], m[2], m[5], gOneHalf),
+ surfaceScale);
+}
+
+static inline SkPoint3 bottomRightNormal(int m[9], SkScalar surfaceScale) {
+ return pointToNormal(sobel(m[0], m[1], m[3], m[4], 0, 0, gTwoThirds),
+ sobel(m[0], m[3], m[1], m[4], 0, 0, gTwoThirds),
+ surfaceScale);
+}
+
+
+class UncheckedPixelFetcher {
+public:
+ static inline uint32_t Fetch(const SkBitmap& src, int x, int y, const SkIRect& bounds) {
+ return SkGetPackedA32(*src.getAddr32(x, y));
+ }
+};
+
+// The DecalPixelFetcher is used when the destination crop rect exceeds the input bitmap bounds.
+class DecalPixelFetcher {
+public:
+ static inline uint32_t Fetch(const SkBitmap& src, int x, int y, const SkIRect& bounds) {
+ if (x < bounds.fLeft || x >= bounds.fRight || y < bounds.fTop || y >= bounds.fBottom) {
+ return 0;
+ } else {
+ return SkGetPackedA32(*src.getAddr32(x, y));
+ }
+ }
+};
+
+template <class PixelFetcher>
+static void lightBitmap(const BaseLightingType& lightingType,
+ const SkImageFilterLight* l,
+ const SkBitmap& src,
+ SkBitmap* dst,
+ SkScalar surfaceScale,
+ const SkIRect& bounds) {
+ SkASSERT(dst->width() == bounds.width() && dst->height() == bounds.height());
+ int left = bounds.left(), right = bounds.right();
+ int bottom = bounds.bottom();
+ int y = bounds.top();
+ SkIRect srcBounds = src.bounds();
+ SkPMColor* dptr = dst->getAddr32(0, 0);
+ {
+ int x = left;
+ int m[9];
+ m[4] = PixelFetcher::Fetch(src, x, y, srcBounds);
+ m[5] = PixelFetcher::Fetch(src, x + 1, y, srcBounds);
+ m[7] = PixelFetcher::Fetch(src, x, y + 1, srcBounds);
+ m[8] = PixelFetcher::Fetch(src, x + 1, y + 1, srcBounds);
+ SkPoint3 surfaceToLight = l->surfaceToLight(x, y, m[4], surfaceScale);
+ *dptr++ = lightingType.light(topLeftNormal(m, surfaceScale), surfaceToLight,
+ l->lightColor(surfaceToLight));
+ for (++x; x < right - 1; ++x)
+ {
+ shiftMatrixLeft(m);
+ m[5] = PixelFetcher::Fetch(src, x + 1, y, srcBounds);
+ m[8] = PixelFetcher::Fetch(src, x + 1, y + 1, srcBounds);
+ surfaceToLight = l->surfaceToLight(x, y, m[4], surfaceScale);
+ *dptr++ = lightingType.light(topNormal(m, surfaceScale), surfaceToLight,
+ l->lightColor(surfaceToLight));
+ }
+ shiftMatrixLeft(m);
+ surfaceToLight = l->surfaceToLight(x, y, m[4], surfaceScale);
+ *dptr++ = lightingType.light(topRightNormal(m, surfaceScale), surfaceToLight,
+ l->lightColor(surfaceToLight));
+ }
+
+ for (++y; y < bottom - 1; ++y) {
+ int x = left;
+ int m[9];
+ m[1] = PixelFetcher::Fetch(src, x, y - 1, srcBounds);
+ m[2] = PixelFetcher::Fetch(src, x + 1, y - 1, srcBounds);
+ m[4] = PixelFetcher::Fetch(src, x, y, srcBounds);
+ m[5] = PixelFetcher::Fetch(src, x + 1, y, srcBounds);
+ m[7] = PixelFetcher::Fetch(src, x, y + 1, srcBounds);
+ m[8] = PixelFetcher::Fetch(src, x + 1, y + 1, srcBounds);
+ SkPoint3 surfaceToLight = l->surfaceToLight(x, y, m[4], surfaceScale);
+ *dptr++ = lightingType.light(leftNormal(m, surfaceScale), surfaceToLight,
+ l->lightColor(surfaceToLight));
+ for (++x; x < right - 1; ++x) {
+ shiftMatrixLeft(m);
+ m[2] = PixelFetcher::Fetch(src, x + 1, y - 1, srcBounds);
+ m[5] = PixelFetcher::Fetch(src, x + 1, y, srcBounds);
+ m[8] = PixelFetcher::Fetch(src, x + 1, y + 1, srcBounds);
+ surfaceToLight = l->surfaceToLight(x, y, m[4], surfaceScale);
+ *dptr++ = lightingType.light(interiorNormal(m, surfaceScale), surfaceToLight,
+ l->lightColor(surfaceToLight));
+ }
+ shiftMatrixLeft(m);
+ surfaceToLight = l->surfaceToLight(x, y, m[4], surfaceScale);
+ *dptr++ = lightingType.light(rightNormal(m, surfaceScale), surfaceToLight,
+ l->lightColor(surfaceToLight));
+ }
+
+ {
+ int x = left;
+ int m[9];
+ m[1] = PixelFetcher::Fetch(src, x, bottom - 2, srcBounds);
+ m[2] = PixelFetcher::Fetch(src, x + 1, bottom - 2, srcBounds);
+ m[4] = PixelFetcher::Fetch(src, x, bottom - 1, srcBounds);
+ m[5] = PixelFetcher::Fetch(src, x + 1, bottom - 1, srcBounds);
+ SkPoint3 surfaceToLight = l->surfaceToLight(x, y, m[4], surfaceScale);
+ *dptr++ = lightingType.light(bottomLeftNormal(m, surfaceScale), surfaceToLight,
+ l->lightColor(surfaceToLight));
+ for (++x; x < right - 1; ++x)
+ {
+ shiftMatrixLeft(m);
+ m[2] = PixelFetcher::Fetch(src, x + 1, bottom - 2, srcBounds);
+ m[5] = PixelFetcher::Fetch(src, x + 1, bottom - 1, srcBounds);
+ surfaceToLight = l->surfaceToLight(x, y, m[4], surfaceScale);
+ *dptr++ = lightingType.light(bottomNormal(m, surfaceScale), surfaceToLight,
+ l->lightColor(surfaceToLight));
+ }
+ shiftMatrixLeft(m);
+ surfaceToLight = l->surfaceToLight(x, y, m[4], surfaceScale);
+ *dptr++ = lightingType.light(bottomRightNormal(m, surfaceScale), surfaceToLight,
+ l->lightColor(surfaceToLight));
+ }
+}
+
+static void lightBitmap(const BaseLightingType& lightingType,
+ const SkImageFilterLight* light,
+ const SkBitmap& src,
+ SkBitmap* dst,
+ SkScalar surfaceScale,
+ const SkIRect& bounds) {
+ if (src.bounds().contains(bounds)) {
+ lightBitmap<UncheckedPixelFetcher>(
+ lightingType, light, src, dst, surfaceScale, bounds);
+ } else {
+ lightBitmap<DecalPixelFetcher>(
+ lightingType, light, src, dst, surfaceScale, bounds);
+ }
+}
+
+enum BoundaryMode {
+ kTopLeft_BoundaryMode,
+ kTop_BoundaryMode,
+ kTopRight_BoundaryMode,
+ kLeft_BoundaryMode,
+ kInterior_BoundaryMode,
+ kRight_BoundaryMode,
+ kBottomLeft_BoundaryMode,
+ kBottom_BoundaryMode,
+ kBottomRight_BoundaryMode,
+
+ kBoundaryModeCount,
+};
+
+class SkLightingImageFilterInternal : public SkLightingImageFilter {
+protected:
+ SkLightingImageFilterInternal(sk_sp<SkImageFilterLight> light,
+ SkScalar surfaceScale,
+ sk_sp<SkImageFilter> input,
+ const CropRect* cropRect)
+ : INHERITED(std::move(light), surfaceScale, std::move(input), cropRect) {
+ }
+
+#if SK_SUPPORT_GPU
+ sk_sp<SkSpecialImage> filterImageGPU(SkSpecialImage* source,
+ SkSpecialImage* input,
+ const SkIRect& bounds,
+ const SkMatrix& matrix,
+ const OutputProperties& outputProperties) const;
+ virtual std::unique_ptr<GrFragmentProcessor> makeFragmentProcessor(
+ sk_sp<GrTextureProxy>,
+ const SkMatrix&,
+ const SkIRect* srcBounds,
+ BoundaryMode boundaryMode) const = 0;
+#endif
+private:
+#if SK_SUPPORT_GPU
+ void drawRect(GrRenderTargetContext*,
+ sk_sp<GrTextureProxy> srcProxy,
+ const SkMatrix& matrix,
+ const GrClip& clip,
+ const SkRect& dstRect,
+ BoundaryMode boundaryMode,
+ const SkIRect* srcBounds,
+ const SkIRect& bounds) const;
+#endif
+ typedef SkLightingImageFilter INHERITED;
+};
+
+#if SK_SUPPORT_GPU
+void SkLightingImageFilterInternal::drawRect(GrRenderTargetContext* renderTargetContext,
+ sk_sp<GrTextureProxy> srcProxy,
+ const SkMatrix& matrix,
+ const GrClip& clip,
+ const SkRect& dstRect,
+ BoundaryMode boundaryMode,
+ const SkIRect* srcBounds,
+ const SkIRect& bounds) const {
+ SkRect srcRect = dstRect.makeOffset(SkIntToScalar(bounds.x()), SkIntToScalar(bounds.y()));
+ GrPaint paint;
+ auto fp = this->makeFragmentProcessor(std::move(srcProxy), matrix, srcBounds, boundaryMode);
+ paint.addColorFragmentProcessor(std::move(fp));
+ paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
+ renderTargetContext->fillRectToRect(clip, std::move(paint), GrAA::kNo, SkMatrix::I(), dstRect,
+ srcRect);
+}
+
+sk_sp<SkSpecialImage> SkLightingImageFilterInternal::filterImageGPU(
+ SkSpecialImage* source,
+ SkSpecialImage* input,
+ const SkIRect& offsetBounds,
+ const SkMatrix& matrix,
+ const OutputProperties& outputProperties) const {
+ SkASSERT(source->isTextureBacked());
+
+ GrContext* context = source->getContext();
+
+ sk_sp<GrTextureProxy> inputProxy(input->asTextureProxyRef(context));
+ SkASSERT(inputProxy);
+
+
+ sk_sp<GrRenderTargetContext> renderTargetContext(
+ context->contextPriv().makeDeferredRenderTargetContext(
+ SkBackingFit::kApprox, offsetBounds.width(), offsetBounds.height(),
+ GrRenderableConfigForColorSpace(outputProperties.colorSpace()),
+ sk_ref_sp(outputProperties.colorSpace())));
+ if (!renderTargetContext) {
+ return nullptr;
+ }
+
+ SkIRect dstIRect = SkIRect::MakeWH(offsetBounds.width(), offsetBounds.height());
+ SkRect dstRect = SkRect::Make(dstIRect);
+
+ // setup new clip
+ GrFixedClip clip(dstIRect);
+
+ const SkIRect inputBounds = SkIRect::MakeWH(input->width(), input->height());
+ SkRect topLeft = SkRect::MakeXYWH(0, 0, 1, 1);
+ SkRect top = SkRect::MakeXYWH(1, 0, dstRect.width() - 2, 1);
+ SkRect topRight = SkRect::MakeXYWH(dstRect.width() - 1, 0, 1, 1);
+ SkRect left = SkRect::MakeXYWH(0, 1, 1, dstRect.height() - 2);
+ SkRect interior = dstRect.makeInset(1, 1);
+ SkRect right = SkRect::MakeXYWH(dstRect.width() - 1, 1, 1, dstRect.height() - 2);
+ SkRect bottomLeft = SkRect::MakeXYWH(0, dstRect.height() - 1, 1, 1);
+ SkRect bottom = SkRect::MakeXYWH(1, dstRect.height() - 1, dstRect.width() - 2, 1);
+ SkRect bottomRight = SkRect::MakeXYWH(dstRect.width() - 1, dstRect.height() - 1, 1, 1);
+
+ const SkIRect* pSrcBounds = inputBounds.contains(offsetBounds) ? nullptr : &inputBounds;
+ this->drawRect(renderTargetContext.get(), inputProxy, matrix, clip, topLeft,
+ kTopLeft_BoundaryMode, pSrcBounds, offsetBounds);
+ this->drawRect(renderTargetContext.get(), inputProxy, matrix, clip, top,
+ kTop_BoundaryMode, pSrcBounds, offsetBounds);
+ this->drawRect(renderTargetContext.get(), inputProxy, matrix, clip, topRight,
+ kTopRight_BoundaryMode, pSrcBounds, offsetBounds);
+ this->drawRect(renderTargetContext.get(), inputProxy, matrix, clip, left,
+ kLeft_BoundaryMode, pSrcBounds, offsetBounds);
+ this->drawRect(renderTargetContext.get(), inputProxy, matrix, clip, interior,
+ kInterior_BoundaryMode, pSrcBounds, offsetBounds);
+ this->drawRect(renderTargetContext.get(), inputProxy, matrix, clip, right,
+ kRight_BoundaryMode, pSrcBounds, offsetBounds);
+ this->drawRect(renderTargetContext.get(), inputProxy, matrix, clip, bottomLeft,
+ kBottomLeft_BoundaryMode, pSrcBounds, offsetBounds);
+ this->drawRect(renderTargetContext.get(), inputProxy, matrix, clip, bottom,
+ kBottom_BoundaryMode, pSrcBounds, offsetBounds);
+ this->drawRect(renderTargetContext.get(), inputProxy, matrix, clip, bottomRight,
+ kBottomRight_BoundaryMode, pSrcBounds, offsetBounds);
+
+ return SkSpecialImage::MakeDeferredFromGpu(
+ context,
+ SkIRect::MakeWH(offsetBounds.width(), offsetBounds.height()),
+ kNeedNewImageUniqueID_SpecialImage,
+ renderTargetContext->asTextureProxyRef(),
+ renderTargetContext->colorSpaceInfo().refColorSpace());
+}
+#endif
+
+class SkDiffuseLightingImageFilter : public SkLightingImageFilterInternal {
+public:
+ static sk_sp<SkImageFilter> Make(sk_sp<SkImageFilterLight> light,
+ SkScalar surfaceScale,
+ SkScalar kd,
+ sk_sp<SkImageFilter>,
+ const CropRect*);
+
+ SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkDiffuseLightingImageFilter)
+ SkScalar kd() const { return fKD; }
+
+protected:
+ SkDiffuseLightingImageFilter(sk_sp<SkImageFilterLight> light, SkScalar surfaceScale,
+ SkScalar kd,
+ sk_sp<SkImageFilter> input, const CropRect* cropRect);
+ void flatten(SkWriteBuffer& buffer) const override;
+
+ sk_sp<SkSpecialImage> onFilterImage(SkSpecialImage* source, const Context&,
+ SkIPoint* offset) const override;
+ sk_sp<SkImageFilter> onMakeColorSpace(SkColorSpaceXformer*) const override;
+
+#if SK_SUPPORT_GPU
+ std::unique_ptr<GrFragmentProcessor> makeFragmentProcessor(sk_sp<GrTextureProxy>,
+ const SkMatrix&,
+ const SkIRect* bounds,
+ BoundaryMode) const override;
+#endif
+
+private:
+ friend class SkLightingImageFilter;
+ SkScalar fKD;
+
+ typedef SkLightingImageFilterInternal INHERITED;
+};
+
+class SkSpecularLightingImageFilter : public SkLightingImageFilterInternal {
+public:
+ static sk_sp<SkImageFilter> Make(sk_sp<SkImageFilterLight> light,
+ SkScalar surfaceScale,
+ SkScalar ks, SkScalar shininess,
+ sk_sp<SkImageFilter>, const CropRect*);
+
+ SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkSpecularLightingImageFilter)
+
+ SkScalar ks() const { return fKS; }
+ SkScalar shininess() const { return fShininess; }
+
+protected:
+ SkSpecularLightingImageFilter(sk_sp<SkImageFilterLight> light,
+ SkScalar surfaceScale, SkScalar ks,
+ SkScalar shininess,
+ sk_sp<SkImageFilter> input, const CropRect*);
+ void flatten(SkWriteBuffer& buffer) const override;
+
+ sk_sp<SkSpecialImage> onFilterImage(SkSpecialImage* source, const Context&,
+ SkIPoint* offset) const override;
+ sk_sp<SkImageFilter> onMakeColorSpace(SkColorSpaceXformer*) const override;
+
+#if SK_SUPPORT_GPU
+ std::unique_ptr<GrFragmentProcessor> makeFragmentProcessor(sk_sp<GrTextureProxy>,
+ const SkMatrix&,
+ const SkIRect* bounds,
+ BoundaryMode) const override;
+#endif
+
+private:
+ SkScalar fKS;
+ SkScalar fShininess;
+ friend class SkLightingImageFilter;
+ typedef SkLightingImageFilterInternal INHERITED;
+};
+
+#if SK_SUPPORT_GPU
+
+class GrLightingEffect : public GrFragmentProcessor {
+public:
+ const SkImageFilterLight* light() const { return fLight.get(); }
+ SkScalar surfaceScale() const { return fSurfaceScale; }
+ const SkMatrix& filterMatrix() const { return fFilterMatrix; }
+ BoundaryMode boundaryMode() const { return fBoundaryMode; }
+ const GrTextureDomain& domain() const { return fDomain; }
+
+protected:
+ GrLightingEffect(ClassID classID, sk_sp<GrTextureProxy>, sk_sp<const SkImageFilterLight> light,
+ SkScalar surfaceScale, const SkMatrix& matrix, BoundaryMode boundaryMode,
+ const SkIRect* srcBounds);
+
+ GrLightingEffect(const GrLightingEffect& that);
+
+ bool onIsEqual(const GrFragmentProcessor&) const override;
+
+private:
+ GrCoordTransform fCoordTransform;
+ GrTextureDomain fDomain;
+ TextureSampler fTextureSampler;
+ sk_sp<const SkImageFilterLight> fLight;
+ SkScalar fSurfaceScale;
+ SkMatrix fFilterMatrix;
+ BoundaryMode fBoundaryMode;
+
+ typedef GrFragmentProcessor INHERITED;
+};
+
+class GrDiffuseLightingEffect : public GrLightingEffect {
+public:
+ static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrTextureProxy> proxy,
+ sk_sp<const SkImageFilterLight> light,
+ SkScalar surfaceScale,
+ const SkMatrix& matrix,
+ SkScalar kd,
+ BoundaryMode boundaryMode,
+ const SkIRect* srcBounds) {
+ return std::unique_ptr<GrFragmentProcessor>(
+ new GrDiffuseLightingEffect(std::move(proxy), std::move(light), surfaceScale,
+ matrix, kd, boundaryMode, srcBounds));
+ }
+
+ const char* name() const override { return "DiffuseLighting"; }
+
+ std::unique_ptr<GrFragmentProcessor> clone() const override {
+ return std::unique_ptr<GrFragmentProcessor>(new GrDiffuseLightingEffect(*this));
+ }
+
+ SkScalar kd() const { return fKD; }
+
+private:
+ GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
+
+ void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
+
+ bool onIsEqual(const GrFragmentProcessor&) const override;
+
+ GrDiffuseLightingEffect(sk_sp<GrTextureProxy>,
+ sk_sp<const SkImageFilterLight> light,
+ SkScalar surfaceScale,
+ const SkMatrix& matrix,
+ SkScalar kd,
+ BoundaryMode boundaryMode,
+ const SkIRect* srcBounds);
+
+ explicit GrDiffuseLightingEffect(const GrDiffuseLightingEffect& that);
+
+ GR_DECLARE_FRAGMENT_PROCESSOR_TEST
+ SkScalar fKD;
+
+ typedef GrLightingEffect INHERITED;
+};
+
+class GrSpecularLightingEffect : public GrLightingEffect {
+public:
+ static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrTextureProxy> proxy,
+ sk_sp<const SkImageFilterLight> light,
+ SkScalar surfaceScale,
+ const SkMatrix& matrix,
+ SkScalar ks,
+ SkScalar shininess,
+ BoundaryMode boundaryMode,
+ const SkIRect* srcBounds) {
+ return std::unique_ptr<GrFragmentProcessor>(
+ new GrSpecularLightingEffect(std::move(proxy), std::move(light), surfaceScale,
+ matrix, ks, shininess, boundaryMode, srcBounds));
+ }
+
+ const char* name() const override { return "SpecularLighting"; }
+
+ std::unique_ptr<GrFragmentProcessor> clone() const override {
+ return std::unique_ptr<GrFragmentProcessor>(new GrSpecularLightingEffect(*this));
+ }
+
+ GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
+
+ SkScalar ks() const { return fKS; }
+ SkScalar shininess() const { return fShininess; }
+
+private:
+ void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
+
+ bool onIsEqual(const GrFragmentProcessor&) const override;
+
+ GrSpecularLightingEffect(sk_sp<GrTextureProxy>,
+ sk_sp<const SkImageFilterLight> light,
+ SkScalar surfaceScale,
+ const SkMatrix& matrix,
+ SkScalar ks,
+ SkScalar shininess,
+ BoundaryMode boundaryMode,
+ const SkIRect* srcBounds);
+
+ explicit GrSpecularLightingEffect(const GrSpecularLightingEffect&);
+
+ GR_DECLARE_FRAGMENT_PROCESSOR_TEST
+ SkScalar fKS;
+ SkScalar fShininess;
+
+ typedef GrLightingEffect INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class GrGLLight {
+public:
+ virtual ~GrGLLight() {}
+
+ /**
+ * This is called by GrGLLightingEffect::emitCode() before either of the two virtual functions
+ * below. It adds a half3 uniform visible in the FS that represents the constant light color.
+ */
+ void emitLightColorUniform(GrGLSLUniformHandler*);
+
+ /**
+ * These two functions are called from GrGLLightingEffect's emitCode() function.
+ * emitSurfaceToLight places an expression in param out that is the vector from the surface to
+ * the light. The expression will be used in the FS. emitLightColor writes an expression into
+ * the FS that is the color of the light. Either function may add functions and/or uniforms to
+ * the FS. The default of emitLightColor appends the name of the constant light color uniform
+ * and so this function only needs to be overridden if the light color varies spatially.
+ */
+ virtual void emitSurfaceToLight(GrGLSLUniformHandler*,
+ GrGLSLFPFragmentBuilder*,
+ const char* z) = 0;
+ virtual void emitLightColor(GrGLSLUniformHandler*,
+ GrGLSLFPFragmentBuilder*,
+ const char *surfaceToLight);
+
+ // This is called from GrGLLightingEffect's setData(). Subclasses of GrGLLight must call
+ // INHERITED::setData().
+ virtual void setData(const GrGLSLProgramDataManager&, const SkImageFilterLight* light) const;
+
+protected:
+ /**
+ * Gets the constant light color uniform. Subclasses can use this in their emitLightColor
+ * function.
+ */
+ UniformHandle lightColorUni() const { return fColorUni; }
+
+private:
+ UniformHandle fColorUni;
+
+ typedef SkRefCnt INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class GrGLDistantLight : public GrGLLight {
+public:
+ ~GrGLDistantLight() override {}
+ void setData(const GrGLSLProgramDataManager&, const SkImageFilterLight* light) const override;
+ void emitSurfaceToLight(GrGLSLUniformHandler*, GrGLSLFPFragmentBuilder*, const char* z) override;
+
+private:
+ typedef GrGLLight INHERITED;
+ UniformHandle fDirectionUni;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class GrGLPointLight : public GrGLLight {
+public:
+ ~GrGLPointLight() override {}
+ void setData(const GrGLSLProgramDataManager&, const SkImageFilterLight* light) const override;
+ void emitSurfaceToLight(GrGLSLUniformHandler*, GrGLSLFPFragmentBuilder*, const char* z) override;
+
+private:
+ typedef GrGLLight INHERITED;
+ UniformHandle fLocationUni;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class GrGLSpotLight : public GrGLLight {
+public:
+ ~GrGLSpotLight() override {}
+ void setData(const GrGLSLProgramDataManager&, const SkImageFilterLight* light) const override;
+ void emitSurfaceToLight(GrGLSLUniformHandler*, GrGLSLFPFragmentBuilder*, const char* z) override;
+ void emitLightColor(GrGLSLUniformHandler*,
+ GrGLSLFPFragmentBuilder*,
+ const char *surfaceToLight) override;
+
+private:
+ typedef GrGLLight INHERITED;
+
+ SkString fLightColorFunc;
+ UniformHandle fLocationUni;
+ UniformHandle fExponentUni;
+ UniformHandle fCosOuterConeAngleUni;
+ UniformHandle fCosInnerConeAngleUni;
+ UniformHandle fConeScaleUni;
+ UniformHandle fSUni;
+};
+#else
+
+class GrGLLight;
+
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+
+static SkColor xform_color(const SkPoint3& color, SkColorSpaceXformer* xformer) {
+ SkColor origColor = SkColorSetRGB(SkScalarRoundToInt(color.fX),
+ SkScalarRoundToInt(color.fY),
+ SkScalarRoundToInt(color.fZ));
+ return xformer->apply(origColor);
+}
+
+class SkDistantLight : public SkImageFilterLight {
+public:
+ SkDistantLight(const SkPoint3& direction, SkColor color)
+ : INHERITED(color), fDirection(direction) {
+ }
+
+ SkPoint3 surfaceToLight(int x, int y, int z, SkScalar surfaceScale) const override {
+ return fDirection;
+ }
+ SkPoint3 lightColor(const SkPoint3&) const override { return this->color(); }
+ LightType type() const override { return kDistant_LightType; }
+ const SkPoint3& direction() const { return fDirection; }
+ GrGLLight* createGLLight() const override {
+#if SK_SUPPORT_GPU
+ return new GrGLDistantLight;
+#else
+ SkDEBUGFAIL("Should not call in GPU-less build");
+ return nullptr;
+#endif
+ }
+
+ sk_sp<SkImageFilterLight> makeColorSpace(SkColorSpaceXformer* xformer) const override {
+ return sk_make_sp<SkDistantLight>(fDirection, xform_color(this->color(), xformer));
+ }
+
+ bool isEqual(const SkImageFilterLight& other) const override {
+ if (other.type() != kDistant_LightType) {
+ return false;
+ }
+
+ const SkDistantLight& o = static_cast<const SkDistantLight&>(other);
+ return INHERITED::isEqual(other) &&
+ fDirection == o.fDirection;
+ }
+
+ SkDistantLight(SkReadBuffer& buffer) : INHERITED(buffer) {
+ fDirection = read_point3(buffer);
+ }
+
+protected:
+ SkDistantLight(const SkPoint3& direction, const SkPoint3& color)
+ : INHERITED(color), fDirection(direction) {
+ }
+ SkImageFilterLight* transform(const SkMatrix& matrix) const override {
+ return new SkDistantLight(direction(), color());
+ }
+ void onFlattenLight(SkWriteBuffer& buffer) const override {
+ write_point3(fDirection, buffer);
+ }
+
+private:
+ SkPoint3 fDirection;
+
+ typedef SkImageFilterLight INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SkPointLight : public SkImageFilterLight {
+public:
+ SkPointLight(const SkPoint3& location, SkColor color)
+ : INHERITED(color), fLocation(location) {}
+
+ SkPoint3 surfaceToLight(int x, int y, int z, SkScalar surfaceScale) const override {
+ SkPoint3 direction = SkPoint3::Make(fLocation.fX - SkIntToScalar(x),
+ fLocation.fY - SkIntToScalar(y),
+ fLocation.fZ - SkIntToScalar(z) * surfaceScale);
+ fast_normalize(&direction);
+ return direction;
+ }
+ SkPoint3 lightColor(const SkPoint3&) const override { return this->color(); }
+ LightType type() const override { return kPoint_LightType; }
+ const SkPoint3& location() const { return fLocation; }
+ GrGLLight* createGLLight() const override {
+#if SK_SUPPORT_GPU
+ return new GrGLPointLight;
+#else
+ SkDEBUGFAIL("Should not call in GPU-less build");
+ return nullptr;
+#endif
+ }
+
+ sk_sp<SkImageFilterLight> makeColorSpace(SkColorSpaceXformer* xformer) const override {
+ return sk_make_sp<SkPointLight>(fLocation, xform_color(this->color(), xformer));
+ }
+
+ bool isEqual(const SkImageFilterLight& other) const override {
+ if (other.type() != kPoint_LightType) {
+ return false;
+ }
+ const SkPointLight& o = static_cast<const SkPointLight&>(other);
+ return INHERITED::isEqual(other) &&
+ fLocation == o.fLocation;
+ }
+ SkImageFilterLight* transform(const SkMatrix& matrix) const override {
+ SkPoint location2 = SkPoint::Make(fLocation.fX, fLocation.fY);
+ matrix.mapPoints(&location2, 1);
+ // Use X scale and Y scale on Z and average the result
+ SkPoint locationZ = SkPoint::Make(fLocation.fZ, fLocation.fZ);
+ matrix.mapVectors(&locationZ, 1);
+ SkPoint3 location = SkPoint3::Make(location2.fX,
+ location2.fY,
+ SkScalarAve(locationZ.fX, locationZ.fY));
+ return new SkPointLight(location, color());
+ }
+
+ SkPointLight(SkReadBuffer& buffer) : INHERITED(buffer) {
+ fLocation = read_point3(buffer);
+ }
+
+protected:
+ SkPointLight(const SkPoint3& location, const SkPoint3& color)
+ : INHERITED(color), fLocation(location) {}
+ void onFlattenLight(SkWriteBuffer& buffer) const override {
+ write_point3(fLocation, buffer);
+ }
+
+private:
+ SkPoint3 fLocation;
+
+ typedef SkImageFilterLight INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SkSpotLight : public SkImageFilterLight {
+public:
+ SkSpotLight(const SkPoint3& location,
+ const SkPoint3& target,
+ SkScalar specularExponent,
+ SkScalar cutoffAngle,
+ SkColor color)
+ : INHERITED(color),
+ fLocation(location),
+ fTarget(target),
+ fSpecularExponent(SkScalarPin(specularExponent, kSpecularExponentMin, kSpecularExponentMax)),
+ fCutoffAngle(cutoffAngle)
+ {
+ fS = target - location;
+ fast_normalize(&fS);
+ fCosOuterConeAngle = SkScalarCos(SkDegreesToRadians(cutoffAngle));
+ const SkScalar antiAliasThreshold = 0.016f;
+ fCosInnerConeAngle = fCosOuterConeAngle + antiAliasThreshold;
+ fConeScale = SkScalarInvert(antiAliasThreshold);
+ }
+
+ sk_sp<SkImageFilterLight> makeColorSpace(SkColorSpaceXformer* xformer) const override {
+ return sk_make_sp<SkSpotLight>(fLocation, fTarget, fSpecularExponent, fCutoffAngle,
+ xform_color(this->color(), xformer));
+ }
+
+ SkImageFilterLight* transform(const SkMatrix& matrix) const override {
+ SkPoint location2 = SkPoint::Make(fLocation.fX, fLocation.fY);
+ matrix.mapPoints(&location2, 1);
+ // Use X scale and Y scale on Z and average the result
+ SkPoint locationZ = SkPoint::Make(fLocation.fZ, fLocation.fZ);
+ matrix.mapVectors(&locationZ, 1);
+ SkPoint3 location = SkPoint3::Make(location2.fX, location2.fY,
+ SkScalarAve(locationZ.fX, locationZ.fY));
+ SkPoint target2 = SkPoint::Make(fTarget.fX, fTarget.fY);
+ matrix.mapPoints(&target2, 1);
+ SkPoint targetZ = SkPoint::Make(fTarget.fZ, fTarget.fZ);
+ matrix.mapVectors(&targetZ, 1);
+ SkPoint3 target = SkPoint3::Make(target2.fX, target2.fY,
+ SkScalarAve(targetZ.fX, targetZ.fY));
+ SkPoint3 s = target - location;
+ fast_normalize(&s);
+ return new SkSpotLight(location,
+ target,
+ fSpecularExponent,
+ fCosOuterConeAngle,
+ fCosInnerConeAngle,
+ fConeScale,
+ s,
+ color());
+ }
+
+ SkPoint3 surfaceToLight(int x, int y, int z, SkScalar surfaceScale) const override {
+ SkPoint3 direction = SkPoint3::Make(fLocation.fX - SkIntToScalar(x),
+ fLocation.fY - SkIntToScalar(y),
+ fLocation.fZ - SkIntToScalar(z) * surfaceScale);
+ fast_normalize(&direction);
+ return direction;
+ }
+ SkPoint3 lightColor(const SkPoint3& surfaceToLight) const override {
+ SkScalar cosAngle = -surfaceToLight.dot(fS);
+ SkScalar scale = 0;
+ if (cosAngle >= fCosOuterConeAngle) {
+ scale = SkScalarPow(cosAngle, fSpecularExponent);
+ if (cosAngle < fCosInnerConeAngle) {
+ scale *= (cosAngle - fCosOuterConeAngle) * fConeScale;
+ }
+ }
+ return this->color().makeScale(scale);
+ }
+ GrGLLight* createGLLight() const override {
+#if SK_SUPPORT_GPU
+ return new GrGLSpotLight;
+#else
+ SkDEBUGFAIL("Should not call in GPU-less build");
+ return nullptr;
+#endif
+ }
+ LightType type() const override { return kSpot_LightType; }
+ const SkPoint3& location() const { return fLocation; }
+ const SkPoint3& target() const { return fTarget; }
+ SkScalar specularExponent() const { return fSpecularExponent; }
+ SkScalar cosInnerConeAngle() const { return fCosInnerConeAngle; }
+ SkScalar cosOuterConeAngle() const { return fCosOuterConeAngle; }
+ SkScalar coneScale() const { return fConeScale; }
+ const SkPoint3& s() const { return fS; }
+
+ SkSpotLight(SkReadBuffer& buffer) : INHERITED(buffer) {
+ fLocation = read_point3(buffer);
+ fTarget = read_point3(buffer);
+ fSpecularExponent = buffer.readScalar();
+ fCosOuterConeAngle = buffer.readScalar();
+ fCosInnerConeAngle = buffer.readScalar();
+ fConeScale = buffer.readScalar();
+ fS = read_point3(buffer);
+ buffer.validate(SkScalarIsFinite(fSpecularExponent) &&
+ SkScalarIsFinite(fCosOuterConeAngle) &&
+ SkScalarIsFinite(fCosInnerConeAngle) &&
+ SkScalarIsFinite(fConeScale));
+ }
+protected:
+ SkSpotLight(const SkPoint3& location,
+ const SkPoint3& target,
+ SkScalar specularExponent,
+ SkScalar cosOuterConeAngle,
+ SkScalar cosInnerConeAngle,
+ SkScalar coneScale,
+ const SkPoint3& s,
+ const SkPoint3& color)
+ : INHERITED(color),
+ fLocation(location),
+ fTarget(target),
+ fSpecularExponent(specularExponent),
+ fCosOuterConeAngle(cosOuterConeAngle),
+ fCosInnerConeAngle(cosInnerConeAngle),
+ fConeScale(coneScale),
+ fS(s)
+ {
+ }
+ void onFlattenLight(SkWriteBuffer& buffer) const override {
+ write_point3(fLocation, buffer);
+ write_point3(fTarget, buffer);
+ buffer.writeScalar(fSpecularExponent);
+ buffer.writeScalar(fCosOuterConeAngle);
+ buffer.writeScalar(fCosInnerConeAngle);
+ buffer.writeScalar(fConeScale);
+ write_point3(fS, buffer);
+ }
+
+ bool isEqual(const SkImageFilterLight& other) const override {
+ if (other.type() != kSpot_LightType) {
+ return false;
+ }
+
+ const SkSpotLight& o = static_cast<const SkSpotLight&>(other);
+ return INHERITED::isEqual(other) &&
+ fLocation == o.fLocation &&
+ fTarget == o.fTarget &&
+ fSpecularExponent == o.fSpecularExponent &&
+ fCosOuterConeAngle == o.fCosOuterConeAngle;
+ }
+
+private:
+ static const SkScalar kSpecularExponentMin;
+ static const SkScalar kSpecularExponentMax;
+
+ SkPoint3 fLocation;
+ SkPoint3 fTarget;
+ SkScalar fSpecularExponent;
+ SkScalar fCutoffAngle;
+ SkScalar fCosOuterConeAngle;
+ SkScalar fCosInnerConeAngle;
+ SkScalar fConeScale;
+ SkPoint3 fS;
+
+ typedef SkImageFilterLight INHERITED;
+};
+
+// According to the spec, the specular term should be in the range [1, 128] :
+// http://www.w3.org/TR/SVG/filters.html#feSpecularLightingSpecularExponentAttribute
+const SkScalar SkSpotLight::kSpecularExponentMin = 1.0f;
+const SkScalar SkSpotLight::kSpecularExponentMax = 128.0f;
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkImageFilterLight::flattenLight(SkWriteBuffer& buffer) const {
+ // Write type first, then baseclass, then subclass.
+ buffer.writeInt(this->type());
+ write_point3(fColor, buffer);
+ this->onFlattenLight(buffer);
+}
+
+/*static*/ SkImageFilterLight* SkImageFilterLight::UnflattenLight(SkReadBuffer& buffer) {
+ SkImageFilterLight::LightType type = buffer.read32LE(SkImageFilterLight::kLast_LightType);
+
+ switch (type) {
+ // Each of these constructors must first call SkLight's, so we'll read the baseclass
+ // then subclass, same order as flattenLight.
+ case SkImageFilterLight::kDistant_LightType:
+ return new SkDistantLight(buffer);
+ case SkImageFilterLight::kPoint_LightType:
+ return new SkPointLight(buffer);
+ case SkImageFilterLight::kSpot_LightType:
+ return new SkSpotLight(buffer);
+ default:
+ // Should never get here due to prior check of SkSafeRange
+ SkDEBUGFAIL("Unknown LightType.");
+ return nullptr;
+ }
+}
+///////////////////////////////////////////////////////////////////////////////
+
+SkLightingImageFilter::SkLightingImageFilter(sk_sp<SkImageFilterLight> light,
+ SkScalar surfaceScale,
+ sk_sp<SkImageFilter> input, const CropRect* cropRect)
+ : INHERITED(&input, 1, cropRect)
+ , fLight(std::move(light))
+ , fSurfaceScale(surfaceScale / 255) {
+}
+
+SkLightingImageFilter::~SkLightingImageFilter() {}
+
+sk_sp<SkImageFilter> SkLightingImageFilter::MakeDistantLitDiffuse(const SkPoint3& direction,
+ SkColor lightColor,
+ SkScalar surfaceScale,
+ SkScalar kd,
+ sk_sp<SkImageFilter> input,
+ const CropRect* cropRect) {
+ sk_sp<SkImageFilterLight> light(new SkDistantLight(direction, lightColor));
+ return SkDiffuseLightingImageFilter::Make(std::move(light), surfaceScale, kd,
+ std::move(input), cropRect);
+}
+
+sk_sp<SkImageFilter> SkLightingImageFilter::MakePointLitDiffuse(const SkPoint3& location,
+ SkColor lightColor,
+ SkScalar surfaceScale,
+ SkScalar kd,
+ sk_sp<SkImageFilter> input,
+ const CropRect* cropRect) {
+ sk_sp<SkImageFilterLight> light(new SkPointLight(location, lightColor));
+ return SkDiffuseLightingImageFilter::Make(std::move(light), surfaceScale, kd,
+ std::move(input), cropRect);
+}
+
+sk_sp<SkImageFilter> SkLightingImageFilter::MakeSpotLitDiffuse(const SkPoint3& location,
+ const SkPoint3& target,
+ SkScalar specularExponent,
+ SkScalar cutoffAngle,
+ SkColor lightColor,
+ SkScalar surfaceScale,
+ SkScalar kd,
+ sk_sp<SkImageFilter> input,
+ const CropRect* cropRect) {
+ sk_sp<SkImageFilterLight> light(
+ new SkSpotLight(location, target, specularExponent, cutoffAngle, lightColor));
+ return SkDiffuseLightingImageFilter::Make(std::move(light), surfaceScale, kd,
+ std::move(input), cropRect);
+}
+
+sk_sp<SkImageFilter> SkLightingImageFilter::MakeDistantLitSpecular(const SkPoint3& direction,
+ SkColor lightColor,
+ SkScalar surfaceScale,
+ SkScalar ks,
+ SkScalar shine,
+ sk_sp<SkImageFilter> input,
+ const CropRect* cropRect) {
+ sk_sp<SkImageFilterLight> light(new SkDistantLight(direction, lightColor));
+ return SkSpecularLightingImageFilter::Make(std::move(light), surfaceScale, ks, shine,
+ std::move(input), cropRect);
+}
+
+sk_sp<SkImageFilter> SkLightingImageFilter::MakePointLitSpecular(const SkPoint3& location,
+ SkColor lightColor,
+ SkScalar surfaceScale,
+ SkScalar ks,
+ SkScalar shine,
+ sk_sp<SkImageFilter> input,
+ const CropRect* cropRect) {
+ sk_sp<SkImageFilterLight> light(new SkPointLight(location, lightColor));
+ return SkSpecularLightingImageFilter::Make(std::move(light), surfaceScale, ks, shine,
+ std::move(input), cropRect);
+}
+
+sk_sp<SkImageFilter> SkLightingImageFilter::MakeSpotLitSpecular(const SkPoint3& location,
+ const SkPoint3& target,
+ SkScalar specularExponent,
+ SkScalar cutoffAngle,
+ SkColor lightColor,
+ SkScalar surfaceScale,
+ SkScalar ks,
+ SkScalar shine,
+ sk_sp<SkImageFilter> input,
+ const CropRect* cropRect) {
+ sk_sp<SkImageFilterLight> light(
+ new SkSpotLight(location, target, specularExponent, cutoffAngle, lightColor));
+ return SkSpecularLightingImageFilter::Make(std::move(light), surfaceScale, ks, shine,
+ std::move(input), cropRect);
+}
+
+void SkLightingImageFilter::flatten(SkWriteBuffer& buffer) const {
+ this->INHERITED::flatten(buffer);
+ fLight->flattenLight(buffer);
+ buffer.writeScalar(fSurfaceScale * 255);
+}
+
+sk_sp<const SkImageFilterLight> SkLightingImageFilter::refLight() const { return fLight; }
+
+///////////////////////////////////////////////////////////////////////////////
+
+sk_sp<SkImageFilter> SkDiffuseLightingImageFilter::Make(sk_sp<SkImageFilterLight> light,
+ SkScalar surfaceScale,
+ SkScalar kd,
+ sk_sp<SkImageFilter> input,
+ const CropRect* cropRect) {
+ if (!light) {
+ return nullptr;
+ }
+ if (!SkScalarIsFinite(surfaceScale) || !SkScalarIsFinite(kd)) {
+ return nullptr;
+ }
+ // According to the spec, kd can be any non-negative number :
+ // http://www.w3.org/TR/SVG/filters.html#feDiffuseLightingElement
+ if (kd < 0) {
+ return nullptr;
+ }
+ return sk_sp<SkImageFilter>(new SkDiffuseLightingImageFilter(std::move(light), surfaceScale,
+ kd, std::move(input), cropRect));
+}
+
+SkDiffuseLightingImageFilter::SkDiffuseLightingImageFilter(sk_sp<SkImageFilterLight> light,
+ SkScalar surfaceScale,
+ SkScalar kd,
+ sk_sp<SkImageFilter> input,
+ const CropRect* cropRect)
+ : INHERITED(std::move(light), surfaceScale, std::move(input), cropRect)
+ , fKD(kd) {
+}
+
+sk_sp<SkFlattenable> SkDiffuseLightingImageFilter::CreateProc(SkReadBuffer& buffer) {
+ SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1);
+
+ sk_sp<SkImageFilterLight> light(SkImageFilterLight::UnflattenLight(buffer));
+ SkScalar surfaceScale = buffer.readScalar();
+ SkScalar kd = buffer.readScalar();
+
+ return Make(std::move(light), surfaceScale, kd, common.getInput(0), &common.cropRect());
+}
+
+void SkDiffuseLightingImageFilter::flatten(SkWriteBuffer& buffer) const {
+ this->INHERITED::flatten(buffer);
+ buffer.writeScalar(fKD);
+}
+
+sk_sp<SkSpecialImage> SkDiffuseLightingImageFilter::onFilterImage(SkSpecialImage* source,
+ const Context& ctx,
+ SkIPoint* offset) const {
+ SkIPoint inputOffset = SkIPoint::Make(0, 0);
+ sk_sp<SkSpecialImage> input(this->filterInput(0, source, ctx, &inputOffset));
+ if (!input) {
+ return nullptr;
+ }
+
+ const SkIRect inputBounds = SkIRect::MakeXYWH(inputOffset.x(), inputOffset.y(),
+ input->width(), input->height());
+ SkIRect bounds;
+ if (!this->applyCropRect(ctx, inputBounds, &bounds)) {
+ return nullptr;
+ }
+
+ offset->fX = bounds.left();
+ offset->fY = bounds.top();
+ bounds.offset(-inputOffset);
+
+#if SK_SUPPORT_GPU
+ if (source->isTextureBacked()) {
+ SkMatrix matrix(ctx.ctm());
+ matrix.postTranslate(SkIntToScalar(-offset->fX), SkIntToScalar(-offset->fY));
+
+ return this->filterImageGPU(source, input.get(), bounds, matrix, ctx.outputProperties());
+ }
+#endif
+
+ if (bounds.width() < 2 || bounds.height() < 2) {
+ return nullptr;
+ }
+
+ SkBitmap inputBM;
+
+ if (!input->getROPixels(&inputBM)) {
+ return nullptr;
+ }
+
+ if (inputBM.colorType() != kN32_SkColorType) {
+ return nullptr;
+ }
+
+ if (!inputBM.getPixels()) {
+ return nullptr;
+ }
+
+ const SkImageInfo info = SkImageInfo::MakeN32Premul(bounds.width(), bounds.height());
+
+ SkBitmap dst;
+ if (!dst.tryAllocPixels(info)) {
+ return nullptr;
+ }
+
+ SkMatrix matrix(ctx.ctm());
+ matrix.postTranslate(SkIntToScalar(-inputOffset.x()), SkIntToScalar(-inputOffset.y()));
+
+ sk_sp<SkImageFilterLight> transformedLight(light()->transform(matrix));
+
+ DiffuseLightingType lightingType(fKD);
+ lightBitmap(lightingType,
+ transformedLight.get(),
+ inputBM,
+ &dst,
+ surfaceScale(),
+ bounds);
+
+ return SkSpecialImage::MakeFromRaster(SkIRect::MakeWH(bounds.width(), bounds.height()),
+ dst);
+}
+
+sk_sp<SkImageFilter> SkDiffuseLightingImageFilter::onMakeColorSpace(SkColorSpaceXformer* xformer)
+const {
+ SkASSERT(1 == this->countInputs());
+ auto input = xformer->apply(this->getInput(0));
+ auto light = this->light()->makeColorSpace(xformer);
+ if (input.get() != this->getInput(0) || light.get() != this->light()) {
+ return SkDiffuseLightingImageFilter::Make(std::move(light), 255.0f * this->surfaceScale(),
+ fKD, std::move(input), this->getCropRectIfSet());
+ }
+ return this->refMe();
+}
+
+#if SK_SUPPORT_GPU
+std::unique_ptr<GrFragmentProcessor> SkDiffuseLightingImageFilter::makeFragmentProcessor(
+ sk_sp<GrTextureProxy> proxy,
+ const SkMatrix& matrix,
+ const SkIRect* srcBounds,
+ BoundaryMode boundaryMode) const {
+ SkScalar scale = this->surfaceScale() * 255;
+ return GrDiffuseLightingEffect::Make(std::move(proxy), this->refLight(), scale, matrix,
+ this->kd(), boundaryMode, srcBounds);
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+sk_sp<SkImageFilter> SkSpecularLightingImageFilter::Make(sk_sp<SkImageFilterLight> light,
+ SkScalar surfaceScale,
+ SkScalar ks,
+ SkScalar shininess,
+ sk_sp<SkImageFilter> input,
+ const CropRect* cropRect) {
+ if (!light) {
+ return nullptr;
+ }
+ if (!SkScalarIsFinite(surfaceScale) || !SkScalarIsFinite(ks) || !SkScalarIsFinite(shininess)) {
+ return nullptr;
+ }
+ // According to the spec, ks can be any non-negative number :
+ // http://www.w3.org/TR/SVG/filters.html#feSpecularLightingElement
+ if (ks < 0) {
+ return nullptr;
+ }
+ return sk_sp<SkImageFilter>(new SkSpecularLightingImageFilter(std::move(light), surfaceScale,
+ ks, shininess,
+ std::move(input), cropRect));
+}
+
+SkSpecularLightingImageFilter::SkSpecularLightingImageFilter(sk_sp<SkImageFilterLight> light,
+ SkScalar surfaceScale,
+ SkScalar ks,
+ SkScalar shininess,
+ sk_sp<SkImageFilter> input,
+ const CropRect* cropRect)
+ : INHERITED(std::move(light), surfaceScale, std::move(input), cropRect)
+ , fKS(ks)
+ , fShininess(shininess) {
+}
+
+sk_sp<SkFlattenable> SkSpecularLightingImageFilter::CreateProc(SkReadBuffer& buffer) {
+ SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1);
+ sk_sp<SkImageFilterLight> light(SkImageFilterLight::UnflattenLight(buffer));
+ SkScalar surfaceScale = buffer.readScalar();
+ SkScalar ks = buffer.readScalar();
+ SkScalar shine = buffer.readScalar();
+
+ return Make(std::move(light), surfaceScale, ks, shine, common.getInput(0),
+ &common.cropRect());
+}
+
+void SkSpecularLightingImageFilter::flatten(SkWriteBuffer& buffer) const {
+ this->INHERITED::flatten(buffer);
+ buffer.writeScalar(fKS);
+ buffer.writeScalar(fShininess);
+}
+
+sk_sp<SkSpecialImage> SkSpecularLightingImageFilter::onFilterImage(SkSpecialImage* source,
+ const Context& ctx,
+ SkIPoint* offset) const {
+ SkIPoint inputOffset = SkIPoint::Make(0, 0);
+ sk_sp<SkSpecialImage> input(this->filterInput(0, source, ctx, &inputOffset));
+ if (!input) {
+ return nullptr;
+ }
+
+ const SkIRect inputBounds = SkIRect::MakeXYWH(inputOffset.x(), inputOffset.y(),
+ input->width(), input->height());
+ SkIRect bounds;
+ if (!this->applyCropRect(ctx, inputBounds, &bounds)) {
+ return nullptr;
+ }
+
+ offset->fX = bounds.left();
+ offset->fY = bounds.top();
+ bounds.offset(-inputOffset);
+
+#if SK_SUPPORT_GPU
+ if (source->isTextureBacked()) {
+ SkMatrix matrix(ctx.ctm());
+ matrix.postTranslate(SkIntToScalar(-offset->fX), SkIntToScalar(-offset->fY));
+
+ return this->filterImageGPU(source, input.get(), bounds, matrix, ctx.outputProperties());
+ }
+#endif
+
+ if (bounds.width() < 2 || bounds.height() < 2) {
+ return nullptr;
+ }
+
+ SkBitmap inputBM;
+
+ if (!input->getROPixels(&inputBM)) {
+ return nullptr;
+ }
+
+ if (inputBM.colorType() != kN32_SkColorType) {
+ return nullptr;
+ }
+
+ if (!inputBM.getPixels()) {
+ return nullptr;
+ }
+
+ const SkImageInfo info = SkImageInfo::MakeN32Premul(bounds.width(), bounds.height());
+
+ SkBitmap dst;
+ if (!dst.tryAllocPixels(info)) {
+ return nullptr;
+ }
+
+ SpecularLightingType lightingType(fKS, fShininess);
+
+ SkMatrix matrix(ctx.ctm());
+ matrix.postTranslate(SkIntToScalar(-inputOffset.x()), SkIntToScalar(-inputOffset.y()));
+
+ sk_sp<SkImageFilterLight> transformedLight(light()->transform(matrix));
+
+ lightBitmap(lightingType,
+ transformedLight.get(),
+ inputBM,
+ &dst,
+ surfaceScale(),
+ bounds);
+
+ return SkSpecialImage::MakeFromRaster(SkIRect::MakeWH(bounds.width(), bounds.height()), dst);
+}
+
+sk_sp<SkImageFilter> SkSpecularLightingImageFilter::onMakeColorSpace(SkColorSpaceXformer* xformer)
+const {
+ SkASSERT(1 == this->countInputs());
+
+ auto input = xformer->apply(this->getInput(0));
+ auto light = this->light()->makeColorSpace(xformer);
+ if (input.get() != this->getInput(0) || light.get() != this->light()) {
+ return SkSpecularLightingImageFilter::Make(std::move(light),
+ 255.0f * this->surfaceScale(), fKS, fShininess,
+ std::move(input), this->getCropRectIfSet());
+ }
+ return this->refMe();
+}
+
+#if SK_SUPPORT_GPU
+std::unique_ptr<GrFragmentProcessor> SkSpecularLightingImageFilter::makeFragmentProcessor(
+ sk_sp<GrTextureProxy> proxy,
+ const SkMatrix& matrix,
+ const SkIRect* srcBounds,
+ BoundaryMode boundaryMode) const {
+ SkScalar scale = this->surfaceScale() * 255;
+ return GrSpecularLightingEffect::Make(std::move(proxy), this->refLight(), scale, matrix,
+ this->ks(), this->shininess(), boundaryMode, srcBounds);
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+#if SK_SUPPORT_GPU
+
+static SkString emitNormalFunc(BoundaryMode mode,
+ const char* pointToNormalName,
+ const char* sobelFuncName) {
+ SkString result;
+ switch (mode) {
+ case kTopLeft_BoundaryMode:
+ result.printf("\treturn %s(%s(0.0, 0.0, m[4], m[5], m[7], m[8], %g),\n"
+ "\t %s(0.0, 0.0, m[4], m[7], m[5], m[8], %g),\n"
+ "\t surfaceScale);\n",
+ pointToNormalName, sobelFuncName, gTwoThirds,
+ sobelFuncName, gTwoThirds);
+ break;
+ case kTop_BoundaryMode:
+ result.printf("\treturn %s(%s(0.0, 0.0, m[3], m[5], m[6], m[8], %g),\n"
+ "\t %s(0.0, 0.0, m[4], m[7], m[5], m[8], %g),\n"
+ "\t surfaceScale);\n",
+ pointToNormalName, sobelFuncName, gOneThird,
+ sobelFuncName, gOneHalf);
+ break;
+ case kTopRight_BoundaryMode:
+ result.printf("\treturn %s(%s( 0.0, 0.0, m[3], m[4], m[6], m[7], %g),\n"
+ "\t %s(m[3], m[6], m[4], m[7], 0.0, 0.0, %g),\n"
+ "\t surfaceScale);\n",
+ pointToNormalName, sobelFuncName, gTwoThirds,
+ sobelFuncName, gTwoThirds);
+ break;
+ case kLeft_BoundaryMode:
+ result.printf("\treturn %s(%s(m[1], m[2], m[4], m[5], m[7], m[8], %g),\n"
+ "\t %s( 0.0, 0.0, m[1], m[7], m[2], m[8], %g),\n"
+ "\t surfaceScale);\n",
+ pointToNormalName, sobelFuncName, gOneHalf,
+ sobelFuncName, gOneThird);
+ break;
+ case kInterior_BoundaryMode:
+ result.printf("\treturn %s(%s(m[0], m[2], m[3], m[5], m[6], m[8], %g),\n"
+ "\t %s(m[0], m[6], m[1], m[7], m[2], m[8], %g),\n"
+ "\t surfaceScale);\n",
+ pointToNormalName, sobelFuncName, gOneQuarter,
+ sobelFuncName, gOneQuarter);
+ break;
+ case kRight_BoundaryMode:
+ result.printf("\treturn %s(%s(m[0], m[1], m[3], m[4], m[6], m[7], %g),\n"
+ "\t %s(m[0], m[6], m[1], m[7], 0.0, 0.0, %g),\n"
+ "\t surfaceScale);\n",
+ pointToNormalName, sobelFuncName, gOneHalf,
+ sobelFuncName, gOneThird);
+ break;
+ case kBottomLeft_BoundaryMode:
+ result.printf("\treturn %s(%s(m[1], m[2], m[4], m[5], 0.0, 0.0, %g),\n"
+ "\t %s( 0.0, 0.0, m[1], m[4], m[2], m[5], %g),\n"
+ "\t surfaceScale);\n",
+ pointToNormalName, sobelFuncName, gTwoThirds,
+ sobelFuncName, gTwoThirds);
+ break;
+ case kBottom_BoundaryMode:
+ result.printf("\treturn %s(%s(m[0], m[2], m[3], m[5], 0.0, 0.0, %g),\n"
+ "\t %s(m[0], m[3], m[1], m[4], m[2], m[5], %g),\n"
+ "\t surfaceScale);\n",
+ pointToNormalName, sobelFuncName, gOneThird,
+ sobelFuncName, gOneHalf);
+ break;
+ case kBottomRight_BoundaryMode:
+ result.printf("\treturn %s(%s(m[0], m[1], m[3], m[4], 0.0, 0.0, %g),\n"
+ "\t %s(m[0], m[3], m[1], m[4], 0.0, 0.0, %g),\n"
+ "\t surfaceScale);\n",
+ pointToNormalName, sobelFuncName, gTwoThirds,
+ sobelFuncName, gTwoThirds);
+ break;
+ default:
+ SkASSERT(false);
+ break;
+ }
+ return result;
+}
+
+class GrGLLightingEffect : public GrGLSLFragmentProcessor {
+public:
+ GrGLLightingEffect() : fLight(nullptr) { }
+ ~GrGLLightingEffect() override { delete fLight; }
+
+ void emitCode(EmitArgs&) override;
+
+ static inline void GenKey(const GrProcessor&, const GrShaderCaps&, GrProcessorKeyBuilder* b);
+
+protected:
+ /**
+ * Subclasses of GrGLLightingEffect must call INHERITED::onSetData();
+ */
+ void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override;
+
+ virtual void emitLightFunc(GrGLSLUniformHandler*,
+ GrGLSLFPFragmentBuilder*,
+ SkString* funcName) = 0;
+
+private:
+ typedef GrGLSLFragmentProcessor INHERITED;
+
+ UniformHandle fImageIncrementUni;
+ UniformHandle fSurfaceScaleUni;
+ GrTextureDomain::GLDomain fDomain;
+ GrGLLight* fLight;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class GrGLDiffuseLightingEffect : public GrGLLightingEffect {
+public:
+ void emitLightFunc(GrGLSLUniformHandler*, GrGLSLFPFragmentBuilder*, SkString* funcName) override;
+
+protected:
+ void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override;
+
+private:
+ typedef GrGLLightingEffect INHERITED;
+
+ UniformHandle fKDUni;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class GrGLSpecularLightingEffect : public GrGLLightingEffect {
+public:
+ void emitLightFunc(GrGLSLUniformHandler*, GrGLSLFPFragmentBuilder*, SkString* funcName) override;
+
+protected:
+ void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override;
+
+private:
+ typedef GrGLLightingEffect INHERITED;
+
+ UniformHandle fKSUni;
+ UniformHandle fShininessUni;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static GrTextureDomain create_domain(GrTextureProxy* proxy, const SkIRect* srcBounds,
+ GrTextureDomain::Mode mode) {
+ if (srcBounds) {
+ SkRect texelDomain = GrTextureDomain::MakeTexelDomainForMode(*srcBounds, mode);
+ return GrTextureDomain(proxy, texelDomain, mode);
+ } else {
+ return GrTextureDomain::IgnoredDomain();
+ }
+}
+
+GrLightingEffect::GrLightingEffect(ClassID classID,
+ sk_sp<GrTextureProxy> proxy,
+ sk_sp<const SkImageFilterLight> light,
+ SkScalar surfaceScale,
+ const SkMatrix& matrix,
+ BoundaryMode boundaryMode,
+ const SkIRect* srcBounds)
+ // Perhaps this could advertise the opaque or coverage-as-alpha optimizations?
+ : INHERITED(classID, kNone_OptimizationFlags)
+ , fCoordTransform(proxy.get())
+ , fDomain(create_domain(proxy.get(), srcBounds, GrTextureDomain::kDecal_Mode))
+ , fTextureSampler(std::move(proxy))
+ , fLight(std::move(light))
+ , fSurfaceScale(surfaceScale)
+ , fFilterMatrix(matrix)
+ , fBoundaryMode(boundaryMode) {
+ this->addCoordTransform(&fCoordTransform);
+ this->addTextureSampler(&fTextureSampler);
+}
+
+GrLightingEffect::GrLightingEffect(const GrLightingEffect& that)
+ : INHERITED(that.classID(), that.optimizationFlags())
+ , fCoordTransform(that.fCoordTransform)
+ , fDomain(that.fDomain)
+ , fTextureSampler(that.fTextureSampler)
+ , fLight(that.fLight)
+ , fSurfaceScale(that.fSurfaceScale)
+ , fFilterMatrix(that.fFilterMatrix)
+ , fBoundaryMode(that.fBoundaryMode) {
+ this->addCoordTransform(&fCoordTransform);
+ this->addTextureSampler(&fTextureSampler);
+}
+
+bool GrLightingEffect::onIsEqual(const GrFragmentProcessor& sBase) const {
+ const GrLightingEffect& s = sBase.cast<GrLightingEffect>();
+ return fLight->isEqual(*s.fLight) &&
+ fSurfaceScale == s.fSurfaceScale &&
+ fBoundaryMode == s.fBoundaryMode;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrDiffuseLightingEffect::GrDiffuseLightingEffect(sk_sp<GrTextureProxy> proxy,
+ sk_sp<const SkImageFilterLight>light,
+ SkScalar surfaceScale,
+ const SkMatrix& matrix,
+ SkScalar kd,
+ BoundaryMode boundaryMode,
+ const SkIRect* srcBounds)
+ : INHERITED(kGrDiffuseLightingEffect_ClassID, std::move(proxy), std::move(light),
+ surfaceScale, matrix, boundaryMode, srcBounds)
+ , fKD(kd) {}
+
+GrDiffuseLightingEffect::GrDiffuseLightingEffect(const GrDiffuseLightingEffect& that)
+ : INHERITED(that), fKD(that.fKD) {}
+
+bool GrDiffuseLightingEffect::onIsEqual(const GrFragmentProcessor& sBase) const {
+ const GrDiffuseLightingEffect& s = sBase.cast<GrDiffuseLightingEffect>();
+ return INHERITED::onIsEqual(sBase) && this->kd() == s.kd();
+}
+
+void GrDiffuseLightingEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
+ GrProcessorKeyBuilder* b) const {
+ GrGLDiffuseLightingEffect::GenKey(*this, caps, b);
+}
+
+GrGLSLFragmentProcessor* GrDiffuseLightingEffect::onCreateGLSLInstance() const {
+ return new GrGLDiffuseLightingEffect;
+}
+
+GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrDiffuseLightingEffect);
+
+#if GR_TEST_UTILS
+
+static SkPoint3 random_point3(SkRandom* random) {
+ return SkPoint3::Make(SkScalarToFloat(random->nextSScalar1()),
+ SkScalarToFloat(random->nextSScalar1()),
+ SkScalarToFloat(random->nextSScalar1()));
+}
+
+static SkImageFilterLight* create_random_light(SkRandom* random) {
+ int type = random->nextULessThan(3);
+ switch (type) {
+ case 0: {
+ return new SkDistantLight(random_point3(random), random->nextU());
+ }
+ case 1: {
+ return new SkPointLight(random_point3(random), random->nextU());
+ }
+ case 2: {
+ return new SkSpotLight(random_point3(random), random_point3(random),
+ random->nextUScalar1(), random->nextUScalar1(), random->nextU());
+ }
+ default:
+ SK_ABORT("Unexpected value.");
+ return nullptr;
+ }
+}
+
+std::unique_ptr<GrFragmentProcessor> GrDiffuseLightingEffect::TestCreate(GrProcessorTestData* d) {
+ int texIdx = d->fRandom->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx
+ : GrProcessorUnitTest::kAlphaTextureIdx;
+ sk_sp<GrTextureProxy> proxy = d->textureProxy(texIdx);
+ SkScalar surfaceScale = d->fRandom->nextSScalar1();
+ SkScalar kd = d->fRandom->nextUScalar1();
+ sk_sp<SkImageFilterLight> light(create_random_light(d->fRandom));
+ SkMatrix matrix;
+ for (int i = 0; i < 9; i++) {
+ matrix[i] = d->fRandom->nextUScalar1();
+ }
+ SkIRect srcBounds = SkIRect::MakeXYWH(d->fRandom->nextRangeU(0, proxy->width()),
+ d->fRandom->nextRangeU(0, proxy->height()),
+ d->fRandom->nextRangeU(0, proxy->width()),
+ d->fRandom->nextRangeU(0, proxy->height()));
+ BoundaryMode mode = static_cast<BoundaryMode>(d->fRandom->nextU() % kBoundaryModeCount);
+ return GrDiffuseLightingEffect::Make(std::move(proxy), std::move(light), surfaceScale, matrix,
+ kd, mode, &srcBounds);
+}
+#endif
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+void GrGLLightingEffect::emitCode(EmitArgs& args) {
+ const GrLightingEffect& le = args.fFp.cast<GrLightingEffect>();
+ if (!fLight) {
+ fLight = le.light()->createGLLight();
+ }
+
+ GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
+ fImageIncrementUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
+ kHalf2_GrSLType, "ImageIncrement");
+ fSurfaceScaleUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
+ kHalf_GrSLType, "SurfaceScale");
+ fLight->emitLightColorUniform(uniformHandler);
+ GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
+ SkString lightFunc;
+ this->emitLightFunc(uniformHandler, fragBuilder, &lightFunc);
+ static const GrShaderVar gSobelArgs[] = {
+ GrShaderVar("a", kHalf_GrSLType),
+ GrShaderVar("b", kHalf_GrSLType),
+ GrShaderVar("c", kHalf_GrSLType),
+ GrShaderVar("d", kHalf_GrSLType),
+ GrShaderVar("e", kHalf_GrSLType),
+ GrShaderVar("f", kHalf_GrSLType),
+ GrShaderVar("scale", kHalf_GrSLType),
+ };
+ SkString sobelFuncName;
+ SkString coords2D = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]);
+
+ fragBuilder->emitFunction(kHalf_GrSLType,
+ "sobel",
+ SK_ARRAY_COUNT(gSobelArgs),
+ gSobelArgs,
+ "\treturn (-a + b - 2.0 * c + 2.0 * d -e + f) * scale;\n",
+ &sobelFuncName);
+ static const GrShaderVar gPointToNormalArgs[] = {
+ GrShaderVar("x", kHalf_GrSLType),
+ GrShaderVar("y", kHalf_GrSLType),
+ GrShaderVar("scale", kHalf_GrSLType),
+ };
+ SkString pointToNormalName;
+ fragBuilder->emitFunction(kHalf3_GrSLType,
+ "pointToNormal",
+ SK_ARRAY_COUNT(gPointToNormalArgs),
+ gPointToNormalArgs,
+ "\treturn normalize(half3(-x * scale, -y * scale, 1));\n",
+ &pointToNormalName);
+
+ static const GrShaderVar gInteriorNormalArgs[] = {
+ GrShaderVar("m", kHalf_GrSLType, 9),
+ GrShaderVar("surfaceScale", kHalf_GrSLType),
+ };
+ SkString normalBody = emitNormalFunc(le.boundaryMode(),
+ pointToNormalName.c_str(),
+ sobelFuncName.c_str());
+ SkString normalName;
+ fragBuilder->emitFunction(kHalf3_GrSLType,
+ "normal",
+ SK_ARRAY_COUNT(gInteriorNormalArgs),
+ gInteriorNormalArgs,
+ normalBody.c_str(),
+ &normalName);
+
+ fragBuilder->codeAppendf("\t\tfloat2 coord = %s;\n", coords2D.c_str());
+ fragBuilder->codeAppend("\t\thalf m[9];\n");
+
+ const char* imgInc = uniformHandler->getUniformCStr(fImageIncrementUni);
+ const char* surfScale = uniformHandler->getUniformCStr(fSurfaceScaleUni);
+
+ int index = 0;
+ for (int dy = 1; dy >= -1; dy--) {
+ for (int dx = -1; dx <= 1; dx++) {
+ SkString texCoords;
+ texCoords.appendf("coord + half2(%d, %d) * %s", dx, dy, imgInc);
+ SkString temp;
+ temp.appendf("temp%d", index);
+ fragBuilder->codeAppendf("half4 %s;", temp.c_str());
+ fDomain.sampleTexture(fragBuilder,
+ args.fUniformHandler,
+ args.fShaderCaps,
+ le.domain(),
+ temp.c_str(),
+ texCoords,
+ args.fTexSamplers[0]);
+ fragBuilder->codeAppendf("m[%d] = %s.a;", index, temp.c_str());
+ index++;
+ }
+ }
+ fragBuilder->codeAppend("\t\thalf3 surfaceToLight = ");
+ SkString arg;
+ arg.appendf("%s * m[4]", surfScale);
+ fLight->emitSurfaceToLight(uniformHandler, fragBuilder, arg.c_str());
+ fragBuilder->codeAppend(";\n");
+ fragBuilder->codeAppendf("\t\t%s = %s(%s(m, %s), surfaceToLight, ",
+ args.fOutputColor, lightFunc.c_str(), normalName.c_str(), surfScale);
+ fLight->emitLightColor(uniformHandler, fragBuilder, "surfaceToLight");
+ fragBuilder->codeAppend(");\n");
+ fragBuilder->codeAppendf("%s *= %s;\n", args.fOutputColor, args.fInputColor);
+}
+
+void GrGLLightingEffect::GenKey(const GrProcessor& proc,
+ const GrShaderCaps& caps, GrProcessorKeyBuilder* b) {
+ const GrLightingEffect& lighting = proc.cast<GrLightingEffect>();
+ b->add32(lighting.boundaryMode() << 2 | lighting.light()->type());
+ b->add32(GrTextureDomain::GLDomain::DomainKey(lighting.domain()));
+}
+
+void GrGLLightingEffect::onSetData(const GrGLSLProgramDataManager& pdman,
+ const GrFragmentProcessor& proc) {
+ const GrLightingEffect& lighting = proc.cast<GrLightingEffect>();
+ if (!fLight) {
+ fLight = lighting.light()->createGLLight();
+ }
+
+ GrTextureProxy* proxy = lighting.textureSampler(0).proxy();
+ GrTexture* texture = proxy->priv().peekTexture();
+
+ float ySign = proxy->origin() == kTopLeft_GrSurfaceOrigin ? -1.0f : 1.0f;
+ pdman.set2f(fImageIncrementUni, 1.0f / texture->width(), ySign / texture->height());
+ pdman.set1f(fSurfaceScaleUni, lighting.surfaceScale());
+ sk_sp<SkImageFilterLight> transformedLight(
+ lighting.light()->transform(lighting.filterMatrix()));
+ fDomain.setData(pdman, lighting.domain(), proxy);
+ fLight->setData(pdman, transformedLight.get());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+
+void GrGLDiffuseLightingEffect::emitLightFunc(GrGLSLUniformHandler* uniformHandler,
+ GrGLSLFPFragmentBuilder* fragBuilder,
+ SkString* funcName) {
+ const char* kd;
+ fKDUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf_GrSLType, "KD", &kd);
+
+ static const GrShaderVar gLightArgs[] = {
+ GrShaderVar("normal", kHalf3_GrSLType),
+ GrShaderVar("surfaceToLight", kHalf3_GrSLType),
+ GrShaderVar("lightColor", kHalf3_GrSLType)
+ };
+ SkString lightBody;
+ lightBody.appendf("\thalf colorScale = %s * dot(normal, surfaceToLight);\n", kd);
+ lightBody.appendf("\treturn half4(lightColor * clamp(colorScale, 0.0, 1.0), 1.0);\n");
+ fragBuilder->emitFunction(kHalf4_GrSLType,
+ "light",
+ SK_ARRAY_COUNT(gLightArgs),
+ gLightArgs,
+ lightBody.c_str(),
+ funcName);
+}
+
+void GrGLDiffuseLightingEffect::onSetData(const GrGLSLProgramDataManager& pdman,
+ const GrFragmentProcessor& proc) {
+ INHERITED::onSetData(pdman, proc);
+ const GrDiffuseLightingEffect& diffuse = proc.cast<GrDiffuseLightingEffect>();
+ pdman.set1f(fKDUni, diffuse.kd());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrSpecularLightingEffect::GrSpecularLightingEffect(sk_sp<GrTextureProxy> proxy,
+ sk_sp<const SkImageFilterLight> light,
+ SkScalar surfaceScale,
+ const SkMatrix& matrix,
+ SkScalar ks,
+ SkScalar shininess,
+ BoundaryMode boundaryMode,
+ const SkIRect* srcBounds)
+ : INHERITED(kGrSpecularLightingEffect_ClassID, std::move(proxy), std::move(light),
+ surfaceScale, matrix, boundaryMode, srcBounds)
+ , fKS(ks)
+ , fShininess(shininess) {}
+
+GrSpecularLightingEffect::GrSpecularLightingEffect(const GrSpecularLightingEffect& that)
+ : INHERITED(that), fKS(that.fKS), fShininess(that.fShininess) {}
+
+bool GrSpecularLightingEffect::onIsEqual(const GrFragmentProcessor& sBase) const {
+ const GrSpecularLightingEffect& s = sBase.cast<GrSpecularLightingEffect>();
+ return INHERITED::onIsEqual(sBase) &&
+ this->ks() == s.ks() &&
+ this->shininess() == s.shininess();
+}
+
+void GrSpecularLightingEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
+ GrProcessorKeyBuilder* b) const {
+ GrGLSpecularLightingEffect::GenKey(*this, caps, b);
+}
+
+GrGLSLFragmentProcessor* GrSpecularLightingEffect::onCreateGLSLInstance() const {
+ return new GrGLSpecularLightingEffect;
+}
+
+GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrSpecularLightingEffect);
+
+#if GR_TEST_UTILS
+std::unique_ptr<GrFragmentProcessor> GrSpecularLightingEffect::TestCreate(GrProcessorTestData* d) {
+ int texIdx = d->fRandom->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx
+ : GrProcessorUnitTest::kAlphaTextureIdx;
+ sk_sp<GrTextureProxy> proxy = d->textureProxy(texIdx);
+ SkScalar surfaceScale = d->fRandom->nextSScalar1();
+ SkScalar ks = d->fRandom->nextUScalar1();
+ SkScalar shininess = d->fRandom->nextUScalar1();
+ sk_sp<SkImageFilterLight> light(create_random_light(d->fRandom));
+ SkMatrix matrix;
+ for (int i = 0; i < 9; i++) {
+ matrix[i] = d->fRandom->nextUScalar1();
+ }
+ BoundaryMode mode = static_cast<BoundaryMode>(d->fRandom->nextU() % kBoundaryModeCount);
+ SkIRect srcBounds = SkIRect::MakeXYWH(d->fRandom->nextRangeU(0, proxy->width()),
+ d->fRandom->nextRangeU(0, proxy->height()),
+ d->fRandom->nextRangeU(0, proxy->width()),
+ d->fRandom->nextRangeU(0, proxy->height()));
+ return GrSpecularLightingEffect::Make(std::move(proxy), std::move(light), surfaceScale, matrix,
+ ks, shininess, mode, &srcBounds);
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+void GrGLSpecularLightingEffect::emitLightFunc(GrGLSLUniformHandler* uniformHandler,
+ GrGLSLFPFragmentBuilder* fragBuilder,
+ SkString* funcName) {
+ const char* ks;
+ const char* shininess;
+
+ fKSUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf_GrSLType, "KS", &ks);
+ fShininessUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
+ kHalf_GrSLType,
+ "Shininess",
+ &shininess);
+
+ static const GrShaderVar gLightArgs[] = {
+ GrShaderVar("normal", kHalf3_GrSLType),
+ GrShaderVar("surfaceToLight", kHalf3_GrSLType),
+ GrShaderVar("lightColor", kHalf3_GrSLType)
+ };
+ SkString lightBody;
+ lightBody.appendf("\thalf3 halfDir = half3(normalize(surfaceToLight + half3(0, 0, 1)));\n");
+ lightBody.appendf("\tfloat colorScale = %s * pow(dot(normal, halfDir), %s);\n",
+ ks, shininess);
+ lightBody.appendf("\thalf3 color = lightColor * clamp(colorScale, 0.0, 1.0);\n");
+ lightBody.appendf("\treturn half4(color, max(max(color.r, color.g), color.b));\n");
+ fragBuilder->emitFunction(kHalf4_GrSLType,
+ "light",
+ SK_ARRAY_COUNT(gLightArgs),
+ gLightArgs,
+ lightBody.c_str(),
+ funcName);
+}
+
+void GrGLSpecularLightingEffect::onSetData(const GrGLSLProgramDataManager& pdman,
+ const GrFragmentProcessor& effect) {
+ INHERITED::onSetData(pdman, effect);
+ const GrSpecularLightingEffect& spec = effect.cast<GrSpecularLightingEffect>();
+ pdman.set1f(fKSUni, spec.ks());
+ pdman.set1f(fShininessUni, spec.shininess());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+void GrGLLight::emitLightColorUniform(GrGLSLUniformHandler* uniformHandler) {
+ fColorUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf3_GrSLType, "LightColor");
+}
+
+void GrGLLight::emitLightColor(GrGLSLUniformHandler* uniformHandler,
+ GrGLSLFPFragmentBuilder* fragBuilder,
+ const char *surfaceToLight) {
+ fragBuilder->codeAppend(uniformHandler->getUniformCStr(this->lightColorUni()));
+}
+
+void GrGLLight::setData(const GrGLSLProgramDataManager& pdman,
+ const SkImageFilterLight* light) const {
+ setUniformPoint3(pdman, fColorUni,
+ light->color().makeScale(SkScalarInvert(SkIntToScalar(255))));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void GrGLDistantLight::setData(const GrGLSLProgramDataManager& pdman,
+ const SkImageFilterLight* light) const {
+ INHERITED::setData(pdman, light);
+ SkASSERT(light->type() == SkImageFilterLight::kDistant_LightType);
+ const SkDistantLight* distantLight = static_cast<const SkDistantLight*>(light);
+ setUniformNormal3(pdman, fDirectionUni, distantLight->direction());
+}
+
+void GrGLDistantLight::emitSurfaceToLight(GrGLSLUniformHandler* uniformHandler,
+ GrGLSLFPFragmentBuilder* fragBuilder,
+ const char* z) {
+ const char* dir;
+ fDirectionUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf3_GrSLType,
+ "LightDirection", &dir);
+ fragBuilder->codeAppend(dir);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void GrGLPointLight::setData(const GrGLSLProgramDataManager& pdman,
+ const SkImageFilterLight* light) const {
+ INHERITED::setData(pdman, light);
+ SkASSERT(light->type() == SkImageFilterLight::kPoint_LightType);
+ const SkPointLight* pointLight = static_cast<const SkPointLight*>(light);
+ setUniformPoint3(pdman, fLocationUni, pointLight->location());
+}
+
+void GrGLPointLight::emitSurfaceToLight(GrGLSLUniformHandler* uniformHandler,
+ GrGLSLFPFragmentBuilder* fragBuilder,
+ const char* z) {
+ const char* loc;
+ fLocationUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf3_GrSLType,
+ "LightLocation", &loc);
+ fragBuilder->codeAppendf("normalize(%s - half3(sk_FragCoord.xy, %s))",
+ loc, z);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void GrGLSpotLight::setData(const GrGLSLProgramDataManager& pdman,
+ const SkImageFilterLight* light) const {
+ INHERITED::setData(pdman, light);
+ SkASSERT(light->type() == SkImageFilterLight::kSpot_LightType);
+ const SkSpotLight* spotLight = static_cast<const SkSpotLight *>(light);
+ setUniformPoint3(pdman, fLocationUni, spotLight->location());
+ pdman.set1f(fExponentUni, spotLight->specularExponent());
+ pdman.set1f(fCosInnerConeAngleUni, spotLight->cosInnerConeAngle());
+ pdman.set1f(fCosOuterConeAngleUni, spotLight->cosOuterConeAngle());
+ pdman.set1f(fConeScaleUni, spotLight->coneScale());
+ setUniformNormal3(pdman, fSUni, spotLight->s());
+}
+
+void GrGLSpotLight::emitSurfaceToLight(GrGLSLUniformHandler* uniformHandler,
+ GrGLSLFPFragmentBuilder* fragBuilder,
+ const char* z) {
+ const char* location;
+ fLocationUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf3_GrSLType,
+ "LightLocation", &location);
+
+ fragBuilder->codeAppendf("normalize(%s - half3(sk_FragCoord.xy, %s))",
+ location, z);
+}
+
+void GrGLSpotLight::emitLightColor(GrGLSLUniformHandler* uniformHandler,
+ GrGLSLFPFragmentBuilder* fragBuilder,
+ const char *surfaceToLight) {
+
+ const char* color = uniformHandler->getUniformCStr(this->lightColorUni()); // created by parent class.
+
+ const char* exponent;
+ const char* cosInner;
+ const char* cosOuter;
+ const char* coneScale;
+ const char* s;
+ fExponentUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf_GrSLType,
+ "Exponent", &exponent);
+ fCosInnerConeAngleUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf_GrSLType,
+ "CosInnerConeAngle", &cosInner);
+ fCosOuterConeAngleUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf_GrSLType,
+ "CosOuterConeAngle", &cosOuter);
+ fConeScaleUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf_GrSLType,
+ "ConeScale", &coneScale);
+ fSUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf3_GrSLType, "S", &s);
+
+ static const GrShaderVar gLightColorArgs[] = {
+ GrShaderVar("surfaceToLight", kHalf3_GrSLType)
+ };
+ SkString lightColorBody;
+ lightColorBody.appendf("\thalf cosAngle = -dot(surfaceToLight, %s);\n", s);
+ lightColorBody.appendf("\tif (cosAngle < %s) {\n", cosOuter);
+ lightColorBody.appendf("\t\treturn half3(0);\n");
+ lightColorBody.appendf("\t}\n");
+ lightColorBody.appendf("\thalf scale = pow(cosAngle, %s);\n", exponent);
+ lightColorBody.appendf("\tif (cosAngle < %s) {\n", cosInner);
+ lightColorBody.appendf("\t\treturn %s * scale * (cosAngle - %s) * %s;\n",
+ color, cosOuter, coneScale);
+ lightColorBody.appendf("\t}\n");
+ lightColorBody.appendf("\treturn %s;\n", color);
+ fragBuilder->emitFunction(kHalf3_GrSLType,
+ "lightColor",
+ SK_ARRAY_COUNT(gLightColorArgs),
+ gLightColorArgs,
+ lightColorBody.c_str(),
+ &fLightColorFunc);
+
+ fragBuilder->codeAppendf("%s(%s)", fLightColorFunc.c_str(), surfaceToLight);
+}
+
+#endif
+
+SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkLightingImageFilter)
+ SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDiffuseLightingImageFilter)
+ SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkSpecularLightingImageFilter)
+SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END