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
diff --git a/bench/LightingBench.cpp b/bench/LightingBench.cpp
index a56ed22..ce6c119 100644
--- a/bench/LightingBench.cpp
+++ b/bench/LightingBench.cpp
@@ -9,6 +9,7 @@
 #include "SkCanvas.h"
 #include "SkDevice.h"
 #include "SkLightingImageFilter.h"
+#include "SkPoint3.h"
 
 #define FILTER_WIDTH_SMALL  SkIntToScalar(32)
 #define FILTER_HEIGHT_SMALL SkIntToScalar(32)
@@ -30,63 +31,65 @@
         }
     }
 
-    static SkPoint3 getPointLocation() {
-        static SkPoint3 pointLocation(0, 0, SkIntToScalar(10));
+    static SkPoint3 GetPointLocation() {
+        static SkPoint3 pointLocation = SkPoint3::Make(0, 0, SkIntToScalar(10));
         return pointLocation;
     }
 
-    static SkPoint3 getDistantDirection() {
+    static SkPoint3 GetDistantDirection() {
         static SkScalar azimuthRad = SkDegreesToRadians(SkIntToScalar(225));
         static SkScalar elevationRad = SkDegreesToRadians(SkIntToScalar(5));
-        static SkPoint3 distantDirection(SkScalarMul(SkScalarCos(azimuthRad),
-                                                     SkScalarCos(elevationRad)),
-                                         SkScalarMul(SkScalarSin(azimuthRad),
-                                                     SkScalarCos(elevationRad)),
-                                         SkScalarSin(elevationRad));
+        static SkPoint3 distantDirection = SkPoint3::Make(SkScalarMul(SkScalarCos(azimuthRad),
+                                                                      SkScalarCos(elevationRad)),
+                                                          SkScalarMul(SkScalarSin(azimuthRad),
+                                                                      SkScalarCos(elevationRad)),
+                                                          SkScalarSin(elevationRad));
         return distantDirection;
     }
 
-    static SkPoint3 getSpotLocation() {
-        static SkPoint3 spotLocation(SkIntToScalar(-10), SkIntToScalar(-10), SkIntToScalar(20));
+    static SkPoint3 GetSpotLocation() {
+        static SkPoint3 spotLocation = SkPoint3::Make(SkIntToScalar(-10),
+                                                      SkIntToScalar(-10),
+                                                      SkIntToScalar(20));
         return spotLocation;
     }
 
-    static SkPoint3 getSpotTarget() {
-        static SkPoint3 spotTarget(SkIntToScalar(40), SkIntToScalar(40), 0);
+    static SkPoint3 GetSpotTarget() {
+        static SkPoint3 spotTarget = SkPoint3::Make(SkIntToScalar(40), SkIntToScalar(40), 0);
         return spotTarget;
     }
 
-    static SkScalar getSpotExponent() {
+    static SkScalar GetSpotExponent() {
         static SkScalar spotExponent = SK_Scalar1;
         return spotExponent;
     }
 
-    static SkScalar getCutoffAngle() {
+    static SkScalar GetCutoffAngle() {
         static SkScalar cutoffAngle = SkIntToScalar(15);
         return cutoffAngle;
     }
 
-    static SkScalar getKd() {
+    static SkScalar GetKd() {
         static SkScalar kd = SkIntToScalar(2);
         return kd;
     }
 
-    static SkScalar getKs() {
+    static SkScalar GetKs() {
         static SkScalar ks = SkIntToScalar(1);
         return ks;
     }
 
-    static SkScalar getShininess() {
+    static SkScalar GetShininess() {
         static SkScalar shininess = SkIntToScalar(8);
         return shininess;
     }
 
-    static SkScalar getSurfaceScale() {
+    static SkScalar GetSurfaceScale() {
         static SkScalar surfaceScale = SkIntToScalar(1);
         return surfaceScale;
     }
 
-    static SkColor getWhite() {
+    static SkColor GetWhite() {
         static SkColor white(0xFFFFFFFF);
         return white;
     }
@@ -106,10 +109,10 @@
     }
 
     void onDraw(const int loops, SkCanvas* canvas) override {
-        draw(loops, canvas, SkLightingImageFilter::CreatePointLitDiffuse(getPointLocation(),
-                                                                         getWhite(),
-                                                                         getSurfaceScale(),
-                                                                         getKd()));
+        draw(loops, canvas, SkLightingImageFilter::CreatePointLitDiffuse(GetPointLocation(),
+                                                                         GetWhite(),
+                                                                         GetSurfaceScale(),
+                                                                         GetKd()));
     }
 
 private:
@@ -127,10 +130,10 @@
     }
 
     void onDraw(const int loops, SkCanvas* canvas) override {
-        draw(loops, canvas, SkLightingImageFilter::CreateDistantLitDiffuse(getDistantDirection(),
-                                                                           getWhite(),
-                                                                           getSurfaceScale(),
-                                                                           getKd()));
+        draw(loops, canvas, SkLightingImageFilter::CreateDistantLitDiffuse(GetDistantDirection(),
+                                                                           GetWhite(),
+                                                                           GetSurfaceScale(),
+                                                                           GetKd()));
     }
 
 private:
@@ -148,13 +151,13 @@
     }
 
     void onDraw(const int loops, SkCanvas* canvas) override {
-        draw(loops, canvas, SkLightingImageFilter::CreateSpotLitDiffuse(getSpotLocation(),
-                                                                        getSpotTarget(),
-                                                                        getSpotExponent(),
-                                                                        getCutoffAngle(),
-                                                                        getWhite(),
-                                                                        getSurfaceScale(),
-                                                                        getKd()));
+        draw(loops, canvas, SkLightingImageFilter::CreateSpotLitDiffuse(GetSpotLocation(),
+                                                                        GetSpotTarget(),
+                                                                        GetSpotExponent(),
+                                                                        GetCutoffAngle(),
+                                                                        GetWhite(),
+                                                                        GetSurfaceScale(),
+                                                                        GetKd()));
     }
 
 private:
@@ -172,11 +175,11 @@
     }
 
     void onDraw(const int loops, SkCanvas* canvas) override {
-        draw(loops, canvas, SkLightingImageFilter::CreatePointLitSpecular(getPointLocation(),
-                                                                          getWhite(),
-                                                                          getSurfaceScale(),
-                                                                          getKs(),
-                                                                          getShininess()));
+        draw(loops, canvas, SkLightingImageFilter::CreatePointLitSpecular(GetPointLocation(),
+                                                                          GetWhite(),
+                                                                          GetSurfaceScale(),
+                                                                          GetKs(),
+                                                                          GetShininess()));
     }
 
 private:
@@ -194,11 +197,11 @@
     }
 
     void onDraw(const int loops, SkCanvas* canvas) override {
-        draw(loops, canvas, SkLightingImageFilter::CreateDistantLitSpecular(getDistantDirection(),
-                                                                            getWhite(),
-                                                                            getSurfaceScale(),
-                                                                            getKs(),
-                                                                            getShininess()));
+        draw(loops, canvas, SkLightingImageFilter::CreateDistantLitSpecular(GetDistantDirection(),
+                                                                            GetWhite(),
+                                                                            GetSurfaceScale(),
+                                                                            GetKs(),
+                                                                            GetShininess()));
     }
 
 private:
@@ -216,14 +219,14 @@
     }
 
     void onDraw(const int loops, SkCanvas* canvas) override {
-        draw(loops, canvas, SkLightingImageFilter::CreateSpotLitSpecular(getSpotLocation(),
-                                                                         getSpotTarget(),
-                                                                         getSpotExponent(),
-                                                                         getCutoffAngle(),
-                                                                         getWhite(),
-                                                                         getSurfaceScale(),
-                                                                         getKs(),
-                                                                         getShininess()));
+        draw(loops, canvas, SkLightingImageFilter::CreateSpotLitSpecular(GetSpotLocation(),
+                                                                         GetSpotTarget(),
+                                                                         GetSpotExponent(),
+                                                                         GetCutoffAngle(),
+                                                                         GetWhite(),
+                                                                         GetSurfaceScale(),
+                                                                         GetKs(),
+                                                                         GetShininess()));
     }
 
 private:
diff --git a/gm/imagefiltersscaled.cpp b/gm/imagefiltersscaled.cpp
index 9a80964..e7a68d7 100644
--- a/gm/imagefiltersscaled.cpp
+++ b/gm/imagefiltersscaled.cpp
@@ -16,6 +16,7 @@
 #include "SkMorphologyImageFilter.h"
 #include "SkOffsetImageFilter.h"
 #include "SkPerlinNoiseShader.h"
+#include "SkPoint3.h"
 #include "SkRectShaderImageFilter.h"
 #include "SkScalar.h"
 #include "gm.h"
@@ -75,9 +76,11 @@
         SkAutoTUnref<SkShader> noise(SkPerlinNoiseShader::CreateFractalNoise(
             SkDoubleToScalar(0.1), SkDoubleToScalar(0.05), 1, 0));
 
-        SkPoint3 pointLocation(0, 0, SkIntToScalar(10));
-        SkPoint3 spotLocation(SkIntToScalar(-10), SkIntToScalar(-10), SkIntToScalar(20));
-        SkPoint3 spotTarget(SkIntToScalar(40), SkIntToScalar(40), 0);
+        SkPoint3 pointLocation = SkPoint3::Make(0, 0, SkIntToScalar(10));
+        SkPoint3 spotLocation = SkPoint3::Make(SkIntToScalar(-10), 
+                                               SkIntToScalar(-10), 
+                                               SkIntToScalar(20));
+        SkPoint3 spotTarget = SkPoint3::Make(SkIntToScalar(40), SkIntToScalar(40), 0);
         SkScalar spotExponent = SK_Scalar1;
         SkScalar cutoffAngle = SkIntToScalar(15);
         SkScalar kd = SkIntToScalar(2);
diff --git a/gm/lighting.cpp b/gm/lighting.cpp
index 0bfbb47..6df5851 100644
--- a/gm/lighting.cpp
+++ b/gm/lighting.cpp
@@ -8,6 +8,7 @@
 #include "gm.h"
 #include "SkLightingImageFilter.h"
 #include "SkOffsetImageFilter.h"
+#include "SkPoint3.h"
 
 #define WIDTH 330
 #define HEIGHT 660
@@ -69,14 +70,18 @@
             canvas->restore();
           }
         }
-        SkPoint3 pointLocation(0, 0, SkIntToScalar(10));
+        SkPoint3 pointLocation = SkPoint3::Make(0, 0, SkIntToScalar(10));
         SkScalar azimuthRad = SkDegreesToRadians(SkIntToScalar(225));
         SkScalar elevationRad = SkDegreesToRadians(SkIntToScalar(5));
-        SkPoint3 distantDirection(SkScalarMul(SkScalarCos(azimuthRad), SkScalarCos(elevationRad)),
-                                  SkScalarMul(SkScalarSin(azimuthRad), SkScalarCos(elevationRad)),
-                                  SkScalarSin(elevationRad));
-        SkPoint3 spotLocation(SkIntToScalar(-10), SkIntToScalar(-10), SkIntToScalar(20));
-        SkPoint3 spotTarget(SkIntToScalar(40), SkIntToScalar(40), 0);
+        SkPoint3 distantDirection = SkPoint3::Make(SkScalarMul(SkScalarCos(azimuthRad),
+                                                               SkScalarCos(elevationRad)),
+                                                   SkScalarMul(SkScalarSin(azimuthRad),
+                                                               SkScalarCos(elevationRad)),
+                                                   SkScalarSin(elevationRad));
+        SkPoint3 spotLocation = SkPoint3::Make(SkIntToScalar(-10),
+                                               SkIntToScalar(-10),
+                                               SkIntToScalar(20));
+        SkPoint3 spotTarget = SkPoint3::Make(SkIntToScalar(40), SkIntToScalar(40), 0);
         SkScalar spotExponent = SK_Scalar1;
         SkScalar cutoffAngle = SkIntToScalar(15);
         SkScalar kd = SkIntToScalar(2);
diff --git a/gyp/effects.gypi b/gyp/effects.gypi
index edba3c7..e0ed2c4 100644
--- a/gyp/effects.gypi
+++ b/gyp/effects.gypi
@@ -55,6 +55,7 @@
     '<(skia_src_path)/effects/SkPerlinNoiseShader.cpp',
     '<(skia_src_path)/effects/SkPictureImageFilter.cpp',
     '<(skia_src_path)/effects/SkPixelXorXfermode.cpp',
+    '<(skia_src_path)/effects/SkPoint3.cpp',
     '<(skia_src_path)/effects/SkRectShaderImageFilter.cpp',
     '<(skia_src_path)/effects/SkTableColorFilter.cpp',
     '<(skia_src_path)/effects/SkTableMaskFilter.cpp',
@@ -82,7 +83,6 @@
 
     '<(skia_include_path)/effects/Sk1DPathEffect.h',
     '<(skia_include_path)/effects/Sk2DPathEffect.h',
-    '<(skia_include_path)/effects/SkXfermodeImageFilter.h',
     '<(skia_include_path)/effects/SkAlphaThresholdFilter.h',
     '<(skia_include_path)/effects/SkArithmeticMode.h',
     '<(skia_include_path)/effects/SkBitmapSource.h',
@@ -106,15 +106,17 @@
     '<(skia_include_path)/effects/SkLerpXfermode.h',
     '<(skia_include_path)/effects/SkLightingImageFilter.h',
     '<(skia_include_path)/effects/SkLumaColorFilter.h',
-    '<(skia_include_path)/effects/SkOffsetImageFilter.h',
+    '<(skia_include_path)/effects/SkMagnifierImageFilter.h',
     '<(skia_include_path)/effects/SkMorphologyImageFilter.h',
+    '<(skia_include_path)/effects/SkOffsetImageFilter.h',
     '<(skia_include_path)/effects/SkPaintFlagsDrawFilter.h',
     '<(skia_include_path)/effects/SkPerlinNoiseShader.h',
     '<(skia_include_path)/effects/SkPixelXorXfermode.h',
+    '<(skia_include_path)/effects/SkPoint3.h',
     '<(skia_include_path)/effects/SkRectShaderImageFilter.h',
     '<(skia_include_path)/effects/SkTableColorFilter.h',
     '<(skia_include_path)/effects/SkTableMaskFilter.h',
     '<(skia_include_path)/effects/SkTileImageFilter.h',
-    '<(skia_include_path)/effects/SkMagnifierImageFilter.h',
+    '<(skia_include_path)/effects/SkXfermodeImageFilter.h',
   ],
 }
diff --git a/gyp/skia_for_chromium_defines.gypi b/gyp/skia_for_chromium_defines.gypi
index a148a83..a1f323c 100644
--- a/gyp/skia_for_chromium_defines.gypi
+++ b/gyp/skia_for_chromium_defines.gypi
@@ -14,6 +14,7 @@
     #
     'skia_for_chromium_defines': [
       'SK_IGNORE_LINEONLY_AA_CONVEX_PATH_OPTS',
+      'SK_LEGACY_SKPOINT3_CTORS',
     ],
   },
 }
