/**
* Copyright (c) 2017-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.litho;
import android.graphics.Matrix;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.widget.ImageView.ScaleType;
/**
* Static class containing a factory method for creating a matrix to apply to a drawable.
*/
public final class DrawableMatrix extends Matrix {
private boolean mShouldClipRect;
private DrawableMatrix() {
}
/**
* @return True if this Matrix requires a clipRect() on the bounds of the drawable.
*/
public boolean shouldClipRect() {
return mShouldClipRect;
}
/**
* Create a matrix to be applied to a drawable which scales the drawable according to its
* scale type.
*
* @param d The drawable to create a matrix for
* @param scaleType A scale type describing how to scale the drawable.
* @param width The width of the drawable's container.
* @param height The height of the drawable's container.
* @return The scale matrix or null if the drawable does not need to be scaled.
*/
public static DrawableMatrix create(
final Drawable d,
ScaleType scaleType,
final int width,
final int height) {
if (scaleType == null) {
scaleType = ScaleType.FIT_CENTER;
}
if (d == null) {
return null;
}
final int intrinsicWidth = d.getIntrinsicWidth();
final int intrinsicHeight = d.getIntrinsicHeight();
if (intrinsicWidth <= 0
|| intrinsicHeight <= 0
|| ScaleType.FIT_XY == scaleType
|| ScaleType.MATRIX == scaleType) {
return null;
}
if (width == intrinsicWidth && height == intrinsicHeight) {
// The bitmap fits exactly, no transform needed.
return null;
}
final DrawableMatrix result = new DrawableMatrix();
if (ScaleType.CENTER == scaleType) {
// Center bitmap in view, no scaling.
result.setTranslate(
FastMath.round((width - intrinsicWidth) * 0.5f),
FastMath.round((height - intrinsicHeight) * 0.5f));
result.mShouldClipRect = (intrinsicWidth > width || intrinsicHeight > height);
} else if (ScaleType.CENTER_CROP == scaleType) {
final float scale;
float dx = 0;
float dy = 0;
if (intrinsicWidth * height > width * intrinsicHeight) {
scale = (float) height / (float) intrinsicHeight;
dx = (width - intrinsicWidth * scale) * 0.5f;
} else {
scale = (float) width / (float) intrinsicWidth;
dy = (height - intrinsicHeight * scale) * 0.5f;
}
result.setScale(scale, scale);
result.postTranslate(FastMath.round(dx), FastMath.round(dy));
result.mShouldClipRect = true;
} else if (ScaleType.CENTER_INSIDE == scaleType) {
final float scale;
if (intrinsicWidth <= width && intrinsicHeight <= height) {
scale = 1.0f;
} else {
scale = Math.min(
(float) width / (float) intrinsicWidth,
(float) height / (float) intrinsicHeight);
}
final float dx = FastMath.round((width - intrinsicWidth * scale) * 0.5f);
final float dy = FastMath.round((height - intrinsicHeight * scale) * 0.5f);
result.setScale(scale, scale);
result.postTranslate(dx, dy);
} else {
RectF src = ComponentsPools.acquireRectF();
RectF dest = ComponentsPools.acquireRectF();
try {
// Generate the required transform.
src.set(0, 0, intrinsicWidth, intrinsicHeight);
dest.set(0, 0, width, height);
result.setRectToRect(src, dest, scaleTypeToScaleToFit(scaleType));
} finally {
ComponentsPools.releaseRectF(src);
ComponentsPools.releaseRectF(dest);
}
}
return result;
}
private static Matrix.ScaleToFit scaleTypeToScaleToFit(ScaleType st) {
// ScaleToFit enum to their corresponding Matrix.ScaleToFit values
switch (st) {
case FIT_XY:
return Matrix.ScaleToFit.FILL;
case FIT_START:
return Matrix.ScaleToFit.START;
case FIT_CENTER:
return Matrix.ScaleToFit.CENTER;
case FIT_END:
return Matrix.ScaleToFit.END;
default:
throw new IllegalArgumentException("Only FIT_... values allowed");
}
}
}