如何判断一张图片是否为GIF图
-
根据后缀名判断:一般服务器返回图片时都有后缀名,这个时候我们可以根据文件的后缀名来判断;但是根据后缀名判断不可靠,有可能是用户手动修改的文件后缀名
-
根据头信息判断:计算机存储数据是以二进制字节码存储,字节码的前几位标识了文件类型,也就是头信息;相对于gif我们可以取文件的前三个字节看是否为
0x474946
GIF文件 -
使用BitmapFactory.Options来判断:BitmapFactory.Options有一个属性
outMimeType
,这个属性就标识了文件的具体类型,我们可以根据这个mimeType来获取文件的类型private fun getBitmapType(bitmapPath: String) { val options = BitmapFactory.Options() options.inJustDecodeBounds = true BitmapFactory.decodeFile(bitmapPath, options) options.inJustDecodeBounds = false val mimeType = options.outMimeType Log.d("图片类型", mimeType) }
Glide如何判断一张图片是什么类型
首先,我们还是根据源码走吧
Glide.with(this).load(samplePath).into(sampleImageView)
我们从into()
方法开始,作为我们分析源码的入口
public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
Util.assertMainThread();
Preconditions.checkNotNull(view);
return into(
glideContext.buildImageViewTarget(view, transcodeClass),
/*targetListener=*/ null,
requestOptions);
}
private <Y extends Target<TranscodeType>> Y into() {
Request request = buildRequest(target, targetListener, options);
requestManager.clear(target);
target.setRequest(request);
requestManager.track(target, request);
return target;
}
先是创建了一个ImageViewTarget,然后创建一个Request,最后执行了这个请求,并返回target;主要逻辑应该是执行请求,那我们进入这个track方法
void track(@NonNull Target<?> target, @NonNull Request request) {
targetTracker.track(target);
requestTracker.runRequest(request);
}
继续进入runRequest逻辑
public void runRequest(@NonNull Request request) {
requests.add(request);
request.begin();
}
然后进入SingleRequest#begin()中的onSizeReady()
@Override
public void onSizeReady(int width, int height) {
loadStatus = engine.load(xxx);
}
public <R> LoadStatus load() {
EngineJob<R> engineJob =engineJobFactory.build(xxx);
DecodeJob<R> decodeJob =decodeJobFactory.build(xxxx);
jobs.put(key, engineJob);
engineJob.addCallback(cb);
engineJob.start(decodeJob);
return new LoadStatus(cb, engineJob);
}
这里创建了一些列解码任务,然后去执行任务,这里我们只需要去查看DecodeJob的run执行逻辑
public void run() {
runWrapped();
}
private void runWrapped() {
switch (runReason) {
case INITIALIZE:
stage = getNextStage(Stage.INITIALIZE);
currentGenerator = getNextGenerator();
runGenerators();
break;
case SWITCH_TO_SOURCE_SERVICE:
runGenerators();
break;
case DECODE_DATA:
decodeFromRetrievedData();
break;
default:
throw new IllegalStateException("Unrecognized run reason: " + runReason);
}
}
然后查看解码数据的逻辑decodeFromRetrievedData()
private void decodeFromRetrievedData() {
Resource<R> resource = null;
try {
resource = decodeFromData(currentFetcher, currentData, currentDataSource);
} catch (GlideException e) {
e.setLoggingDetails(currentAttemptingKey, currentDataSource);
throwables.add(e);
}
}
private <Data> Resource<R> decodeFromData(xxx) throws GlideException {
try {
long startTime = LogTime.getLogTime();
Resource<R> result = decodeFromFetcher(data, dataSource);
return result;
} finally {
fetcher.cleanup();
}
}
private <Data> Resource<R> decodeFromFetcher(xxx)throws GlideException {
LoadPath<Data, ?, R> path = decodeHelper.getLoadPath((Class<Data>) data.getClass());
return runLoadPath(data, dataSource, path);
}
private <Data, ResourceType> Resource<R> runLoadPath(xxx) throws GlideException {
Options options = getOptionsWithHardwareConfig(dataSource);
DataRewinder<Data> rewinder = glideContext.getRegistry().getRewinder(data);
try {
return path.load(
rewinder, options, width, height, new DecodeCallback<ResourceType>(dataSource));
} finally {
rewinder.cleanup();
}
}
然后查看path.load()
public Resource<Transcode> load(xxx) throws GlideException {
return loadWithExceptionList(rewinder, options, width, height, decodeCallback, throwables);
}
private Resource<Transcode> loadWithExceptionList(xxx) throws GlideException {
for (int i = 0, size = decodePaths.size(); i < size; i++) {
DecodePath<Data, ResourceType, Transcode> path = decodePaths.get(i);
result = path.decode(rewinder, width, height, options, decodeCallback);
}
return result;
}
public Resource<Transcode> decode(xxx) throws GlideException {
Resource<ResourceType> decoded = decodeResource(rewinder, width, height, options);
Resource<ResourceType> transformed = callback.onResourceDecoded(decoded);
return transcoder.transcode(transformed, options);
}
private Resource<ResourceType> decodeResource(xxx) throws GlideException {
return decodeResourceWithList(rewinder, width, height, options, exceptions);
}
private Resource<ResourceType> decodeResourceWithList(xxx) throws GlideException {
Resource<ResourceType> result = null;
for (int i = 0, size = decoders.size(); i < size; i++) {
ResourceDecoder<DataType, ResourceType> decoder = decoders.get(i);
try {
DataType data = rewinder.rewindAndGet();
if (decoder.handles(data, options)) {
data = rewinder.rewindAndGet();
result = decoder.decode(data, width, height, options);
}
} catch (IOException | RuntimeException | OutOfMemoryError e) {
exceptions.add(e);
}
}
return result;
}
主要的处理逻辑就在这里了,这里会获取到一个decoders列表,然后把sourceData让每一个decoder调用handles()
去尝试处理,如果这个decoder能够处理这个data,就会调用自己的decode方法去处理这个数据,然后得到一个Resource;而decoder的handles方法就是去判断文件类型的,而这decoders集合是在Glide初始化的时候添加的;那么gif文件的判断是ByteBufferGifDecoder
处理的
public boolean handles(@NonNull ByteBuffer source, @NonNull Options options) throws IOException {
return !options.get(GifOptions.DISABLE_ANIMATION)
&& ImageHeaderParserUtils.getType(parsers, source) == ImageType.GIF;
}
public static ImageType getType(xxx)throws IOException {
if (buffer == null) {
return ImageType.UNKNOWN;
}
for (int i = 0, size = parsers.size(); i < size; i++) {
ImageHeaderParser parser = parsers.get(i);
ImageType type = parser.getType(buffer);
if (type != ImageType.UNKNOWN) {
return type;
}
}
return ImageType.UNKNOWN;
}
这里时候全局的工具类ImageHeaderParserUtils来处理的,而getType这个静态方法是由ImageHeaderParser
接口来实现的,所以具体的实现需要看子类的实现逻辑,这里以DefaultImageHeaderParser
来分析
private ImageType getType(Reader reader) throws IOException {
final int firstTwoBytes = reader.getUInt16();
// JPEG.
if (firstTwoBytes == EXIF_MAGIC_NUMBER) {
return JPEG;
}
final int firstFourBytes = (firstTwoBytes << 16 & 0xFFFF0000) | (reader.getUInt16() & 0xFFFF);
// PNG.
if (firstFourBytes == PNG_HEADER) {
// See: http://stackoverflow.com/questions/2057923/how-to-check-a-png-for-grayscale-alpha
// -color-type
reader.skip(25 - 4);
int alpha = reader.getByte();
// A RGB indexed PNG can also have transparency. Better safe than sorry!
return alpha >= 3 ? PNG_A : PNG;
}
// GIF from first 3 bytes.
if (firstFourBytes >> 8 == GIF_HEADER) {
return GIF;
}
// WebP (reads up to 21 bytes). See https://developers.google.com/speed/webp/docs/riff_container
// for details.
if (firstFourBytes != RIFF_HEADER) {
return UNKNOWN;
}
// Bytes 4 - 7 contain length information. Skip these.
reader.skip(4);
final int thirdFourBytes =
(reader.getUInt16() << 16 & 0xFFFF0000) | (reader.getUInt16() & 0xFFFF);
if (thirdFourBytes != WEBP_HEADER) {
return UNKNOWN;
}
final int fourthFourBytes =
(reader.getUInt16() << 16 & 0xFFFF0000) | (reader.getUInt16() & 0xFFFF);
if ((fourthFourBytes & VP8_HEADER_MASK) != VP8_HEADER) {
return UNKNOWN;
}
if ((fourthFourBytes & VP8_HEADER_TYPE_MASK) == VP8_HEADER_TYPE_EXTENDED) {
// Skip some more length bytes and check for transparency/alpha flag.
reader.skip(4);
return (reader.getByte() & WEBP_EXTENDED_ALPHA_FLAG) != 0 ? ImageType.WEBP_A : ImageType.WEBP;
}
if ((fourthFourBytes & VP8_HEADER_TYPE_MASK) == VP8_HEADER_TYPE_LOSSLESS) {
// See chromium.googlesource.com/webm/libwebp/+/master/doc/webp-lossless-bitstream-spec.txt
// for more info.
reader.skip(4);
return (reader.getByte() & WEBP_LOSSLESS_ALPHA_FLAG) != 0 ? ImageType.WEBP_A : ImageType.WEBP;
}
return ImageType.WEBP;
}
这个方法里面就是图片的各种格式判断逻辑
网友评论