diff --git a/include/effects/SkLightingImageFilter.h b/include/effects/SkLightingImageFilter.h
index e5048ee..326e7b8 100644
--- a/include/effects/SkLightingImageFilter.h
+++ b/include/effects/SkLightingImageFilter.h
@@ -11,35 +11,12 @@
 #include "SkImageFilter.h"
 #include "SkColor.h"
 
-class SK_API SkPoint3 {
-public:
-    SkPoint3() {}
-    SkPoint3(SkScalar x, SkScalar y, SkScalar z)
-      : fX(x), fY(y), fZ(z) {}
-    SkScalar dot(const SkPoint3& other) const {
-        return fX * other.fX + fY * other.fY + fZ * other.fZ;
-    }
-    SkScalar maxComponent() const {
-        return fX > fY ? (fX > fZ ? fX : fZ) : (fY > fZ ? fY : fZ);
-    }
-    void normalize() {
-        // Small epsilon is added to prevent division by 0.
-        SkScalar scale = SkScalarInvert(SkScalarSqrt(dot(*this)) + SK_ScalarNearlyZero);
-        fX = fX * scale;
-        fY = fY * scale;
-        fZ = fZ * scale;
-    }
-    SkPoint3 operator*(SkScalar scalar) const {
-        return SkPoint3(fX * scalar, fY * scalar, fZ * scalar);
-    }
-    SkPoint3 operator-(const SkPoint3& other) const {
-        return SkPoint3(fX - other.fX, fY - other.fY, fZ - other.fZ);
-    }
-    bool operator==(const SkPoint3& other) const {
-        return fX == other.fX && fY == other.fY && fZ == other.fZ;
-    }
-    SkScalar fX, fY, fZ;
-};
+#ifdef SK_LEGACY_SKPOINT3_CTORS
+// TODO: remove this. Chromium relies on having this included here
+#include "SkPoint3.h"
+#else
+struct SkPoint3;
+#endif
 
 class SkLight;
 
