Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2c64d573 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Modify VectorDrawable to scale path through canvas matrix"

parents a9031fe3 cc29a5dd
Loading
Loading
Loading
Loading
+55 −108
Original line number Diff line number Diff line
@@ -33,65 +33,10 @@ namespace VectorDrawable {

const int Tree::MAX_CACHED_BITMAP_SIZE = 2048;

void Path::draw(SkCanvas* outCanvas, const SkMatrix& groupStackedMatrix, float scaleX, float scaleY,
        bool useStagingData) {
    float matrixScale = getMatrixScale(groupStackedMatrix);
    if (matrixScale == 0) {
        // When either x or y is scaled to 0, we don't need to draw anything.
        return;
    }

    SkMatrix pathMatrix(groupStackedMatrix);
    pathMatrix.postScale(scaleX, scaleY);

    //TODO: try apply the path matrix to the canvas instead of creating a new path.
    SkPath renderPath;
    renderPath.reset();

    if (useStagingData) {
        SkPath tmpPath;
        getStagingPath(&tmpPath);
        renderPath.addPath(tmpPath, pathMatrix);
    } else {
        renderPath.addPath(getUpdatedPath(), pathMatrix);
    }

    float minScale = fmin(scaleX, scaleY);
    float strokeScale = minScale * matrixScale;
    drawPath(outCanvas, renderPath, strokeScale, pathMatrix, useStagingData);
}

void Path::dump() {
    ALOGD("Path: %s has %zu points", mName.c_str(), mProperties.getData().points.size());
}

float Path::getMatrixScale(const SkMatrix& groupStackedMatrix) {
    // Given unit vectors A = (0, 1) and B = (1, 0).
    // After matrix mapping, we got A' and B'. Let theta = the angel b/t A' and B'.
    // Therefore, the final scale we want is min(|A'| * sin(theta), |B'| * sin(theta)),
    // which is (|A'| * |B'| * sin(theta)) / max (|A'|, |B'|);
    // If  max (|A'|, |B'|) = 0, that means either x or y has a scale of 0.
    //
    // For non-skew case, which is most of the cases, matrix scale is computing exactly the
    // scale on x and y axis, and take the minimal of these two.
    // For skew case, an unit square will mapped to a parallelogram. And this function will
    // return the minimal height of the 2 bases.
    SkVector skVectors[2];
    skVectors[0].set(0, 1);
    skVectors[1].set(1, 0);
    groupStackedMatrix.mapVectors(skVectors, 2);
    float scaleX = hypotf(skVectors[0].fX, skVectors[0].fY);
    float scaleY = hypotf(skVectors[1].fX, skVectors[1].fY);
    float crossProduct = skVectors[0].cross(skVectors[1]);
    float maxScale = fmax(scaleX, scaleY);

    float matrixScale = 0;
    if (maxScale > 0) {
        matrixScale = fabs(crossProduct) / maxScale;
    }
    return matrixScale;
}

// Called from UI thread during the initial setup/theme change.
Path::Path(const char* pathStr, size_t strLength) {
    PathParser::ParseResult result;
@@ -104,7 +49,12 @@ Path::Path(const Path& path) : Node(path) {
    mStagingProperties.syncProperties(path.mStagingProperties);
}

const SkPath& Path::getUpdatedPath() {
const SkPath& Path::getUpdatedPath(bool useStagingData, SkPath* tempStagingPath) {
    if (useStagingData) {
        tempStagingPath->reset();
        VectorDrawableUtils::verbsToPath(tempStagingPath, mStagingProperties.getData());
        return *tempStagingPath;
    } else {
        if (mSkPathDirty) {
            mSkPath.reset();
            VectorDrawableUtils::verbsToPath(&mSkPath, mProperties.getData());
@@ -112,10 +62,6 @@ const SkPath& Path::getUpdatedPath() {
        }
        return mSkPath;
    }

void Path::getStagingPath(SkPath* outPath) {
    outPath->reset();
    VectorDrawableUtils::verbsToPath(outPath, mStagingProperties.getData());
}

void Path::syncProperties() {
@@ -157,26 +103,35 @@ static void applyTrim(SkPath* outPath, const SkPath& inPath, float trimPathStart
    }
}

const SkPath& FullPath::getUpdatedPath() {
    if (!mSkPathDirty && !mProperties.mTrimDirty) {
const SkPath& FullPath::getUpdatedPath(bool useStagingData, SkPath* tempStagingPath) {
    if (!useStagingData && !mSkPathDirty && !mProperties.mTrimDirty) {
        return mTrimmedSkPath;
    }
    Path::getUpdatedPath();
    Path::getUpdatedPath(useStagingData, tempStagingPath);
    SkPath *outPath;
    if (useStagingData) {
        SkPath inPath = *tempStagingPath;
        applyTrim(tempStagingPath, inPath, mStagingProperties.getTrimPathStart(),
                mStagingProperties.getTrimPathEnd(), mStagingProperties.getTrimPathOffset());
        outPath = tempStagingPath;
    } else {
        if (mProperties.getTrimPathStart() != 0.0f || mProperties.getTrimPathEnd() != 1.0f) {
            mProperties.mTrimDirty = false;
            applyTrim(&mTrimmedSkPath, mSkPath, mProperties.getTrimPathStart(),
                    mProperties.getTrimPathEnd(), mProperties.getTrimPathOffset());
        return mTrimmedSkPath;
            outPath = &mTrimmedSkPath;
        } else {
        return mSkPath;
            outPath = &mSkPath;
        }
    }

void FullPath::getStagingPath(SkPath* outPath) {
    Path::getStagingPath(outPath);
    SkPath inPath = *outPath;
    applyTrim(outPath, inPath, mStagingProperties.getTrimPathStart(),
            mStagingProperties.getTrimPathEnd(), mStagingProperties.getTrimPathOffset());
    const FullPathProperties& properties = useStagingData ? mStagingProperties : mProperties;
    bool setFillPath = properties.getFillGradient() != nullptr
            || properties.getFillColor() != SK_ColorTRANSPARENT;
    if (setFillPath) {
        SkPath::FillType ft = static_cast<SkPath::FillType>(properties.getFillType());
        outPath->setFillType(ft);
    }
    return *outPath;
}

void FullPath::dump() {
@@ -192,16 +147,17 @@ inline SkColor applyAlpha(SkColor color, float alpha) {
    return SkColorSetA(color, alphaBytes * alpha);
}

void FullPath::drawPath(SkCanvas* outCanvas, SkPath& renderPath, float strokeScale,
                        const SkMatrix& matrix, bool useStagingData){
void FullPath::draw(SkCanvas* outCanvas, bool useStagingData) {
    const FullPathProperties& properties = useStagingData ? mStagingProperties : mProperties;
    SkPath tempStagingPath;
    const SkPath& renderPath = getUpdatedPath(useStagingData, &tempStagingPath);

    // Draw path's fill, if fill color or gradient is valid
    bool needsFill = false;
    SkPaint paint;
    if (properties.getFillGradient() != nullptr) {
        paint.setColor(applyAlpha(SK_ColorBLACK, properties.getFillAlpha()));
        paint.setShader(properties.getFillGradient()->makeWithLocalMatrix(matrix));
        paint.setShader(sk_sp<SkShader>(SkSafeRef(properties.getFillGradient())));
        needsFill = true;
    } else if (properties.getFillColor() != SK_ColorTRANSPARENT) {
        paint.setColor(applyAlpha(properties.getFillColor(), properties.getFillAlpha()));
@@ -211,8 +167,6 @@ void FullPath::drawPath(SkCanvas* outCanvas, SkPath& renderPath, float strokeSca
    if (needsFill) {
        paint.setStyle(SkPaint::Style::kFill_Style);
        paint.setAntiAlias(true);
        SkPath::FillType ft = static_cast<SkPath::FillType>(properties.getFillType());
        renderPath.setFillType(ft);
        outCanvas->drawPath(renderPath, paint);
    }

@@ -220,7 +174,7 @@ void FullPath::drawPath(SkCanvas* outCanvas, SkPath& renderPath, float strokeSca
    bool needsStroke = false;
    if (properties.getStrokeGradient() != nullptr) {
        paint.setColor(applyAlpha(SK_ColorBLACK, properties.getStrokeAlpha()));
        paint.setShader(properties.getStrokeGradient()->makeWithLocalMatrix(matrix));
        paint.setShader(sk_sp<SkShader>(SkSafeRef(properties.getStrokeGradient())));
        needsStroke = true;
    } else if (properties.getStrokeColor() != SK_ColorTRANSPARENT) {
        paint.setColor(applyAlpha(properties.getStrokeColor(), properties.getStrokeAlpha()));
@@ -232,7 +186,7 @@ void FullPath::drawPath(SkCanvas* outCanvas, SkPath& renderPath, float strokeSca
        paint.setStrokeJoin(SkPaint::Join(properties.getStrokeLineJoin()));
        paint.setStrokeCap(SkPaint::Cap(properties.getStrokeLineCap()));
        paint.setStrokeMiter(properties.getStrokeMiterLimit());
        paint.setStrokeWidth(properties.getStrokeWidth() * strokeScale);
        paint.setStrokeWidth(properties.getStrokeWidth());
        outCanvas->drawPath(renderPath, paint);
    }
}
@@ -306,36 +260,28 @@ void FullPath::FullPathProperties::setPropertyValue(int propertyId, float value)
    }
}

void ClipPath::drawPath(SkCanvas* outCanvas, SkPath& renderPath,
        float strokeScale, const SkMatrix& matrix, bool useStagingData){
    outCanvas->clipPath(renderPath);
void ClipPath::draw(SkCanvas* outCanvas, bool useStagingData) {
    SkPath tempStagingPath;
    outCanvas->clipPath(getUpdatedPath(useStagingData, &tempStagingPath));
}

Group::Group(const Group& group) : Node(group) {
    mStagingProperties.syncProperties(group.mStagingProperties);
}

void Group::draw(SkCanvas* outCanvas, const SkMatrix& currentMatrix, float scaleX,
        float scaleY, bool useStagingData) {
    // TODO: Try apply the matrix to the canvas instead of passing it down the tree

    // Calculate current group's matrix by preConcat the parent's and
    // and the current one on the top of the stack.
    // Basically the Mfinal = Mviewport * M0 * M1 * M2;
    // Mi the local matrix at level i of the group tree.
void Group::draw(SkCanvas* outCanvas, bool useStagingData) {
    // Save the current clip and matrix information, which is local to this group.
    SkAutoCanvasRestore saver(outCanvas, true);
    // apply the current group's matrix to the canvas
    SkMatrix stackedMatrix;
    const GroupProperties& prop = useStagingData ? mStagingProperties : mProperties;
    getLocalMatrix(&stackedMatrix, prop);
    stackedMatrix.postConcat(currentMatrix);

    // Save the current clip information, which is local to this group.
    outCanvas->save();
    outCanvas->concat(stackedMatrix);
    // Draw the group tree in the same order as the XML file.
    for (auto& child : mChildren) {
        child->draw(outCanvas, stackedMatrix, scaleX, scaleY, useStagingData);
        child->draw(outCanvas, useStagingData);
    }
    // Restore the previous clip information.
    outCanvas->restore();
    // Restore the previous clip and matrix information.
}

void Group::dump() {
@@ -556,7 +502,8 @@ void Tree::updateBitmapCache(Bitmap& bitmap, bool useStagingData) {
            mStagingProperties.getViewportHeight() : mProperties.getViewportHeight();
    float scaleX = outCache.width() / viewportWidth;
    float scaleY = outCache.height() / viewportHeight;
    mRootNode->draw(&outCanvas, SkMatrix::I(), scaleX, scaleY, useStagingData);
    outCanvas.scale(scaleX, scaleY);
    mRootNode->draw(&outCanvas, useStagingData);
}

bool Tree::allocateBitmapIfNeeded(Cache& cache, int width, int height) {
+6 −19
Original line number Diff line number Diff line
@@ -109,8 +109,7 @@ public:
        mName = node.mName;
    }
    Node() {}
    virtual void draw(SkCanvas* outCanvas, const SkMatrix& currentMatrix,
            float scaleX, float scaleY, bool useStagingData) = 0;
    virtual void draw(SkCanvas* outCanvas, bool useStagingData) = 0;
    virtual void dump() = 0;
    void setName(const char* name) {
        mName = name;
@@ -169,9 +168,6 @@ public:
    Path() {}

    void dump() override;
    void draw(SkCanvas* outCanvas, const SkMatrix& groupStackedMatrix,
            float scaleX, float scaleY, bool useStagingData) override;
    static float getMatrixScale(const SkMatrix& groupStackedMatrix);
    virtual void syncProperties() override;
    virtual void onPropertyChanged(Properties* prop) override {
        if (prop == &mStagingProperties) {
@@ -193,10 +189,7 @@ public:
    PathProperties* mutateProperties() { return &mProperties; }

protected:
    virtual const SkPath& getUpdatedPath();
    virtual void getStagingPath(SkPath* outPath);
    virtual void drawPath(SkCanvas *outCanvas, SkPath& renderPath,
            float strokeScale, const SkMatrix& matrix, bool useStagingData) = 0;
    virtual const SkPath& getUpdatedPath(bool useStagingData, SkPath* tempStagingPath);

    // Internal data, render thread only.
    bool mSkPathDirty = true;
@@ -364,6 +357,7 @@ public:
    FullPath(const FullPath& path); // for cloning
    FullPath(const char* path, size_t strLength) : Path(path, strLength) {}
    FullPath() : Path() {}
    void draw(SkCanvas* outCanvas, bool useStagingData) override;
    void dump() override;
    FullPathProperties* mutateStagingProperties() { return &mStagingProperties; }
    const FullPathProperties* stagingProperties() { return &mStagingProperties; }
@@ -387,10 +381,7 @@ public:
    }

protected:
    const SkPath& getUpdatedPath() override;
    void getStagingPath(SkPath* outPath) override;
    void drawPath(SkCanvas* outCanvas, SkPath& renderPath,
            float strokeScale, const SkMatrix& matrix, bool useStagingData) override;
    const SkPath& getUpdatedPath(bool useStagingData, SkPath* tempStagingPath) override;
private:

    FullPathProperties mProperties = FullPathProperties(this);
@@ -407,10 +398,7 @@ public:
    ClipPath(const ClipPath& path) : Path(path) {}
    ClipPath(const char* path, size_t strLength) : Path(path, strLength) {}
    ClipPath() : Path() {}

protected:
    void drawPath(SkCanvas* outCanvas, SkPath& renderPath,
            float strokeScale, const SkMatrix& matrix, bool useStagingData) override;
    void draw(SkCanvas* outCanvas, bool useStagingData) override;
};

class ANDROID_API Group: public Node {
@@ -519,8 +507,7 @@ public:
    GroupProperties* mutateProperties() { return &mProperties; }

    // Methods below could be called from either UI thread or Render Thread.
    virtual void draw(SkCanvas* outCanvas, const SkMatrix& currentMatrix,
            float scaleX, float scaleY, bool useStagingData) override;
    virtual void draw(SkCanvas* outCanvas, bool useStagingData) override;
    void getLocalMatrix(SkMatrix* outMatrix, const GroupProperties& properties);
    void dump() override;
    static bool isValidProperty(int propertyId);
+1 −46
Original line number Diff line number Diff line
@@ -347,51 +347,6 @@ TEST(VectorDrawableUtils, interpolatePathData) {
    }
}

TEST(VectorDrawable, matrixScale) {
    struct MatrixAndScale {
        float buffer[9];
        float matrixScale;
    };

    const MatrixAndScale sMatrixAndScales[] {
        {
            {1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f},
            1.0
        },
        {
            {1.0f, 0.0f, 240.0f, 0.0f, 1.0f, 240.0f, 0.0f, 0.0f, 1.0f},
            1.0f,
        },
        {
            {1.5f, 0.0f, 24.0f, 0.0f, 1.5f, 24.0f, 0.0f, 0.0f, 1.0f},
            1.5f,
        },
        {
            {0.99999994f, 0.0f, 300.0f, 0.0f, 0.99999994f, 158.57864f, 0.0f, 0.0f, 1.0f},
            0.99999994f,
        },
        {
            {0.7071067f, 0.7071067f, 402.5305f, -0.7071067f, 0.7071067f, 169.18524f, 0.0f, 0.0f, 1.0f},
            0.99999994f,
        },
        {
            {0.0f, 0.9999999f, 482.5305f, -0.9999999f, 0.0f, 104.18525f, 0.0f, 0.0f, 1.0f},
            0.9999999f,
        },
        {
            {-0.35810637f, -0.93368083f, 76.55821f, 0.93368083f, -0.35810637f, 89.538506f, 0.0f, 0.0f, 1.0f},
            1.0000001f,
        },
    };

    for (MatrixAndScale matrixAndScale : sMatrixAndScales) {
        SkMatrix matrix;
        matrix.set9(matrixAndScale.buffer);
        float actualMatrixScale = VectorDrawable::Path::getMatrixScale(matrix);
        EXPECT_EQ(matrixAndScale.matrixScale, actualMatrixScale);
    }
}

TEST(VectorDrawable, groupProperties) {
    //TODO: Also need to test property sync and dirty flag when properties change.
    VectorDrawable::Group group;
@@ -458,7 +413,7 @@ TEST(VectorDrawable, drawPathWithoutIncrementingShaderRefCount) {

    // Setting the fill gradient increments the ref count of the shader by 1
    path.mutateStagingProperties()->setFillGradient(shader);
    path.draw(&canvas, SkMatrix::I(), 1.0f, 1.0f, true);
    path.draw(&canvas, true);
    // Resetting the fill gradient decrements the ref count of the shader by 1
    path.mutateStagingProperties()->setFillGradient(nullptr);