/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
package com.facebook.drawee.drawable;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import com.facebook.common.internal.Preconditions;
/**
* Drawable that can adjust underlying drawable based on specified {@link Matrix}.
*/
public class MatrixDrawable extends ForwardingDrawable {
// Specified matrix.
private Matrix mMatrix;
// Matrix that is actually being used for drawing. In case underlying drawable doesn't have
// intrinsic dimensions, this will be null (i.e. no matrix will be applied).
private Matrix mDrawMatrix;
// Last known dimensions of the underlying drawable. Used to avoid computing bounds every time
// if underlying size hasn't changed.
private int mUnderlyingWidth = 0;
private int mUnderlyingHeight = 0;
/**
* Creates a new MatrixDrawable with given underlying drawable and matrix.
* @param drawable underlying drawable to apply the matrix to
* @param matrix matrix to be applied to the drawable
*/
public MatrixDrawable(Drawable drawable, Matrix matrix) {
super(Preconditions.checkNotNull(drawable));
mMatrix = matrix;
}
/**
* Gets the current matrix.
* @return matrix
*/
public Matrix getMatrix() {
return mMatrix;
}
/**
* Sets the matrix.
* @param matrix matrix to set
*/
public void setMatrix(Matrix matrix) {
mMatrix = matrix;
configureBounds();
invalidateSelf();
}
@Override
public void draw(Canvas canvas) {
configureBoundsIfUnderlyingChanged();
if (mDrawMatrix != null) {
int saveCount = canvas.save();
canvas.clipRect(getBounds());
canvas.concat(mDrawMatrix);
super.draw(canvas);
canvas.restoreToCount(saveCount);
} else {
// mDrawMatrix == null means our bounds match and we can take fast path
super.draw(canvas);
}
}
@Override
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
configureBounds();
}
private void configureBoundsIfUnderlyingChanged() {
if (mUnderlyingWidth != getCurrent().getIntrinsicWidth() ||
mUnderlyingHeight != getCurrent().getIntrinsicHeight()) {
configureBounds();
}
}
/**
* Determines bounds for the underlying drawable and a matrix that should be applied on it.
*/
private void configureBounds() {
Drawable underlyingDrawable = getCurrent();
Rect bounds = getBounds();
int underlyingWidth = mUnderlyingWidth = underlyingDrawable.getIntrinsicWidth();
int underlyingHeight = mUnderlyingHeight = underlyingDrawable.getIntrinsicHeight();
// In case underlying drawable doesn't have intrinsic dimensions, we cannot set its bounds to
// -1 so we use our bounds and discard specified matrix. In normal case we use drawable's
// intrinsic dimensions for its bounds and apply specified matrix to it.
if (underlyingWidth <= 0 || underlyingHeight <= 0) {
underlyingDrawable.setBounds(bounds);
mDrawMatrix = null;
} else {
underlyingDrawable.setBounds(0, 0, underlyingWidth, underlyingHeight);
mDrawMatrix = mMatrix;
}
}
/**
* TransformationCallback method
* @param transform
*/
@Override
public void getTransform(Matrix transform) {
super.getTransform(transform);
if (mDrawMatrix != null) {
transform.preConcat(mDrawMatrix);
}
}
}