美文网首页
Glide的图片格式判断逻辑

Glide的图片格式判断逻辑

作者: Hsicen | 来源:发表于2020-06-01 18:30 被阅读0次
如何判断一张图片是否为GIF图
  • 根据后缀名判断:一般服务器返回图片时都有后缀名,这个时候我们可以根据文件的后缀名来判断;但是根据后缀名判断不可靠,有可能是用户手动修改的文件后缀名

  • 根据头信息判断:计算机存储数据是以二进制字节码存储,字节码的前几位标识了文件类型,也就是头信息;相对于gif我们可以取文件的前三个字节看是否为0x474946GIF文件

  • 使用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;
}

这个方法里面就是图片的各种格式判断逻辑

相关文章

网友评论

      本文标题:Glide的图片格式判断逻辑

      本文链接:https://www.haomeiwen.com/subject/mrwszhtx.html