/** * We get the size from a generic image */ private Pair<Integer, Integer> readImageSize() { InputStream inputStream = null; Pair<Integer, Integer> dimensions = null; try { inputStream = getInputStream(); dimensions = BitmapUtil.decodeDimensions(inputStream); if (dimensions != null) { mWidth = dimensions.first; mHeight = dimensions.second; } }finally { if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { // Head in the sand } } } return dimensions; }
/** * Pins the bitmap */ public CloseableReference<Bitmap> pinBitmap(Bitmap bitmap) { try { // Real decoding happens here - if the image was corrupted, this will throw an exception Bitmaps.pinBitmap(bitmap); } catch (Exception e) { bitmap.recycle(); throw Throwables.propagate(e); } if (!mUnpooledBitmapsCounter.increase(bitmap)) { int bitmapSize = BitmapUtil.getSizeInBytes(bitmap); bitmap.recycle(); String detailMessage = String.format( Locale.US, "Attempted to pin a bitmap of size %d bytes." + " The current pool count is %d, the current pool size is %d bytes." + " The current pool max count is %d, the current pool max size is %d bytes.", bitmapSize, mUnpooledBitmapsCounter.getCount(), mUnpooledBitmapsCounter.getSize(), mUnpooledBitmapsCounter.getMaxCount(), mUnpooledBitmapsCounter.getMaxSize()); throw new TooManyBitmapsException(detailMessage); } return CloseableReference.of(bitmap, mUnpooledBitmapsCounter.getReleaser()); }
@Before public void setUp() { MockitoAnnotations.initMocks(this); PowerMockito.mockStatic(ImageFormatChecker.class, JfifUtil.class, BitmapUtil.class); mAddMetaDataProducer = new AddImageTransformMetaDataProducer(mInputProducer); mIntermediateResultBufferRef = CloseableReference.of(mock(PooledByteBuffer.class)); mFinalResultBufferRef = CloseableReference.of(mock(PooledByteBuffer.class)); mIntermediateResult = new EncodedImage(mIntermediateResultBufferRef); mFinalResult = new EncodedImage(mFinalResultBufferRef); mAddMetaDataConsumer = null; doAnswer( new Answer() { @Override public Object answer(InvocationOnMock invocation) throws Throwable { mAddMetaDataConsumer = (Consumer<EncodedImage>) invocation.getArguments()[0]; return null; } }).when(mInputProducer).produceResults(any(Consumer.class), any(ProducerContext.class)); mAddMetaDataProducer.produceResults(mConsumer, mProducerContext); }
@Test public void testOnNewResultNotLast_DimensionsNotFound() { int rotationAngle = 180; int orientation = 1; when(ImageFormatChecker.getImageFormat_WrapIOException(any(InputStream.class))) .thenReturn(DefaultImageFormats.JPEG); when(JfifUtil.getAutoRotateAngleFromOrientation(orientation)).thenReturn(rotationAngle); when(JfifUtil.getOrientation(any(InputStream.class))).thenReturn(orientation); when(BitmapUtil.decodeDimensions(any(InputStream.class))).thenReturn(null); mAddMetaDataConsumer.onNewResult(mIntermediateResult, Consumer.NO_FLAGS); ArgumentCaptor<EncodedImage> argumentCaptor = ArgumentCaptor.forClass(EncodedImage.class); verify(mConsumer).onNewResult(argumentCaptor.capture(), eq(Consumer.NO_FLAGS)); EncodedImage encodedImage = argumentCaptor.getValue(); assertTrue(EncodedImage.isValid(encodedImage)); assertEquals(-1, encodedImage.getRotationAngle()); assertEquals(-1, encodedImage.getWidth()); assertEquals(-1, encodedImage.getHeight()); }
@Test public void testOnNewResultNotLastAndJpeg() { int rotationAngle = 180; int orientation = 1; int width = 10; int height = 20; when(ImageFormatChecker.getImageFormat_WrapIOException(any(InputStream.class))) .thenReturn(DefaultImageFormats.JPEG); when(JfifUtil.getAutoRotateAngleFromOrientation(orientation)).thenReturn(rotationAngle); when(JfifUtil.getOrientation(any(InputStream.class))).thenReturn(orientation); when(BitmapUtil.decodeDimensions(any(InputStream.class))).thenReturn(new Pair(width, height)); mAddMetaDataConsumer.onNewResult(mFinalResult, Consumer.IS_LAST); ArgumentCaptor<EncodedImage> argumentCaptor = ArgumentCaptor.forClass(EncodedImage.class); verify(mConsumer).onNewResult(argumentCaptor.capture(), eq(Consumer.IS_LAST)); EncodedImage encodedImage = argumentCaptor.getValue(); assertTrue(EncodedImage.isValid(encodedImage)); assertEquals(DefaultImageFormats.JPEG, encodedImage.getImageFormat()); assertEquals(rotationAngle, encodedImage.getRotationAngle()); assertEquals(width, encodedImage.getWidth()); assertEquals(height, encodedImage.getHeight()); }
@Test public void testOnNewResultLastAndJpeg() { int rotationAngle = 180; int orientation = 1; int width = 10; int height = 20; when(ImageFormatChecker.getImageFormat_WrapIOException(any(InputStream.class))) .thenReturn(DefaultImageFormats.JPEG); when(JfifUtil.getAutoRotateAngleFromOrientation(orientation)).thenReturn(rotationAngle); when(JfifUtil.getOrientation(any(InputStream.class))).thenReturn(orientation); when(BitmapUtil.decodeDimensions(any(InputStream.class))).thenReturn(new Pair(width, height)); mAddMetaDataConsumer.onNewResult(mFinalResult, Consumer.IS_LAST); ArgumentCaptor<EncodedImage> argumentCaptor = ArgumentCaptor.forClass(EncodedImage.class); verify(mConsumer).onNewResult(argumentCaptor.capture(), eq(Consumer.IS_LAST)); EncodedImage encodedImage = argumentCaptor.getValue(); assertTrue(EncodedImage.isValid(encodedImage)); assertEquals(DefaultImageFormats.JPEG, encodedImage.getImageFormat()); assertEquals(rotationAngle, encodedImage.getRotationAngle()); assertEquals(width, encodedImage.getWidth()); assertEquals(height, encodedImage.getHeight()); }
private EncodedImage buildEncodedImage( PooledByteBuffer imageBytes, ExifInterface exifInterface) { Pair<Integer, Integer> dimensions = BitmapUtil.decodeDimensions(new PooledByteBufferInputStream(imageBytes)); int rotationAngle = getRotationAngle(exifInterface); int width = dimensions != null ? dimensions.first : EncodedImage.UNKNOWN_WIDTH; int height = dimensions != null ? dimensions.second : EncodedImage.UNKNOWN_HEIGHT; EncodedImage encodedImage; CloseableReference<PooledByteBuffer> closeableByteBuffer = CloseableReference.of(imageBytes); try { encodedImage = new EncodedImage(closeableByteBuffer); } finally { CloseableReference.closeSafely(closeableByteBuffer); } encodedImage.setImageFormat(DefaultImageFormats.JPEG); encodedImage.setRotationAngle(rotationAngle); encodedImage.setWidth(width); encodedImage.setHeight(height); return encodedImage; }
public static Bitmap create(int width, int height, Bitmap.Config config) { Preconditions.checkArgument(width > 0); Preconditions.checkArgument(height > 0); Preconditions.checkNotNull(config); Bitmap bitmap = mock(Bitmap.class); when(bitmap.getWidth()).thenReturn(width); when(bitmap.getHeight()).thenReturn(height); when(bitmap.getConfig()).thenReturn(config); when(bitmap.isMutable()).thenReturn(true); when(bitmap.getRowBytes()).thenReturn(width * BitmapUtil.getPixelSizeForBitmapConfig(config)); when(bitmap.getByteCount()).thenReturn(bitmapSize(width, height, config)); return bitmap; }
/** * Creates a bitmap of the specified width and height. * @param width the width of the bitmap * @param height the height of the bitmap * @param bitmapConfig the {@link android.graphics.Bitmap.Config} * used to create the decoded Bitmap * @return a reference to the bitmap * @exception java.lang.OutOfMemoryError if the Bitmap cannot be allocated */ @Override public CloseableReference<Bitmap> createBitmapInternal( int width, int height, Bitmap.Config bitmapConfig) { int sizeInBytes = BitmapUtil.getSizeInByteForBitmap(width, height, bitmapConfig); Bitmap bitmap = mBitmapPool.get(sizeInBytes); Bitmaps.reconfigureBitmap(bitmap, width, height, bitmapConfig); return CloseableReference.of(bitmap, mBitmapPool); }
/** * Includes given bitmap in the bitmap count. The bitmap is included only if doing so * does not violate configured limit * * @param bitmap to include in the count * @return true if and only if bitmap is successfully included in the count */ public synchronized boolean increase(Bitmap bitmap) { final int bitmapSize = BitmapUtil.getSizeInBytes(bitmap); if (mCount >= mMaxCount || mSize + bitmapSize > mMaxSize) { return false; } mCount++; mSize += bitmapSize; return true; }
/** * Excludes given bitmap from the count. * * @param bitmap to be excluded from the count */ public synchronized void decrease(Bitmap bitmap) { final int bitmapSize = BitmapUtil.getSizeInBytes(bitmap); Preconditions.checkArgument(mCount > 0, "No bitmaps registered."); Preconditions.checkArgument( bitmapSize <= mSize, "Bitmap size bigger than the total registered size: %d, %d", bitmapSize, mSize); mSize -= bitmapSize; mCount--; }
/** * Allocate a bitmap that has a backing memory allocation of 'size' bytes. * This is configuration agnostic so the size is the actual size in bytes of the bitmap. * @param size the 'size' in bytes of the bitmap * @return a new bitmap with the specified size in memory */ @Override protected Bitmap alloc(int size) { return Bitmap.createBitmap( 1, (int) Math.ceil(size / (double) BitmapUtil.RGB_565_BYTES_PER_PIXEL), Bitmap.Config.RGB_565); }
/** * Checks whether the producer may be able to produce images of the specified size. This makes no * promise about being able to produce images for a particular source, only generally being able * to produce output of the desired resolution. * * @param width the desired width * @param height the desired height * @return true if the producer can meet these needs */ public static boolean isImageBigEnough(int width, int height, ResizeOptions resizeOptions) { if (resizeOptions == null) { return getAcceptableSize(width) >= BitmapUtil.MAX_BITMAP_SIZE && getAcceptableSize(height) >= (int) BitmapUtil.MAX_BITMAP_SIZE; } else { return getAcceptableSize(width) >= resizeOptions.width && getAcceptableSize(height) >= resizeOptions.height; } }
/** * 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() == DefaultImageFormats.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()); final ResizeOptions resizeOptions = imageRequest.getResizeOptions(); final float maxBitmapSize = resizeOptions != null ? resizeOptions.maxBitmapSize : BitmapUtil.MAX_BITMAP_SIZE; while (maxDimension / sampleSize > maxBitmapSize) { if (encodedImage.getImageFormat() == DefaultImageFormats.JPEG) { sampleSize *= 2; } else { sampleSize++; } } return sampleSize; }
/** * Reconfigures bitmap after checking its allocation size. * * <p> This method is here to overcome our testing framework limit. Robolectric does not provide * KitKat specific APIs: {@link Bitmap#reconfigure} and {@link Bitmap#getAllocationByteCount} * are part of that. */ @TargetApi(19) public static void reconfigureBitmap( Bitmap bitmap, int width, int height, Bitmap.Config bitmapConfig) { Preconditions.checkArgument( bitmap.getAllocationByteCount() >= width * height * BitmapUtil.getPixelSizeForBitmapConfig(bitmapConfig)); bitmap.reconfigure(width, height, bitmapConfig); }
@Test public void testOnNewResultNotLastNotJpeg() { when(ImageFormatChecker.getImageFormat_WrapIOException(any(InputStream.class))) .thenReturn(DefaultImageFormats.WEBP_SIMPLE); when(BitmapUtil.decodeDimensions(any(InputStream.class))).thenReturn(null); mAddMetaDataConsumer.onNewResult(mIntermediateResult, Consumer.NO_FLAGS); ArgumentCaptor<EncodedImage> argumentCaptor = ArgumentCaptor.forClass(EncodedImage.class); verify(mConsumer).onNewResult(argumentCaptor.capture(), eq(Consumer.NO_FLAGS)); EncodedImage encodedImage = argumentCaptor.getValue(); assertTrue(EncodedImage.isValid(encodedImage)); assertEquals(DefaultImageFormats.WEBP_SIMPLE, encodedImage.getImageFormat()); assertEquals(0, encodedImage.getRotationAngle()); assertEquals(-1, encodedImage.getWidth()); assertEquals(-1, encodedImage.getHeight()); }
@Before public void setUp() throws IOException { MockitoAnnotations.initMocks(this); PowerMockito.mockStatic(JfifUtil.class, BitmapUtil.class); mTestExecutorService = new TestExecutorService(new FakeClock()); mTestLocalExifThumbnailProducer = new TestLocalExifThumbnailProducer( mTestExecutorService, mPooledByteBufferFactory, mContentResolver); when(mProducerContext.getImageRequest()).thenReturn(mImageRequest); when(mImageRequest.getSourceUri()).thenReturn(mUri); when(mProducerContext.getListener()).thenReturn(mProducerListener); mThumbnailBytes = new byte[100]; when(mExifInterface.hasThumbnail()).thenReturn(true); when(mExifInterface.getThumbnail()).thenReturn(mThumbnailBytes); when(mPooledByteBufferFactory.newByteBuffer(mThumbnailBytes)) .thenReturn(mThumbnailByteBuffer); when(mExifInterface.getAttribute(ExifInterface.TAG_ORIENTATION)) .thenReturn(Integer.toString(ORIENTATION)); when(JfifUtil.getAutoRotateAngleFromOrientation(ORIENTATION)).thenReturn(ANGLE); when(BitmapUtil.decodeDimensions(any(InputStream.class))).thenReturn(new Pair(WIDTH, HEIGHT)); doAnswer( new Answer() { @Override public Object answer(InvocationOnMock invocation) throws Throwable { mCapturedEncodedImage = EncodedImage.cloneOrNull( (EncodedImage) invocation.getArguments()[0]); return null; } }) .when(mConsumer) .onNewResult(notNull(EncodedImage.class), anyInt()); }
@Override public synchronized int getSizeInBytes() { int size = 0; for (int i = 0; i < mBitmapSparseArray.size(); i++) { size += BitmapUtil.getSizeInBytes(mBitmapSparseArray.valueAt(i).get()); } return size; }
@Override public synchronized int getSizeInBytes() { return mLastBitmapReference == null ? 0 : BitmapUtil.getSizeInBytes(mLastBitmapReference.get()); }
private static int getBitmapSizeBytes(@Nullable CloseableImage image) { if (!(image instanceof CloseableBitmap)) { return 0; } return BitmapUtil.getSizeInBytes(((CloseableBitmap) image).getUnderlyingBitmap()); }
public static Bitmap createForSize(int size, Bitmap.Config config) { Preconditions.checkArgument(size % BitmapUtil.getPixelSizeForBitmapConfig(config) == 0); return create(1, size / BitmapUtil.getPixelSizeForBitmapConfig(config), config); }
public static int bitmapSize(int width, int height, Bitmap.Config config) { return BitmapUtil.getSizeInByteForBitmap(width, height, config); }
public ResizeOptions( int width, int height) { this(width, height, BitmapUtil.MAX_BITMAP_SIZE); }