700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > android webp格式的图片 Android使用Glide加载SVG Webp格式的图片

android webp格式的图片 Android使用Glide加载SVG Webp格式的图片

时间:2021-05-12 05:58:31

相关推荐

android webp格式的图片 Android使用Glide加载SVG Webp格式的图片

项目中想使用一个框架实现常见的所有图片格式的加载,并且对代码的影响降到最低,Glide框架提供了很好的扩展,这里使用Glide+androidsvg+fresco实现加载GIF、SVG、WebP等多种格式的图片和动画文件。

实现原理

具体步骤

1、添加依赖

dependencies {

implementation 'com.github.bumptech.glide:glide:4.9.0'

annotationProcessor 'com.github.bumptech.glide:compiler:4.9.0'

implementation 'com.caverock:androidsvg-aar:1.3'

implementation "com.facebook.fresco:animated-webp:1.9.0"

}

2、SVG

1、自定义ResourceDecoder,将InputStream转为SVG对象:

import android.support.annotation.NonNull;

import com.bumptech.glide.load.Options;

import com.bumptech.glide.load.ResourceDecoder;

import com.bumptech.glide.load.engine.Resource;

import com.bumptech.glide.load.resource.SimpleResource;

import com.caverock.androidsvg.SVG;

import com.caverock.androidsvg.SVGParseException;

import java.io.IOException;

import java.io.InputStream;

/**

* Decodes an SVG internal representation from an {@link InputStream}.

*/

public class SvgDecoder implements ResourceDecoder {

@Override

public boolean handles(@NonNull InputStream source, @NonNull Options options) {

// TODO: Can we tell?

return true;

}

public Resource decode(@NonNull InputStream source, int width, int height,

@NonNull Options options)

throws IOException {

try {

SVG svg = SVG.getFromInputStream(source);

return new SimpleResource<>(svg);

} catch (SVGParseException ex) {

throw new IOException("Cannot load SVG from stream", ex);

}

}

}

2、自定义ResourceTranscoder,将SVG转为Drawable对象

import android.graphics.Picture;

import android.graphics.drawable.PictureDrawable;

import android.support.annotation.NonNull;

import android.support.annotation.Nullable;

import com.bumptech.glide.load.Options;

import com.bumptech.glide.load.engine.Resource;

import com.bumptech.glide.load.resource.SimpleResource;

import com.bumptech.glide.load.resource.transcode.ResourceTranscoder;

import com.caverock.androidsvg.SVG;

/**

* Convert the {@link SVG}'s internal representation to an Android-compatible one

* ({@link Picture}).

*/

public class SvgDrawableTranscoder implements ResourceTranscoder {

@Nullable

@Override

public Resource transcode(@NonNull Resource toTranscode,

@NonNull Options options) {

SVG svg = toTranscode.get();

Picture picture = svg.renderToPicture();

PictureDrawable drawable = new PictureDrawable(picture);

return new SimpleResource<>(drawable);

}

}

3、SVG格式不能硬解码,自定义RequestListener,在onResourceReady时设置ImageView为软解码

import android.graphics.drawable.PictureDrawable;

import android.widget.ImageView;

import com.bumptech.glide.load.DataSource;

import com.bumptech.glide.load.engine.GlideException;

import com.bumptech.glide.request.RequestListener;

import com.bumptech.glide.request.target.ImageViewTarget;

import com.bumptech.glide.request.target.Target;

/**

* Listener which updates the {@link ImageView} to be software rendered, because

* {@link com.caverock.androidsvg.SVG SVG}/{@link android.graphics.Picture Picture} can't render on

* a hardware backed {@link android.graphics.Canvas Canvas}.

*/

public class SvgSoftwareLayerSetter implements RequestListener {

@Override

public boolean onLoadFailed(GlideException e, Object model, Target target,

boolean isFirstResource) {

ImageView view = ((ImageViewTarget>) target).getView();

view.setLayerType(ImageView.LAYER_TYPE_NONE, null);

return false;

}

@Override

public boolean onResourceReady(PictureDrawable resource, Object model,

Target target, DataSource dataSource, boolean isFirstResource) {

ImageView view = ((ImageViewTarget>) target).getView();

view.setLayerType(ImageView.LAYER_TYPE_SOFTWARE, null);

return false;

}

}

3、WebP

1、WebpFrameLoader,加载每帧图片

import android.graphics.Bitmap;

import android.os.Handler;

import android.os.Looper;

import android.os.Message;

import android.os.SystemClock;

import com.bumptech.glide.Glide;

import com.bumptech.glide.RequestBuilder;

import com.bumptech.glide.RequestManager;

import com.bumptech.glide.gifdecoder.GifDecoder;

import com.bumptech.glide.load.Key;

import com.bumptech.glide.load.Transformation;

import com.bumptech.glide.load.engine.DiskCacheStrategy;

import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;

import com.bumptech.glide.request.RequestOptions;

import com.bumptech.glide.request.target.SimpleTarget;

import com.bumptech.glide.request.transition.Transition;

import com.bumptech.glide.util.Preconditions;

import com.bumptech.glide.util.Util;

