/* * 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.imagepipeline.common; import android.support.annotation.IntDef; import com.facebook.common.util.HashCodeUtil; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Locale; /** * Options for rotation. * * <p> These options are applied to JPEG images only. * * <p> Describes how the image should be rotated, whether following image meta-data or a specified * amount. * * <p> These options are only relevant for JPEG images. Fresco doesn't support rotation of other * image formats. * * <p> The options also include whether the rotation can be deferred until the bitmap is rendered. * This should be be false if a post-processor is used which needs to operate on the bitmap * correctly oriented but can otherwise generally be true, particularly if using drawee. */ public class RotationOptions { @IntDef(flag=false, value={ NO_ROTATION, ROTATE_90, ROTATE_180, ROTATE_270, USE_EXIF_ROTATION_ANGLE, DISABLE_ROTATION }) @Retention(RetentionPolicy.SOURCE) private @interface Rotation {} @IntDef(flag=false, value={ NO_ROTATION, ROTATE_90, ROTATE_180, ROTATE_270 }) @Retention(RetentionPolicy.SOURCE) public @interface RotationAngle {} public static final int NO_ROTATION = 0; public static final int ROTATE_90 = 90; public static final int ROTATE_180 = 180; public static final int ROTATE_270 = 270; private static final int USE_EXIF_ROTATION_ANGLE = -1; private static final int DISABLE_ROTATION = -2; private final @Rotation int mRotation; private final boolean mDeferUntilRendered; private static final RotationOptions ROTATION_OPTIONS_AUTO_ROTATE = new RotationOptions(USE_EXIF_ROTATION_ANGLE, false); private static final RotationOptions ROTATION_OPTIONS_DISABLE_ROTATION = new RotationOptions(DISABLE_ROTATION, false); private static final RotationOptions ROTATION_OPTIONS_ROTATE_AT_RENDER_TIME = new RotationOptions(USE_EXIF_ROTATION_ANGLE, true); /** * Creates a new set of rotation options for JPEG images to use the rotation angle in the image * metadata. * * <p> This is the default option for requests which don't specify rotation options. * * <p> The rotation will not be deferred for defensiveness but that can improve performance. To * defer, use {@link #autoRotateAtRenderTime()}. */ public static RotationOptions autoRotate() { return ROTATION_OPTIONS_AUTO_ROTATE; } /** * Creates a new set of rotation options for JPEG images to load image without any rotation. * */ public static RotationOptions disableRotation() { return ROTATION_OPTIONS_DISABLE_ROTATION; } /** * Creates a new set of rotation options for JPEG images to use the rotation angle in the image * metadata. * * <p> The rotation may be deferred until the image is rendered. */ public static RotationOptions autoRotateAtRenderTime() { return ROTATION_OPTIONS_ROTATE_AT_RENDER_TIME; } /** * Creates a new set of rotation options to use a specific rotation angle. * * <p> The rotation will be carried out in the pipeline. * * @param angle the angle to rotate - valid values are 0, 90, 180 and 270 */ public static RotationOptions forceRotation(@RotationAngle int angle) { return new RotationOptions(angle, false); } private RotationOptions(@Rotation int rotation, boolean canDeferUntilRendered) { this.mRotation = rotation; this.mDeferUntilRendered = canDeferUntilRendered; } public boolean useImageMetadata() { return mRotation == USE_EXIF_ROTATION_ANGLE; } public boolean rotationEnabled() { return mRotation != DISABLE_ROTATION; } /** * Gets the explicit angle to rotate to, if one was set. * * @throws IllegalStateException if the instance was create using one of the * {@code autoRotate()} constructors. */ public @RotationAngle int getForcedAngle() { if (useImageMetadata()) { throw new IllegalStateException("Rotation is set to use EXIF"); } return mRotation; } public boolean canDeferUntilRendered() { return mDeferUntilRendered; } @Override public int hashCode() { return HashCodeUtil.hashCode(mRotation, mDeferUntilRendered); } @Override public boolean equals(Object other) { if (other == this) { return true; } if (!(other instanceof RotationOptions)) { return false; } RotationOptions that = (RotationOptions) other; return this.mRotation == that.mRotation && this.mDeferUntilRendered == that.mDeferUntilRendered; } @Override public String toString() { return String.format((Locale) null, "%d defer:%b", mRotation, mDeferUntilRendered); } }