From f510149da8d32f60f08d0a809eb037496079af3c Mon Sep 17 00:00:00 2001 From: Ruiqi Mao Date: Fri, 29 Jun 2018 14:32:21 -0400 Subject: skeletal animation support added to API and software backend SkCanvas::drawVertices now supports overloads that take an array of bone deformation matrices. SkVertices::MakeCopy and SkVertices::Builder now support two additional optional attributes, boneIndices and boneWeights. Bug: skia: Change-Id: I30a3b11691e7cdb13924907cc1401ff86d127aea Reviewed-on: https://skia-review.googlesource.com/137221 Reviewed-by: Brian Osman Reviewed-by: Robert Phillips Commit-Queue: Ruiqi Mao --- BUILD.gn | 19 +- include/core/SkCanvas.h | 42 ++- include/core/SkCanvasVirtualEnforcer.h | 4 +- include/core/SkOverdrawCanvas.h | 3 +- include/core/SkVertices.h | 82 +++++- include/utils/SkLuaCanvas.h | 3 +- include/utils/SkNWayCanvas.h | 3 +- include/utils/SkNoDrawCanvas.h | 3 +- include/utils/SkPaintFilterCanvas.h | 3 +- resources/nima/Robot.nima | Bin 33062 -> 33446 bytes src/core/SkBitmapDevice.cpp | 9 +- src/core/SkBitmapDevice.h | 3 +- src/core/SkCanvas.cpp | 24 +- src/core/SkColorSpaceXformCanvas.cpp | 7 +- src/core/SkDevice.cpp | 4 +- src/core/SkDevice.h | 6 +- src/core/SkDraw.h | 7 +- src/core/SkDraw_vertices.cpp | 88 +++++- src/core/SkLiteDL.cpp | 20 +- src/core/SkLiteDL.h | 3 +- src/core/SkLiteRecorder.cpp | 6 +- src/core/SkLiteRecorder.h | 3 +- src/core/SkOverdrawCanvas.cpp | 9 +- src/core/SkPicturePlayback.cpp | 6 +- src/core/SkPictureRecord.cpp | 10 +- src/core/SkPictureRecord.h | 3 +- src/core/SkRecordDraw.cpp | 2 +- src/core/SkRecorder.cpp | 10 +- src/core/SkRecorder.h | 3 +- src/core/SkRecords.h | 2 + src/core/SkThreadedBMPDevice.cpp | 9 +- src/core/SkThreadedBMPDevice.h | 3 +- src/core/SkVertices.cpp | 76 ++++- src/gpu/SkGpuDevice.cpp | 4 +- src/gpu/SkGpuDevice.h | 3 +- src/pdf/SkPDFDevice.cpp | 3 +- src/pdf/SkPDFDevice.h | 3 +- src/pipe/SkPipeCanvas.cpp | 6 +- src/pipe/SkPipeCanvas.h | 3 +- src/pipe/SkPipeReader.cpp | 9 +- src/svg/SkSVGDevice.cpp | 3 +- src/svg/SkSVGDevice.h | 3 +- src/utils/SkLuaCanvas.cpp | 3 +- src/utils/SkNWayCanvas.cpp | 6 +- src/utils/SkPaintFilterCanvas.cpp | 5 +- src/utils/SkShadowUtils.cpp | 6 +- src/xps/SkXPSDevice.cpp | 6 +- src/xps/SkXPSDevice.h | 3 +- tools/debugger/SkDebugCanvas.cpp | 5 +- tools/debugger/SkDebugCanvas.h | 3 +- tools/flags/SkCommonFlags.cpp | 2 + tools/flags/SkCommonFlags.h | 1 + tools/viewer/NIMASlide.cpp | 516 +++++++++++++++++++++++++++++++++ tools/viewer/NIMASlide.h | 59 ++++ tools/viewer/Viewer.cpp | 10 + 55 files changed, 1013 insertions(+), 124 deletions(-) create mode 100644 tools/viewer/NIMASlide.cpp create mode 100644 tools/viewer/NIMASlide.h diff --git a/BUILD.gn b/BUILD.gn index 97cbf731f6..f10d232fcc 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -1551,9 +1551,13 @@ if (skia_enable_tools) { ":xml", "modules/sksg:samples", "modules/skshaper", - "//third_party/Nima-Cpp", ] + # NIMA does not build on Windows clang + if (!is_win || !is_clang) { + deps += [ "//third_party/Nima-Cpp" ] + } + if (skia_use_lua) { sources += [ "samplecode/SampleLua.cpp" ] deps += [ @@ -1581,10 +1585,14 @@ if (skia_enable_tools) { ":tool_utils", "modules/skottie", "modules/sksg", - "//third_party/Nima-Cpp", "//third_party/jsoncpp", "//third_party/libpng", ] + + # NIMA does not build on Windows clang + if (!is_win || !is_clang) { + deps += [ "//third_party/Nima-Cpp" ] + } } } @@ -2005,9 +2013,14 @@ if (skia_enable_tools) { ":views", "modules/skottie", "modules/sksg", - "//third_party/Nima-Cpp", "//third_party/imgui", ] + + # NIMA does not build on Windows clang + if (!is_win || !is_clang) { + sources += [ "tools/viewer/NIMASlide.cpp" ] + deps += [ "//third_party/Nima-Cpp" ] + } } if (is_android) { diff --git a/include/core/SkCanvas.h b/include/core/SkCanvas.h index 5a307c8e6b..15ed034db6 100644 --- a/include/core/SkCanvas.h +++ b/include/core/SkCanvas.h @@ -2149,6 +2149,40 @@ public: */ void drawVertices(const sk_sp& vertices, SkBlendMode mode, const SkPaint& paint); + /** Draw SkVertices vertices, a triangle mesh, using clip and SkMatrix. Bone data is used to + deform vertices with bone weights. + If vertices texs and vertices colors are defined in vertices, and SkPaint paint + contains SkShader, SkBlendMode mode combines vertices colors with SkShader. + 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. + + @param vertices triangle mesh to draw + @param bones bone matrix data + @param boneCount number of bone matrices + @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, + SkBlendMode mode, const SkPaint& paint); + + /** Draw SkVertices vertices, a triangle mesh, using clip and SkMatrix. Bone data is used to + deform vertices with bone weights. + If vertices texs and vertices colors are defined in vertices, and SkPaint paint + contains SkShader, SkBlendMode mode combines vertices colors with SkShader. + 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. + + @param vertices triangle mesh to draw + @param bones bone matrix data + @param boneCount number of bone matrices + @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 vertices, const SkMatrix* 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. @@ -2472,8 +2506,14 @@ protected: const SkPoint texCoords[4], SkBlendMode mode, const SkPaint& paint); virtual void onDrawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint); + + // TODO: Remove old signature virtual void onDrawVerticesObject(const SkVertices* vertices, SkBlendMode mode, - const SkPaint& paint); + const SkPaint& paint) { + this->onDrawVerticesObject(vertices, nullptr, 0, mode, paint); + } + virtual void onDrawVerticesObject(const SkVertices* vertices, const SkMatrix* bones, + int boneCount, SkBlendMode mode, const SkPaint& paint); virtual void onDrawImage(const SkImage* image, SkScalar dx, SkScalar dy, const SkPaint* paint); virtual void onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst, diff --git a/include/core/SkCanvasVirtualEnforcer.h b/include/core/SkCanvasVirtualEnforcer.h index 1999802170..84b209d895 100644 --- a/include/core/SkCanvasVirtualEnforcer.h +++ b/include/core/SkCanvasVirtualEnforcer.h @@ -48,8 +48,8 @@ protected: 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* vertices, SkBlendMode mode, - const SkPaint& paint) override = 0; + void onDrawVerticesObject(const SkVertices*, const SkMatrix* 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 47b5e04872..380f9ebb87 100644 --- a/include/core/SkOverdrawCanvas.h +++ b/include/core/SkOverdrawCanvas.h @@ -39,7 +39,8 @@ public: 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*, SkBlendMode, const SkPaint&) override; + void onDrawVerticesObject(const SkVertices*, const SkMatrix* 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 d5e97e12d5..6c4fb390d0 100644 --- a/include/core/SkVertices.h +++ b/include/core/SkVertices.h @@ -19,6 +19,20 @@ */ class SK_API SkVertices : public SkNVRefCnt { public: + // BoneIndices indicates which (of a maximum of 4 bones) a given vertex will interpolate + // between. To indicate that a slot is not used, the convention is to assign the bone index + // to 0. + struct BoneIndices { + uint32_t indices[4]; + }; + + // BoneWeights stores the interpolation weight for each of the (maximum of 4) bones a given + // vertex interpolates between. To indicate that a slot is not used, the weight for that + // slot should be 0. + struct BoneWeights { + float weights[4]; + }; + enum VertexMode { kTriangles_VertexMode, kTriangleStrip_VertexMode, @@ -28,21 +42,60 @@ public: }; /** - * Create a vertices by copying the specified arrays. texs and colors may be nullptr, - * and indices is ignored if indexCount == 0. + * Create a vertices by copying the specified arrays. texs, colors, boneIndices, and + * boneWeights may be nullptr, and indices is ignored if indexCount == 0. + * + * boneIndices and boneWeights must either both be nullptr or both point to valid data. + * If specified, they must both contain 'vertexCount' entries. */ static sk_sp MakeCopy(VertexMode mode, int vertexCount, const SkPoint positions[], const SkPoint texs[], const SkColor colors[], + const BoneIndices boneIndices[], + const BoneWeights boneWeights[], int indexCount, const uint16_t indices[]); + static sk_sp MakeCopy(VertexMode mode, int vertexCount, + const SkPoint positions[], + const SkPoint texs[], + const SkColor colors[], + const BoneIndices boneIndices[], + const BoneWeights boneWeights[]) { + return MakeCopy(mode, + vertexCount, + positions, + texs, + colors, + boneIndices, + boneWeights, + 0, + nullptr); + } + + static sk_sp MakeCopy(VertexMode mode, int vertexCount, + const SkPoint positions[], + const SkPoint texs[], + const SkColor colors[], + int indexCount, + const uint16_t indices[]) { + return MakeCopy(mode, + vertexCount, + positions, + texs, + colors, + nullptr, + nullptr, + indexCount, + indices); + } + static sk_sp MakeCopy(VertexMode mode, int vertexCount, const SkPoint positions[], const SkPoint texs[], const SkColor colors[]) { - return MakeCopy(mode, vertexCount, positions, texs, colors, 0, nullptr); + return MakeCopy(mode, vertexCount, positions, texs, colors, nullptr, nullptr); } struct Sizes; @@ -50,6 +103,7 @@ public: enum BuilderFlags { kHasTexCoords_BuilderFlag = 1 << 0, kHasColors_BuilderFlag = 1 << 1, + kHasBones_BuilderFlag = 1 << 2, }; class Builder { public: @@ -61,9 +115,11 @@ public: int vertexCount() const; int indexCount() const; SkPoint* positions(); - SkPoint* texCoords(); // returns null if there are no texCoords - SkColor* colors(); // returns null if there are no colors - uint16_t* indices(); // returns null if there are no indices + SkPoint* texCoords(); // returns null if there are no texCoords + SkColor* colors(); // returns null if there are no colors + BoneIndices* boneIndices(); // returns null if there are no bone indices + BoneWeights* boneWeights(); // returns null if there are no bone weights + uint16_t* indices(); // returns null if there are no indices // Detach the built vertices object. After the first call, this will always return null. sk_sp detach(); @@ -88,6 +144,7 @@ public: bool hasColors() const { return SkToBool(this->colors()); } bool hasTexCoords() const { return SkToBool(this->texCoords()); } + bool hasBones() const { return SkToBool(this->boneIndices()); } bool hasIndices() const { return SkToBool(this->indices()); } int vertexCount() const { return fVertexCnt; } @@ -95,6 +152,9 @@ public: const SkPoint* texCoords() const { return fTexs; } const SkColor* colors() const { return fColors; } + const BoneIndices* boneIndices() const { return fBoneIndices; } + const BoneWeights* boneWeights() const { return fBoneWeights; } + int indexCount() const { return fIndexCnt; } const uint16_t* indices() const { return fIndices; } @@ -128,10 +188,12 @@ private: uint32_t fUniqueID; // these point inside our allocation, so none of these can be "freed" - SkPoint* fPositions; - SkPoint* fTexs; - SkColor* fColors; - uint16_t* fIndices; + SkPoint* fPositions; + SkPoint* fTexs; + SkColor* fColors; + BoneIndices* fBoneIndices; + BoneWeights* fBoneWeights; + uint16_t* fIndices; SkRect fBounds; // computed to be the union of the fPositions[] int fVertexCnt; diff --git a/include/utils/SkLuaCanvas.h b/include/utils/SkLuaCanvas.h index 2fef2c0785..e29e0b5eb3 100644 --- a/include/utils/SkLuaCanvas.h +++ b/include/utils/SkLuaCanvas.h @@ -58,7 +58,8 @@ protected: const SkPaint*, SrcRectConstraint) override; void onDrawBitmapNine(const SkBitmap&, const SkIRect& center, const SkRect& dst, const SkPaint*) override; - void onDrawVerticesObject(const SkVertices*, SkBlendMode, const SkPaint&) override; + void onDrawVerticesObject(const SkVertices*, const SkMatrix* 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 e6a6b9962e..188c0a102c 100644 --- a/include/utils/SkNWayCanvas.h +++ b/include/utils/SkNWayCanvas.h @@ -78,7 +78,8 @@ protected: const SkPaint*) override; void onDrawBitmapNine(const SkBitmap&, const SkIRect& center, const SkRect& dst, const SkPaint*) override; - void onDrawVerticesObject(const SkVertices*, SkBlendMode, const SkPaint&) override; + void onDrawVerticesObject(const SkVertices*, const SkMatrix* 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 518d090786..0e86ed65e7 100644 --- a/include/utils/SkNoDrawCanvas.h +++ b/include/utils/SkNoDrawCanvas.h @@ -75,7 +75,8 @@ protected: const SkPaint*) override {} void onDrawBitmapLattice(const SkBitmap&, const Lattice&, const SkRect&, const SkPaint*) override {} - void onDrawVerticesObject(const SkVertices*, SkBlendMode, const SkPaint&) override {} + void onDrawVerticesObject(const SkVertices*, const SkMatrix*, int, 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/SkPaintFilterCanvas.h b/include/utils/SkPaintFilterCanvas.h index 72c9784725..4728792013 100644 --- a/include/utils/SkPaintFilterCanvas.h +++ b/include/utils/SkPaintFilterCanvas.h @@ -88,7 +88,8 @@ protected: const SkPaint*) override; void onDrawImageLattice(const SkImage*, const Lattice&, const SkRect&, const SkPaint*) override; - void onDrawVerticesObject(const SkVertices*, SkBlendMode, const SkPaint&) override; + void onDrawVerticesObject(const SkVertices*, const SkMatrix* 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/resources/nima/Robot.nima b/resources/nima/Robot.nima index af035ff1e8..c9c6af52fa 100644 Binary files a/resources/nima/Robot.nima and b/resources/nima/Robot.nima differ diff --git a/src/core/SkBitmapDevice.cpp b/src/core/SkBitmapDevice.cpp index 2361468d56..0d1ec8ddc5 100644 --- a/src/core/SkBitmapDevice.cpp +++ b/src/core/SkBitmapDevice.cpp @@ -570,11 +570,12 @@ void SkBitmapDevice::drawPosText(const void* text, size_t len, const SkScalar xp nullptr) } -void SkBitmapDevice::drawVertices(const SkVertices* vertices, SkBlendMode bmode, - const SkPaint& paint) { +void SkBitmapDevice::drawVertices(const SkVertices* vertices, const SkMatrix* bones, int boneCount, + SkBlendMode bmode, const SkPaint& paint) { BDDraw(this).drawVertices(vertices->mode(), vertices->vertexCount(), vertices->positions(), - vertices->texCoords(), vertices->colors(), bmode, - vertices->indices(), vertices->indexCount(), paint); + vertices->texCoords(), vertices->colors(), vertices->boneIndices(), + vertices->boneWeights(), bmode, vertices->indices(), + vertices->indexCount(), paint, bones, boneCount); } void SkBitmapDevice::drawDevice(SkBaseDevice* device, int x, int y, const SkPaint& origPaint) { diff --git a/src/core/SkBitmapDevice.h b/src/core/SkBitmapDevice.h index ba7846ca43..e2cedaa7e1 100644 --- a/src/core/SkBitmapDevice.h +++ b/src/core/SkBitmapDevice.h @@ -113,7 +113,8 @@ protected: */ void drawPosText(const void* text, size_t len, const SkScalar pos[], int scalarsPerPos, const SkPoint& offset, const SkPaint& paint) override; - void drawVertices(const SkVertices*, SkBlendMode, const SkPaint&) override; + void drawVertices(const SkVertices*, const SkMatrix* 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 b297f98156..bf32aafae9 100644 --- a/src/core/SkCanvas.cpp +++ b/src/core/SkCanvas.cpp @@ -1702,13 +1702,27 @@ void SkCanvas::drawVertices(const sk_sp& vertices, SkBlendMode mode, RETURN_ON_NULL(vertices); // We expect fans to be converted to triangles when building or deserializing SkVertices. SkASSERT(vertices->mode() != SkVertices::kTriangleFan_VertexMode); - this->onDrawVerticesObject(vertices.get(), mode, paint); + this->onDrawVerticesObject(vertices.get(), nullptr, 0, mode, paint); } void SkCanvas::drawVertices(const SkVertices* vertices, SkBlendMode mode, const SkPaint& paint) { TRACE_EVENT0("skia", TRACE_FUNC); RETURN_ON_NULL(vertices); - this->onDrawVerticesObject(vertices, mode, paint); + this->onDrawVerticesObject(vertices, nullptr, 0, mode, paint); +} + +void SkCanvas::drawVertices(const sk_sp vertices, const SkMatrix* bones, int boneCount, + SkBlendMode mode, const SkPaint& paint) { + TRACE_EVENT0("skia", TRACE_FUNC); + RETURN_ON_NULL(vertices); + this->onDrawVerticesObject(vertices.get(), bones, boneCount, mode, paint); +} + +void SkCanvas::drawVertices(const SkVertices* vertices, const SkMatrix* bones, int boneCount, + SkBlendMode mode, const SkPaint& paint) { + TRACE_EVENT0("skia", TRACE_FUNC); + RETURN_ON_NULL(vertices); + this->onDrawVerticesObject(vertices, bones, boneCount, mode, paint); } void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) { @@ -2597,13 +2611,13 @@ void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, this->onDrawTextBlob(blob, x, y, paint); } -void SkCanvas::onDrawVerticesObject(const SkVertices* vertices, SkBlendMode bmode, - const SkPaint& paint) { +void SkCanvas::onDrawVerticesObject(const SkVertices* vertices, const SkMatrix* bones, + int boneCount, SkBlendMode bmode, const SkPaint& paint) { LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr) while (iter.next()) { // In the common case of one iteration we could std::move vertices here. - iter.fDevice->drawVertices(vertices, bmode, looper.paint()); + iter.fDevice->drawVertices(vertices, bones, boneCount, bmode, looper.paint()); } LOOPER_END diff --git a/src/core/SkColorSpaceXformCanvas.cpp b/src/core/SkColorSpaceXformCanvas.cpp index 3ef4d6d00d..caf07fb5d9 100644 --- a/src/core/SkColorSpaceXformCanvas.cpp +++ b/src/core/SkColorSpaceXformCanvas.cpp @@ -90,8 +90,8 @@ public: const SkPaint& paint) override { fTarget->drawPoints(mode, count, pts, fXformer->apply(paint)); } - void onDrawVerticesObject(const SkVertices* vertices, SkBlendMode mode, - const SkPaint& paint) override { + void onDrawVerticesObject(const SkVertices* vertices, const SkMatrix* bones, int boneCount, + SkBlendMode mode, const SkPaint& paint) override { sk_sp copy; if (vertices->hasColors()) { int count = vertices->vertexCount(); @@ -99,11 +99,12 @@ public: fXformer->apply(xformed.begin(), vertices->colors(), count); copy = SkVertices::MakeCopy(vertices->mode(), count, vertices->positions(), vertices->texCoords(), xformed.begin(), + vertices->boneIndices(), vertices->boneWeights(), vertices->indexCount(), vertices->indices()); vertices = copy.get(); } - fTarget->drawVertices(vertices, mode, fXformer->apply(paint)); + fTarget->drawVertices(vertices, bones, boneCount, mode, fXformer->apply(paint)); } void onDrawText(const void* ptr, size_t len, diff --git a/src/core/SkDevice.cpp b/src/core/SkDevice.cpp index 46404b2887..08fc3e2b0d 100644 --- a/src/core/SkDevice.cpp +++ b/src/core/SkDevice.cpp @@ -137,7 +137,7 @@ void SkBaseDevice::drawPatch(const SkPoint cubics[12], const SkColor colors[4], auto vertices = SkPatchUtils::MakeVertices(cubics, colors, texCoords, lod.width(), lod.height(), interpColorsLinearly); if (vertices) { - this->drawVertices(vertices.get(), bmode, paint); + this->drawVertices(vertices.get(), nullptr, 0, bmode, paint); } } @@ -311,7 +311,7 @@ void SkBaseDevice::drawAtlas(const SkImage* atlas, const SkRSXform xform[], } SkPaint p(paint); p.setShader(atlas->makeShader()); - this->drawVertices(builder.detach().get(), mode, p); + this->drawVertices(builder.detach().get(), nullptr, 0, mode, p); } /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/core/SkDevice.h b/src/core/SkDevice.h index f840cf6218..93ed3e184a 100644 --- a/src/core/SkDevice.h +++ b/src/core/SkDevice.h @@ -225,7 +225,8 @@ protected: * Decorations (underline and stike-thru) will be handled by SkCanvas. */ virtual void drawGlyphRun(const SkPaint& paint, SkGlyphRun* glyphRun); - virtual void drawVertices(const SkVertices*, SkBlendMode, const SkPaint&) = 0; + virtual void drawVertices(const SkVertices*, const SkMatrix* bones, int boneCount, SkBlendMode, + const SkPaint&) = 0; virtual void drawShadow(const SkPath&, const SkDrawShadowRec&); // default implementation unrolls the blob runs. @@ -434,7 +435,8 @@ protected: 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*, SkBlendMode, const SkPaint&) override {} + void drawVertices(const SkVertices*, const SkMatrix*, int, SkBlendMode, + const SkPaint&) override {} private: typedef SkBaseDevice INHERITED; diff --git a/src/core/SkDraw.h b/src/core/SkDraw.h index 8032ad9d3c..afd50ea229 100644 --- a/src/core/SkDraw.h +++ b/src/core/SkDraw.h @@ -65,11 +65,12 @@ public: void drawPosText(const char text[], size_t byteLength, const SkScalar pos[], int scalarsPerPosition, const SkPoint& offset, const SkPaint&, const SkSurfaceProps*) const; - void drawVertices(SkVertices::VertexMode mode, int count, + void drawVertices(SkVertices::VertexMode mode, int vertexCount, const SkPoint vertices[], const SkPoint textures[], - const SkColor colors[], SkBlendMode bmode, + const SkColor colors[], const SkVertices::BoneIndices boneIndices[], + const SkVertices::BoneWeights boneWeights[], SkBlendMode bmode, const uint16_t indices[], int ptCount, - const SkPaint& paint) const; + const SkPaint& paint, const SkMatrix* 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 de6eaea5b6..f918269992 100644 --- a/src/core/SkDraw_vertices.cpp +++ b/src/core/SkDraw_vertices.cpp @@ -161,15 +161,16 @@ static bool compute_is_opaque(const SkColor colors[], int count) { return SkColorGetA(c) == 0xFF; } -void SkDraw::drawVertices(SkVertices::VertexMode vmode, int count, +void SkDraw::drawVertices(SkVertices::VertexMode vmode, int vertexCount, const SkPoint vertices[], const SkPoint textures[], - const SkColor colors[], SkBlendMode bmode, + const SkColor colors[], const SkVertices::BoneIndices boneIndices[], + const SkVertices::BoneWeights boneWeights[], SkBlendMode bmode, const uint16_t indices[], int indexCount, - const SkPaint& paint) const { - SkASSERT(0 == count || vertices); + const SkPaint& paint, const SkMatrix* bones, int boneCount) const { + SkASSERT(0 == vertexCount || vertices); // abort early if there is nothing to draw - if (count < 3 || (indices && indexCount < 3) || fRC->isEmpty()) { + if (vertexCount < 3 || (indices && indexCount < 3) || fRC->isEmpty()) { return; } SkMatrix ctmInv; @@ -204,25 +205,81 @@ void SkDraw::drawVertices(SkVertices::VertexMode vmode, int count, shader = nullptr; } - constexpr size_t defCount = 16; - constexpr size_t outerSize = sizeof(SkTriColorShader) + + constexpr size_t kDefVertexCount = 16; + constexpr size_t kDefBoneCount = 8; + constexpr size_t kOuterSize = sizeof(SkTriColorShader) + sizeof(SkComposeShader) + - (sizeof(SkPoint) + sizeof(SkPM4f)) * defCount; - SkSTArenaAlloc outerAlloc; + (2 * sizeof(SkPoint) + sizeof(SkPM4f)) * kDefVertexCount + + sizeof(SkMatrix) * kDefBoneCount; + SkSTArenaAlloc outerAlloc; + + // deform vertices using the skeleton if it is passed in + if (bones && boneCount) { + // allocate space for the deformed vertices + SkPoint* deformed = outerAlloc.makeArray(vertexCount); + + // get the bone matrices + SkMatrix* transformedBones = outerAlloc.makeArray(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 bone deformations + SkPoint result = SkPoint::Make(0.0f, 0.0f); + SkPoint transformed; + for (uint32_t j = 0; j < 4; j ++) { + // get the attachment data + uint32_t index = indices.indices[j]; + float weight = weights.weights[j]; + + // skip the bone if there is no weight + if (weight == 0.0f) { + continue; + } + SkASSERT(index != 0); + + // transformed = M * v + transformedBones[index].mapPoints(&transformed, &vertices[i], 1); + + // result += transformed * w + result += transformed * weight; + } + + // set the deformed point + deformed[i] = result; + } + } else { + // no bones, so only apply world transform + const SkMatrix& worldTransform = bones[0]; + worldTransform.mapPoints(deformed, vertices, vertexCount); + } + + // change the vertices to point to deformed + vertices = deformed; + } - SkPoint* devVerts = outerAlloc.makeArray(count); - fMatrix->mapPoints(devVerts, vertices, count); + SkPoint* devVerts = outerAlloc.makeArray(vertexCount); + fMatrix->mapPoints(devVerts, vertices, vertexCount); { SkRect bounds; // this also sets bounds to empty if we see a non-finite value - bounds.set(devVerts, count); + bounds.set(devVerts, vertexCount); if (bounds.isEmpty()) { return; } } - VertState state(count, indices, indexCount); + VertState state(vertexCount, indices, indexCount); VertState::Proc vertProc = state.chooseProc(vmode); if (colors || textures) { @@ -230,10 +287,11 @@ void SkDraw::drawVertices(SkVertices::VertexMode vmode, int count, Matrix43* matrix43 = nullptr; if (colors) { - dstColors = convert_colors(colors, count, fDst.colorSpace(), &outerAlloc); + dstColors = convert_colors(colors, vertexCount, fDst.colorSpace(), &outerAlloc); SkTriColorShader* triShader = outerAlloc.make( - compute_is_opaque(colors, count)); + compute_is_opaque(colors, + vertexCount)); matrix43 = triShader->getMatrix43(); if (shader) { shader = outerAlloc.make(sk_ref_sp(triShader), sk_ref_sp(shader), diff --git a/src/core/SkLiteDL.cpp b/src/core/SkLiteDL.cpp index ce6f0d3191..4240055480 100644 --- a/src/core/SkLiteDL.cpp +++ b/src/core/SkLiteDL.cpp @@ -459,13 +459,17 @@ namespace { }; struct DrawVertices final : Op { static const auto kType = Type::DrawVertices; - DrawVertices(const SkVertices* v, SkBlendMode m, const SkPaint& p) - : vertices(sk_ref_sp(const_cast(v))), mode(m), paint(p) {} + DrawVertices(const SkVertices* v, int bc, SkBlendMode m, const SkPaint& p) + : vertices(sk_ref_sp(const_cast(v))) + , boneCount(bc) + , mode(m) + , paint(p) {} sk_sp vertices; + int boneCount; SkBlendMode mode; SkPaint paint; void draw(SkCanvas* c, const SkMatrix&) const { - c->drawVertices(vertices, mode, paint); + c->drawVertices(vertices, pod(this), boneCount, mode, paint); } }; struct DrawAtlas final : Op { @@ -676,8 +680,14 @@ void SkLiteDL::drawPoints(SkCanvas::PointMode mode, size_t count, const SkPoint void* pod = this->push(count*sizeof(SkPoint), mode, count, paint); copy_v(pod, points,count); } -void SkLiteDL::drawVertices(const SkVertices* vertices, SkBlendMode mode, const SkPaint& paint) { - this->push(0, vertices, mode, paint); +void SkLiteDL::drawVertices(const SkVertices* vertices, const SkMatrix* bones, int boneCount, + SkBlendMode mode, const SkPaint& paint) { + void* pod = this->push(boneCount * sizeof(SkMatrix), + vertices, + boneCount, + mode, + paint); + copy_v(pod, bones, boneCount); } void SkLiteDL::drawAtlas(const SkImage* atlas, const SkRSXform xforms[], const SkRect texs[], const SkColor colors[], int count, SkBlendMode xfermode, diff --git a/src/core/SkLiteDL.h b/src/core/SkLiteDL.h index 6d63657084..b63ed0d514 100644 --- a/src/core/SkLiteDL.h +++ b/src/core/SkLiteDL.h @@ -76,7 +76,8 @@ public: 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*, SkBlendMode, const SkPaint&); + void drawVertices(const SkVertices*, const SkMatrix* bones, int boneCount, SkBlendMode, + const SkPaint&); void drawAtlas(const SkImage*, const SkRSXform[], const SkRect[], const SkColor[], int, SkBlendMode, const SkRect*, const SkPaint*); void drawShadowRec(const SkPath&, const SkDrawShadowRec&); diff --git a/src/core/SkLiteRecorder.cpp b/src/core/SkLiteRecorder.cpp index bd41ff3913..0891508c92 100644 --- a/src/core/SkLiteRecorder.cpp +++ b/src/core/SkLiteRecorder.cpp @@ -182,9 +182,9 @@ void SkLiteRecorder::onDrawPoints(SkCanvas::PointMode mode, const SkPaint& paint) { fDL->drawPoints(mode, count, pts, paint); } -void SkLiteRecorder::onDrawVerticesObject(const SkVertices* vertices, SkBlendMode mode, - const SkPaint& paint) { - fDL->drawVertices(vertices, mode, paint); +void SkLiteRecorder::onDrawVerticesObject(const SkVertices* vertices, const SkMatrix* bones, + int boneCount, SkBlendMode mode, const SkPaint& paint) { + fDL->drawVertices(vertices, bones, boneCount, mode, paint); } void SkLiteRecorder::onDrawAtlas(const SkImage* atlas, const SkRSXform xforms[], diff --git a/src/core/SkLiteRecorder.h b/src/core/SkLiteRecorder.h index 6b21c8984b..af538ee3aa 100644 --- a/src/core/SkLiteRecorder.h +++ b/src/core/SkLiteRecorder.h @@ -77,7 +77,8 @@ public: 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*, SkBlendMode, const SkPaint&) override; + void onDrawVerticesObject(const SkVertices*, const SkMatrix* 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 25bc7fb160..b86f9a333c 100644 --- a/src/core/SkOverdrawCanvas.cpp +++ b/src/core/SkOverdrawCanvas.cpp @@ -219,9 +219,14 @@ void SkOverdrawCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint fList[0]->onDrawPoints(mode, count, points, this->overdrawPaint(paint)); } -void SkOverdrawCanvas::onDrawVerticesObject(const SkVertices* vertices, SkBlendMode blendMode, +void SkOverdrawCanvas::onDrawVerticesObject(const SkVertices* vertices, const SkMatrix* bones, + int boneCount, SkBlendMode blendMode, const SkPaint& paint) { - fList[0]->onDrawVerticesObject(vertices, blendMode, this->overdrawPaint(paint)); + fList[0]->onDrawVerticesObject(vertices, + bones, + boneCount, + blendMode, + this->overdrawPaint(paint)); } void SkOverdrawCanvas::onDrawAtlas(const SkImage* image, const SkRSXform xform[], diff --git a/src/core/SkPicturePlayback.cpp b/src/core/SkPicturePlayback.cpp index 780cb13349..f63abe724c 100644 --- a/src/core/SkPicturePlayback.cpp +++ b/src/core/SkPicturePlayback.cpp @@ -615,11 +615,15 @@ void SkPicturePlayback::handleOp(SkReadBuffer* reader, case DRAW_VERTICES_OBJECT: { 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; SkBlendMode bmode = reader->read32LE(SkBlendMode::kLastMode); BREAK_ON_READ_ERROR(reader); if (paint && vertices) { - canvas->drawVertices(vertices, bmode, *paint); + canvas->drawVertices(vertices, bones, boneCount, bmode, *paint); } } break; case RESTORE: diff --git a/src/core/SkPictureRecord.cpp b/src/core/SkPictureRecord.cpp index 87c8c4d2fb..922fb6a425 100644 --- a/src/core/SkPictureRecord.cpp +++ b/src/core/SkPictureRecord.cpp @@ -682,14 +682,16 @@ void SkPictureRecord::onDrawDrawable(SkDrawable* drawable, const SkMatrix* matri this->validate(initialOffset, size); } -void SkPictureRecord::onDrawVerticesObject(const SkVertices* vertices, SkBlendMode mode, - const SkPaint& paint) { - // op + paint index + vertices index + mode - size_t size = 4 * kUInt32Size; +void SkPictureRecord::onDrawVerticesObject(const SkVertices* vertices, const SkMatrix* 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 initialOffset = this->addDraw(DRAW_VERTICES_OBJECT, &size); this->addPaint(paint); this->addVertices(vertices); + this->addInt(boneCount); + fWriter.write(bones, boneCount * sizeof(SkMatrix)); this->addInt(static_cast(mode)); this->validate(initialOffset, size); diff --git a/src/core/SkPictureRecord.h b/src/core/SkPictureRecord.h index ad2dbdfd24..dc9f4d9bdc 100644 --- a/src/core/SkPictureRecord.h +++ b/src/core/SkPictureRecord.h @@ -196,7 +196,8 @@ protected: 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*, SkBlendMode, const SkPaint&) override; + void onDrawVerticesObject(const SkVertices*, const SkMatrix* 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/SkRecordDraw.cpp b/src/core/SkRecordDraw.cpp index f712c0e436..9c58456295 100644 --- a/src/core/SkRecordDraw.cpp +++ b/src/core/SkRecordDraw.cpp @@ -126,7 +126,7 @@ DRAW(DrawTextOnPath, drawTextOnPath(r.text, r.byteLength, r.path, &r.matrix, r.p DRAW(DrawTextRSXform, drawTextRSXform(r.text, r.byteLength, r.xforms, r.cull, r.paint)); DRAW(DrawAtlas, drawAtlas(r.atlas.get(), r.xforms, r.texs, r.colors, r.count, r.mode, r.cull, r.paint)); -DRAW(DrawVertices, drawVertices(r.vertices, r.bmode, r.paint)); +DRAW(DrawVertices, drawVertices(r.vertices, r.bones, r.boneCount, r.bmode, r.paint)); DRAW(DrawShadowRec, private_draw_shadow_rec(r.path, r.rec)); DRAW(DrawAnnotation, drawAnnotation(r.rect, r.key.c_str(), r.value.get())); #undef DRAW diff --git a/src/core/SkRecorder.cpp b/src/core/SkRecorder.cpp index 65dcd7bb4b..1abd5823af 100644 --- a/src/core/SkRecorder.cpp +++ b/src/core/SkRecorder.cpp @@ -317,9 +317,13 @@ void SkRecorder::onDrawPicture(const SkPicture* pic, const SkMatrix* matrix, con } } -void SkRecorder::onDrawVerticesObject(const SkVertices* vertices, SkBlendMode bmode, - const SkPaint& paint) { - this->append(paint, sk_ref_sp(const_cast(vertices)), bmode); +void SkRecorder::onDrawVerticesObject(const SkVertices* vertices, const SkMatrix* bones, + int boneCount, SkBlendMode bmode, const SkPaint& paint) { + this->append(paint, + sk_ref_sp(const_cast(vertices)), + this->copy(bones, boneCount), + boneCount, + bmode); } void SkRecorder::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4], diff --git a/src/core/SkRecorder.h b/src/core/SkRecorder.h index 6a7a83bdc2..7fed8eaacc 100644 --- a/src/core/SkRecorder.h +++ b/src/core/SkRecorder.h @@ -121,7 +121,8 @@ public: const SkPaint*) override; void onDrawBitmapLattice(const SkBitmap&, const Lattice& lattice, const SkRect& dst, const SkPaint*) override; - void onDrawVerticesObject(const SkVertices*, SkBlendMode, const SkPaint&) override; + void onDrawVerticesObject(const SkVertices*, const SkMatrix* 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 67424aca2a..4ec2ae68c7 100644 --- a/src/core/SkRecords.h +++ b/src/core/SkRecords.h @@ -344,6 +344,8 @@ RECORD(DrawAtlas, kDraw_Tag|kHasImage_Tag|kHasPaint_Tag, RECORD(DrawVertices, kDraw_Tag|kHasPaint_Tag, SkPaint paint; sk_sp vertices; + PODArray bones; + int boneCount; SkBlendMode bmode); RECORD(DrawShadowRec, kDraw_Tag, PreCachedPath path; diff --git a/src/core/SkThreadedBMPDevice.cpp b/src/core/SkThreadedBMPDevice.cpp index 10cd824205..de2b6c0230 100644 --- a/src/core/SkThreadedBMPDevice.cpp +++ b/src/core/SkThreadedBMPDevice.cpp @@ -222,15 +222,16 @@ void SkThreadedBMPDevice::drawPosText(const void* text, size_t len, const SkScal }); } -void SkThreadedBMPDevice::drawVertices(const SkVertices* vertices, SkBlendMode bmode, - const SkPaint& paint) { +void SkThreadedBMPDevice::drawVertices(const SkVertices* vertices, const SkMatrix* bones, + int boneCount, SkBlendMode bmode, const SkPaint& paint) { const sk_sp verts = sk_ref_sp(vertices); // retain vertices until flush SkRect drawBounds = SkRectPriv::MakeLargest(); // TODO tighter drawBounds fQueue.push(drawBounds, [=](SkArenaAlloc*, const DrawState& ds, const SkIRect& tileBounds){ TileDraw(ds, tileBounds).drawVertices(verts->mode(), verts->vertexCount(), verts->positions(), verts->texCoords(), - verts->colors(), bmode, verts->indices(), - verts->indexCount(), paint); + verts->colors(), verts->boneIndices(), + verts->boneWeights(), bmode, verts->indices(), + verts->indexCount(), paint, bones, boneCount); }); } diff --git a/src/core/SkThreadedBMPDevice.h b/src/core/SkThreadedBMPDevice.h index 13d837424c..0cfb91db07 100644 --- a/src/core/SkThreadedBMPDevice.h +++ b/src/core/SkThreadedBMPDevice.h @@ -35,7 +35,8 @@ protected: void drawSprite(const SkBitmap&, int x, int y, const SkPaint&) override; void drawPosText(const void* text, size_t len, const SkScalar pos[], int scalarsPerPos, const SkPoint& offset, const SkPaint& paint) override; - void drawVertices(const SkVertices*, SkBlendMode, const SkPaint&) override; + void drawVertices(const SkVertices*, const SkMatrix* bones, int boneCount, SkBlendMode, + const SkPaint&) override; void drawBitmap(const SkBitmap&, const SkMatrix&, const SkRect* dstOrNull, const SkPaint&) override; diff --git a/src/core/SkVertices.cpp b/src/core/SkVertices.cpp index d4c6ed56de..04db0d35fb 100644 --- a/src/core/SkVertices.cpp +++ b/src/core/SkVertices.cpp @@ -27,12 +27,14 @@ static int32_t next_id() { struct SkVertices::Sizes { Sizes(SkVertices::VertexMode mode, int vertexCount, int indexCount, bool hasTexs, - bool hasColors) { + bool hasColors, bool hasBones) { SkSafeMath safe; fVSize = safe.mul(vertexCount, sizeof(SkPoint)); fTSize = hasTexs ? safe.mul(vertexCount, sizeof(SkPoint)) : 0; fCSize = hasColors ? safe.mul(vertexCount, sizeof(SkColor)) : 0; + fBISize = hasBones ? safe.mul(vertexCount, sizeof(BoneIndices)) : 0; + fBWSize = hasBones ? safe.mul(vertexCount, sizeof(BoneWeights)) : 0; fBuilderTriFanISize = 0; fISize = safe.mul(indexCount, sizeof(uint16_t)); @@ -61,7 +63,9 @@ struct SkVertices::Sizes { safe.add(fVSize, safe.add(fTSize, safe.add(fCSize, - fISize)))); + safe.add(fBISize, + safe.add(fBWSize, + fISize)))))); if (safe.ok()) { fArrays = fTotal - sizeof(SkVertices); // just the sum of the arrays @@ -73,10 +77,12 @@ struct SkVertices::Sizes { bool isValid() const { return fTotal != 0; } size_t fTotal; // size of entire SkVertices allocation (obj + arrays) - size_t fArrays; // size of all the arrays (V + T + C + I) + size_t fArrays; // size of all the arrays (V + T + C + BI + BW + I) size_t fVSize; size_t fTSize; size_t fCSize; + size_t fBISize; + size_t fBWSize; size_t fISize; // For indexed tri-fans this is the number of amount of space fo indices needed in the builder @@ -88,8 +94,9 @@ SkVertices::Builder::Builder(VertexMode mode, int vertexCount, int indexCount, uint32_t builderFlags) { bool hasTexs = SkToBool(builderFlags & SkVertices::kHasTexCoords_BuilderFlag); bool hasColors = SkToBool(builderFlags & SkVertices::kHasColors_BuilderFlag); + bool hasBones = SkToBool(builderFlags & SkVertices::kHasBones_BuilderFlag); this->init(mode, vertexCount, indexCount, - SkVertices::Sizes(mode, vertexCount, indexCount, hasTexs, hasColors)); + SkVertices::Sizes(mode, vertexCount, indexCount, hasTexs, hasColors, hasBones)); } SkVertices::Builder::Builder(VertexMode mode, int vertexCount, int indexCount, @@ -113,13 +120,16 @@ void SkVertices::Builder::init(VertexMode mode, int vertexCount, int indexCount, // need to point past the object to store the arrays char* ptr = (char*)storage + sizeof(SkVertices); - fVertices->fPositions = (SkPoint*)ptr; ptr += sizes.fVSize; - fVertices->fTexs = sizes.fTSize ? (SkPoint*)ptr : nullptr; ptr += sizes.fTSize; - fVertices->fColors = sizes.fCSize ? (SkColor*)ptr : nullptr; ptr += sizes.fCSize; + fVertices->fPositions = (SkPoint*)ptr; ptr += sizes.fVSize; + fVertices->fTexs = sizes.fTSize ? (SkPoint*)ptr : nullptr; ptr += sizes.fTSize; + fVertices->fColors = sizes.fCSize ? (SkColor*)ptr : nullptr; ptr += sizes.fCSize; + fVertices->fBoneIndices = sizes.fBISize ? (BoneIndices*) ptr : nullptr; ptr += sizes.fBISize; + fVertices->fBoneWeights = sizes.fBWSize ? (BoneWeights*) ptr : nullptr; ptr += sizes.fBWSize; fVertices->fIndices = sizes.fISize ? (uint16_t*)ptr : nullptr; fVertices->fVertexCnt = vertexCount; fVertices->fIndexCnt = indexCount; fVertices->fMode = mode; + // We defer assigning fBounds and fUniqueID until detach() is called } @@ -173,6 +183,14 @@ SkColor* SkVertices::Builder::colors() { return fVertices ? const_cast(fVertices->colors()) : nullptr; } +SkVertices::BoneIndices* SkVertices::Builder::boneIndices() { + return fVertices ? const_cast(fVertices->boneIndices()) : nullptr; +} + +SkVertices::BoneWeights* SkVertices::Builder::boneWeights() { + return fVertices ? const_cast(fVertices->boneWeights()) : nullptr; +} + uint16_t* SkVertices::Builder::indices() { if (!fVertices) { return nullptr; @@ -187,9 +205,17 @@ uint16_t* SkVertices::Builder::indices() { sk_sp SkVertices::MakeCopy(VertexMode mode, int vertexCount, const SkPoint pos[], const SkPoint texs[], - const SkColor colors[], int indexCount, - const uint16_t indices[]) { - Sizes sizes(mode, vertexCount, indexCount, texs != nullptr, colors != nullptr); + const SkColor colors[], + const BoneIndices boneIndices[], + const BoneWeights boneWeights[], + int indexCount, const uint16_t indices[]) { + SkASSERT((!boneIndices && !boneWeights) || (boneIndices && boneWeights)); + Sizes sizes(mode, + vertexCount, + indexCount, + texs != nullptr, + colors != nullptr, + boneIndices != nullptr); if (!sizes.isValid()) { return nullptr; } @@ -200,6 +226,8 @@ sk_sp SkVertices::MakeCopy(VertexMode mode, int vertexCount, sk_careful_memcpy(builder.positions(), pos, sizes.fVSize); sk_careful_memcpy(builder.texCoords(), texs, sizes.fTSize); sk_careful_memcpy(builder.colors(), colors, sizes.fCSize); + sk_careful_memcpy(builder.boneIndices(), boneIndices, sizes.fBISize); + sk_careful_memcpy(builder.boneWeights(), boneWeights, sizes.fBWSize); size_t isize = (mode == kTriangleFan_VertexMode) ? sizes.fBuilderTriFanISize : sizes.fISize; sk_careful_memcpy(builder.indices(), indices, isize); @@ -207,19 +235,26 @@ sk_sp SkVertices::MakeCopy(VertexMode mode, int vertexCount, } size_t SkVertices::approximateSize() const { - Sizes sizes(fMode, fVertexCnt, fIndexCnt, this->hasTexCoords(), this->hasColors()); + Sizes sizes(fMode, + fVertexCnt, + fIndexCnt, + this->hasTexCoords(), + this->hasColors(), + this->hasBones()); SkASSERT(sizes.isValid()); return sizeof(SkVertices) + sizes.fArrays; } /////////////////////////////////////////////////////////////////////////////////////////////////// -// storage = packed | vertex_count | index_count | pos[] | texs[] | colors[] | indices[] +// storage = packed | vertex_count | index_count | pos[] | texs[] | colors[] | boneIndices[] | +// boneWeights[] | indices[] // = header + arrays #define kMode_Mask 0x0FF #define kHasTexs_Mask 0x100 #define kHasColors_Mask 0x200 +#define kHasBones_Mask 0x400 #define kHeaderSize (3 * sizeof(uint32_t)) sk_sp SkVertices::encode() const { @@ -232,8 +267,16 @@ sk_sp SkVertices::encode() const { if (this->hasColors()) { packed |= kHasColors_Mask; } + if (this->hasBones()) { + packed |= kHasBones_Mask; + } - Sizes sizes(fMode, fVertexCnt, fIndexCnt, this->hasTexCoords(), this->hasColors()); + Sizes sizes(fMode, + fVertexCnt, + fIndexCnt, + this->hasTexCoords(), + this->hasColors(), + this->hasBones()); SkASSERT(sizes.isValid()); SkASSERT(!sizes.fBuilderTriFanISize); // need to force alignment to 4 for SkWriter32 -- will pad w/ 0s as needed @@ -248,6 +291,8 @@ sk_sp SkVertices::encode() const { writer.write(fPositions, sizes.fVSize); writer.write(fTexs, sizes.fTSize); writer.write(fColors, sizes.fCSize); + writer.write(fBoneIndices, sizes.fBISize); + writer.write(fBoneWeights, sizes.fBWSize); // if index-count is odd, we won't be 4-bytes aligned, so we call the pad version writer.writePad(fIndices, sizes.fISize); @@ -272,7 +317,8 @@ sk_sp SkVertices::Decode(const void* data, size_t length) { } const bool hasTexs = SkToBool(packed & kHasTexs_Mask); const bool hasColors = SkToBool(packed & kHasColors_Mask); - Sizes sizes(mode, vertexCount, indexCount, hasTexs, hasColors); + const bool hasBones = SkToBool(packed & kHasBones_Mask); + Sizes sizes(mode, vertexCount, indexCount, hasTexs, hasColors, hasBones); if (!sizes.isValid()) { return nullptr; } @@ -286,6 +332,8 @@ sk_sp SkVertices::Decode(const void* data, size_t length) { reader.read(builder.positions(), sizes.fVSize); reader.read(builder.texCoords(), sizes.fTSize); reader.read(builder.colors(), sizes.fCSize); + reader.read(builder.boneIndices(), sizes.fBISize); + reader.read(builder.boneWeights(), sizes.fBWSize); size_t isize = (mode == kTriangleFan_VertexMode) ? sizes.fBuilderTriFanISize : sizes.fISize; reader.read(builder.indices(), isize); if (indexCount > 0) { diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp index 819acd2ef8..a665517d4f 100644 --- a/src/gpu/SkGpuDevice.cpp +++ b/src/gpu/SkGpuDevice.cpp @@ -1541,7 +1541,9 @@ void SkGpuDevice::wireframeVertices(SkVertices::VertexMode vmode, int vertexCoun &primitiveType); } -void SkGpuDevice::drawVertices(const SkVertices* vertices, SkBlendMode mode, const SkPaint& paint) { +void SkGpuDevice::drawVertices(const SkVertices* vertices, const SkMatrix* bones, int boneCount, + SkBlendMode mode, const SkPaint& paint) { + // TODO: GPU ANIMATION 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 7c7030e1ab..48ba8ac8a1 100644 --- a/src/gpu/SkGpuDevice.h +++ b/src/gpu/SkGpuDevice.h @@ -91,7 +91,8 @@ public: int scalarsPerPos, const SkPoint& offset, const SkPaint&) override; void drawTextBlob(const SkTextBlob*, SkScalar x, SkScalar y, const SkPaint& paint, SkDrawFilter* drawFilter) override; - void drawVertices(const SkVertices*, SkBlendMode, const SkPaint&) override; + void drawVertices(const SkVertices*, const SkMatrix* bones, int boneCount, SkBlendMode, + const SkPaint&) override; void drawShadow(const SkPath&, const SkDrawShadowRec&) override; void drawAtlas(const SkImage* atlas, const SkRSXform[], const SkRect[], const SkColor[], int count, SkBlendMode, const SkPaint&) override; diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp index 45a9b95915..2d2e27bf1f 100644 --- a/src/pdf/SkPDFDevice.cpp +++ b/src/pdf/SkPDFDevice.cpp @@ -1473,7 +1473,8 @@ void SkPDFDevice::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, } } -void SkPDFDevice::drawVertices(const SkVertices*, SkBlendMode, const SkPaint&) { +void SkPDFDevice::drawVertices(const SkVertices*, const SkMatrix*, int, SkBlendMode, + const SkPaint&) { if (this->hasEmptyClip()) { return; } diff --git a/src/pdf/SkPDFDevice.h b/src/pdf/SkPDFDevice.h index 33d0e70e67..4c9c9c88ac 100644 --- a/src/pdf/SkPDFDevice.h +++ b/src/pdf/SkPDFDevice.h @@ -100,7 +100,8 @@ public: const SkPoint& offset, const SkPaint&) override; void drawTextBlob(const SkTextBlob*, SkScalar x, SkScalar y, const SkPaint &, SkDrawFilter*) override; - void drawVertices(const SkVertices*, SkBlendMode, const SkPaint&) override; + void drawVertices(const SkVertices*, const SkMatrix* 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 d66953c5bd..aabff5bfae 100644 --- a/src/pipe/SkPipeCanvas.cpp +++ b/src/pipe/SkPipeCanvas.cpp @@ -728,14 +728,16 @@ void SkPipeCanvas::onDrawRegion(const SkRegion& region, const SkPaint& paint) { write_paint(writer, paint, kGeometry_PaintUsage); } -void SkPipeCanvas::onDrawVerticesObject(const SkVertices* vertices, SkBlendMode bmode, - const SkPaint& paint) { +void SkPipeCanvas::onDrawVerticesObject(const SkVertices* vertices, const SkMatrix* bones, + int boneCount, SkBlendMode bmode, const SkPaint& paint) { unsigned extra = static_cast(bmode); SkPipeWriter writer(this); writer.write32(pack_verb(SkPipeVerb::kDrawVertices, extra)); // TODO: dedup vertices? writer.writeDataAsByteArray(vertices->encode().get()); + writer.write32(boneCount); + writer.write(bones, sizeof(SkMatrix) * boneCount); write_paint(writer, paint, kVertices_PaintUsage); } diff --git a/src/pipe/SkPipeCanvas.h b/src/pipe/SkPipeCanvas.h index 65bca4dfc4..b39f80920b 100644 --- a/src/pipe/SkPipeCanvas.h +++ b/src/pipe/SkPipeCanvas.h @@ -135,7 +135,8 @@ protected: const SkPaint*) override; void onDrawImageLattice(const SkImage*, const Lattice& lattice, const SkRect& dst, const SkPaint*) override; - void onDrawVerticesObject(const SkVertices*, SkBlendMode, const SkPaint&) override; + void onDrawVerticesObject(const SkVertices*, const SkMatrix* 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 ada4a21342..312e3da4e8 100644 --- a/src/pipe/SkPipeReader.cpp +++ b/src/pipe/SkPipeReader.cpp @@ -562,9 +562,14 @@ static void drawImageLattice_handler(SkPipeReader& reader, uint32_t packedVerb, static void drawVertices_handler(SkPipeReader& reader, uint32_t packedVerb, SkCanvas* canvas) { SkASSERT(SkPipeVerb::kDrawVertices == unpack_verb(packedVerb)); SkBlendMode bmode = (SkBlendMode)unpack_verb_extra(packedVerb); + sk_sp vertices = nullptr; if (sk_sp data = reader.readByteArrayAsData()) { - canvas->drawVertices(SkVertices::Decode(data->data(), data->size()), bmode, - read_paint(reader)); + vertices = SkVertices::Decode(data->data(), data->size()); + } + int boneCount = reader.read32(); + const SkMatrix* bones = boneCount ? reader.skipT(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 d552803e73..c2a402f695 100644 --- a/src/svg/SkSVGDevice.cpp +++ b/src/svg/SkSVGDevice.cpp @@ -988,7 +988,8 @@ void SkSVGDevice::drawTextOnPath(const void* text, size_t len, const SkPath& pat } } -void SkSVGDevice::drawVertices(const SkVertices*, SkBlendMode, const SkPaint&) { +void SkSVGDevice::drawVertices(const SkVertices*, const SkMatrix*, int, SkBlendMode, + const SkPaint&) { // todo } diff --git a/src/svg/SkSVGDevice.h b/src/svg/SkSVGDevice.h index 4784c7e238..6b8ee4ff44 100644 --- a/src/svg/SkSVGDevice.h +++ b/src/svg/SkSVGDevice.h @@ -43,7 +43,8 @@ protected: void drawTextOnPath(const void* text, size_t len, const SkPath& path, const SkMatrix* matrix, const SkPaint& paint) override; - void drawVertices(const SkVertices*, SkBlendMode, const SkPaint& paint) override; + void drawVertices(const SkVertices*, const SkMatrix* bones, int boneCount, SkBlendMode, + const SkPaint& paint) override; void drawDevice(SkBaseDevice*, int x, int y, const SkPaint&) override; diff --git a/src/utils/SkLuaCanvas.cpp b/src/utils/SkLuaCanvas.cpp index c7e3064c9b..441857f664 100644 --- a/src/utils/SkLuaCanvas.cpp +++ b/src/utils/SkLuaCanvas.cpp @@ -314,7 +314,8 @@ void SkLuaCanvas::onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) { this->INHERITED::onDrawDrawable(drawable, matrix); } -void SkLuaCanvas::onDrawVerticesObject(const SkVertices*, SkBlendMode, const SkPaint& paint) { +void SkLuaCanvas::onDrawVerticesObject(const SkVertices*, const SkMatrix*, 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 c9f9768e7c..017ec0551b 100644 --- a/src/utils/SkNWayCanvas.cpp +++ b/src/utils/SkNWayCanvas.cpp @@ -320,11 +320,11 @@ void SkNWayCanvas::onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) } } -void SkNWayCanvas::onDrawVerticesObject(const SkVertices* vertices, SkBlendMode bmode, - const SkPaint& paint) { +void SkNWayCanvas::onDrawVerticesObject(const SkVertices* vertices, const SkMatrix* bones, + int boneCount, SkBlendMode bmode, const SkPaint& paint) { Iter iter(fList); while (iter.next()) { - iter->drawVertices(vertices, bmode, paint); + iter->drawVertices(vertices, bones, boneCount, bmode, paint); } } diff --git a/src/utils/SkPaintFilterCanvas.cpp b/src/utils/SkPaintFilterCanvas.cpp index c17990d536..929488caea 100644 --- a/src/utils/SkPaintFilterCanvas.cpp +++ b/src/utils/SkPaintFilterCanvas.cpp @@ -173,11 +173,12 @@ void SkPaintFilterCanvas::onDrawImageLattice(const SkImage* image, const Lattice } } -void SkPaintFilterCanvas::onDrawVerticesObject(const SkVertices* vertices, SkBlendMode bmode, +void SkPaintFilterCanvas::onDrawVerticesObject(const SkVertices* vertices, const SkMatrix* bones, + int boneCount, SkBlendMode bmode, const SkPaint& paint) { AutoPaintFilter apf(this, kVertices_Type, paint); if (apf.shouldDraw()) { - this->SkNWayCanvas::onDrawVerticesObject(vertices, bmode, *apf.paint()); + this->SkNWayCanvas::onDrawVerticesObject(vertices, bones, boneCount, bmode, *apf.paint()); } } diff --git a/src/utils/SkShadowUtils.cpp b/src/utils/SkShadowUtils.cpp index 6d5b7e16b4..c6b92ad985 100644 --- a/src/utils/SkShadowUtils.cpp +++ b/src/utils/SkShadowUtils.cpp @@ -545,7 +545,7 @@ void SkBaseDevice::drawShadow(const SkPath& path, const SkDrawShadowRec& rec) { if (vertices->vertexCount()) { SkAutoDeviceCTMRestore adr(this, SkMatrix::Concat(this->ctm(), SkMatrix::MakeTrans(tx, ty))); - this->drawVertices(vertices, mode, paint); + this->drawVertices(vertices, nullptr, 0, mode, paint); } }; @@ -580,7 +580,7 @@ void SkBaseDevice::drawShadow(const SkPath& path, const SkDrawShadowRec& rec) { SkColorFilter::MakeModeFilter(rec.fAmbientColor, SkBlendMode::kModulate)->makeComposed( SkGaussianColorFilter::Make())); - this->drawVertices(vertices.get(), SkBlendMode::kModulate, paint); + this->drawVertices(vertices.get(), nullptr, 0, SkBlendMode::kModulate, paint); success = true; } } @@ -661,7 +661,7 @@ void SkBaseDevice::drawShadow(const SkPath& path, const SkDrawShadowRec& rec) { SkColorFilter::MakeModeFilter(rec.fSpotColor, SkBlendMode::kModulate)->makeComposed( SkGaussianColorFilter::Make())); - this->drawVertices(vertices.get(), SkBlendMode::kModulate, paint); + this->drawVertices(vertices.get(), nullptr, 0, SkBlendMode::kModulate, paint); success = true; } } diff --git a/src/xps/SkXPSDevice.cpp b/src/xps/SkXPSDevice.cpp index 36d1bd171f..797ff38483 100644 --- a/src/xps/SkXPSDevice.cpp +++ b/src/xps/SkXPSDevice.cpp @@ -1156,9 +1156,11 @@ void SkXPSDevice::drawPoints(SkCanvas::PointMode mode, draw(this, &SkDraw::drawPoints, mode, count, points, paint, this); } -void SkXPSDevice::drawVertices(const SkVertices* v, SkBlendMode blendMode, const SkPaint& paint) { +void SkXPSDevice::drawVertices(const SkVertices* v, const SkMatrix* bones, int boneCount, + SkBlendMode blendMode, const SkPaint& paint) { draw(this, &SkDraw::drawVertices, v->mode(), v->vertexCount(), v->positions(), v->texCoords(), - v->colors(), blendMode, v->indices(), v->indexCount(), paint); + v->colors(), v->boneIndices(), v->boneWeights(), blendMode, v->indices(), v->indexCount(), + paint, bones, boneCount); } void SkXPSDevice::drawPaint(const SkPaint& origPaint) { diff --git a/src/xps/SkXPSDevice.h b/src/xps/SkXPSDevice.h index 81e881ea82..03de5f1dcb 100644 --- a/src/xps/SkXPSDevice.h +++ b/src/xps/SkXPSDevice.h @@ -100,7 +100,8 @@ protected: void drawPosText(const void* text, size_t len, const SkScalar pos[], int scalarsPerPos, const SkPoint& offset, const SkPaint& paint) override; - void drawVertices(const SkVertices*, SkBlendMode, const SkPaint&) override; + void drawVertices(const SkVertices*, const SkMatrix* 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 8026eed479..537bcb1bf9 100644 --- a/tools/debugger/SkDebugCanvas.cpp +++ b/tools/debugger/SkDebugCanvas.cpp @@ -445,8 +445,9 @@ void SkDebugCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4 this->addDrawCommand(new SkDrawPatchCommand(cubics, colors, texCoords, bmode, paint)); } -void SkDebugCanvas::onDrawVerticesObject(const SkVertices* vertices, SkBlendMode bmode, - const SkPaint& paint) { +void SkDebugCanvas::onDrawVerticesObject(const SkVertices* vertices, const SkMatrix* bones, + int boneCount, SkBlendMode bmode, const SkPaint& paint) { + // TODO: ANIMATION NOT LOGGED this->addDrawCommand(new SkDrawVerticesCommand(sk_ref_sp(const_cast(vertices)), bmode, paint)); } diff --git a/tools/debugger/SkDebugCanvas.h b/tools/debugger/SkDebugCanvas.h index c4a61e7887..b95614931b 100644 --- a/tools/debugger/SkDebugCanvas.h +++ b/tools/debugger/SkDebugCanvas.h @@ -146,7 +146,8 @@ protected: 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*, SkBlendMode, const SkPaint&) override; + void onDrawVerticesObject(const SkVertices*, const SkMatrix* 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/flags/SkCommonFlags.cpp b/tools/flags/SkCommonFlags.cpp index 73b0eb4f12..ed4d169585 100644 --- a/tools/flags/SkCommonFlags.cpp +++ b/tools/flags/SkCommonFlags.cpp @@ -57,10 +57,12 @@ DEFINE_bool(disableDriverCorrectnessWorkarounds, false, "Disables all GPU driver DEFINE_string(skps, "/data/local/tmp/skps", "Directory to read skps from."); DEFINE_string(jpgs, "/data/local/tmp/resources", "Directory to read jpgs from."); DEFINE_string(jsons, "/data/local/tmp/jsons", "Directory to read (Bodymovin) jsons from."); +DEFINE_string(nimas, "/data/local/tmp/nimas", "Directory to read NIMA animations from."); #else DEFINE_string(skps, "skps", "Directory to read skps from."); DEFINE_string(jpgs, "jpgs", "Directory to read jpgs from."); DEFINE_string(jsons, "jsons", "Directory to read (Bodymovin) jsons from."); +DEFINE_string(nimas, "nimas", "Directory to read NIMA animations from."); #endif DEFINE_int32(skpViewportSize, 1000, "Width & height of the viewport used to crop skp rendering."); diff --git a/tools/flags/SkCommonFlags.h b/tools/flags/SkCommonFlags.h index 3dca16f36d..10f90c0293 100644 --- a/tools/flags/SkCommonFlags.h +++ b/tools/flags/SkCommonFlags.h @@ -28,6 +28,7 @@ DECLARE_int32(skpViewportSize); DECLARE_string(jpgs); DECLARE_string(jsons); DECLARE_string(svgs); +DECLARE_string(nimas); DECLARE_bool(nativeFonts); DECLARE_int32(threads); DECLARE_string(resourcePath); diff --git a/tools/viewer/NIMASlide.cpp b/tools/viewer/NIMASlide.cpp new file mode 100644 index 0000000000..c13ff2f11d --- /dev/null +++ b/tools/viewer/NIMASlide.cpp @@ -0,0 +1,516 @@ +/* +* Copyright 2018 Google Inc. +* +* Use of this source code is governed by a BSD-style license that can be +* found in the LICENSE file. +*/ + +#include "NIMASlide.h" + +#include "SkAnimTimer.h" +#include "SkOSPath.h" +#include "Resources.h" +#include "imgui.h" + +#include +#include + +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. +static bool vector_getter(void* v, int index, const char** out) { + auto vector = reinterpret_cast*>(v); + *out = vector->at(index).c_str(); + return true; +} + +// A wrapper class that handles rendering of ActorImages (renderable components NIMA Actors). +class NIMAActorImage { +public: + NIMAActorImage(ActorImage* actorImage, SkImage* texture, SkPaint* paint) + : fActorImage(actorImage) + , fTexture(texture) + , fPaint(paint) + , fSkinned(false) + , fPositions() + , fTexs() + , fBoneIdx() + , fBoneWgt() + , fIndices() + , fBones() + , fVertices(nullptr) + , fRenderMode(kBackend_RenderMode) { + // Update the vertices and bones. + this->updateVertices(); + this->updateBones(); + + // Update the vertices object. + this->updateVerticesObject(false); + } + + void renderBackend(SkCanvas* canvas) { + // Reset vertices if the render mode has changed. + if (fRenderMode != kBackend_RenderMode) { + fRenderMode = kBackend_RenderMode; + this->updateVertices(); + this->updateVerticesObject(false); + } + + canvas->save(); + + // Update the vertex data. + if (fActorImage->doesAnimationVertexDeform() && fActorImage->isVertexDeformDirty()) { + this->updateVertices(); + this->updateVerticesObject(false); + fActorImage->isVertexDeformDirty(false); + } + + // Update the bones. + this->updateBones(); + + // Draw the vertices object. + this->drawVerticesObject(canvas, true); + + canvas->restore(); + } + + void renderImmediate(SkCanvas* canvas) { + // Reset vertices if the render mode has changed. + if (fRenderMode != kImmediate_RenderMode) { + fRenderMode = kImmediate_RenderMode; + this->updateVertices(); + this->updateVerticesObject(true); + } + + // Update the vertex data. + if (fActorImage->doesAnimationVertexDeform() && fActorImage->isVertexDeformDirty()) { + this->updateVertices(); + fActorImage->isVertexDeformDirty(false); + } + + // Update the vertices object. + this->updateVerticesObject(true); + + // Draw the vertices object. + this->drawVerticesObject(canvas, false); + } + + int drawOrder() const { return fActorImage->drawOrder(); } + +private: + void updateVertices() { + // Update whether the image is skinned. + fSkinned = fActorImage->connectedBoneCount() > 0; + + // Retrieve data from the image. + uint32_t vertexCount = fActorImage->vertexCount(); + uint32_t vertexStride = fActorImage->vertexStride(); + float* vertexData = fActorImage->vertices(); + uint32_t indexCount = fActorImage->triangleCount() * 3; + uint16_t* indexData = fActorImage->triangles(); + + // Don't render if not visible. + if (!vertexCount || fActorImage->textureIndex() < 0) { + fPositions.clear(); + fTexs.clear(); + fBoneIdx.clear(); + fBoneWgt.clear(); + fIndices.clear(); + return; + } + + // Split the vertex data. + fPositions.resize(vertexCount); + fTexs.resize(vertexCount); + fIndices.resize(indexCount); + if (fSkinned) { + fBoneIdx.resize(vertexCount * 4); + fBoneWgt.resize(vertexCount * 4); + } + for (uint32_t i = 0; i < vertexCount; i ++) { + uint32_t j = i * vertexStride; + + // Get the attributes. + float* attrPosition = vertexData + j; + float* attrTex = vertexData + j + 2; + float* attrBoneIdx = vertexData + j + 4; + float* attrBoneWgt = vertexData + j + 8; + + // Get deformed positions if necessary. + if (fActorImage->doesAnimationVertexDeform()) { + attrPosition = fActorImage->animationDeformedVertices() + i * 2; + } + + // Set the data. + fPositions[i].set(attrPosition[0], attrPosition[1]); + 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(attrBoneIdx[k]); + fBoneWgt[i].weights[k] = attrBoneWgt[k]; + } + } + } + memcpy(fIndices.data(), indexData, indexCount * sizeof(uint16_t)); + } + + void updateBones() { + // NIMA matrices are a collection of 6 floats. + constexpr int kNIMAMatrixSize = 6; + + // Set up the matrices for the first time. + if (fBones.size() == 0) { + int numMatrices = 1; + if (fSkinned) { + numMatrices = fActorImage->boneInfluenceMatricesLength() / kNIMAMatrixSize; + } + fBones.assign(numMatrices, SkMatrix()); + } + + 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); + } + } + + // Set the zero matrix to be the world transform. + nima_to_skmatrix(fActorImage->worldTransform().values(), fBones[0]); + } + + void updateVerticesObject(bool applyDeforms) { + std::vector* positions = &fPositions; + + // Apply deforms if requested. + uint32_t vertexCount = fPositions.size(); + std::vector 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()); + } + + void drawVerticesObject(SkCanvas* canvas, bool useBones) const { + // Determine the blend mode. + SkBlendMode blendMode; + switch (fActorImage->blendMode()) { + case BlendMode::Off: { + blendMode = SkBlendMode::kSrc; + break; + } + case BlendMode::Normal: { + blendMode = SkBlendMode::kSrcOver; + break; + } + case BlendMode::Additive: { + blendMode = SkBlendMode::kPlus; + break; + } + case BlendMode::Multiply: { + blendMode = SkBlendMode::kMultiply; + break; + } + case BlendMode::Screen: { + blendMode = SkBlendMode::kScreen; + break; + } + } + + // Set the opacity. + fPaint->setAlpha(static_cast(fActorImage->renderOpacity() * 255)); + + // Draw the vertices. + if (useBones) { + canvas->drawVertices(fVertices, fBones.data(), fBones.size(), blendMode, *fPaint); + } else { + canvas->drawVertices(fVertices, 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; + SkPaint* fPaint; + + bool fSkinned; + std::vector fPositions; + std::vector fTexs; + std::vector fBoneIdx; + std::vector fBoneWgt; + std::vector fIndices; + + std::vector fBones; + sk_sp fVertices; + + RenderMode fRenderMode; +}; + +////////////////////////////////////////////////////////////////////////////////////////////////// + +// Represents an Actor, or an animated character, in NIMA. +class NIMAActor : public Actor { +public: + NIMAActor(const std::string& basePath) + : fTexture(nullptr) + , fActorImages() + , fPaint() + , fAnimations() { + // Load the NIMA data. + std::string nimaPath((basePath + ".nima").c_str()); + INHERITED::load(nimaPath); + + // Load the image asset. + sk_sp imageData = SkData::MakeFromFileName((basePath + ".png").c_str()); + fTexture = SkImage::MakeFromEncoded(imageData); + + // Create the paint. + fPaint.setShader(fTexture->makeShader(nullptr)); + fPaint.setFilterQuality(SkFilterQuality::kLow_SkFilterQuality); + + // Load the image nodes. + fActorImages.reserve(m_ImageNodeCount); + for (uint32_t i = 0; i < m_ImageNodeCount; i ++) { + fActorImages.emplace_back(m_ImageNodes[i], fTexture.get(), &fPaint); + } + + // Sort the image nodes. + std::sort(fActorImages.begin(), fActorImages.end(), [](auto a, auto b) { + return a.drawOrder() < b.drawOrder(); + }); + + // Get the list of animations. + fAnimations.reserve(m_AnimationsCount); + for (uint32_t i = 0; i < m_AnimationsCount; i ++) { + fAnimations.push_back(m_Animations[i].name()); + } + } + + void render(SkCanvas* canvas, RenderMode renderMode) { + // Render the image nodes. + for (auto& image : fActorImages) { + switch (renderMode) { + case kBackend_RenderMode: { + // Render with Skia backend. + image.renderBackend(canvas); + break; + } + case kImmediate_RenderMode: { + // Render with immediate backend. + image.renderImmediate(canvas); + break; + } + } + } + } + + const std::vector& getAnimations() const { + return fAnimations; + } + +private: + sk_sp fTexture; + std::vector fActorImages; + SkPaint fPaint; + std::vector fAnimations; + + typedef Actor INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////////////////////////// + +NIMASlide::NIMASlide(const SkString& name, const SkString& path) + : fBasePath() + , fActor(nullptr) + , fPlaying(true) + , fTime(0.0f) + , fRenderMode(kBackend_RenderMode) + , fAnimation(nullptr) + , fAnimationIndex(0) { + fName = name; + + // Get the path components. + SkString baseName = SkOSPath::Basename(path.c_str()); + baseName.resize(baseName.size() - 5); + SkString dirName = SkOSPath::Dirname(path.c_str()); + SkString basePath = SkOSPath::Join(dirName.c_str(), baseName.c_str()); + + // Save the base path. + fBasePath = std::string(basePath.c_str()); +} + +NIMASlide::~NIMASlide() {} + +void NIMASlide::draw(SkCanvas* canvas) { + canvas->save(); + + canvas->translate(500, 500); + canvas->scale(1, -1); + + // Render the actor. + fActor->render(canvas, fRenderMode); + + canvas->restore(); + + // Render the GUI. + this->renderGUI(); +} + +void NIMASlide::load(SkScalar winWidth, SkScalar winHeight) { + this->resetActor(); +} + +void NIMASlide::unload() { + // Discard resources. + fAnimation = nullptr; + fActor.reset(nullptr); +} + +bool NIMASlide::animate(const SkAnimTimer& timer) { + // Apply the animation. + if (fAnimation) { + if (fPlaying) { + fTime = std::fmod(timer.secs(), fAnimation->max()); + } + fAnimation->time(fTime); + fAnimation->apply(1.0f); + } + return true; +} + +bool NIMASlide::onChar(SkUnichar c) { + return false; +} + +bool NIMASlide::onMouse(SkScalar x, SkScalar y, Window::InputState state, uint32_t modifiers) { + return false; +} + +void NIMASlide::resetActor() { + // Create the actor. + fActor = std::make_unique(fBasePath); + + // Get the animation. + fAnimation = fActor->animationInstance(fActor->getAnimations()[fAnimationIndex]); +} + +void NIMASlide::renderGUI() { + ImGui::SetNextWindowSize(ImVec2(300, 220)); + ImGui::Begin("NIMA"); + + // List of animations. + auto animations = const_cast&>(fActor->getAnimations()); + ImGui::PushItemWidth(-1); + if (ImGui::ListBox("Animations", + &fAnimationIndex, + vector_getter, + reinterpret_cast(&animations), + animations.size(), + 5)) { + resetActor(); + } + + // Playback control. + ImGui::Spacing(); + if (ImGui::Button("Play")) { + fPlaying = true; + } + ImGui::SameLine(); + if (ImGui::Button("Pause")) { + fPlaying = false; + } + + // Time slider. + ImGui::PushItemWidth(-1); + ImGui::SliderFloat("Time", &fTime, 0.0f, fAnimation->max(), "Time: %.3f"); + + // Backend control. + int renderMode = fRenderMode; + ImGui::Spacing(); + ImGui::RadioButton("Skia Backend", &renderMode, 0); + ImGui::RadioButton("Immediate Backend", &renderMode, 1); + if (renderMode == 0) { + fRenderMode = kBackend_RenderMode; + } else { + fRenderMode = kImmediate_RenderMode; + } + + ImGui::End(); +} diff --git a/tools/viewer/NIMASlide.h b/tools/viewer/NIMASlide.h new file mode 100644 index 0000000000..d19d10119e --- /dev/null +++ b/tools/viewer/NIMASlide.h @@ -0,0 +1,59 @@ +/* +* Copyright 2018 Google Inc. +* +* Use of this source code is governed by a BSD-style license that can be +* found in the LICENSE file. +*/ + +#ifndef NIMASlide_DEFINED +#define NIMASlide_DEFINED + +#include "Slide.h" + +#include "SkCanvas.h" +#include "SkVertices.h" +#include +#include +#include +#include + +class NIMAActor; +class NIMAActorImage; + +enum RenderMode { + kBackend_RenderMode = 0, + kImmediate_RenderMode = 1, +}; + +class NIMASlide : public Slide { +public: + NIMASlide(const SkString& name, const SkString& path); + ~NIMASlide() override; + + void draw(SkCanvas* canvas) override; + void load(SkScalar winWidth, SkScalar winHeight) override; + void unload() override; + bool animate(const SkAnimTimer& timer) override; + + bool onChar(SkUnichar c) override; + bool onMouse(SkScalar x, SkScalar y, sk_app::Window::InputState state, + uint32_t modifiers) override; + +private: + void resetActor(); + + void renderGUI(); + +private: + std::string fBasePath; + std::unique_ptr fActor; + + bool fPlaying; + float fTime; + RenderMode fRenderMode; + + nima::ActorAnimationInstance* fAnimation; + int fAnimationIndex; +}; + +#endif diff --git a/tools/viewer/Viewer.cpp b/tools/viewer/Viewer.cpp index 81c1851783..27c88191c3 100644 --- a/tools/viewer/Viewer.cpp +++ b/tools/viewer/Viewer.cpp @@ -52,6 +52,10 @@ #include "SkottieSlide.h" #endif +#if !(defined(SK_BUILD_FOR_WIN) && defined(__clang__)) + #include "NIMASlide.h" +#endif + using namespace sk_app; static std::map gPathRendererNames; @@ -565,6 +569,12 @@ void Viewer::initSlides() { [](const SkString& name, const SkString& path) -> sk_sp { return sk_make_sp(name, path);} }, +#if !(defined(SK_BUILD_FOR_WIN) && defined(__clang__)) + { ".nima", "nima-dir", FLAGS_nimas, + [](const SkString& name, const SkString& path) -> sk_sp { + return sk_make_sp(name, path);} + }, +#endif }; SkTArray, true> dirSlides; -- cgit v1.2.3