import java.nio.ByteBuffer;

import java.security.MessageDigest;

import java.util.ArrayList;

import java.util.List;

import java.util.UUID;

public class WebpFrameLoader {

final RequestManager requestManager;

private final GifDecoder gifDecoder;

private final Handler handler;

private final List callbacks;

private final BitmapPool bitmapPool;

private boolean isRunning;

private boolean isLoadPending;

private boolean startFromFirstFrame;

private RequestBuilder requestBuilder;

private DelayTarget current;

private boolean isCleared;

private DelayTarget next;

private Bitmap firstFrame;

private Transformation transformation;

public WebpFrameLoader(Glide glide, GifDecoder gifDecoder, int width, int height, Transformation transformation, Bitmap firstFrame) {

this(glide.getBitmapPool(), Glide.with(glide.getContext()), gifDecoder, null, getRequestBuilder(Glide.with(glide.getContext()), width, height), transformation, firstFrame);

}

WebpFrameLoader(BitmapPool bitmapPool, RequestManager requestManager, GifDecoder gifDecoder, Handler handler, RequestBuilder requestBuilder, Transformation transformation, Bitmap firstFrame) {

this.callbacks = new ArrayList();

this.isRunning = false;

this.isLoadPending = false;

this.startFromFirstFrame = false;

this.requestManager = requestManager;

if (handler == null) {

handler = new Handler(Looper.getMainLooper(), new FrameLoaderCallback());

}

this.bitmapPool = bitmapPool;

this.handler = handler;

this.requestBuilder = requestBuilder;

this.gifDecoder = gifDecoder;

this.setFrameTransformation(transformation, firstFrame);

}

private static RequestBuilder getRequestBuilder(RequestManager requestManager, int width, int height) {

return requestManager.asBitmap().apply(RequestOptions.diskCacheStrategyOf(DiskCacheStrategy.NONE).skipMemoryCache(true).override(width, height));

}

void setFrameTransformation(Transformation transformation, Bitmap firstFrame) {

this.transformation = Preconditions.checkNotNull(transformation);

this.firstFrame = Preconditions.checkNotNull(firstFrame);

this.requestBuilder = this.requestBuilder.apply((new RequestOptions()).transform(transformation));

}

Transformation getFrameTransformation() {

return this.transformation;

}

Bitmap getFirstFrame() {

return this.firstFrame;

}

void subscribe(FrameCallback frameCallback) {

if (this.isCleared) {

throw new IllegalStateException("Cannot subscribe to a cleared frame loader");

} else {

boolean start = this.callbacks.isEmpty();

if (this.callbacks.contains(frameCallback)) {

throw new IllegalStateException("Cannot subscribe twice in a row");

} else {

this.callbacks.add(frameCallback);

if (start) {

this.start();

}

}

}

}

void unsubscribe(FrameCallback frameCallback) {

this.callbacks.remove(frameCallback);

if (this.callbacks.isEmpty()) {

this.stop();

}

}

int getWidth() {

return this.getCurrentFrame().getWidth();

}

int getHeight() {

return this.getCurrentFrame().getHeight();

}

int getSize() {

return this.gifDecoder.getByteSize() + this.getFrameSize();

}

int getCurrentIndex() {

return this.current != null ? this.current.index : -1;

}

private int getFrameSize() {

return Util.getBitmapByteSize(this.getCurrentFrame().getWidth(), this.getCurrentFrame().getHeight(), this.getCurrentFrame().getConfig());

}

ByteBuffer getBuffer() {

return this.gifDecoder.getData().asReadOnlyBuffer();

}

int getFrameCount() {

return this.gifDecoder.getFrameCount();

}

int getLoopCount() {

return this.gifDecoder.getTotalIterationCount();

}

private void start() {

if (!this.isRunning) {

this.isRunning = true;

this.isCleared = false;

this.loadNextFrame();

}

}

private void stop() {

this.isRunning = false;

}

void clear() {

this.callbacks.clear();

this.recycleFirstFrame();

this.stop();

if (this.current != null) {

this.requestManager.clear(this.current);

this.current = null;

}

if (this.next != null) {

this.requestManager.clear(this.next);

this.next = null;

}

this.gifDecoder.clear();

this.isCleared = true;

}

Bitmap getCurrentFrame() {

return this.current != null ? this.current.getResource() : this.firstFrame;

}

private void loadNextFrame() {

if (this.isRunning && !this.isLoadPending) {

if (this.startFromFirstFrame) {

this.gifDecoder.resetFrameIndex();

this.startFromFirstFrame = false;

}

this.isLoadPending = true;

int delay = this.gifDecoder.getNextDelay();

long targetTime = SystemClock.uptimeMillis() + (long) delay;

this.gifDecoder.advance();

this.next = new DelayTarget(this.handler, this.gifDecoder.getCurrentFrameIndex(), targetTime);

this.requestBuilder.clone().apply(RequestOptions.signatureOf(new FrameSignature())).load(this.gifDecoder).into(this.next);

}

}

private void recycleFirstFrame() {

if (this.firstFrame != null) {

this.bitmapPool.put(this.firstFrame);

this.firstFrame = null;

}

}

void setNextStartFromFirstFrame() {

Preconditions.checkArgument(!this.isRunning, "Can\'t restart a running animation");

this.startFromFirstFrame = true;

}

void onFrameReady(DelayTarget delayTarget) {

if (this.isCleared) {

this.handler.obtainMessage(2, delayTarget).sendToTarget();

} else {

if (delayTarget.getResource() != null) {

this.recycleFirstFrame();

DelayTarget previous = this.current;

this.current = delayTarget;

for (int i = this.callbacks.size() - 1; i >= 0; --i) {

FrameCallback cb = this.callbacks.get(i);

cb.onFrameReady();

}

if (previous != null) {

this.handler.obtainMessage(2, previous).sendToTarget();

}

}

this.isLoadPending = false;

this.loadNextFrame();

}

}

public interface FrameCallback {

void onFrameReady();

}

static class FrameSignature implements Key {

private final UUID uuid;

public FrameSignature() {

this(UUID.randomUUID());

}

FrameSignature(UUID uuid) {

this.uuid = uuid;

}

public boolean equals(Object o) {

if (o instanceof FrameSignature) {

FrameSignature other = (FrameSignature) o;

return other.uuid.equals(this.uuid);

} else {

return false;

}

}

public int hashCode() {

return this.uuid.hashCode();

}

public void updateDiskCacheKey(MessageDigest messageDigest) {

throw new UnsupportedOperationException("Not implemented");

}

}

static class DelayTarget extends SimpleTarget {

final int index;

private final Handler handler;

private final long targetTime;

private Bitmap resource;

DelayTarget(Handler handler, int index, long targetTime) {

this.handler = handler;

this.index = index;

this.targetTime = targetTime;

}

Bitmap getResource() {

return this.resource;

}

public void onResourceReady(Bitmap resource, Transition super Bitmap> transition) {

this.resource = resource;

Message msg = this.handler.obtainMessage(1, this);

this.handler.sendMessageAtTime(msg, this.targetTime);

}

}

private class FrameLoaderCallback implements Handler.Callback {

public static final int MSG_DELAY = 1;

public static final int MSG_CLEAR = 2;

FrameLoaderCallback() {

}

public boolean handleMessage(Message msg) {

DelayTarget target;

if (msg.what == 1) {

target = (DelayTarget) msg.obj;

WebpFrameLoader.this.onFrameReady(target);

return true;

} else {

if (msg.what == 2) {

target = (DelayTarget) msg.obj;

WebpFrameLoader.this.requestManager.clear(target);

}

return false;

}

}

}

}

