/*
* Copyright (C) 2016 Peng fei Pan <sky@xiaopan.me>
*
* 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 me.xiaopan.sketch.shaper;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import me.xiaopan.sketch.request.ShapeSize;
/**
* 圆角矩形的图片,还可以有描边
*/
@SuppressWarnings("unused")
public class RoundRectImageShaper implements ImageShaper {
private float[] outerRadii;
private Rect boundsCached = new Rect();
private Path bitmapPath = new Path();
private Path innerStrokePath;
private Path outerStrokePath;
private Path clipPath;
private int strokeWidth;
private int strokeColor;
private Paint strokePaint;
public RoundRectImageShaper(float[] radiis) {
if (radiis == null || radiis.length < 8) {
throw new ArrayIndexOutOfBoundsException("outer radii must have >= 8 values");
}
this.outerRadii = radiis;
}
public RoundRectImageShaper(float topLeftRadii, float topRightRadii, float bottomLeftRadii, float bottomRightRadii) {
this(new float[]{topLeftRadii, topLeftRadii, topRightRadii, topRightRadii, bottomLeftRadii, bottomLeftRadii, bottomRightRadii, bottomRightRadii});
}
public RoundRectImageShaper(float radii) {
this(radii, radii, radii, radii);
}
public float[] getOuterRadii() {
return outerRadii;
}
@SuppressWarnings("unused")
public int getStrokeColor() {
return strokeColor;
}
@SuppressWarnings("unused")
public RoundRectImageShaper setStroke(int strokeColor, int strokeWidth) {
this.strokeColor = strokeColor;
this.strokeWidth = strokeWidth;
updatePaint();
return this;
}
@SuppressWarnings("unused")
public int getStrokeWidth() {
return strokeWidth;
}
private void updatePaint() {
if (hasStroke()) {
if (strokePaint == null) {
strokePaint = new Paint();
strokePaint.setStyle(Paint.Style.STROKE);
strokePaint.setAntiAlias(true);
}
strokePaint.setColor(strokeColor);
strokePaint.setStrokeWidth(strokeWidth);
if (innerStrokePath == null) {
innerStrokePath = new Path();
}
if (outerStrokePath == null) {
outerStrokePath = new Path();
}
if (clipPath == null) {
clipPath = new Path();
}
}
}
private boolean hasStroke() {
return strokeColor != 0 && strokeWidth > 0;
}
@Override
public void onUpdateShaderMatrix(Matrix matrix, Rect bounds, int bitmapWidth, int bitmapHeight,
ShapeSize shapeSize, Rect srcRect) {
}
@Override
public void draw(Canvas canvas, Paint paint, Rect bounds) {
if (!boundsCached.equals(bounds)) {
RectF rectF = new RectF(bounds);
bitmapPath.reset();
bitmapPath.addRoundRect(rectF, outerRadii, Path.Direction.CW);
// 假如描边宽度是10,那么会是5个像素在图片外面,5个像素在图片里面
// 因为描边会有一半是在图片外面,所以如果图片被紧紧(没有缝隙)包括在Layout中,那么描边就会丢失一半
if (hasStroke()) {
// 内圈,往图片里面偏移描边宽度的一半,让描边都在图片里面,都在里面导致圆角部分会露出来一些
final float offset = strokeWidth / 2f;
rectF.set(bounds.left + offset, bounds.top + offset,
bounds.right - offset, bounds.bottom - offset);
innerStrokePath.reset();
innerStrokePath.addRoundRect(rectF, outerRadii, Path.Direction.CW);
// 外圈,主要用来盖住内圈描边无法覆盖导致露出的圆角部分
outerStrokePath.reset();
rectF.set(bounds.left, bounds.top, bounds.right, bounds.bottom);
outerStrokePath.addRoundRect(rectF, outerRadii, Path.Direction.CW);
rectF.set(bounds);
clipPath.addRoundRect(rectF, outerRadii, Path.Direction.CW);
}
}
paint.setAntiAlias(true);
canvas.drawPath(bitmapPath, paint);
if (hasStroke() && strokePaint != null) {
// 裁掉外圈跑出图片的部分
canvas.clipPath(clipPath);
canvas.drawPath(innerStrokePath, strokePaint);
canvas.drawPath(outerStrokePath, strokePaint);
}
}
}