added optimizations to speed up skinning

Docs-Preview: https://skia.org/?cl=145148
Bug: skia:
Change-Id: If27722105a1e8999f6440b6fd4044cc1f327827e
Reviewed-on: https://skia-review.googlesource.com/145148
Commit-Queue: Ruiqi Mao <ruiqimao@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
diff --git a/docs/SkCanvas_Reference.bmh b/docs/SkCanvas_Reference.bmh
index c310b11..8ec893d 100644
--- a/docs/SkCanvas_Reference.bmh
+++ b/docs/SkCanvas_Reference.bmh
@@ -5749,8 +5749,8 @@
 
 # ------------------------------------------------------------------------------
 
-#Method void drawVertices(const SkVertices* vertices, const SkMatrix* bones, int boneCount,
-                          SkBlendMode mode, const SkPaint& paint)
+#Method void drawVertices(const SkVertices* vertices, const SkVertices::Bone bones[],
+                          int boneCount, SkBlendMode mode, const SkPaint& paint)
 
 Draws Vertices vertices, a triangle mesh, using Clip and Matrix. Bone data is used to
 deform vertices with bone weights.
@@ -5759,7 +5759,7 @@
 The first element of bones should be an object to world space transformation matrix that
 will be applied before performing mesh deformations. If no such transformation is needed,
 it should be the identity matrix.
-boneCount must be at most 100, and thus the size of bones should be at most 100.
+boneCount must be at most 80, and thus the size of bones should be at most 80.
 
 #Param  vertices  triangle mesh to draw ##
 #Param  bones     bone matrix data ##
@@ -5767,7 +5767,7 @@
 #Param  mode      combines Vertices_Colors with Shader, if both are present ##
 #Param  paint     specifies the Shader, used as Vertices texture, may be nullptr ##
 
-#Example
+#NoExample
 void draw(SkCanvas* canvas) {
     SkPaint paint;
     SkPoint points[] = { { 0, 0 }, { 250, 0 }, { 100, 100 }, { 0, 250 } };
@@ -5781,10 +5781,10 @@
                                               { 1.0f, 0.0f, 0.0f, 0.0f },
                                               { 1.0f, 0.0f, 0.0f, 0.0f },
                                               { 1.0f, 0.0f, 0.0f, 0.0f } };
-    SkMatrix bones[] = { SkMatrix::I(),
-                         SkMatrix::MakeTrans(0, 20),
-                         SkMatrix::MakeTrans(50, 50),
-                         SkMatrix::MakeTrans(20, 0) };
+    SkVertices::Bone bones[] = { {{ 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f }},
+                                 {{ 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 20.0f }},
+                                 {{ 1.0f, 0.0f, 0.0f, 1.0f, 50.0f, 50.0f }},
+                                 {{ 1.0f, 0.0f, 0.0f, 1.0f, 20.0f, 0.0f }} };
     paint.setShader(SkGradientShader::MakeLinear(points, colors, nullptr, 4,
             SkShader::kClamp_TileMode));
     auto vertices = SkVertices::MakeCopy(SkVertices::kTriangleFan_VertexMode,
@@ -5799,8 +5799,8 @@
 
 # ------------------------------------------------------------------------------
 
-#Method void drawVertices(const sk_sp<SkVertices>& vertices, const SkMatrix* bones, int boneCount,
-                          SkBlendMode mode, const SkPaint& paint)
+#Method void drawVertices(const sk_sp<SkVertices>& vertices, const SkVertices::Bone bones[],
+                          int boneCount, SkBlendMode mode, const SkPaint& paint)
 
 Draws Vertices vertices, a triangle mesh, using Clip and Matrix. Bone data is used to
 deform vertices with bone weights.
@@ -5809,7 +5809,7 @@
 The first element of bones should be an object to world space transformation matrix that
 will be applied before performing mesh deformations. If no such transformation is needed,
 it should be the identity matrix.
-boneCount must be at most 100, and thus the size of bones should be at most 100.
+boneCount must be at most 80, and thus the size of bones should be at most 80.
 
 #Param  vertices  triangle mesh to draw ##
 #Param  bones     bone matrix data ##
@@ -5817,7 +5817,7 @@
 #Param  mode      combines Vertices_Colors with Shader, if both are present ##
 #Param  paint     specifies the Shader, used as Vertices texture, may be nullptr ##
 
-#Example
+#NoExample
 void draw(SkCanvas* canvas) {
     SkPaint paint;
     SkPoint points[] = { { 0, 0 }, { 250, 0 }, { 100, 100 }, { 0, 250 } };
@@ -5831,10 +5831,10 @@
                                               { 1.0f, 0.0f, 0.0f, 0.0f },
                                               { 1.0f, 0.0f, 0.0f, 0.0f },
                                               { 1.0f, 0.0f, 0.0f, 0.0f } };