2、WebpDrawable,用于播放每帧图片

import android.content.Context;

import android.content.res.Resources;

import android.graphics.Bitmap;

import android.graphics.Canvas;

import android.graphics.ColorFilter;

import android.graphics.Paint;

import android.graphics.PixelFormat;

import android.graphics.Rect;

import android.graphics.drawable.Animatable;

import android.graphics.drawable.Drawable;

import android.support.annotation.VisibleForTesting;

import android.view.Gravity;

import com.bumptech.glide.Glide;

import com.bumptech.glide.gifdecoder.GifDecoder;

import com.bumptech.glide.load.Transformation;

import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;

import com.bumptech.glide.util.Preconditions;

import java.nio.ByteBuffer;

/**

* An animated {@link android.graphics.drawable.Drawable} that plays the frames of an animated Webp.

*/

public class WebpDrawable extends Drawable implements WebpFrameLoader.FrameCallback, Animatable {

public static final int LOOP_FOREVER = -1;

public static final int LOOP_INTRINSIC = 0;

private final GifState state;

private boolean isRunning;

private boolean isStarted;

private boolean isRecycled;

private boolean isVisible;

private int loopCount;

private int maxLoopCount;

private boolean applyGravity;

private Paint paint;

private Rect destRect;

public WebpDrawable(Context context, GifDecoder gifDecoder, BitmapPool bitmapPool, Transformation frameTransformation, int targetFrameWidth, int targetFrameHeight, Bitmap firstFrame) {

this(new GifState(bitmapPool, new WebpFrameLoader(Glide.get(context), gifDecoder, targetFrameWidth, targetFrameHeight, frameTransformation, firstFrame)));

}

WebpDrawable(GifState state) {

this.isVisible = true;

this.maxLoopCount = -1;

this.state = Preconditions.checkNotNull(state);

}

@VisibleForTesting

WebpDrawable(WebpFrameLoader frameLoader, BitmapPool bitmapPool, Paint paint) {

this(new GifState(bitmapPool, frameLoader));

this.paint = paint;

}

public int getSize() {

return this.state.frameLoader.getSize();

}

public Bitmap getFirstFrame() {

return this.state.frameLoader.getFirstFrame();

}

public void setFrameTransformation(Transformation frameTransformation, Bitmap firstFrame) {

this.state.frameLoader.setFrameTransformation(frameTransformation, firstFrame);

}

public Transformation getFrameTransformation() {

return this.state.frameLoader.getFrameTransformation();

}

public ByteBuffer getBuffer() {

return this.state.frameLoader.getBuffer();

}

public int getFrameCount() {

return this.state.frameLoader.getFrameCount();

}

public int getFrameIndex() {

return this.state.frameLoader.getCurrentIndex();

}

private void resetLoopCount() {

this.loopCount = 0;

}

public void startFromFirstFrame() {

Preconditions.checkArgument(!this.isRunning, "You cannot restart a currently running animation.");

this.state.frameLoader.setNextStartFromFirstFrame();

this.start();

}

public void start() {

this.isStarted = true;

this.resetLoopCount();

if (this.isVisible) {

this.startRunning();

}

}

public void stop() {

this.isStarted = false;

this.stopRunning();

}

private void startRunning() {

Preconditions.checkArgument(!this.isRecycled, "You cannot start a recycled Drawable. Ensure thatyou clear any references to the Drawable when clearing the corresponding request.");

if (this.state.frameLoader.getFrameCount() == 1) {

this.invalidateSelf();

} else if (!this.isRunning) {

this.isRunning = true;

this.state.frameLoader.subscribe(this);

this.invalidateSelf();

}

}

private void stopRunning() {

this.isRunning = false;

this.state.frameLoader.unsubscribe(this);

}

public boolean setVisible(boolean visible, boolean restart) {

Preconditions.checkArgument(!this.isRecycled, "Cannot change the visibility of a recycled resource. Ensure that you unset the Drawable from your View before changing the View\'s visibility.");

this.isVisible = visible;

if (!visible) {

this.stopRunning();

} else if (this.isStarted) {

this.startRunning();

}

return super.setVisible(visible, restart);

}

public int getIntrinsicWidth() {

return this.state.frameLoader.getWidth();

}

public int getIntrinsicHeight() {

return this.state.frameLoader.getHeight();

}

public boolean isRunning() {

return this.isRunning;

}

void setIsRunning(boolean isRunning) {

this.isRunning = isRunning;

}

protected void onBoundsChange(Rect bounds) {

super.onBoundsChange(bounds);

this.applyGravity = true;

}

public void draw(Canvas canvas) {

if (!this.isRecycled) {

if (this.applyGravity) {

Gravity.apply(GifState.GRAVITY, this.getIntrinsicWidth(), this.getIntrinsicHeight(), this.getBounds(), this.getDestRect());

this.applyGravity = false;

}

Bitmap currentFrame = this.state.frameLoader.getCurrentFrame();

canvas.drawBitmap(currentFrame, null, this.getDestRect(), this.getPaint());

}

}

public void setAlpha(int i) {

this.getPaint().setAlpha(i);

}

public void setColorFilter(ColorFilter colorFilter) {

this.getPaint().setColorFilter(colorFilter);

}

private Rect getDestRect() {

if (this.destRect == null) {

this.destRect = new Rect();

}

return this.destRect;

}

private Paint getPaint() {

if (this.paint == null) {

this.paint = new Paint(2);

}

return this.paint;

}

public int getOpacity() {

return PixelFormat.TRANSPARENT;

}

public void onFrameReady() {

if (this.getCallback() == null) {

this.stop();

this.invalidateSelf();

} else {

this.invalidateSelf();

if (this.getFrameIndex() == this.getFrameCount() - 1) {

++this.loopCount;

}

if (this.maxLoopCount != LOOP_FOREVER && this.loopCount >= this.maxLoopCount) {

this.stop();

}

}

}

public ConstantState getConstantState() {

return this.state;

}

public void recycle() {

this.isRecycled = true;

this.state.frameLoader.clear();

}

boolean isRecycled() {

return this.isRecycled;

}

public void setLoopCount(int loopCount) {

if (loopCount <= 0 && loopCount != LOOP_FOREVER && loopCount != LOOP_INTRINSIC) {

throw new IllegalArgumentException("Loop count must be greater than 0, or equal to LOOP_FOREVER, or equal to LOOP_INTRINSIC");

} else {

if (loopCount == LOOP_INTRINSIC) {

int intrinsicCount = this.state.frameLoader.getLoopCount();

this.maxLoopCount = intrinsicCount == LOOP_INTRINSIC ? LOOP_FOREVER : intrinsicCount;

} else {

this.maxLoopCount = loopCount;

}

}

}

static class GifState extends ConstantState {

static final int GRAVITY = Gravity.FILL;

final BitmapPool bitmapPool;

final WebpFrameLoader frameLoader;

public GifState(BitmapPool bitmapPool, WebpFrameLoader frameLoader) {

this.bitmapPool = bitmapPool;

this.frameLoader = frameLoader;

}

public Drawable newDrawable(Resources res) {

return this.newDrawable();

}

public Drawable newDrawable() {

return new WebpDrawable(this);

}

public int getChangingConfigurations() {

return 0;

}

}

}

