aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorGravatar robertphillips <robertphillips@google.com>2015-07-13 13:16:44 -0700
committerGravatar Commit bot <commit-bot@chromium.org>2015-07-13 13:16:44 -0700
commit3d32d768cd8b66c49c070495c08f7933b9dd2423 (patch)
tree6676e917988dcecc00b5f33f5e33a4e7c2849344 /src
parent52e7657cd850f95e66eb23c6d138ee45149a1039 (diff)
Add new SkPoint3 class
The existing Light filter and the upcoming Lighting Shader both need a Point3 class Review URL: https://codereview.chromium.org/1229693009
Diffstat (limited to 'src')
-rw-r--r--src/effects/SkLightingImageFilter.cpp91
-rw-r--r--src/effects/SkPoint3.cpp80
2 files changed, 130 insertions, 41 deletions
diff --git a/src/effects/SkLightingImageFilter.cpp b/src/effects/SkLightingImageFilter.cpp
index 0df6f7a76f..ae49462b5e 100644
--- a/src/effects/SkLightingImageFilter.cpp
+++ b/src/effects/SkLightingImageFilter.cpp
@@ -8,11 +8,10 @@
#include "SkLightingImageFilter.h"
#include "SkBitmap.h"
#include "SkColorPriv.h"
+#include "SkPoint3.h"
#include "SkReadBuffer.h"
-#include "SkWriteBuffer.h"
-#include "SkReadBuffer.h"
-#include "SkWriteBuffer.h"
#include "SkTypes.h"
+#include "SkWriteBuffer.h"
#if SK_SUPPORT_GPU
#include "GrContext.h"
@@ -47,7 +46,7 @@ void setUniformPoint3(const GrGLProgramDataManager& pdman, UniformHandle uni,
void setUniformNormal3(const GrGLProgramDataManager& pdman, UniformHandle uni,
const SkPoint3& point) {
- setUniformPoint3(pdman, uni, SkPoint3(point.fX, point.fY, point.fZ));
+ setUniformPoint3(pdman, uni, point);
}
#endif
@@ -69,7 +68,7 @@ public:
const SkPoint3& lightColor) const {
SkScalar colorScale = SkScalarMul(fKD, normal.dot(surfaceTolight));
colorScale = SkScalarClampMax(colorScale, SK_Scalar1);
- SkPoint3 color(lightColor * colorScale);
+ SkPoint3 color = lightColor.makeScale(colorScale);
return SkPackARGB32(255,
SkClampMax(SkScalarRoundToInt(color.fX), 255),
SkClampMax(SkScalarRoundToInt(color.fY), 255),
@@ -79,6 +78,10 @@ private:
SkScalar fKD;
};
+static SkScalar max_component(const SkPoint3& p) {
+ return p.x() > p.y() ? (p.x() > p.z() ? p.x() : p.z()) : (p.y() > p.z() ? p.y() : p.z());
+}
+
class SpecularLightingType {
public:
SpecularLightingType(SkScalar ks, SkScalar shininess)
@@ -91,8 +94,8 @@ public:
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),
+ SkPoint3 color = lightColor.makeScale(colorScale);
+ return SkPackARGB32(SkClampMax(SkScalarRoundToInt(max_component(color)), 255),
SkClampMax(SkScalarRoundToInt(color.fX), 255),
SkClampMax(SkScalarRoundToInt(color.fY), 255),
SkClampMax(SkScalarRoundToInt(color.fZ), 255));
@@ -107,9 +110,9 @@ inline SkScalar sobel(int a, int b, int c, int d, int e, int f, SkScalar scale)
}
inline SkPoint3 pointToNormal(SkScalar x, SkScalar y, SkScalar surfaceScale) {
- SkPoint3 vector(SkScalarMul(-x, surfaceScale),
- SkScalarMul(-y, surfaceScale),
- SK_Scalar1);
+ SkPoint3 vector = SkPoint3::Make(SkScalarMul(-x, surfaceScale),
+ SkScalarMul(-y, surfaceScale),
+ SK_Scalar1);
vector.normalize();
return vector;
}
@@ -712,10 +715,11 @@ public:
static SkLight* UnflattenLight(SkReadBuffer& buffer);
protected:
- SkLight(SkColor color)
- : fColor(SkIntToScalar(SkColorGetR(color)),
- SkIntToScalar(SkColorGetG(color)),
- SkIntToScalar(SkColorGetB(color))) {}
+ SkLight(SkColor color) {
+ fColor = SkPoint3::Make(SkIntToScalar(SkColorGetR(color)),
+ SkIntToScalar(SkColorGetG(color)),
+ SkIntToScalar(SkColorGetB(color)));
+ }
SkLight(const SkPoint3& color)
: fColor(color) {}
SkLight(SkReadBuffer& buffer) {
@@ -741,7 +745,7 @@ public:
SkPoint3 surfaceToLight(int x, int y, int z, SkScalar surfaceScale) const {
return fDirection;
};
- SkPoint3 lightColor(const SkPoint3&) const { return color(); }
+ const SkPoint3& lightColor(const SkPoint3&) const { return this->color(); }
LightType type() const override { return kDistant_LightType; }
const SkPoint3& direction() const { return fDirection; }
GrGLLight* createGLLight() const override {
@@ -792,13 +796,14 @@ public:
: 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));
+ SkPoint3 direction = SkPoint3::Make(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(); }
+ const SkPoint3& lightColor(const SkPoint3&) const { return this->color(); }
LightType type() const override { return kPoint_LightType; }
const SkPoint3& location() const { return fLocation; }
GrGLLight* createGLLight() const override {
@@ -824,7 +829,9 @@ public:
// 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));
+ SkPoint3 location = SkPoint3::Make(location2.fX,
+ location2.fY,
+ SkScalarAve(locationZ.fX, locationZ.fY));
return new SkPointLight(location, color());
}
@@ -872,12 +879,14 @@ public:
// 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));
+ SkPoint3 location = SkPoint3::Make(location2.fX, location2.fY,
+ SkScalarAve(locationZ.fX, locationZ.fY));
SkPoint target2 = SkPoint::Make(fTarget.fX, fTarget.fY);
matrix.mapPoints(&target2, 1);
SkPoint targetZ = SkPoint::Make(fTarget.fZ, fTarget.fZ);
matrix.mapVectors(&targetZ, 1);
- SkPoint3 target(target2.fX, target2.fY, SkScalarAve(targetZ.fX, targetZ.fY));
+ SkPoint3 target = SkPoint3::Make(target2.fX, target2.fY,
+ SkScalarAve(targetZ.fX, targetZ.fY));
SkPoint3 s = target - location;
s.normalize();
return new SkSpotLight(location,
@@ -891,23 +900,24 @@ public:
}
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));
+ SkPoint3 direction = SkPoint3::Make(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);
+ SkScalar scale = 0;
+ if (cosAngle >= fCosOuterConeAngle) {
+ scale = SkScalarPow(cosAngle, fSpecularExponent);
+ if (cosAngle < fCosInnerConeAngle) {
+ scale = SkScalarMul(scale, cosAngle - fCosOuterConeAngle);
+ scale *= fConeScale;
+ }
}
- return color() * scale;
+ return this->color().makeScale(scale);
}
GrGLLight* createGLLight() const override {
#if SK_SUPPORT_GPU
@@ -1386,9 +1396,9 @@ GrFragmentProcessor* SkSpecularLightingImageFilter::getFragmentProcessor(
namespace {
SkPoint3 random_point3(SkRandom* random) {
- return SkPoint3(SkScalarToFloat(random->nextSScalar1()),
- SkScalarToFloat(random->nextSScalar1()),
- SkScalarToFloat(random->nextSScalar1()));
+ return SkPoint3::Make(SkScalarToFloat(random->nextSScalar1()),
+ SkScalarToFloat(random->nextSScalar1()),
+ SkScalarToFloat(random->nextSScalar1()));
}
SkLight* create_random_light(SkRandom* random) {
@@ -1883,14 +1893,13 @@ void GrGLLight::emitLightColorUniform(GrGLFPBuilder* builder) {
"LightColor");
}
-void GrGLLight::emitLightColor(GrGLFPBuilder* builder,
- const char *surfaceToLight) {
+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 GrGLLight::setData(const GrGLProgramDataManager& pdman, const SkLight* light) const {
+ setUniformPoint3(pdman, fColorUni,
+ light->color().makeScale(SkScalarInvert(SkIntToScalar(255))));
}
///////////////////////////////////////////////////////////////////////////////
diff --git a/src/effects/SkPoint3.cpp b/src/effects/SkPoint3.cpp
new file mode 100644
index 0000000000..3b5586b067
--- /dev/null
+++ b/src/effects/SkPoint3.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkPoint3.h"
+
+// Returns the square of the Euclidian distance to (x,y,z).
+static inline float get_length_squared(float x, float y, float z) {
+ return x * x + y * y + z * z;
+}
+
+// Calculates the square of the Euclidian distance to (x,y,z) and stores it in
+// *lengthSquared. Returns true if the distance is judged to be "nearly zero".
+//
+// This logic is encapsulated in a helper method to make it explicit that we
+// always perform this check in the same manner, to avoid inconsistencies
+// (see http://code.google.com/p/skia/issues/detail?id=560 ).
+static inline bool is_length_nearly_zero(float x, float y, float z, float *lengthSquared) {
+ *lengthSquared = get_length_squared(x, y, z);
+ return *lengthSquared <= (SK_ScalarNearlyZero * SK_ScalarNearlyZero);
+}
+
+SkScalar SkPoint3::Length(SkScalar x, SkScalar y, SkScalar z) {
+ float magSq = get_length_squared(x, y, z);
+ if (SkScalarIsFinite(magSq)) {
+ return sk_float_sqrt(magSq);
+ } else {
+ double xx = x;
+ double yy = y;
+ double zz = z;
+ return (float)sqrt(xx * xx + yy * yy + zz * zz);
+ }
+}
+
+/*
+ * We have to worry about 2 tricky conditions:
+ * 1. underflow of magSq (compared against nearlyzero^2)
+ * 2. overflow of magSq (compared w/ isfinite)
+ *
+ * If we underflow, we return false. If we overflow, we compute again using
+ * doubles, which is much slower (3x in a desktop test) but will not overflow.
+ */
+bool SkPoint3::normalize() {
+ float magSq;
+ if (is_length_nearly_zero(fX, fY, fZ, &magSq)) {
+ this->set(0, 0, 0);
+ return false;
+ }
+
+ float scale;
+ if (SkScalarIsFinite(magSq)) {
+ scale = 1.0f / sk_float_sqrt(magSq);
+ } else {
+ // our magSq step overflowed to infinity, so use doubles instead.
+ // much slower, but needed when x, y or z is very large, otherwise we
+ // divide by inf. and return (0,0,0) vector.
+ double xx = fX;
+ double yy = fY;
+ double zz = fZ;
+#ifdef SK_CPU_FLUSH_TO_ZERO
+ // The iOS ARM processor discards small denormalized numbers to go faster.
+ // Casting this to a float would cause the scale to go to zero. Keeping it
+ // as a double for the multiply keeps the scale non-zero.
+ double dscale = 1.0f / sqrt(xx * xx + yy * yy + zz * zz);
+ fX = x * dscale;
+ fY = y * dscale;
+ fZ = z * dscale;
+ return true;
+#else
+ scale = (float)(1.0f / sqrt(xx * xx + yy * yy + zz * zz));
+#endif
+ }
+ fX *= scale;
+ fY *= scale;
+ fZ *= scale;
+ return true;
+}