Skip to content

Instantly share code, notes, and snippets.

@s1rius
Created June 30, 2016 08:20
Show Gist options
  • Save s1rius/0230994835320e68e9c9aa1b8fd24ad7 to your computer and use it in GitHub Desktop.
Save s1rius/0230994835320e68e9c9aa1b8fd24ad7 to your computer and use it in GitHub Desktop.
Fresco
/*
* 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.producers;
import com.facebook.common.internal.Preconditions;
import com.facebook.common.internal.VisibleForTesting;
import com.facebook.common.logging.FLog;
import com.facebook.imageformat.ImageFormat;
import com.facebook.imagepipeline.image.EncodedImage;
import com.facebook.imagepipeline.common.ResizeOptions;
import com.facebook.imagepipeline.request.ImageRequest;
public class DownsampleUtil {
private static final float MAX_BITMAP_SIZE = 2048f;
private static final float MAX_JPEG_BITMAP_SIZE = 4096f;
private static final float INTERVAL_ROUNDING = 1.0f/3;
private static final int DEFAULT_SAMPLE_SIZE = 1;
private DownsampleUtil() {}
/**
* Get the factor between the dimensions of the encodedImage (actual image) and the ones of the
* imageRequest (requested size).
*
* @param imageRequest the request containing the requested dimensions
* @param encodedImage the encoded image with the actual dimensions
* @return
*/
public static int determineSampleSize(ImageRequest imageRequest, EncodedImage encodedImage) {
if (!EncodedImage.isMetaDataAvailable(encodedImage)) {
return DEFAULT_SAMPLE_SIZE;
}
float ratio = determineDownsampleRatio(imageRequest, encodedImage);
int sampleSize;
if (encodedImage.getImageFormat() == ImageFormat.JPEG) {
sampleSize = ratioToSampleSizeJPEG(ratio);
} else {
sampleSize = ratioToSampleSize(ratio);
}
// Check the case when the dimension of the downsampled image is still larger than the max
// possible dimension for an image.
int maxDimension = Math.max(encodedImage.getHeight(), encodedImage.getWidth());
// Make the bitmap not allocate more memory, special with JPEG
// TODO to prevent that the newwork image no so small, cant show detail
// 为了确保网络上长条形的长微博图片能够正确的显示,降低JPEG图片的压缩比例
// TODO will fix int future
if (encodedImage.getImageFormat() == ImageFormat.JPEG) {
while (maxDimension / sampleSize > MAX_JPEG_BITMAP_SIZE) {
sampleSize++;
}
} else {
while (maxDimension / sampleSize > MAX_BITMAP_SIZE) {
sampleSize++;
}
}
return sampleSize;
}
@VisibleForTesting
static float determineDownsampleRatio(
ImageRequest imageRequest, EncodedImage encodedImage) {
Preconditions.checkArgument(EncodedImage.isMetaDataAvailable(encodedImage));
final ResizeOptions resizeOptions = imageRequest.getResizeOptions();
if (resizeOptions == null || resizeOptions.height <= 0 || resizeOptions.width <= 0
|| encodedImage.getWidth() == 0 || encodedImage.getHeight() == 0) {
return 1.0f;
}
final int rotationAngle = getRotationAngle(imageRequest, encodedImage);
final boolean swapDimensions = rotationAngle == 90 || rotationAngle == 270;
final int widthAfterRotation = swapDimensions ?
encodedImage.getHeight() : encodedImage.getWidth();
final int heightAfterRotation = swapDimensions ?
encodedImage.getWidth() : encodedImage.getHeight();
final float widthRatio = ((float) resizeOptions.width) / widthAfterRotation;
final float heightRatio = ((float) resizeOptions.height) / heightAfterRotation;
float ratio = Math.max(widthRatio, heightRatio);
FLog.v(
"DownsampleUtil",
"Downsample - Specified size: %dx%d, image size: %dx%d " +
"ratio: %.1f x %.1f, ratio: %.3f for %s",
resizeOptions.width,
resizeOptions.height,
widthAfterRotation,
heightAfterRotation,
widthRatio,
heightRatio,
ratio,
imageRequest.getSourceUri().toString());
return ratio;
}
@VisibleForTesting
static int ratioToSampleSize(float ratio) {
if (ratio > 0.5f + 0.5f * INTERVAL_ROUNDING) {
return 1; // should have resized
}
int sampleSize = 1;
while (true) {
double intervalLength = 1.0 / (Math.pow(sampleSize, 2) - sampleSize);
double compare = (1.0 / sampleSize) + (intervalLength * INTERVAL_ROUNDING);
if (compare <= ratio) {
return sampleSize - 1;
}
sampleSize++;
}
}
@VisibleForTesting
static int ratioToSampleSizeJPEG(float ratio) {
if (ratio > 0.5f + 0.5f * INTERVAL_ROUNDING) {
return 1; // should have resized
}
int sampleSize = 1;
while (true) {
double intervalLength = 1.0 / (sampleSize + 1);
double compare = (1.0 / (2 * sampleSize)) + (intervalLength * INTERVAL_ROUNDING);
if (compare <= ratio) {
return sampleSize;
}
sampleSize++;
}
}
private static int getRotationAngle(ImageRequest imageRequest, EncodedImage encodedImage) {
if (!imageRequest.getAutoRotateEnabled()) {
return 0;
}
int rotationAngle = encodedImage.getRotationAngle();
Preconditions.checkArgument(rotationAngle == 0 || rotationAngle == 90
|| rotationAngle == 180 || rotationAngle == 270);
return rotationAngle;
}
@VisibleForTesting
static int roundToPowerOfTwo(int sampleSize) {
int compare = 1;
while (true) {
if (compare >= sampleSize) {
return compare;
}
compare++;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment