package com.alibaba.weex.svg.component; import android.content.Context; import android.graphics.Canvas; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Path; import android.graphics.RectF; import android.support.annotation.NonNull; import android.text.TextUtils; import android.util.Log; import com.alibaba.weex.svg.ISvgDrawable; import com.alibaba.weex.svg.SvgBrush; import com.alibaba.weex.svg.view.WXSvgView; import com.taobao.weex.WXSDKInstance; import com.taobao.weex.common.Constants; import com.taobao.weex.dom.WXDomObject; import com.taobao.weex.ui.component.WXComponent; import com.taobao.weex.ui.component.WXComponentProp; import com.taobao.weex.ui.component.WXVContainer; import com.taobao.weex.utils.WXViewUtils; import java.util.HashMap; import java.util.Map; /** * Created by budao on 2017/2/21. */ public class WXSvgContainer extends WXVContainer<WXSvgView> implements ISvgDrawable { private static final Map<String, WXSvgAbsComponent> mDefinedClipPaths = new HashMap<>(); private static final Map<String, WXSvgAbsComponent> mDefinedTemplates = new HashMap<>(); private static final Map<String, SvgBrush> mDefinedBrushes = new HashMap<>(); private int mWidth; private int mHeight; private int mViewBoxX = 0; private int mViewBoxY = 0; private int mviewBoxWidth = 0; private int mviewBoxHeight = 0; private WXDomObject mDom; private float mScale; public WXSvgContainer(WXSDKInstance instance, WXDomObject dom, WXVContainer parent) { super(instance, dom, parent); mDom = dom; Log.v("WXSvgContainer", mDom.getAttrs().values().toString()); if (mDom.getStyles().get(Constants.Name.HEIGHT) == null) { mDom.getStyles().put(Constants.Name.HEIGHT, "0"); } if (mDom.getStyles().get(Constants.Name.WIDTH) == null) { mDom.getStyles().put(Constants.Name.WIDTH, "0"); } mScale = (float) (WXViewUtils.getScreenWidth(parent.getContext()) * 1.0 / dom.getViewPortWidth()); } @Override protected WXSvgView initComponentHostView(@NonNull Context context) { WXSvgView host = new WXSvgView(context); host.setWillNotDraw(false); host.setSvgDrawable(this); host.setTag(getDomObject().getRef()); return host; } @WXComponentProp(name = Constants.Name.WIDTH) public void setWidth(int width) { mWidth = width; getHostView().getLayoutParams().width = (int) WXViewUtils.getRealPxByWidth(width, 750); } @WXComponentProp(name = Constants.Name.HEIGHT) public void setHeight(int height) { mHeight = height; getHostView().getLayoutParams().height = (int) WXViewUtils.getRealPxByWidth(height, 750); } @WXComponentProp(name = "viewBox") public void setViewBox(String viewBox) { String box = viewBox; Log.v("WXSvgContainer", "box is " + box); if (!TextUtils.isEmpty(box)) { String[] p = box.split(" "); if (p != null && p.length == 4) { mViewBoxX = Integer.valueOf(p[0]); mViewBoxY = Integer.valueOf(p[1]); mviewBoxWidth = Integer.valueOf(p[2]); mviewBoxHeight = Integer.valueOf(p[3]); } } } private void drawElements(Canvas canvas) { Paint paint = new Paint(); processChildren(canvas, paint); } public synchronized void processChildren(Canvas canvas, Paint paint) { for (int i = 0; i < getChildCount(); i++) { if (!(getChild(i) instanceof ISvgDrawable)) { continue; } ISvgDrawable child = (ISvgDrawable) getChild(i); child.draw(canvas, paint, 1f); } } @Override protected void onFinishLayout() { super.onFinishLayout(); //getHostView().invalidate(); } protected Path getPath(Canvas canvas, Paint paint) { return null; } public void defineClipPath(WXSvgAbsComponent clipPath, String clipPathRef) { mDefinedClipPaths.put(clipPathRef, clipPath); } public WXSvgAbsComponent getDefinedClipPath(String clipPathRef) { return mDefinedClipPaths.get(clipPathRef); } public void defineTemplate(WXSvgAbsComponent template, String templateRef) { mDefinedTemplates.put(templateRef, template); } public WXSvgAbsComponent getDefinedTemplate(String templateRef) { return mDefinedTemplates.get(templateRef); } public void defineBrush(SvgBrush brush, String brushRef) { mDefinedBrushes.put(brushRef, brush); } public SvgBrush getDefinedBrush(String brushRef) { return mDefinedBrushes.get(brushRef); } @Override public void draw(Canvas canvas, Paint paint, float opacity) { if (mviewBoxHeight == 0 || mviewBoxWidth == 0) { mviewBoxHeight = mHeight; mviewBoxWidth = mWidth; } RectF vbRect = new RectF(mViewBoxX * mScale, mViewBoxY * mScale, (mViewBoxX + mviewBoxWidth) * mScale, (mViewBoxY + mviewBoxHeight) * mScale); RectF eRect = new RectF(0, 0, mWidth * mScale, mHeight * mScale); Matrix mViewBoxMatrix = getTransform(vbRect, eRect, "none", MOS_NONE, false); canvas.concat(mViewBoxMatrix); drawElements(canvas); } private static final int MOS_MEET = 0; private static final int MOS_SLICE = 1; private static final int MOS_NONE = 2; private static Matrix getTransform(RectF vbRect, RectF eRect, String align, int meetOrSlice, boolean fromSymbol) { // based on https://svgwg.org/svg2-draft/coords.html#ComputingAViewportsTransform // Let vb-x, vb-y, vb-width, vb-height be the min-x, min-y, width and height values of the viewBox attribute respectively. float vbX = vbRect.left; float vbY = vbRect.top; float vbWidth = vbRect.width(); float vbHeight = vbRect.height(); // Let e-x, e-y, e-width, e-height be the position and size of the element respectively. float eX = eRect.left; float eY = eRect.top; float eWidth = eRect.width(); float eHeight = eRect.height(); // Initialize scale-x to e-width/vb-width. float scaleX = eWidth / vbWidth; // Initialize scale-y to e-height/vb-height. float scaleY = eHeight / vbHeight; // Initialize translate-x to vb-x - e-x. // Initialize translate-y to vb-y - e-y. float translateX = vbX - eX; float translateY = vbY - eY; // If align is 'none' if (meetOrSlice == MOS_NONE) { // Let scale be set the smaller value of scale-x and scale-y. // Assign scale-x and scale-y to scale. float scale = scaleX = scaleY = Math.min(scaleX, scaleY); // If scale is greater than 1 if (scale > 1) { // Minus translateX by (eWidth / scale - vbWidth) / 2 // Minus translateY by (eHeight / scale - vbHeight) / 2 translateX -= (eWidth / scale - vbWidth) / 2; translateY -= (eHeight / scale - vbHeight) / 2; } else { translateX -= (eWidth - vbWidth * scale) / 2; translateY -= (eHeight - vbHeight * scale) / 2; } } else { // If align is not 'none' and meetOrSlice is 'meet', set the larger of scale-x and scale-y to the smaller. // Otherwise, if align is not 'none' and meetOrSlice is 'slice', set the smaller of scale-x and scale-y to the larger. if (!align.equals("none") && meetOrSlice == MOS_MEET) { scaleX = scaleY = Math.min(scaleX, scaleY); } else if (!align.equals("none") && meetOrSlice == MOS_SLICE) { scaleX = scaleY = Math.max(scaleX, scaleY); } // If align contains 'xMid', minus (e-width / scale-x - vb-width) / 2 from transform-x. if (align.contains("xMid")) { translateX -= (eWidth / scaleX - vbWidth) / 2; } // If align contains 'xMax', minus (e-width / scale-x - vb-width) from transform-x. if (align.contains("xMax")) { translateX -= eWidth / scaleX - vbWidth; } // If align contains 'yMid', minus (e-height / scale-y - vb-height) / 2 from transform-y. if (align.contains("yMid")) { translateY -= (eHeight / scaleY - vbHeight) / 2; } // If align contains 'yMax', minus (e-height / scale-y - vb-height) from transform-y. if (align.contains("yMax")) { translateY -= eHeight / scaleY - vbHeight; } } // The transform applied to content contained by the element is given by // translate(translate-x, translate-y) scale(scale-x, scale-y). Matrix transform = new Matrix(); transform.postTranslate(-translateX * (fromSymbol ? scaleX : 1), -translateY * (fromSymbol ? scaleY : 1)); transform.postScale(scaleX, scaleY); return transform; } @Override protected boolean setProperty(String key, Object param) { switch (key) { case Constants.Name.WIDTH: case Constants.Name.HEIGHT: if (mDom != null) { mDom.getStyles().put(key, param.toString()); } return false; default: return super.setProperty(key, param); } } @Override public void updateProperties(Map<String, Object> props) { super.updateProperties(props); invalidate(); } public void addChild(WXComponent child, int index) { super.addChild(child, index); //invalidate(); } // private Bitmap drawBitmap() { // WXSvgView host = getHostView(); // if (host != null) { // int width = host.getWidth(); // int height = host.getHeight(); // Log.v("WXSvgAbsComponent", "width is " + width + ", height is " + height); // Bitmap bitmap = Bitmap.createBitmap( // Math.max(1, width), // Math.max(1, height), // Bitmap.Config.ARGB_8888); // Canvas canvas = new Canvas(bitmap); // Paint paint = new Paint(); // processChildren(canvas, paint); // return bitmap; // } // return null; // } public void invalidate() { getHostView().setWillNotDraw(false); getHostView().postInvalidate(); Log.v("WXSvgAbsComponent", "invalidate " + getRef()); } }