/* Copyright (C) 2013 Make Ramen, LLC */ package com.makeramen.rounded; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; 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.RectF; import android.graphics.Shader; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.TransitionDrawable; import android.util.Log; import android.widget.ImageView.ScaleType; public class RoundedDrawable extends Drawable { public static final String TAG = "RoundedDrawable"; private final RectF mBounds = new RectF(); private final RectF mDrawableRect = new RectF(); private float mCornerRadius; private final RectF mBitmapRect = new RectF(); private final BitmapShader mBitmapShader; private final Paint mBitmapPaint; private final int mBitmapWidth; private final int mBitmapHeight; private final RectF mBorderRect = new RectF(); private final Paint mBorderPaint; private int mBorderWidth; private int mBorderColor; private ScaleType mScaleType = ScaleType.FIT_XY; private final Matrix mShaderMatrix = new Matrix(); RoundedDrawable(Bitmap bitmap, float cornerRadius, int border, int borderColor) { mBorderWidth = border; mBorderColor = borderColor; mBitmapWidth = bitmap.getWidth(); mBitmapHeight = bitmap.getHeight(); mBitmapRect.set(0, 0, mBitmapWidth, mBitmapHeight); mCornerRadius = cornerRadius; mBitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); mBitmapShader.setLocalMatrix(mShaderMatrix); mBitmapPaint = new Paint(); mBitmapPaint.setAntiAlias(true); mBitmapPaint.setShader(mBitmapShader); mBorderPaint = new Paint(); mBorderPaint.setAntiAlias(true); mBorderPaint.setColor(mBorderColor); mBorderPaint.setStrokeWidth(border); } protected void setScaleType(ScaleType scaleType) { if (scaleType == null) { scaleType = ScaleType.FIT_XY; } if (mScaleType != scaleType) { mScaleType = scaleType; setMatrix(); } } protected ScaleType getScaleType() { return mScaleType; } private void setMatrix() { mBorderRect.set(mBounds); mDrawableRect.set(0 + mBorderWidth, 0 + mBorderWidth, mBorderRect.width() - mBorderWidth, mBorderRect.height() - mBorderWidth); float scale; float dx; float dy; switch(mScaleType) { case CENTER: // Log.d(TAG, "CENTER"); mBorderRect.set(mBounds); mDrawableRect.set(0 + mBorderWidth, 0 + mBorderWidth, mBorderRect.width() - mBorderWidth, mBorderRect.height() - mBorderWidth); mShaderMatrix.set(null); mShaderMatrix.setTranslate((int) ((mDrawableRect.width() - mBitmapWidth) * 0.5f + 0.5f), (int) ((mDrawableRect.height() - mBitmapHeight) * 0.5f + 0.5f)); break; case CENTER_CROP: // Log.d(TAG, "CENTER_CROP"); mBorderRect.set(mBounds); mDrawableRect.set(0 + mBorderWidth, 0 + mBorderWidth, mBorderRect.width() - mBorderWidth, mBorderRect.height() - mBorderWidth); mShaderMatrix.set(null); dx = 0; dy = 0; if (mBitmapWidth * mDrawableRect.height() > mDrawableRect.width() * mBitmapHeight) { scale = (float) mDrawableRect.height() / (float) mBitmapHeight; dx = (mDrawableRect.width() - mBitmapWidth * scale) * 0.5f; } else { scale = (float) mDrawableRect.width() / (float) mBitmapWidth; dy = (mDrawableRect.height() - mBitmapHeight * scale) * 0.5f; } mShaderMatrix.setScale(scale, scale); mShaderMatrix.postTranslate((int) (dx + 0.5f) + mBorderWidth, (int) (dy + 0.5f) + mBorderWidth); break; case CENTER_INSIDE: // Log.d(TAG, "CENTER_INSIDE"); mShaderMatrix.set(null); if (mBitmapWidth <= mBounds.width() && mBitmapHeight <= mBounds.height()) { scale = 1.0f; } else { scale = Math.min((float) mBounds.width() / (float) mBitmapWidth, (float) mBounds.height() / (float) mBitmapHeight); } dx = (int) ((mBounds.width() - mBitmapWidth * scale) * 0.5f + 0.5f); dy = (int) ((mBounds.height() - mBitmapHeight * scale) * 0.5f + 0.5f); mShaderMatrix.setScale(scale, scale); mShaderMatrix.postTranslate(dx, dy); mBorderRect.set(mBitmapRect); mShaderMatrix.mapRect(mBorderRect); mDrawableRect.set(mBorderRect.left + mBorderWidth, mBorderRect.top + mBorderWidth, mBorderRect.right - mBorderWidth, mBorderRect.bottom - mBorderWidth); mShaderMatrix.setRectToRect(mBitmapRect, mDrawableRect, Matrix.ScaleToFit.FILL); break; case FIT_CENTER: mBorderRect.set(mBitmapRect); mShaderMatrix.setRectToRect(mBitmapRect, mBounds, Matrix.ScaleToFit.CENTER); mShaderMatrix.mapRect(mBorderRect); mDrawableRect.set(mBorderRect.left + mBorderWidth, mBorderRect.top + mBorderWidth, mBorderRect.right - mBorderWidth, mBorderRect.bottom - mBorderWidth); mShaderMatrix.setRectToRect(mBitmapRect, mDrawableRect, Matrix.ScaleToFit.FILL); break; case FIT_END: mBorderRect.set(mBitmapRect); mShaderMatrix.setRectToRect(mBitmapRect, mBounds, Matrix.ScaleToFit.END); mShaderMatrix.mapRect(mBorderRect); mDrawableRect.set(mBorderRect.left + mBorderWidth, mBorderRect.top + mBorderWidth, mBorderRect.right - mBorderWidth, mBorderRect.bottom - mBorderWidth); mShaderMatrix.setRectToRect(mBitmapRect, mDrawableRect, Matrix.ScaleToFit.FILL); break; case FIT_START: mBorderRect.set(mBitmapRect); mShaderMatrix.setRectToRect(mBitmapRect, mBounds, Matrix.ScaleToFit.START); mShaderMatrix.mapRect(mBorderRect); mDrawableRect.set(mBorderRect.left + mBorderWidth, mBorderRect.top + mBorderWidth, mBorderRect.right - mBorderWidth, mBorderRect.bottom - mBorderWidth); mShaderMatrix.setRectToRect(mBitmapRect, mDrawableRect, Matrix.ScaleToFit.FILL); break; case FIT_XY: default: // Log.d(TAG, "DEFAULT TO FILL"); mBorderRect.set(mBounds); mDrawableRect.set(0 + mBorderWidth, 0 + mBorderWidth, mBorderRect.width() - mBorderWidth, mBorderRect.height() - mBorderWidth); mShaderMatrix.set(null); mShaderMatrix.setRectToRect(mBitmapRect, mDrawableRect, Matrix.ScaleToFit.FILL); break; } mBitmapShader.setLocalMatrix(mShaderMatrix); } @Override protected void onBoundsChange(Rect bounds) { // Log.i(TAG, "onboundschange: w: " + bounds.width() + "h:" + bounds.height()); super.onBoundsChange(bounds); mBounds.set(bounds); // if (USE_VIGNETTE) { // RadialGradient vignette = new RadialGradient( // mDrawableRect.centerX(), mDrawableRect.centerY() * 1.0f / 0.7f, mDrawableRect.centerX() * 1.3f, // new int[] { 0, 0, 0x7f000000 }, new float[] { 0.0f, 0.7f, 1.0f }, // Shader.TileMode.CLAMP); // // Matrix oval = new Matrix(); // oval.setScale(1.0f, 0.7f); // vignette.setLocalMatrix(oval); // // mBitmapPaint.setShader( // new ComposeShader(mBitmapShader, vignette, PorterDuff.Mode.SRC_OVER)); // } setMatrix(); } @Override public void draw(Canvas canvas) { // Log.w(TAG, "Draw: " + mScaleType.toString()); if (mBorderWidth > 0) { canvas.drawRoundRect(mBorderRect, mCornerRadius, mCornerRadius, mBorderPaint); canvas.drawRoundRect(mDrawableRect, Math.max(mCornerRadius - mBorderWidth, 0), Math.max(mCornerRadius - mBorderWidth, 0), mBitmapPaint); } else { canvas.drawRoundRect(mDrawableRect, mCornerRadius, mCornerRadius, mBitmapPaint); } } @Override public int getOpacity() { return PixelFormat.TRANSLUCENT; } @Override public void setAlpha(int alpha) { mBitmapPaint.setAlpha(alpha); } @Override public void setColorFilter(ColorFilter cf) { mBitmapPaint.setColorFilter(cf); } @Override public int getIntrinsicWidth() { return mBitmapWidth; } @Override public int getIntrinsicHeight() { return mBitmapHeight; } public static Bitmap drawableToBitmap(Drawable drawable) { if (drawable instanceof BitmapDrawable) { return ((BitmapDrawable)drawable).getBitmap(); } Bitmap bitmap; int width = drawable.getIntrinsicWidth(); int height = drawable.getIntrinsicHeight(); if (width > 0 && height > 0) { bitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); drawable.draw(canvas); } else { bitmap = null; } return bitmap; } public static Drawable fromDrawable(Drawable drawable, float radius) { return fromDrawable(drawable, radius, 0, 0); } public static Drawable fromDrawable(Drawable drawable, float radius, int border, int borderColor) { if (drawable != null) { if (drawable instanceof TransitionDrawable) { TransitionDrawable td = (TransitionDrawable) drawable; int num = td.getNumberOfLayers(); Drawable[] drawableList = new Drawable[num]; for (int i = 0; i < num; i++) { Drawable d = td.getDrawable(i); if (d instanceof ColorDrawable) { // TODO skip colordrawables for now drawableList[i] = d; } else { drawableList[i] = new RoundedDrawable(drawableToBitmap(td.getDrawable(i)), radius, border, borderColor); } } return new TransitionDrawable(drawableList); } Bitmap bm = drawableToBitmap(drawable); if (bm != null) { return new RoundedDrawable(bm, radius, border, borderColor); } else { Log.w(TAG, "Failed to create bitmap from drawable!"); } } return drawable; } public float getCornerRadius() { return mCornerRadius; } public int getBorderWidth() { return mBorderWidth; } public int getBorderColor() { return mBorderColor; } public void setCornerRadius(float radius) { this.mCornerRadius = radius; } public void setBorderWidth(int width) { this.mBorderWidth = width; mBorderPaint.setStrokeWidth(mBorderWidth); } public void setBorderColor(int color) { this.mBorderColor = color; mBorderPaint.setColor(color); } }