blob: 2454dc1b36cf43d30cac29e9e380c6292823554b [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"
#include "SkReadBuffer.h"
#include "SkWriteBuffer.h"
#include "SkReadBuffer.h"
#include "SkWriteBuffer.h"
#include "SkTypes.h"
#if SK_SUPPORT_GPU
#include "GrFragmentProcessor.h"
#include "GrInvariantOutput.h"
#include "effects/GrSingleTextureEffect.h"
#include "gl/GrGLProcessor.h"
#include "gl/builders/GrGLProgramBuilder.h"
class GrGLDiffuseLightingEffect;
class GrGLSpecularLightingEffect;
// For brevity
typedef GrGLProgramDataManager::UniformHandle UniformHandle;
#endif
namespace {
const SkScalar gOneThird = SkScalarInvert(SkIntToScalar(3));
const SkScalar gTwoThirds = SkScalarDiv(SkIntToScalar(2), SkIntToScalar(3));
const SkScalar gOneHalf = 0.5f;
const SkScalar gOneQuarter = 0.25f;
#if SK_SUPPORT_GPU
void setUniformPoint3(const GrGLProgramDataManager& pdman, UniformHandle uni,
const SkPoint3& point) {
GR_STATIC_ASSERT(sizeof(SkPoint3) == 3 * sizeof(GrGLfloat));
pdman.set3fv(uni, 1, &point.fX);
}
void setUniformNormal3(const GrGLProgramDataManager& pdman, UniformHandle uni,
const SkPoint3& point) {
setUniformPoint3(pdman, uni, SkPoint3(point.fX, point.fY, point.fZ));
}
#endif
// 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,
SkClampMax(SkScalarRoundToInt(color.fX), 255),
SkClampMax(SkScalarRoundToInt(color.fY), 255),
SkClampMax(SkScalarRoundToInt(color.fZ), 255));
}
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(SkClampMax(SkScalarRoundToInt(color.maxComponent()), 255),
SkClampMax(SkScalarRoundToInt(color.fX), 255),
SkClampMax(SkScalarRoundToInt(color.fY), 255),
SkClampMax(SkScalarRoundToInt(color.fZ), 255));
}
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,
SkScalar surfaceScale, const SkIRect& bounds) {
SkASSERT(dst->width() == bounds.width() && dst->height() == bounds.height());
const LightType* l = static_cast<const LightType*>(light);
int left = bounds.left(), right = bounds.right();
int bottom = bounds.bottom();
int y = bounds.top();
SkPMColor* dptr = dst->getAddr32(0, 0);
{
int x = left;
const SkPMColor* row1 = src.getAddr32(x, y);
const SkPMColor* row2 = src.getAddr32(x, y + 1);
int m[9];
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,
l->lightColor(surfaceToLight));
for (++x; x < right - 1; ++x)
{
shiftMatrixLeft(m);
m[5] = SkGetPackedA32(*row1++);
m[8] = SkGetPackedA32(*row2++);
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;
const SkPMColor* row0 = src.getAddr32(x, y - 1);
const SkPMColor* row1 = src.getAddr32(x, y);
const SkPMColor* row2 = src.getAddr32(x, y + 1);
int m[9];
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,
l->lightColor(surfaceToLight));
for (++x; x < right - 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,
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;
const SkPMColor* row0 = src.getAddr32(x, bottom - 2);
const SkPMColor* row1 = src.getAddr32(x, bottom - 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,
l->lightColor(surfaceToLight));
for (++x; x < right - 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,
l->lightColor(surfaceToLight));
}
shiftMatrixLeft(m);
surfaceToLight = l->surfaceToLight(x, y, m[4], surfaceScale);
*dptr++ = lightingType.light(bottomRightNormal(m, surfaceScale), surfaceToLight,
l->lightColor(surfaceToLight));
}
}
SkPoint3 readPoint3(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;
};
void writePoint3(const SkPoint3& point, SkWriteBuffer& buffer) {
buffer.writeScalar(point.fX);
buffer.writeScalar(point.fY);
buffer.writeScalar(point.fZ);
};
class SkDiffuseLightingImageFilter : public SkLightingImageFilter {
public:
static SkImageFilter* Create(SkLight* light, SkScalar surfaceScale, SkScalar kd, SkImageFilter*,
const CropRect*, uint32_t uniqueID = 0);
SK_TO_STRING_OVERRIDE()
SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkDiffuseLightingImageFilter)
SkScalar kd() const { return fKD; }
protected:
SkDiffuseLightingImageFilter(SkLight* light, SkScalar surfaceScale,
SkScalar kd, SkImageFilter* input, const CropRect* cropRect,
uint32_t uniqueID);
void flatten(SkWriteBuffer& buffer) const SK_OVERRIDE;
virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context&,
SkBitmap* result, SkIPoint* offset) const SK_OVERRIDE;
#if SK_SUPPORT_GPU
virtual bool asFragmentProcessor(GrFragmentProcessor**, GrTexture*, const SkMatrix&,
const SkIRect& bounds) const SK_OVERRIDE;
#endif
private:
friend class SkLightingImageFilter;
typedef SkLightingImageFilter INHERITED;
SkScalar fKD;
};
class SkSpecularLightingImageFilter : public SkLightingImageFilter {
public:
static SkImageFilter* Create(SkLight* light, SkScalar surfaceScale,
SkScalar ks, SkScalar shininess, SkImageFilter*, const CropRect*,
uint32_t uniqueID = 0);
SK_TO_STRING_OVERRIDE()
SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkSpecularLightingImageFilter)
SkScalar ks() const { return fKS; }
SkScalar shininess() const { return fShininess; }
protected:
SkSpecularLightingImageFilter(SkLight* light, SkScalar surfaceScale, SkScalar ks,
SkScalar shininess, SkImageFilter* input, const CropRect*,
uint32_t uniqueID);
void flatten(SkWriteBuffer& buffer) const SK_OVERRIDE;
virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context&,
SkBitmap* result, SkIPoint* offset) const SK_OVERRIDE;
#if SK_SUPPORT_GPU
virtual bool asFragmentProcessor(GrFragmentProcessor**, GrTexture*, const SkMatrix&,
const SkIRect& bounds) const SK_OVERRIDE;
#endif
private:
SkScalar fKS;
SkScalar fShininess;
friend class SkLightingImageFilter;
typedef SkLightingImageFilter INHERITED;
};
#if SK_SUPPORT_GPU
class GrLightingEffect : public GrSingleTextureEffect {
public:
GrLightingEffect(GrTexture* texture, const SkLight* light, SkScalar surfaceScale, const SkMatrix& matrix);
virtual ~GrLightingEffect();
const SkLight* light() const { return fLight; }
SkScalar surfaceScale() const { return fSurfaceScale; }
const SkMatrix& filterMatrix() const { return fFilterMatrix; }
protected:
bool onIsEqual(const GrFragmentProcessor&) const SK_OVERRIDE;
void onComputeInvariantOutput(GrInvariantOutput* inout) const SK_OVERRIDE {
// lighting shaders are complicated. We just throw up our hands.
inout->mulByUnknownFourComponents();
}
private:
typedef GrSingleTextureEffect INHERITED;
const SkLight* fLight;
SkScalar fSurfaceScale;
SkMatrix fFilterMatrix;
};
class GrDiffuseLightingEffect : public GrLightingEffect {
public:
static GrFragmentProcessor* Create(GrTexture* texture,
const SkLight* light,
SkScalar surfaceScale,
const SkMatrix& matrix,
SkScalar kd) {
return SkNEW_ARGS(GrDiffuseLightingEffect, (texture,
light,
surfaceScale,
matrix,
kd));
}
const char* name() const SK_OVERRIDE { return "DiffuseLighting"; }
void getGLProcessorKey(const GrGLCaps&, GrProcessorKeyBuilder*) const SK_OVERRIDE;
GrGLFragmentProcessor* createGLInstance() const SK_OVERRIDE;
SkScalar kd() const { return fKD; }
private:
bool onIsEqual(const GrFragmentProcessor&) const SK_OVERRIDE;
GrDiffuseLightingEffect(GrTexture* texture,
const SkLight* light,
SkScalar surfaceScale,
const SkMatrix& matrix,
SkScalar kd);
GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
typedef GrLightingEffect INHERITED;
SkScalar fKD;
};
class GrSpecularLightingEffect : public GrLightingEffect {
public:
static GrFragmentProcessor* Create(GrTexture* texture,
const SkLight* light,
SkScalar surfaceScale,
const SkMatrix& matrix,
SkScalar ks,
SkScalar shininess) {
return SkNEW_ARGS(GrSpecularLightingEffect, (texture,
light,
surfaceScale,
matrix,
ks,
shininess));
}
const char* name() const SK_OVERRIDE { return "SpecularLighting"; }
void getGLProcessorKey(const GrGLCaps&, GrProcessorKeyBuilder*) const SK_OVERRIDE;
GrGLFragmentProcessor* createGLInstance() const SK_OVERRIDE;
SkScalar ks() const { return fKS; }
SkScalar shininess() const { return fShininess; }
private:
bool onIsEqual(const GrFragmentProcessor&) const SK_OVERRIDE;
GrSpecularLightingEffect(GrTexture* texture,
const SkLight* light,
SkScalar surfaceScale,
const SkMatrix& matrix,
SkScalar ks,
SkScalar shininess);
GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
typedef GrLightingEffect INHERITED;
SkScalar fKS;
SkScalar fShininess;
};
///////////////////////////////////////////////////////////////////////////////
class GrGLLight {
public:
virtual ~GrGLLight() {}
/**
* This is called by GrGLLightingEffect::emitCode() before either of the two virtual functions
* below. It adds a vec3f uniform visible in the FS that represents the constant light color.
*/
void emitLightColorUniform(GrGLFPBuilder*);
/**
* 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(GrGLFPBuilder*, const char* z) = 0;
virtual void emitLightColor(GrGLFPBuilder*, const char *surfaceToLight);
// This is called from GrGLLightingEffect's setData(). Subclasses of GrGLLight must call
// INHERITED::setData().
virtual void setData(const GrGLProgramDataManager&,
const SkLight* 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:
virtual ~GrGLDistantLight() {}
virtual void setData(const GrGLProgramDataManager&,
const SkLight* light) const SK_OVERRIDE;
void emitSurfaceToLight(GrGLFPBuilder*, const char* z) SK_OVERRIDE;
private:
typedef GrGLLight INHERITED;
UniformHandle fDirectionUni;
};
///////////////////////////////////////////////////////////////////////////////
class GrGLPointLight : public GrGLLight {
public:
virtual ~GrGLPointLight() {}
virtual void setData(const GrGLProgramDataManager&,
const SkLight* light) const SK_OVERRIDE;
void emitSurfaceToLight(GrGLFPBuilder*, const char* z) SK_OVERRIDE;
private:
typedef GrGLLight INHERITED;
UniformHandle fLocationUni;
};
///////////////////////////////////////////////////////////////////////////////
class GrGLSpotLight : public GrGLLight {
public:
virtual ~GrGLSpotLight() {}
virtual void setData(const GrGLProgramDataManager&,
const SkLight* light) const SK_OVERRIDE;
void emitSurfaceToLight(GrGLFPBuilder*, const char* z) SK_OVERRIDE;
void emitLightColor(GrGLFPBuilder*, const char *surfaceToLight) SK_OVERRIDE;
private:
typedef GrGLLight INHERITED;
SkString fLightColorFunc;
UniformHandle fLocationUni;
UniformHandle fExponentUni;
UniformHandle fCosOuterConeAngleUni;
UniformHandle fCosInnerConeAngleUni;
UniformHandle fConeScaleUni;
UniformHandle fSUni;
};
#else
class GrGLLight;
#endif
};
///////////////////////////////////////////////////////////////////////////////
class SkLight : public SkRefCnt {
public:
SK_DECLARE_INST_COUNT(SkLight)
enum LightType {
kDistant_LightType,
kPoint_LightType,
kSpot_LightType,
};
virtual LightType type() const = 0;
const SkPoint3& color() const { return fColor; }
virtual GrGLLight* createGLLight() const = 0;
virtual bool isEqual(const SkLight& other) const {
return fColor == other.fColor;
}
// Called to know whether the generated GrGLLight will require access to the fragment position.
virtual bool requiresFragmentPosition() const = 0;
virtual SkLight* transform(const SkMatrix& matrix) const = 0;
// Defined below SkLight's subclasses.
void flattenLight(SkWriteBuffer& buffer) const;
static SkLight* UnflattenLight(SkReadBuffer& buffer);
protected:
SkLight(SkColor color)
: fColor(SkIntToScalar(SkColorGetR(color)),
SkIntToScalar(SkColorGetG(color)),
SkIntToScalar(SkColorGetB(color))) {}
SkLight(const SkPoint3& color)
: fColor(color) {}
SkLight(SkReadBuffer& buffer) {
fColor = readPoint3(buffer);
}
virtual void onFlattenLight(SkWriteBuffer& buffer) const = 0;
private:
typedef SkRefCnt INHERITED;
SkPoint3 fColor;
};
///////////////////////////////////////////////////////////////////////////////
class SkDistantLight : public SkLight {
public:
SkDistantLight(const SkPoint3& direction, SkColor color)
: INHERITED(color), fDirection(direction) {
}
SkPoint3 surfaceToLight(int x, int y, int z, SkScalar surfaceScale) const {
return fDirection;
};
SkPoint3 lightColor(const SkPoint3&) const { return color(); }
LightType type() const SK_OVERRIDE { return kDistant_LightType; }
const SkPoint3& direction() const { return fDirection; }
GrGLLight* createGLLight() const SK_OVERRIDE {
#if SK_SUPPORT_GPU
return SkNEW(GrGLDistantLight);
#else
SkDEBUGFAIL("Should not call in GPU-less build");
return NULL;
#endif
}
bool requiresFragmentPosition() const SK_OVERRIDE { return false; }
bool isEqual(const SkLight& other) const SK_OVERRIDE {
if (other.type() != kDistant_LightType) {
return false;
}
const SkDistantLight& o = static_cast<const SkDistantLight&>(other);
return INHERITED::isEqual(other) &&
fDirection == o.fDirection;
}
SkDistantLight(SkReadBuffer& buffer) : INHERITED(buffer) {
fDirection = readPoint3(buffer);
}
protected:
SkDistantLight(const SkPoint3& direction, const SkPoint3& color)
: INHERITED(color), fDirection(direction) {
}
SkLight* transform(const SkMatrix& matrix) const SK_OVERRIDE {
return new SkDistantLight(direction(), color());
}
void onFlattenLight(SkWriteBuffer& buffer) const SK_OVERRIDE {
writePoint3(fDirection, buffer);
}
private:
typedef SkLight INHERITED;
SkPoint3 fDirection;
};
///////////////////////////////////////////////////////////////////////////////
class SkPointLight : public SkLight {
public:
SkPointLight(const SkPoint3& location, SkColor color)
: INHERITED(color), 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;
};
SkPoint3 lightColor(const SkPoint3&) const { return color(); }
LightType type() const SK_OVERRIDE { return kPoint_LightType; }
const SkPoint3& location() const { return fLocation; }
GrGLLight* createGLLight() const SK_OVERRIDE {
#if SK_SUPPORT_GPU
return SkNEW(GrGLPointLight);
#else
SkDEBUGFAIL("Should not call in GPU-less build");
return NULL;
#endif
}
bool requiresFragmentPosition() const SK_OVERRIDE { return true; }
bool isEqual(const SkLight& other) const SK_OVERRIDE {
if (other.type() != kPoint_LightType) {
return false;
}
const SkPointLight& o = static_cast<const SkPointLight&>(other);
return INHERITED::isEqual(other) &&
fLocation == o.fLocation;
}
SkLight* transform(const SkMatrix& matrix) const SK_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(location2.fX, location2.fY, SkScalarAve(locationZ.fX, locationZ.fY));
return new SkPointLight(location, color());
}
SkPointLight(SkReadBuffer& buffer) : INHERITED(buffer) {
fLocation = readPoint3(buffer);
}
protected:
SkPointLight(const SkPoint3& location, const SkPoint3& color)
: INHERITED(color), fLocation(location) {}
void onFlattenLight(SkWriteBuffer& buffer) const SK_OVERRIDE {
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, SkColor color)
: INHERITED(color),
fLocation(location),
fTarget(target),
fSpecularExponent(SkScalarPin(specularExponent, kSpecularExponentMin, kSpecularExponentMax))
{
fS = target - location;
fS.normalize();
fCosOuterConeAngle = SkScalarCos(SkDegreesToRadians(cutoffAngle));
const SkScalar antiAliasThreshold = 0.016f;
fCosInnerConeAngle = fCosOuterConeAngle + antiAliasThreshold;
fConeScale = SkScalarInvert(antiAliasThreshold);
}
SkLight* transform(const SkMatrix& matrix) const SK_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(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(target2.fX, target2.fY, SkScalarAve(targetZ.fX, targetZ.fY));
SkPoint3 s = target - location;
s.normalize();
return new SkSpotLight(location, target, fSpecularExponent, fCosOuterConeAngle, fCosInnerConeAngle, fConeScale, s, color());
}
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;
};
SkPoint3 lightColor(const SkPoint3& surfaceToLight) const {
SkScalar cosAngle = -surfaceToLight.dot(fS);
if (cosAngle < fCosOuterConeAngle) {
return SkPoint3(0, 0, 0);
}
SkScalar scale = SkScalarPow(cosAngle, fSpecularExponent);
if (cosAngle < fCosInnerConeAngle) {
scale = SkScalarMul(scale, cosAngle - fCosOuterConeAngle);
return color() * SkScalarMul(scale, fConeScale);
}
return color() * scale;
}
GrGLLight* createGLLight() const SK_OVERRIDE {
#if SK_SUPPORT_GPU
return SkNEW(GrGLSpotLight);
#else
SkDEBUGFAIL("Should not call in GPU-less build");
return NULL;
#endif
}
bool requiresFragmentPosition() const SK_OVERRIDE { return true; }
LightType type() const SK_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 = readPoint3(buffer);
fTarget = readPoint3(buffer);
fSpecularExponent = buffer.readScalar();
fCosOuterConeAngle = buffer.readScalar();
fCosInnerConeAngle = buffer.readScalar();
fConeScale = buffer.readScalar();
fS = readPoint3(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 SK_OVERRIDE {
writePoint3(fLocation, buffer);
writePoint3(fTarget, buffer);
buffer.writeScalar(fSpecularExponent);
buffer.writeScalar(fCosOuterConeAngle);
buffer.writeScalar(fCosInnerConeAngle);
buffer.writeScalar(fConeScale);
writePoint3(fS, buffer);
}
bool isEqual(const SkLight& other) const SK_OVERRIDE {
if (other.type() != kSpot_LightType) {
return false;
}
const SkSpotLight& o = static_cast<const SkSpotLight&>(other);
return INHERITED::isEqual(other) &&
fLocation == o.fLocation &&
fTarget == o.fTarget &&
fSpecularExponent == o.fSpecularExponent &&
fCosOuterConeAngle == o.fCosOuterConeAngle;
}
private:
static const SkScalar kSpecularExponentMin;
static const SkScalar kSpecularExponentMax;
typedef SkLight INHERITED;
SkPoint3 fLocation;
SkPoint3 fTarget;
SkScalar fSpecularExponent;
SkScalar fCosOuterConeAngle;
SkScalar fCosInnerConeAngle;
SkScalar fConeScale;
SkPoint3 fS;
};
// 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 SkLight::flattenLight(SkWriteBuffer& buffer) const {
// Write type first, then baseclass, then subclass.
buffer.writeInt(this->type());
writePoint3(fColor, buffer);
this->onFlattenLight(buffer);
}
/*static*/ SkLight* SkLight::UnflattenLight(SkReadBuffer& buffer) {
// Read type first.
const SkLight::LightType type = (SkLight::LightType)buffer.readInt();
switch (type) {
// Each of these constructors must first call SkLight's, so we'll read the baseclass
// then subclass, same order as flattenLight.
case SkLight::kDistant_LightType: return SkNEW_ARGS(SkDistantLight, (buffer));
case SkLight::kPoint_LightType: return SkNEW_ARGS(SkPointLight, (buffer));
case SkLight::kSpot_LightType: return SkNEW_ARGS(SkSpotLight, (buffer));
default:
SkDEBUGFAIL("Unknown LightType.");
buffer.validate(false);
return NULL;
}
}
///////////////////////////////////////////////////////////////////////////////
SkLightingImageFilter::SkLightingImageFilter(SkLight* light, SkScalar surfaceScale,
SkImageFilter* input, const CropRect* cropRect,
uint32_t uniqueID)
: INHERITED(1, &input, cropRect, uniqueID)
, fLight(SkRef(light))
, fSurfaceScale(surfaceScale / 255)
{}
SkImageFilter* SkLightingImageFilter::CreateDistantLitDiffuse(const SkPoint3& direction,
SkColor lightColor,
SkScalar surfaceScale,
SkScalar kd,
SkImageFilter* input,
const CropRect* cropRect) {
SkAutoTUnref<SkLight> light(SkNEW_ARGS(SkDistantLight, (direction, lightColor)));
return SkDiffuseLightingImageFilter::Create(light, surfaceScale, kd, input, cropRect);
}
SkImageFilter* SkLightingImageFilter::CreatePointLitDiffuse(const SkPoint3& location,
SkColor lightColor,
SkScalar surfaceScale,
SkScalar kd,
SkImageFilter* input,
const CropRect* cropRect) {
SkAutoTUnref<SkLight> light(SkNEW_ARGS(SkPointLight, (location, lightColor)));
return SkDiffuseLightingImageFilter::Create(light, surfaceScale, kd, input, cropRect);
}
SkImageFilter* SkLightingImageFilter::CreateSpotLitDiffuse(const SkPoint3& location,
const SkPoint3& target,
SkScalar specularExponent,
SkScalar cutoffAngle,
SkColor lightColor,
SkScalar surfaceScale,
SkScalar kd,
SkImageFilter* input,
const CropRect* cropRect) {
SkAutoTUnref<SkLight> light(SkNEW_ARGS(SkSpotLight, (location, target, specularExponent,
cutoffAngle, lightColor)));
return SkDiffuseLightingImageFilter::Create(light, surfaceScale, kd, input, cropRect);
}
SkImageFilter* SkLightingImageFilter::CreateDistantLitSpecular(const SkPoint3& direction,
SkColor lightColor,
SkScalar surfaceScale,
SkScalar ks,
SkScalar shine,
SkImageFilter* input,
const CropRect* cropRect) {
SkAutoTUnref<SkLight> light(SkNEW_ARGS(SkDistantLight, (direction, lightColor)));
return SkSpecularLightingImageFilter::Create(light, surfaceScale, ks, shine, input, cropRect);
}
SkImageFilter* SkLightingImageFilter::CreatePointLitSpecular(const SkPoint3& location,
SkColor lightColor,
SkScalar surfaceScale,
SkScalar ks,
SkScalar shine,
SkImageFilter* input,
const CropRect* cropRect) {
SkAutoTUnref<SkLight> light(SkNEW_ARGS(SkPointLight, (location, lightColor)));
return SkSpecularLightingImageFilter::Create(light, surfaceScale, ks, shine, input, cropRect);
}
SkImageFilter* SkLightingImageFilter::CreateSpotLitSpecular(const SkPoint3& location,
const SkPoint3& target,
SkScalar specularExponent,
SkScalar cutoffAngle,
SkColor lightColor,
SkScalar surfaceScale,
SkScalar ks,
SkScalar shine,
SkImageFilter* input,
const CropRect* cropRect) {
SkAutoTUnref<SkLight> light(SkNEW_ARGS(SkSpotLight, (location, target, specularExponent,
cutoffAngle, lightColor)));
return SkSpecularLightingImageFilter::Create(light, surfaceScale, ks, shine, input, cropRect);
}
SkLightingImageFilter::~SkLightingImageFilter() {}
void SkLightingImageFilter::flatten(SkWriteBuffer& buffer) const {
this->INHERITED::flatten(buffer);
fLight->flattenLight(buffer);
buffer.writeScalar(fSurfaceScale * 255);
}
///////////////////////////////////////////////////////////////////////////////
SkImageFilter* SkDiffuseLightingImageFilter::Create(SkLight* light, SkScalar surfaceScale,
SkScalar kd, SkImageFilter* input, const CropRect* cropRect, uint32_t uniqueID) {
if (NULL == light) {
return NULL;
}
if (!SkScalarIsFinite(surfaceScale) || !SkScalarIsFinite(kd)) {
return NULL;
}
// According to the spec, kd can be any non-negative number :
// http://www.w3.org/TR/SVG/filters.html#feDiffuseLightingElement
if (kd < 0) {
return NULL;
}
return SkNEW_ARGS(SkDiffuseLightingImageFilter, (light, surfaceScale, kd, input, cropRect, uniqueID));
}
SkDiffuseLightingImageFilter::SkDiffuseLightingImageFilter(SkLight* light, SkScalar surfaceScale, SkScalar kd, SkImageFilter* input, const CropRect* cropRect, uint32_t uniqueID)
: SkLightingImageFilter(light, surfaceScale, input, cropRect, uniqueID),
fKD(kd)
{
}
SkFlattenable* SkDiffuseLightingImageFilter::CreateProc(SkReadBuffer& buffer) {
SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1);
SkAutoTUnref<SkLight> light(SkLight::UnflattenLight(buffer));
SkScalar surfaceScale = buffer.readScalar();
SkScalar kd = buffer.readScalar();
return Create(light, surfaceScale, kd, common.getInput(0), &common.cropRect(), common.uniqueID());
}
void SkDiffuseLightingImageFilter::flatten(SkWriteBuffer& buffer) const {
this->INHERITED::flatten(buffer);
buffer.writeScalar(fKD);
}
bool SkDiffuseLightingImageFilter::onFilterImage(Proxy* proxy,
const SkBitmap& source,
const Context& ctx,
SkBitmap* dst,
SkIPoint* offset) const {
SkImageFilter* input = getInput(0);
SkBitmap src = source;
SkIPoint srcOffset = SkIPoint::Make(0, 0);
if (input && !input->filterImage(proxy, source, ctx, &src, &srcOffset)) {
return false;
}
if (src.colorType() != kN32_SkColorType) {
return false;
}
SkIRect bounds;
if (!this->applyCropRect(ctx, proxy, src, &srcOffset, &bounds, &src)) {
return false;
}
if (bounds.width() < 2 || bounds.height() < 2) {
return false;
}
SkAutoLockPixels alp(src);
if (!src.getPixels()) {
return false;
}
if (!dst->tryAllocPixels(src.info().makeWH(bounds.width(), bounds.height()))) {
return false;
}
SkAutoTUnref<SkLight> transformedLight(light()->transform(ctx.ctm()));
DiffuseLightingType lightingType(fKD);
offset->fX = bounds.left();
offset->fY = bounds.top();
bounds.offset(-srcOffset);
switch (transformedLight->type()) {
case SkLight::kDistant_LightType:
lightBitmap<DiffuseLightingType, SkDistantLight>(lightingType, transformedLight, src, dst, surfaceScale(), bounds);
break;
case SkLight::kPoint_LightType:
lightBitmap<DiffuseLightingType, SkPointLight>(lightingType, transformedLight, src, dst, surfaceScale(), bounds);
break;
case SkLight::kSpot_LightType:
lightBitmap<DiffuseLightingType, SkSpotLight>(lightingType, transformedLight, src, dst, surfaceScale(), bounds);
break;
}
return true;
}
#ifndef SK_IGNORE_TO_STRING
void SkDiffuseLightingImageFilter::toString(SkString* str) const {
str->appendf("SkDiffuseLightingImageFilter: (");
str->appendf("kD: %f\n", fKD);
str->append(")");
}
#endif
#if SK_SUPPORT_GPU
bool SkDiffuseLightingImageFilter::asFragmentProcessor(GrFragmentProcessor** fp,
GrTexture* texture,
const SkMatrix& matrix,
const SkIRect&) const {
if (fp) {
SkScalar scale = SkScalarMul(surfaceScale(), SkIntToScalar(255));
*fp = GrDiffuseLightingEffect::Create(texture, light(), scale, matrix, kd());
}
return true;
}
#endif
///////////////////////////////////////////////////////////////////////////////
SkImageFilter* SkSpecularLightingImageFilter::Create(SkLight* light, SkScalar surfaceScale,
SkScalar ks, SkScalar shininess, SkImageFilter* input, const CropRect* cropRect, uint32_t uniqueID) {
if (NULL == light) {
return NULL;
}
if (!SkScalarIsFinite(surfaceScale) || !SkScalarIsFinite(ks) || !SkScalarIsFinite(shininess)) {
return NULL;
}
// According to the spec, ks can be any non-negative number :
// http://www.w3.org/TR/SVG/filters.html#feSpecularLightingElement
if (ks < 0) {
return NULL;
}
return SkNEW_ARGS(SkSpecularLightingImageFilter,
(light, surfaceScale, ks, shininess, input, cropRect, uniqueID));
}
SkSpecularLightingImageFilter::SkSpecularLightingImageFilter(SkLight* light, SkScalar surfaceScale, SkScalar ks, SkScalar shininess, SkImageFilter* input, const CropRect* cropRect, uint32_t uniqueID)
: SkLightingImageFilter(light, surfaceScale, input, cropRect, uniqueID),
fKS(ks),
fShininess(shininess)
{
}
SkFlattenable* SkSpecularLightingImageFilter::CreateProc(SkReadBuffer& buffer) {
SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1);
SkAutoTUnref<SkLight> light(SkLight::UnflattenLight(buffer));
SkScalar surfaceScale = buffer.readScalar();
SkScalar ks = buffer.readScalar();
SkScalar shine = buffer.readScalar();
return Create(light, surfaceScale, ks, shine, common.getInput(0), &common.cropRect(), common.uniqueID());
}
void SkSpecularLightingImageFilter::flatten(SkWriteBuffer& buffer) const {
this->INHERITED::flatten(buffer);
buffer.writeScalar(fKS);
buffer.writeScalar(fShininess);
}
bool SkSpecularLightingImageFilter::onFilterImage(Proxy* proxy,
const SkBitmap& source,
const Context& ctx,
SkBitmap* dst,
SkIPoint* offset) const {
SkImageFilter* input = getInput(0);
SkBitmap src = source;
SkIPoint srcOffset = SkIPoint::Make(0, 0);
if (input && !input->filterImage(proxy, source, ctx, &src, &srcOffset)) {
return false;
}
if (src.colorType() != kN32_SkColorType) {
return false;
}
SkIRect bounds;
if (!this->applyCropRect(ctx, proxy, src, &srcOffset, &bounds, &src)) {
return false;
}
if (bounds.width() < 2 || bounds.height() < 2) {
return false;
}
SkAutoLockPixels alp(src);
if (!src.getPixels()) {
return false;
}
if (!dst->tryAllocPixels(src.info().makeWH(bounds.width(), bounds.height()))) {
return false;
}
SpecularLightingType lightingType(fKS, fShininess);
offset->fX = bounds.left();
offset->fY = bounds.top();
bounds.offset(-srcOffset);
SkAutoTUnref<SkLight> transformedLight(light()->transform(ctx.ctm()));
switch (transformedLight->type()) {
case SkLight::kDistant_LightType:
lightBitmap<SpecularLightingType, SkDistantLight>(lightingType, transformedLight, src, dst, surfaceScale(), bounds);
break;
case SkLight::kPoint_LightType:
lightBitmap<SpecularLightingType, SkPointLight>(lightingType, transformedLight, src, dst, surfaceScale(), bounds);
break;
case SkLight::kSpot_LightType:
lightBitmap<SpecularLightingType, SkSpotLight>(lightingType, transformedLight, src, dst, surfaceScale(), bounds);
break;
}
return true;
}
#ifndef SK_IGNORE_TO_STRING
void SkSpecularLightingImageFilter::toString(SkString* str) const {
str->appendf("SkSpecularLightingImageFilter: (");
str->appendf("kS: %f shininess: %f", fKS, fShininess);
str->append(")");
}
#endif
#if SK_SUPPORT_GPU
bool SkSpecularLightingImageFilter::asFragmentProcessor(GrFragmentProcessor** fp,
GrTexture* texture,
const SkMatrix& matrix,
const SkIRect&) const {
if (fp) {
SkScalar scale = SkScalarMul(surfaceScale(), SkIntToScalar(255));
*fp = GrSpecularLightingEffect::Create(texture, light(), scale, matrix, ks(), shininess());
}
return true;
}
#endif
///////////////////////////////////////////////////////////////////////////////
#if SK_SUPPORT_GPU
namespace {
SkPoint3 random_point3(SkRandom* random) {
return SkPoint3(SkScalarToFloat(random->nextSScalar1()),
SkScalarToFloat(random->nextSScalar1()),
SkScalarToFloat(random->nextSScalar1()));
}
SkLight* create_random_light(SkRandom* random) {
int type = random->nextULessThan(3);
switch (type) {
case 0: {
return SkNEW_ARGS(SkDistantLight, (random_point3(random), random->nextU()));
}
case 1: {
return SkNEW_ARGS(SkPointLight, (random_point3(random), random->nextU()));
}
case 2: {
return SkNEW_ARGS(SkSpotLight, (random_point3(random),
random_point3(random),
random->nextUScalar1(),
random->nextUScalar1(),
random->nextU()));
}
default:
SkFAIL("Unexpected value.");
return NULL;
}
}
}
class GrGLLightingEffect : public GrGLFragmentProcessor {
public:
GrGLLightingEffect(const GrProcessor&);
virtual ~GrGLLightingEffect();
virtual void emitCode(GrGLFPBuilder*,
const GrFragmentProcessor&,
const char* outputColor,
const char* inputColor,
const TransformedCoordsArray&,
const TextureSamplerArray&) SK_OVERRIDE;
static inline void GenKey(const GrProcessor&, const GrGLCaps&, GrProcessorKeyBuilder* b);
/**
* Subclasses of GrGLLightingEffect must call INHERITED::setData();
*/
void setData(const GrGLProgramDataManager&, const GrProcessor&) SK_OVERRIDE;
protected:
virtual void emitLightFunc(GrGLFPBuilder*, SkString* funcName) = 0;
private:
typedef GrGLFragmentProcessor INHERITED;
UniformHandle fImageIncrementUni;
UniformHandle fSurfaceScaleUni;
GrGLLight* fLight;
};
///////////////////////////////////////////////////////////////////////////////
class GrGLDiffuseLightingEffect : public GrGLLightingEffect {
public:
GrGLDiffuseLightingEffect(const GrProcessor&);
void emitLightFunc(GrGLFPBuilder*, SkString* funcName) SK_OVERRIDE;
void setData(const GrGLProgramDataManager&, const GrProcessor&) SK_OVERRIDE;
private:
typedef GrGLLightingEffect INHERITED;
UniformHandle fKDUni;
};
///////////////////////////////////////////////////////////////////////////////
class GrGLSpecularLightingEffect : public GrGLLightingEffect {
public:
GrGLSpecularLightingEffect(const GrProcessor&);
void emitLightFunc(GrGLFPBuilder*, SkString* funcName) SK_OVERRIDE;
void setData(const GrGLProgramDataManager&, const GrProcessor&) SK_OVERRIDE;
private:
typedef GrGLLightingEffect INHERITED;
UniformHandle fKSUni;
UniformHandle fShininessUni;
};
///////////////////////////////////////////////////////////////////////////////
GrLightingEffect::GrLightingEffect(GrTexture* texture,
const SkLight* light,
SkScalar surfaceScale,
const SkMatrix& matrix)
: INHERITED(texture, GrCoordTransform::MakeDivByTextureWHMatrix(texture))
, fLight(light)
, fSurfaceScale(surfaceScale)
, fFilterMatrix(matrix) {
fLight->ref();
if (light->requiresFragmentPosition()) {
this->setWillReadFragmentPosition();
}
}
GrLightingEffect::~GrLightingEffect() {
fLight->unref();
}
bool GrLightingEffect::onIsEqual(const GrFragmentProcessor& sBase) const {
const GrLightingEffect& s = sBase.cast<GrLightingEffect>();
return fLight->isEqual(*s.fLight) &&
fSurfaceScale == s.fSurfaceScale;
}
///////////////////////////////////////////////////////////////////////////////
GrDiffuseLightingEffect::GrDiffuseLightingEffect(GrTexture* texture,
const SkLight* light,
SkScalar surfaceScale,
const SkMatrix& matrix,
SkScalar kd)
: INHERITED(texture, light, surfaceScale, matrix), fKD(kd) {
this->initClassID<GrDiffuseLightingEffect>();
}
bool GrDiffuseLightingEffect::onIsEqual(const GrFragmentProcessor& sBase) const {
const GrDiffuseLightingEffect& s = sBase.cast<GrDiffuseLightingEffect>();
return INHERITED::onIsEqual(sBase) &&
this->kd() == s.kd();
}
void GrDiffuseLightingEffect::getGLProcessorKey(const GrGLCaps& caps,
GrProcessorKeyBuilder* b) const {
GrGLDiffuseLightingEffect::GenKey(*this, caps, b);
}
GrGLFragmentProcessor* GrDiffuseLightingEffect::createGLInstance() const {
return SkNEW_ARGS(GrGLDiffuseLightingEffect, (*this));
}
GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrDiffuseLightingEffect);
GrFragmentProcessor* GrDiffuseLightingEffect::TestCreate(SkRandom* random,
GrContext* context,
const GrDrawTargetCaps&,
GrTexture* textures[]) {
SkScalar surfaceScale = random->nextSScalar1();
SkScalar kd = random->nextUScalar1();
SkAutoTUnref<SkLight> light(create_random_light(random));
SkMatrix matrix;
for (int i = 0; i < 9; i++) {
matrix[i] = random->nextUScalar1();
}
return GrDiffuseLightingEffect::Create(textures[GrProcessorUnitTest::kAlphaTextureIdx],
light, surfaceScale, matrix, kd);
}
///////////////////////////////////////////////////////////////////////////////
GrGLLightingEffect::GrGLLightingEffect(const GrProcessor& fp) {
const GrLightingEffect& m = fp.cast<GrLightingEffect>();
fLight = m.light()->createGLLight();
}
GrGLLightingEffect::~GrGLLightingEffect() {
delete fLight;
}
void GrGLLightingEffect::emitCode(GrGLFPBuilder* builder,
const GrFragmentProcessor&,
const char* outputColor,
const char* inputColor,
const TransformedCoordsArray& coords,
const TextureSamplerArray& samplers) {
fImageIncrementUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
kVec2f_GrSLType, kDefault_GrSLPrecision,
"ImageIncrement");
fSurfaceScaleUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
kFloat_GrSLType, kDefault_GrSLPrecision,
"SurfaceScale");
fLight->emitLightColorUniform(builder);
SkString lightFunc;
this->emitLightFunc(builder, &lightFunc);
static const GrGLShaderVar gSobelArgs[] = {
GrGLShaderVar("a", kFloat_GrSLType),
GrGLShaderVar("b", kFloat_GrSLType),
GrGLShaderVar("c", kFloat_GrSLType),
GrGLShaderVar("d", kFloat_GrSLType),
GrGLShaderVar("e", kFloat_GrSLType),
GrGLShaderVar("f", kFloat_GrSLType),
GrGLShaderVar("scale", kFloat_GrSLType),
};
SkString sobelFuncName;
GrGLFPFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
SkString coords2D = fsBuilder->ensureFSCoords2D(coords, 0);
fsBuilder->emitFunction(kFloat_GrSLType,
"sobel",
SK_ARRAY_COUNT(gSobelArgs),
gSobelArgs,
"\treturn (-a + b - 2.0 * c + 2.0 * d -e + f) * scale;\n",
&sobelFuncName);
static const GrGLShaderVar gPointToNormalArgs[] = {
GrGLShaderVar("x", kFloat_GrSLType),
GrGLShaderVar("y", kFloat_GrSLType),
GrGLShaderVar("scale", kFloat_GrSLType),
};
SkString pointToNormalName;
fsBuilder->emitFunction(kVec3f_GrSLType,
"pointToNormal",
SK_ARRAY_COUNT(gPointToNormalArgs),
gPointToNormalArgs,
"\treturn normalize(vec3(-x * scale, y * scale, 1));\n",
&pointToNormalName);
static const GrGLShaderVar gInteriorNormalArgs[] = {
GrGLShaderVar("m", kFloat_GrSLType, 9),
GrGLShaderVar("surfaceScale", kFloat_GrSLType),
};
SkString interiorNormalBody;
interiorNormalBody.appendf("\treturn %s(%s(m[0], m[2], m[3], m[5], m[6], m[8], 0.25),\n"
"\t %s(m[0], m[6], m[1], m[7], m[2], m[8], 0.25),\n"
"\t surfaceScale);\n",
pointToNormalName.c_str(),
sobelFuncName.c_str(),
sobelFuncName.c_str());
SkString interiorNormalName;
fsBuilder->emitFunction(kVec3f_GrSLType,
"interiorNormal",
SK_ARRAY_COUNT(gInteriorNormalArgs),
gInteriorNormalArgs,
interiorNormalBody.c_str(),
&interiorNormalName);
fsBuilder->codeAppendf("\t\tvec2 coord = %s;\n", coords2D.c_str());
fsBuilder->codeAppend("\t\tfloat m[9];\n");
const char* imgInc = builder->getUniformCStr(fImageIncrementUni);
const char* surfScale = builder->getUniformCStr(fSurfaceScaleUni);
int index = 0;
for (int dy = -1; dy <= 1; dy++) {
for (int dx = -1; dx <= 1; dx++) {
SkString texCoords;
texCoords.appendf("coord + vec2(%d, %d) * %s", dx, dy, imgInc);
fsBuilder->codeAppendf("\t\tm[%d] = ", index++);
fsBuilder->appendTextureLookup(samplers[0], texCoords.c_str());
fsBuilder->codeAppend(".a;\n");
}
}
fsBuilder->codeAppend("\t\tvec3 surfaceToLight = ");
SkString arg;
arg.appendf("%s * m[4]", surfScale);
fLight->emitSurfaceToLight(builder, arg.c_str());
fsBuilder->codeAppend(";\n");
fsBuilder->codeAppendf("\t\t%s = %s(%s(m, %s), surfaceToLight, ",
outputColor, lightFunc.c_str(), interiorNormalName.c_str(), surfScale);
fLight->emitLightColor(builder, "surfaceToLight");
fsBuilder->codeAppend(");\n");
SkString modulate;
GrGLSLMulVarBy4f(&modulate, outputColor, inputColor);
fsBuilder->codeAppend(modulate.c_str());
}
void GrGLLightingEffect::GenKey(const GrProcessor& proc,
const GrGLCaps& caps, GrProcessorKeyBuilder* b) {
b->add32(proc.cast<GrLightingEffect>().light()->type());
}
void GrGLLightingEffect::setData(const GrGLProgramDataManager& pdman,
const GrProcessor& proc) {
const GrLightingEffect& lighting = proc.cast<GrLightingEffect>();
GrTexture* texture = lighting.texture(0);
float ySign = texture->origin() == kTopLeft_GrSurfaceOrigin ? -1.0f : 1.0f;
pdman.set2f(fImageIncrementUni, 1.0f / texture->width(), ySign / texture->height());
pdman.set1f(fSurfaceScaleUni, lighting.surfaceScale());
SkAutoTUnref<SkLight> transformedLight(lighting.light()->transform(lighting.filterMatrix()));
fLight->setData(pdman, transformedLight);
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
GrGLDiffuseLightingEffect::GrGLDiffuseLightingEffect(const GrProcessor& proc)
: INHERITED(proc) {
}
void GrGLDiffuseLightingEffect::emitLightFunc(GrGLFPBuilder* builder, SkString* funcName) {
const char* kd;
fKDUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
kFloat_GrSLType, kDefault_GrSLPrecision,
"KD", &kd);
static const GrGLShaderVar gLightArgs[] = {
GrGLShaderVar("normal", kVec3f_GrSLType),
GrGLShaderVar("surfaceToLight", kVec3f_GrSLType),
GrGLShaderVar("lightColor", kVec3f_GrSLType)
};
SkString lightBody;
lightBody.appendf("\tfloat colorScale = %s * dot(normal, surfaceToLight);\n", kd);
lightBody.appendf("\treturn vec4(lightColor * clamp(colorScale, 0.0, 1.0), 1.0);\n");
builder->getFragmentShaderBuilder()->emitFunction(kVec4f_GrSLType,
"light",
SK_ARRAY_COUNT(gLightArgs),
gLightArgs,
lightBody.c_str(),
funcName);
}
void GrGLDiffuseLightingEffect::setData(const GrGLProgramDataManager& pdman,
const GrProcessor& proc) {
INHERITED::setData(pdman, proc);
const GrDiffuseLightingEffect& diffuse = proc.cast<GrDiffuseLightingEffect>();
pdman.set1f(fKDUni, diffuse.kd());
}
///////////////////////////////////////////////////////////////////////////////
GrSpecularLightingEffect::GrSpecularLightingEffect(GrTexture* texture,
const SkLight* light,
SkScalar surfaceScale,
const SkMatrix& matrix,
SkScalar ks,
SkScalar shininess)
: INHERITED(texture, light, surfaceScale, matrix),
fKS(ks),
fShininess(shininess) {
this->initClassID<GrSpecularLightingEffect>();
}
bool GrSpecularLightingEffect::onIsEqual(const GrFragmentProcessor& sBase) const {
const GrSpecularLightingEffect& s = sBase.cast<GrSpecularLightingEffect>();
return INHERITED::onIsEqual(sBase) &&
this->ks() == s.ks() &&
this->shininess() == s.shininess();
}
void GrSpecularLightingEffect::getGLProcessorKey(const GrGLCaps& caps,
GrProcessorKeyBuilder* b) const {
GrGLSpecularLightingEffect::GenKey(*this, caps, b);
}
GrGLFragmentProcessor* GrSpecularLightingEffect::createGLInstance() const {
return SkNEW_ARGS(GrGLSpecularLightingEffect, (*this));
}
GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrSpecularLightingEffect);
GrFragmentProcessor* GrSpecularLightingEffect::TestCreate(SkRandom* random,
GrContext* context,
const GrDrawTargetCaps&,
GrTexture* textures[]) {
SkScalar surfaceScale = random->nextSScalar1();
SkScalar ks = random->nextUScalar1();
SkScalar shininess = random->nextUScalar1();
SkAutoTUnref<SkLight> light(create_random_light(random));
SkMatrix matrix;
for (int i = 0; i < 9; i++) {
matrix[i] = random->nextUScalar1();
}
return GrSpecularLightingEffect::Create(textures[GrProcessorUnitTest::kAlphaTextureIdx],
light, surfaceScale, matrix, ks, shininess);
}
///////////////////////////////////////////////////////////////////////////////
GrGLSpecularLightingEffect::GrGLSpecularLightingEffect(const GrProcessor& proc)
: INHERITED(proc) {
}
void GrGLSpecularLightingEffect::emitLightFunc(GrGLFPBuilder* builder, SkString* funcName) {
const char* ks;
const char* shininess;
fKSUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
kFloat_GrSLType, kDefault_GrSLPrecision, "KS", &ks);
fShininessUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
kFloat_GrSLType, kDefault_GrSLPrecision, "Shininess", &shininess);
static const GrGLShaderVar gLightArgs[] = {
GrGLShaderVar("normal", kVec3f_GrSLType),
GrGLShaderVar("surfaceToLight", kVec3f_GrSLType),
GrGLShaderVar("lightColor", kVec3f_GrSLType)
};
SkString lightBody;
lightBody.appendf("\tvec3 halfDir = vec3(normalize(surfaceToLight + vec3(0, 0, 1)));\n");
lightBody.appendf("\tfloat colorScale = %s * pow(dot(normal, halfDir), %s);\n", ks, shininess);
lightBody.appendf("\tvec3 color = lightColor * clamp(colorScale, 0.0, 1.0);\n");
lightBody.appendf("\treturn vec4(color, max(max(color.r, color.g), color.b));\n");
builder->getFragmentShaderBuilder()->emitFunction(kVec4f_GrSLType,
"light",
SK_ARRAY_COUNT(gLightArgs),
gLightArgs,
lightBody.c_str(),
funcName);
}
void GrGLSpecularLightingEffect::setData(const GrGLProgramDataManager& pdman,
const GrProcessor& effect) {
INHERITED::setData(pdman, effect);
const GrSpecularLightingEffect& spec = effect.cast<GrSpecularLightingEffect>();
pdman.set1f(fKSUni, spec.ks());
pdman.set1f(fShininessUni, spec.shininess());
}
///////////////////////////////////////////////////////////////////////////////
void GrGLLight::emitLightColorUniform(GrGLFPBuilder* builder) {
fColorUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
kVec3f_GrSLType, kDefault_GrSLPrecision,
"LightColor");
}
void GrGLLight::emitLightColor(GrGLFPBuilder* builder,
const char *surfaceToLight) {
builder->getFragmentShaderBuilder()->codeAppend(builder->getUniformCStr(this->lightColorUni()));
}
void GrGLLight::setData(const GrGLProgramDataManager& pdman,
const SkLight* light) const {
setUniformPoint3(pdman, fColorUni, light->color() * SkScalarInvert(SkIntToScalar(255)));
}
///////////////////////////////////////////////////////////////////////////////
void GrGLDistantLight::setData(const GrGLProgramDataManager& pdman,
const SkLight* light) const {
INHERITED::setData(pdman, light);
SkASSERT(light->type() == SkLight::kDistant_LightType);
const SkDistantLight* distantLight = static_cast<const SkDistantLight*>(light);
setUniformNormal3(pdman, fDirectionUni, distantLight->direction());
}
void GrGLDistantLight::emitSurfaceToLight(GrGLFPBuilder* builder, const char* z) {
const char* dir;
fDirectionUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
kVec3f_GrSLType, kDefault_GrSLPrecision,
"LightDirection", &dir);
builder->getFragmentShaderBuilder()->codeAppend(dir);
}
///////////////////////////////////////////////////////////////////////////////
void GrGLPointLight::setData(const GrGLProgramDataManager& pdman,
const SkLight* light) const {
INHERITED::setData(pdman, light);
SkASSERT(light->type() == SkLight::kPoint_LightType);
const SkPointLight* pointLight = static_cast<const SkPointLight*>(light);
setUniformPoint3(pdman, fLocationUni, pointLight->location());
}
void GrGLPointLight::emitSurfaceToLight(GrGLFPBuilder* builder, const char* z) {
const char* loc;
fLocationUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
kVec3f_GrSLType, kDefault_GrSLPrecision,
"LightLocation", &loc);
GrGLFPFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
fsBuilder->codeAppendf("normalize(%s - vec3(%s.xy, %s))",
loc, fsBuilder->fragmentPosition(), z);
}
///////////////////////////////////////////////////////////////////////////////
void GrGLSpotLight::setData(const GrGLProgramDataManager& pdman,
const SkLight* light) const {
INHERITED::setData(pdman, light);
SkASSERT(light->type() == SkLight::kSpot_LightType);
const SkSpotLight* spotLight = static_cast<const SkSpotLight *>(light);
setUniformPoint3(pdman, fLocationUni, spotLight->location());
pdman.set1f(fExponentUni, spotLight->specularExponent());
pdman.set1f(fCosInnerConeAngleUni, spotLight->cosInnerConeAngle());
pdman.set1f(fCosOuterConeAngleUni, spotLight->cosOuterConeAngle());
pdman.set1f(fConeScaleUni, spotLight->coneScale());
setUniformNormal3(pdman, fSUni, spotLight->s());
}
void GrGLSpotLight::emitSurfaceToLight(GrGLFPBuilder* builder, const char* z) {
const char* location;
fLocationUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
kVec3f_GrSLType, kDefault_GrSLPrecision,
"LightLocation", &location);
GrGLFPFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
fsBuilder->codeAppendf("normalize(%s - vec3(%s.xy, %s))",
location, fsBuilder->fragmentPosition(), z);
}
void GrGLSpotLight::emitLightColor(GrGLFPBuilder* builder,
const char *surfaceToLight) {
const char* color = builder->getUniformCStr(this->lightColorUni()); // created by parent class.
const char* exponent;
const char* cosInner;
const char* cosOuter;
const char* coneScale;
const char* s;
fExponentUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
kFloat_GrSLType, kDefault_GrSLPrecision,
"Exponent", &exponent);
fCosInnerConeAngleUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
kFloat_GrSLType, kDefault_GrSLPrecision,
"CosInnerConeAngle", &cosInner);
fCosOuterConeAngleUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
kFloat_GrSLType, kDefault_GrSLPrecision,
"CosOuterConeAngle", &cosOuter);
fConeScaleUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
kFloat_GrSLType, kDefault_GrSLPrecision,
"ConeScale", &coneScale);
fSUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
kVec3f_GrSLType, kDefault_GrSLPrecision, "S", &s);
static const GrGLShaderVar gLightColorArgs[] = {
GrGLShaderVar("surfaceToLight", kVec3f_GrSLType)
};
SkString lightColorBody;
lightColorBody.appendf("\tfloat cosAngle = -dot(surfaceToLight, %s);\n", s);
lightColorBody.appendf("\tif (cosAngle < %s) {\n", cosOuter);
lightColorBody.appendf("\t\treturn vec3(0);\n");
lightColorBody.appendf("\t}\n");
lightColorBody.appendf("\tfloat 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);
GrGLFPFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
fsBuilder->emitFunction(kVec3f_GrSLType,
"lightColor",
SK_ARRAY_COUNT(gLightColorArgs),
gLightColorArgs,
lightColorBody.c_str(),
&fLightColorFunc);
fsBuilder->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