快速进阶
更新时间:2023-07-26
下面介绍Android端HEIF图片加载SDK配合Glide和Freso库的使用方法,以及SO后下载功能的使用方法。
配合Glide实现HEIF图片解码
- 在gradle中引入Glide库
implementation 'com.baidubce.mediasdk:libheif:1.1.0'
implementation 'com.github.bumptech.glide:glide:4.15.1'
annotationProcessor 'com.github.bumptech.glide:compiler:4.15.1'
- 基于百度智能云HEIF图片加载SDK编写Glide解码器插件
// 以Bytebuffer为输入
public class HeifByteBufferBitmapDecoder implements ResourceDecoder<ByteBuffer, Bitmap> {
private static final String TAG = "HeifByteBufferDecoder";
private BitmapPool bitmapPool;
public HeifByteBufferBitmapDecoder(BitmapPool pool) {
bitmapPool = Preconditions.checkNotNull(pool);
}
@Override
// 判断输入图片是否为HEIF格式
public boolean handles(@NonNull ByteBuffer source, @NonNull Options options) throws IOException {
byte[] header = new byte[13];
ByteStreams.readHeaderFromStream(13, ByteBufferUtil.toStream(source), header);
boolean isHeic = HeifDecoder.isHeic(header.length, header);
Log.i(TAG, "isHeic " + isHeic);
return isHeic;
}
@Override
// 使用百度智能云HEIF图片加载SDK进行解码
public Resource<Bitmap> decode(@NonNull ByteBuffer source, int width, int height,
@NonNull Options options) throws IOException {
byte[] bytes = ByteBufferUtil.toBytes(source);
HeifInfo heifInfo = new HeifInfo();
HeifDecoder.getInfo(bytes.length, bytes, heifInfo);
HeifSize heifSize = heifInfo.getFrameList().get(0);
int sourceWidth = heifSize.getWidth();
int sourceHeight = heifSize.getHeight();
// 缩放处理
int requestedWidth = width;
int requestedHeight = height;
DownsampleStrategy downsampleStrategy =
(DownsampleStrategy) options.get(DownsampleStrategy.OPTION);
DownsampleStrategy.SampleSizeRounding rounding
= downsampleStrategy.getSampleSizeRounding(sourceWidth, sourceHeight, requestedWidth, requestedHeight);
float exactScaleFactor = downsampleStrategy.getScaleFactor(sourceWidth, sourceHeight,
requestedWidth, requestedHeight);
int outWidth = round((double) (exactScaleFactor * (float) sourceWidth));
int outHeight = round((double) (exactScaleFactor * (float) sourceHeight));
int widthScaleFactor = sourceWidth / outWidth;
int heightScaleFactor = sourceHeight / outHeight;
int scaleFactor = rounding == DownsampleStrategy.SampleSizeRounding.MEMORY ?
Math.max(widthScaleFactor, heightScaleFactor) : Math.min(widthScaleFactor, heightScaleFactor);
int powerOfTwoSampleSize = Math.max(1, Integer.highestOneBit(scaleFactor));
if (rounding == DownsampleStrategy.SampleSizeRounding.MEMORY
&& (float) powerOfTwoSampleSize < 1.0F / exactScaleFactor) {
powerOfTwoSampleSize <<= 1;
}
Log.i(TAG, "heifSize: " + heifSize.getWidth() + "," + heifSize.getHeight() +
",sample size: " + powerOfTwoSampleSize);
// 创建Bitmap
Bitmap bitmap = Bitmap.createBitmap(sourceWidth / powerOfTwoSampleSize, sourceHeight / powerOfTwoSampleSize,
Bitmap.Config.ARGB_8888);
// 将HEIF图片解码为Bitmap
HeifDecoder.toRgba(bytes.length, bytes, bitmap);
return BitmapResource.obtain(bitmap, bitmapPool);
}
private static int round(double value) {
return (int) (value + 0.5);
}
}
// 以InputStream为输入,内部直接复用上面的HeifByteBufferBitmapDecoder
public class HeifStreamBitmapDecoder implements ResourceDecoder<InputStream, Bitmap> {
private static final String TAG = "HeifStreamBitmapDecoder";
private final List<ImageHeaderParser> parsers;
private final HeifByteBufferBitmapDecoder heifByteBufferBitmapDecoder;
private final ArrayPool arrayPool;
public HeifStreamBitmapDecoder(
List<ImageHeaderParser> parsers,
HeifByteBufferBitmapDecoder heifByteBufferBitmapDecoder,
ArrayPool arrayPool) {
this.parsers = parsers;
this.heifByteBufferBitmapDecoder = Preconditions.checkNotNull(heifByteBufferBitmapDecoder);
this.arrayPool = Preconditions.checkNotNull(arrayPool);
}
@Override
@Nullable
public Resource<Bitmap> decode(InputStream source, int width, int height, Options options)
throws IOException {
return heifByteBufferBitmapDecoder.decode(ByteBufferUtil.fromStream(source), width, height, options);
}
@Override
public boolean handles(InputStream source, Options options) throws IOException {
byte[] header = new byte[13];
ByteStreams.readHeaderFromStream(13, source, header);
boolean isheif = HeifDecoder.isHeic(header.length, header);
Log.i(TAG, "isHeic " + isheif);
return isheif;
}
}
- 注册解码器插件
@GlideModule
public class HeifGlideModule extends AppGlideModule {
@Override
public void registerComponents(@NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) {
// 添加刚才编写的两个解码器插件
HeifByteBufferBitmapDecoder heifByteBufferBitmapDecoder
= new HeifByteBufferBitmapDecoder(glide.getBitmapPool());
registry.prepend(ByteBuffer.class, Bitmap.class, heifByteBufferBitmapDecoder);
HeifStreamBitmapDecoder streamBitmapDecoder =
new HeifStreamBitmapDecoder(
registry.getImageHeaderParsers(), heifByteBufferBitmapDecoder, glide.getArrayPool());
registry.prepend(InputStream.class, Bitmap.class, streamBitmapDecoder);
}
// Disable manifest parsing to avoid adding similar modules twice.
@Override
public boolean isManifestParsingEnabled() {
return false;
}
}
- 使用
ImageView image = mRootView.findViewById(R.id.image_glide);
Glide.with(this)
.asBitmap()
.load(url)
.override(100, 100) // 缩放
.into(image)
配合Freso实现HEIF图片解码
- 在gradle中引入Freso库
implementation 'com.baidubce.mediasdk:libheif:1.1.0'
implementation 'com.facebook.fresco:fresco:2.6.0'
- 基于百度智能云HEIF加载SDK编写Freso解码器插件
public class HeifFrescoDecoder implements ImageDecoder {
private static final String TAG = "HeifFrescoDecoder";
@Override
public CloseableImage decode(
EncodedImage encodedImage,
int length,
QualityInfo qualityInfo,
ImageDecodeOptions options) {
// Decode the given encodedImage and return a
// corresponding (decoded) CloseableImage.
if (encodedImage == null) {
return null;
} else {
InputStream inputStream = encodedImage.getInputStream();
if (inputStream == null) {
return null;
}
Bitmap bitmap;
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
try {
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
byte[] heifData = outputStream.toByteArray();
// 获取HEIF元数据
HeifInfo heifInfo = new HeifInfo();
HeifDecoder.getInfo(heifData.length, heifData, heifInfo);
HeifSize heifSize = heifInfo.getFrameList().get(0);
int sampleSize = encodedImage.getSampleSize();
int width = heifSize.getWidth();
int height = heifSize.getHeight();
Log.i(TAG, "heifSize: " + heifSize.getWidth() + "," + heifSize.getHeight() + ", "
+ "out sample size: " + sampleSize);
// 创建Android Bitmap,考虑缩放
bitmap = Bitmap.createBitmap(width / sampleSize, height / sampleSize,
Bitmap.Config.ARGB_8888);
// 使用百度智能云HEIF图片加载SDK将HEIF图片解码为Bitmap
HeifDecoder.toRgba(heifData.length, heifData, bitmap);
CloseableReference<Bitmap> closeableReference =
CloseableReference.of(Preconditions.checkNotNull(bitmap),
SimpleBitmapReleaser.getInstance());
return new CloseableStaticBitmap(
closeableReference,
ImmutableQualityInfo.FULL_QUALITY,
encodedImage.getRotationAngle(),
encodedImage.getExifOrientation());
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
Closeables.closeQuietly(inputStream);
}
}
}
}
- 注册解码器插件
ImagePipelineConfig config = ImagePipelineConfig.newBuilder(this)
.setDownsampleEnabled(true)
.setImageDecoderConfig(ImageDecoderConfig.newBuilder()
.addDecodingCapability(DefaultImageFormats.HEIF,
new DefaultImageFormatChecker(),
new HeifFrescoDecoder()) // 添加前面编写的解码器插件
.build())
.build();
Fresco.initialize(this, config);
- 使用
SimpleDraweeView draweeView = (SimpleDraweeView) mRootView.findViewById(R.id.image_freso);
DraweeController draweeController = Fresco.newDraweeControllerBuilder()
.setImageRequest(
ImageRequestBuilder.newBuilderWithSource(Uri.parse(url))
.setCacheChoice(ImageRequest.CacheChoice.DEFAULT)
.setResizeOptions(ResizeOptions.forDimensions(100, 100)) // 缩放
.build())
.build();
draweeView.setController(draweeController);
在Demo工程的OnlineImageFragment.java中对上面两种用法都做了详细的展示,可以参考。
SO后下载
通过SO后下载,可以减少包体积增量。
- 配置gradle集成无SO的HEIF图片加载SDK
dependencies {
implementation 'com.baidubce.mediasdk:libheif-lite:1.1.0'
}
- 下载SDK压缩包,解压找到arm64-v8a_heif.zip和armeabi-v7a_heif.zip,这两个压缩包中保存有对应架构的SO文件。
- 将SO文件上传到您的服务器(或使用百度智能云对象存储BOS),并记录下载地址,例如 https://xxx.com/arm64-v8a_heif.zip。
- 调用HeifSoLoadManager从指定位置加载对应架构的SO,加载成功后再进行后续的解码
HeifSoLoadManager.getInstance(this).loadLibraries("https://xxx.com/arm64-v8a_heif.zip", "arm64-v8a", mLoadListener);
private HeifSoLoadManager.LoadListener mLoadListener = new HeifSoLoadManager.LoadListener() {
@Override
public void onLoadError(int errCode, String errMsg) {
Log.d(TAG, "external load library error " + errCode + errMsg);
}
@Override
public void onLibsDownloadCompleted() {
Log.d(TAG, "libs download completed.");
}
@Override
public void onLoadSuccess() {
Log.d(TAG, "external load library success.");
}
@Override
public void onLoadProgress(float progress) {
Log.d(TAG, "external load library so progress " + progress);
}
};