/* * 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.animated.util; import java.util.Arrays; import android.annotation.SuppressLint; import android.graphics.Bitmap; import android.os.Build; import com.facebook.imagepipeline.animated.base.AnimatedDrawable; /** * Utility methods for {@link AnimatedDrawable}. */ public class AnimatedDrawableUtil { // See comment in fixFrameDurations below. private static final int MIN_FRAME_DURATION_MS = 11; private static final int FRAME_DURATION_MS_FOR_MIN = 100; public void appendMemoryString(StringBuilder sb, int bytes) { int kbUsed = bytes / 1024; if (kbUsed < 1024) { sb.append(kbUsed); sb.append("KB"); } else { int mbUsed = kbUsed / 1024; int mbUsedDecimal = (kbUsed % 1024) / 100; sb.append(mbUsed); sb.append("."); sb.append(mbUsedDecimal); sb.append("MB"); } } /** * Adjusts the frame duration array to respect logic for minimum frame duration time. * * @param frameDurationMs the frame duration array */ public void fixFrameDurations(int[] frameDurationMs) { // We follow Chrome's behavior which comes from Firefox. // Comment from Chrome's ImageSource.cpp follows: // We follow Firefox's behavior and use a duration of 100 ms for any frames that specify // a duration of <= 10 ms. See <rdar://problem/7689300> and <http://webkit.org/b/36082> // for more information. for (int i = 0; i < frameDurationMs.length; i++) { if (frameDurationMs[i] < MIN_FRAME_DURATION_MS) { frameDurationMs[i] = FRAME_DURATION_MS_FOR_MIN; } } } /** * Gets the total duration of an image by summing up the duration of the frames. * * @param frameDurationMs the frame duration array * @return the total duration in milliseconds */ public int getTotalDurationFromFrameDurations(int[] frameDurationMs) { int totalMs = 0; for (int i = 0; i < frameDurationMs.length; i++) { totalMs += frameDurationMs[i]; } return totalMs; } /** * Given an array of frame durations, generate an array of timestamps corresponding to when each * frame beings. * * @param frameDurationsMs an array of frame durations * @return an array of timestamps */ public int[] getFrameTimeStampsFromDurations(int[] frameDurationsMs) { int[] frameTimestampsMs = new int[frameDurationsMs.length]; int accumulatedDurationMs = 0; for (int i = 0; i < frameDurationsMs.length; i++) { frameTimestampsMs[i] = accumulatedDurationMs; accumulatedDurationMs += frameDurationsMs[i]; } return frameTimestampsMs; } /** * Gets the frame index for specified timestamp. * * @param frameTimestampsMs an array of timestamps generated by {@link #getFrameForTimestampMs)} * @param timestampMs the timestamp * @return the frame index for the timestamp or the last frame number if the timestamp is outside * the duration of the entire animation */ public int getFrameForTimestampMs(int frameTimestampsMs[], int timestampMs) { int index = Arrays.binarySearch(frameTimestampsMs, timestampMs); if (index < 0) { return -index - 1 - 1; } else { return index; } } @SuppressLint("NewApi") public int getSizeOfBitmap(Bitmap bitmap) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { return bitmap.getAllocationByteCount(); } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) { return bitmap.getByteCount(); } else { // Estimate for earlier platforms. return bitmap.getWidth() * bitmap.getHeight() * 4; } } /** * Checks whether the specified frame number is outside the range inclusive of both start and end. * If start <= end, start is within, end is within, and everything in between is within. * If start > end, start is within, end is within, everything less than start is within and * everything greater than end is within. This behavior is useful for handling the wrapping case. * * @param startFrame the start frame * @param endFrame the end frame * @param frameNumber the frame number * @return whether the frame is outside the range of [start, end] */ public static boolean isOutsideRange(int startFrame, int endFrame, int frameNumber) { if (startFrame == -1 || endFrame == -1) { // This means nothing should pass. return true; } boolean outsideRange; if (startFrame <= endFrame) { outsideRange = frameNumber < startFrame || frameNumber > endFrame; } else { // Wrapping outsideRange = frameNumber < startFrame && frameNumber > endFrame; } return outsideRange; } }