diff --git a/include/effects/SkPoint3.h b/include/effects/SkPoint3.h
new file mode 100644
index 0000000..f9b727b
--- /dev/null
+++ b/include/effects/SkPoint3.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkPoint3_DEFINED
+#define SkPoint3_DEFINED
+
+#include "SkScalar.h"
+
+struct SK_API SkPoint3 {
+    SkScalar fX, fY, fZ;
+
+#ifdef SK_LEGACY_SKPOINT3_CTORS
+    // TODO: remove these - they are needed for Chromium staging
+    SkPoint3() {}
+    SkPoint3(SkScalar x, SkScalar y, SkScalar z) : fX(x), fY(y), fZ(z) {}
+#endif
+
+    static SkPoint3 Make(SkScalar x, SkScalar y, SkScalar z) {
+        SkPoint3 pt;
+        pt.set(x, y, z);
+        return pt;
+    }
+
+    SkScalar x() const { return fX; }
+    SkScalar y() const { return fY; }
+    SkScalar z() const { return fZ; }
+
+    void set(SkScalar x, SkScalar y, SkScalar z) { fX = x; fY = y; fZ = z; }
+
+    friend bool operator==(const SkPoint3& a, const SkPoint3& b) {
+        return a.fX == b.fX && a.fY == b.fY && a.fZ == b.fZ;
+    }
+
+    friend bool operator!=(const SkPoint3& a, const SkPoint3& b) {
+        return !(a == b);
+    }
+
+    /** Returns the Euclidian distance from (0,0,0) to (x,y,z)
+    */
+    static SkScalar Length(SkScalar x, SkScalar y, SkScalar z);
+
+    /** Return the Euclidian distance from (0,0,0) to the point
+    */
+    SkScalar length() const { return SkPoint3::Length(fX, fY, fZ); }
+
+    /** Set the point (vector) to be unit-length in the same direction as it
+        already points.  If the point has a degenerate length (i.e., nearly 0)
+        then set it to (0,0,0) and return false; otherwise return true.
+    */
+    bool normalize();
+
+    /** Return a new point whose X, Y and Z coordinates are scaled.
+    */
+    SkPoint3 makeScale(SkScalar scale) const {
+        SkPoint3 p;
+        p.set(scale * fX, scale * fY, scale * fZ);
+        return p;
+    }
+
+    /** Scale the point's coordinates by scale.
+    */
+    void scale(SkScalar value) { 
+        fX *= value;
+        fY *= value;
+        fZ *= value;
+    }
+
+    /** Return a new point whose X, Y and Z coordinates are the negative of the
+        original point's
+    */
+    SkPoint3 operator-() const {
+        SkPoint3 neg;
+        neg.fX = -fX;
+        neg.fY = -fY;
+        neg.fZ = -fZ;
+        return neg;
+    }
+
+    /** Returns a new point whose coordinates are the difference between
+        a and b (i.e., a - b)
+    */
+    friend SkPoint3 operator-(const SkPoint3& a, const SkPoint3& b) {
+        SkPoint3 v;
+        v.set(a.fX - b.fX, a.fY - b.fY, a.fZ - b.fZ);
+        return v;
+    }
+
+    /** Returns a new point whose coordinates are the sum of a and b (a + b)
+    */
+    friend SkPoint3 operator+(const SkPoint3& a, const SkPoint3& b) {
+        SkPoint3 v;
+        v.set(a.fX + b.fX, a.fY + b.fY, a.fZ + b.fZ);
+        return v;
+    }
+
+    /** Returns the dot product of a and b, treating them as 3D vectors
+    */
+    static SkScalar DotProduct(const SkPoint3& a, const SkPoint3& b) {
+        return a.fX * b.fX + a.fY * b.fY + a.fZ * b.fZ;
+    }
+
+    SkScalar dot(const SkPoint3& vec) const {
+        return DotProduct(*this, vec);
+    }
+};
+
+typedef SkPoint3 SkVector3;
+
+#endif
diff --git a/samplecode/SampleFilterFuzz.cpp b/samplecode/SampleFilterFuzz.cpp
index 30389b6..bae7c21 100644
--- a/samplecode/SampleFilterFuzz.cpp
+++ b/samplecode/SampleFilterFuzz.cpp
@@ -26,6 +26,7 @@
 #include "SkPerlinNoiseShader.h"
 #include "SkPictureImageFilter.h"
 #include "SkPictureRecorder.h"
