VectorDrawable native rendering - Step 4 of MANY
This CL runs VectorDrawable animation on RenderThread. The changes in this CL
include:
- Convert all the animators in AnimatorSet for AVD into a set of RenderNodeAnimators.
- Hook up the new animators with RenderThread
- Add drawOp in RecordingCanvas for drawing VD so that during the animation
on RenderThread, all the property changes on VD can be reflected on the screen.
TODO:
- Implement reverse and reset for AVD.
Change-Id: I2df1d754f2db0ad098d9c15dde4bb2bdfafc2315
diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp
index 1cf15ac..c72f87d 100644
--- a/libs/hwui/VectorDrawable.cpp
+++ b/libs/hwui/VectorDrawable.cpp
@@ -138,18 +138,7 @@
}
FullPath::FullPath(const FullPath& path) : Path(path) {
- mStrokeWidth = path.mStrokeWidth;
- mStrokeColor = path.mStrokeColor;
- mStrokeAlpha = path.mStrokeAlpha;
- mFillColor = path.mFillColor;
- mFillAlpha = path.mFillAlpha;
- mTrimPathStart = path.mTrimPathStart;
- mTrimPathEnd = path.mTrimPathEnd;
- mTrimPathOffset = path.mTrimPathOffset;
- mStrokeMiterLimit = path.mStrokeMiterLimit;
- mStrokeLineCap = path.mStrokeLineCap;
- mStrokeLineJoin = path.mStrokeLineJoin;
-
+ mProperties = path.mProperties;
SkRefCnt_SafeAssign(mStrokeGradient, path.mStrokeGradient);
SkRefCnt_SafeAssign(mFillGradient, path.mFillGradient);
}
@@ -159,7 +148,7 @@
return mTrimmedSkPath;
}
Path::getUpdatedPath();
- if (mTrimPathStart != 0.0f || mTrimPathEnd != 1.0f) {
+ if (mProperties.trimPathStart != 0.0f || mProperties.trimPathEnd != 1.0f) {
applyTrim();
return mTrimmedSkPath;
} else {
@@ -170,14 +159,14 @@
void FullPath::updateProperties(float strokeWidth, SkColor strokeColor, float strokeAlpha,
SkColor fillColor, float fillAlpha, float trimPathStart, float trimPathEnd,
float trimPathOffset, float strokeMiterLimit, int strokeLineCap, int strokeLineJoin) {
- mStrokeWidth = strokeWidth;
- mStrokeColor = strokeColor;
- mStrokeAlpha = strokeAlpha;
- mFillColor = fillColor;
- mFillAlpha = fillAlpha;
- mStrokeMiterLimit = strokeMiterLimit;
- mStrokeLineCap = SkPaint::Cap(strokeLineCap);
- mStrokeLineJoin = SkPaint::Join(strokeLineJoin);
+ mProperties.strokeWidth = strokeWidth;
+ mProperties.strokeColor = strokeColor;
+ mProperties.strokeAlpha = strokeAlpha;
+ mProperties.fillColor = fillColor;
+ mProperties.fillAlpha = fillAlpha;
+ mProperties.strokeMiterLimit = strokeMiterLimit;
+ mProperties.strokeLineCap = strokeLineCap;
+ mProperties.strokeLineJoin = strokeLineJoin;
// If any trim property changes, mark trim dirty and update the trim path
setTrimPathStart(trimPathStart);
@@ -195,12 +184,12 @@
// Draw path's fill, if fill color or gradient is valid
bool needsFill = false;
if (mFillGradient != nullptr) {
- mPaint.setColor(applyAlpha(SK_ColorBLACK, mFillAlpha));
+ mPaint.setColor(applyAlpha(SK_ColorBLACK, mProperties.fillAlpha));
SkShader* newShader = mFillGradient->newWithLocalMatrix(matrix);
mPaint.setShader(newShader);
needsFill = true;
- } else if (mFillColor != SK_ColorTRANSPARENT) {
- mPaint.setColor(applyAlpha(mFillColor, mFillAlpha));
+ } else if (mProperties.fillColor != SK_ColorTRANSPARENT) {
+ mPaint.setColor(applyAlpha(mProperties.fillColor, mProperties.fillAlpha));
needsFill = true;
}
@@ -213,21 +202,21 @@
// Draw path's stroke, if stroke color or gradient is valid
bool needsStroke = false;
if (mStrokeGradient != nullptr) {
- mPaint.setColor(applyAlpha(SK_ColorBLACK, mStrokeAlpha));
+ mPaint.setColor(applyAlpha(SK_ColorBLACK, mProperties.strokeAlpha));
SkShader* newShader = mStrokeGradient->newWithLocalMatrix(matrix);
mPaint.setShader(newShader);
needsStroke = true;
- } else if (mStrokeColor != SK_ColorTRANSPARENT) {
- mPaint.setColor(applyAlpha(mStrokeColor, mStrokeAlpha));
+ } else if (mProperties.strokeColor != SK_ColorTRANSPARENT) {
+ mPaint.setColor(applyAlpha(mProperties.strokeColor, mProperties.strokeAlpha));
needsStroke = true;
}
if (needsStroke) {
mPaint.setStyle(SkPaint::Style::kStroke_Style);
mPaint.setAntiAlias(true);
- mPaint.setStrokeJoin(mStrokeLineJoin);
- mPaint.setStrokeCap(mStrokeLineCap);
- mPaint.setStrokeMiter(mStrokeMiterLimit);
- mPaint.setStrokeWidth(mStrokeWidth * strokeScale);
+ mPaint.setStrokeJoin(SkPaint::Join(mProperties.strokeLineJoin));
+ mPaint.setStrokeCap(SkPaint::Cap(mProperties.strokeLineCap));
+ mPaint.setStrokeMiter(mProperties.strokeMiterLimit);
+ mPaint.setStrokeWidth(mProperties.strokeWidth * strokeScale);
outCanvas->drawPath(renderPath, mPaint);
}
}
@@ -236,14 +225,14 @@
* Applies trimming to the specified path.
*/
void FullPath::applyTrim() {
- if (mTrimPathStart == 0.0f && mTrimPathEnd == 1.0f) {
+ if (mProperties.trimPathStart == 0.0f && mProperties.trimPathEnd == 1.0f) {
// No trimming necessary.
return;
}
SkPathMeasure measure(mSkPath, false);
float len = SkScalarToFloat(measure.getLength());
- float start = len * fmod((mTrimPathStart + mTrimPathOffset), 1.0f);
- float end = len * fmod((mTrimPathEnd + mTrimPathOffset), 1.0f);
+ float start = len * fmod((mProperties.trimPathStart + mProperties.trimPathOffset), 1.0f);
+ float end = len * fmod((mProperties.trimPathEnd + mProperties.trimPathOffset), 1.0f);
mTrimmedSkPath.reset();
if (start > end) {
@@ -255,76 +244,69 @@
mTrimDirty = false;
}
-inline int putData(int8_t* outBytes, int startIndex, float value) {
- int size = sizeof(float);
- memcpy(&outBytes[startIndex], &value, size);
- return size;
-}
-
-inline int putData(int8_t* outBytes, int startIndex, int value) {
- int size = sizeof(int);
- memcpy(&outBytes[startIndex], &value, size);
- return size;
-}
-
-struct FullPathProperties {
- // TODO: Consider storing full path properties in this struct instead of the fields.
- float strokeWidth;
- SkColor strokeColor;
- float strokeAlpha;
- SkColor fillColor;
- float fillAlpha;
- float trimPathStart;
- float trimPathEnd;
- float trimPathOffset;
- int32_t strokeLineCap;
- int32_t strokeLineJoin;
- float strokeMiterLimit;
-};
-
-REQUIRE_COMPATIBLE_LAYOUT(FullPathProperties);
+REQUIRE_COMPATIBLE_LAYOUT(FullPath::Properties);
static_assert(sizeof(float) == sizeof(int32_t), "float is not the same size as int32_t");
static_assert(sizeof(SkColor) == sizeof(int32_t), "SkColor is not the same size as int32_t");
bool FullPath::getProperties(int8_t* outProperties, int length) {
- int propertyDataSize = sizeof(FullPathProperties);
+ int propertyDataSize = sizeof(Properties);
if (length != propertyDataSize) {
LOG_ALWAYS_FATAL("Properties needs exactly %d bytes, a byte array of size %d is provided",
propertyDataSize, length);
return false;
}
- // TODO: consider replacing the property fields with a FullPathProperties struct.
- FullPathProperties properties;
- properties.strokeWidth = mStrokeWidth;
- properties.strokeColor = mStrokeColor;
- properties.strokeAlpha = mStrokeAlpha;
- properties.fillColor = mFillColor;
- properties.fillAlpha = mFillAlpha;
- properties.trimPathStart = mTrimPathStart;
- properties.trimPathEnd = mTrimPathEnd;
- properties.trimPathOffset = mTrimPathOffset;
- properties.strokeLineCap = mStrokeLineCap;
- properties.strokeLineJoin = mStrokeLineJoin;
- properties.strokeMiterLimit = mStrokeMiterLimit;
-
- memcpy(outProperties, &properties, length);
+ Properties* out = reinterpret_cast<Properties*>(outProperties);
+ *out = mProperties;
return true;
}
+void FullPath::setColorPropertyValue(int propertyId, int32_t value) {
+ Property currentProperty = static_cast<Property>(propertyId);
+ if (currentProperty == Property::StrokeColor) {
+ mProperties.strokeColor = value;
+ } else if (currentProperty == Property::FillColor) {
+ mProperties.fillColor = value;
+ } else {
+ LOG_ALWAYS_FATAL("Error setting color property on FullPath: No valid property with id: %d",
+ propertyId);
+ }
+}
+
+void FullPath::setPropertyValue(int propertyId, float value) {
+ Property property = static_cast<Property>(propertyId);
+ switch (property) {
+ case Property::StrokeWidth:
+ setStrokeWidth(value);
+ break;
+ case Property::StrokeAlpha:
+ setStrokeAlpha(value);
+ break;
+ case Property::FillAlpha:
+ setFillAlpha(value);
+ break;
+ case Property::TrimPathStart:
+ setTrimPathStart(value);
+ break;
+ case Property::TrimPathEnd:
+ setTrimPathEnd(value);
+ break;
+ case Property::TrimPathOffset:
+ setTrimPathOffset(value);
+ break;
+ default:
+ LOG_ALWAYS_FATAL("Invalid property id: %d for animation", propertyId);
+ break;
+ }
+}
+
void ClipPath::drawPath(SkCanvas* outCanvas, const SkPath& renderPath,
float strokeScale, const SkMatrix& matrix){
outCanvas->clipPath(renderPath, SkRegion::kIntersect_Op);
}
Group::Group(const Group& group) : Node(group) {
- mRotate = group.mRotate;
- mPivotX = group.mPivotX;
- mPivotY = group.mPivotY;
- mScaleX = group.mScaleX;
- mScaleY = group.mScaleY;
- mTranslateX = group.mTranslateX;
- mTranslateY = group.mTranslateY;
+ mProperties = group.mProperties;
}
void Group::draw(SkCanvas* outCanvas, const SkMatrix& currentMatrix, float scaleX,
@@ -371,10 +353,11 @@
outMatrix->reset();
// TODO: use rotate(mRotate, mPivotX, mPivotY) and scale with pivot point, instead of
// translating to pivot for rotating and scaling, then translating back.
- outMatrix->postTranslate(-mPivotX, -mPivotY);
- outMatrix->postScale(mScaleX, mScaleY);
- outMatrix->postRotate(mRotate, 0, 0);
- outMatrix->postTranslate(mTranslateX + mPivotX, mTranslateY + mPivotY);
+ outMatrix->postTranslate(-mProperties.pivotX, -mProperties.pivotY);
+ outMatrix->postScale(mProperties.scaleX, mProperties.scaleY);
+ outMatrix->postRotate(mProperties.rotate, 0, 0);
+ outMatrix->postTranslate(mProperties.translateX + mProperties.pivotX,
+ mProperties.translateY + mProperties.pivotY);
}
void Group::addChild(Node* child) {
@@ -388,38 +371,68 @@
propertyCount, length);
return false;
}
- for (int i = 0; i < propertyCount; i++) {
- Property currentProperty = static_cast<Property>(i);
- switch (currentProperty) {
- case Property::Rotate_Property:
- outProperties[i] = mRotate;
- break;
- case Property::PivotX_Property:
- outProperties[i] = mPivotX;
- break;
- case Property::PivotY_Property:
- outProperties[i] = mPivotY;
- break;
- case Property::ScaleX_Property:
- outProperties[i] = mScaleX;
- break;
- case Property::ScaleY_Property:
- outProperties[i] = mScaleY;
- break;
- case Property::TranslateX_Property:
- outProperties[i] = mTranslateX;
- break;
- case Property::TranslateY_Property:
- outProperties[i] = mTranslateY;
- break;
- default:
- LOG_ALWAYS_FATAL("Invalid input index: %d", i);
- return false;
- }
- }
+ Properties* out = reinterpret_cast<Properties*>(outProperties);
+ *out = mProperties;
return true;
}
+// TODO: Consider animating the properties as float pointers
+float Group::getPropertyValue(int propertyId) const {
+ Property currentProperty = static_cast<Property>(propertyId);
+ switch (currentProperty) {
+ case Property::Rotate:
+ return mProperties.rotate;
+ case Property::PivotX:
+ return mProperties.pivotX;
+ case Property::PivotY:
+ return mProperties.pivotY;
+ case Property::ScaleX:
+ return mProperties.scaleX;
+ case Property::ScaleY:
+ return mProperties.scaleY;
+ case Property::TranslateX:
+ return mProperties.translateX;
+ case Property::TranslateY:
+ return mProperties.translateY;
+ default:
+ LOG_ALWAYS_FATAL("Invalid property index: %d", propertyId);
+ return 0;
+ }
+}
+
+void Group::setPropertyValue(int propertyId, float value) {
+ Property currentProperty = static_cast<Property>(propertyId);
+ switch (currentProperty) {
+ case Property::Rotate:
+ mProperties.rotate = value;
+ break;
+ case Property::PivotX:
+ mProperties.pivotX = value;
+ break;
+ case Property::PivotY:
+ mProperties.pivotY = value;
+ break;
+ case Property::ScaleX:
+ mProperties.scaleX = value;
+ break;
+ case Property::ScaleY:
+ mProperties.scaleY = value;
+ break;
+ case Property::TranslateX:
+ mProperties.translateX = value;
+ break;
+ case Property::TranslateY:
+ mProperties.translateY = value;
+ break;
+ default:
+ LOG_ALWAYS_FATAL("Invalid property index: %d", propertyId);
+ }
+}
+
+bool Group::isValidProperty(int propertyId) {
+ return propertyId >= 0 && propertyId < static_cast<int>(Property::Count);
+}
+
void Tree::draw(Canvas* outCanvas, SkColorFilter* colorFilter,
const SkRect& bounds, bool needsMirroring, bool canReuseCache) {
// The imageView can scale the canvas in different ways, in order to
@@ -445,7 +458,9 @@
return;
}
- int saveCount = outCanvas->save(SaveFlags::MatrixClip);
+ mPaint.setColorFilter(colorFilter);
+
+ int saveCount = outCanvas->save(SkCanvas::SaveFlags::kMatrixClip_SaveFlag);
outCanvas->translate(mBounds.fLeft, mBounds.fTop);
// Handle RTL mirroring.
@@ -458,43 +473,33 @@
// And we use this bound for the destination rect for the drawBitmap, so
// we offset to (0, 0);
mBounds.offsetTo(0, 0);
-
createCachedBitmapIfNeeded(scaledWidth, scaledHeight);
- if (!mAllowCaching) {
- updateCachedBitmap(scaledWidth, scaledHeight);
- } else {
- if (!canReuseCache || mCacheDirty) {
- updateCachedBitmap(scaledWidth, scaledHeight);
- }
- }
- drawCachedBitmapWithRootAlpha(outCanvas, colorFilter, mBounds);
+
+ outCanvas->drawVectorDrawable(this);
outCanvas->restoreToCount(saveCount);
}
-void Tree::drawCachedBitmapWithRootAlpha(Canvas* outCanvas, SkColorFilter* filter,
- const SkRect& originalBounds) {
+SkPaint* Tree::getPaint() {
SkPaint* paint;
- if (mRootAlpha == 1.0f && filter == NULL) {
+ if (mRootAlpha == 1.0f && mPaint.getColorFilter() == NULL) {
paint = NULL;
} else {
mPaint.setFilterQuality(kLow_SkFilterQuality);
mPaint.setAlpha(mRootAlpha * 255);
- mPaint.setColorFilter(filter);
paint = &mPaint;
}
- outCanvas->drawBitmap(mCachedBitmap, 0, 0, mCachedBitmap.width(), mCachedBitmap.height(),
- originalBounds.fLeft, originalBounds.fTop, originalBounds.fRight,
- originalBounds.fBottom, paint);
+ return paint;
}
-void Tree::updateCachedBitmap(int width, int height) {
+const SkBitmap& Tree::getBitmapUpdateIfDirty() {
mCachedBitmap.eraseColor(SK_ColorTRANSPARENT);
SkCanvas outCanvas(mCachedBitmap);
- float scaleX = width / mViewportWidth;
- float scaleY = height / mViewportHeight;
+ float scaleX = (float) mCachedBitmap.width() / mViewportWidth;
+ float scaleY = (float) mCachedBitmap.height() / mViewportHeight;
mRootNode->draw(&outCanvas, SkMatrix::I(), scaleX, scaleY);
mCacheDirty = false;
+ return mCachedBitmap;
}
void Tree::createCachedBitmapIfNeeded(int width, int height) {