aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar senorblanco@chromium.org <senorblanco@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>2012-06-22 21:01:23 +0000
committerGravatar senorblanco@chromium.org <senorblanco@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>2012-06-22 21:01:23 +0000
commitf49b429ceface4f75f5f96570ea5a8b94896529d (patch)
tree15ec6b7d0f904ba949098f286c1588209e267e09
parent7d6afdd795eb4c7ce8f5a327117cfdba5f957ddb (diff)
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
-rw-r--r--gm/lighting.cpp91
-rw-r--r--gyp/effects.gyp2
-rw-r--r--gyp/gmslides.gypi1
-rw-r--r--include/effects/SkLightingImageFilter.h87
-rw-r--r--src/effects/SkLightingImageFilter.cpp599
5 files changed, 780 insertions, 0 deletions
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 <class LightingType, class LightType> void lightBitmap(const LightingType& lightingType, const SkLight* light, const SkBitmap& src, SkBitmap* dst, const SkPoint3& lightColor, SkScalar surfaceScale) {
+ const LightType* l = static_cast<const LightType*>(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<LightType>(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<DiffuseLightingType, SkDistantLight>(lightingType, light(), src, dst, lightColor(), surfaceScale());
+ break;
+ case SkLight::kPoint_LightType:
+ lightBitmap<DiffuseLightingType, SkPointLight>(lightingType, light(), src, dst, lightColor(), surfaceScale());
+ break;
+ case SkLight::kSpot_LightType:
+ lightBitmap<DiffuseLightingType, SkSpotLight>(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<SpecularLightingType, SkDistantLight>(lightingType, light(), src, dst, lightColor(), surfaceScale());
+ break;
+ case SkLight::kPoint_LightType:
+ lightBitmap<SpecularLightingType, SkPointLight>(lightingType, light(), src, dst, lightColor(), surfaceScale());
+ break;
+ case SkLight::kSpot_LightType:
+ lightBitmap<SpecularLightingType, SkSpotLight>(lightingType, light(), src, dst, lightColor(), surfaceScale());
+ break;
+ }
+ return true;
+}
+
+SK_DEFINE_FLATTENABLE_REGISTRAR(SkLightingImageFilter)