Because dynamic webp is used more and more, here is a way of loading processing. Currently, only the commonly used Android image loading libraries fresco
can directly load the animated webp. So how to deal with it? Make a record, otherwise the pit will be stepped for nothing. In essence, webp and gif are continuous pictures composed of a group of pictures. What if you want to parse each frame separately.
Android support
If you want to get the first frame of webp, you can directly use the following method on Android. The default is the first frame, but this method is only useful in 8.0 and above.
Bitmap bitmap = BitmapFactory.decodeFile(filename);
fresco control animated webp
The way to add fresco
support for webp is:
implementation 'com.facebook.fresco:fresco:1.13.0'
implementation 'com.facebook.fresco:webpsupport:1.13.0'
//implementation 'com.facebook.fresco:animated-gif:1.13.0'
After adding, you can load webp directly, or you can set to automatically start playing, as follows:
ImageRequestBuilder imageRequestBuilder = ImageRequestBuilder.newBuilderWithSource(uri);
PipelineDraweeControllerBuilder builder = Fresco.newDraweeControllerBuilder();
builder.setImageRequest(imageRequestBuilder.build());
builder.setAutoPlayAnimations(true);
builder.setControllerListener(new BaseControllerListener<ImageInfo>() {
@Override
public void onFinalImageSet(String id, ImageInfo imageInfo, Animatable animatable) {
//加载完成的监听
}
});
simpleDraweeView.setController(builder.build());
Up to this step is very simple, but what about loading the first frame of webp?
First comes the simplest one, similar to that BitmapFactory.decodeFile()
, fresco
there is also a method in the same, this method also only supports 8.0 and above
Bitmap bitmap = WebpSupportStatus
.loadWebpBitmapFactoryIfExists()
.decodeFile(filename, new BitmapFactory.Options());
So what else can be done? You can also use the following method. This method is used for fresco
subscribers, which can process the data after loading, can get every frame of webp, and then do the processing you want, the official website link .
ImageDecodeOptionsBuilder decodeOptionsBuilder = new ImageDecodeOptionsBuilder();
decodeOptionsBuilder.setDecodeAllFrames(true);
decodeOptionsBuilder.setBitmapConfig(Bitmap.Config.ARGB_8888);
ImagePipelineFactory.getInstance()
.getImagePipeline()
.fetchDecodedImage(ImageRequestBuilder
.newBuilderWithSource(Uri.fromFile(new File(filename)))
.setImageDecodeOptions(new ImageDecodeOptions(decodeOptionsBuilder))
.build()
, context)
.subscribe(new BaseDataSubscriber<CloseableReference<CloseableImage>>() {
@Override
protected void onNewResultImpl(DataSource<CloseableReference<CloseableImage>> dataSource) {
if (dataSource.getResult().get() instanceof CloseableAnimatedImage) {
CloseableAnimatedImage animatedImage = (CloseableAnimatedImage) dataSource.getResult().get();
if (animatedImage.getImage().getFrameCount() > 0) {
Bitmap frameBitmap = Bitmap.createBitmap(animatedImage.getWidth(), animatedImage.getHeight(), Bitmap.Config.ARGB_8888);
//添加透明通道
frameBitmap.eraseColor(Color.TRANSPARENT);
frameBitmap.setHasAlpha(true);
animatedImage.getImage().getFrame(0).renderFrame(animatedImage.getWidth(), animatedImage.getHeight(), frameBitmap);
try {
//保存成文件
File file = new File(filename);
File outFile = new File(file.getParent(), file.getName() + ".png");
FileOutputStream out = new FileOutputStream(outFile);
frameBitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
out.flush();
out.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
@Override
protected void onFailureImpl(DataSource<CloseableReference<CloseableImage>> dataSource) {
}
}, CallerThreadExecutor.getInstance());
There are three things to pay attention to:
- The bitmap solved by the following method by default does not have a transparent channel, and you need to process it again.
- The second is that the bitmap obtained by this method may be recycled after leaving this method. If you want to use it for a long time, it is recommended to copy it again by yourself. To save the time of decompression, I will save it directly as a copy. png file, and then use it, you can also copy the bitmap directly to use it.
- This method can decompress each frame, just select the number of frames you need to process.
Glide4 control GIF
Regarding GIF, the only mainstream loading libraries currently supported are Glide
and fresco
, which Picasso
are not supported by default.
Here, by the way, record how Glide4.0 loads Gif and controls playback. It GlideDrawableImageViewTarget
has been abandoned in Glide3 and can be used GifDrawable
to control it. There are methods to control GIF, and you can also get every frame of GIF to process. The defined interface method is there, and you can't leave it. Look at the interface and you will know Up.
public void setStartPlayGif(final boolean startPlayGif) {
Glide.with(context)
.asGif()
.load("gifFile")
.into(new ImageViewTarget<GifDrawable>(imageView) {
@Override
protected void setResource(@Nullable GifDrawable resource) {
if (resource != null) {
if(startPlayGif){
//resource.setLoopCount(1);
view.setImageDrawable(resource);
} else {
view.setImageBitmap(resource.getFirstFrame());
}
}
}
});
}