3、WebpDecoder 继承 GifDecoder,从InputStream源和byte []中读取图像数据。

import android.graphics.Bitmap;

import android.graphics.Canvas;

import android.support.annotation.NonNull;

import com.bumptech.glide.gifdecoder.GifDecoder;

import com.bumptech.glide.gifdecoder.GifHeader;

import com.facebook.animated.webp.WebPFrame;

import com.facebook.animated.webp.WebPImage;

import com.facebook.imagepipeline.animated.base.AnimatedDrawableFrameInfo;

import java.io.InputStream;

import java.nio.ByteBuffer;

/**

* 可以从InputStream源和byte []中读取图像数据。

*/

public class WebpDecoder implements GifDecoder {

private WebPImage mWebPImage;

private BitmapProvider mProvider;

private int mFramePointer;

private int[] mFrameDurations;

private int downsampledWidth;

private int downsampledHeight;

private boolean[] mKeyFrame;

private int mSampleSize;

// 缓存上一帧,用于非关键帧

private Bitmap mCacheBmp;

public WebpDecoder(BitmapProvider provider, WebPImage webPImage, int sampleSize) {

mProvider = provider;

mWebPImage = webPImage;

mFrameDurations = webPImage.getFrameDurations();

mKeyFrame = new boolean[mFrameDurations.length];

downsampledWidth = webPImage.getWidth() / sampleSize;

downsampledHeight = webPImage.getHeight() / sampleSize;

mSampleSize = sampleSize;

}

@Override

public int getWidth() {

return mWebPImage.getWidth();

}

@Override

public int getHeight() {

return mWebPImage.getHeight();

}

@Override

public ByteBuffer getData() {

return null;

}

@Override

public int getStatus() {

return 0;

}

@Override

public void advance() {

mFramePointer = (mFramePointer + 1) % mWebPImage.getFrameCount();

}

@Override

public int getDelay(int n) {

int delay = -1;

if ((n >= 0) && (n < mFrameDurations.length)) {

delay = mFrameDurations[n];

}

return delay;

}

@Override

public int getNextDelay() {

if (mFrameDurations.length == 0 || mFramePointer < 0) {

return 0;

}

return getDelay(mFramePointer);

}

@Override

public int getFrameCount() {

return mWebPImage.getFrameCount();

}

@Override

public int getCurrentFrameIndex() {

return mFramePointer;

}

@Override

public void resetFrameIndex() {

mFramePointer = -1;

}

@Override

public int getLoopCount() {

return mWebPImage.getLoopCount();

}

@Override

public int getNetscapeLoopCount() {

return mWebPImage.getLoopCount();

}

@Override

public int getTotalIterationCount() {

if (mWebPImage.getLoopCount() == 0) {

return TOTAL_ITERATION_COUNT_FOREVER;

}

return mWebPImage.getFrameCount() + 1;

}

@Override

public int getByteSize() {

return mWebPImage.getSizeInBytes();

}

@Override

public Bitmap getNextFrame() {

Bitmap result = mProvider.obtain(downsampledWidth, downsampledHeight, Bitmap.Config.ARGB_8888);

int currentIndex = getCurrentFrameIndex();

WebPFrame currentFrame = mWebPImage.getFrame(currentIndex);

// render key frame

if (isKeyFrame(currentIndex)) {

mKeyFrame[currentIndex] = true;

currentFrame.renderFrame(downsampledWidth, downsampledHeight, result);

mCacheBmp = result;

} else {

int frameW = currentFrame.getWidth() / mSampleSize;

int frameH = currentFrame.getHeight() / mSampleSize;

int offX = currentFrame.getXOffset() / mSampleSize;

int offY = currentFrame.getYOffset() / mSampleSize;

Canvas canvas = new Canvas(result);

canvas.drawBitmap(mCacheBmp, 0, 0, null);

Bitmap frameBmp = mProvider.obtain(frameW, frameH, Bitmap.Config.ARGB_8888);

currentFrame.renderFrame(frameW, frameH, frameBmp);

canvas.drawBitmap(frameBmp, offX, offY, null);

mProvider.release(frameBmp);

mCacheBmp = result;

}

currentFrame.dispose();

return result;

}

private boolean isKeyFrame(int index) {

if (index == 0) {

return true;

}

AnimatedDrawableFrameInfo curFrameInfo = mWebPImage.getFrameInfo(index);

AnimatedDrawableFrameInfo prevFrameInfo = mWebPImage.getFrameInfo(index - 1);

if (curFrameInfo.blendOperation == AnimatedDrawableFrameInfo.BlendOperation.NO_BLEND

&& isFullFrame(curFrameInfo)) {

return true;

} else {

return prevFrameInfo.disposalMethod == AnimatedDrawableFrameInfo.DisposalMethod.DISPOSE_TO_BACKGROUND

&& isFullFrame(prevFrameInfo);

}

}

private boolean isFullFrame(AnimatedDrawableFrameInfo info) {

return info.yOffset == 0 && info.xOffset == 0

&& mWebPImage.getHeight() == info.width

&& mWebPImage.getWidth() == info.height;

}

@Override

public int read(InputStream inputStream, int i) {

return 0;

}

@Override

public void clear() {

mWebPImage.dispose();

mWebPImage = null;

}

@Override

public void setData(GifHeader gifHeader, byte[] bytes) {

}

@Override

public void setData(GifHeader gifHeader, ByteBuffer byteBuffer) {

}

@Override

public void setData(GifHeader gifHeader, ByteBuffer byteBuffer, int i) {

}

@Override

public int read(byte[] bytes) {

return 0;

}

@Override

public void setDefaultBitmapConfig(@NonNull Bitmap.Config format) {

}

}

