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