[go: nahoru, domu]

blob: 5cef7abd97828596e8cff8de3bf2a9209f2df238 [file] [log] [blame]
/*
* Copyright 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.ui.graphics.vector.compat
import android.content.res.Resources
import android.util.AttributeSet
import androidx.core.content.res.ComplexColorCompat
import androidx.core.content.res.TypedArrayUtils
import androidx.ui.core.Px
import androidx.ui.graphics.vector.VectorAssetBuilder
import androidx.ui.graphics.Brush
import androidx.ui.graphics.Color
import androidx.ui.graphics.ShaderBrush
import androidx.ui.graphics.SolidColor
import androidx.ui.graphics.Shader
import androidx.ui.graphics.StrokeCap
import androidx.ui.graphics.StrokeJoin
import androidx.ui.graphics.vector.addPathNodes
import androidx.ui.graphics.vector.DefaultPivotX
import androidx.ui.graphics.vector.DefaultPivotY
import androidx.ui.graphics.vector.DefaultRotation
import androidx.ui.graphics.vector.DefaultScaleX
import androidx.ui.graphics.vector.DefaultScaleY
import androidx.ui.graphics.vector.DefaultTranslationX
import androidx.ui.graphics.vector.DefaultTranslationY
import androidx.ui.graphics.vector.EmptyPath
import androidx.ui.graphics.vector.PathNode
import org.xmlpull.v1.XmlPullParser
import org.xmlpull.v1.XmlPullParserException
private val LINECAP_BUTT = 0
private val LINECAP_ROUND = 1
private val LINECAP_SQUARE = 2
private val LINEJOIN_MITER = 0
private val LINEJOIN_ROUND = 1
private val LINEJOIN_BEVEL = 2
private val FILL_TYPE_WINDING = 0
private val SHAPE_CLIP_PATH = "clip-VPath"
private val SHAPE_GROUP = "group"
private val SHAPE_PATH = "path"
private fun getStrokeLineCap(id: Int, defValue: StrokeCap = StrokeCap.butt): StrokeCap =
when (id) {
LINECAP_BUTT -> StrokeCap.butt
LINECAP_ROUND -> StrokeCap.round
LINECAP_SQUARE -> StrokeCap.square
else -> defValue
}
private fun getStrokeLineJoin(id: Int, defValue: StrokeJoin = StrokeJoin.miter): StrokeJoin =
when (id) {
LINEJOIN_MITER -> StrokeJoin.miter
LINEJOIN_ROUND -> StrokeJoin.round
LINEJOIN_BEVEL -> StrokeJoin.bevel
else -> defValue
}
internal fun XmlPullParser.isAtEnd(): Boolean =
eventType == XmlPullParser.END_DOCUMENT ||
(depth < 1 && eventType == XmlPullParser.END_TAG)
internal fun XmlPullParser.parseCurrentVectorNode(
res: Resources,
attrs: AttributeSet,
theme: Resources.Theme? = null,
builder: VectorAssetBuilder
) {
when (eventType) {
XmlPullParser.START_TAG -> {
when (name) {
SHAPE_PATH -> {
parsePath(res, theme, attrs, builder)
}
SHAPE_CLIP_PATH -> {
// TODO parse clipping paths
}
SHAPE_GROUP -> {
parseGroup(res, theme, attrs, builder)
}
}
}
XmlPullParser.END_TAG -> {
if (SHAPE_GROUP == name) {
builder.popGroup()
}
}
}
}
/**
* Helper method to seek to the first tag within the VectorDrawable xml asset
*/
@Throws(XmlPullParserException::class)
internal fun XmlPullParser.seekToStartTag(): XmlPullParser {
var type = next()
while (type != XmlPullParser.START_TAG && type != XmlPullParser.END_DOCUMENT) {
// Empty loop
type = next()
}
if (type != XmlPullParser.START_TAG) {
throw XmlPullParserException("No start tag found")
}
return this
}
@SuppressWarnings("RestrictedApi")
internal fun XmlPullParser.createVectorImageBuilder(
res: Resources,
theme: Resources.Theme?,
attrs: AttributeSet
): VectorAssetBuilder {
val vectorAttrs = TypedArrayUtils.obtainAttributes(
res,
theme,
attrs,
AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_TYPE_ARRAY
)
// TODO (njawad) handle mirroring here
// state.mAutoMirrored = TypedArrayUtils.getNamedBoolean(a, parser, "autoMirrored",
// AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_AUTO_MIRRORED, state.mAutoMirrored)
val viewportWidth = TypedArrayUtils.getNamedFloat(
vectorAttrs,
this,
"viewportWidth",
AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_VIEWPORT_WIDTH,
0.0f
)
val viewportHeight = TypedArrayUtils.getNamedFloat(
vectorAttrs,
this,
"viewportHeight",
AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_VIEWPORT_HEIGHT,
0.0f
)
if (viewportWidth <= 0) {
throw XmlPullParserException(
vectorAttrs.getPositionDescription() +
"<VectorGraphic> tag requires viewportWidth > 0"
)
} else if (viewportHeight <= 0) {
throw XmlPullParserException(
vectorAttrs.getPositionDescription() +
"<VectorGraphic> tag requires viewportHeight > 0"
)
}
val defaultWidth = vectorAttrs.getDimension(
AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_WIDTH, 0.0f
)
val defaultHeight = vectorAttrs.getDimension(
AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_HEIGHT, 0.0f
)
vectorAttrs.recycle()
return VectorAssetBuilder(
defaultWidth = Px(defaultWidth),
defaultHeight = Px(defaultHeight),
viewportWidth = viewportWidth,
viewportHeight = viewportHeight
)
}
@Throws(IllegalArgumentException::class)
@SuppressWarnings("RestrictedApi")
internal fun XmlPullParser.parsePath(
res: Resources,
theme: Resources.Theme?,
attrs: AttributeSet,
builder: VectorAssetBuilder
) {
val a = TypedArrayUtils.obtainAttributes(
res,
theme,
attrs,
AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH
)
val hasPathData = TypedArrayUtils.hasAttribute(this, "pathData")
if (!hasPathData) {
// If there is no pathData in the VPath tag, then this is an empty VPath,
// nothing need to be drawn.
throw IllegalArgumentException("No path data available")
}
val name: String = a.getString(AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_NAME) ?: ""
val pathStr = a.getString(AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_PATH_DATA)
val pathData: Array<PathNode> = addPathNodes(pathStr)
val fillColor = TypedArrayUtils.getNamedComplexColor(
a,
this,
theme,
"fillColor",
AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_FILL_COLOR, 0
)
val fillAlpha = TypedArrayUtils.getNamedFloat(
a,
this,
"fillAlpha",
AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_FILL_ALPHA, 1.0f
)
val lineCap = TypedArrayUtils.getNamedInt(
a,
this,
"strokeLineCap",
AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_STROKE_LINE_CAP, -1
)
val strokeLineCap = getStrokeLineCap(lineCap, StrokeCap.butt)
val lineJoin = TypedArrayUtils.getNamedInt(
a,
this,
"strokeLineJoin",
AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_STROKE_LINE_JOIN, -1
)
val strokeLineJoin =
getStrokeLineJoin(lineJoin, StrokeJoin.bevel)
val strokeMiterLimit = TypedArrayUtils.getNamedFloat(
a,
this,
"strokeMiterLimit",
AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_STROKE_MITER_LIMIT,
1.0f
)
val strokeColor = TypedArrayUtils.getNamedComplexColor(
a,
this,
theme,
"strokeColor",
AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_STROKE_COLOR, 0
)
val strokeAlpha = TypedArrayUtils.getNamedFloat(
a,
this,
"strokeAlpha",
AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_STROKE_ALPHA, 1.0f
)
val strokeLineWidth = TypedArrayUtils.getNamedFloat(
a,
this,
"strokeWidth",
AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_STROKE_WIDTH, 1.0f
)
// TODO (njawad) handle trim paths + fill rule
// val trimPathEnd = TypedArrayUtils.getNamedFloat(
// a, this, "trimPathEnd",
// AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_TRIM_PATH_END, 1.0f
// )
// val trimPathOffset = TypedArrayUtils.getNamedFloat(
// a, this, "trimPathOffset",
// AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_TRIM_PATH_OFFSET,
// 0.0f
// )
// val trimPathStart = TypedArrayUtils.getNamedFloat(
// a, this, "trimPathStart",
// AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_TRIM_PATH_START,
// 0.0f
// )
// val fillRule = TypedArrayUtils.getNamedInt(
// a, this, "fillType",
// AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_TRIM_PATH_FILLTYPE,
// FILL_TYPE_WINDING
// )
a.recycle()
val fillBrush = obtainBrushFromComplexColor(fillColor)
val strokeBrush = obtainBrushFromComplexColor(strokeColor)
builder.addPath(
pathData,
name,
fillBrush,
fillAlpha,
strokeBrush,
strokeAlpha,
strokeLineWidth,
strokeLineCap,
strokeLineJoin,
strokeMiterLimit)
}
@SuppressWarnings("RestrictedApi")
private fun obtainBrushFromComplexColor(complexColor: ComplexColorCompat): Brush? =
if (complexColor.willDraw()) {
val shader = complexColor.shader
if (shader != null) {
ShaderBrush(Shader(shader))
} else {
SolidColor(Color(complexColor.color))
}
} else {
null
}
@SuppressWarnings("RestrictedApi")
internal fun XmlPullParser.parseGroup(
res: Resources,
theme: Resources.Theme?,
attrs: AttributeSet,
builder: VectorAssetBuilder
) {
val a = TypedArrayUtils.obtainAttributes(
res,
theme,
attrs,
AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_GROUP
)
// Account for any configuration changes.
// mChangingConfigurations |= Utils.getChangingConfigurations(a);
// Extract the theme attributes, if any.
// mThemeAttrs = null // TODO TINT THEME Not supported yet a.extractThemeAttrs();
// This is added in API 11
val rotate = TypedArrayUtils.getNamedFloat(
a,
this,
"rotation",
AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_GROUP_ROTATION,
DefaultRotation
)
val pivotX = a.getFloat(
AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_GROUP_PIVOT_X,
DefaultPivotX
)
val pivotY = a.getFloat(
AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_GROUP_PIVOT_Y,
DefaultPivotY
)
// This is added in API 11
val scaleX = TypedArrayUtils.getNamedFloat(
a,
this,
"scaleX",
AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_GROUP_SCALE_X,
DefaultScaleX
)
// This is added in API 11
val scaleY = TypedArrayUtils.getNamedFloat(
a,
this,
"scaleY",
AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_GROUP_SCALE_Y,
DefaultScaleY
)
val translateX = TypedArrayUtils.getNamedFloat(
a,
this,
"translationX",
AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_GROUP_TRANSLATE_X,
DefaultTranslationX
)
val translateY = TypedArrayUtils.getNamedFloat(
a,
this,
"translationY",
AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_GROUP_TRANSLATE_Y,
DefaultTranslationY
)
val name: String =
a.getString(AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_GROUP_NAME) ?: ""
// TODO parse clip path
val clipPathData = EmptyPath
a.recycle()
builder.pushGroup(
name,
rotate,
scaleX,
scaleY,
translateX,
translateY,
pivotX,
pivotY,
clipPathData
)
}