4、WebpBytebufferDecoder和WebpResourceDecoder,将数据转为WebpDrawable对象

WebpBytebufferDecoder

import android.content.Context;

import android.graphics.Bitmap;

import android.support.annotation.NonNull;

import android.support.annotation.Nullable;

import android.util.Log;

import com.bumptech.glide.Glide;

import com.bumptech.glide.load.ImageHeaderParser;

import com.bumptech.glide.load.ImageHeaderParserUtils;

import com.bumptech.glide.load.Options;

import com.bumptech.glide.load.ResourceDecoder;

import com.bumptech.glide.load.Transformation;

import com.bumptech.glide.load.engine.Initializable;

import com.bumptech.glide.load.engine.Resource;

import com.bumptech.glide.load.engine.bitmap_recycle.ArrayPool;

import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;

import com.bumptech.glide.load.resource.UnitTransformation;

import com.bumptech.glide.load.resource.drawable.DrawableResource;

import com.bumptech.glide.load.resource.gif.GifBitmapProvider;

import com.facebook.animated.webp.WebPImage;

import com.facebook.soloader.SoLoader;

import java.io.IOException;

import java.nio.ByteBuffer;

import java.util.List;

/**

* An {@link com.bumptech.glide.load.ResourceDecoder} that decodes {@link

* WebpDrawable} from {@link java.io.InputStream} data.

*/

