blob: 36a8aebeaa332c77ee4b3e6e659b8bb9aee0197e [file] [log] [blame]
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef ANDROID_HWUI_VPATH_H
#define ANDROID_HWUI_VPATH_H
#include "Canvas.h"
#include <SkBitmap.h>
#include <SkColor.h>
#include <SkCanvas.h>
#include <SkMatrix.h>
#include <SkPaint.h>
#include <SkPath.h>
#include <SkPathMeasure.h>
#include <SkRect.h>
#include <SkShader.h>
#include <cutils/compiler.h>
#include <stddef.h>
#include <vector>
#include <string>
namespace android {
namespace uirenderer {
namespace VectorDrawable {
#define VD_SET_PROP_WITH_FLAG(field, value, flag) (VD_SET_PROP(field, value) ? (flag = true, true): false);
#define VD_SET_PROP(field, value) (value != field ? (field = value, true) : false)
/* A VectorDrawable is composed of a tree of nodes.
* Each node can be a group node, or a path.
* A group node can have groups or paths as children, but a path node has
* no children.
* One example can be:
* Root Group
* / | \
* Group Path Group
* / \ |
* Path Path Path
*
*/
class ANDROID_API Node {
public:
Node(const Node& node) {
mName = node.mName;
}
Node() {}
virtual void draw(SkCanvas* outCanvas, const SkMatrix& currentMatrix,
float scaleX, float scaleY) = 0;
virtual void dump() = 0;
void setName(const char* name) {
mName = name;
}
virtual ~Node(){}
protected:
std::string mName;
};
class ANDROID_API Path : public Node {
public:
struct ANDROID_API Data {
std::vector<char> verbs;
std::vector<size_t> verbSizes;
std::vector<float> points;
bool operator==(const Data& data) const {
return verbs == data.verbs && verbSizes == data.verbSizes
&& points == data.points;
}
};
Path(const Data& nodes);
Path(const Path& path);
Path(const char* path, size_t strLength);
Path() {}
void dump() override;
bool canMorph(const Data& path);
bool canMorph(const Path& path);
void draw(SkCanvas* outCanvas, const SkMatrix& groupStackedMatrix,
float scaleX, float scaleY) override;
void setPath(const char* path, size_t strLength);
void setPathData(const Data& data);
static float getMatrixScale(const SkMatrix& groupStackedMatrix);
protected:
virtual const SkPath& getUpdatedPath();
virtual void drawPath(SkCanvas *outCanvas, const SkPath& renderPath,
float strokeScale, const SkMatrix& matrix) = 0;
Data mData;
SkPath mSkPath;
bool mSkPathDirty = true;
};
class ANDROID_API FullPath: public Path {
public:
struct Properties {
float strokeWidth = 0;
SkColor strokeColor = SK_ColorTRANSPARENT;
float strokeAlpha = 1;
SkColor fillColor = SK_ColorTRANSPARENT;
float fillAlpha = 1;
float trimPathStart = 0;
float trimPathEnd = 1;
float trimPathOffset = 0;
int32_t strokeLineCap = SkPaint::Cap::kButt_Cap;
int32_t strokeLineJoin = SkPaint::Join::kMiter_Join;
float strokeMiterLimit = 4;
};
FullPath(const FullPath& path); // for cloning
FullPath(const char* path, size_t strLength) : Path(path, strLength) {}
FullPath() : Path() {}
FullPath(const Data& nodes) : Path(nodes) {}
~FullPath() {
SkSafeUnref(mFillGradient);
SkSafeUnref(mStrokeGradient);
}
void updateProperties(float strokeWidth, SkColor strokeColor,
float strokeAlpha, SkColor fillColor, float fillAlpha,
float trimPathStart, float trimPathEnd, float trimPathOffset,
float strokeMiterLimit, int strokeLineCap, int strokeLineJoin);
// TODO: Cleanup: Remove the setter and getters below, and their counterparts in java and JNI
float getStrokeWidth() {
return mProperties.strokeWidth;
}
void setStrokeWidth(float strokeWidth) {
mProperties.strokeWidth = strokeWidth;
}
SkColor getStrokeColor() {
return mProperties.strokeColor;
}
void setStrokeColor(SkColor strokeColor) {
mProperties.strokeColor = strokeColor;
}
float getStrokeAlpha() {
return mProperties.strokeAlpha;
}
void setStrokeAlpha(float strokeAlpha) {
mProperties.strokeAlpha = strokeAlpha;
}
SkColor getFillColor() {
return mProperties.fillColor;
}
void setFillColor(SkColor fillColor) {
mProperties.fillColor = fillColor;
}
float getFillAlpha() {
return mProperties.fillAlpha;
}
void setFillAlpha(float fillAlpha) {
mProperties.fillAlpha = fillAlpha;
}
float getTrimPathStart() {
return mProperties.trimPathStart;
}
void setTrimPathStart(float trimPathStart) {
VD_SET_PROP_WITH_FLAG(mProperties.trimPathStart, trimPathStart, mTrimDirty);
}
float getTrimPathEnd() {
return mProperties.trimPathEnd;
}
void setTrimPathEnd(float trimPathEnd) {
VD_SET_PROP_WITH_FLAG(mProperties.trimPathEnd, trimPathEnd, mTrimDirty);
}
float getTrimPathOffset() {
return mProperties.trimPathOffset;
}
void setTrimPathOffset(float trimPathOffset) {
VD_SET_PROP_WITH_FLAG(mProperties.trimPathOffset, trimPathOffset, mTrimDirty);
}
bool getProperties(int8_t* outProperties, int length);
void setColorPropertyValue(int propertyId, int32_t value);
void setPropertyValue(int propertyId, float value);
void setFillGradient(SkShader* fillGradient) {
SkRefCnt_SafeAssign(mFillGradient, fillGradient);
};
void setStrokeGradient(SkShader* strokeGradient) {
SkRefCnt_SafeAssign(mStrokeGradient, strokeGradient);
};
protected:
const SkPath& getUpdatedPath() override;
void drawPath(SkCanvas* outCanvas, const SkPath& renderPath,
float strokeScale, const SkMatrix& matrix) override;
private:
enum class Property {
StrokeWidth = 0,
StrokeColor,
StrokeAlpha,
FillColor,
FillAlpha,
TrimPathStart,
TrimPathEnd,
TrimPathOffset,
StrokeLineCap,
StrokeLineJoin,
StrokeMiterLimit,
Count,
};
// Applies trimming to the specified path.
void applyTrim();
Properties mProperties;
bool mTrimDirty = true;
SkPath mTrimmedSkPath;
SkPaint mPaint;
SkShader* mStrokeGradient = nullptr;
SkShader* mFillGradient = nullptr;
};
class ANDROID_API ClipPath: public Path {
public:
ClipPath(const ClipPath& path) : Path(path) {}
ClipPath(const char* path, size_t strLength) : Path(path, strLength) {}
ClipPath() : Path() {}
ClipPath(const Data& nodes) : Path(nodes) {}
protected:
void drawPath(SkCanvas* outCanvas, const SkPath& renderPath,
float strokeScale, const SkMatrix& matrix) override;
};
class ANDROID_API Group: public Node {
public:
struct Properties {
float rotate = 0;
float pivotX = 0;
float pivotY = 0;
float scaleX = 1;
float scaleY = 1;
float translateX = 0;
float translateY = 0;
};
Group(const Group& group);
Group() {}
float getRotation() {
return mProperties.rotate;
}
void setRotation(float rotation) {
mProperties.rotate = rotation;
}
float getPivotX() {
return mProperties.pivotX;
}
void setPivotX(float pivotX) {
mProperties.pivotX = pivotX;
}
float getPivotY() {
return mProperties.pivotY;
}
void setPivotY(float pivotY) {
mProperties.pivotY = pivotY;
}
float getScaleX() {
return mProperties.scaleX;
}
void setScaleX(float scaleX) {
mProperties.scaleX = scaleX;
}
float getScaleY() {
return mProperties.scaleY;
}
void setScaleY(float scaleY) {
mProperties.scaleY = scaleY;
}
float getTranslateX() {
return mProperties.translateX;
}
void setTranslateX(float translateX) {
mProperties.translateX = translateX;
}
float getTranslateY() {
return mProperties.translateY;
}
void setTranslateY(float translateY) {
mProperties.translateY = translateY;
}
virtual void draw(SkCanvas* outCanvas, const SkMatrix& currentMatrix,
float scaleX, float scaleY) override;
void updateLocalMatrix(float rotate, float pivotX, float pivotY,
float scaleX, float scaleY, float translateX, float translateY);
void getLocalMatrix(SkMatrix* outMatrix);
void addChild(Node* child);
void dump() override;
bool getProperties(float* outProperties, int length);
float getPropertyValue(int propertyId) const;
void setPropertyValue(int propertyId, float value);
static bool isValidProperty(int propertyId);
private:
enum class Property {
Rotate = 0,
PivotX,
PivotY,
ScaleX,
ScaleY,
TranslateX,
TranslateY,
// Count of the properties, must be at the end.
Count,
};
std::vector< std::unique_ptr<Node> > mChildren;
Properties mProperties;
};
class ANDROID_API Tree : public VirtualLightRefBase {
public:
Tree(Group* rootNode) : mRootNode(rootNode) {}
void draw(Canvas* outCanvas, SkColorFilter* colorFilter,
const SkRect& bounds, bool needsMirroring, bool canReuseCache);
const SkBitmap& getBitmapUpdateIfDirty();
void createCachedBitmapIfNeeded(int width, int height);
bool canReuseBitmap(int width, int height);
void setAllowCaching(bool allowCaching) {
mAllowCaching = allowCaching;
}
bool setRootAlpha(float rootAlpha) {
return VD_SET_PROP(mRootAlpha, rootAlpha);
}
float getRootAlpha() {
return mRootAlpha;
}
void setViewportSize(float viewportWidth, float viewportHeight) {
mViewportWidth = viewportWidth;
mViewportHeight = viewportHeight;
}
SkPaint* getPaint();
const SkRect& getBounds() const {
return mBounds;
}
private:
// Cap the bitmap size, such that it won't hurt the performance too much
// and it won't crash due to a very large scale.
// The drawable will look blurry above this size.
const static int MAX_CACHED_BITMAP_SIZE;
bool mCacheDirty = true;
bool mAllowCaching = true;
float mViewportWidth = 0;
float mViewportHeight = 0;
float mRootAlpha = 1.0f;
std::unique_ptr<Group> mRootNode;
SkRect mBounds;
SkMatrix mCanvasMatrix;
SkPaint mPaint;
SkPathMeasure mPathMeasure;
SkBitmap mCachedBitmap;
};
} // namespace VectorDrawable
typedef VectorDrawable::Path::Data PathData;
} // namespace uirenderer
} // namespace android
#endif // ANDROID_HWUI_VPATH_H