Commit 08a8d884 authored by Brandon Wylie's avatar Brandon Wylie Committed by Commit Bot

[IC] Scale images before storing them in memory and returning

Scaling/cropping images we get for Feed, yields a roughly 5x
decrease in memory usage.

Image size before: 1064160 bytes
Image size after:  188356  bytes

Bug: 910192
Change-Id: I7b4347c9e34f6f8680886bb2c0dd14f5b4346ebe
Reviewed-on: https://chromium-review.googlesource.com/c/1357608
Commit-Queue: Brandon Wylie <wylieb@chromium.org>
Reviewed-by: default avatarTheresa <twellington@chromium.org>
Reviewed-by: default avatarSky Malice <skym@chromium.org>
Cr-Commit-Position: refs/heads/master@{#613372}
parent 9a69fc89
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
package org.chromium.chrome.browser.cached_image_fetcher; package org.chromium.chrome.browser.cached_image_fetcher;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.media.ThumbnailUtils;
import org.chromium.base.Callback; import org.chromium.base.Callback;
import org.chromium.base.DiscardableReferencePool; import org.chromium.base.DiscardableReferencePool;
...@@ -63,6 +64,7 @@ public class InMemoryCachedImageFetcher implements CachedImageFetcher { ...@@ -63,6 +64,7 @@ public class InMemoryCachedImageFetcher implements CachedImageFetcher {
} }
mCachedImageFetcher.fetchImage(url, width, height, (Bitmap bitmap) -> { mCachedImageFetcher.fetchImage(url, width, height, (Bitmap bitmap) -> {
bitmap = tryToResizeImage(bitmap, width, height);
storeBitmap(bitmap, url, width, height); storeBitmap(bitmap, url, width, height);
callback.onResult(bitmap); callback.onResult(bitmap);
}); });
...@@ -140,6 +142,32 @@ public class InMemoryCachedImageFetcher implements CachedImageFetcher { ...@@ -140,6 +142,32 @@ public class InMemoryCachedImageFetcher implements CachedImageFetcher {
return Math.min(maxCacheUsage, preferredCacheSize); return Math.min(maxCacheUsage, preferredCacheSize);
} }
/**
* Try to resize the given image if the conditions are met.
*
* @param bitmap The input bitmap, will be recycled if scaled.
* @param width The desired width of the output.
* @param height The desired height of the output.
*
* @return The resized image, or the original image if the conditions aren't met.
*/
@VisibleForTesting
Bitmap tryToResizeImage(Bitmap bitmap, int width, int height) {
if (width != 0 && height != 0 && bitmap.getWidth() != width
&& bitmap.getHeight() != height) {
/* The resizing rules are the as follows:
(1) The image will be scaled up (if smaller) in a way that maximizes the area of the
source bitmap that's in the destination bitmap.
(2) A crop is made in the middle of the bitmap for the given size (width, height).
The x/y are placed appropriately (conceptually just think of it as a properly sized
chunk taken from the middle). */
return ThumbnailUtils.extractThumbnail(
bitmap, width, height, ThumbnailUtils.OPTIONS_RECYCLE_INPUT);
} else {
return bitmap;
}
}
/** Test constructor. */ /** Test constructor. */
@VisibleForTesting @VisibleForTesting
InMemoryCachedImageFetcher(BitmapCache bitmapCache, CachedImageFetcher cachedImageFetcher) { InMemoryCachedImageFetcher(BitmapCache bitmapCache, CachedImageFetcher cachedImageFetcher) {
......
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
package org.chromium.chrome.browser.cached_image_fetcher; package org.chromium.chrome.browser.cached_image_fetcher;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
...@@ -36,6 +38,7 @@ import org.chromium.chrome.browser.BitmapCache; ...@@ -36,6 +38,7 @@ import org.chromium.chrome.browser.BitmapCache;
/** /**
* Unit tests for InMemoryCachedImageFetcher. * Unit tests for InMemoryCachedImageFetcher.
*/ */
@SuppressWarnings("unchecked")
@RunWith(BaseRobolectricTestRunner.class) @RunWith(BaseRobolectricTestRunner.class)
@Config(manifest = Config.NONE) @Config(manifest = Config.NONE)
public class InMemoryCachedImageFetcherTest { public class InMemoryCachedImageFetcherTest {
...@@ -68,6 +71,8 @@ public class InMemoryCachedImageFetcherTest { ...@@ -68,6 +71,8 @@ public class InMemoryCachedImageFetcherTest {
MockitoAnnotations.initMocks(this); MockitoAnnotations.initMocks(this);
mReferencePool = new DiscardableReferencePool(); mReferencePool = new DiscardableReferencePool();
mBitmapCache = new BitmapCache(mReferencePool, DEFAULT_CACHE_SIZE); mBitmapCache = new BitmapCache(mReferencePool, DEFAULT_CACHE_SIZE);
mInMemoryCachedImageFetcher =
spy(new InMemoryCachedImageFetcher(mBitmapCache, mCachedImageFetcherImpl));
} }
@After @After
...@@ -93,9 +98,12 @@ public class InMemoryCachedImageFetcherTest { ...@@ -93,9 +98,12 @@ public class InMemoryCachedImageFetcherTest {
.fetchImage(eq(URL), mWidthCaptor.capture(), mHeightCaptor.capture(), .fetchImage(eq(URL), mWidthCaptor.capture(), mHeightCaptor.capture(),
mCallbackCaptor.capture()); mCallbackCaptor.capture());
// clang-format on // clang-format on
doReturn(bitmap)
.when(mInMemoryCachedImageFetcher)
.tryToResizeImage(eq(bitmap), eq(WIDTH_PX), eq(HEIGHT_PX));
} }
@SuppressWarnings("unchecked")
@Test @Test
@SmallTest @SmallTest
public void testFetchImageCachesFirstCall() throws Exception { public void testFetchImageCachesFirstCall() throws Exception {
...@@ -126,6 +134,27 @@ public class InMemoryCachedImageFetcherTest { ...@@ -126,6 +134,27 @@ public class InMemoryCachedImageFetcherTest {
.fetchImage(eq(URL), eq(WIDTH_PX), eq(HEIGHT_PX), any()); .fetchImage(eq(URL), eq(WIDTH_PX), eq(HEIGHT_PX), any());
} }
@Test
@SmallTest
public void testResize() throws Exception {
Bitmap result =
mInMemoryCachedImageFetcher.tryToResizeImage(mBitmap, WIDTH_PX / 2, HEIGHT_PX / 2);
assertNotEquals(result, mBitmap);
}
@Test
@SmallTest
public void testResizeBailsOutIfSizeIsZero() throws Exception {
Bitmap result = mInMemoryCachedImageFetcher.tryToResizeImage(mBitmap, 0, HEIGHT_PX);
assertEquals(result, mBitmap);
result = mInMemoryCachedImageFetcher.tryToResizeImage(mBitmap, WIDTH_PX, 0);
assertEquals(result, mBitmap);
result = mInMemoryCachedImageFetcher.tryToResizeImage(mBitmap, 0, 0);
assertEquals(result, mBitmap);
}
@Test @Test
@SmallTest @SmallTest
public void testFetchImageDoesNotCacheAfterDestroy() { public void testFetchImageDoesNotCacheAfterDestroy() {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment