/*
* Copyright (C) 2013 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.drawable;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.Shader;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.widget.ImageView;
import me.xiaopan.sketch.Sketch;
import me.xiaopan.sketch.feature.ResizeCalculator;
import me.xiaopan.sketch.request.ImageFrom;
import me.xiaopan.sketch.request.ShapeSize;
import me.xiaopan.sketch.shaper.ImageShaper;
/**
* 可以改变BitmapDrawable的形状和尺寸
* <p>
* fixedSize用来改变尺寸,如果bitmap的尺寸和fixedSize的比例不一致,那么就仅显示bitmap的中间部分(参考CENTER_CROP的效果)
* </p>
* <p>
* shapeImage用来改变形状
* </p>
*/
public class ShapeBitmapDrawable extends Drawable implements RefDrawable {
private static final int DEFAULT_PAINT_FLAGS = Paint.FILTER_BITMAP_FLAG | Paint.DITHER_FLAG;
private BitmapDrawable bitmapDrawable;
private ShapeSize shapeSize;
private ImageShaper imageShaper;
private Paint paint;
private Rect srcRect;
private BitmapShader bitmapShader;
private RefDrawable refDrawable;
private SketchDrawable sketchDrawable;
private ResizeCalculator resizeCalculator;
public ShapeBitmapDrawable(Context context, BitmapDrawable bitmapDrawable, ShapeSize shapeSize, ImageShaper imageShaper) {
Bitmap bitmap = bitmapDrawable.getBitmap();
if (bitmap == null || bitmap.isRecycled()) {
throw new IllegalArgumentException(bitmap == null ? "bitmap is null" : "bitmap recycled");
}
if (shapeSize == null && imageShaper == null) {
throw new IllegalArgumentException("shapeSize is null and shapeImage is null");
}
this.bitmapDrawable = bitmapDrawable;
this.paint = new Paint(DEFAULT_PAINT_FLAGS);
this.srcRect = new Rect();
this.resizeCalculator = Sketch.with(context).getConfiguration().getResizeCalculator();
setShapeSize(shapeSize);
setImageShaper(imageShaper);
if (bitmapDrawable instanceof RefDrawable) {
this.refDrawable = (RefDrawable) bitmapDrawable;
}
if (bitmapDrawable instanceof SketchDrawable) {
this.sketchDrawable = (SketchDrawable) bitmapDrawable;
}
if (bitmapDrawable instanceof RefBitmapDrawable) {
((RefBitmapDrawable) bitmapDrawable).setLogName("ShapeBitmapDrawable");
}
}
@SuppressWarnings("unused")
public ShapeBitmapDrawable(Context context, BitmapDrawable bitmapDrawable, ShapeSize shapeSize) {
this(context, bitmapDrawable, shapeSize, null);
}
@SuppressWarnings("unused")
public ShapeBitmapDrawable(Context context, BitmapDrawable bitmapDrawable, ImageShaper imageShaper) {
this(context, bitmapDrawable, null, imageShaper);
}
@Override
public void draw(@SuppressWarnings("NullableProblems") Canvas canvas) {
Rect bounds = getBounds();
Bitmap bitmap = bitmapDrawable.getBitmap();
if (bounds.isEmpty() || bitmap == null || bitmap.isRecycled()) {
return;
}
if (imageShaper != null && bitmapShader != null) {
imageShaper.draw(canvas, paint, bounds);
} else {
canvas.drawBitmap(bitmap, srcRect != null && !srcRect.isEmpty() ? srcRect : null, bounds, paint);
}
}
@Override
public int getIntrinsicWidth() {
return shapeSize != null ? shapeSize.getWidth() : bitmapDrawable.getIntrinsicWidth();
}
@Override
public int getIntrinsicHeight() {
return shapeSize != null ? shapeSize.getHeight() : bitmapDrawable.getIntrinsicHeight();
}
@Override
public int getAlpha() {
return paint.getAlpha();
}
@Override
public void setAlpha(int alpha) {
final int oldAlpha = paint.getAlpha();
if (alpha != oldAlpha) {
paint.setAlpha(alpha);
invalidateSelf();
}
}
@Override
public ColorFilter getColorFilter() {
return paint.getColorFilter();
}
@Override
public void setColorFilter(ColorFilter cf) {
paint.setColorFilter(cf);
invalidateSelf();
}
@Override
public void setDither(boolean dither) {
paint.setDither(dither);
invalidateSelf();
}
@Override
public void setFilterBitmap(boolean filter) {
paint.setFilterBitmap(filter);
invalidateSelf();
}
@Override
public int getOpacity() {
Bitmap bitmap = bitmapDrawable.getBitmap();
return (bitmap.hasAlpha() || paint.getAlpha() < 255) ? PixelFormat.TRANSLUCENT : PixelFormat.OPAQUE;
}
@Override
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
int boundsWidth = bounds.width();
int boundsHeight = bounds.height();
int bitmapWidth = bitmapDrawable.getBitmap().getWidth();
int bitmapHeight = bitmapDrawable.getBitmap().getHeight();
if (boundsWidth == 0 || boundsHeight == 0 || bitmapWidth == 0 || bitmapHeight == 0) {
srcRect.setEmpty();
} else if ((float) bitmapWidth / (float) bitmapHeight == (float) boundsWidth / (float) boundsHeight) {
srcRect.set(0, 0, bitmapWidth, bitmapHeight);
} else {
ImageView.ScaleType scaleType = shapeSize != null ? shapeSize.getScaleType() : ImageView.ScaleType.FIT_CENTER;
ResizeCalculator.Result result = resizeCalculator.calculator(bitmapWidth, bitmapHeight, boundsWidth, boundsHeight, scaleType, true);
srcRect.set(result.srcRect);
}
if (imageShaper != null && bitmapShader != null) {
float widthScale = (float) boundsWidth / bitmapWidth;
float heightScale = (float) boundsHeight / bitmapHeight;
// 缩放图片充满bounds
Matrix shaderMatrix = new Matrix();
float scale = Math.max(widthScale, heightScale);
shaderMatrix.postScale(scale, scale);
// 显示图片中间部分
if (srcRect != null && !srcRect.isEmpty()) {
shaderMatrix.postTranslate(-srcRect.left * scale, -srcRect.top * scale);
}
imageShaper.onUpdateShaderMatrix(shaderMatrix, bounds, bitmapWidth, bitmapHeight, shapeSize, srcRect);
bitmapShader.setLocalMatrix(shaderMatrix);
paint.setShader(bitmapShader);
}
}
@SuppressWarnings("unused")
public BitmapDrawable getBitmapDrawable() {
return bitmapDrawable;
}
@SuppressWarnings("unused")
public ShapeSize getShapeSize() {
return shapeSize;
}
public void setShapeSize(ShapeSize shapeSize) {
this.shapeSize = shapeSize;
invalidateSelf();
}
@SuppressWarnings("unused")
public ImageShaper getImageShaper() {
return imageShaper;
}
public void setImageShaper(ImageShaper imageShaper) {
this.imageShaper = imageShaper;
if (this.imageShaper != null) {
if (bitmapShader == null) {
bitmapShader = new BitmapShader(bitmapDrawable.getBitmap(), Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
paint.setShader(bitmapShader);
}
} else {
if (bitmapShader != null) {
bitmapShader = null;
paint.setShader(null);
}
}
invalidateSelf();
}
@Override
public String getKey() {
return sketchDrawable != null ? sketchDrawable.getKey() : null;
}
@Override
public String getUri() {
return sketchDrawable != null ? sketchDrawable.getUri() : null;
}
@Override
public int getOriginWidth() {
return sketchDrawable != null ? sketchDrawable.getOriginWidth() : 0;
}
@Override
public int getOriginHeight() {
return sketchDrawable != null ? sketchDrawable.getOriginHeight() : 0;
}
@Override
public String getMimeType() {
return sketchDrawable != null ? sketchDrawable.getMimeType() : null;
}
@Override
public int getOrientation() {
return sketchDrawable != null ? sketchDrawable.getOrientation() : 0;
}
@Override
public int getByteCount() {
return sketchDrawable != null ? sketchDrawable.getByteCount() : 0;
}
@Override
public Bitmap.Config getBitmapConfig() {
return sketchDrawable != null ? sketchDrawable.getBitmapConfig() : null;
}
@Override
public ImageFrom getImageFrom() {
return sketchDrawable != null ? sketchDrawable.getImageFrom() : null;
}
@Override
public void setImageFrom(ImageFrom imageFrom) {
if (sketchDrawable != null) {
sketchDrawable.setImageFrom(imageFrom);
}
}
@Override
public String getInfo() {
return sketchDrawable != null ? sketchDrawable.getInfo() : null;
}
@Override
public void setIsDisplayed(String callingStation, boolean displayed) {
if (refDrawable != null) {
refDrawable.setIsDisplayed(callingStation, displayed);
}
}
@Override
public void setIsCached(String callingStation, boolean cached) {
if (refDrawable != null) {
refDrawable.setIsCached(callingStation, cached);
}
}
@Override
public void setIsWaitingUse(String callingStation, boolean waitingUse) {
if (refDrawable != null) {
refDrawable.setIsWaitingUse(callingStation, waitingUse);
}
}
@Override
public boolean isRecycled() {
return refDrawable == null || refDrawable.isRecycled();
}
}