• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "VectorDrawable.h"
18 
19 #include "PathParser.h"
20 #include "SkColorFilter.h"
21 #include "SkImageInfo.h"
22 #include "SkShader.h"
23 #include <utils/Log.h>
24 #include "utils/Macros.h"
25 #include "utils/TraceUtils.h"
26 #include "utils/VectorDrawableUtils.h"
27 
28 #include <math.h>
29 #include <string.h>
30 
31 namespace android {
32 namespace uirenderer {
33 namespace VectorDrawable {
34 
35 const int Tree::MAX_CACHED_BITMAP_SIZE = 2048;
36 
dump()37 void Path::dump() {
38     ALOGD("Path: %s has %zu points", mName.c_str(), mProperties.getData().points.size());
39 }
40 
41 // Called from UI thread during the initial setup/theme change.
Path(const char * pathStr,size_t strLength)42 Path::Path(const char* pathStr, size_t strLength) {
43     PathParser::ParseResult result;
44     Data data;
45     PathParser::getPathDataFromAsciiString(&data, &result, pathStr, strLength);
46     mStagingProperties.setData(data);
47 }
48 
Path(const Path & path)49 Path::Path(const Path& path) : Node(path) {
50     mStagingProperties.syncProperties(path.mStagingProperties);
51 }
52 
getUpdatedPath(bool useStagingData,SkPath * tempStagingPath)53 const SkPath& Path::getUpdatedPath(bool useStagingData, SkPath* tempStagingPath) {
54     if (useStagingData) {
55         tempStagingPath->reset();
56         VectorDrawableUtils::verbsToPath(tempStagingPath, mStagingProperties.getData());
57         return *tempStagingPath;
58     } else {
59         if (mSkPathDirty) {
60             mSkPath.reset();
61             VectorDrawableUtils::verbsToPath(&mSkPath, mProperties.getData());
62             mSkPathDirty = false;
63         }
64         return mSkPath;
65     }
66 }
67 
syncProperties()68 void Path::syncProperties() {
69     if (mStagingPropertiesDirty) {
70         mProperties.syncProperties(mStagingProperties);
71     } else {
72         mStagingProperties.syncProperties(mProperties);
73     }
74     mStagingPropertiesDirty = false;
75 }
76 
FullPath(const FullPath & path)77 FullPath::FullPath(const FullPath& path) : Path(path) {
78     mStagingProperties.syncProperties(path.mStagingProperties);
79 }
80 
applyTrim(SkPath * outPath,const SkPath & inPath,float trimPathStart,float trimPathEnd,float trimPathOffset)81 static void applyTrim(SkPath* outPath, const SkPath& inPath, float trimPathStart, float trimPathEnd,
82         float trimPathOffset) {
83     if (trimPathStart == 0.0f && trimPathEnd == 1.0f) {
84         *outPath = inPath;
85         return;
86     }
87     outPath->reset();
88     if (trimPathStart == trimPathEnd) {
89         // Trimmed path should be empty.
90         return;
91     }
92     SkPathMeasure measure(inPath, false);
93     float len = SkScalarToFloat(measure.getLength());
94     float start = len * fmod((trimPathStart + trimPathOffset), 1.0f);
95     float end = len * fmod((trimPathEnd + trimPathOffset), 1.0f);
96 
97     if (start > end) {
98         measure.getSegment(start, len, outPath, true);
99         if (end > 0) {
100             measure.getSegment(0, end, outPath, true);
101         }
102     } else {
103         measure.getSegment(start, end, outPath, true);
104     }
105 }
106 
getUpdatedPath(bool useStagingData,SkPath * tempStagingPath)107 const SkPath& FullPath::getUpdatedPath(bool useStagingData, SkPath* tempStagingPath) {
108     if (!useStagingData && !mSkPathDirty && !mProperties.mTrimDirty) {
109         return mTrimmedSkPath;
110     }
111     Path::getUpdatedPath(useStagingData, tempStagingPath);
112     SkPath *outPath;
113     if (useStagingData) {
114         SkPath inPath = *tempStagingPath;
115         applyTrim(tempStagingPath, inPath, mStagingProperties.getTrimPathStart(),
116                 mStagingProperties.getTrimPathEnd(), mStagingProperties.getTrimPathOffset());
117         outPath = tempStagingPath;
118     } else {
119         if (mProperties.getTrimPathStart() != 0.0f || mProperties.getTrimPathEnd() != 1.0f) {
120             mProperties.mTrimDirty = false;
121             applyTrim(&mTrimmedSkPath, mSkPath, mProperties.getTrimPathStart(),
122                     mProperties.getTrimPathEnd(), mProperties.getTrimPathOffset());
123             outPath = &mTrimmedSkPath;
124         } else {
125             outPath = &mSkPath;
126         }
127     }
128     const FullPathProperties& properties = useStagingData ? mStagingProperties : mProperties;
129     bool setFillPath = properties.getFillGradient() != nullptr
130             || properties.getFillColor() != SK_ColorTRANSPARENT;
131     if (setFillPath) {
132         SkPath::FillType ft = static_cast<SkPath::FillType>(properties.getFillType());
133         outPath->setFillType(ft);
134     }
135     return *outPath;
136 }
137 
dump()138 void FullPath::dump() {
139     Path::dump();
140     ALOGD("stroke width, color, alpha: %f, %d, %f, fill color, alpha: %d, %f",
141             mProperties.getStrokeWidth(), mProperties.getStrokeColor(), mProperties.getStrokeAlpha(),
142             mProperties.getFillColor(), mProperties.getFillAlpha());
143 }
144 
145 
applyAlpha(SkColor color,float alpha)146 inline SkColor applyAlpha(SkColor color, float alpha) {
147     int alphaBytes = SkColorGetA(color);
148     return SkColorSetA(color, alphaBytes * alpha);
149 }
150 
draw(SkCanvas * outCanvas,bool useStagingData)151 void FullPath::draw(SkCanvas* outCanvas, bool useStagingData) {
152     const FullPathProperties& properties = useStagingData ? mStagingProperties : mProperties;
153     SkPath tempStagingPath;
154     const SkPath& renderPath = getUpdatedPath(useStagingData, &tempStagingPath);
155 
156     // Draw path's fill, if fill color or gradient is valid
157     bool needsFill = false;
158     SkPaint paint;
159     if (properties.getFillGradient() != nullptr) {
160         paint.setColor(applyAlpha(SK_ColorBLACK, properties.getFillAlpha()));
161         paint.setShader(sk_sp<SkShader>(SkSafeRef(properties.getFillGradient())));
162         needsFill = true;
163     } else if (properties.getFillColor() != SK_ColorTRANSPARENT) {
164         paint.setColor(applyAlpha(properties.getFillColor(), properties.getFillAlpha()));
165         needsFill = true;
166     }
167 
168     if (needsFill) {
169         paint.setStyle(SkPaint::Style::kFill_Style);
170         paint.setAntiAlias(true);
171         outCanvas->drawPath(renderPath, paint);
172     }
173 
174     // Draw path's stroke, if stroke color or Gradient is valid
175     bool needsStroke = false;
176     if (properties.getStrokeGradient() != nullptr) {
177         paint.setColor(applyAlpha(SK_ColorBLACK, properties.getStrokeAlpha()));
178         paint.setShader(sk_sp<SkShader>(SkSafeRef(properties.getStrokeGradient())));
179         needsStroke = true;
180     } else if (properties.getStrokeColor() != SK_ColorTRANSPARENT) {
181         paint.setColor(applyAlpha(properties.getStrokeColor(), properties.getStrokeAlpha()));
182         needsStroke = true;
183     }
184     if (needsStroke) {
185         paint.setStyle(SkPaint::Style::kStroke_Style);
186         paint.setAntiAlias(true);
187         paint.setStrokeJoin(SkPaint::Join(properties.getStrokeLineJoin()));
188         paint.setStrokeCap(SkPaint::Cap(properties.getStrokeLineCap()));
189         paint.setStrokeMiter(properties.getStrokeMiterLimit());
190         paint.setStrokeWidth(properties.getStrokeWidth());
191         outCanvas->drawPath(renderPath, paint);
192     }
193 }
194 
syncProperties()195 void FullPath::syncProperties() {
196     Path::syncProperties();
197 
198     if (mStagingPropertiesDirty) {
199         mProperties.syncProperties(mStagingProperties);
200     } else {
201         // Update staging property with property values from animation.
202         mStagingProperties.syncProperties(mProperties);
203     }
204     mStagingPropertiesDirty = false;
205 }
206 
207 REQUIRE_COMPATIBLE_LAYOUT(FullPath::FullPathProperties::PrimitiveFields);
208 
209 static_assert(sizeof(float) == sizeof(int32_t), "float is not the same size as int32_t");
210 static_assert(sizeof(SkColor) == sizeof(int32_t), "SkColor is not the same size as int32_t");
211 
copyProperties(int8_t * outProperties,int length) const212 bool FullPath::FullPathProperties::copyProperties(int8_t* outProperties, int length) const {
213     int propertyDataSize = sizeof(FullPathProperties::PrimitiveFields);
214     if (length != propertyDataSize) {
215         LOG_ALWAYS_FATAL("Properties needs exactly %d bytes, a byte array of size %d is provided",
216                 propertyDataSize, length);
217         return false;
218     }
219 
220     PrimitiveFields* out = reinterpret_cast<PrimitiveFields*>(outProperties);
221     *out = mPrimitiveFields;
222     return true;
223 }
224 
setColorPropertyValue(int propertyId,int32_t value)225 void FullPath::FullPathProperties::setColorPropertyValue(int propertyId, int32_t value) {
226     Property currentProperty = static_cast<Property>(propertyId);
227     if (currentProperty == Property::strokeColor) {
228         setStrokeColor(value);
229     } else if (currentProperty == Property::fillColor) {
230         setFillColor(value);
231     } else {
232         LOG_ALWAYS_FATAL("Error setting color property on FullPath: No valid property"
233                 " with id: %d", propertyId);
234     }
235 }
236 
setPropertyValue(int propertyId,float value)237 void FullPath::FullPathProperties::setPropertyValue(int propertyId, float value) {
238     Property property = static_cast<Property>(propertyId);
239     switch (property) {
240     case Property::strokeWidth:
241         setStrokeWidth(value);
242         break;
243     case Property::strokeAlpha:
244         setStrokeAlpha(value);
245         break;
246     case Property::fillAlpha:
247         setFillAlpha(value);
248         break;
249     case Property::trimPathStart:
250         setTrimPathStart(value);
251         break;
252     case Property::trimPathEnd:
253         setTrimPathEnd(value);
254         break;
255     case Property::trimPathOffset:
256         setTrimPathOffset(value);
257         break;
258     default:
259         LOG_ALWAYS_FATAL("Invalid property id: %d for animation", propertyId);
260         break;
261     }
262 }
263 
draw(SkCanvas * outCanvas,bool useStagingData)264 void ClipPath::draw(SkCanvas* outCanvas, bool useStagingData) {
265     SkPath tempStagingPath;
266     outCanvas->clipPath(getUpdatedPath(useStagingData, &tempStagingPath));
267 }
268 
Group(const Group & group)269 Group::Group(const Group& group) : Node(group) {
270     mStagingProperties.syncProperties(group.mStagingProperties);
271 }
272 
draw(SkCanvas * outCanvas,bool useStagingData)273 void Group::draw(SkCanvas* outCanvas, bool useStagingData) {
274     // Save the current clip and matrix information, which is local to this group.
275     SkAutoCanvasRestore saver(outCanvas, true);
276     // apply the current group's matrix to the canvas
277     SkMatrix stackedMatrix;
278     const GroupProperties& prop = useStagingData ? mStagingProperties : mProperties;
279     getLocalMatrix(&stackedMatrix, prop);
280     outCanvas->concat(stackedMatrix);
281     // Draw the group tree in the same order as the XML file.
282     for (auto& child : mChildren) {
283         child->draw(outCanvas, useStagingData);
284     }
285     // Restore the previous clip and matrix information.
286 }
287 
dump()288 void Group::dump() {
289     ALOGD("Group %s has %zu children: ", mName.c_str(), mChildren.size());
290     ALOGD("Group translateX, Y : %f, %f, scaleX, Y: %f, %f", mProperties.getTranslateX(),
291             mProperties.getTranslateY(), mProperties.getScaleX(), mProperties.getScaleY());
292     for (size_t i = 0; i < mChildren.size(); i++) {
293         mChildren[i]->dump();
294     }
295 }
296 
syncProperties()297 void Group::syncProperties() {
298     // Copy over the dirty staging properties
299     if (mStagingPropertiesDirty) {
300         mProperties.syncProperties(mStagingProperties);
301     } else {
302         mStagingProperties.syncProperties(mProperties);
303     }
304     mStagingPropertiesDirty = false;
305     for (auto& child : mChildren) {
306         child->syncProperties();
307     }
308 }
309 
getLocalMatrix(SkMatrix * outMatrix,const GroupProperties & properties)310 void Group::getLocalMatrix(SkMatrix* outMatrix, const GroupProperties& properties) {
311     outMatrix->reset();
312     // TODO: use rotate(mRotate, mPivotX, mPivotY) and scale with pivot point, instead of
313     // translating to pivot for rotating and scaling, then translating back.
314     outMatrix->postTranslate(-properties.getPivotX(), -properties.getPivotY());
315     outMatrix->postScale(properties.getScaleX(), properties.getScaleY());
316     outMatrix->postRotate(properties.getRotation(), 0, 0);
317     outMatrix->postTranslate(properties.getTranslateX() + properties.getPivotX(),
318             properties.getTranslateY() + properties.getPivotY());
319 }
320 
addChild(Node * child)321 void Group::addChild(Node* child) {
322     mChildren.emplace_back(child);
323     if (mPropertyChangedListener != nullptr) {
324         child->setPropertyChangedListener(mPropertyChangedListener);
325     }
326 }
327 
copyProperties(float * outProperties,int length) const328 bool Group::GroupProperties::copyProperties(float* outProperties, int length) const {
329     int propertyCount = static_cast<int>(Property::count);
330     if (length != propertyCount) {
331         LOG_ALWAYS_FATAL("Properties needs exactly %d bytes, a byte array of size %d is provided",
332                 propertyCount, length);
333         return false;
334     }
335 
336     PrimitiveFields* out = reinterpret_cast<PrimitiveFields*>(outProperties);
337     *out = mPrimitiveFields;
338     return true;
339 }
340 
341 // TODO: Consider animating the properties as float pointers
342 // Called on render thread
getPropertyValue(int propertyId) const343 float Group::GroupProperties::getPropertyValue(int propertyId) const {
344     Property currentProperty = static_cast<Property>(propertyId);
345     switch (currentProperty) {
346     case Property::rotate:
347         return getRotation();
348     case Property::pivotX:
349         return getPivotX();
350     case Property::pivotY:
351         return getPivotY();
352     case Property::scaleX:
353         return getScaleX();
354     case Property::scaleY:
355         return getScaleY();
356     case Property::translateX:
357         return getTranslateX();
358     case Property::translateY:
359         return getTranslateY();
360     default:
361         LOG_ALWAYS_FATAL("Invalid property index: %d", propertyId);
362         return 0;
363     }
364 }
365 
366 // Called on render thread
setPropertyValue(int propertyId,float value)367 void Group::GroupProperties::setPropertyValue(int propertyId, float value) {
368     Property currentProperty = static_cast<Property>(propertyId);
369     switch (currentProperty) {
370     case Property::rotate:
371         setRotation(value);
372         break;
373     case Property::pivotX:
374         setPivotX(value);
375         break;
376     case Property::pivotY:
377         setPivotY(value);
378         break;
379     case Property::scaleX:
380         setScaleX(value);
381         break;
382     case Property::scaleY:
383         setScaleY(value);
384         break;
385     case Property::translateX:
386         setTranslateX(value);
387         break;
388     case Property::translateY:
389         setTranslateY(value);
390         break;
391     default:
392         LOG_ALWAYS_FATAL("Invalid property index: %d", propertyId);
393     }
394 }
395 
isValidProperty(int propertyId)396 bool Group::isValidProperty(int propertyId) {
397     return GroupProperties::isValidProperty(propertyId);
398 }
399 
isValidProperty(int propertyId)400 bool Group::GroupProperties::isValidProperty(int propertyId) {
401     return propertyId >= 0 && propertyId < static_cast<int>(Property::count);
402 }
403 
draw(Canvas * outCanvas,SkColorFilter * colorFilter,const SkRect & bounds,bool needsMirroring,bool canReuseCache)404 int Tree::draw(Canvas* outCanvas, SkColorFilter* colorFilter,
405         const SkRect& bounds, bool needsMirroring, bool canReuseCache) {
406     // The imageView can scale the canvas in different ways, in order to
407     // avoid blurry scaling, we have to draw into a bitmap with exact pixel
408     // size first. This bitmap size is determined by the bounds and the
409     // canvas scale.
410     SkMatrix canvasMatrix;
411     outCanvas->getMatrix(&canvasMatrix);
412     float canvasScaleX = 1.0f;
413     float canvasScaleY = 1.0f;
414     if (canvasMatrix.getSkewX() == 0 && canvasMatrix.getSkewY() == 0) {
415         // Only use the scale value when there's no skew or rotation in the canvas matrix.
416         // TODO: Add a cts test for drawing VD on a canvas with negative scaling factors.
417         canvasScaleX = fabs(canvasMatrix.getScaleX());
418         canvasScaleY = fabs(canvasMatrix.getScaleY());
419     }
420     int scaledWidth = (int) (bounds.width() * canvasScaleX);
421     int scaledHeight = (int) (bounds.height() * canvasScaleY);
422     scaledWidth = std::min(Tree::MAX_CACHED_BITMAP_SIZE, scaledWidth);
423     scaledHeight = std::min(Tree::MAX_CACHED_BITMAP_SIZE, scaledHeight);
424 
425     if (scaledWidth <= 0 || scaledHeight <= 0) {
426         return 0;
427     }
428 
429     mStagingProperties.setScaledSize(scaledWidth, scaledHeight);
430     int saveCount = outCanvas->save(SaveFlags::MatrixClip);
431     outCanvas->translate(bounds.fLeft, bounds.fTop);
432 
433     // Handle RTL mirroring.
434     if (needsMirroring) {
435         outCanvas->translate(bounds.width(), 0);
436         outCanvas->scale(-1.0f, 1.0f);
437     }
438     mStagingProperties.setColorFilter(colorFilter);
439 
440     // At this point, canvas has been translated to the right position.
441     // And we use this bound for the destination rect for the drawBitmap, so
442     // we offset to (0, 0);
443     SkRect tmpBounds = bounds;
444     tmpBounds.offsetTo(0, 0);
445     mStagingProperties.setBounds(tmpBounds);
446     outCanvas->drawVectorDrawable(this);
447     outCanvas->restoreToCount(saveCount);
448     return scaledWidth * scaledHeight;
449 }
450 
drawStaging(Canvas * outCanvas)451 void Tree::drawStaging(Canvas* outCanvas) {
452     bool redrawNeeded = allocateBitmapIfNeeded(mStagingCache,
453             mStagingProperties.getScaledWidth(), mStagingProperties.getScaledHeight());
454     // draw bitmap cache
455     if (redrawNeeded || mStagingCache.dirty) {
456         updateBitmapCache(*mStagingCache.bitmap, true);
457         mStagingCache.dirty = false;
458     }
459 
460     SkPaint tmpPaint;
461     SkPaint* paint = updatePaint(&tmpPaint, &mStagingProperties);
462     outCanvas->drawBitmap(*mStagingCache.bitmap, 0, 0,
463             mStagingCache.bitmap->width(), mStagingCache.bitmap->height(),
464             mStagingProperties.getBounds().left(), mStagingProperties.getBounds().top(),
465             mStagingProperties.getBounds().right(), mStagingProperties.getBounds().bottom(), paint);
466 }
467 
getPaint()468 SkPaint* Tree::getPaint() {
469     return updatePaint(&mPaint, &mProperties);
470 }
471 
472 // Update the given paint with alpha and color filter. Return nullptr if no color filter is
473 // specified and root alpha is 1. Otherwise, return updated paint.
updatePaint(SkPaint * outPaint,TreeProperties * prop)474 SkPaint* Tree::updatePaint(SkPaint* outPaint, TreeProperties* prop) {
475     if (prop->getRootAlpha() == 1.0f && prop->getColorFilter() == nullptr) {
476         return nullptr;
477     } else {
478         outPaint->setColorFilter(sk_ref_sp(prop->getColorFilter()));
479         outPaint->setFilterQuality(kLow_SkFilterQuality);
480         outPaint->setAlpha(prop->getRootAlpha() * 255);
481         return outPaint;
482     }
483 }
484 
getBitmapUpdateIfDirty()485 Bitmap& Tree::getBitmapUpdateIfDirty() {
486     bool redrawNeeded = allocateBitmapIfNeeded(mCache, mProperties.getScaledWidth(),
487             mProperties.getScaledHeight());
488     if (redrawNeeded || mCache.dirty) {
489         updateBitmapCache(*mCache.bitmap, false);
490         mCache.dirty = false;
491     }
492     return *mCache.bitmap;
493 }
494 
updateCache(sp<skiapipeline::VectorDrawableAtlas> & atlas,GrContext * context)495 void Tree::updateCache(sp<skiapipeline::VectorDrawableAtlas>& atlas, GrContext* context) {
496     SkRect dst;
497     sk_sp<SkSurface> surface = mCache.getSurface(&dst);
498     bool canReuseSurface = surface && dst.width() >= mProperties.getScaledWidth()
499             && dst.height() >= mProperties.getScaledHeight();
500     if (!canReuseSurface) {
501         int scaledWidth = SkScalarCeilToInt(mProperties.getScaledWidth());
502         int scaledHeight = SkScalarCeilToInt(mProperties.getScaledHeight());
503         auto atlasEntry = atlas->requestNewEntry(scaledWidth, scaledHeight, context);
504         if (INVALID_ATLAS_KEY != atlasEntry.key) {
505             dst = atlasEntry.rect;
506             surface = atlasEntry.surface;
507             mCache.setAtlas(atlas, atlasEntry.key);
508         } else {
509             //don't draw, if we failed to allocate an offscreen buffer
510             mCache.clear();
511             surface.reset();
512         }
513     }
514     if (!canReuseSurface || mCache.dirty) {
515         draw(surface.get(), dst);
516         mCache.dirty = false;
517     }
518 }
519 
draw(SkSurface * surface,const SkRect & dst)520 void Tree::draw(SkSurface* surface, const SkRect& dst) {
521     if (surface) {
522         SkCanvas* canvas = surface->getCanvas();
523         float scaleX = dst.width() / mProperties.getViewportWidth();
524         float scaleY = dst.height() / mProperties.getViewportHeight();
525         SkAutoCanvasRestore acr(canvas, true);
526         canvas->translate(dst.fLeft, dst.fTop);
527         canvas->clipRect(SkRect::MakeWH(dst.width(), dst.height()));
528         canvas->clear(SK_ColorTRANSPARENT);
529         canvas->scale(scaleX, scaleY);
530         mRootNode->draw(canvas, false);
531     }
532 }
533 
setAtlas(sp<skiapipeline::VectorDrawableAtlas> newAtlas,skiapipeline::AtlasKey newAtlasKey)534 void Tree::Cache::setAtlas(sp<skiapipeline::VectorDrawableAtlas> newAtlas,
535         skiapipeline::AtlasKey newAtlasKey) {
536     LOG_ALWAYS_FATAL_IF(newAtlasKey == INVALID_ATLAS_KEY);
537     clear();
538     mAtlas = newAtlas;
539     mAtlasKey = newAtlasKey;
540 }
541 
getSurface(SkRect * bounds)542 sk_sp<SkSurface> Tree::Cache::getSurface(SkRect* bounds) {
543     sk_sp<SkSurface> surface;
544     sp<skiapipeline::VectorDrawableAtlas> atlas = mAtlas.promote();
545     if (atlas.get() && mAtlasKey != INVALID_ATLAS_KEY) {
546         auto atlasEntry = atlas->getEntry(mAtlasKey);
547         *bounds = atlasEntry.rect;
548         surface = atlasEntry.surface;
549         mAtlasKey = atlasEntry.key;
550     }
551 
552     return surface;
553 }
554 
clear()555 void Tree::Cache::clear() {
556     sp<skiapipeline::VectorDrawableAtlas> lockAtlas = mAtlas.promote();
557     if (lockAtlas.get()) {
558         lockAtlas->releaseEntry(mAtlasKey);
559     }
560     mAtlas = nullptr;
561     mAtlasKey = INVALID_ATLAS_KEY;
562 }
563 
draw(SkCanvas * canvas)564 void Tree::draw(SkCanvas* canvas) {
565     SkRect src;
566     sk_sp<SkSurface> vdSurface = mCache.getSurface(&src);
567     if (vdSurface) {
568         canvas->drawImageRect(vdSurface->makeImageSnapshot().get(), src,
569                 mutateProperties()->getBounds(), getPaint());
570     } else {
571         // Handle the case when VectorDrawableAtlas has been destroyed, because of memory pressure.
572         // We render the VD into a temporary standalone buffer and mark the frame as dirty. Next
573         // frame will be cached into the atlas.
574         int scaledWidth = SkScalarCeilToInt(mProperties.getScaledWidth());
575         int scaledHeight = SkScalarCeilToInt(mProperties.getScaledHeight());
576         SkRect src = SkRect::MakeWH(scaledWidth, scaledHeight);
577 #ifndef ANDROID_ENABLE_LINEAR_BLENDING
578         sk_sp<SkColorSpace> colorSpace = nullptr;
579 #else
580         sk_sp<SkColorSpace> colorSpace = SkColorSpace::MakeSRGB();
581 #endif
582         SkImageInfo info = SkImageInfo::MakeN32(scaledWidth, scaledHeight, kPremul_SkAlphaType,
583                 colorSpace);
584         sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(canvas->getGrContext(),
585                 SkBudgeted::kYes, info);
586         draw(surface.get(), src);
587         mCache.clear();
588         canvas->drawImageRect(surface->makeImageSnapshot().get(), mutateProperties()->getBounds(),
589                 getPaint());
590         markDirty();
591     }
592 }
593 
updateBitmapCache(Bitmap & bitmap,bool useStagingData)594 void Tree::updateBitmapCache(Bitmap& bitmap, bool useStagingData) {
595     SkBitmap outCache;
596     bitmap.getSkBitmap(&outCache);
597     int cacheWidth = outCache.width();
598     int cacheHeight = outCache.height();
599     ATRACE_FORMAT("VectorDrawable repaint %dx%d", cacheWidth, cacheHeight);
600     outCache.eraseColor(SK_ColorTRANSPARENT);
601     SkCanvas outCanvas(outCache);
602     float viewportWidth = useStagingData ?
603             mStagingProperties.getViewportWidth() : mProperties.getViewportWidth();
604     float viewportHeight = useStagingData ?
605             mStagingProperties.getViewportHeight() : mProperties.getViewportHeight();
606     float scaleX = cacheWidth / viewportWidth;
607     float scaleY = cacheHeight / viewportHeight;
608     outCanvas.scale(scaleX, scaleY);
609     mRootNode->draw(&outCanvas, useStagingData);
610 }
611 
allocateBitmapIfNeeded(Cache & cache,int width,int height)612 bool Tree::allocateBitmapIfNeeded(Cache& cache, int width, int height) {
613     if (!canReuseBitmap(cache.bitmap.get(), width, height)) {
614 #ifndef ANDROID_ENABLE_LINEAR_BLENDING
615         sk_sp<SkColorSpace> colorSpace = nullptr;
616 #else
617         sk_sp<SkColorSpace> colorSpace = SkColorSpace::MakeSRGB();
618 #endif
619         SkImageInfo info = SkImageInfo::MakeN32(width, height, kPremul_SkAlphaType, colorSpace);
620         cache.bitmap = Bitmap::allocateHeapBitmap(info);
621         return true;
622     }
623     return false;
624 }
625 
canReuseBitmap(Bitmap * bitmap,int width,int height)626 bool Tree::canReuseBitmap(Bitmap* bitmap, int width, int height) {
627     return bitmap && width <= bitmap->width() && height <= bitmap->height();
628 }
629 
onPropertyChanged(TreeProperties * prop)630 void Tree::onPropertyChanged(TreeProperties* prop) {
631     if (prop == &mStagingProperties) {
632         mStagingCache.dirty = true;
633     } else {
634         mCache.dirty = true;
635     }
636 }
637 
638 }; // namespace VectorDrawable
639 
640 }; // namespace uirenderer
641 }; // namespace android
642