+#include "SkPoint3.h"
 #include "SkRandom.h"
 #include "SkRectShaderImageFilter.h"
 #include "SkTestImageFilters.h"
@@ -128,7 +129,7 @@
 }
 
 static SkPoint3 make_point() {
-    return SkPoint3(make_scalar(), make_scalar(), make_scalar(true));
+    return SkPoint3::Make(make_scalar(), make_scalar(), make_scalar(true));
 }
 
 static SkDisplacementMapEffect::ChannelSelectorType make_channel_selector_type() {
@@ -357,10 +358,10 @@
         break;
     case SPOT_LIGHT:
         filter = (R(2) == 1) ?
-                 SkLightingImageFilter::CreateSpotLitDiffuse(SkPoint3(0, 0, 0),
+                 SkLightingImageFilter::CreateSpotLitDiffuse(SkPoint3::Make(0, 0, 0),
                  make_point(), make_scalar(), make_scalar(), make_color(),
                  make_scalar(), make_scalar(), make_image_filter()) :
-                 SkLightingImageFilter::CreateSpotLitSpecular(SkPoint3(0, 0, 0),
+                 SkLightingImageFilter::CreateSpotLitSpecular(SkPoint3::Make(0, 0, 0),
                  make_point(), make_scalar(), make_scalar(), make_color(),
                  make_scalar(), make_scalar(), SkIntToScalar(R(10)), make_image_filter());
         break;
diff --git a/samplecode/SampleLighting.cpp b/samplecode/SampleLighting.cpp
index 40e6fbb..a002a93 100755
--- a/samplecode/SampleLighting.cpp
+++ b/samplecode/SampleLighting.cpp
@@ -11,6 +11,7 @@
 #include "SkCanvas.h"
 #include "SkErrorInternals.h"
 #include "SkGr.h"
+#include "SkPoint3.h"
 #include "SkReadBuffer.h"
 #include "SkShader.h"
 #include "SkWriteBuffer.h"
@@ -21,18 +22,6 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-struct SkVector3 {
-    SkScalar fX, fY, fZ;
-
-    bool operator==(const SkVector3& other) const {
-        return fX == other.fX && fY == other.fY && fZ == other.fZ;
-    }
-
-    bool operator!=(const SkVector3& other) const {
-        return !(*this == other);
-    }
-};
-
 class LightingShader : public SkShader {
 public:
     struct Light {
diff --git a/src/effects/SkLightingImageFilter.cpp b/src/effects/SkLightingImageFilter.cpp
index 0df6f7a..ae49462 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 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 @@
                     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 @@
     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 @@
         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 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 @@
     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 @@
     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 @@
      : 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 @@
         // 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 @@
         // 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 @@
     }
 
     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 = 0;
+        if (cosAngle >= fCosOuterConeAngle) {
+            scale = SkScalarPow(cosAngle, fSpecularExponent);
+            if (cosAngle < fCosInnerConeAngle) {
+                scale = SkScalarMul(scale, cosAngle - fCosOuterConeAngle);
+                scale *= fConeScale;
+            }
         }
-        SkScalar scale = SkScalarPow(cosAngle, fSpecularExponent);
-        if (cosAngle < fCosInnerConeAngle) {
-            scale = SkScalarMul(scale, cosAngle - fCosOuterConeAngle);
-            return color() * SkScalarMul(scale, fConeScale);
-        }
-        return color() * scale;
+        return this->color().makeScale(scale);
     }
     GrGLLight* createGLLight() const override {
 #if SK_SUPPORT_GPU
@@ -1386,9 +1396,9 @@
 
 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 @@
                                     "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 0000000..3b5586b
--- /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;
+}
diff --git a/tests/ImageFilterTest.cpp b/tests/ImageFilterTest.cpp
index dc35332..a5ffb1b 100644
--- a/tests/ImageFilterTest.cpp
+++ b/tests/ImageFilterTest.cpp
@@ -26,6 +26,7 @@
 #include "SkPicture.h"
 #include "SkPictureImageFilter.h"
 #include "SkPictureRecorder.h"
+#include "SkPoint3.h"
 #include "SkReadBuffer.h"
 #include "SkRect.h"
 #include "SkRectShaderImageFilter.h"
@@ -244,9 +245,9 @@
         {
             // This tests for :
             // 1 ) location at (0,0,1)
-            SkPoint3 location(0, 0, SK_Scalar1);
+            SkPoint3 location = SkPoint3::Make(0, 0, SK_Scalar1);
             // 2 ) location and target at same value
-            SkPoint3 target(location.fX, location.fY, location.fZ);
+            SkPoint3 target = SkPoint3::Make(location.fX, location.fY, location.fZ);
             // 3 ) large negative specular exponent value
             SkScalar specularExponent = -1000;
 
@@ -277,8 +278,7 @@
     SkAutoTUnref<SkImageFilter> input(make_grayscale(NULL, &inputCropRect));
 
     SkAutoTUnref<SkColorFilter> cf(SkColorFilter::CreateModeFilter(SK_ColorRED, SkXfermode::kSrcIn_Mode));
-    SkPoint3 location(0, 0, SK_Scalar1);
-    SkPoint3 target(SK_Scalar1, SK_Scalar1, SK_Scalar1);
+    SkPoint3 location = SkPoint3::Make(0, 0, SK_Scalar1);
     SkScalar kernel[9] = {
         SkIntToScalar( 1), SkIntToScalar( 1), SkIntToScalar( 1),
         SkIntToScalar( 1), SkIntToScalar(-7), SkIntToScalar( 1),
@@ -411,8 +411,7 @@
     // Tests pass by not asserting.
 
     SkAutoTUnref<SkColorFilter> cf(SkColorFilter::CreateModeFilter(SK_ColorRED, SkXfermode::kSrcIn_Mode));
-    SkPoint3 location(0, 0, SK_Scalar1);
-    SkPoint3 target(SK_Scalar1, SK_Scalar1, SK_Scalar1);
+    SkPoint3 location = SkPoint3::Make(0, 0, SK_Scalar1);
     SkScalar kernel[9] = {
         SkIntToScalar( 1), SkIntToScalar( 1), SkIntToScalar( 1),
         SkIntToScalar( 1), SkIntToScalar(-7), SkIntToScalar( 1),
diff --git a/tests/Point3Test.cpp b/tests/Point3Test.cpp
new file mode 100644
index 0000000..bf3b902
--- /dev/null
+++ b/tests/Point3Test.cpp
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+// Unit tests for src/core/SkPoint3.cpp and its header
+
+#include "SkPoint3.h"
+#include "Test.h"
+
+static void test_eq_ops(skiatest::Reporter* reporter) {
+    const SkPoint3 p0 = SkPoint3::Make(0, 0, 0);
+    const SkPoint3 p1 = SkPoint3::Make(1, 1, 1);
+    const SkPoint3 p2 = SkPoint3::Make(1, 1, 1);
+
+    REPORTER_ASSERT(reporter, p0 != p1);
+    REPORTER_ASSERT(reporter, p1 == p2);
+}
+
+static void test_ops(skiatest::Reporter* reporter) {
+    SkPoint3 v = SkPoint3::Make(1, 1, 1);
+    v.normalize();
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(v.length(), SK_Scalar1));
+
+    // scale
+    SkPoint3 p = v.makeScale(3.0f);
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(p.length(), 3.0f));
+
+    p.scale(1.0f/3.0f);
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(p.length(), SK_Scalar1));
+
+    SkPoint3 p1 = SkPoint3::Make(20.0f, 2.0f, 10.0f);
+    SkPoint3 p2 = -p1;
+
+    // -
+    p = p1 - p1;
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(p.x(), 0.0f));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(p.y(), 0.0f));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(p.z(), 0.0f));
+
+    // +
+    p = p1 + p2;
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(p.x(), 0.0f));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(p.y(), 0.0f));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(p.z(), 0.0f));
+}
+
+static void test_dot(skiatest::Reporter* reporter) {
+    const SkPoint3 xAxis = SkPoint3::Make(1.0f, 0.0f, 0.0f);
+    const SkPoint3 yAxis = SkPoint3::Make(0.0f, 1.0f, 0.0f);
+    const SkPoint3 zAxis = SkPoint3::Make(0.0f, 0.0f, 1.0f);
+
+    SkScalar dot = xAxis.dot(yAxis);
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dot, 0.0f));
+
+    dot = yAxis.dot(zAxis);
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dot, 0.0f));
+
+    dot = zAxis.dot(xAxis);
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dot, 0.0f));
+
+    SkPoint3 v = SkPoint3::Make(13.0f, 2.0f, 7.0f);
+    v.normalize();
+
+    dot = v.dot(v);
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dot, 1.0f));
+
+    v = SkPoint3::Make(SK_ScalarRoot2Over2, SK_ScalarRoot2Over2, 0.0f);
+
+    dot = xAxis.dot(v);
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dot, SK_ScalarRoot2Over2));
+
+    dot = yAxis.dot(v);
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dot, SK_ScalarRoot2Over2));
+}
+
+static void test_length(skiatest::Reporter* reporter, 
+                        SkScalar x, SkScalar y, SkScalar z, SkScalar expectedLen) {
+    SkPoint3 point = SkPoint3::Make(x, y, z);
+
+    SkScalar s1 = point.length();
+    SkScalar s2 = SkPoint3::Length(x, y, z);
+
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(s1, s2));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(s1, expectedLen));
+}
+
+static void test_normalize(skiatest::Reporter* reporter,
+                           SkScalar x, SkScalar y, SkScalar z, SkScalar expectedLen) {
+    SkPoint3 point = SkPoint3::Make(x, y, z);
+
+    bool result = point.normalize();
+    SkScalar newLength = point.length();
+
+    if (0 == expectedLen) {
+        const SkPoint3 empty = SkPoint3::Make(0.0f, 0.0f, 0.0f);
+
+        REPORTER_ASSERT(reporter, SkScalarNearlyEqual(newLength, 0));
+        REPORTER_ASSERT(reporter, !result);
+        REPORTER_ASSERT(reporter, point == empty);
+    } else {
+        REPORTER_ASSERT(reporter, SkScalarNearlyEqual(newLength, SK_Scalar1));
+        REPORTER_ASSERT(reporter, result);
+    }
+}
+
+DEF_TEST(Point3, reporter) {
+    test_eq_ops(reporter);
+    test_ops(reporter);
+    test_dot(reporter);
+
+    static const struct {
+        SkScalar fX;
+        SkScalar fY;
+        SkScalar fZ;
+        SkScalar fLength;    
+    } gRec[] = {
+        { 0.0f, 0.0f, 0.0f, 0.0f },
+        { 0.3f, 0.4f, 0.5f, SK_ScalarRoot2Over2 },
+        { 1.0e-37f, 1.0e-37f, 1.0e-37f, 0.0f },  // underflows
+        { 3.4e38f, 0.0f, 0.0f, 3.4e38f }         // overflows
+    };
+
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
+        test_length(reporter, gRec[i].fX, gRec[i].fY, gRec[i].fZ, gRec[i].fLength);
+        test_normalize(reporter, gRec[i].fX, gRec[i].fY, gRec[i].fZ, gRec[i].fLength);
+    }
+}