下面列出了怎么用android.graphics.BitmapRegionDecoder的API类实例代码及写法,或者点击链接到github查看源代码。
/**
* Reads and crops the bitmap.
* @param outOptions Bitmap options, useful to determine {@code outMimeType}.
*/
private Bitmap crop(BitmapFactory.Options outOptions) throws IOException {
InputStream inputStream = openBitmapInputStream();
// Effeciently crops image without loading full resolution into memory
// https://developer.android.com/reference/android/graphics/BitmapRegionDecoder.html
BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(inputStream, false);
try {
Rect rect = new Rect(mX, mY, mX + mWidth, mY + mHeight);
return decoder.decodeRegion(rect, outOptions);
} finally {
if (inputStream != null) {
inputStream.close();
}
decoder.recycle();
}
}
/**
* 由使用者输入一张图片
*/
public void setImage(InputStream is) {
//先读取原图片的信息 高,宽
mOptions.inJustDecodeBounds = true;
BitmapFactory.decodeStream(is, null, mOptions);
mImageWidth = mOptions.outWidth;
mImageHeight = mOptions.outHeight;
//开启复用
mOptions.inMutable = true;
//设置格式成RGB_565
mOptions.inPreferredConfig = Bitmap.Config.RGB_565;
mOptions.inJustDecodeBounds = false;
//创建一个区域解码器
try {
mDecoder = BitmapRegionDecoder.newInstance(is, false);
} catch (IOException e) {
e.printStackTrace();
}
requestLayout();
}
@Override
public Point init(Context context, Uri uri) throws Exception {
ALog.w(TAG, "Init!");
if (!PartAuthority.isLocalUri(uri)) {
passthrough = new SkiaImageRegionDecoder();
return passthrough.init(context, uri);
}
MasterSecret masterSecret = accountContext.getMasterSecret();
if (masterSecret == null) {
throw new IllegalStateException("No master secret available...");
}
InputStream inputStream = PartAuthority.getAttachmentStream(context, masterSecret, uri);
this.bitmapRegionDecoder = BitmapRegionDecoder.newInstance(inputStream, false);
inputStream.close();
return new Point(bitmapRegionDecoder.getWidth(), bitmapRegionDecoder.getHeight());
}
public static BitmapRegionDecoder createBitmapRegionDecoder(
ThreadPool.JobContext jc, byte[] bytes, int offset, int length,
boolean shareable) {
if (offset < 0 || length <= 0 || offset + length > bytes.length) {
throw new IllegalArgumentException(String.format(
"offset = %s, length = %s, bytes = %s",
offset, length, bytes.length));
}
try {
return BitmapRegionDecoder.newInstance(
bytes, offset, length, shareable);
} catch (Throwable t) {
Log.w(TAG, t);
return null;
}
}
@RequiresApi(api = Build.VERSION_CODES.GINGERBREAD_MR1)
@Override
public Point init(Context context, Uri uri) throws Exception {
Log.w(TAG, "Init!");
if (!PartAuthority.isLocalUri(uri)) {
passthrough = new SkiaImageRegionDecoder();
return passthrough.init(context, uri);
}
MasterSecret masterSecret = KeyCachingService.getMasterSecret(context);
if (masterSecret == null) {
throw new IllegalStateException("No master secret available...");
}
InputStream inputStream = PartAuthority.getAttachmentStream(context, masterSecret, uri);
this.bitmapRegionDecoder = BitmapRegionDecoder.newInstance(inputStream, false);
inputStream.close();
return new Point(bitmapRegionDecoder.getWidth(), bitmapRegionDecoder.getHeight());
}
private Bitmap readBitmap (final int scalePercentage, final Rect cropRect) throws IOException {
if (100 % scalePercentage != 0) throw new IllegalArgumentException("scalePercentage " + scalePercentage + " is not a int ratio.");
final Options opts = new Options();
opts.inPurgeable = true;
opts.inInputShareable = true;
opts.inSampleSize = 100 / scalePercentage;
if (cropRect != null) {
final BitmapRegionDecoder dec = BitmapRegionDecoder.newInstance(openFileDescriptor().getFileDescriptor(), true);
try {
return dec.decodeRegion(cropRect, opts);
}
finally {
dec.recycle();
}
}
return BitmapFactory.decodeFileDescriptor(openFileDescriptor().getFileDescriptor(), null, opts);
}
/**
* Acquire a read lock to prevent decoding overlapping with recycling, then check the pool still
* exists and acquire a decoder to load the requested region. There is no check whether the pool
* currently has decoders, because it's guaranteed to have one decoder after {@link #init(Context, Uri)}
* is called and be null once {@link #recycle()} is called. In practice the view can't call this
* method until after {@link #init(Context, Uri)}, so there will be no blocking on an empty pool.
*/
@Override
@NonNull
public Bitmap decodeRegion(@NonNull Rect sRect, int sampleSize) {
debug("Decode region " + sRect + " on thread " + Thread.currentThread().getName());
if (sRect.width() < imageDimensions.x || sRect.height() < imageDimensions.y) {
lazyInit();
}
decoderLock.readLock().lock();
try {
if (decoderPool != null) {
BitmapRegionDecoder decoder = decoderPool.acquire();
try {
// Decoder can't be null or recycled in practice
if (decoder != null && !decoder.isRecycled()) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = sampleSize;
options.inPreferredConfig = bitmapConfig;
Bitmap bitmap = decoder.decodeRegion(sRect, options);
if (bitmap == null) {
throw new RuntimeException("Skia image decoder returned null bitmap - image format may not be supported");
}
return bitmap;
}
} finally {
if (decoder != null) {
decoderPool.release(decoder);
}
}
}
throw new IllegalStateException("Cannot decode region after decoder has been recycled");
} finally {
decoderLock.readLock().unlock();
}
}
private static byte[] transformByteArray(@NonNull byte[] data, @Nullable Rect cropRect, int rotation, boolean flip) throws IOException {
Stopwatch stopwatch = new Stopwatch("transform");
Bitmap in;
if (cropRect != null) {
BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(data, 0, data.length, false);
in = decoder.decodeRegion(cropRect, new BitmapFactory.Options());
decoder.recycle();
stopwatch.split("crop");
} else {
in = BitmapFactory.decodeByteArray(data, 0, data.length);
}
Bitmap out = in;
if (rotation != 0 || flip) {
Matrix matrix = new Matrix();
matrix.postRotate(rotation);
if (flip) {
matrix.postScale(-1, 1);
matrix.postTranslate(in.getWidth(), 0);
}
out = Bitmap.createBitmap(in, 0, 0, in.getWidth(), in.getHeight(), matrix, true);
}
byte[] transformedData = toJpegBytes(out);
stopwatch.split("transcode");
in.recycle();
out.recycle();
stopwatch.stop(TAG);
return transformedData;
}
@Override
public Point init(Context context, Uri uri) throws Exception {
Log.d(TAG, "Init!");
if (!PartAuthority.isLocalUri(uri)) {
passthrough = new SkiaImageRegionDecoder();
return passthrough.init(context, uri);
}
InputStream inputStream = PartAuthority.getAttachmentStream(context, uri);
this.bitmapRegionDecoder = BitmapRegionDecoder.newInstance(inputStream, false);
inputStream.close();
return new Point(bitmapRegionDecoder.getWidth(), bitmapRegionDecoder.getHeight());
}
/**
* Mock impl.
* @throws IOException
*/
@Implementation
public static BitmapRegionDecoder newInstance (final InputStream is, final boolean isShareable) throws IOException {
final BitmapRegionDecoder brd = spy(Robolectric.newInstanceOf(BitmapRegionDecoder.class));
final ShadowBitmapRegionDecoder shadowBrd = Robolectric.shadowOf_(brd);
shadowBrd.setSpy(brd);
shadowBrd.setInputStream(is);
shadowBrd.setSharable(isShareable);
System.err.println("is=" + is + " isShareable=" + isShareable);
return brd;
}
/**
* Acquire a read lock to prevent decoding overlapping with recycling, then check the pool still
* exists and acquire a decoder to load the requested region. There is no check whether the pool
* currently has decoders, because it's guaranteed to have one decoder after {@link #init(Context, Uri)}
* is called and be null once {@link #recycle()} is called. In practice the view can't call this
* method until after {@link #init(Context, Uri)}, so there will be no blocking on an empty pool.
*/
@Override
@NonNull
public Bitmap decodeRegion(@NonNull Rect sRect, int sampleSize) {
debug("Decode region " + sRect + " on thread " + Thread.currentThread().getName());
if (sRect.width() < imageDimensions.x || sRect.height() < imageDimensions.y) {
lazyInit();
}
decoderLock.readLock().lock();
try {
if (decoderPool != null) {
BitmapRegionDecoder decoder = decoderPool.acquire();
try {
// Decoder can't be null or recycled in practice
if (decoder != null && !decoder.isRecycled()) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = sampleSize;
options.inPreferredConfig = bitmapConfig;
Bitmap bitmap = decoder.decodeRegion(sRect, options);
if (bitmap == null) {
throw new RuntimeException("Skia image decoder returned null bitmap - image format may not be supported");
}
return bitmap;
}
} finally {
if (decoder != null) {
decoderPool.release(decoder);
}
}
}
throw new IllegalStateException("Cannot decode region after decoder has been recycled");
} finally {
decoderLock.readLock().unlock();
}
}
/**
* Acquire a read lock to prevent decoding overlapping with recycling, then check the pool still
* exists and acquire a decoder to load the requested region. There is no check whether the pool
* currently has decoders, because it's guaranteed to have one decoder after {@link #init(Context, Uri)}
* is called and be null once {@link #recycle()} is called. In practice the view can't call this
* method until after {@link #init(Context, Uri)}, so there will be no blocking on an empty pool.
*/
@Override
@NonNull
public Bitmap decodeRegion(@NonNull Rect sRect, int sampleSize) {
debug("Decode region " + sRect + " on thread " + Thread.currentThread().getName());
if (sRect.width() < imageDimensions.x || sRect.height() < imageDimensions.y) {
lazyInit();
}
decoderLock.readLock().lock();
try {
if (decoderPool != null) {
BitmapRegionDecoder decoder = decoderPool.acquire();
try {
// Decoder can't be null or recycled in practice
if (decoder != null && !decoder.isRecycled()) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = sampleSize;
options.inPreferredConfig = bitmapConfig;
Bitmap bitmap = decoder.decodeRegion(sRect, options);
if (bitmap == null) {
throw new RuntimeException("Skia image decoder returned null bitmap - image format may not be supported");
}
return bitmap;
}
} finally {
if (decoder != null) {
decoderPool.release(decoder);
}
}
}
throw new IllegalStateException("Cannot decode region after decoder has been recycled");
} finally {
decoderLock.readLock().unlock();
}
}
@Before
public void setUp() throws Exception {
final Random random = new Random();
random.setSeed(RANDOM_SEED);
mEncodedBytes = new byte[ENCODED_BYTES_LENGTH];
random.nextBytes(mEncodedBytes);
mPooledByteBuffer = new TrivialPooledByteBuffer(mEncodedBytes);
mBitmapPool = mock(BitmapPool.class);
mArtDecoder = new ArtDecoder(mBitmapPool, 1, new Pools.SynchronizedPool(1));
mByteBufferRef = CloseableReference.of(mPooledByteBuffer);
mEncodedImage = new EncodedImage(mByteBufferRef);
mEncodedImage.setImageFormat(DefaultImageFormats.JPEG);
mBitmap = MockBitmapFactory.create();
doReturn(mBitmap).when(mBitmapPool).get(MockBitmapFactory.DEFAULT_BITMAP_SIZE);
mBitmapFactoryDefaultAnswer =
new Answer<Bitmap>() {
@Override
public Bitmap answer(InvocationOnMock invocation) throws Throwable {
final BitmapFactory.Options options =
(BitmapFactory.Options) invocation.getArguments()[2];
options.outWidth = MockBitmapFactory.DEFAULT_BITMAP_WIDTH;
options.outHeight = MockBitmapFactory.DEFAULT_BITMAP_HEIGHT;
verifyBitmapFactoryOptions(options);
return options.inJustDecodeBounds ? null : mBitmap;
}
};
whenBitmapFactoryDecodeStream().thenAnswer(mBitmapFactoryDefaultAnswer);
mBitmapRegionDecoder = mock(BitmapRegionDecoder.class);
whenBitmapRegionDecoderNewInstance().thenReturn(mBitmapRegionDecoder);
ByteBuffer buf = mArtDecoder.mDecodeBuffers.acquire();
mTempStorage = buf.array();
mArtDecoder.mDecodeBuffers.release(buf);
}
private synchronized boolean markAsUnused(BitmapRegionDecoder decoder) {
for (Map.Entry<BitmapRegionDecoder, Boolean> entry : decoders.entrySet()) {
if (decoder == entry.getKey()) {
if (entry.getValue()) {
entry.setValue(false);
return true;
} else {
return false;
}
}
}
return false;
}
/**
* While there are decoders in the map, wait until each is available before acquiring,
* recycling and removing it. After this is called, any call to {@link #acquire()} will
* block forever, so this call should happen within a write lock, and all calls to
* {@link #acquire()} should be made within a read lock so they cannot end up blocking on
* the semaphore when it has no permits.
*/
private synchronized void recycle() {
while (!decoders.isEmpty()) {
BitmapRegionDecoder decoder = acquire();
decoder.recycle();
decoders.remove(decoder);
}
}
/**
* While there are decoders in the map, wait until each is available before acquiring,
* recycling and removing it. After this is called, any call to {@link #acquire()} will
* block forever, so this call should happen within a write lock, and all calls to
* {@link #acquire()} should be made within a read lock so they cannot end up blocking on
* the semaphore when it has no permits.
*/
private synchronized void recycle() {
while (!decoders.isEmpty()) {
BitmapRegionDecoder decoder = acquire();
decoder.recycle();
decoders.remove(decoder);
}
}
private synchronized BitmapRegionDecoder getNextAvailable() {
for (Map.Entry<BitmapRegionDecoder, Boolean> entry : decoders.entrySet()) {
if (!entry.getValue()) {
entry.setValue(true);
return entry.getKey();
}
}
return null;
}
/**
* Acquire a read lock to prevent decoding overlapping with recycling, then check the pool still
* exists and acquire a decoder to load the requested region. There is no check whether the pool
* currently has decoders, because it's guaranteed to have one decoder after {@link #init(Context, Uri)}
* is called and be null once {@link #recycle()} is called. In practice the view can't call this
* method until after {@link #init(Context, Uri)}, so there will be no blocking on an empty pool.
*/
@Override
@NonNull
public Bitmap decodeRegion(@NonNull Rect sRect, int sampleSize) {
debug("Decode region " + sRect + " on thread " + Thread.currentThread().getName());
if (sRect.width() < imageDimensions.x || sRect.height() < imageDimensions.y) {
lazyInit();
}
decoderLock.readLock().lock();
try {
if (decoderPool != null) {
BitmapRegionDecoder decoder = decoderPool.acquire();
try {
// Decoder can't be null or recycled in practice
if (decoder != null && !decoder.isRecycled()) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = sampleSize;
options.inPreferredConfig = bitmapConfig;
Bitmap bitmap = decoder.decodeRegion(sRect, options);
if (bitmap == null) {
throw new RuntimeException("Skia image decoder returned null bitmap - image format may not be supported");
}
return bitmap;
}
} finally {
if (decoder != null) {
decoderPool.release(decoder);
}
}
}
throw new IllegalStateException("Cannot decode region after decoder has been recycled");
} finally {
decoderLock.readLock().unlock();
}
}
private synchronized BitmapRegionDecoder getNextAvailable() {
for (Map.Entry<BitmapRegionDecoder, Boolean> entry : decoders.entrySet()) {
if (!entry.getValue()) {
entry.setValue(true);
return entry.getKey();
}
}
return null;
}
/**
* While there are decoders in the map, wait until each is available before acquiring,
* recycling and removing it. After this is called, any call to {@link #acquire()} will
* block forever, so this call should happen within a write lock, and all calls to
* {@link #acquire()} should be made within a read lock so they cannot end up blocking on
* the semaphore when it has no permits.
*/
private synchronized void recycle() {
while (!decoders.isEmpty()) {
BitmapRegionDecoder decoder = acquire();
decoder.recycle();
decoders.remove(decoder);
}
}
private synchronized BitmapRegionDecoder getNextAvailable() {
for (Map.Entry<BitmapRegionDecoder, Boolean> entry : decoders.entrySet()) {
if (!entry.getValue()) {
entry.setValue(true);
return entry.getKey();
}
}
return null;
}
private synchronized boolean markAsUnused(BitmapRegionDecoder decoder) {
for (Map.Entry<BitmapRegionDecoder, Boolean> entry : decoders.entrySet()) {
if (decoder == entry.getKey()) {
if (entry.getValue()) {
entry.setValue(false);
return true;
} else {
return false;
}
}
}
return false;
}
@Nullable
private synchronized BitmapRegionDecoder getNextAvailable() {
for (Map.Entry<BitmapRegionDecoder, Boolean> entry : decoders.entrySet()) {
if (!entry.getValue()) {
entry.setValue(true);
return entry.getKey();
}
}
return null;
}
@TargetApi(Build.VERSION_CODES.N)
private Palette getHotseatPalette() {
WallpaperManager wallpaperManager = WallpaperManager.getInstance(this);
if (AndroidVersion.isAtLeastNougat) {
try (ParcelFileDescriptor fd = wallpaperManager
.getWallpaperFile(WallpaperManager.FLAG_SYSTEM)) {
BitmapRegionDecoder decoder = BitmapRegionDecoder
.newInstance(fd.getFileDescriptor(), false);
int height = decoder.getHeight();
Rect decodeRegion = new Rect(0, (int) (height * (1f - HOTSEAT_FRACTION)),
decoder.getWidth(), height);
Bitmap bitmap = decoder.decodeRegion(decodeRegion, null);
decoder.recycle();
if (bitmap != null) {
return Palette.from(bitmap).clearFilters().generate();
}
} catch (IOException | NullPointerException e) {
e.printStackTrace();
}
}
Bitmap wallpaper = ((BitmapDrawable) wallpaperManager.getDrawable()).getBitmap();
return Palette.from(wallpaper)
.setRegion(0, (int) (wallpaper.getHeight() * (1f - HOTSEAT_FRACTION)),
wallpaper.getWidth(), wallpaper.getHeight())
.clearFilters()
.generate();
}
@TargetApi(Build.VERSION_CODES.N)
private Palette getStatusBarPalette() {
WallpaperManager wallpaperManager = WallpaperManager.getInstance(this);
int statusBarHeight = getResources()
.getDimensionPixelSize(R.dimen.status_bar_height);
if (AndroidVersion.isAtLeastNougat) {
try (ParcelFileDescriptor fd = wallpaperManager
.getWallpaperFile(WallpaperManager.FLAG_SYSTEM)) {
BitmapRegionDecoder decoder = BitmapRegionDecoder
.newInstance(fd.getFileDescriptor(), false);
Rect decodeRegion = new Rect(0, 0,
decoder.getWidth(), statusBarHeight);
Bitmap bitmap = decoder.decodeRegion(decodeRegion, null);
decoder.recycle();
if (bitmap != null) {
return Palette.from(bitmap).clearFilters().generate();
}
} catch (IOException | NullPointerException e) {
e.printStackTrace();
}
}
Bitmap wallpaper = ((BitmapDrawable) wallpaperManager.getDrawable()).getBitmap();
return Palette.from(wallpaper)
.setRegion(0, 0, wallpaper.getWidth(), statusBarHeight)
.clearFilters()
.generate();
}
public static Bitmap loadBitmapRegion(Path path,int sampleSize,Rect regionRect) throws IOException
{
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = sampleSize;
InputStream data = path.getFile().getInputStream();
try
{
if(Build.VERSION.SDK_INT>= Build.VERSION_CODES.GINGERBREAD_MR1)
{
BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(data, true);
try
{
return decoder.decodeRegion(regionRect, options);
}
finally
{
decoder.recycle();
}
}
else
return BitmapFactory.decodeStream(data, null, options);
}
finally
{
data.close();
}
}
public ImageRegionDecoder(@NonNull String imageUri, @NonNull Point imageSize, @Nullable ImageType imageType,
int exifOrientation, @NonNull BitmapRegionDecoder regionDecoder) {
this.imageUri = imageUri;
this.imageSize = imageSize;
this.imageType = imageType;
this.exifOrientation = exifOrientation;
this.regionDecoder = regionDecoder;
}
public static BitmapRegionDecoder createBitmapRegionDecoder(
ThreadPool.JobContext jc, String filePath, boolean shareable) {
try {
return BitmapRegionDecoder.newInstance(filePath, shareable);
} catch (Throwable t) {
Log.w(TAG, t);
return null;
}
}
private synchronized boolean markAsUnused(BitmapRegionDecoder decoder) {
for (Map.Entry<BitmapRegionDecoder, Boolean> entry : decoders.entrySet()) {
if (decoder == entry.getKey()) {
if (entry.getValue()) {
entry.setValue(false);
return true;
} else {
return false;
}
}
}
return false;
}
public static BitmapRegionDecoder createBitmapRegionDecoder(
ThreadPool.JobContext jc, InputStream is, boolean shareable) {
try {
return BitmapRegionDecoder.newInstance(is, shareable);
} catch (Throwable t) {
// We often cancel the creating of bitmap region decoder,
// so just log one line.
Log.w(TAG, "requestCreateBitmapRegionDecoder: " + t);
return null;
}
}