content: /*
   * Copyright (C) 2014 The Android Open Source Project
   *
   * 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 com.android.bitmap.drawable;
  
  import android.content.res.Resources;
  import android.graphics.Bitmap;
  import android.graphics.BitmapShader;
  import android.graphics.Canvas;
  import android.graphics.Color;
  import android.graphics.ColorFilter;
  import android.graphics.Matrix;
  import android.graphics.Paint;
  import android.graphics.Paint.Style;
  import android.graphics.Rect;
  import android.graphics.Shader.TileMode;
  import android.graphics.drawable.BitmapDrawable;
  
  import com.android.bitmap.BitmapCache;
  
  /**
   * Custom BasicBitmapDrawable implementation for circular images.
   *
   * This draws all bitmaps as a circle with an optional border stroke.
   */
  public class CircularBitmapDrawable extends ExtendedBitmapDrawable {
      private final Paint mBitmapPaint = new Paint();
      private final Paint mBorderPaint = new Paint();
      private final Rect mRect = new Rect();
      private final Matrix mMatrix = new Matrix();
  
      private float mBorderWidth;
      private Bitmap mShaderBitmap;
  
      public CircularBitmapDrawable(Resources res,
              BitmapCache cache, boolean limitDensity) {
          this(res, cache, limitDensity, null);
      }
  
      public CircularBitmapDrawable(Resources res,
              BitmapCache cache, boolean limitDensity, ExtendedOptions opts) {
          super(res, cache, limitDensity, opts);
  
          mBitmapPaint.setAntiAlias(true);
          mBitmapPaint.setFilterBitmap(true);
          mBitmapPaint.setDither(true);
  
          mBorderPaint.setColor(Color.TRANSPARENT);
          mBorderPaint.setStyle(Style.STROKE);
          mBorderPaint.setStrokeWidth(mBorderWidth);
          mBorderPaint.setAntiAlias(true);
      }
  
      /**
       * Set the border stroke width of this drawable.
       */
      public void setBorderWidth(final float borderWidth) {
          final boolean changed = mBorderPaint.getStrokeWidth() != borderWidth;
          mBorderPaint.setStrokeWidth(borderWidth);
          mBorderWidth = borderWidth;
  
          if (changed) {
              invalidateSelf();
          }
      }
  
      /**
       * Set the border stroke color of this drawable. Set to {@link Color#TRANSPARENT} to disable.
       */
      public void setBorderColor(final int color) {
          final boolean changed = mBorderPaint.getColor() != color;
          mBorderPaint.setColor(color);
  
          if (changed) {
              invalidateSelf();
          }
      }
  
      @Override
      protected void onDrawBitmap(final Canvas canvas, final Rect src,
              final Rect dst) {
          onDrawCircularBitmap(getBitmap().bmp, canvas, src, dst, 1f);
      }
  
      @Override
      protected void onDrawPlaceholderOrProgress(final Canvas canvas,
              final TileDrawable drawable) {
          Rect bounds = getBounds();
          if (drawable.getInnerDrawable() instanceof BitmapDrawable) {
              BitmapDrawable placeholder =
                  (BitmapDrawable) drawable.getInnerDrawable();
              Bitmap bitmap = placeholder.getBitmap();
              float alpha = placeholder.getPaint().getAlpha() / 255f;
              mRect.set(0, 0, bitmap.getWidth(), bitmap.getHeight());
              onDrawCircularBitmap(bitmap, canvas, mRect, bounds, alpha);
          } else {
            super.onDrawPlaceholderOrProgress(canvas, drawable);
          }
  
          // Then draw the border.
          canvas.drawCircle(bounds.centerX(), bounds.centerY(),
                  bounds.width() / 2f - mBorderWidth / 2, mBorderPaint);
      }
  
      /**
       * Call this method with a given bitmap to draw it onto the given canvas, masked by a circular
       * BitmapShader.
       */
      protected void onDrawCircularBitmap(final Bitmap bitmap, final Canvas canvas,
              final Rect src, final Rect dst) {
          onDrawCircularBitmap(bitmap, canvas, src, dst, 1f);
      }
  
      /**
       * Call this method with a given bitmap to draw it onto the given canvas, masked by a circular
       * BitmapShader. The alpha parameter is the value from 0f to 1f to attenuate the alpha by.
       */
      protected void onDrawCircularBitmap(final Bitmap bitmap, final Canvas canvas,
              final Rect src, final Rect dst, final float alpha) {
          // Draw bitmap through shader first.
          BitmapShader shader = (BitmapShader) mBitmapPaint.getShader();
          if (shader == null || mShaderBitmap != bitmap) {
            shader = new BitmapShader(bitmap, TileMode.CLAMP, TileMode.CLAMP);
            mShaderBitmap = bitmap;
          }
  
          mMatrix.reset();
          // Fit bitmap to bounds.
          float scale = Math.max((float) dst.width() / src.width(),
                  (float) dst.height() / src.height());
          mMatrix.postScale(scale, scale);
          // Translate bitmap to dst bounds.
          mMatrix.postTranslate(dst.left, dst.top);
          shader.setLocalMatrix(mMatrix);
          mBitmapPaint.setShader(shader);
  
          int oldAlpha = mBitmapPaint.getAlpha();
          mBitmapPaint.setAlpha((int) (oldAlpha * alpha));
          canvas.drawCircle(dst.centerX(), dst.centerY(), dst.width() / 2,
                  mBitmapPaint);
          mBitmapPaint.setAlpha(oldAlpha);
  
          // Then draw the border.
          canvas.drawCircle(dst.centerX(), dst.centerY(),
                  dst.width() / 2f - mBorderWidth / 2, mBorderPaint);
      }
  
      @Override
      public void setAlpha(int alpha) {
          super.setAlpha(alpha);
  
          final int old = mBitmapPaint.getAlpha();
          mBitmapPaint.setAlpha(alpha);
          if (alpha != old) {
              invalidateSelf();
          }
      }
  
      @Override
      public void setColorFilter(ColorFilter cf) {
          super.setColorFilter(cf);
          mPaint.setColorFilter(cf);
      }
  }
  