package com.marshalchen.common.uimodule.shapeimageview.shader; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Path; import android.util.AttributeSet; import com.marshalchen.common.uimodule.R; import com.marshalchen.common.uimodule.shapeimageview.path.SvgUtil; import com.marshalchen.common.uimodule.shapeimageview.path.parser.PathInfo; @SuppressWarnings("WeakerAccess") public class SvgShader extends ShaderHelper { public static final int BORDER_TYPE_DEFAULT = 0; public static final int BORDER_TYPE_FILL = 1; public static final int STROKE_CAP_DEFAULT = -1; public static final int STROKE_CAP_BUTT = 0; public static final int STROKE_CAP_ROUND = 1; public static final int STROKE_CAP_SQUARE = 2; public static final int STROKE_JOIN_DEFAULT = -1; public static final int STROKE_JOIN_BEVEL = 0; public static final int STROKE_JOIN_MITER = 1; public static final int STROKE_JOIN_ROUND = 2; private final Path path = new Path(); private final Path borderPath = new Path(); private final Matrix pathMatrix = new Matrix(); private final float[] pathDimensions = new float[2]; private PathInfo shapePath; private int resId = -1; private int borderType = BORDER_TYPE_DEFAULT; private int strokeCap = STROKE_CAP_DEFAULT; private int strokeJoin = STROKE_JOIN_DEFAULT; private int strokeMiter = 0; public SvgShader() { } public SvgShader(int resId) { this.resId = resId; } public SvgShader(int resId, int borderType) { this.resId = resId; this.borderType = borderType; } @Override public void init(Context context, AttributeSet attrs, int defStyle) { super.init(context, attrs, defStyle); if (attrs != null) { TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ShaderImageView, defStyle, 0); resId = typedArray.getResourceId(R.styleable.ShaderImageView_ShaderImageViewShape, resId); borderType = typedArray.getInt(R.styleable.ShaderImageView_borderType, borderType); strokeCap = typedArray.getInt(R.styleable.ShaderImageView_strokeCap, strokeCap); strokeJoin = typedArray.getInt(R.styleable.ShaderImageView_strokeJoin, strokeJoin); strokeMiter = typedArray.getDimensionPixelSize(R.styleable.ShaderImageView_strokeMiter, strokeMiter); typedArray.recycle(); } if (resId != -1) { shapePath = SvgUtil.readSvg(context, resId); } else { throw new RuntimeException("No resource is defined as shape"); } setBorderType(borderType); setStrokeCap(strokeCap); setStrokeJoin(strokeJoin); setStrokeMiter(strokeMiter); } public void setStrokeMiter(int strokeMiter) { this.strokeMiter = strokeMiter; if (strokeMiter > 0) { borderPaint.setStrokeMiter(strokeMiter); } } public void setStrokeCap(int strokeCap) { this.strokeCap = strokeCap; switch (strokeCap) { case STROKE_CAP_BUTT: borderPaint.setStrokeCap(Paint.Cap.BUTT); break; case STROKE_CAP_ROUND: borderPaint.setStrokeCap(Paint.Cap.ROUND); break; case STROKE_CAP_SQUARE: borderPaint.setStrokeCap(Paint.Cap.SQUARE); break; case STROKE_CAP_DEFAULT: default: break; } } public void setStrokeJoin(int strokeJoin) { this.strokeJoin = strokeJoin; switch (strokeJoin) { case STROKE_JOIN_BEVEL: borderPaint.setStrokeJoin(Paint.Join.BEVEL); break; case STROKE_JOIN_MITER: borderPaint.setStrokeJoin(Paint.Join.MITER); break; case STROKE_JOIN_ROUND: borderPaint.setStrokeJoin(Paint.Join.ROUND); break; case STROKE_CAP_DEFAULT: default: break; } } public void setBorderType(int borderType) { this.borderType = borderType; switch (borderType) { case BORDER_TYPE_FILL: borderPaint.setStyle(Paint.Style.FILL); break; case BORDER_TYPE_DEFAULT: default: borderPaint.setStyle(Paint.Style.STROKE); break; } } @Override public void draw(Canvas canvas, Paint imagePaint, Paint borderPaint) { canvas.save(); canvas.drawPath(borderPath, borderPaint); canvas.concat(matrix); canvas.drawPath(path, imagePaint); canvas.restore(); } @Override public void calculate(int bitmapWidth, int bitmapHeight, float width, float height, float scale, float translateX, float translateY) { path.reset(); borderPath.reset(); pathDimensions[0] = shapePath.getWidth(); pathDimensions[1] = shapePath.getHeight(); pathMatrix.reset(); scale = Math.min(width / pathDimensions[0], height / pathDimensions[1]); translateX = Math.round((width - pathDimensions[0] * scale) * 0.5f); translateY = Math.round((height - pathDimensions[1] * scale) * 0.5f); pathMatrix.setScale(scale, scale); pathMatrix.postTranslate(translateX, translateY); shapePath.transform(pathMatrix, path); path.offset(borderWidth, borderWidth); if (borderWidth > 0) { pathMatrix.reset(); float newWidth; float newHeight; float d; if (borderType == BORDER_TYPE_DEFAULT) { newWidth = viewWidth - borderWidth; newHeight = viewHeight - borderWidth; d = borderWidth / 2f; } else { newWidth = viewWidth; newHeight = viewHeight; d = 0; } scale = Math.min(newWidth / pathDimensions[0], newHeight / pathDimensions[1]); translateX = Math.round((newWidth - pathDimensions[0] * scale) * 0.5f + d); translateY = Math.round((newHeight - pathDimensions[1] * scale) * 0.5f + d); pathMatrix.setScale(scale, scale); pathMatrix.postTranslate(translateX, translateY); shapePath.transform(pathMatrix, borderPath); // borderPath.op(path, Path.Op.DIFFERENCE); } pathMatrix.reset(); matrix.invert(pathMatrix); path.transform(pathMatrix); } @Override public void reset() { path.reset(); borderPath.reset(); } }