/******************************************************************************* * Copyright 2012-present Pixate, Inc. * * 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. ******************************************************************************/ /** * Copyright (c) 2012-2013 Pixate, Inc. All rights reserved. */ package com.pixate.freestyle.cg.strokes; import java.util.Arrays; import android.graphics.Canvas; import android.graphics.DashPathEffect; import android.graphics.Paint; import android.graphics.Paint.Cap; import android.graphics.Paint.Join; import android.graphics.Paint.Style; import android.graphics.Path; import com.pixate.freestyle.cg.paints.PXPaint; import com.pixate.freestyle.util.ObjectPool; /** * A PX stroke representation. */ public class PXStroke implements PXStrokeRenderer { public enum PXStrokeType { CENTER, INNER, OUTER } protected PXStrokeType type; protected float width; protected PXPaint color; protected float[] dashArray; protected int dashOffset; protected Cap lineCap; protected Join lineJoin; protected float miterLimit; /** * Constructs a new PX stroke. */ public PXStroke() { this.type = PXStrokeType.CENTER; this.width = 1.0f; this.dashOffset = 0; this.lineCap = Cap.BUTT; this.lineJoin = Join.MITER; this.miterLimit = 4.0f; // What is a reasonable default here? } public PXStroke(float width) { this(); this.width = width; } /** * Returns a stroke {@link Paint}. Note that this method does not check into * the pool any Paint that may have been pulled from it. The responsibility * here is up to the caller. * * @param p * @param useOriginal Indicate that the given {@link Paint} instance should * be applied with the stroke information. * @return A {@link Paint} reference (a new Paint in case the useOriginal * was false) */ public Paint getStrokedPaint(Paint p, boolean useOriginal) { Paint paint = useOriginal ? p : ObjectPool.paintPool.checkOut(p); paint.setStyle(Style.STROKE); paint.setStrokeWidth(width); if (dashArray != null && dashArray.length > 0) { paint.setPathEffect(new DashPathEffect(dashArray, dashOffset)); } paint.setStrokeCap(this.lineCap); paint.setStrokeJoin(this.lineJoin); paint.setStrokeMiter(this.miterLimit); return paint; } public boolean isOpaque() { return color != null && color.isOpaque(); } public void applyStrokeToPath(Path path, Paint paint, Canvas context) { if (color != null && width > 0) { // stroke and possibly dash incoming path boolean paintCreated = false; if (paint == null) { paint = ObjectPool.paintPool.checkOut(); paintCreated = true; } Paint p = getStrokedPaint(paint, paintCreated); // set up masking for inner/outer/center stroke if (type == PXStrokeType.INNER) { context.save(); // clip to path context.clipPath(path); } else if (type == PXStrokeType.OUTER) { // TODO: } // else is center, so do nothing color.applyFillToPath(path, p, context); // reset environment if (type == PXStrokeType.INNER) { context.restore(); } else if (type == PXStrokeType.OUTER) { // TODO: just move condition into above test } // Check the paint back into the pool. Make sure that we check in // only Paint instances that were pulled from the pool in this // method. if (paintCreated) { ObjectPool.paintPool.checkIn(paint); } else { ObjectPool.paintPool.checkIn(p); } } } /** * @return the type */ public PXStrokeType getType() { return type; } /** * @param type the type to set */ public void setType(PXStrokeType type) { this.type = type; } /** * @return the color */ public PXPaint getColor() { return color; } /** * @param color the color to set */ public void setColor(PXPaint color) { this.color = color; } /** * @return the width */ public float getWidth() { return width; } /** * @param width the width to set */ public void setWidth(float width) { this.width = width; } /** * @return the dashArray */ public float[] getDashArray() { return dashArray; } /** * @param dashArray the dashArray to set */ public void setDashArray(float[] dashArray) { this.dashArray = dashArray; } /** * @return the dashOffset */ public int getDashOffset() { return dashOffset; } /** * @param dashOffset the dashOffset to set */ public void setDashOffset(int dashOffset) { this.dashOffset = dashOffset; } /** * @return the lineCap */ public Cap getLineCap() { return lineCap; } /** * @param lineCap the lineCap to set */ public void setLineCap(Cap lineCap) { this.lineCap = lineCap; } /** * @return the lineJoin */ public Join getLineJoin() { return lineJoin; } /** * @param lineJoin the lineJoin to set */ public void setLineJoin(Join lineJoin) { this.lineJoin = lineJoin; } /** * @return the miterLimit */ public float getMiterLimit() { return miterLimit; } /** * @param miterLimit the miterLimit to set */ public void setMiterLimit(float miterLimit) { this.miterLimit = miterLimit; } @Override public boolean equals(Object o) { if (o instanceof PXStroke) { PXStroke other = (PXStroke) o; // @formatter:off return (this.type == other.type) && (this.color == other.color) && (this.width == other.width) && Arrays.equals(this.dashArray ,other.dashArray) && (this.dashOffset == other.dashOffset) && (this.lineCap == other.lineCap) && (this.lineJoin == other.lineJoin) && (this.miterLimit == other.miterLimit); // @formatter:on } return false; } }