From f49b429ceface4f75f5f96570ea5a8b94896529d Mon Sep 17 00:00:00 2001 From: "senorblanco@chromium.org" Date: Fri, 22 Jun 2012 21:01:23 +0000 Subject: [PATCH] Raster implementation of diffuse and specular lighting filters. Externally, the caller instantiates a light (distant, point or spot), and an SkDiffuseLightingFilter or SkSpecularLightingImageFilter with that light. A Sobel edge detection filter is applied to the alpha of the incoming bitmap, and the result is used as a height map for lighting calculations. Review URL: http://codereview.appspot.com/6302101/ git-svn-id: http://skia.googlecode.com/svn/trunk@4314 2bbb7eff-a529-9590-31e7-b0007b416f81 --- gm/lighting.cpp | 91 ++++ gyp/effects.gyp | 2 + gyp/gmslides.gypi | 1 + include/effects/SkLightingImageFilter.h | 87 ++++ src/effects/SkLightingImageFilter.cpp | 599 ++++++++++++++++++++++++ 5 files changed, 780 insertions(+) create mode 100644 gm/lighting.cpp create mode 100644 include/effects/SkLightingImageFilter.h create mode 100644 src/effects/SkLightingImageFilter.cpp diff --git a/gm/lighting.cpp b/gm/lighting.cpp new file mode 100644 index 0000000000..2b41528a38 --- /dev/null +++ b/gm/lighting.cpp @@ -0,0 +1,91 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "gm.h" +#include "SkLightingImageFilter.h" + +#define WIDTH 330 +#define HEIGHT 220 + +namespace skiagm { + +class ImageLightingGM : public GM { +public: + ImageLightingGM() : fInitialized(false) { + this->setBGColor(0xFF000000); + } + +protected: + virtual SkString onShortName() { + return SkString("lighting"); + } + + void make_bitmap() { + fBitmap.setConfig(SkBitmap::kARGB_8888_Config, 100, 100); + fBitmap.allocPixels(); + SkDevice device(fBitmap); + SkCanvas canvas(&device); + canvas.clear(0x00000000); + SkPaint paint; + paint.setAntiAlias(true); + paint.setColor(0xFFFFFFFF); + paint.setTextSize(SkIntToScalar(96)); + const char* str = "e"; + canvas.drawText(str, strlen(str), SkIntToScalar(20), SkIntToScalar(70), paint); + } + + virtual SkISize onISize() { + return make_isize(WIDTH, HEIGHT); + } + + virtual void onDraw(SkCanvas* canvas) { + if (!fInitialized) { + make_bitmap(); + fInitialized = true; + } + SkPoint3 pointLocation(0, 0, SkIntToScalar(10)); + SkScalar azimuthRad = SkDegreesToRadians(SkIntToScalar(225)); + SkScalar elevationRad = SkDegreesToRadians(SkIntToScalar(5)); + SkPoint3 distantDirection(SkScalarMul(SkScalarCos(azimuthRad), SkScalarCos(elevationRad)), + SkScalarMul(SkScalarSin(azimuthRad), SkScalarCos(elevationRad)), + SkScalarSin(elevationRad)); + SkPoint3 spotLocation(SkIntToScalar(-10), SkIntToScalar(-10), SkIntToScalar(20)); + SkPoint3 spotTarget(SkIntToScalar(40), SkIntToScalar(40), 0); + SkScalar spotExponent = SK_Scalar1; + SkScalar cutoffAngle = SkIntToScalar(15); + SkScalar kd = SkIntToScalar(2); + SkScalar ks = SkIntToScalar(1); + SkScalar shininess = SkIntToScalar(8); + SkScalar surfaceScale = SkIntToScalar(1); + SkColor white(0xFFFFFFFF); + SkPaint paint; + paint.setImageFilter(SkLightingImageFilter::CreatePointLitDiffuse(pointLocation, white, surfaceScale, kd))->unref(); + canvas->drawSprite(fBitmap, 0, 0, &paint); + paint.setImageFilter(SkLightingImageFilter::CreateDistantLitDiffuse(distantDirection, white, surfaceScale, kd))->unref(); + canvas->drawSprite(fBitmap, 110, 0, &paint); + paint.setImageFilter(SkLightingImageFilter::CreateSpotLitDiffuse(spotLocation, spotTarget, spotExponent, cutoffAngle, white, surfaceScale, kd))->unref(); + canvas->drawSprite(fBitmap, 220, 0, &paint); + paint.setImageFilter(SkLightingImageFilter::CreatePointLitSpecular(pointLocation, white, surfaceScale, ks, shininess))->unref(); + canvas->drawSprite(fBitmap, 0, 110, &paint); + paint.setImageFilter(SkLightingImageFilter::CreateDistantLitSpecular(distantDirection, white, surfaceScale, ks, shininess))->unref(); + canvas->drawSprite(fBitmap, 110, 110, &paint); + paint.setImageFilter(SkLightingImageFilter::CreateSpotLitSpecular(spotLocation, spotTarget, spotExponent, cutoffAngle, white, surfaceScale, ks, shininess))->unref(); + canvas->drawSprite(fBitmap, 220, 110, &paint); + } + +private: + typedef GM INHERITED; + SkBitmap fBitmap; + bool fInitialized; +}; + +////////////////////////////////////////////////////////////////////////////// + +static GM* MyFactory(void*) { return new ImageLightingGM; } +static GMRegistry reg(MyFactory); + +} diff --git a/gyp/effects.gyp b/gyp/effects.gyp index 7d0cd9a732..fbccb401b9 100644 --- a/gyp/effects.gyp +++ b/gyp/effects.gyp @@ -28,6 +28,7 @@ '../include/effects/SkKernel33MaskFilter.h', '../include/effects/SkLayerDrawLooper.h', '../include/effects/SkLayerRasterizer.h', + '../include/effects/SkLightingImageFilter.h', '../include/effects/SkMorphologyImageFilter.h', '../include/effects/SkPaintFlagsDrawFilter.h', '../include/effects/SkPixelXorXfermode.h', @@ -66,6 +67,7 @@ '../src/effects/SkKernel33MaskFilter.cpp', '../src/effects/SkLayerDrawLooper.cpp', '../src/effects/SkLayerRasterizer.cpp', + '../src/effects/SkLightingImageFilter.cpp', '../src/effects/SkMorphologyImageFilter.cpp', '../src/effects/SkPaintFlagsDrawFilter.cpp', '../src/effects/SkPixelXorXfermode.cpp', diff --git a/gyp/gmslides.gypi b/gyp/gmslides.gypi index d88ab0dcc1..874c79a143 100644 --- a/gyp/gmslides.gypi +++ b/gyp/gmslides.gypi @@ -29,6 +29,7 @@ '../gm/gradtext.cpp', '../gm/hairmodes.cpp', '../gm/imageblur.cpp', + '../gm/lighting.cpp', '../gm/imagefiltersbase.cpp', '../gm/lcdtext.cpp', '../gm/linepaths.cpp', diff --git a/include/effects/SkLightingImageFilter.h b/include/effects/SkLightingImageFilter.h new file mode 100644 index 0000000000..f8a5ccf4af --- /dev/null +++ b/include/effects/SkLightingImageFilter.h @@ -0,0 +1,87 @@ +/* + * 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. + */ + + +#ifndef SkLightingImageFilter_DEFINED +#define SkLightingImageFilter_DEFINED + +#include "SkImageFilter.h" +#include "SkColor.h" + +class SK_API SkPoint3 { +public: + SkPoint3() {} + SkPoint3(SkScalar x, SkScalar y, SkScalar z) + : fX(x), fY(y), fZ(z) {} + SkScalar dot(const SkPoint3& other) const { + return SkScalarMul(fX, other.fX) + + SkScalarMul(fY, other.fY) + + SkScalarMul(fZ, other.fZ); + } + SkScalar maxComponent() const { + return fX > fY ? (fX > fZ ? fX : fZ) : (fY > fZ ? fY : fZ); + } + void normalize() { + SkScalar scale = SkScalarInvert(SkScalarSqrt(dot(*this))); + fX = SkScalarMul(fX, scale); + fY = SkScalarMul(fY, scale); + fZ = SkScalarMul(fZ, scale); + } + SkPoint3 operator*(SkScalar scalar) const { + return SkPoint3(SkScalarMul(fX, scalar), + SkScalarMul(fY, scalar), + SkScalarMul(fZ, scalar)); + } + SkPoint3 operator-(const SkPoint3& other) const { + return SkPoint3(fX - other.fX, fY - other.fY, fZ - other.fZ); + } + SkScalar fX, fY, fZ; +}; + +class SkLight; + +class SK_API SkLightingImageFilter : public SkImageFilter { +public: + static SkImageFilter* CreateDistantLitDiffuse(const SkPoint3& direction, + const SkColor& lightColor, SkScalar surfaceScale, SkScalar kd); + static SkImageFilter* CreatePointLitDiffuse(SkPoint3& location, + const SkColor& lightColor, SkScalar surfaceScale, SkScalar kd); + static SkImageFilter* CreateSpotLitDiffuse(const SkPoint3& location, + const SkPoint3& target, SkScalar specularExponent, SkScalar cutoffAngle, + const SkColor& lightColor, SkScalar surfaceScale, SkScalar kd); + static SkImageFilter* CreateDistantLitSpecular(const SkPoint3& direction, + const SkColor& lightColor, SkScalar surfaceScale, SkScalar ks, + SkScalar shininess); + static SkImageFilter* CreatePointLitSpecular(SkPoint3& location, + const SkColor& lightColor, SkScalar surfaceScale, SkScalar ks, + SkScalar shininess); + static SkImageFilter* CreateSpotLitSpecular(const SkPoint3& location, + const SkPoint3& target, SkScalar specularExponent, SkScalar cutoffAngle, + const SkColor& lightColor, SkScalar surfaceScale, SkScalar ks, + SkScalar shininess); + ~SkLightingImageFilter(); + + SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkLightingImageFilter) + +protected: + SkLightingImageFilter(SkLight* light, const SkColor& lightColor, + SkScalar surfaceScale); + explicit SkLightingImageFilter(SkFlattenableReadBuffer& buffer); + virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE; + const SkLight* light() const { return fLight; } + const SkPoint3& lightColor() const { return fLightColor; } + SkScalar surfaceScale() const { return fSurfaceScale; } + +private: + typedef SkImageFilter INHERITED; + SkLight* fLight; + SkPoint3 fLightColor; + SkScalar fSurfaceScale; +}; + +#endif + diff --git a/src/effects/SkLightingImageFilter.cpp b/src/effects/SkLightingImageFilter.cpp new file mode 100644 index 0000000000..c2551d138a --- /dev/null +++ b/src/effects/SkLightingImageFilter.cpp @@ -0,0 +1,599 @@ +/* + * 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 "SkColorPriv.h" + +// FIXME: Eventually, this should be implemented properly, and put in +// SkScalar.h. +#define SkScalarPow(x, y) SkFloatToScalar(powf(SkScalarToFloat(x), SkScalarToFloat(y))) +namespace { + +const SkScalar gOneThird = SkScalarInvert(SkIntToScalar(3)); +const SkScalar gTwoThirds = SkScalarDiv(SkIntToScalar(2), SkIntToScalar(3)); +const SkScalar gOneHalf = SkFloatToScalar(0.5f); +const SkScalar gOneQuarter = SkFloatToScalar(0.25f); + +// Shift matrix components to the left, as we advance pixels to the right. +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]; +} + +class DiffuseLightingType { +public: + DiffuseLightingType(SkScalar kd) + : fKD(kd) {} + SkPMColor light(const SkPoint3& normal, const SkPoint3& surfaceTolight, const SkPoint3& lightColor) const { + SkScalar colorScale = SkScalarMul(fKD, normal.dot(surfaceTolight)); + colorScale = SkScalarClampMax(colorScale, SK_Scalar1); + SkPoint3 color(lightColor * colorScale); + return SkPackARGB32(255, + SkScalarFloorToInt(color.fX), + SkScalarFloorToInt(color.fY), + SkScalarFloorToInt(color.fZ)); + } +private: + SkScalar fKD; +}; + +class SpecularLightingType { +public: + SpecularLightingType(SkScalar ks, SkScalar shininess) + : fKS(ks), fShininess(shininess) {} + SkPMColor light(const SkPoint3& normal, const SkPoint3& surfaceTolight, const SkPoint3& lightColor) const { + SkPoint3 halfDir(surfaceTolight); + halfDir.fZ += SK_Scalar1; // eye position is always (0, 0, 1) + halfDir.normalize(); + SkScalar colorScale = SkScalarMul(fKS, + SkScalarPow(normal.dot(halfDir), fShininess)); + colorScale = SkScalarClampMax(colorScale, SK_Scalar1); + SkPoint3 color(lightColor * colorScale); + return SkPackARGB32(SkScalarFloorToInt(color.maxComponent()), + SkScalarFloorToInt(color.fX), + SkScalarFloorToInt(color.fY), + SkScalarFloorToInt(color.fZ)); + } +private: + SkScalar fKS; + SkScalar fShininess; +}; + +inline SkScalar sobel(int a, int b, int c, int d, int e, int f, SkScalar scale) { + return SkScalarMul(SkIntToScalar(-a + b - 2 * c + 2 * d -e + f), scale); +} + +inline SkPoint3 pointToNormal(SkScalar x, SkScalar y, SkScalar surfaceScale) { + SkPoint3 vector(SkScalarMul(-x, surfaceScale), + SkScalarMul(-y, surfaceScale), + SK_Scalar1); + vector.normalize(); + return vector; +} + +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); +} + +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); +} + +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); +} + +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); +} + + +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); +} + +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); +} + +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); +} + +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); +} + +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); +} + +template void lightBitmap(const LightingType& lightingType, const SkLight* light, const SkBitmap& src, SkBitmap* dst, const SkPoint3& lightColor, SkScalar surfaceScale) { + const LightType* l = static_cast(light); + int y = 0; + { + const SkPMColor* row1 = src.getAddr32(0, 0); + const SkPMColor* row2 = src.getAddr32(0, 1); + SkPMColor* dptr = dst->getAddr32(0, 0); + int m[9]; + int x = 0; + m[4] = SkGetPackedA32(*row1++); + m[5] = SkGetPackedA32(*row1++); + m[7] = SkGetPackedA32(*row2++); + m[8] = SkGetPackedA32(*row2++); + SkPoint3 surfaceToLight = l->surfaceToLight(x, y, m[4], surfaceScale); + *dptr++ = lightingType.light(topLeftNormal(m, surfaceScale), surfaceToLight, lightColor * l->lightColorScale(surfaceToLight)); + for (x = 1; x < src.width() - 1; ++x) + { + shiftMatrixLeft(m); + m[5] = SkGetPackedA32(*row1++); + m[8] = SkGetPackedA32(*row2++); + surfaceToLight = l->surfaceToLight(x, 0, m[4], surfaceScale); + *dptr++ = lightingType.light(topNormal(m, surfaceScale), surfaceToLight, lightColor * l->lightColorScale(surfaceToLight)); + } + shiftMatrixLeft(m); + surfaceToLight = l->surfaceToLight(x, y, m[4], surfaceScale); + *dptr++ = lightingType.light(topRightNormal(m, surfaceScale), surfaceToLight, lightColor * l->lightColorScale(surfaceToLight)); + } + + for (++y; y < src.height() - 1; ++y) { + const SkPMColor* row0 = src.getAddr32(0, y - 1); + const SkPMColor* row1 = src.getAddr32(0, y); + const SkPMColor* row2 = src.getAddr32(0, y + 1); + SkPMColor* dptr = dst->getAddr32(0, y); + int m[9]; + int x = 0; + m[1] = SkGetPackedA32(*row0++); + m[2] = SkGetPackedA32(*row0++); + m[4] = SkGetPackedA32(*row1++); + m[5] = SkGetPackedA32(*row1++); + m[7] = SkGetPackedA32(*row2++); + m[8] = SkGetPackedA32(*row2++); + SkPoint3 surfaceToLight = l->surfaceToLight(x, y, m[4], surfaceScale); + *dptr++ = lightingType.light(leftNormal(m, surfaceScale), surfaceToLight, lightColor * l->lightColorScale(surfaceToLight)); + for (x = 1; x < src.width() - 1; ++x) { + shiftMatrixLeft(m); + m[2] = SkGetPackedA32(*row0++); + m[5] = SkGetPackedA32(*row1++); + m[8] = SkGetPackedA32(*row2++); + surfaceToLight = l->surfaceToLight(x, y, m[4], surfaceScale); + *dptr++ = lightingType.light(interiorNormal(m, surfaceScale), surfaceToLight, lightColor * l->lightColorScale(surfaceToLight)); + } + shiftMatrixLeft(m); + surfaceToLight = l->surfaceToLight(x, y, m[4], surfaceScale); + *dptr++ = lightingType.light(rightNormal(m, surfaceScale), surfaceToLight, lightColor * l->lightColorScale(surfaceToLight)); + } + + { + const SkPMColor* row0 = src.getAddr32(0, src.height() - 2); + const SkPMColor* row1 = src.getAddr32(0, src.height() - 1); + int x = 0; + SkPMColor* dptr = dst->getAddr32(0, src.height() - 1); + int m[9]; + m[1] = SkGetPackedA32(*row0++); + m[2] = SkGetPackedA32(*row0++); + m[4] = SkGetPackedA32(*row1++); + m[5] = SkGetPackedA32(*row1++); + SkPoint3 surfaceToLight = l->surfaceToLight(x, y, m[4], surfaceScale); + *dptr++ = lightingType.light(bottomLeftNormal(m, surfaceScale), surfaceToLight, lightColor * l->lightColorScale(surfaceToLight)); + for (x = 1; x < src.width() - 1; ++x) + { + shiftMatrixLeft(m); + m[2] = SkGetPackedA32(*row0++); + m[5] = SkGetPackedA32(*row1++); + surfaceToLight = l->surfaceToLight(x, y, m[4], surfaceScale); + *dptr++ = lightingType.light(bottomNormal(m, surfaceScale), surfaceToLight, lightColor * l->lightColorScale(surfaceToLight)); + } + shiftMatrixLeft(m); + surfaceToLight = l->surfaceToLight(x, y, m[4], surfaceScale); + *dptr++ = lightingType.light(bottomRightNormal(m, surfaceScale), surfaceToLight, lightColor * l->lightColorScale(surfaceToLight)); + } +} + +SkPoint3 readPoint3(SkFlattenableReadBuffer& buffer) { + SkPoint3 point; + point.fX = buffer.readScalar(); + point.fY = buffer.readScalar(); + point.fZ = buffer.readScalar(); + return point; +}; + +void writePoint3(const SkPoint3& point, SkFlattenableWriteBuffer& buffer) { + buffer.writeScalar(point.fX); + buffer.writeScalar(point.fY); + buffer.writeScalar(point.fZ); +}; + +class SkDiffuseLightingImageFilter : public SkLightingImageFilter { +public: + SkDiffuseLightingImageFilter(SkLight* light, const SkColor& lightColor, + SkScalar surfaceScale, SkScalar kd); + SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkDiffuseLightingImageFilter) + +protected: + explicit SkDiffuseLightingImageFilter(SkFlattenableReadBuffer& buffer); + virtual void flatten(SkFlattenableWriteBuffer& buffer) const SK_OVERRIDE; + virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&, + SkBitmap* result, SkIPoint* offset) SK_OVERRIDE; + + +private: + typedef SkLightingImageFilter INHERITED; + SkScalar fKD; +}; + +class SkSpecularLightingImageFilter : public SkLightingImageFilter { +public: + SkSpecularLightingImageFilter(SkLight* light, const SkColor& lightColor, + SkScalar surfaceScale, SkScalar ks, SkScalar shininess); + SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkSpecularLightingImageFilter) + +protected: + explicit SkSpecularLightingImageFilter(SkFlattenableReadBuffer& buffer); + virtual void flatten(SkFlattenableWriteBuffer& buffer) const SK_OVERRIDE; + virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&, + SkBitmap* result, SkIPoint* offset) SK_OVERRIDE; + +private: + typedef SkLightingImageFilter INHERITED; + SkScalar fKS; + SkScalar fShininess; +}; + +}; + +class SkLight : public SkFlattenable { +public: + enum LightType { + kDistant_LightType, + kPoint_LightType, + kSpot_LightType, + }; + virtual LightType type() const = 0; + + static SkLight* Create(SkFlattenableReadBuffer& buffer); + virtual void flatten(SkFlattenableWriteBuffer& buffer) const SK_OVERRIDE { + buffer.write32(type()); + } +}; + +class SkDistantLight : public SkLight { +public: + SkDistantLight(const SkPoint3& direction) : fDirection(direction) { + } + SkDistantLight(SkFlattenableReadBuffer& buffer) { + fDirection = readPoint3(buffer); + } + SkPoint3 surfaceToLight(int x, int y, int z, SkScalar surfaceScale) const { + return fDirection; + }; + SkScalar lightColorScale(const SkPoint3&) const { return SK_Scalar1; } + virtual LightType type() const { return kDistant_LightType; } + virtual void flatten(SkFlattenableWriteBuffer& buffer) const SK_OVERRIDE { + SkLight::flatten(buffer); + writePoint3(fDirection, buffer); + } + + SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkDistantLight) + +private: + SkPoint3 fDirection; +}; + +class SkPointLight : public SkLight { +public: + SkPointLight(const SkPoint3& location) + : fLocation(location) {} + SkPointLight(SkFlattenableReadBuffer& buffer) { + fLocation = readPoint3(buffer); + } + SkPoint3 surfaceToLight(int x, int y, int z, SkScalar surfaceScale) const { + SkPoint3 direction(fLocation.fX - SkIntToScalar(x), + fLocation.fY - SkIntToScalar(y), + fLocation.fZ - SkScalarMul(SkIntToScalar(z), surfaceScale)); + direction.normalize(); + return direction; + }; + SkScalar lightColorScale(const SkPoint3&) const { return SK_Scalar1; } + virtual LightType type() const { return kPoint_LightType; } + virtual void flatten(SkFlattenableWriteBuffer& buffer) const SK_OVERRIDE { + SkLight::flatten(buffer); + writePoint3(fLocation, buffer); + } + + SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkPointLight) + +private: + SkPoint3 fLocation; +}; + +class SkSpotLight : public SkLight { +public: + SkSpotLight(const SkPoint3& location, const SkPoint3& target, SkScalar specularExponent, SkScalar cutoffAngle) + : fLocation(location), + fTarget(target), + fSpecularExponent(specularExponent) + { + fS = target - location; + fS.normalize(); + fCosOuterConeAngle = SkScalarCos(SkDegreesToRadians(cutoffAngle)); + const SkScalar antiAliasThreshold = SkFloatToScalar(0.016f); + fCosInnerConeAngle = fCosOuterConeAngle + antiAliasThreshold; + fConeScale = SkScalarInvert(antiAliasThreshold); + } + SkSpotLight(SkFlattenableReadBuffer& buffer) { + fLocation = readPoint3(buffer); + fTarget = readPoint3(buffer); + fSpecularExponent = buffer.readScalar(); + fCosOuterConeAngle = buffer.readScalar(); + fCosInnerConeAngle = buffer.readScalar(); + fConeScale = buffer.readScalar(); + fS = readPoint3(buffer); + } + virtual void flatten(SkFlattenableWriteBuffer& buffer) const SK_OVERRIDE { + SkLight::flatten(buffer); + writePoint3(fLocation, buffer); + writePoint3(fTarget, buffer); + buffer.writeScalar(fSpecularExponent); + buffer.writeScalar(fCosOuterConeAngle); + buffer.writeScalar(fCosInnerConeAngle); + buffer.writeScalar(fConeScale); + writePoint3(fS, buffer); + } + SkPoint3 surfaceToLight(int x, int y, int z, SkScalar surfaceScale) const { + SkPoint3 direction(fLocation.fX - SkIntToScalar(x), + fLocation.fY - SkIntToScalar(y), + fLocation.fZ - SkScalarMul(SkIntToScalar(z), surfaceScale)); + direction.normalize(); + return direction; + }; + SkScalar lightColorScale(const SkPoint3& surfaceToLight) const { + SkScalar cosAngle = -surfaceToLight.dot(fS); + if (cosAngle < fCosOuterConeAngle) { + return 0; + } + SkScalar scale = SkScalarPow(cosAngle, fSpecularExponent); + if (cosAngle < fCosInnerConeAngle) { + scale = SkScalarMul(scale, cosAngle - fCosOuterConeAngle); + return SkScalarMul(scale, fConeScale); + } + return scale; + } + + SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkSpotLight) + + virtual LightType type() const { return kSpot_LightType; } +private: + SkPoint3 fLocation; + SkPoint3 fTarget; + SkScalar fSpecularExponent; + SkScalar fCosOuterConeAngle; + SkScalar fCosInnerConeAngle; + SkScalar fConeScale; + SkPoint3 fS; +}; + +SkLight* SkLight::Create(SkFlattenableReadBuffer& buffer) { + LightType type = static_cast(buffer.readU32()); + switch (type) { + case kDistant_LightType: + return new SkDistantLight(buffer); + case kPoint_LightType: + return new SkPointLight(buffer); + case kSpot_LightType: + return new SkSpotLight(buffer); + default: + SkASSERT(false); + return 0; + } +} + +SkLightingImageFilter::SkLightingImageFilter(SkLight* light, const SkColor& lightColor, SkScalar surfaceScale) + : fLight(light), + fLightColor(SkIntToScalar(SkColorGetR(lightColor)), + SkIntToScalar(SkColorGetG(lightColor)), + SkIntToScalar(SkColorGetB(lightColor))), + fSurfaceScale(SkScalarDiv(surfaceScale, SkIntToScalar(255))) +{ + SkASSERT(fLight); + fLight->ref(); +} + +SkImageFilter* SkLightingImageFilter::CreateDistantLitDiffuse( + const SkPoint3& direction, const SkColor& lightColor, SkScalar surfaceScale, + SkScalar kd) { + return new SkDiffuseLightingImageFilter( + new SkDistantLight(direction), lightColor, surfaceScale, kd); +} + +SkImageFilter* SkLightingImageFilter::CreatePointLitDiffuse( + SkPoint3& location, const SkColor& lightColor, SkScalar surfaceScale, + SkScalar kd) { + return new SkDiffuseLightingImageFilter( + new SkPointLight(location), lightColor, surfaceScale, kd); +} + +SkImageFilter* SkLightingImageFilter::CreateSpotLitDiffuse( + const SkPoint3& location, const SkPoint3& target, SkScalar specularExponent, + SkScalar cutoffAngle, const SkColor& lightColor, SkScalar surfaceScale, + SkScalar kd) { + return new SkDiffuseLightingImageFilter( + new SkSpotLight(location, target, specularExponent, cutoffAngle), + lightColor, surfaceScale, kd); +} + +SkImageFilter* SkLightingImageFilter::CreateDistantLitSpecular( + const SkPoint3& direction, const SkColor& lightColor, SkScalar surfaceScale, + SkScalar ks, SkScalar shininess) { + return new SkSpecularLightingImageFilter( + new SkDistantLight(direction), lightColor, surfaceScale, ks, shininess); +} + +SkImageFilter* SkLightingImageFilter::CreatePointLitSpecular( + SkPoint3& location, + const SkColor& lightColor, SkScalar surfaceScale, SkScalar ks, + SkScalar shininess) { + return new SkSpecularLightingImageFilter( + new SkPointLight(location), lightColor, surfaceScale, ks, shininess); +} + +SkImageFilter* SkLightingImageFilter::CreateSpotLitSpecular( + const SkPoint3& location, + const SkPoint3& target, SkScalar specularExponent, SkScalar cutoffAngle, + const SkColor& lightColor, SkScalar surfaceScale, SkScalar ks, + SkScalar shininess) { + return new SkSpecularLightingImageFilter( + new SkSpotLight(location, target, specularExponent, cutoffAngle), + lightColor, surfaceScale, ks, shininess); +} + +SkLightingImageFilter::~SkLightingImageFilter() { + fLight->unref(); +} + +SkLightingImageFilter::SkLightingImageFilter(SkFlattenableReadBuffer& buffer) + : INHERITED(buffer) +{ + fLight = SkLight::Create(buffer); + fLightColor = readPoint3(buffer); + fSurfaceScale = buffer.readScalar(); +} + +void SkLightingImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const { + this->INHERITED::flatten(buffer); + fLight->flatten(buffer); + writePoint3(fLightColor, buffer); + buffer.writeScalar(fSurfaceScale); +} + +SkDiffuseLightingImageFilter::SkDiffuseLightingImageFilter(SkLight* light, const SkColor& lightColor, SkScalar surfaceScale, SkScalar kd) + : SkLightingImageFilter(light, lightColor, surfaceScale), + fKD(kd) +{ +} + +SkDiffuseLightingImageFilter::SkDiffuseLightingImageFilter(SkFlattenableReadBuffer& buffer) + : INHERITED(buffer) +{ + fKD = buffer.readScalar(); +} + +void SkDiffuseLightingImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const { + this->INHERITED::flatten(buffer); + buffer.writeScalar(fKD); +} + +bool SkDiffuseLightingImageFilter::onFilterImage(Proxy*, + const SkBitmap& src, + const SkMatrix&, + SkBitmap* dst, + SkIPoint*) { + if (src.config() != SkBitmap::kARGB_8888_Config) { + return false; + } + SkAutoLockPixels alp(src); + if (!src.getPixels()) { + return false; + } + if (src.width() < 2 || src.height() < 2) { + return false; + } + dst->setConfig(src.config(), src.width(), src.height()); + dst->allocPixels(); + + DiffuseLightingType lightingType(fKD); + switch (light()->type()) { + case SkLight::kDistant_LightType: + lightBitmap(lightingType, light(), src, dst, lightColor(), surfaceScale()); + break; + case SkLight::kPoint_LightType: + lightBitmap(lightingType, light(), src, dst, lightColor(), surfaceScale()); + break; + case SkLight::kSpot_LightType: + lightBitmap(lightingType, light(), src, dst, lightColor(), surfaceScale()); + break; + } + return true; +} + +SkSpecularLightingImageFilter::SkSpecularLightingImageFilter(SkLight* light, const SkColor& lightColor, SkScalar surfaceScale, SkScalar ks, SkScalar shininess) + : SkLightingImageFilter(light, lightColor, surfaceScale), + fKS(ks), + fShininess(shininess) +{ +} + +SkSpecularLightingImageFilter::SkSpecularLightingImageFilter(SkFlattenableReadBuffer& buffer) + : INHERITED(buffer) +{ + fKS = buffer.readScalar(); + fShininess = buffer.readScalar(); +} + +void SkSpecularLightingImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const { + this->INHERITED::flatten(buffer); + buffer.writeScalar(fKS); + buffer.writeScalar(fShininess); +} + +bool SkSpecularLightingImageFilter::onFilterImage(Proxy*, + const SkBitmap& src, + const SkMatrix&, + SkBitmap* dst, + SkIPoint*) { + if (src.config() != SkBitmap::kARGB_8888_Config) { + return false; + } + SkAutoLockPixels alp(src); + if (!src.getPixels()) { + return false; + } + if (src.width() < 2 || src.height() < 2) { + return false; + } + dst->setConfig(src.config(), src.width(), src.height()); + dst->allocPixels(); + + SpecularLightingType lightingType(fKS, fShininess); + switch (light()->type()) { + case SkLight::kDistant_LightType: + lightBitmap(lightingType, light(), src, dst, lightColor(), surfaceScale()); + break; + case SkLight::kPoint_LightType: + lightBitmap(lightingType, light(), src, dst, lightColor(), surfaceScale()); + break; + case SkLight::kSpot_LightType: + lightBitmap(lightingType, light(), src, dst, lightColor(), surfaceScale()); + break; + } + return true; +} + +SK_DEFINE_FLATTENABLE_REGISTRAR(SkLightingImageFilter) -- 2.34.1