diff options
author | robertphillips <robertphillips@google.com> | 2015-07-13 13:16:44 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-07-13 13:16:44 -0700 |
commit | 3d32d768cd8b66c49c070495c08f7933b9dd2423 (patch) | |
tree | 6676e917988dcecc00b5f33f5e33a4e7c2849344 /src | |
parent | 52e7657cd850f95e66eb23c6d138ee45149a1039 (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.cpp | 91 | ||||
-rw-r--r-- | src/effects/SkPoint3.cpp | 80 |
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; +} |