[go: nahoru, domu]

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