public class WebpBytebufferDecoder implements ResourceDecoder {

public final String TAG = "WebpBytebufferDecoder";

private final List mParsers;

private final Context mContext;

private final BitmapPool mBitmapPool;

private final GifBitmapProvider mProvider;

private final ArrayPool mByteArrayPool;

public WebpBytebufferDecoder(Context context, Glide glide) {

this(context, glide.getRegistry().getImageHeaderParsers(), glide.getArrayPool(),

glide.getBitmapPool());

// if not init Soloader, will get error when decode

try {

SoLoader.init(context, 0);

} catch (IOException e) {

Log.v(TAG, "Failed to init SoLoader", e);

}

}

public WebpBytebufferDecoder(Context context, List parsers, ArrayPool byteArrayPool, BitmapPool bitmapPool) {

mContext = context.getApplicationContext();

mParsers = parsers;

mBitmapPool = bitmapPool;

mProvider = new GifBitmapProvider(bitmapPool, byteArrayPool);

mByteArrayPool = byteArrayPool;

}

private static int getSampleSize(int srcWidth, int srcHeight, int targetWidth, int targetHeight) {

int exactSampleSize = Math.min(srcHeight / targetHeight,

srcWidth / targetWidth);

int powerOfTwoSampleSize = exactSampleSize == 0 ? 0 : Integer.highestOneBit(exactSampleSize);

// Although functionally equivalent to 0 for BitmapFactory, 1 is a safer default for our code

// than 0.

int sampleSize = Math.max(1, powerOfTwoSampleSize);

return sampleSize;

}

@Override

public boolean handles(@NonNull ByteBuffer buffer, @NonNull Options options) throws IOException {

ImageHeaderParser.ImageType type = ImageHeaderParserUtils.getType(mParsers, buffer);

return type == ImageHeaderParser.ImageType.WEBP || type == ImageHeaderParser.ImageType.WEBP_A;

}

@Nullable

@Override

public Resource decode(@NonNull ByteBuffer buffer, int width, int height, @NonNull Options options) {

byte[] arr;

if (buffer.hasArray())

arr = buffer.array();

else {

arr = new byte[buffer.capacity()];

buffer.get(arr);

}

WebPImage webp = WebPImage.create(arr);

int sampleSize = getSampleSize(webp.getWidth(), webp.getHeight(), width, height);

WebpDecoder webpDecoder = new WebpDecoder(mProvider, webp, sampleSize);

Bitmap firstFrame = webpDecoder.getNextFrame();

if (firstFrame == null) {

return null;

}

Transformation unitTransformation = UnitTransformation.get();

return new WebpDrawableResource(new WebpDrawable(mContext, webpDecoder, mBitmapPool, unitTransformation, width, height,

firstFrame));

}

public class WebpDrawableResource extends DrawableResource implements Initializable {

public WebpDrawableResource(WebpDrawable drawable) {

super(drawable);

}

public Class getResourceClass() {

return WebpDrawable.class;

}

public int getSize() {

return drawable.getSize();

}

public void recycle() {

}

public void initialize() {

}

}

}

