/* * 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" 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 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 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( lightingType, light, src, dst, surfaceScale, bounds); } else { lightBitmap( 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 light, SkScalar surfaceScale, sk_sp input, const CropRect* cropRect) : INHERITED(std::move(light), surfaceScale, std::move(input), cropRect) { } #if SK_SUPPORT_GPU sk_sp filterImageGPU(SkSpecialImage* source, SkSpecialImage* input, const SkIRect& bounds, const SkMatrix& matrix, const OutputProperties& outputProperties) const; virtual std::unique_ptr makeFragmentProcessor( sk_sp, const SkMatrix&, const SkIRect* srcBounds, BoundaryMode boundaryMode) const = 0; #endif private: #if SK_SUPPORT_GPU void drawRect(GrRenderTargetContext*, sk_sp 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 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 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 inputProxy(input->asTextureProxyRef(context)); SkASSERT(inputProxy); sk_sp renderTargetContext( context->contextPriv().makeDeferredRenderTargetContext( SkBackingFit::kApprox, offsetBounds.width(), offsetBounds.height(), SkColorType2GrPixelConfig(outputProperties.colorType()), 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 Make(sk_sp light, SkScalar surfaceScale, SkScalar kd, sk_sp, const CropRect*); SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkDiffuseLightingImageFilter) SkScalar kd() const { return fKD; } protected: SkDiffuseLightingImageFilter(sk_sp light, SkScalar surfaceScale, SkScalar kd, sk_sp input, const CropRect* cropRect); void flatten(SkWriteBuffer& buffer) const override; sk_sp onFilterImage(SkSpecialImage* source, const Context&, SkIPoint* offset) const override; sk_sp onMakeColorSpace(SkColorSpaceXformer*) const override; #if SK_SUPPORT_GPU std::unique_ptr makeFragmentProcessor(sk_sp, 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 Make(sk_sp light, SkScalar surfaceScale, SkScalar ks, SkScalar shininess, sk_sp, const CropRect*); SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkSpecularLightingImageFilter) SkScalar ks() const { return fKS; } SkScalar shininess() const { return fShininess; } protected: SkSpecularLightingImageFilter(sk_sp light, SkScalar surfaceScale, SkScalar ks, SkScalar shininess, sk_sp input, const CropRect*); void flatten(SkWriteBuffer& buffer) const override; sk_sp onFilterImage(SkSpecialImage* source, const Context&, SkIPoint* offset) const override; sk_sp onMakeColorSpace(SkColorSpaceXformer*) const override; #if SK_SUPPORT_GPU std::unique_ptr makeFragmentProcessor(sk_sp, 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, sk_sp light, SkScalar surfaceScale, const SkMatrix& matrix, BoundaryMode boundaryMode, const SkIRect* srcBounds); GrLightingEffect(const GrLightingEffect& that); bool onIsEqual(const GrFragmentProcessor&) const override; private: const TextureSampler& onTextureSampler(int) const override { return fTextureSampler; } GrCoordTransform fCoordTransform; GrTextureDomain fDomain; TextureSampler fTextureSampler; sk_sp fLight; SkScalar fSurfaceScale; SkMatrix fFilterMatrix; BoundaryMode fBoundaryMode; typedef GrFragmentProcessor INHERITED; }; class GrDiffuseLightingEffect : public GrLightingEffect { public: static std::unique_ptr Make(sk_sp proxy, sk_sp light, SkScalar surfaceScale, const SkMatrix& matrix, SkScalar kd, BoundaryMode boundaryMode, const SkIRect* srcBounds) { return std::unique_ptr( new GrDiffuseLightingEffect(std::move(proxy), std::move(light), surfaceScale, matrix, kd, boundaryMode, srcBounds)); } const char* name() const override { return "DiffuseLighting"; } std::unique_ptr clone() const override { return std::unique_ptr(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, sk_sp 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 Make(sk_sp proxy, sk_sp light, SkScalar surfaceScale, const SkMatrix& matrix, SkScalar ks, SkScalar shininess, BoundaryMode boundaryMode, const SkIRect* srcBounds) { return std::unique_ptr( new GrSpecularLightingEffect(std::move(proxy), std::move(light), surfaceScale, matrix, ks, shininess, boundaryMode, srcBounds)); } const char* name() const override { return "SpecularLighting"; } std::unique_ptr clone() const override { return std::unique_ptr(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, sk_sp 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 makeColorSpace(SkColorSpaceXformer* xformer) const override { return sk_make_sp(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(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 makeColorSpace(SkColorSpaceXformer* xformer) const override { return sk_make_sp(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(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 makeColorSpace(SkColorSpaceXformer* xformer) const override { return sk_make_sp(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(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 light, SkScalar surfaceScale, sk_sp input, const CropRect* cropRect) : INHERITED(&input, 1, cropRect) , fLight(std::move(light)) , fSurfaceScale(surfaceScale / 255) { } SkLightingImageFilter::~SkLightingImageFilter() {} sk_sp SkLightingImageFilter::MakeDistantLitDiffuse(const SkPoint3& direction, SkColor lightColor, SkScalar surfaceScale, SkScalar kd, sk_sp input, const CropRect* cropRect) { sk_sp light(new SkDistantLight(direction, lightColor)); return SkDiffuseLightingImageFilter::Make(std::move(light), surfaceScale, kd, std::move(input), cropRect); } sk_sp SkLightingImageFilter::MakePointLitDiffuse(const SkPoint3& location, SkColor lightColor, SkScalar surfaceScale, SkScalar kd, sk_sp input, const CropRect* cropRect) { sk_sp light(new SkPointLight(location, lightColor)); return SkDiffuseLightingImageFilter::Make(std::move(light), surfaceScale, kd, std::move(input), cropRect); } sk_sp SkLightingImageFilter::MakeSpotLitDiffuse(const SkPoint3& location, const SkPoint3& target, SkScalar specularExponent, SkScalar cutoffAngle, SkColor lightColor, SkScalar surfaceScale, SkScalar kd, sk_sp input, const CropRect* cropRect) { sk_sp light( new SkSpotLight(location, target, specularExponent, cutoffAngle, lightColor)); return SkDiffuseLightingImageFilter::Make(std::move(light), surfaceScale, kd, std::move(input), cropRect); } sk_sp SkLightingImageFilter::MakeDistantLitSpecular(const SkPoint3& direction, SkColor lightColor, SkScalar surfaceScale, SkScalar ks, SkScalar shine, sk_sp input, const CropRect* cropRect) { sk_sp light(new SkDistantLight(direction, lightColor)); return SkSpecularLightingImageFilter::Make(std::move(light), surfaceScale, ks, shine, std::move(input), cropRect); } sk_sp SkLightingImageFilter::MakePointLitSpecular(const SkPoint3& location, SkColor lightColor, SkScalar surfaceScale, SkScalar ks, SkScalar shine, sk_sp input, const CropRect* cropRect) { sk_sp light(new SkPointLight(location, lightColor)); return SkSpecularLightingImageFilter::Make(std::move(light), surfaceScale, ks, shine, std::move(input), cropRect); } sk_sp SkLightingImageFilter::MakeSpotLitSpecular(const SkPoint3& location, const SkPoint3& target, SkScalar specularExponent, SkScalar cutoffAngle, SkColor lightColor, SkScalar surfaceScale, SkScalar ks, SkScalar shine, sk_sp input, const CropRect* cropRect) { sk_sp 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 SkLightingImageFilter::refLight() const { return fLight; } /////////////////////////////////////////////////////////////////////////////// sk_sp SkDiffuseLightingImageFilter::Make(sk_sp light, SkScalar surfaceScale, SkScalar kd, sk_sp 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(new SkDiffuseLightingImageFilter(std::move(light), surfaceScale, kd, std::move(input), cropRect)); } SkDiffuseLightingImageFilter::SkDiffuseLightingImageFilter(sk_sp light, SkScalar surfaceScale, SkScalar kd, sk_sp input, const CropRect* cropRect) : INHERITED(std::move(light), surfaceScale, std::move(input), cropRect) , fKD(kd) { } sk_sp SkDiffuseLightingImageFilter::CreateProc(SkReadBuffer& buffer) { SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1); sk_sp 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 SkDiffuseLightingImageFilter::onFilterImage(SkSpecialImage* source, const Context& ctx, SkIPoint* offset) const { SkIPoint inputOffset = SkIPoint::Make(0, 0); sk_sp 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 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 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 SkDiffuseLightingImageFilter::makeFragmentProcessor( sk_sp 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 SkSpecularLightingImageFilter::Make(sk_sp light, SkScalar surfaceScale, SkScalar ks, SkScalar shininess, sk_sp 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(new SkSpecularLightingImageFilter(std::move(light), surfaceScale, ks, shininess, std::move(input), cropRect)); } SkSpecularLightingImageFilter::SkSpecularLightingImageFilter(sk_sp light, SkScalar surfaceScale, SkScalar ks, SkScalar shininess, sk_sp input, const CropRect* cropRect) : INHERITED(std::move(light), surfaceScale, std::move(input), cropRect) , fKS(ks) , fShininess(shininess) { } sk_sp SkSpecularLightingImageFilter::CreateProc(SkReadBuffer& buffer) { SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1); sk_sp 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 SkSpecularLightingImageFilter::onFilterImage(SkSpecialImage* source, const Context& ctx, SkIPoint* offset) const { SkIPoint inputOffset = SkIPoint::Make(0, 0); sk_sp 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 transformedLight(light()->transform(matrix)); lightBitmap(lightingType, transformedLight.get(), inputBM, &dst, surfaceScale(), bounds); return SkSpecialImage::MakeFromRaster(SkIRect::MakeWH(bounds.width(), bounds.height()), dst); } sk_sp 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 SkSpecularLightingImageFilter::makeFragmentProcessor( sk_sp 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 proxy, sk_sp 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->setTextureSamplerCnt(1); } 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->setTextureSamplerCnt(1); } bool GrLightingEffect::onIsEqual(const GrFragmentProcessor& sBase) const { const GrLightingEffect& s = sBase.cast(); return fLight->isEqual(*s.fLight) && fSurfaceScale == s.fSurfaceScale && fBoundaryMode == s.fBoundaryMode; } /////////////////////////////////////////////////////////////////////////////// GrDiffuseLightingEffect::GrDiffuseLightingEffect(sk_sp proxy, sk_splight, 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(); 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 GrDiffuseLightingEffect::TestCreate(GrProcessorTestData* d) { int texIdx = d->fRandom->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx : GrProcessorUnitTest::kAlphaTextureIdx; sk_sp proxy = d->textureProxy(texIdx); SkScalar surfaceScale = d->fRandom->nextSScalar1(); SkScalar kd = d->fRandom->nextUScalar1(); sk_sp 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(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(); 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(); 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(); 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 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(); pdman.set1f(fKDUni, diffuse.kd()); } /////////////////////////////////////////////////////////////////////////////// GrSpecularLightingEffect::GrSpecularLightingEffect(sk_sp proxy, sk_sp 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(); 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 GrSpecularLightingEffect::TestCreate(GrProcessorTestData* d) { int texIdx = d->fRandom->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx : GrProcessorUnitTest::kAlphaTextureIdx; sk_sp proxy = d->textureProxy(texIdx); SkScalar surfaceScale = d->fRandom->nextSScalar1(); SkScalar ks = d->fRandom->nextUScalar1(); SkScalar shininess = d->fRandom->nextUScalar1(); sk_sp light(create_random_light(d->fRandom)); SkMatrix matrix; for (int i = 0; i < 9; i++) { matrix[i] = d->fRandom->nextUScalar1(); } BoundaryMode mode = static_cast(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(); 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(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(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(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