/*
* 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.Matrix;
import android.graphics.Rect;
/**
* Performs scale type calculations.
*/
public class ScalingUtils {
/**
* Options for scaling the child bounds to the parent bounds.
* <p>
* Similar to {@link android.widget.ImageView.ScaleType}, but ScaleType.MATRIX is not supported.
* To use matrix scaling, use a {@link MatrixDrawable}. An additional scale type (FOCUS_CROP) is
* provided.
*/
public enum ScaleType {
/**
* Scales width and height independently, so that the child matches the parent exactly.
* This may change the aspect ratio of the child.
*/
FIT_XY,
/**
* Scales the child so that it fits entirely inside the parent. At least one dimension (width or
* height) will fit exactly. Aspect ratio is preserved.
* Child is aligned to the top-left corner of the parent.
*/
FIT_START,
/**
* Scales the child so that it fits entirely inside the parent. At least one dimension (width or
* height) will fit exactly. Aspect ratio is preserved.
* Child is centered within the parent's bounds.
*/
FIT_CENTER,
/**
* Scales the child so that it fits entirely inside the parent. At least one dimension (width or
* height) will fit exactly. Aspect ratio is preserved.
* Child is aligned to the bottom-right corner of the parent.
*/
FIT_END,
/**
* Performs no scaling.
* Child is centered within parent's bounds.
*/
CENTER,
/**
* Scales the child so that it fits entirely inside the parent. Unlike FIT_CENTER, if the child
* is smaller, no up-scaling will be performed. Aspect ratio is preserved.
* Child is centered within parent's bounds.
*/
CENTER_INSIDE,
/**
* Scales the child so that both dimensions will be greater than or equal to the corresponding
* dimension of the parent. At least one dimension (width or height) will fit exactly.
* Child is centered within parent's bounds.
*/
CENTER_CROP,
/**
* Scales the child so that both dimensions will be greater than or equal to the corresponding
* dimension of the parent. At least one dimension (width or height) will fit exactly.
* The child's focus point will be centered within the parent's bounds as much as possible
* without leaving empty space.
* It is guaranteed that the focus point will be visible and centered as much as possible.
* If the focus point is set to (0.5f, 0.5f), result will be equivalent to CENTER_CROP.
*/
FOCUS_CROP;
/**
* Gets the scale type out of string.
*
* <p> Used by GenericDraweeView styleable in
* android_res/com/facebook/custom/res/values/attrs.xml
*
* @param value string value to parse
* @return scale type if recognized
* @throws IllegalArgumentException if scale type is not recognized
*/
public static ScaleType fromString(String value) {
if (value.equals("none")) {
return null;
} else if (value.equals("fitXY")) {
return ScaleType.FIT_XY;
} else if (value.equals("fitStart")) {
return ScaleType.FIT_START;
} else if (value.equals("fitCenter")) {
return FIT_CENTER;
} else if (value.equals("fitEnd")) {
return FIT_END;
} else if (value.equals("center")) {
return CENTER;
} else if (value.equals("centerInside")) {
return CENTER_INSIDE;
} else if (value.equals("centerCrop")) {
return CENTER_CROP;
} else if (value.equals("focusCrop")) {
return FOCUS_CROP;
} else {
throw new IllegalArgumentException(
"Unrecognized scale type: " + value +
"; use a value defined in the ScalingUtils.fromString method");
}
}
}
/**
* Gets transformation based on the scale type.
* @param transform out matrix to store result
* @param parentBounds parent bounds
* @param childWidth child width
* @param childHeight child height
* @param focusX focus point x coordinate, relative [0...1]
* @param focusY focus point y coordinate, relative [0...1]
* @param scaleType scale type to be used
* @return reference to the out matrix
*/
public static Matrix getTransform(
final Matrix transform,
final Rect parentBounds,
final int childWidth,
final int childHeight,
final float focusX,
final float focusY,
final ScaleType scaleType) {
final int parentWidth = parentBounds.width();
final int parentHeight = parentBounds.height();
final float scaleX = (float) parentWidth / (float) childWidth;
final float scaleY = (float) parentHeight / (float) childHeight;
float scale = 1.0f;
float dx = 0;
float dy = 0;
switch (scaleType) {
case FIT_XY:
dx = parentBounds.left;
dy = parentBounds.top;
transform.setScale(scaleX, scaleY);
transform.postTranslate((int) (dx + 0.5f), (int) (dy + 0.5f));
break;
case FIT_START:
scale = Math.min(scaleX, scaleY);
dx = parentBounds.left;
dy = parentBounds.top;
transform.setScale(scale, scale);
transform.postTranslate((int) (dx + 0.5f), (int) (dy + 0.5f));
break;
case FIT_CENTER:
scale = Math.min(scaleX, scaleY);
dx = parentBounds.left + (parentWidth - childWidth * scale) * 0.5f;
dy = parentBounds.top + (parentHeight - childHeight * scale) * 0.5f;
transform.setScale(scale, scale);
transform.postTranslate((int) (dx + 0.5f), (int) (dy + 0.5f));
break;
case FIT_END:
scale = Math.min(scaleX, scaleY);
dx = parentBounds.left + (parentWidth - childWidth * scale);
dy = parentBounds.top + (parentHeight - childHeight * scale);
transform.setScale(scale, scale);
transform.postTranslate((int) (dx + 0.5f), (int) (dy + 0.5f));
break;
case CENTER:
dx = parentBounds.left + (parentWidth - childWidth) * 0.5f;
dy = parentBounds.top + (parentHeight - childHeight) * 0.5f;
transform.setTranslate((int) (dx + 0.5f), (int) (dy + 0.5f));
break;
case CENTER_INSIDE:
scale = Math.min(Math.min(scaleX, scaleY), 1.0f);
dx = parentBounds.left + (parentWidth - childWidth * scale) * 0.5f;
dy = parentBounds.top + (parentHeight - childHeight * scale) * 0.5f;
transform.setScale(scale, scale);
transform.postTranslate((int) (dx + 0.5f), (int) (dy + 0.5f));
break;
case CENTER_CROP:
if (scaleY > scaleX) {
scale = scaleY;
dx = parentBounds.left + (parentWidth - childWidth * scale) * 0.5f;
dy = parentBounds.top;
} else {
scale = scaleX;
dx = parentBounds.left;
dy = parentBounds.top + (parentHeight - childHeight * scale) * 0.5f;
}
transform.setScale(scale, scale);
transform.postTranslate((int) (dx + 0.5f), (int) (dy + 0.5f));
break;
case FOCUS_CROP:
if (scaleY > scaleX) {
scale = scaleY;
dx = parentWidth * 0.5f - childWidth * scale * focusX;
dx = parentBounds.left + Math.max(Math.min(dx, 0), parentWidth - childWidth * scale);
dy = parentBounds.top;
} else {
scale = scaleX;
dx = parentBounds.left;
dy = parentHeight * 0.5f - childHeight * scale * focusY;
dy = parentBounds.top + Math.max(Math.min(dy, 0), parentHeight - childHeight * scale);
}
transform.setScale(scale, scale);
transform.postTranslate((int) (dx + 0.5f), (int) (dy + 0.5f));
break;
default:
throw new UnsupportedOperationException("Unsupported scale type: " + scaleType);
}
return transform;
}
}