public class WebpBytebufferDecoder implements ResourceDecoder {

public final String TAG = "WebpBytebufferDecoder";

private final List mParsers;

private final Context mContext;

private final BitmapPool mBitmapPool;

private final GifBitmapProvider mProvider;

private final ArrayPool mByteArrayPool;

public WebpBytebufferDecoder(Context context, Glide glide) {

this(context, glide.getRegistry().getImageHeaderParsers(), glide.getArrayPool(),

glide.getBitmapPool());

// if not init Soloader, will get error when decode

try {

SoLoader.init(context, 0);

} catch (IOException e) {

Log.v(TAG, "Failed to init SoLoader", e);

}

}

public WebpBytebufferDecoder(Context context, List parsers, ArrayPool byteArrayPool, BitmapPool bitmapPool) {

mContext = context.getApplicationContext();

mParsers = parsers;

mBitmapPool = bitmapPool;

mProvider = new GifBitmapProvider(bitmapPool, byteArrayPool);

mByteArrayPool = byteArrayPool;

}

private static int getSampleSize(int srcWidth, int srcHeight, int targetWidth, int targetHeight) {

int exactSampleSize = Math.min(srcHeight / targetHeight,

srcWidth / targetWidth);

int powerOfTwoSampleSize = exactSampleSize == 0 ? 0 : Integer.highestOneBit(exactSampleSize);

// Although functionally equivalent to 0 for BitmapFactory, 1 is a safer default for our code

// than 0.

int sampleSize = Math.max(1, powerOfTwoSampleSize);

return sampleSize;

}

@Override

public boolean handles(@NonNull ByteBuffer buffer, @NonNull Options options) throws IOException {

ImageHeaderParser.ImageType type = ImageHeaderParserUtils.getType(mParsers, buffer);

return type == ImageHeaderParser.ImageType.WEBP || type == ImageHeaderParser.ImageType.WEBP_A;

}

@Nullable

@Override

public Resource decode(@NonNull ByteBuffer buffer, int width, int height, @NonNull Options options) {

byte[] arr;

if (buffer.hasArray())

arr = buffer.array();

else {

arr = new byte[buffer.capacity()];

buffer.get(arr);

}

WebPImage webp = WebPImage.create(arr);

int sampleSize = getSampleSize(webp.getWidth(), webp.getHeight(), width, height);

WebpDecoder webpDecoder = new WebpDecoder(mProvider, webp, sampleSize);

Bitmap firstFrame = webpDecoder.getNextFrame();

if (firstFrame == null) {

return null;

}

Transformation unitTransformation = UnitTransformation.get();

return new WebpDrawableResource(new WebpDrawable(mContext, webpDecoder, mBitmapPool, unitTransformation, width, height,

firstFrame));

}

public class WebpDrawableResource extends DrawableResource implements Initializable {

public WebpDrawableResource(WebpDrawable drawable) {

super(drawable);

}

public Class getResourceClass() {

return WebpDrawable.class;

}

public int getSize() {

return drawable.getSize();

}

public void recycle() {

}

public void initialize() {

}

}

}

WebpResourceDecoder

import android.content.Context;

import android.graphics.Bitmap;

import android.support.annotation.Nullable;

import android.util.Log;

import com.bumptech.glide.Glide;

import com.bumptech.glide.load.ImageHeaderParser;

import com.bumptech.glide.load.ImageHeaderParserUtils;

import com.bumptech.glide.load.Options;

import com.bumptech.glide.load.ResourceDecoder;

import com.bumptech.glide.load.Transformation;

import com.bumptech.glide.load.engine.Initializable;

import com.bumptech.glide.load.engine.Resource;

import com.bumptech.glide.load.engine.bitmap_recycle.ArrayPool;

import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;

import com.bumptech.glide.load.resource.UnitTransformation;

import com.bumptech.glide.load.resource.drawable.DrawableResource;

import com.bumptech.glide.load.resource.gif.GifBitmapProvider;

import com.facebook.animated.webp.WebPImage;

import com.facebook.soloader.SoLoader;

import java.io.ByteArrayOutputStream;

import java.io.IOException;

import java.io.InputStream;

import java.util.List;

/**

* Decodes {@link Bitmap}s from {@link WebpDecoder}s representing a particular frame of a particular

* WEBP image.

*/

