blob: c4a68960af1f9dd8a2dc1ff950f5c9a8881b9a0e [file] [log] [blame]
/*
* 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:
SK_DECLARE_INST_COUNT(SkLight)
enum LightType {
kDistant_LightType,
kPoint_LightType,
kSpot_LightType,
};
virtual LightType type() const = 0;
protected:
SkLight() {}
SkLight(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {}
virtual void flatten(SkFlattenableWriteBuffer& buffer) const SK_OVERRIDE {
INHERITED::flatten(buffer);
}
private:
typedef SkFlattenable INHERITED;
};
SK_DEFINE_INST_COUNT(SkLight)
class SkDistantLight : public SkLight {
public:
SkDistantLight(const SkPoint3& direction) : fDirection(direction) {
}
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; }
SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkDistantLight)
protected:
SkDistantLight(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
fDirection = readPoint3(buffer);
}
virtual void flatten(SkFlattenableWriteBuffer& buffer) const {
INHERITED::flatten(buffer);
writePoint3(fDirection, buffer);
}
private:
typedef SkLight INHERITED;
SkPoint3 fDirection;
};
class SkPointLight : public SkLight {
public:
SkPointLight(const SkPoint3& location)
: fLocation(location) {}
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; }
SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkPointLight)
protected:
SkPointLight(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
fLocation = readPoint3(buffer);
}
virtual void flatten(SkFlattenableWriteBuffer& buffer) const {
INHERITED::flatten(buffer);
writePoint3(fLocation, buffer);
}
private:
typedef SkLight INHERITED;
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);
}
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;
}
virtual LightType type() const { return kSpot_LightType; }
SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkSpotLight)
protected:
SkSpotLight(SkFlattenableReadBuffer& buffer) : INHERITED(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 {
INHERITED::flatten(buffer);
writePoint3(fLocation, buffer);
writePoint3(fTarget, buffer);
buffer.writeScalar(fSpecularExponent);
buffer.writeScalar(fCosOuterConeAngle);
buffer.writeScalar(fCosInnerConeAngle);
buffer.writeScalar(fConeScale);
writePoint3(fS, buffer);
}
private:
typedef SkLight INHERITED;
SkPoint3 fLocation;
SkPoint3 fTarget;
SkScalar fSpecularExponent;
SkScalar fCosOuterConeAngle;
SkScalar fCosInnerConeAngle;
SkScalar fConeScale;
SkPoint3 fS;
};
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);
// our caller knows that we take ownership of the light, so we don't
// need to call ref() here.
}
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*)buffer.readFlattenable();
fLightColor = readPoint3(buffer);
fSurfaceScale = buffer.readScalar();
}
void SkLightingImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
this->INHERITED::flatten(buffer);
buffer.writeFlattenable(fLight);
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_GROUP_START(SkLightingImageFilter)
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDiffuseLightingImageFilter)
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkSpecularLightingImageFilter)
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDistantLight)
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkPointLight)
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkSpotLight)
SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END