[go: nahoru, domu]

blob: 7fee446a2f89d2cc9ddeccfe6bf519924790c3e7 [file] [log] [blame]
Nader Jawadb1030d42019-05-07 15:33:13 -07001/*
2 * Copyright 2019 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
Nader Jawad02a9d4e2019-08-27 10:49:47 -070017package androidx.ui.graphics.vector.compat
Nader Jawadb1030d42019-05-07 15:33:13 -070018
19import android.content.res.Resources
20import android.util.AttributeSet
Nader Jawadd02c0972019-08-27 15:01:07 -070021import androidx.core.content.res.ComplexColorCompat
Nader Jawadb1030d42019-05-07 15:33:13 -070022import androidx.core.content.res.TypedArrayUtils
Louis Pullen-Freilich0d2d0ed2019-12-13 18:41:46 +000023import androidx.ui.core.dp
Nader Jawadd02c0972019-08-27 15:01:07 -070024import androidx.ui.graphics.Brush
25import androidx.ui.graphics.Color
Louis Pullen-Freilich0d2d0ed2019-12-13 18:41:46 +000026import androidx.ui.graphics.Shader
Nader Jawadd02c0972019-08-27 15:01:07 -070027import androidx.ui.graphics.ShaderBrush
28import androidx.ui.graphics.SolidColor
Nader Jawadad983532019-08-29 16:27:37 -070029import androidx.ui.graphics.StrokeCap
30import androidx.ui.graphics.StrokeJoin
Nader Jawad02a9d4e2019-08-27 10:49:47 -070031import androidx.ui.graphics.vector.DefaultPivotX
32import androidx.ui.graphics.vector.DefaultPivotY
33import androidx.ui.graphics.vector.DefaultRotation
34import androidx.ui.graphics.vector.DefaultScaleX
35import androidx.ui.graphics.vector.DefaultScaleY
36import androidx.ui.graphics.vector.DefaultTranslationX
37import androidx.ui.graphics.vector.DefaultTranslationY
38import androidx.ui.graphics.vector.EmptyPath
39import androidx.ui.graphics.vector.PathNode
Louis Pullen-Freilich0d2d0ed2019-12-13 18:41:46 +000040import androidx.ui.graphics.vector.VectorAssetBuilder
41import androidx.ui.graphics.vector.addPathNodes
Nader Jawadb1030d42019-05-07 15:33:13 -070042import org.xmlpull.v1.XmlPullParser
43import org.xmlpull.v1.XmlPullParserException
44
Louis Pullen-Freilich78e7cd72020-01-09 14:54:01 +000045private const val LINECAP_BUTT = 0
46private const val LINECAP_ROUND = 1
47private const val LINECAP_SQUARE = 2
Nader Jawadb1030d42019-05-07 15:33:13 -070048
Louis Pullen-Freilich78e7cd72020-01-09 14:54:01 +000049private const val LINEJOIN_MITER = 0
50private const val LINEJOIN_ROUND = 1
51private const val LINEJOIN_BEVEL = 2
Nader Jawadb1030d42019-05-07 15:33:13 -070052
53private val FILL_TYPE_WINDING = 0
54
Louis Pullen-Freilich78e7cd72020-01-09 14:54:01 +000055private const val SHAPE_CLIP_PATH = "clip-path"
56private const val SHAPE_GROUP = "group"
57private const val SHAPE_PATH = "path"
Nader Jawadb1030d42019-05-07 15:33:13 -070058
59private fun getStrokeLineCap(id: Int, defValue: StrokeCap = StrokeCap.butt): StrokeCap =
60 when (id) {
61 LINECAP_BUTT -> StrokeCap.butt
62 LINECAP_ROUND -> StrokeCap.round
63 LINECAP_SQUARE -> StrokeCap.square
64 else -> defValue
65 }
66
67private fun getStrokeLineJoin(id: Int, defValue: StrokeJoin = StrokeJoin.miter): StrokeJoin =
68 when (id) {
69 LINEJOIN_MITER -> StrokeJoin.miter
70 LINEJOIN_ROUND -> StrokeJoin.round
71 LINEJOIN_BEVEL -> StrokeJoin.bevel
72 else -> defValue
73 }
74
75internal fun XmlPullParser.isAtEnd(): Boolean =
76 eventType == XmlPullParser.END_DOCUMENT ||
77 (depth < 1 && eventType == XmlPullParser.END_TAG)
78
79internal fun XmlPullParser.parseCurrentVectorNode(
80 res: Resources,
81 attrs: AttributeSet,
82 theme: Resources.Theme? = null,
83 builder: VectorAssetBuilder
84) {
85 when (eventType) {
86 XmlPullParser.START_TAG -> {
87 when (name) {
88 SHAPE_PATH -> {
89 parsePath(res, theme, attrs, builder)
90 }
91 SHAPE_CLIP_PATH -> {
Louis Pullen-Freilich78e7cd72020-01-09 14:54:01 +000092 // TODO: b/147418351 - parse clipping paths
Nader Jawadb1030d42019-05-07 15:33:13 -070093 }
94 SHAPE_GROUP -> {
95 parseGroup(res, theme, attrs, builder)
96 }
97 }
98 }
99 XmlPullParser.END_TAG -> {
100 if (SHAPE_GROUP == name) {
101 builder.popGroup()
102 }
103 }
104 }
105}
106
107/**
108 * Helper method to seek to the first tag within the VectorDrawable xml asset
109 */
110@Throws(XmlPullParserException::class)
111internal fun XmlPullParser.seekToStartTag(): XmlPullParser {
112 var type = next()
113 while (type != XmlPullParser.START_TAG && type != XmlPullParser.END_DOCUMENT) {
114 // Empty loop
115 type = next()
116 }
117 if (type != XmlPullParser.START_TAG) {
118 throw XmlPullParserException("No start tag found")
119 }
120 return this
121}
122
123@SuppressWarnings("RestrictedApi")
124internal fun XmlPullParser.createVectorImageBuilder(
125 res: Resources,
126 theme: Resources.Theme?,
127 attrs: AttributeSet
128): VectorAssetBuilder {
129 val vectorAttrs = TypedArrayUtils.obtainAttributes(
130 res,
131 theme,
132 attrs,
133 AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_TYPE_ARRAY
134 )
135
136 // TODO (njawad) handle mirroring here
137// state.mAutoMirrored = TypedArrayUtils.getNamedBoolean(a, parser, "autoMirrored",
138// AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_AUTO_MIRRORED, state.mAutoMirrored)
139
140 val viewportWidth = TypedArrayUtils.getNamedFloat(
141 vectorAttrs,
142 this,
143 "viewportWidth",
144 AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_VIEWPORT_WIDTH,
145 0.0f
146 )
147
148 val viewportHeight = TypedArrayUtils.getNamedFloat(
149 vectorAttrs,
150 this,
151 "viewportHeight",
152 AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_VIEWPORT_HEIGHT,
153 0.0f
154 )
155
156 if (viewportWidth <= 0) {
157 throw XmlPullParserException(
Louis Pullen-Freilich0d2d0ed2019-12-13 18:41:46 +0000158 vectorAttrs.positionDescription + "<VectorGraphic> tag requires viewportWidth > 0"
Nader Jawadb1030d42019-05-07 15:33:13 -0700159 )
160 } else if (viewportHeight <= 0) {
161 throw XmlPullParserException(
Louis Pullen-Freilich0d2d0ed2019-12-13 18:41:46 +0000162 vectorAttrs.positionDescription + "<VectorGraphic> tag requires viewportHeight > 0"
Nader Jawadb1030d42019-05-07 15:33:13 -0700163 )
164 }
165
166 val defaultWidth = vectorAttrs.getDimension(
167 AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_WIDTH, 0.0f
168 )
169 val defaultHeight = vectorAttrs.getDimension(
170 AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_HEIGHT, 0.0f
171 )
172
Louis Pullen-Freilich0d2d0ed2019-12-13 18:41:46 +0000173 val defaultWidthDp = (defaultWidth / res.displayMetrics.density).dp
174 val defaultHeightDp = (defaultHeight / res.displayMetrics.density).dp
175
Nader Jawadb1030d42019-05-07 15:33:13 -0700176 vectorAttrs.recycle()
177
178 return VectorAssetBuilder(
Louis Pullen-Freilich0d2d0ed2019-12-13 18:41:46 +0000179 defaultWidth = defaultWidthDp,
180 defaultHeight = defaultHeightDp,
Nader Jawadb1030d42019-05-07 15:33:13 -0700181 viewportWidth = viewportWidth,
182 viewportHeight = viewportHeight
183 )
184}
185
186@Throws(IllegalArgumentException::class)
187@SuppressWarnings("RestrictedApi")
188internal fun XmlPullParser.parsePath(
189 res: Resources,
190 theme: Resources.Theme?,
191 attrs: AttributeSet,
192 builder: VectorAssetBuilder
193) {
194 val a = TypedArrayUtils.obtainAttributes(
195 res,
196 theme,
197 attrs,
198 AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH
199 )
200
201 val hasPathData = TypedArrayUtils.hasAttribute(this, "pathData")
202 if (!hasPathData) {
203 // If there is no pathData in the VPath tag, then this is an empty VPath,
204 // nothing need to be drawn.
205 throw IllegalArgumentException("No path data available")
206 }
207
208 val name: String = a.getString(AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_NAME) ?: ""
209
210 val pathStr = a.getString(AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_PATH_DATA)
211
Nader Jawad4ef199b2019-10-22 18:37:01 -0700212 val pathData: List<PathNode> = addPathNodes(pathStr)
Nader Jawadb1030d42019-05-07 15:33:13 -0700213
214 val fillColor = TypedArrayUtils.getNamedComplexColor(
215 a,
216 this,
217 theme,
218 "fillColor",
219 AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_FILL_COLOR, 0
220 )
221 val fillAlpha = TypedArrayUtils.getNamedFloat(
222 a,
223 this,
224 "fillAlpha",
225 AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_FILL_ALPHA, 1.0f
226 )
227 val lineCap = TypedArrayUtils.getNamedInt(
228 a,
229 this,
230 "strokeLineCap",
231 AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_STROKE_LINE_CAP, -1
232 )
233 val strokeLineCap = getStrokeLineCap(lineCap, StrokeCap.butt)
234 val lineJoin = TypedArrayUtils.getNamedInt(
235 a,
236 this,
237 "strokeLineJoin",
238 AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_STROKE_LINE_JOIN, -1
239 )
Nader Jawad02a9d4e2019-08-27 10:49:47 -0700240 val strokeLineJoin =
241 getStrokeLineJoin(lineJoin, StrokeJoin.bevel)
Nader Jawadb1030d42019-05-07 15:33:13 -0700242 val strokeMiterLimit = TypedArrayUtils.getNamedFloat(
243 a,
244 this,
245 "strokeMiterLimit",
246 AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_STROKE_MITER_LIMIT,
247 1.0f
248 )
249 val strokeColor = TypedArrayUtils.getNamedComplexColor(
250 a,
251 this,
252 theme,
253 "strokeColor",
254 AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_STROKE_COLOR, 0
255 )
256 val strokeAlpha = TypedArrayUtils.getNamedFloat(
257 a,
258 this,
259 "strokeAlpha",
260 AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_STROKE_ALPHA, 1.0f
261 )
262 val strokeLineWidth = TypedArrayUtils.getNamedFloat(
263 a,
264 this,
265 "strokeWidth",
266 AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_STROKE_WIDTH, 1.0f
267 )
268
269 // TODO (njawad) handle trim paths + fill rule
270// val trimPathEnd = TypedArrayUtils.getNamedFloat(
271// a, this, "trimPathEnd",
272// AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_TRIM_PATH_END, 1.0f
273// )
274// val trimPathOffset = TypedArrayUtils.getNamedFloat(
275// a, this, "trimPathOffset",
276// AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_TRIM_PATH_OFFSET,
277// 0.0f
278// )
279// val trimPathStart = TypedArrayUtils.getNamedFloat(
280// a, this, "trimPathStart",
281// AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_TRIM_PATH_START,
282// 0.0f
283// )
284// val fillRule = TypedArrayUtils.getNamedInt(
285// a, this, "fillType",
286// AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_TRIM_PATH_FILLTYPE,
287// FILL_TYPE_WINDING
288// )
289
290 a.recycle()
291
Nader Jawadd02c0972019-08-27 15:01:07 -0700292 val fillBrush = obtainBrushFromComplexColor(fillColor)
293 val strokeBrush = obtainBrushFromComplexColor(strokeColor)
Nader Jawadb1030d42019-05-07 15:33:13 -0700294
295 builder.addPath(
296 pathData,
297 name,
298 fillBrush,
299 fillAlpha,
300 strokeBrush,
301 strokeAlpha,
302 strokeLineWidth,
303 strokeLineCap,
304 strokeLineJoin,
305 strokeMiterLimit)
306}
307
308@SuppressWarnings("RestrictedApi")
Nader Jawadd02c0972019-08-27 15:01:07 -0700309private fun obtainBrushFromComplexColor(complexColor: ComplexColorCompat): Brush? =
Aurimas Liutikas7a828d32019-10-07 17:16:05 -0700310 if (complexColor.willDraw()) {
311 val shader = complexColor.shader
312 if (shader != null) {
313 ShaderBrush(Shader(shader))
314 } else {
315 SolidColor(Color(complexColor.color))
316 }
317 } else {
318 null
319 }
Nader Jawadd02c0972019-08-27 15:01:07 -0700320
321@SuppressWarnings("RestrictedApi")
Nader Jawadb1030d42019-05-07 15:33:13 -0700322internal fun XmlPullParser.parseGroup(
323 res: Resources,
324 theme: Resources.Theme?,
325 attrs: AttributeSet,
326 builder: VectorAssetBuilder
327) {
328 val a = TypedArrayUtils.obtainAttributes(
329 res,
330 theme,
331 attrs,
332 AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_GROUP
333 )
334
335 // Account for any configuration changes.
336 // mChangingConfigurations |= Utils.getChangingConfigurations(a);
337
338 // Extract the theme attributes, if any.
339 // mThemeAttrs = null // TODO TINT THEME Not supported yet a.extractThemeAttrs();
340
341 // This is added in API 11
342 val rotate = TypedArrayUtils.getNamedFloat(
343 a,
344 this,
345 "rotation",
346 AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_GROUP_ROTATION,
347 DefaultRotation
348 )
349
350 val pivotX = a.getFloat(
351 AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_GROUP_PIVOT_X,
352 DefaultPivotX
353 )
354 val pivotY = a.getFloat(
355 AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_GROUP_PIVOT_Y,
356 DefaultPivotY
357 )
358
359 // This is added in API 11
360 val scaleX = TypedArrayUtils.getNamedFloat(
361 a,
362 this,
363 "scaleX",
364 AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_GROUP_SCALE_X,
365 DefaultScaleX
366 )
367
368 // This is added in API 11
369 val scaleY = TypedArrayUtils.getNamedFloat(
370 a,
371 this,
372 "scaleY",
373 AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_GROUP_SCALE_Y,
374 DefaultScaleY
375 )
376
377 val translateX = TypedArrayUtils.getNamedFloat(
378 a,
379 this,
380 "translationX",
381 AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_GROUP_TRANSLATE_X,
382 DefaultTranslationX
383 )
384 val translateY = TypedArrayUtils.getNamedFloat(
385 a,
386 this,
387 "translationY",
388 AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_GROUP_TRANSLATE_Y,
389 DefaultTranslationY
390 )
391
392 val name: String =
393 a.getString(AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_GROUP_NAME) ?: ""
394
395 // TODO parse clip path
396 val clipPathData = EmptyPath
397
398 a.recycle()
399
400 builder.pushGroup(
401 name,
402 rotate,
Louis Pullen-Freilich78e7cd72020-01-09 14:54:01 +0000403 pivotX,
404 pivotY,
Nader Jawadb1030d42019-05-07 15:33:13 -0700405 scaleX,
406 scaleY,
407 translateX,
408 translateY,
Nader Jawadb1030d42019-05-07 15:33:13 -0700409 clipPathData
410 )
411}