public class WebpResourceDecoder implements ResourceDecoder {

public final String TAG = "WebpResourceDecoder";

private final List mParsers;

private final ArrayPool mByteArrayPool;

private final Context mContext;

private final BitmapPool mBitmapPool;

private final GifBitmapProvider mProvider;

public WebpResourceDecoder(Context context, Glide glide) {

this(context, glide.getRegistry().getImageHeaderParsers(), glide.getArrayPool(),

glide.getBitmapPool());

// if not init Soloader, will get error when decode

try {

SoLoader.init(context, 0);

} catch (IOException e) {

Log.v(TAG, "Failed to init SoLoader", e);

}

}

public WebpResourceDecoder(Context context) {

this(context, Glide.get(context).getRegistry().getImageHeaderParsers(), Glide.get(context).getArrayPool(),

Glide.get(context).getBitmapPool());

// if not init Soloader, will get error when decode

try {

SoLoader.init(context, 0);

} catch (IOException e) {

Log.v(TAG, "Failed to init SoLoader", e);

}

}

public WebpResourceDecoder(Context context, List parsers, ArrayPool byteArrayPool, BitmapPool bitmapPool) {

mContext = context.getApplicationContext();

mParsers = parsers;

mByteArrayPool = byteArrayPool;

mBitmapPool = bitmapPool;

mProvider = new GifBitmapProvider(bitmapPool, byteArrayPool);

}

private static int getSampleSize(int srcWidth, int srcHeight, int targetWidth, int targetHeight) {

int exactSampleSize = Math.min(srcHeight / targetHeight,

srcWidth / targetWidth);

int powerOfTwoSampleSize = exactSampleSize == 0 ? 0 : Integer.highestOneBit(exactSampleSize);

// Although functionally equivalent to 0 for BitmapFactory, 1 is a safer default for our code

// than 0.

int sampleSize = Math.max(1, powerOfTwoSampleSize);

return sampleSize;

}

@Override

public boolean handles(InputStream inputStream, Options options) throws IOException {

ImageHeaderParser.ImageType type = ImageHeaderParserUtils.getType(mParsers, inputStream, mByteArrayPool);

return type == ImageHeaderParser.ImageType.WEBP || type == ImageHeaderParser.ImageType.WEBP_A;

}

@Nullable

@Override

public Resource decode(InputStream inputStream, int width, int height, Options options) throws IOException {

ByteArrayOutputStream swapStream = new ByteArrayOutputStream();

byte[] buff = new byte[100]; //buff用于存放循环读取的临时数据

int rc = 0;

while ((rc = inputStream.read(buff, 0, 100)) > 0) {

swapStream.write(buff, 0, rc);

}

byte[] in_b = swapStream.toByteArray();

WebPImage webp = WebPImage.create(in_b);

int sampleSize = getSampleSize(webp.getWidth(), webp.getHeight(), width, height);

WebpDecoder webpDecoder = new WebpDecoder(mProvider, webp, sampleSize);

Bitmap firstFrame = webpDecoder.getNextFrame();

if (firstFrame == null) {

return null;

}

Transformation unitTransformation = UnitTransformation.get();

return new WebpDrawableResource(new WebpDrawable(mContext, webpDecoder, mBitmapPool, unitTransformation, width, height,

firstFrame));

}

public class WebpDrawableResource extends DrawableResource implements Initializable {

public WebpDrawableResource(WebpDrawable drawable) {

super(drawable);

}

public Class getResourceClass() {

return WebpDrawable.class;

}

public int getSize() {

return drawable.getSize();

}

public void recycle() {

}

public void initialize() {

}

}

}

5、WebpDrawableEncoder,将WebpDrawable转为OutPutStream

import android.support.annotation.NonNull;

import android.util.Log;

import com.bumptech.glide.load.EncodeStrategy;

import com.bumptech.glide.load.Options;

import com.bumptech.glide.load.ResourceEncoder;

import com.bumptech.glide.load.engine.Resource;

import com.bumptech.glide.util.ByteBufferUtil;

import java.io.File;

import java.io.IOException;

/**

* Writes the original bytes of a {@link WebpDrawable} to an

* {@link java.io.OutputStream}.

* @author Lee

* @date /4/18

*/

public class WebpDrawableEncoder implements ResourceEncoder {

private static final String TAG = WebpDrawableEncoder.class.getSimpleName();

@NonNull

@Override

public EncodeStrategy getEncodeStrategy(@NonNull Options options) {

return EncodeStrategy.SOURCE;

}

@Override

public boolean encode(@NonNull Resource data, @NonNull File file,

@NonNull Options options) {

WebpDrawable drawable = data.get();

boolean success = false;

try {

ByteBufferUtil.toFile(drawable.getBuffer(), file);

success = true;

} catch (IOException e) {

if (Log.isLoggable(TAG, Log.WARN)) {

Log.w(TAG, "Failed to encode Webp drawable data", e);

}

}

return success;

}

}

3、创建Module

自定义Module,注册SVG和WebpDrawble

Glide4.0在包的根路径下创建CustomModule,makeProject后会自动加载Module

@GlideModule

public class CustomModule extends AppGlideModule {

@Override

public void registerComponents(@NonNull Context context, @NonNull Glide glide,

@NonNull Registry registry) {

registry.register(WebpDrawable.class, new WebpDrawableEncoder())

.append(InputStream.class, WebpDrawable.class, new WebpResourceDecoder(context, glide))

.append(ByteBuffer.class, WebpDrawable.class, new WebpBytebufferDecoder(context, glide))

.register(SVG.class, PictureDrawable.class, new SvgDrawableTranscoder())

.append(InputStream.class, SVG.class, new SvgDecoder());

}

// Disable manifest parsing to avoid adding similar modules twice.

@Override

public boolean isManifestParsingEnabled() {

return false;

}

}

4、使用

// 加载本地图片

Glide.with(context).load(R.drawable.gif).into(view);

Glide.with(context).load("file:///android_asset/Spinner.svg").into(view);

Glide.with(context).load(R.drawable.webp).into(view);

// 加载网络图片

Glide.with(context).asGif().load("http://xxx.xxx.xxx.gif").into(view);

Glide.with(context).as(PictureDrawable.class)

.listener(new SvgSoftwareLayerSetter())

.load("http://xxx.xxx.xxx.svg").into(view);

Glide.with(context).load("http://xxx.xxx.xxx.webp").into(view);

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。