-    SkMatrix bones[] = { SkMatrix::I(),
-                         SkMatrix::MakeTrans(0, 20),
-                         SkMatrix::MakeTrans(50, 50),
-                         SkMatrix::MakeTrans(20, 0) };
+    SkVertices::Bone bones[] = { {{ 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f }},
+                                 {{ 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 20.0f }},
+                                 {{ 1.0f, 0.0f, 0.0f, 1.0f, 50.0f, 50.0f }},
+                                 {{ 1.0f, 0.0f, 0.0f, 1.0f, 20.0f, 0.0f }} };
     paint.setShader(SkGradientShader::MakeLinear(points, colors, nullptr, 4,
             SkShader::kClamp_TileMode));
     auto vertices = SkVertices::MakeCopy(SkVertices::kTriangleFan_VertexMode,
diff --git a/docs/undocumented.bmh b/docs/undocumented.bmh
index 6161398..30c0657 100644
--- a/docs/undocumented.bmh
+++ b/docs/undocumented.bmh
@@ -7,7 +7,7 @@
  API
  BMP GIF HEIF ICO JPEG PNG WBMP WebP
  CPU
- GPU GPU-backed OpenGL Vulkan I/O MSAA 
+ GPU GPU-backed OpenGL Vulkan I/O MSAA
  PDF XPS
  RFC
  NaN NaNs
@@ -81,8 +81,8 @@
 #Substitute unhinted
 ##
 
-# this jargon requires a substitute to space the phrase. 
-#Topic Little_Endian 
+# this jargon requires a substitute to space the phrase.
+#Topic Little_Endian
 #Substitute little endian
 ##
 
@@ -90,11 +90,11 @@
 #Substitute big endian
 ##
 
-#Topic YUV_Component_Y 
+#Topic YUV_Component_Y
 #Substitute YUV component y
 ##
 
-#Topic YUV_Component_U 
+#Topic YUV_Component_U
 #Substitute YUV component u
 ##
 
@@ -106,7 +106,7 @@
 #Substitute UV mapping
 ##
 
-#Topic Multi_Sample_Anti_Aliasing 
+#Topic Multi_Sample_Anti_Aliasing
 #Substitute multi-sample anti-aliasing
 ##
 
@@ -114,43 +114,43 @@
 #Substitute GPU share group
 ##
 
-#Topic Bezier_Curve 
+#Topic Bezier_Curve
 #Substitute Bezier cruve
 ##
 
-#Topic Coons_Patch 
+#Topic Coons_Patch
 #Substitute Coons patch
 ##
 
-#Topic Cartesian_Coordinate 
+#Topic Cartesian_Coordinate
 #Substitute Cartesian coordinate
 ##
 
-#Topic Euclidean_Distance 
+#Topic Euclidean_Distance
 #Substitute Euclidean distance
 ##
 
-#Topic Euclidean_Space 
+#Topic Euclidean_Space
 #Substitute Euclidean space
 ##
 
-#Topic HTML_Gray 
+#Topic HTML_Gray
 #Substitute HTML gray
 ##
 
-#Topic HTML_Silver 
+#Topic HTML_Silver
 #Substitute HTML silver
 ##
 
-#Topic HTML_Lime 
+#Topic HTML_Lime
 #Substitute HTML lime
 ##
 
-#Topic HTML_Green 
+#Topic HTML_Green
 #Substitute HTML green
 ##
 
-#Topic HTML_Aqua 
+#Topic HTML_Aqua
 #Substitute HTML aqua
 ##
 
@@ -158,7 +158,7 @@
 #Substitute HTML fuchsia
 ##
 
-#Topic SVG_lightgray 
+#Topic SVG_lightgray
 #Substitute SVG light gray
 ##
 
@@ -787,6 +787,8 @@
 
 #Topic Vertices
 #Class SkVertices
+#Class Bone
+##
 ##
 #Subtopic Colors
 ##
diff --git a/docs/usingBookmaker.bmh b/docs/usingBookmaker.bmh
index f74db8f..4e952e2 100644
--- a/docs/usingBookmaker.bmh
+++ b/docs/usingBookmaker.bmh
@@ -147,6 +147,7 @@
 $ ./out/skia/bookmaker -a docs/status.json -e fiddle.json
 $ ~/go/bin/fiddlecli --input fiddle.json --output fiddleout.json
 $ ./out/skia/bookmaker -a docs/status.json -f fiddleout.json -r site/user/api -c
+$ ./out/skia/bookmaker -a docs/status.json -f fiddleout.json -r site/user/api
 $ ./out/skia/bookmaker -a docs/status.json -x
 $ ./out/skia/bookmaker -a docs/status.json -p
 ##
diff --git a/gm/skinning.cpp b/gm/skinning.cpp
index 9b59594..44a1b13 100644
--- a/gm/skinning.cpp
+++ b/gm/skinning.cpp
@@ -18,14 +18,14 @@
 static const int kColumnSize = 36;
 
 static const int kBoneCount = 7;
-static const SkMatrix kBones[] = {
-    SkMatrix::I(),
-    SkMatrix::MakeTrans(10, 0),
-    SkMatrix::MakeTrans(0, 10),
-    SkMatrix::MakeTrans(-10, 0),
-    SkMatrix::MakeTrans(0, -10),
-    SkMatrix::MakeScale(0.5f),
-    SkMatrix::MakeScale(1.5f),
+static const SkVertices::Bone kBones[] = {
+    {{ 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f }},   // SkMatrix::I()
+    {{ 1.0f, 0.0f, 0.0f, 1.0f, 10.0f, 0.0f }},  // SkMatrix::MakeTrans(10, 0)
+    {{ 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 10.0f }},  // SkMatrix::MakeTrans(0, 10)
+    {{ 1.0f, 0.0f, 0.0f, 1.0f, -10.0f, 0.0f }}, // SkMatrix::MakeTrans(-10, 0)
+    {{ 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, -10.0f }}, // SkMatrix::MakeTrans(0, -10)
+    {{ 0.5f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f }},   // SkMatrix::MakeScale(0.5)
+    {{ 1.5f, 0.0f, 0.0f, 1.5f, 0.0f, 0.0f }},   // SkMatrix::MakeScale(1.5)
 };
 
 static const int kVertexCount = 4;
@@ -60,9 +60,9 @@
     2, 3, 0,
 };
 
-// Swap two SkMatrix pointers in place.
-static void swap(const SkMatrix** x, const SkMatrix** y) {
-    const SkMatrix* temp = *x;
+// Swap two SkVertices::Bone pointers in place.
+static void swap(const SkVertices::Bone** x, const SkVertices::Bone** y) {
+    const SkVertices::Bone* temp = *x;
     *x = *y;
     *y = temp;
 }
@@ -116,7 +116,7 @@
         int ypos = kCellSize;
 
         // Create the mutable set of bones.
-        const SkMatrix* bones[kBoneCount];
+        const SkVertices::Bone* bones[kBoneCount];
         for (int i = 0; i < kBoneCount; i ++) {
             bones[i] = &kBones[i];
         }
@@ -129,14 +129,14 @@
     void drawPermutations(SkCanvas* canvas,
                           int& xpos,
                           int& ypos,
-                          const SkMatrix** bones,
+                          const SkVertices::Bone** bones,
                           int start) {
         if (start == kBoneCount) {
             // Reached the end of the permutations, so draw.
             canvas->save();
 
             // Copy the bones.
-            SkMatrix copiedBones[kBoneCount];
+            SkVertices::Bone copiedBones[kBoneCount];
             for (int i = 0; i < kBoneCount; i ++) {
                 copiedBones[i] = *bones[i];
             }
@@ -146,42 +146,11 @@
 
             // Draw the vertices.
             if (fDeformUsingCPU) {
+                // Apply the bones.
+                sk_sp<SkVertices> vertices = fVertices->applyBones(copiedBones,
+                                                                   kBoneCount);
+
                 // Deform with CPU.
-                std::vector<SkPoint> positions(kVertexCount);
-                for (int i = 0; i < kVertexCount; i ++) {
-                    const SkVertices::BoneIndices& indices = kBoneIndices[i];
-                    const SkVertices::BoneWeights& weights = kBoneWeights[i];
-
-                    // Apply deformations.
-                    SkPoint& result = positions[i];
-                    SkPoint transformed;
-                    for (uint32_t j = 0; j < 4; j ++) {
-                        // Get the bone attachment data.
-                        uint32_t index = indices.indices[j];
-                        float weight = weights.weights[j];
-
-                        // Skip the bone is there is no weight.
-                        if (weight == 0.0f) {
-                            continue;
-                        }
-                        SkASSERT(index != 0);
-
-                        // transformed = M * v
-                        copiedBones[index].mapPoints(&transformed, &kPositions[i], 1);
-
-                        // result += transformed * w
-                        result += transformed * weight;
-                    }
-                }
-
-                sk_sp<SkVertices> vertices = SkVertices::MakeCopy(SkVertices::kTriangles_VertexMode,
-                                                                  kVertexCount,
-                                                                  positions.data(),
-                                                                  nullptr,
-                                                                  kColors,
-                                                                  kIndexCount,
-                                                                  kIndices,
-                                                                  !fCache);
                 canvas->drawVertices(vertices.get(),
                                      SkBlendMode::kSrc,
                                      fPaint);
diff --git a/include/core/SkCanvas.h b/include/core/SkCanvas.h
index 594ecb3..9a743b1 100644
--- a/include/core/SkCanvas.h
+++ b/include/core/SkCanvas.h
@@ -25,6 +25,7 @@
 #include "SkPaint.h"
 #include "SkRasterHandleAllocator.h"
 #include "SkSurfaceProps.h"
+#include "SkVertices.h"
 
 class GrContext;
 class GrRenderTargetContext;
@@ -50,7 +51,6 @@
 class SkSurface;
 class SkSurface_Base;
 class SkTextBlob;
-class SkVertices;
 
 /** \class SkCanvas
     SkCanvas provides an interface for drawing, and how the drawing is clipped and transformed.
@@ -2149,7 +2149,7 @@
         The first element of bones should be an object to world space transformation matrix that
         will be applied before performing mesh deformations. If no such transformation is needed,
         it should be the identity matrix.
-        boneCount must be at most 100, and thus the size of bones should be at most 100.
+        boneCount must be at most 80, and thus the size of bones should be at most 80.
 
         @param vertices   triangle mesh to draw
         @param bones      bone matrix data
@@ -2157,7 +2157,7 @@
         @param mode       combines vertices colors with SkShader, if both are present
         @param paint      specifies the SkShader, used as SkVertices texture, may be nullptr
     */
-    void drawVertices(const SkVertices* vertices, const SkMatrix* bones, int boneCount,
+    void drawVertices(const SkVertices* vertices, const SkVertices::Bone bones[], int boneCount,
                       SkBlendMode mode, const SkPaint& paint);
 
     /** Draws SkVertices vertices, a triangle mesh, using clip and SkMatrix. Bone data is used to
@@ -2167,7 +2167,7 @@
         The first element of bones should be an object to world space transformation matrix that
         will be applied before performing mesh deformations. If no such transformation is needed,
         it should be the identity matrix.
-        boneCount must be at most 100, and thus the size of bones should be at most 100.
+        boneCount must be at most 80, and thus the size of bones should be at most 80.
 
         @param vertices   triangle mesh to draw
         @param bones      bone matrix data
@@ -2175,8 +2175,8 @@
         @param mode       combines vertices colors with SkShader, if both are present
         @param paint      specifies the SkShader, used as SkVertices texture, may be nullptr
     */
-    void drawVertices(const sk_sp<SkVertices>& vertices, const SkMatrix* bones, int boneCount,
-                      SkBlendMode mode, const SkPaint& paint);
+    void drawVertices(const sk_sp<SkVertices>& vertices, const SkVertices::Bone bones[],
+                      int boneCount, SkBlendMode mode, const SkPaint& paint);
 
     /** Draws a Coons patch: the interpolation of four cubics with shared corners,
         associating a color, and optionally a texture SkPoint, with each corner.
@@ -2496,7 +2496,7 @@
                                       const SkPaint& paint) {
         this->onDrawVerticesObject(vertices, nullptr, 0, mode, paint);
     }
-    virtual void onDrawVerticesObject(const SkVertices* vertices, const SkMatrix* bones,
+    virtual void onDrawVerticesObject(const SkVertices* vertices, const SkVertices::Bone bones[],
                                       int boneCount, SkBlendMode mode, const SkPaint& paint);
 
     virtual void onDrawImage(const SkImage* image, SkScalar dx, SkScalar dy, const SkPaint* paint);
diff --git a/include/core/SkCanvasVirtualEnforcer.h b/include/core/SkCanvasVirtualEnforcer.h
index 84b209d..7e779a1 100644
--- a/include/core/SkCanvasVirtualEnforcer.h
+++ b/include/core/SkCanvasVirtualEnforcer.h
@@ -48,8 +48,8 @@
                      const SkPaint& paint) override = 0;
     void onDrawPoints(SkCanvas::PointMode mode, size_t count, const SkPoint pts[],
                       const SkPaint& paint) override = 0;
-    void onDrawVerticesObject(const SkVertices*, const SkMatrix* bones, int boneCount, SkBlendMode,
-                              const SkPaint&) override = 0;
+    void onDrawVerticesObject(const SkVertices*, const SkVertices::Bone bones[], int boneCount,
+                              SkBlendMode, const SkPaint&) override = 0;
 
     void onDrawImage(const SkImage* image, SkScalar dx, SkScalar dy,
                      const SkPaint* paint) override = 0;
diff --git a/include/core/SkOverdrawCanvas.h b/include/core/SkOverdrawCanvas.h
index 380f9eb..aab47d4 100644
--- a/include/core/SkOverdrawCanvas.h
+++ b/include/core/SkOverdrawCanvas.h
@@ -39,8 +39,8 @@
     void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) override;
     void onDrawRRect(const SkRRect&, const SkPaint&) override;
     void onDrawPoints(PointMode, size_t, const SkPoint[], const SkPaint&) override;
-    void onDrawVerticesObject(const SkVertices*, const SkMatrix* bones, int boneCount, SkBlendMode,
-                              const SkPaint&) override;
+    void onDrawVerticesObject(const SkVertices*, const SkVertices::Bone bones[], int boneCount,
+                              SkBlendMode, const SkPaint&) override;
     void onDrawAtlas(const SkImage*, const SkRSXform[], const SkRect[], const SkColor[],
                      int, SkBlendMode, const SkRect*, const SkPaint*) override;
     void onDrawPath(const SkPath&, const SkPaint&) override;
diff --git a/include/core/SkVertices.h b/include/core/SkVertices.h
index 9c1300b..63a27a0 100644
--- a/include/core/SkVertices.h
+++ b/include/core/SkVertices.h
@@ -24,6 +24,18 @@
     // to 0.
     struct BoneIndices {
         uint32_t indices[4];
+
+        uint32_t& operator[] (int i) {
+            SkASSERT(i >= 0);
+            SkASSERT(i < 4);
+            return indices[i];
+        }
+
+        const uint32_t& operator[] (int i) const {
+            SkASSERT(i >= 0);
+            SkASSERT(i < 4);
+            return indices[i];
+        }
     };
 
     // BoneWeights stores the interpolation weight for each of the (maximum of 4) bones a given
@@ -31,6 +43,55 @@
     // slot should be 0.
     struct BoneWeights {
         float weights[4];
+
+        float& operator[] (int i) {
+            SkASSERT(i >= 0);
+            SkASSERT(i < 4);
+            return weights[i];
+        }
+
+        const float& operator[] (int i) const {
+            SkASSERT(i >= 0);
+            SkASSERT(i < 4);
+            return weights[i];
+        }
+    };
+
+    // Bone stores a 3x2 transformation matrix in column major order:
+    // | scaleX   skewX transX |
+    // |  skewY  scaleY transY |
+    // SkRSXform is insufficient because bones can have non uniform scale.
+    struct Bone {
+        float values[6];
+
+        float& operator[] (int i) {
+            SkASSERT(i >= 0);
+            SkASSERT(i < 6);
+            return values[i];
+        }
+
+        const float& operator[] (int i) const {
+            SkASSERT(i >= 0);
+            SkASSERT(i < 6);
+            return values[i];
+        }
+
+        SkPoint mapPoint(const SkPoint& point) const {
+            float x = values[0] * point.x() + values[2] * point.y() + values[4];
+            float y = values[1] * point.x() + values[3] * point.y() + values[5];
+            return SkPoint::Make(x, y);
+        }
+
+        SkRect mapRect(const SkRect& rect) const {
+            SkRect dst = SkRect::MakeEmpty();
+            SkPoint quad[4];
+            rect.toQuad(quad);
+            for (int i = 0; i < 4; i ++) {
+                quad[i] = mapPoint(quad[i]);
+            }
+            dst.setBoundsNoCheck(quad, 4);
+            return dst;
+        }
     };
 
     enum VertexMode {
@@ -168,6 +229,8 @@
 
     bool isVolatile() const { return fIsVolatile; }
 
+    sk_sp<SkVertices> applyBones(const Bone bones[], int boneCount) const;
+
     // returns approximate byte size of the vertices object
     size_t approximateSize() const;
 
diff --git a/include/utils/SkLuaCanvas.h b/include/utils/SkLuaCanvas.h
index e29e0b5..0b43da6 100644
--- a/include/utils/SkLuaCanvas.h
+++ b/include/utils/SkLuaCanvas.h
@@ -58,8 +58,8 @@
                          const SkPaint*, SrcRectConstraint) override;
     void onDrawBitmapNine(const SkBitmap&, const SkIRect& center, const SkRect& dst,
                           const SkPaint*) override;
-    void onDrawVerticesObject(const SkVertices*, const SkMatrix* bones, int boneCount, SkBlendMode,
-                              const SkPaint&) override;
+    void onDrawVerticesObject(const SkVertices*, const SkVertices::Bone bones[], int boneCount,
+                              SkBlendMode, const SkPaint&) override;
 
     void onClipRect(const SkRect&, SkClipOp, ClipEdgeStyle) override;
     void onClipRRect(const SkRRect&, SkClipOp, ClipEdgeStyle) override;
diff --git a/include/utils/SkNWayCanvas.h b/include/utils/SkNWayCanvas.h
index f7307d9..793081a 100644
--- a/include/utils/SkNWayCanvas.h
+++ b/include/utils/SkNWayCanvas.h
@@ -71,8 +71,8 @@
                          const SkPaint*) override;
     void onDrawBitmapNine(const SkBitmap&, const SkIRect& center, const SkRect& dst,
                           const SkPaint*) override;
-    void onDrawVerticesObject(const SkVertices*, const SkMatrix* bones, int boneCount, SkBlendMode,
-                              const SkPaint&) override;
+    void onDrawVerticesObject(const SkVertices*, const SkVertices::Bone bones[], int boneCount,
+                              SkBlendMode, const SkPaint&) override;
     void onDrawAtlas(const SkImage*, const SkRSXform[], const SkRect[], const SkColor[],
                      int, SkBlendMode, const SkRect*, const SkPaint*) override;
     void onDrawShadowRec(const SkPath&, const SkDrawShadowRec&) override;
diff --git a/include/utils/SkNoDrawCanvas.h b/include/utils/SkNoDrawCanvas.h
index 0e86ed6..5043131 100644
--- a/include/utils/SkNoDrawCanvas.h
+++ b/include/utils/SkNoDrawCanvas.h
@@ -75,7 +75,7 @@
                             const SkPaint*) override {}
     void onDrawBitmapLattice(const SkBitmap&, const Lattice&, const SkRect&,
                              const SkPaint*) override {}
-    void onDrawVerticesObject(const SkVertices*, const SkMatrix*, int, SkBlendMode,
+    void onDrawVerticesObject(const SkVertices*, const SkVertices::Bone[], int, SkBlendMode,
                               const SkPaint&) override {}
     void onDrawAtlas(const SkImage*, const SkRSXform[], const SkRect[], const SkColor[],
                      int, SkBlendMode, const SkRect*, const SkPaint*) override {}
diff --git a/include/utils/SkPaintFilterCanvas.h b/include/utils/SkPaintFilterCanvas.h
index 4728792..1a5b59d 100644
--- a/include/utils/SkPaintFilterCanvas.h
+++ b/include/utils/SkPaintFilterCanvas.h
@@ -88,8 +88,8 @@
                          const SkPaint*) override;
     void onDrawImageLattice(const SkImage*, const Lattice&, const SkRect&,
                             const SkPaint*) override;
-    void onDrawVerticesObject(const SkVertices*, const SkMatrix* bones, int boneCount, SkBlendMode,
-                              const SkPaint&) override;
+    void onDrawVerticesObject(const SkVertices*, const SkVertices::Bone bones[], int boneCount,
+                              SkBlendMode, const SkPaint&) override;
     void onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
                              const SkPoint texCoords[4], SkBlendMode,
                              const SkPaint& paint) override;
diff --git a/site/user/api/SkCanvas_Reference.md b/site/user/api/SkCanvas_Reference.md
index 12ff419..83a24e6 100644
--- a/site/user/api/SkCanvas_Reference.md
+++ b/site/user/api/SkCanvas_Reference.md
@@ -3808,11 +3808,11 @@
   </tr>
   <tr style='background-color: #f0f0f0; '>
     <td style='text-align: left; border: 2px solid #dddddd; padding: 8px; '></td>
-    <td style='text-align: left; border: 2px solid #dddddd; padding: 8px; '><a href='#SkCanvas_drawVertices_3'>drawVertices(const SkVertices* vertices, const SkMatrix* bones, int boneCount, SkBlendMode mode, const SkPaint& paint)</a></td>
+    <td style='text-align: left; border: 2px solid #dddddd; padding: 8px; '><a href='#SkCanvas_drawVertices_3'>drawVertices(const SkVertices* vertices, const SkVertices::Bone bones[], int boneCount, SkBlendMode mode, const SkPaint& paint)</a></td>
   </tr>
   <tr>
     <td style='text-align: left; border: 2px solid #dddddd; padding: 8px; '></td>
-    <td style='text-align: left; border: 2px solid #dddddd; padding: 8px; '><a href='#SkCanvas_drawVertices_4'>drawVertices(const sk sp&lt;SkVertices&gt;& vertices, const SkMatrix* bones, int boneCount, SkBlendMode mode, const SkPaint& paint)</a></td>
+    <td style='text-align: left; border: 2px solid #dddddd; padding: 8px; '><a href='#SkCanvas_drawVertices_4'>drawVertices(const sk sp&lt;SkVertices&gt;& vertices, const SkVertices::Bone bones[], int boneCount, SkBlendMode mode, const SkPaint& paint)</a></td>
   </tr>
 </table>
 
@@ -6534,8 +6534,8 @@
 <a name='SkCanvas_drawVertices_3'></a>
 
 <pre style="padding: 1em 1em 1em 1em; width: 62.5em;background-color: #f0f0f0">
-void <a href='#SkCanvas_drawVertices'>drawVertices</a>(const <a href='undocumented#SkVertices'>SkVertices</a>* vertices, const <a href='SkMatrix_Reference#SkMatrix'>SkMatrix</a>* bones, int boneCount, <a href='SkBlendMode_Reference#SkBlendMode'>SkBlendMode</a> mode,
-                  const <a href='SkPaint_Reference#SkPaint'>SkPaint</a>& paint)
+void <a href='#SkCanvas_drawVertices'>drawVertices</a>(const <a href='undocumented#SkVertices'>SkVertices</a>* vertices, const <a href='undocumented#SkVertices_Bone'>SkVertices::Bone</a> bones[], int boneCount,
+                  <a href='SkBlendMode_Reference#SkBlendMode'>SkBlendMode</a> mode, const <a href='SkPaint_Reference#SkPaint'>SkPaint</a>& paint)
 </pre>
 
 Draws <a href='undocumented#Vertices'>Vertices</a> <a href='#SkCanvas_drawVertices_3_vertices'>vertices</a>, a triangle mesh, using <a href='#Clip'>Clip</a> and <a href='#Matrix'>Matrix</a>. Bone data is used to
@@ -6545,7 +6545,7 @@
 The first element of <a href='#SkCanvas_drawVertices_3_bones'>bones</a> should be an object to world space transformation matrix that
 will be applied before performing mesh deformations. If no such transformation is needed,
 it should be the identity matrix.
-<a href='#SkCanvas_drawVertices_3_boneCount'>boneCount</a> must be at most 100, and thus the size of <a href='#SkCanvas_drawVertices_3_bones'>bones</a> should be at most 100.
+<a href='#SkCanvas_drawVertices_3_boneCount'>boneCount</a> must be at most 80, and thus the size of <a href='#SkCanvas_drawVertices_3_bones'>bones</a> should be at most 80.
 
 ### Parameters
 
@@ -6566,10 +6566,6 @@
   </tr>
 </table>
 
-### Example
-
-<div><fiddle-embed name="7db6ad6b01931d713d7390736239001b"></fiddle-embed></div>
-
 ### See Also
 
 <a href='#SkCanvas_drawPatch'>drawPatch</a><sup><a href='#SkCanvas_drawPatch_2'>[2]</a></sup> <a href='#SkCanvas_drawPicture'>drawPicture</a><sup><a href='#SkCanvas_drawPicture_2'>[2]</a></sup><sup><a href='#SkCanvas_drawPicture_3'>[3]</a></sup><sup><a href='#SkCanvas_drawPicture_4'>[4]</a></sup>
@@ -6579,7 +6575,7 @@
 <a name='SkCanvas_drawVertices_4'></a>
 
 <pre style="padding: 1em 1em 1em 1em; width: 62.5em;background-color: #f0f0f0">
-void <a href='#SkCanvas_drawVertices'>drawVertices</a>(const <a href='undocumented#sk_sp'>sk sp</a>&lt;<a href='undocumented#SkVertices'>SkVertices</a>&gt;& vertices, const <a href='SkMatrix_Reference#SkMatrix'>SkMatrix</a>* bones, int boneCount,
+void <a href='#SkCanvas_drawVertices'>drawVertices</a>(const <a href='undocumented#sk_sp'>sk sp</a>&lt;<a href='undocumented#SkVertices'>SkVertices</a>&gt;& vertices, const <a href='undocumented#SkVertices_Bone'>SkVertices::Bone</a> bones[], int boneCount,
                   <a href='SkBlendMode_Reference#SkBlendMode'>SkBlendMode</a> mode, const <a href='SkPaint_Reference#SkPaint'>SkPaint</a>& paint)
 </pre>
 
@@ -6590,7 +6586,7 @@
 The first element of <a href='#SkCanvas_drawVertices_4_bones'>bones</a> should be an object to world space transformation matrix that
 will be applied before performing mesh deformations. If no such transformation is needed,
 it should be the identity matrix.
-<a href='#SkCanvas_drawVertices_4_boneCount'>boneCount</a> must be at most 100, and thus the size of <a href='#SkCanvas_drawVertices_4_bones'>bones</a> should be at most 100.
+<a href='#SkCanvas_drawVertices_4_boneCount'>boneCount</a> must be at most 80, and thus the size of <a href='#SkCanvas_drawVertices_4_bones'>bones</a> should be at most 80.
 
 ### Parameters
 
@@ -6611,10 +6607,6 @@
   </tr>
 </table>
 
-### Example
-
-<div><fiddle-embed name="cc1fc7f3462abc79ec6dec3405e2812d"></fiddle-embed></div>
-
 ### See Also
 
 <a href='#SkCanvas_drawPatch'>drawPatch</a><sup><a href='#SkCanvas_drawPatch_2'>[2]</a></sup> <a href='#SkCanvas_drawPicture'>drawPicture</a><sup><a href='#SkCanvas_drawPicture_2'>[2]</a></sup><sup><a href='#SkCanvas_drawPicture_3'>[3]</a></sup><sup><a href='#SkCanvas_drawPicture_4'>[4]</a></sup>
diff --git a/site/user/api/catalog.htm b/site/user/api/catalog.htm
index 7f8fa28..5b9aea5 100644
--- a/site/user/api/catalog.htm
+++ b/site/user/api/catalog.htm
@@ -5354,22 +5354,6 @@
     "file": "SkCanvas_Reference",
     "name": "SkCanvas::drawVertices_2"
 },
-    "SkCanvas_drawVertices_3": {
-    "code": "void draw(SkCanvas* canvas) {\n    SkPaint paint;\n    SkPoint points[] = { { 0, 0 }, { 250, 0 }, { 100, 100 }, { 0, 250 } };\n    SkPoint texs[] = { { 0, 0 }, { 0, 250 }, { 250, 250 }, { 250, 0 } };\n    SkColor colors[] = { SK_ColorRED, SK_ColorBLUE, SK_ColorYELLOW, SK_ColorCYAN };\n    SkVertices::BoneIndices boneIndices[] = { { 0, 0, 0, 0 },\n                                              { 1, 0, 0, 0 },\n                                              { 2, 0, 0, 0 },\n                                              { 3, 0, 0, 0 } };\n    SkVertices::BoneWeights boneWeights[] = { { 0.0f, 0.0f, 0.0f, 0.0f },\n                                              { 1.0f, 0.0f, 0.0f, 0.0f },\n                                              { 1.0f, 0.0f, 0.0f, 0.0f },\n                                              { 1.0f, 0.0f, 0.0f, 0.0f } };\n    SkMatrix bones[] = { SkMatrix::I(),\n                         SkMatrix::MakeTrans(0, 20),\n                         SkMatrix::MakeTrans(50, 50),\n                         SkMatrix::MakeTrans(20, 0) };\n    paint.setShader(SkGradientShader::MakeLinear(points, colors, nullptr, 4,\n            SkShader::kClamp_TileMode));\n    auto vertices = SkVertices::MakeCopy(SkVertices::kTriangleFan_VertexMode,\n            SK_ARRAY_COUNT(points), points, texs, colors, boneIndices, boneWeights);\n    canvas->drawVertices(vertices.get(), bones, SK_ARRAY_COUNT(bones), SkBlendMode::kDarken, paint);\n}\n",
-    "width": 256,
-    "height": 256,
-    "hash": "7db6ad6b01931d713d7390736239001b",
-    "file": "SkCanvas_Reference",
-    "name": "SkCanvas::drawVertices_3"
-},
-    "SkCanvas_drawVertices_4": {
-    "code": "void draw(SkCanvas* canvas) {\n    SkPaint paint;\n    SkPoint points[] = { { 0, 0 }, { 250, 0 }, { 100, 100 }, { 0, 250 } };\n    SkPoint texs[] = { { 0, 0 }, { 0, 250 }, { 250, 250 }, { 250, 0 } };\n    SkColor colors[] = { SK_ColorRED, SK_ColorBLUE, SK_ColorYELLOW, SK_ColorCYAN };\n    SkVertices::BoneIndices boneIndices[] = { { 0, 0, 0, 0 },\n                                              { 1, 0, 0, 0 },\n                                              { 2, 0, 0, 0 },\n                                              { 3, 0, 0, 0 } };\n    SkVertices::BoneWeights boneWeights[] = { { 0.0f, 0.0f, 0.0f, 0.0f },\n                                              { 1.0f, 0.0f, 0.0f, 0.0f },\n                                              { 1.0f, 0.0f, 0.0f, 0.0f },\n                                              { 1.0f, 0.0f, 0.0f, 0.0f } };\n    SkMatrix bones[] = { SkMatrix::I(),\n                         SkMatrix::MakeTrans(0, 20),\n                         SkMatrix::MakeTrans(50, 50),\n                         SkMatrix::MakeTrans(20, 0) };\n    paint.setShader(SkGradientShader::MakeLinear(points, colors, nullptr, 4,\n            SkShader::kClamp_TileMode));\n    auto vertices = SkVertices::MakeCopy(SkVertices::kTriangleFan_VertexMode,\n            SK_ARRAY_COUNT(points), points, texs, colors, boneIndices, boneWeights);\n    canvas->drawVertices(vertices, bones, SK_ARRAY_COUNT(bones), SkBlendMode::kDarken, paint);\n}\n",
-    "width": 256,
-    "height": 256,
-    "hash": "cc1fc7f3462abc79ec6dec3405e2812d",
-    "file": "SkCanvas_Reference",
-    "name": "SkCanvas::drawVertices_4"
-},
     "SkCanvas_getGrContext": {
     "code": "void draw(SkCanvas* canvas) {\n    if (canvas->getGrContext()) {\n         canvas->clear(SK_ColorRED);\n    } else {\n         canvas->clear(SK_ColorBLUE);\n    }\n}\n",
     "width": 256,
diff --git a/site/user/api/undocumented.md b/site/user/api/undocumented.md
index 8c06cb2..e4ca275 100644
--- a/site/user/api/undocumented.md
+++ b/site/user/api/undocumented.md
@@ -645,6 +645,8 @@
 
 # <a name='SkVertices'>Class SkVertices</a>
 
+# <a name='SkVertices_Bone'>Class SkVertices::Bone</a>
+
 ## <a name='Colors'>Colors</a>
 
 ## <a name='Texs'>Texs</a>
diff --git a/site/user/api/usingBookmaker.md b/site/user/api/usingBookmaker.md
index 684e906..e010cd9 100644
--- a/site/user/api/usingBookmaker.md
+++ b/site/user/api/usingBookmaker.md
@@ -127,6 +127,7 @@
 $ ./out/skia/bookmaker -a docs/status.json -e fiddle.json
 $ ~/go/bin/fiddlecli --input fiddle.json --output fiddleout.json
 $ ./out/skia/bookmaker -a docs/status.json -f fiddleout.json -r site/user/api -c
+$ ./out/skia/bookmaker -a docs/status.json -f fiddleout.json -r site/user/api
 $ ./out/skia/bookmaker -a docs/status.json -x
 $ ./out/skia/bookmaker -a docs/status.json -p
 </pre>
diff --git a/src/core/SkBitmapDevice.cpp b/src/core/SkBitmapDevice.cpp
index 7b11980..52fed02 100644
--- a/src/core/SkBitmapDevice.cpp
+++ b/src/core/SkBitmapDevice.cpp
@@ -593,8 +593,8 @@
 #endif
 }
 
-void SkBitmapDevice::drawVertices(const SkVertices* vertices, const SkMatrix* bones, int boneCount,
-                                  SkBlendMode bmode, const SkPaint& paint) {
+void SkBitmapDevice::drawVertices(const SkVertices* vertices, const SkVertices::Bone bones[],
+                                  int boneCount, SkBlendMode bmode, const SkPaint& paint) {
     BDDraw(this).drawVertices(vertices->mode(), vertices->vertexCount(), vertices->positions(),
                               vertices->texCoords(), vertices->colors(), vertices->boneIndices(),
                               vertices->boneWeights(), bmode, vertices->indices(),
diff --git a/src/core/SkBitmapDevice.h b/src/core/SkBitmapDevice.h
index a578d2c..65971ed 100644
--- a/src/core/SkBitmapDevice.h
+++ b/src/core/SkBitmapDevice.h
@@ -102,7 +102,7 @@
                         const SkPaint&, SkCanvas::SrcRectConstraint) override;
 
     void drawGlyphRunList(const SkGlyphRunList& glyphRunList) override;
-    void drawVertices(const SkVertices*, const SkMatrix* bones, int boneCount, SkBlendMode,
+    void drawVertices(const SkVertices*, const SkVertices::Bone bones[], int boneCount, SkBlendMode,
                       const SkPaint& paint) override;
     void drawDevice(SkBaseDevice*, int x, int y, const SkPaint&) override;
 
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp
index 1da6e28..fb9e499 100644
--- a/src/core/SkCanvas.cpp
+++ b/src/core/SkCanvas.cpp
@@ -1678,16 +1678,16 @@
     this->onDrawVerticesObject(vertices, nullptr, 0, mode, paint);
 }
 
-void SkCanvas::drawVertices(const sk_sp<SkVertices>& vertices, const SkMatrix* bones, int boneCount,
-                            SkBlendMode mode, const SkPaint& paint) {
+void SkCanvas::drawVertices(const sk_sp<SkVertices>& vertices, const SkVertices::Bone bones[],
+                            int boneCount, SkBlendMode mode, const SkPaint& paint) {
     TRACE_EVENT0("skia", TRACE_FUNC);
     RETURN_ON_NULL(vertices);
     SkASSERT(boneCount <= 80);
     this->onDrawVerticesObject(vertices.get(), bones, boneCount, mode, paint);
 }
 
-void SkCanvas::drawVertices(const SkVertices* vertices, const SkMatrix* bones, int boneCount,
-                            SkBlendMode mode, const SkPaint& paint) {
+void SkCanvas::drawVertices(const SkVertices* vertices, const SkVertices::Bone bones[],
+                            int boneCount, SkBlendMode mode, const SkPaint& paint) {
     TRACE_EVENT0("skia", TRACE_FUNC);
     RETURN_ON_NULL(vertices);
     SkASSERT(boneCount <= 80);
@@ -2576,7 +2576,7 @@
     this->onDrawTextBlob(blob, x, y, paint);
 }
 
-void SkCanvas::onDrawVerticesObject(const SkVertices* vertices, const SkMatrix* bones,
+void SkCanvas::onDrawVerticesObject(const SkVertices* vertices, const SkVertices::Bone bones[],
                                     int boneCount, SkBlendMode bmode, const SkPaint& paint) {
     LOOPER_BEGIN(paint, nullptr)
 
diff --git a/src/core/SkColorSpaceXformCanvas.cpp b/src/core/SkColorSpaceXformCanvas.cpp
index 949dd4a..d0032b4 100644
--- a/src/core/SkColorSpaceXformCanvas.cpp
+++ b/src/core/SkColorSpaceXformCanvas.cpp
@@ -90,7 +90,8 @@
                       const SkPaint& paint) override {
         fTarget->drawPoints(mode, count, pts, fXformer->apply(paint));
     }
-    void onDrawVerticesObject(const SkVertices* vertices, const SkMatrix* bones, int boneCount,
+
+    void onDrawVerticesObject(const SkVertices* vertices, const SkVertices::Bone bones[], int boneCount,
                               SkBlendMode mode, const SkPaint& paint) override {
         sk_sp<SkVertices> copy;
         if (vertices->hasColors()) {
diff --git a/src/core/SkDevice.h b/src/core/SkDevice.h
index e00ffdd..ad07e81 100644
--- a/src/core/SkDevice.h
+++ b/src/core/SkDevice.h
@@ -213,8 +213,8 @@
                                   const SkRect& dst, const SkPaint&);
 
 
-    virtual void drawVertices(const SkVertices*, const SkMatrix* bones, int boneCount, SkBlendMode,
-                              const SkPaint&) = 0;
+    virtual void drawVertices(const SkVertices*, const SkVertices::Bone bones[], int boneCount,
+                              SkBlendMode, const SkPaint&) = 0;
     virtual void drawShadow(const SkPath&, const SkDrawShadowRec&);
 
     virtual void drawGlyphRunList(const SkGlyphRunList& glyphRunList);
@@ -420,7 +420,7 @@
     void drawPosText(const void*, size_t, const SkScalar[], int, const SkPoint&,
                      const SkPaint&) override {}
     void drawDevice(SkBaseDevice*, int, int, const SkPaint&) override {}
-    void drawVertices(const SkVertices*, const SkMatrix*, int, SkBlendMode,
+    void drawVertices(const SkVertices*, const SkVertices::Bone[], int, SkBlendMode,
                       const SkPaint&) override {}
 
 private:
diff --git a/src/core/SkDraw.h b/src/core/SkDraw.h
index ca37c09..bf023bc 100644
--- a/src/core/SkDraw.h
+++ b/src/core/SkDraw.h
@@ -74,7 +74,7 @@
                          const SkColor colors[], const SkVertices::BoneIndices boneIndices[],
                          const SkVertices::BoneWeights boneWeights[], SkBlendMode bmode,
                          const uint16_t indices[], int ptCount,
-                         const SkPaint& paint, const SkMatrix* bones, int boneCount) const;
+                         const SkPaint& paint, const SkVertices::Bone bones[], int boneCount) const;
 
     /**
      *  Overwrite the target with the path's coverage (i.e. its mask).
diff --git a/src/core/SkDraw_vertices.cpp b/src/core/SkDraw_vertices.cpp
index 4d1ccea..b9320d4 100644
--- a/src/core/SkDraw_vertices.cpp
+++ b/src/core/SkDraw_vertices.cpp
@@ -167,7 +167,8 @@
                           const SkColor colors[], const SkVertices::BoneIndices boneIndices[],
                           const SkVertices::BoneWeights boneWeights[], SkBlendMode bmode,
                           const uint16_t indices[], int indexCount,
-                          const SkPaint& paint, const SkMatrix* bones, int boneCount) const {
+                          const SkPaint& paint, const SkVertices::Bone bones[],
+                          int boneCount) const {
     SkASSERT(0 == vertexCount || vertices);
 
     // abort early if there is nothing to draw
@@ -207,11 +208,9 @@
     }
 
     constexpr size_t kDefVertexCount = 16;
-    constexpr size_t kDefBoneCount = 8;
     constexpr size_t kOuterSize = sizeof(SkTriColorShader) +
                                  sizeof(SkComposeShader) +
-                                 (2 * sizeof(SkPoint) + sizeof(SkPM4f)) * kDefVertexCount +
-                                 sizeof(SkMatrix) * kDefBoneCount;
+                                 (2 * sizeof(SkPoint) + sizeof(SkPM4f)) * kDefVertexCount;
     SkSTArenaAlloc<kOuterSize> outerAlloc;
 
     // deform vertices using the skeleton if it is passed in
@@ -219,28 +218,21 @@
         // allocate space for the deformed vertices
         SkPoint* deformed = outerAlloc.makeArray<SkPoint>(vertexCount);
 
-        // get the bone matrices
-        SkMatrix* transformedBones = outerAlloc.makeArray<SkMatrix>(boneCount);
-
-        // transform the bone matrices by the world transform
-        transformedBones[0] = bones[0];
-        for (int i = 1; i < boneCount; i ++) {
-            transformedBones[i] = SkMatrix::Concat(bones[i], bones[0]);
-        }
-
         // deform the vertices
         if (boneIndices && boneWeights) {
             for (int i = 0; i < vertexCount; i ++) {
                 const SkVertices::BoneIndices& indices = boneIndices[i];
                 const SkVertices::BoneWeights& weights = boneWeights[i];
 
+                // apply the world transform
+                SkPoint worldPoint = bones[0].mapPoint(vertices[i]);
+
                 // apply bone deformations
-                SkPoint result = SkPoint::Make(0.0f, 0.0f);
-                SkPoint transformed;
+                deformed[i] = SkPoint::Make(0.0f, 0.0f);
                 for (uint32_t j = 0; j < 4; j ++) {
                     // get the attachment data
-                    uint32_t index = indices.indices[j];
-                    float weight = weights.weights[j];
+                    uint32_t index = indices[j];
+                    float weight = weights[j];
 
                     // skip the bone if there is no weight
                     if (weight == 0.0f) {
@@ -248,19 +240,14 @@
                     }
                     SkASSERT(index != 0);
 
-                    // transformed = M * v
-                    transformedBones[index].mapPoints(&transformed, &vertices[i], 1);
-
-                    // result += transformed * w
-                    result += transformed * weight;
+                    // deformed += M * v * w
+                    deformed[i] += bones[index].mapPoint(worldPoint) * weight;
                 }
-
-                // set the deformed point
-                deformed[i] = result;
             }
         } else {
             // no bones, so only apply world transform
-            const SkMatrix& worldTransform = bones[0];
+            SkMatrix worldTransform = SkMatrix::I();
+            worldTransform.setAffine(bones[0].values);
             worldTransform.mapPoints(deformed, vertices, vertexCount);
         }
 
diff --git a/src/core/SkLiteDL.cpp b/src/core/SkLiteDL.cpp
index ea3ca6d..d4e3901 100644
--- a/src/core/SkLiteDL.cpp
+++ b/src/core/SkLiteDL.cpp
@@ -455,7 +455,7 @@
         SkBlendMode mode;
         SkPaint paint;
         void draw(SkCanvas* c, const SkMatrix&) const {
-            c->drawVertices(vertices, pod<SkMatrix>(this), boneCount, mode, paint);
+            c->drawVertices(vertices, pod<SkVertices::Bone>(this), boneCount, mode, paint);
         }
     };
     struct DrawAtlas final : Op {
@@ -660,9 +660,9 @@
     void* pod = this->push<DrawPoints>(count*sizeof(SkPoint), mode, count, paint);
     copy_v(pod, points,count);
 }
-void SkLiteDL::drawVertices(const SkVertices* vertices, const SkMatrix* bones, int boneCount,
-                            SkBlendMode mode, const SkPaint& paint) {
-    void* pod = this->push<DrawVertices>(boneCount * sizeof(SkMatrix),
+void SkLiteDL::drawVertices(const SkVertices* vertices, const SkVertices::Bone bones[],
+                            int boneCount, SkBlendMode mode, const SkPaint& paint) {
+    void* pod = this->push<DrawVertices>(boneCount * sizeof(SkVertices::Bone),
                                          vertices,
                                          boneCount,
                                          mode,
diff --git a/src/core/SkLiteDL.h b/src/core/SkLiteDL.h
index aa32e02..ffcdb89 100644
--- a/src/core/SkLiteDL.h
+++ b/src/core/SkLiteDL.h
@@ -72,7 +72,7 @@
     void drawPatch(const SkPoint[12], const SkColor[4], const SkPoint[4],
                    SkBlendMode, const SkPaint&);
     void drawPoints(SkCanvas::PointMode, size_t, const SkPoint[], const SkPaint&);
-    void drawVertices(const SkVertices*, const SkMatrix* bones, int boneCount, SkBlendMode,
+    void drawVertices(const SkVertices*, const SkVertices::Bone bones[], int boneCount, SkBlendMode,
                       const SkPaint&);
     void drawAtlas(const SkImage*, const SkRSXform[], const SkRect[], const SkColor[], int,
                    SkBlendMode, const SkRect*, const SkPaint*);
diff --git a/src/core/SkLiteRecorder.cpp b/src/core/SkLiteRecorder.cpp
index 971e62c..fedfa99 100644
--- a/src/core/SkLiteRecorder.cpp
+++ b/src/core/SkLiteRecorder.cpp
@@ -175,8 +175,9 @@
                                   const SkPaint& paint) {
     fDL->drawPoints(mode, count, pts, paint);
 }
-void SkLiteRecorder::onDrawVerticesObject(const SkVertices* vertices, const SkMatrix* bones,
-                                          int boneCount, SkBlendMode mode, const SkPaint& paint) {
+void SkLiteRecorder::onDrawVerticesObject(const SkVertices* vertices,
+                                          const SkVertices::Bone bones[], int boneCount,
+                                          SkBlendMode mode, const SkPaint& paint) {
     fDL->drawVertices(vertices, bones, boneCount, mode, paint);
 }
 void SkLiteRecorder::onDrawAtlas(const SkImage* atlas,
diff --git a/src/core/SkLiteRecorder.h b/src/core/SkLiteRecorder.h
index bc37546..5ae08fa 100644
--- a/src/core/SkLiteRecorder.h
+++ b/src/core/SkLiteRecorder.h
@@ -73,8 +73,8 @@
     void onDrawPatch(const SkPoint[12], const SkColor[4],
                      const SkPoint[4], SkBlendMode, const SkPaint&) override;
     void onDrawPoints(PointMode, size_t count, const SkPoint pts[], const SkPaint&) override;
-    void onDrawVerticesObject(const SkVertices*, const SkMatrix* bones, int boneCount, SkBlendMode,
-                              const SkPaint&) override;
+    void onDrawVerticesObject(const SkVertices*, const SkVertices::Bone bones[], int boneCount,
+                              SkBlendMode, const SkPaint&) override;
     void onDrawAtlas(const SkImage*, const SkRSXform[], const SkRect[], const SkColor[],
                      int, SkBlendMode, const SkRect*, const SkPaint*) override;
     void onDrawShadowRec(const SkPath&, const SkDrawShadowRec&) override;
diff --git a/src/core/SkOverdrawCanvas.cpp b/src/core/SkOverdrawCanvas.cpp
index 0e20878..4e6b8c5 100644
--- a/src/core/SkOverdrawCanvas.cpp
+++ b/src/core/SkOverdrawCanvas.cpp
@@ -216,9 +216,9 @@
     fList[0]->onDrawPoints(mode, count, points, this->overdrawPaint(paint));
 }
 
-void SkOverdrawCanvas::onDrawVerticesObject(const SkVertices* vertices, const SkMatrix* bones,
-                                            int boneCount, SkBlendMode blendMode,
-                                            const SkPaint& paint) {
+void SkOverdrawCanvas::onDrawVerticesObject(const SkVertices* vertices,
+                                            const SkVertices::Bone bones[], int boneCount,
+                                            SkBlendMode blendMode, const SkPaint& paint) {
     fList[0]->onDrawVerticesObject(vertices,
                                    bones,
                                    boneCount,
diff --git a/src/core/SkPicturePlayback.cpp b/src/core/SkPicturePlayback.cpp
index f63abe7..4ee0eae 100644
--- a/src/core/SkPicturePlayback.cpp
+++ b/src/core/SkPicturePlayback.cpp
@@ -616,9 +616,9 @@
             const SkPaint* paint = fPictureData->getPaint(reader);
             const SkVertices* vertices = fPictureData->getVertices(reader);
             const int boneCount = reader->readInt();
-            const SkMatrix* bones = boneCount ?
-                                    (const SkMatrix*) reader->skip(boneCount, sizeof(SkMatrix)) :
-                                    nullptr;
+            const SkVertices::Bone* bones = boneCount ?
+                    (const SkVertices::Bone*) reader->skip(boneCount, sizeof(SkVertices::Bone)) :
+                    nullptr;
             SkBlendMode bmode = reader->read32LE(SkBlendMode::kLastMode);
             BREAK_ON_READ_ERROR(reader);
 
diff --git a/src/core/SkPictureRecord.cpp b/src/core/SkPictureRecord.cpp
index ca36e7e..81e5135 100644
--- a/src/core/SkPictureRecord.cpp
+++ b/src/core/SkPictureRecord.cpp
@@ -682,16 +682,17 @@
     this->validate(initialOffset, size);
 }
 
-void SkPictureRecord::onDrawVerticesObject(const SkVertices* vertices, const SkMatrix* bones,
-                                           int boneCount, SkBlendMode mode, const SkPaint& paint) {
+void SkPictureRecord::onDrawVerticesObject(const SkVertices* vertices,
+                                           const SkVertices::Bone bones[], int boneCount,
+                                           SkBlendMode mode, const SkPaint& paint) {
     // op + paint index + vertices index + number of bones + bone matrices + mode
-    size_t size = 5 * kUInt32Size + boneCount * sizeof(SkMatrix);
+    size_t size = 5 * kUInt32Size + boneCount * sizeof(SkVertices::Bone);
     size_t initialOffset = this->addDraw(DRAW_VERTICES_OBJECT, &size);
 
     this->addPaint(paint);
     this->addVertices(vertices);
     this->addInt(boneCount);
-    fWriter.write(bones, boneCount * sizeof(SkMatrix));
+    fWriter.write(bones, boneCount * sizeof(SkVertices::Bone));
     this->addInt(static_cast<uint32_t>(mode));
 
     this->validate(initialOffset, size);
diff --git a/src/core/SkPictureRecord.h b/src/core/SkPictureRecord.h
index dc9f4d9..dcec581 100644
--- a/src/core/SkPictureRecord.h
+++ b/src/core/SkPictureRecord.h
@@ -196,8 +196,8 @@
     void onDrawImageLattice(const SkImage*, const SkCanvas::Lattice& lattice, const SkRect& dst,
                             const SkPaint*) override;
     void onDrawShadowRec(const SkPath&, const SkDrawShadowRec&) override;
-    void onDrawVerticesObject(const SkVertices*, const SkMatrix* bones, int boneCount, SkBlendMode,
-                              const SkPaint&) override;
+    void onDrawVerticesObject(const SkVertices*, const SkVertices::Bone bones[], int boneCount,
+                              SkBlendMode, const SkPaint&) override;
 
     void onClipRect(const SkRect&, SkClipOp, ClipEdgeStyle) override;
     void onClipRRect(const SkRRect&, SkClipOp, ClipEdgeStyle) override;
diff --git a/src/core/SkRecorder.cpp b/src/core/SkRecorder.cpp
index 1abd582..f7c8dd4 100644
--- a/src/core/SkRecorder.cpp
+++ b/src/core/SkRecorder.cpp
@@ -317,7 +317,7 @@
     }
 }
 
-void SkRecorder::onDrawVerticesObject(const SkVertices* vertices, const SkMatrix* bones,
+void SkRecorder::onDrawVerticesObject(const SkVertices* vertices, const SkVertices::Bone bones[],
                                       int boneCount, SkBlendMode bmode, const SkPaint& paint) {
     this->append<SkRecords::DrawVertices>(paint,
                                           sk_ref_sp(const_cast<SkVertices*>(vertices)),
diff --git a/src/core/SkRecorder.h b/src/core/SkRecorder.h
index 7fed8ea..ce1131f 100644
--- a/src/core/SkRecorder.h
+++ b/src/core/SkRecorder.h
@@ -121,8 +121,8 @@
                             const SkPaint*) override;
     void onDrawBitmapLattice(const SkBitmap&, const Lattice& lattice, const SkRect& dst,
                              const SkPaint*) override;
-    void onDrawVerticesObject(const SkVertices*, const SkMatrix* bones, int boneCount, SkBlendMode,
-                              const SkPaint&) override;
+    void onDrawVerticesObject(const SkVertices*, const SkVertices::Bone bones[], int boneCount,
+                              SkBlendMode, const SkPaint&) override;
     void onDrawAtlas(const SkImage*, const SkRSXform[], const SkRect[], const SkColor[],
                      int count, SkBlendMode, const SkRect* cull, const SkPaint*) override;
     void onDrawShadowRec(const SkPath&, const SkDrawShadowRec&) override;
diff --git a/src/core/SkRecords.h b/src/core/SkRecords.h
index 4ec2ae6..cce68c9 100644
--- a/src/core/SkRecords.h
+++ b/src/core/SkRecords.h
@@ -344,7 +344,7 @@
 RECORD(DrawVertices, kDraw_Tag|kHasPaint_Tag,
         SkPaint paint;
         sk_sp<SkVertices> vertices;
-        PODArray<SkMatrix> bones;
+        PODArray<SkVertices::Bone> bones;
         int boneCount;
         SkBlendMode bmode);
 RECORD(DrawShadowRec, kDraw_Tag,
diff --git a/src/core/SkVertices.cpp b/src/core/SkVertices.cpp
index 97a136c..a3a3989 100644
--- a/src/core/SkVertices.cpp
+++ b/src/core/SkVertices.cpp
@@ -207,6 +207,69 @@
     return const_cast<uint16_t*>(fVertices->indices());
 }
 
+/** Makes a copy of the SkVertices and applies a set of bones, then returns the deformed
+    vertices.
+
+    @param bones      The bones to apply.
+    @param boneCount  The number of bones.
+    @return           The transformed SkVertices.
+*/
+sk_sp<SkVertices> SkVertices::applyBones(const SkVertices::Bone bones[], int boneCount) const {
+    // If there aren't any bones, then nothing changes.
+    // We don't check if the SkVertices object has bone indices/weights because there is the case
+    // where the object can have no indices/weights but still have a world transform applied.
+    if (!bones || !boneCount) {
+        return sk_ref_sp(this);
+    }
+    SkASSERT(boneCount >= 1);
+
+    // Copy the SkVertices.
+    sk_sp<SkVertices> copy = SkVertices::MakeCopy(this->mode(),
+                                                  this->vertexCount(),
+                                                  this->positions(),
+                                                  this->texCoords(),
+                                                  this->colors(),
+                                                  nullptr,
+                                                  nullptr,
+                                                  this->indexCount(),
+                                                  this->indices());
+
+    // Transform the positions.
+    for (int i = 0; i < this->vertexCount(); i++) {
+        SkPoint& position = copy->fPositions[i];
+
+        // Apply the world transform.
+        position = bones[0].mapPoint(position);
+
+        // Apply the bone deformations.
+        if (boneCount > 1) {
+            SkASSERT(this->boneIndices());
+            SkASSERT(this->boneWeights());
+
+            SkPoint result = SkPoint::Make(0.0f, 0.0f);
+            const SkVertices::BoneIndices& indices = this->boneIndices()[i];
+            const SkVertices::BoneWeights& weights = this->boneWeights()[i];
+            for (int j = 0; j < 4; j++) {
+                int index = indices[j];
+                float weight = weights[j];
+                if (index == 0 || weight == 0.0f) {
+                    continue;
+                }
+                SkASSERT(index < boneCount);
+
+                // result += M * v * w.
+                result += bones[index].mapPoint(position) * weight;
+            }
+            position = result;
+        }
+    }
+
+    // Recalculate the bounds.
+    copy->fBounds.set(copy->fPositions, copy->fVertexCnt);
+
+    return copy;
+}
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 sk_sp<SkVertices> SkVertices::MakeCopy(VertexMode mode, int vertexCount,
diff --git a/src/gpu/GrDefaultGeoProcFactory.cpp b/src/gpu/GrDefaultGeoProcFactory.cpp
index 027296c..9d61fb3 100644
--- a/src/gpu/GrDefaultGeoProcFactory.cpp
+++ b/src/gpu/GrDefaultGeoProcFactory.cpp
@@ -31,8 +31,9 @@
     kBonesAttribute_GPFlag          = 0x10,
 };
 
-static constexpr int kMaxBones = 80; // Due to GPU memory limitations, only up to 80 bone
-                                     // matrices are accepted.
+static constexpr int kNumVec2sPerBone = 3; // Our bone matrices are 3x2 matrices passed in as
+                                           // vec2s in column major order, and thus there are 3
+                                           // vec2s per bone.
 
 class DefaultGeoProc : public GrGeometryProcessor {
 public:
@@ -111,21 +112,36 @@
             }
 
             // Setup bone transforms
+            // NOTE: This code path is currently unused. Benchmarks have found that for all
+            // reasonable cases of skinned vertices, the overhead involved in copying and uploading
+            // bone data makes performing the transformations on the CPU faster than doing so on
+            // the GPU. This is being kept here in case that changes.
             const char* transformedPositionName = gp.fInPosition.name();
             if (gp.hasBones()) {
+                // Set up the uniform for the bones.
                 const char* vertBonesUniformName;
                 fBonesUniform = uniformHandler->addUniformArray(kVertex_GrShaderFlag,
-                                                                kFloat3x3_GrSLType,
+                                                                kFloat2_GrSLType,
                                                                 "Bones",
-                                                                kMaxBones,
+                                                                kMaxBones * kNumVec2sPerBone,
                                                                 &vertBonesUniformName);
+
+                // Set up the bone application function.
+                SkString applyBoneFunctionName;
+                this->emitApplyBoneFunction(vertBuilder,
+                                            vertBonesUniformName,
+                                            &applyBoneFunctionName);
+
+                // Apply the world transform to the position first.
                 vertBuilder->codeAppendf(
-                        "float3 originalPosition = %s[0] * float3(%s, 1);"
-                        "float2 transformedPosition = float2(0);"
+                        "float2 worldPosition = %s(0, %s);"
+                        "float2 transformedPosition = float2(0, 0);"
                         "for (int i = 0; i < 4; i++) {",
-                        vertBonesUniformName,
+                        applyBoneFunctionName.c_str(),
                         gp.fInPosition.name());
 
+                // If the GPU supports unsigned integers, then we can read the index. Otherwise,
+                // we have to estimate it given the float representation.
                 if (args.fShaderCaps->unsignedSupport()) {
                     vertBuilder->codeAppendf(
                         "    byte index = %s[i];",
@@ -136,12 +152,13 @@
                         gp.fInBoneIndices.name());
                 }
 
+                // Get the weight and apply the transformation.
                 vertBuilder->codeAppendf(
                         "    float weight = %s[i];"
-                        "    transformedPosition += (%s[index] * originalPosition * weight).xy;"
+                        "    transformedPosition += %s(index, worldPosition) * weight;"
                         "}",
                         gp.fInBoneWeights.name(),
-                        vertBonesUniformName);
+                        applyBoneFunctionName.c_str());
                 transformedPositionName = "transformedPosition";
             }
 
@@ -228,11 +245,41 @@
             fColorSpaceHelper.setData(pdman, dgp.fColorSpaceXform.get());
 
             if (dgp.hasBones()) {
-                pdman.setMatrix3fv(fBonesUniform, dgp.boneCount(), dgp.bones());
+                pdman.set2fv(fBonesUniform, dgp.boneCount() * kNumVec2sPerBone, dgp.bones());
             }
         }
 
     private:
+        void emitApplyBoneFunction(GrGLSLVertexBuilder* vertBuilder,
+                                   const char* vertBonesUniformName,
+                                   SkString* funcName) {
+                // The bone matrices are passed in as 3x2 matrices in column-major order as groups
+                // of 3 float2s. This code takes those float2s and performs the matrix operation on
+                // a given matrix and float2.
+                static const GrShaderVar gApplyBoneArgs[] = {
+                    GrShaderVar("index", kByte_GrSLType),
+                    GrShaderVar("vec", kFloat2_GrSLType),
+                };
+                SkString body;
+                body.appendf(
+                    "    float2 c0 = %s[index * 3];"
+                    "    float2 c1 = %s[index * 3 + 1];"
+                    "    float2 c2 = %s[index * 3 + 2];"
+                    "    float x = c0.x * vec.x + c1.x * vec.y + c2.x;"
+                    "    float y = c0.y * vec.x + c1.y * vec.y + c2.y;"
+                    "    return float2(x, y);",
+                    vertBonesUniformName,
+                    vertBonesUniformName,
+                    vertBonesUniformName);
+                vertBuilder->emitFunction(kFloat2_GrSLType,
+                                          "applyBone",
+                                          SK_ARRAY_COUNT(gApplyBoneArgs),
+                                          gApplyBoneArgs,
+                                          body.c_str(),
+                                          funcName);
+        }
+
+    private:
         SkMatrix fViewMatrix;
         GrColor fColor;
         uint8_t fCoverage;
@@ -336,13 +383,13 @@
 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DefaultGeoProc);
 
 #if GR_TEST_UTILS
-static constexpr int kNumFloatsPerSkMatrix = 9;
+static constexpr int kNumFloatsPerBone = 6;
 static constexpr int kTestBoneCount = 4;
-static constexpr float kTestBones[kTestBoneCount * kNumFloatsPerSkMatrix] = {
-    1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
-    1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
-    1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
-    1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
+static constexpr float kTestBones[kTestBoneCount * kNumFloatsPerBone] = {
+    1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
+    1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
+    1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
+    1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
 };
 
 sk_sp<GrGeometryProcessor> DefaultGeoProc::TestCreate(GrProcessorTestData* d) {
diff --git a/src/gpu/GrDefaultGeoProcFactory.h b/src/gpu/GrDefaultGeoProcFactory.h
index 3c4bd8e..b5aef98 100644
--- a/src/gpu/GrDefaultGeoProcFactory.h
+++ b/src/gpu/GrDefaultGeoProcFactory.h
@@ -12,6 +12,8 @@
 #include "GrGeometryProcessor.h"
 #include "GrShaderCaps.h"
 
+constexpr int kMaxBones = 80; // Supports up to 80 bones per mesh.
+
 /*
  * A factory for creating default Geometry Processors which simply multiply position by the uniform
  * view matrix and wire through color, coverage, UV coords if requested.
diff --git a/src/gpu/GrRenderTargetContext.cpp b/src/gpu/GrRenderTargetContext.cpp
index 787bf7c..9664109 100644
--- a/src/gpu/GrRenderTargetContext.cpp
+++ b/src/gpu/GrRenderTargetContext.cpp
@@ -839,7 +839,7 @@
                                          GrPaint&& paint,
                                          const SkMatrix& viewMatrix,
                                          sk_sp<SkVertices> vertices,
-                                         const SkMatrix bones[],
+                                         const SkVertices::Bone bones[],
                                          int boneCount,
                                          GrPrimitiveType* overridePrimType) {
     ASSERT_SINGLE_OWNER
diff --git a/src/gpu/GrRenderTargetContext.h b/src/gpu/GrRenderTargetContext.h
index b0756a0..808d442 100644
--- a/src/gpu/GrRenderTargetContext.h
+++ b/src/gpu/GrRenderTargetContext.h
@@ -215,7 +215,7 @@
                       GrPaint&& paint,
                       const SkMatrix& viewMatrix,
                       sk_sp<SkVertices> vertices,
-                      const SkMatrix bones[],
+                      const SkVertices::Bone bones[],
                       int boneCount,
                       GrPrimitiveType* overridePrimType = nullptr);
 
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index 1fe2ae4..6c3bd0a 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -1484,7 +1484,7 @@
 
 void SkGpuDevice::wireframeVertices(SkVertices::VertexMode vmode, int vertexCount,
                                     const SkPoint vertices[],
-                                    const SkMatrix bones[], int boneCount,
+                                    const SkVertices::Bone bones[], int boneCount,
                                     SkBlendMode bmode,
                                     const uint16_t indices[], int indexCount,
                                     const SkPaint& paint) {
@@ -1548,8 +1548,8 @@
                                        &primitiveType);
 }
 
-void SkGpuDevice::drawVertices(const SkVertices* vertices, const SkMatrix bones[], int boneCount,
-                               SkBlendMode mode, const SkPaint& paint) {
+void SkGpuDevice::drawVertices(const SkVertices* vertices, const SkVertices::Bone bones[],
+                               int boneCount, SkBlendMode mode, const SkPaint& paint) {
     ASSERT_SINGLE_OWNER
     GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawVertices", fContext.get());
 
diff --git a/src/gpu/SkGpuDevice.h b/src/gpu/SkGpuDevice.h
index a3233ad..555e25e 100644
--- a/src/gpu/SkGpuDevice.h
+++ b/src/gpu/SkGpuDevice.h
@@ -87,7 +87,7 @@
     void drawPosText(const void* text, size_t len, const SkScalar pos[],
                      int scalarsPerPos, const SkPoint& offset, const SkPaint&) override;
     void drawGlyphRunList(const SkGlyphRunList& glyphRunList) override;
-    void drawVertices(const SkVertices*, const SkMatrix bones[], int boneCount, SkBlendMode,
+    void drawVertices(const SkVertices*, const SkVertices::Bone bones[], int boneCount, SkBlendMode,
                       const SkPaint&) override;
     void drawShadow(const SkPath&, const SkDrawShadowRec&) override;
     void drawAtlas(const SkImage* atlas, const SkRSXform[], const SkRect[],
@@ -244,7 +244,7 @@
     void drawStrokedLine(const SkPoint pts[2], const SkPaint&);
 
     void wireframeVertices(SkVertices::VertexMode, int vertexCount, const SkPoint verts[],
-                           const SkMatrix bones[], int boneCount, SkBlendMode,
+                           const SkVertices::Bone bones[], int boneCount, SkBlendMode,
                            const uint16_t indices[], int indexCount, const SkPaint&);
 
     static sk_sp<GrRenderTargetContext> MakeRenderTargetContext(GrContext*,
diff --git a/src/gpu/ops/GrDrawVerticesOp.cpp b/src/gpu/ops/GrDrawVerticesOp.cpp
index dd91452..04b537f 100644
--- a/src/gpu/ops/GrDrawVerticesOp.cpp
+++ b/src/gpu/ops/GrDrawVerticesOp.cpp
@@ -12,12 +12,10 @@
 #include "SkGr.h"
 #include "SkRectPriv.h"
 
-static constexpr int kNumFloatsPerSkMatrix = 9;
-
 std::unique_ptr<GrDrawOp> GrDrawVerticesOp::Make(GrContext* context,
                                                  GrPaint&& paint,
                                                  sk_sp<SkVertices> vertices,
-                                                 const SkMatrix bones[],
+                                                 const SkVertices::Bone bones[],
                                                  int boneCount,
                                                  const SkMatrix& viewMatrix,
                                                  GrAAType aaType,
@@ -32,7 +30,7 @@
 }
 
 GrDrawVerticesOp::GrDrawVerticesOp(const Helper::MakeArgs& helperArgs, GrColor color,
-                                   sk_sp<SkVertices> vertices, const SkMatrix bones[],
+                                   sk_sp<SkVertices> vertices, const SkVertices::Bone bones[],
                                    int boneCount, GrPrimitiveType primitiveType, GrAAType aaType,
                                    sk_sp<GrColorSpaceXform> colorSpaceXform,
                                    const SkMatrix& viewMatrix)
@@ -51,26 +49,23 @@
     mesh.fColor = color;
     mesh.fViewMatrix = viewMatrix;
     mesh.fVertices = std::move(vertices);
-    if (bones) {
-        // Copy the bone data over in the format that the GPU would upload.
-        mesh.fBones.reserve(boneCount * kNumFloatsPerSkMatrix);
-        for (int i = 0; i < boneCount; i ++) {
-            const SkMatrix& matrix = bones[i];
-            mesh.fBones.push_back(matrix.get(SkMatrix::kMScaleX));
-            mesh.fBones.push_back(matrix.get(SkMatrix::kMSkewY));
-            mesh.fBones.push_back(matrix.get(SkMatrix::kMPersp0));
-            mesh.fBones.push_back(matrix.get(SkMatrix::kMSkewX));
-            mesh.fBones.push_back(matrix.get(SkMatrix::kMScaleY));
-            mesh.fBones.push_back(matrix.get(SkMatrix::kMPersp1));
-            mesh.fBones.push_back(matrix.get(SkMatrix::kMTransX));
-            mesh.fBones.push_back(matrix.get(SkMatrix::kMTransY));
-            mesh.fBones.push_back(matrix.get(SkMatrix::kMPersp2));
-        }
-    }
     mesh.fIgnoreTexCoords = false;
     mesh.fIgnoreColors = false;
     mesh.fIgnoreBones = false;
 
+    if (mesh.fVertices->hasBones() && bones) {
+        // Perform the transformations on the CPU instead of the GPU.
+        mesh.fVertices = mesh.fVertices->applyBones(bones, boneCount);
+    } else {
+        if (bones && boneCount > 1) {
+            // NOTE: This should never be used. All bone transforms are being done on the CPU
+            // instead of the GPU.
+
+            // Copy the bone data.
+            fBones.assign(bones, bones + boneCount);
+        }
+    }
+
     fFlags = 0;
     if (mesh.hasPerVertexColors()) {
         fFlags |= kRequiresPerVertexColors_Flag;
@@ -85,7 +80,9 @@
     // Special case for meshes with a world transform but no bone weights.
     // These will be considered normal vertices draws without bones.
     if (!mesh.fVertices->hasBones() && boneCount == 1) {
-        mesh.fViewMatrix.preConcat(bones[0]);
+        SkMatrix worldTransform;
+        worldTransform.setAffine(bones[0].values);
+        mesh.fViewMatrix.preConcat(worldTransform);
     }
 
     IsZeroArea zeroArea;
@@ -101,7 +98,7 @@
         SkRect bounds = SkRect::MakeEmpty();
         const SkRect originalBounds = bones[0].mapRect(mesh.fVertices->bounds());
         for (int i = 1; i < boneCount; i++) {
-            const SkMatrix& matrix = bones[i];
+            const SkVertices::Bone& matrix = bones[i];
             bounds.join(matrix.mapRect(originalBounds));
         }
 
@@ -188,7 +185,9 @@
 
     const SkMatrix& vm = this->hasMultipleViewMatrices() ? SkMatrix::I() : fMeshes[0].fViewMatrix;
 
-    Bones bones(fMeshes[0].fBones.data(), fMeshes[0].fBones.size() / kNumFloatsPerSkMatrix);
+    // The bones are packed as 6 floats in column major order, so we can directly upload them to
+    // the GPU as groups of 3 vec2s.
+    Bones bones(reinterpret_cast<const float*>(fBones.data()), fBones.size());
     *hasBoneAttribute = this->hasBones();
 
     if (this->hasBones()) {
diff --git a/src/gpu/ops/GrDrawVerticesOp.h b/src/gpu/ops/GrDrawVerticesOp.h
index 07227e0..fd40e461 100644
--- a/src/gpu/ops/GrDrawVerticesOp.h
+++ b/src/gpu/ops/GrDrawVerticesOp.h
@@ -38,16 +38,16 @@
     static std::unique_ptr<GrDrawOp> Make(GrContext* context,
                                           GrPaint&&,
                                           sk_sp<SkVertices>,
-                                          const SkMatrix bones[],
+                                          const SkVertices::Bone bones[],
                                           int boneCount,
                                           const SkMatrix& viewMatrix,
                                           GrAAType,
                                           sk_sp<GrColorSpaceXform>,
                                           GrPrimitiveType* overridePrimType = nullptr);
 
-    GrDrawVerticesOp(const Helper::MakeArgs&, GrColor, sk_sp<SkVertices>, const SkMatrix bones[],
-                     int boneCount, GrPrimitiveType, GrAAType, sk_sp<GrColorSpaceXform>,
-                     const SkMatrix& viewMatrix);
+    GrDrawVerticesOp(const Helper::MakeArgs&, GrColor, sk_sp<SkVertices>,
+                     const SkVertices::Bone bones[], int boneCount, GrPrimitiveType, GrAAType,
+                     sk_sp<GrColorSpaceXform>, const SkMatrix& viewMatrix);
 
     const char* name() const override { return "DrawVerticesOp"; }
 
@@ -103,7 +103,6 @@
     struct Mesh {
         GrColor fColor;  // Used if this->hasPerVertexColors() is false.
         sk_sp<SkVertices> fVertices;
-        std::vector<float> fBones; // Transformation matrices stored in GPU format.
         SkMatrix fViewMatrix;
         bool fIgnoreTexCoords;
         bool fIgnoreColors;
@@ -118,7 +117,7 @@
         }
 
         bool hasBones() const {
-            return fVertices->hasBones() && fBones.size() && !fIgnoreBones;
+            return fVertices->hasBones() && !fIgnoreBones;
         }
     };
 
@@ -152,6 +151,7 @@
 
     Helper fHelper;
     SkSTArray<1, Mesh, true> fMeshes;
+    std::vector<SkVertices::Bone> fBones; // Bone transformation matrices.
     // GrPrimitiveType is more expressive than fVertices.mode() so it is used instead and we ignore
     // the SkVertices mode (though fPrimitiveType may have been inferred from it).
     GrPrimitiveType fPrimitiveType;
diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp
index 8664e50..fb208f8 100644
--- a/src/pdf/SkPDFDevice.cpp
+++ b/src/pdf/SkPDFDevice.cpp
@@ -1375,7 +1375,7 @@
     }
 }
 
-void SkPDFDevice::drawVertices(const SkVertices*, const SkMatrix*, int, SkBlendMode,
+void SkPDFDevice::drawVertices(const SkVertices*, const SkVertices::Bone[], int, SkBlendMode,
                                const SkPaint&) {
     if (this->hasEmptyClip()) {
         return;
diff --git a/src/pdf/SkPDFDevice.h b/src/pdf/SkPDFDevice.h
index 9a91790..0197b5a 100644
--- a/src/pdf/SkPDFDevice.h
+++ b/src/pdf/SkPDFDevice.h
@@ -97,7 +97,7 @@
                      const SkScalar pos[], int scalarsPerPos,
                      const SkPoint& offset, const SkPaint&) override { SkASSERT(false); }
     void drawGlyphRunList(const SkGlyphRunList& glyphRunList) override;
-    void drawVertices(const SkVertices*, const SkMatrix* bones, int boneCount, SkBlendMode,
+    void drawVertices(const SkVertices*, const SkVertices::Bone bones[], int boneCount, SkBlendMode,
                       const SkPaint&) override;
     void drawDevice(SkBaseDevice*, int x, int y,
                     const SkPaint&) override;
diff --git a/src/pipe/SkPipeCanvas.cpp b/src/pipe/SkPipeCanvas.cpp
index 258160c..bf06579 100644
--- a/src/pipe/SkPipeCanvas.cpp
+++ b/src/pipe/SkPipeCanvas.cpp
@@ -728,7 +728,7 @@
     write_paint(writer, paint, kGeometry_PaintUsage);
 }
 
-void SkPipeCanvas::onDrawVerticesObject(const SkVertices* vertices, const SkMatrix* bones,
+void SkPipeCanvas::onDrawVerticesObject(const SkVertices* vertices, const SkVertices::Bone bones[],
                                         int boneCount, SkBlendMode bmode, const SkPaint& paint) {
     unsigned extra = static_cast<unsigned>(bmode);
 
@@ -737,7 +737,7 @@
     // TODO: dedup vertices?
     writer.writeDataAsByteArray(vertices->encode().get());
     writer.write32(boneCount);
-    writer.write(bones, sizeof(SkMatrix) * boneCount);
+    writer.write(bones, sizeof(SkVertices::Bone) * boneCount);
     write_paint(writer, paint, kVertices_PaintUsage);
 }
 
diff --git a/src/pipe/SkPipeCanvas.h b/src/pipe/SkPipeCanvas.h
index b39f809..a679491 100644
--- a/src/pipe/SkPipeCanvas.h
+++ b/src/pipe/SkPipeCanvas.h
@@ -135,8 +135,8 @@
                          const SkPaint*) override;
     void onDrawImageLattice(const SkImage*, const Lattice& lattice, const SkRect& dst,
                             const SkPaint*) override;
-    void onDrawVerticesObject(const SkVertices*, const SkMatrix* bones, int boneCount, SkBlendMode,
-                              const SkPaint&) override;
+    void onDrawVerticesObject(const SkVertices*, const SkVertices::Bone bones[], int boneCount,
+                              SkBlendMode, const SkPaint&) override;
 
     void onClipRect(const SkRect&, SkClipOp, ClipEdgeStyle) override;
     void onClipRRect(const SkRRect&, SkClipOp, ClipEdgeStyle) override;
diff --git a/src/pipe/SkPipeReader.cpp b/src/pipe/SkPipeReader.cpp
index 909276f..1d77779 100644
--- a/src/pipe/SkPipeReader.cpp
+++ b/src/pipe/SkPipeReader.cpp
@@ -567,7 +567,7 @@
         vertices = SkVertices::Decode(data->data(), data->size());
     }
     int boneCount = reader.read32();
-    const SkMatrix* bones = boneCount ? reader.skipT<SkMatrix>(boneCount) : nullptr;
+    const SkVertices::Bone* bones = boneCount ? reader.skipT<SkVertices::Bone>(boneCount) : nullptr;
     if (vertices) {
         canvas->drawVertices(vertices, bones, boneCount, bmode, read_paint(reader));
     }
diff --git a/src/svg/SkSVGDevice.cpp b/src/svg/SkSVGDevice.cpp
index 1f7ec36..593fe40 100644
--- a/src/svg/SkSVGDevice.cpp
+++ b/src/svg/SkSVGDevice.cpp
@@ -1040,7 +1040,7 @@
     }
 }
 
-void SkSVGDevice::drawVertices(const SkVertices*, const SkMatrix*, int, SkBlendMode,
+void SkSVGDevice::drawVertices(const SkVertices*, const SkVertices::Bone[], int, SkBlendMode,
                                const SkPaint&) {
     // todo
 }
diff --git a/src/svg/SkSVGDevice.h b/src/svg/SkSVGDevice.h
index 452229d..e57ef36 100644
--- a/src/svg/SkSVGDevice.h
+++ b/src/svg/SkSVGDevice.h
@@ -42,7 +42,7 @@
     void drawTextOnPath(const void* text, size_t len,
                         const SkPath& path, const SkMatrix* matrix,
                         const SkPaint& paint) override;
-    void drawVertices(const SkVertices*, const SkMatrix* bones, int boneCount, SkBlendMode,
+    void drawVertices(const SkVertices*, const SkVertices::Bone bones[], int boneCount, SkBlendMode,
                       const SkPaint& paint) override;
 
     void drawDevice(SkBaseDevice*, int x, int y,
diff --git a/src/utils/SkLuaCanvas.cpp b/src/utils/SkLuaCanvas.cpp
index 441857f..edef7e3 100644
--- a/src/utils/SkLuaCanvas.cpp
+++ b/src/utils/SkLuaCanvas.cpp
@@ -314,8 +314,8 @@
     this->INHERITED::onDrawDrawable(drawable, matrix);
 }
 
-void SkLuaCanvas::onDrawVerticesObject(const SkVertices*, const SkMatrix*, int, SkBlendMode,
-                                       const SkPaint& paint) {
+void SkLuaCanvas::onDrawVerticesObject(const SkVertices*, const SkVertices::Bone[], int,
+                                       SkBlendMode, const SkPaint& paint) {
     AUTO_LUA("drawVertices");
     lua.pushPaint(paint, "paint");
 }
diff --git a/src/utils/SkNWayCanvas.cpp b/src/utils/SkNWayCanvas.cpp
index 7d1e981..ac3a971 100644
--- a/src/utils/SkNWayCanvas.cpp
+++ b/src/utils/SkNWayCanvas.cpp
@@ -320,7 +320,7 @@
     }
 }
 
-void SkNWayCanvas::onDrawVerticesObject(const SkVertices* vertices, const SkMatrix* bones,
+void SkNWayCanvas::onDrawVerticesObject(const SkVertices* vertices, const SkVertices::Bone bones[],
                                         int boneCount, SkBlendMode bmode, const SkPaint& paint) {
     Iter iter(fList);
     while (iter.next()) {
diff --git a/src/utils/SkPaintFilterCanvas.cpp b/src/utils/SkPaintFilterCanvas.cpp
index 929488c..862bca7 100644
--- a/src/utils/SkPaintFilterCanvas.cpp
+++ b/src/utils/SkPaintFilterCanvas.cpp
@@ -173,9 +173,9 @@
     }
 }
 
-void SkPaintFilterCanvas::onDrawVerticesObject(const SkVertices* vertices, const SkMatrix* bones,
-                                               int boneCount, SkBlendMode bmode,
-                                               const SkPaint& paint) {
+void SkPaintFilterCanvas::onDrawVerticesObject(const SkVertices* vertices,
+                                               const SkVertices::Bone bones[], int boneCount,
+                                               SkBlendMode bmode, const SkPaint& paint) {
     AutoPaintFilter apf(this, kVertices_Type, paint);
     if (apf.shouldDraw()) {
         this->SkNWayCanvas::onDrawVerticesObject(vertices, bones, boneCount, bmode, *apf.paint());
diff --git a/src/xps/SkXPSDevice.cpp b/src/xps/SkXPSDevice.cpp
index ff66e3b..e73b726 100644
--- a/src/xps/SkXPSDevice.cpp
+++ b/src/xps/SkXPSDevice.cpp
@@ -1156,7 +1156,7 @@
     draw(this, &SkDraw::drawPoints, mode, count, points, paint, this);
 }
 
-void SkXPSDevice::drawVertices(const SkVertices* v, const SkMatrix* bones, int boneCount,
+void SkXPSDevice::drawVertices(const SkVertices* v, const SkVertices::Bone bones[], int boneCount,
                                SkBlendMode blendMode, const SkPaint& paint) {
     draw(this, &SkDraw::drawVertices, v->mode(), v->vertexCount(), v->positions(), v->texCoords(),
          v->colors(), v->boneIndices(), v->boneWeights(), blendMode, v->indices(), v->indexCount(),
diff --git a/src/xps/SkXPSDevice.h b/src/xps/SkXPSDevice.h
index c3f27c4..877eb3b 100644
--- a/src/xps/SkXPSDevice.h
+++ b/src/xps/SkXPSDevice.h
@@ -99,7 +99,7 @@
     void drawPosText(const void* text, size_t len,
                      const SkScalar pos[], int scalarsPerPos,
                      const SkPoint& offset, const SkPaint& paint) override;
-    void drawVertices(const SkVertices*, const SkMatrix* bones, int boneCount, SkBlendMode,
+    void drawVertices(const SkVertices*, const SkVertices::Bone bones[], int boneCount, SkBlendMode,
                       const SkPaint&) override;
     void drawDevice(SkBaseDevice*, int x, int y,
                     const SkPaint&) override;
diff --git a/tools/debugger/SkDebugCanvas.cpp b/tools/debugger/SkDebugCanvas.cpp
index 19b88e9..94b6ea9 100644
--- a/tools/debugger/SkDebugCanvas.cpp
+++ b/tools/debugger/SkDebugCanvas.cpp
@@ -445,7 +445,7 @@
     this->addDrawCommand(new SkDrawPatchCommand(cubics, colors, texCoords, bmode, paint));
 }
 
-void SkDebugCanvas::onDrawVerticesObject(const SkVertices* vertices, const SkMatrix* bones,
+void SkDebugCanvas::onDrawVerticesObject(const SkVertices* vertices, const SkVertices::Bone bones[],
                                          int boneCount, SkBlendMode bmode, const SkPaint& paint) {
     // TODO: ANIMATION NOT LOGGED
     this->addDrawCommand(new SkDrawVerticesCommand(sk_ref_sp(const_cast<SkVertices*>(vertices)),
diff --git a/tools/debugger/SkDebugCanvas.h b/tools/debugger/SkDebugCanvas.h
index b956149..e574722 100644
--- a/tools/debugger/SkDebugCanvas.h
+++ b/tools/debugger/SkDebugCanvas.h
@@ -146,8 +146,8 @@
     void onDrawArc(const SkRect&, SkScalar, SkScalar, bool, const SkPaint&) override;
     void onDrawRRect(const SkRRect&, const SkPaint&) override;
     void onDrawPoints(PointMode, size_t count, const SkPoint pts[], const SkPaint&) override;
-    void onDrawVerticesObject(const SkVertices*, const SkMatrix* bones, int boneCount, SkBlendMode,
-                              const SkPaint&) override;
+    void onDrawVerticesObject(const SkVertices*, const SkVertices::Bone bones[], int boneCount,
+                              SkBlendMode, const SkPaint&) override;
     void onDrawPath(const SkPath&, const SkPaint&) override;
     void onDrawRegion(const SkRegion&, const SkPaint&) override;
     void onDrawBitmap(const SkBitmap&, SkScalar left, SkScalar top, const SkPaint*) override;
diff --git a/tools/viewer/NIMASlide.cpp b/tools/viewer/NIMASlide.cpp
index e8827cc..4d094c4 100644
--- a/tools/viewer/NIMASlide.cpp
+++ b/tools/viewer/NIMASlide.cpp
@@ -18,20 +18,6 @@
 using namespace sk_app;
 using namespace nima;
 
-// NIMA stores its matrices as 6 floats to represent translation and scale. This function takes
-// that format and converts it into a 3x3 matrix representation.
-static void nima_to_skmatrix(const float* nimaData, SkMatrix& matrix) {
-    matrix[0] = nimaData[0];
-    matrix[1] = nimaData[2];
-    matrix[2] = nimaData[4];
-    matrix[3] = nimaData[1];
-    matrix[4] = nimaData[3];
-    matrix[5] = nimaData[5];
-    matrix[6] = 0.0f;
-    matrix[7] = 0.0f;
-    matrix[8] = 1.0f;
-}
-
 // ImGui expects an array of const char* when displaying a ListBox. This function is for an
 // overload of ImGui::ListBox that takes a getter so that ListBox works with
 // std::vector<std::string>.
@@ -58,11 +44,8 @@
             , fVertices(nullptr)
             , fRenderFlags(0) {
         // Update the vertices and bones.
-        this->updateVertices();
+        this->updateVertices(true);
         this->updateBones();
-
-        // Update the vertices object.
-        this->updateVerticesObject(false, false);
     }
 
     void render(SkCanvas* canvas, uint32_t renderFlags) {
@@ -73,47 +56,39 @@
         bool useCache = renderFlags & kCache_RenderFlag;
         bool drawBounds = renderFlags & kBounds_RenderFlag;
 
+        // Don't use the cache if drawing in immediate mode.
+        useCache &= !useImmediate;
+
+        if (fActorImage->doesAnimationVertexDeform() || dirty) {
+            // These are vertices that transform beyond just bone transforms, so they must be
+            // updated every frame.
+            // If the render flags are dirty, reset the vertices object.
+            this->updateVertices(!useCache);
+        }
+
+        // Update the bones.
+        this->updateBones();
+
+        // Deform the bones in immediate mode.
+        sk_sp<SkVertices> vertices = fVertices;
         if (useImmediate) {
-            // Immediate mode transforms.
-            // Update the vertex data.
-            if (fActorImage->doesAnimationVertexDeform() && fActorImage->isVertexDeformDirty()) {
-                this->updateVertices();
-                fActorImage->isVertexDeformDirty(false);
-            }
-
-            // Update the vertices object.
-            this->updateVerticesObject(true, true); // Immediate mode vertices change every frame,
-                                                    // so they must be volatile.
-        } else {
-            // Backend transformations.
-            if (fActorImage->doesAnimationVertexDeform()) {
-                // These are vertices that transform beyond just bone transforms, so they must be
-                // updated every frame.
-                this->updateVertices();
-                this->updateVerticesObject(false, true);
-            } else if (dirty) {
-                // If the render flags are dirty, reset the vertices object.
-                this->updateVertices();
-                this->updateVerticesObject(false, !useCache);
-            }
-
-            // Update the bones.
-            this->updateBones();
+            vertices = fVertices->applyBones(fBones.data(), fBones.size());
         }
 
         // Draw the vertices object.
-        this->drawVerticesObject(canvas, !useImmediate);
+        this->drawVerticesObject(vertices.get(), canvas, !useImmediate);
 
+        // Draw the bounds.
         if (drawBounds && fActorImage->renderOpacity() > 0.0f) {
             // Get the bounds.
-            SkRect bounds = fVertices->bounds();
+            SkRect bounds = vertices->bounds();
 
             // Approximate bounds if not using immediate transforms.
             if (!useImmediate) {
-                const SkRect originalBounds = fBones[0].mapRect(fVertices->bounds());
+                const SkRect originalBounds = fBones[0].mapRect(vertices->bounds());
                 bounds = originalBounds;
                 for (size_t i = 1; i < fBones.size(); i++) {
-                    const SkMatrix& matrix = fBones[i];
+                    const SkVertices::Bone& matrix = fBones[i];
                     bounds.join(matrix.mapRect(originalBounds));
                 }
             }
@@ -129,7 +104,7 @@
     int drawOrder() const { return fActorImage->drawOrder(); }
 
 private:
-    void updateVertices() {
+    void updateVertices(bool isVolatile) {
         // Update whether the image is skinned.
         fSkinned = fActorImage->connectedBoneCount() > 0;
 
@@ -177,12 +152,24 @@
             fTexs[i].set(attrTex[0] * fTexture->width(), attrTex[1] * fTexture->height());
             if (fSkinned) {
                 for (uint32_t k = 0; k < 4; k ++) {
-                    fBoneIdx[i].indices[k] = static_cast<uint32_t>(attrBoneIdx[k]);
-                    fBoneWgt[i].weights[k] = attrBoneWgt[k];
+                    fBoneIdx[i][k] = static_cast<uint32_t>(attrBoneIdx[k]);
+                    fBoneWgt[i][k] = attrBoneWgt[k];
                 }
             }
         }
         memcpy(fIndices.data(), indexData, indexCount * sizeof(uint16_t));
+
+        // Update the vertices object.
+        fVertices = SkVertices::MakeCopy(SkVertices::kTriangles_VertexMode,
+                                         vertexCount,
+                                         fPositions.data(),
+                                         fTexs.data(),
+                                         nullptr,
+                                         fBoneIdx.data(),
+                                         fBoneWgt.data(),
+                                         fIndices.size(),
+                                         fIndices.data(),
+                                         isVolatile);
     }
 
     void updateBones() {
@@ -195,59 +182,24 @@
             if (fSkinned) {
                 numMatrices = fActorImage->boneInfluenceMatricesLength() / kNIMAMatrixSize;
             }
-            fBones.assign(numMatrices, SkMatrix());
+
+            // Initialize all matrices to the identity matrix.
+            fBones.assign(numMatrices, {{ 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f }});
         }
 
         if (fSkinned) {
             // Update the matrices.
             float* matrixData = fActorImage->boneInfluenceMatrices();
-            for (uint32_t i = 1; i < fBones.size(); i ++) {
-                SkMatrix& matrix = fBones[i];
-                float* data = matrixData + i * kNIMAMatrixSize;
-                nima_to_skmatrix(data, matrix);
-            }
+            memcpy(fBones.data(), matrixData, fBones.size() * kNIMAMatrixSize * sizeof(float));
         }
 
         // Set the zero matrix to be the world transform.
-        nima_to_skmatrix(fActorImage->worldTransform().values(), fBones[0]);
+        memcpy(fBones.data(),
+               fActorImage->worldTransform().values(),
+               kNIMAMatrixSize * sizeof(float));
     }
 
-    void updateVerticesObject(bool applyDeforms, bool isVolatile) {
-        std::vector<SkPoint>* positions = &fPositions;
-
-        // Apply deforms if requested.
-        uint32_t vertexCount = fPositions.size();
-        std::vector<SkPoint> deformedPositions;
-        if (applyDeforms) {
-            positions = &deformedPositions;
-            deformedPositions.reserve(vertexCount);
-            for (uint32_t i = 0; i < vertexCount; i ++) {
-                Vec2D nimaPoint(fPositions[i].x(), fPositions[i].y());
-                uint32_t* boneIdx = nullptr;
-                float* boneWgt = nullptr;
-                if (fSkinned) {
-                    boneIdx = fBoneIdx[i].indices;
-                    boneWgt = fBoneWgt[i].weights;
-                }
-                nimaPoint = this->deform(nimaPoint, boneIdx, boneWgt);
-                deformedPositions.push_back(SkPoint::Make(nimaPoint[0], nimaPoint[1]));
-            }
-        }
-
-        // Update the vertices object.
-        fVertices = SkVertices::MakeCopy(SkVertices::kTriangles_VertexMode,
-                                         vertexCount,
-                                         positions->data(),
-                                         fTexs.data(),
-                                         nullptr,
-                                         fBoneIdx.data(),
-                                         fBoneWgt.data(),
-                                         fIndices.size(),
-                                         fIndices.data(),
-                                         isVolatile);
-    }
-
-    void drawVerticesObject(SkCanvas* canvas, bool useBones) const {
+    void drawVerticesObject(SkVertices* vertices, SkCanvas* canvas, bool useBones) const {
         // Determine the blend mode.
         SkBlendMode blendMode;
         switch (fActorImage->blendMode()) {
@@ -278,48 +230,15 @@
 
         // Draw the vertices.
         if (useBones) {
-            canvas->drawVertices(fVertices, fBones.data(), fBones.size(), blendMode, *fPaint);
+            canvas->drawVertices(vertices, fBones.data(), fBones.size(), blendMode, *fPaint);
         } else {
-            canvas->drawVertices(fVertices, blendMode, *fPaint);
+            canvas->drawVertices(vertices, blendMode, *fPaint);
         }
 
         // Reset the opacity.
         fPaint->setAlpha(255);
     }
 
-    Vec2D deform(const Vec2D& position, uint32_t* boneIdx, float* boneWgt) const {
-        float px = position[0], py = position[1];
-        float px2 = px, py2 = py;
-        float influence[6] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
-
-        // Apply the world transform.
-        Mat2D worldTransform = fActorImage->worldTransform();
-        px2 = worldTransform[0] * px + worldTransform[2] * py + worldTransform[4];
-        py2 = worldTransform[1] * px + worldTransform[3] * py + worldTransform[5];
-
-        // Apply deformations based on bone offsets.
-        if (boneIdx && boneWgt) {
-            float* matrices = fActorImage->boneInfluenceMatrices();
-
-            for (uint32_t i = 0; i < 4; i ++) {
-                uint32_t index = boneIdx[i];
-                float weight = boneWgt[i];
-                for (int j = 0; j < 6; j ++) {
-                    influence[j] += matrices[index * 6 + j] * weight;
-                }
-            }
-
-            px = influence[0] * px2 + influence[2] * py2 + influence[4];
-            py = influence[1] * px2 + influence[3] * py2 + influence[5];
-        } else {
-            px = px2;
-            py = py2;
-        }
-
-        // Return the transformed position.
-        return Vec2D(px, py);
-    }
-
 private:
     ActorImage* fActorImage;
     SkImage*    fTexture;
@@ -332,8 +251,8 @@
     std::vector<SkVertices::BoneWeights> fBoneWgt;
     std::vector<uint16_t>                fIndices;
 
-    std::vector<SkMatrix> fBones;
-    sk_sp<SkVertices>     fVertices;
+    std::vector<SkVertices::Bone> fBones;
+    sk_sp<SkVertices>             fVertices;
 
     uint32_t fRenderFlags;
 };