Skip to content

Commit

Permalink
improve: Improve the accuracy of animation-related Decoder in determi…
Browse files Browse the repository at this point in the history
…ning the image type. No longer rely on the mimeType attribute of FetchResult because it may be inaccurate.
  • Loading branch information
panpf committed Aug 15, 2024
1 parent d0f3448 commit d07d5cb
Show file tree
Hide file tree
Showing 17 changed files with 117 additions and 61 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ Translations: [简体中文](CHANGELOG_zh.md)

* fix: Fixed a bug where SkiaAnimatedImagePainter crashed when encountering disguised gif
images. [#205](https://github.com/panpf/sketch/issues/205)
* improve: Improve the accuracy of animation-related Decoder in determining the image type. No
longer rely on the mimeType attribute of FetchResult because it may be inaccurate.

# 4.0.0-alpha05

Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG_zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

* fix: 修复 SkiaAnimatedImagePainter 在遇到伪装的 gif 图片时崩溃的
bug。[#205](https://github.com/panpf/sketch/issues/205)
* improve: 改进动图相关 Decoder 在判断图片类型时的准确性,不再依赖 FetchResult 的 mimeType
属性,因为它可能不准确

# 4.0.0-alpha05

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import com.github.panpf.sketch.decode.internal.createInSampledTransformed
import com.github.panpf.sketch.decode.supportKoralGif
import com.github.panpf.sketch.drawable.AnimatableDrawable
import com.github.panpf.sketch.drawable.GifDrawableWrapperDrawable
import com.github.panpf.sketch.fetch.FetchResult
import com.github.panpf.sketch.fetch.copy
import com.github.panpf.sketch.getDrawableOrThrow
import com.github.panpf.sketch.images.ResourceImages
Expand All @@ -33,6 +34,7 @@ import com.github.panpf.sketch.request.animatedTransformation
import com.github.panpf.sketch.request.onAnimationEnd
import com.github.panpf.sketch.request.onAnimationStart
import com.github.panpf.sketch.request.repeatCount
import com.github.panpf.sketch.source.AssetDataSource
import com.github.panpf.sketch.source.DataFrom.LOCAL
import com.github.panpf.sketch.test.singleton.sketch
import com.github.panpf.sketch.test.utils.decode
Expand Down Expand Up @@ -140,6 +142,18 @@ class GifDrawableDecoderTest {
}.apply {
Assert.assertNull(this)
}

// Disguised, mimeType; data error
ImageRequest(context, ResourceImages.png.uri).let {
val fetchResult =
FetchResult(
AssetDataSource(sketch, it, ResourceImages.png.resourceName),
"image/gif"
)
factory.create(it.toRequestContext(sketch), fetchResult)
}.apply {
Assert.assertNull(this)
}
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,13 @@ import android.graphics.Rect
import androidx.annotation.WorkerThread
import com.github.panpf.sketch.ComponentRegistry
import com.github.panpf.sketch.asSketchImage
import com.github.panpf.sketch.source.DataSource
import com.github.panpf.sketch.decode.internal.ImageFormat
import com.github.panpf.sketch.decode.internal.calculateSampleSize
import com.github.panpf.sketch.decode.internal.createInSampledTransformed
import com.github.panpf.sketch.decode.internal.isGif
import com.github.panpf.sketch.decode.internal.isSmallerSizeMode
import com.github.panpf.sketch.drawable.GifDrawableWrapperDrawable
import com.github.panpf.sketch.drawable.AnimatableDrawable
import com.github.panpf.sketch.drawable.GifDrawableWrapperDrawable
import com.github.panpf.sketch.fetch.FetchResult
import com.github.panpf.sketch.request.ANIMATION_REPEAT_INFINITE
import com.github.panpf.sketch.request.animatable2CompatCallbackOf
Expand All @@ -38,6 +37,7 @@ import com.github.panpf.sketch.request.animationEndCallback
import com.github.panpf.sketch.request.animationStartCallback
import com.github.panpf.sketch.request.internal.RequestContext
import com.github.panpf.sketch.request.repeatCount
import com.github.panpf.sketch.source.DataSource
import com.github.panpf.sketch.util.Size
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
Expand Down Expand Up @@ -85,7 +85,7 @@ class GifDrawableDecoder(
height = imageHeight,
mimeType = ImageFormat.GIF.mimeType,
)
val resize= requestContext.computeResize(imageInfo.size)
val resize = requestContext.computeResize(imageInfo.size)
val inSampleSize = calculateSampleSize(
imageSize = imageSize,
targetSize = size,
Expand Down Expand Up @@ -144,12 +144,11 @@ class GifDrawableDecoder(
requestContext: RequestContext,
fetchResult: FetchResult
): Decoder? {
if (!requestContext.request.disallowAnimatedImage) {
val imageFormat = ImageFormat.parseMimeType(fetchResult.mimeType)
val isGif = imageFormat == ImageFormat.GIF || fetchResult.headerBytes.isGif()
if (isGif) {
return GifDrawableDecoder(requestContext, fetchResult.dataSource)
}
if (
!requestContext.request.disallowAnimatedImage
&& fetchResult.headerBytes.isGif()
) {
return GifDrawableDecoder(requestContext, fetchResult.dataSource)
}
return null
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,18 @@ class GifAnimatedDecoderTest {
}.apply {
Assert.assertNull(this)
}

// Disguised, mimeType; data error
ImageRequest(context, ResourceImages.png.uri).let {
val fetchResult =
FetchResult(
AssetDataSource(sketch, it, ResourceImages.png.resourceName),
"image/gif"
)
factory.create(it.toRequestContext(sketch), fetchResult)
}.apply {
Assert.assertNull(this)
}
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,18 @@ class GifMovieDecoderTest {
}.apply {
Assert.assertNull(this)
}

// Disguised, mimeType; data error
ImageRequest(context, ResourceImages.png.uri).let {
val fetchResult =
FetchResult(
AssetDataSource(sketch, it, ResourceImages.png.resourceName),
"image/gif"
)
factory.create(it.toRequestContext(sketch), fetchResult)
}.apply {
Assert.assertNull(this)
}
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,18 @@ class HeifAnimatedDecoderTest {
"image/jpeg",
)
factory.create(it.toRequestContext(sketch), fetchResult)
}.apply {
Assert.assertNotNull(this)
}

// Disguised, mimeType; data error
ImageRequest(context, ResourceImages.png.uri).let {
val fetchResult =
FetchResult(
AssetDataSource(sketch, it, ResourceImages.png.resourceName),
"image/heif"
)
factory.create(it.toRequestContext(sketch), fetchResult)
}.apply {
Assert.assertNull(this)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,18 @@ class WebpAnimatedDecoderTest {
"image/jpeg",
)
factory.create(it.toRequestContext(sketch), fetchResult)
}.apply {
Assert.assertNotNull(this)
}

// Disguised, mimeType; data error
ImageRequest(context, ResourceImages.png.uri).let {
val fetchResult =
FetchResult(
AssetDataSource(sketch, it, ResourceImages.png.resourceName),
"image/webp"
)
factory.create(it.toRequestContext(sketch), fetchResult)
}.apply {
Assert.assertNull(this)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,11 @@ package com.github.panpf.sketch.decode
import android.os.Build
import androidx.annotation.RequiresApi
import com.github.panpf.sketch.ComponentRegistry
import com.github.panpf.sketch.source.DataSource
import com.github.panpf.sketch.decode.internal.ImageDecoderAnimatedDecoder
import com.github.panpf.sketch.decode.internal.ImageFormat
import com.github.panpf.sketch.decode.internal.isGif
import com.github.panpf.sketch.fetch.FetchResult
import com.github.panpf.sketch.request.internal.RequestContext
import com.github.panpf.sketch.source.DataSource

/**
* Adds gif support by AnimatedImageDrawable
Expand Down Expand Up @@ -62,12 +61,9 @@ class GifAnimatedDecoder(
if (
Build.VERSION.SDK_INT >= Build.VERSION_CODES.P
&& !requestContext.request.disallowAnimatedImage
&& fetchResult.headerBytes.isGif()
) {
val imageFormat = ImageFormat.parseMimeType(fetchResult.mimeType)
val isGif = imageFormat == ImageFormat.GIF || fetchResult.headerBytes.isGif()
if (isGif) {
return GifAnimatedDecoder(requestContext, dataSource)
}
return GifAnimatedDecoder(requestContext, dataSource)
}
return null
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,21 @@ import android.os.Build
import androidx.annotation.RequiresApi
import androidx.annotation.WorkerThread
import com.github.panpf.sketch.ComponentRegistry
import com.github.panpf.sketch.source.DataSource
import com.github.panpf.sketch.asSketchImage
import com.github.panpf.sketch.decode.internal.ImageFormat
import com.github.panpf.sketch.decode.internal.isGif
import com.github.panpf.sketch.drawable.MovieDrawable
import com.github.panpf.sketch.drawable.AnimatableDrawable
import com.github.panpf.sketch.drawable.MovieDrawable
import com.github.panpf.sketch.fetch.FetchResult
import com.github.panpf.sketch.request.ANIMATION_REPEAT_INFINITE
import com.github.panpf.sketch.request.animatable2CompatCallbackOf
import com.github.panpf.sketch.request.animatedTransformation
import com.github.panpf.sketch.request.animationEndCallback
import com.github.panpf.sketch.request.animationStartCallback
import com.github.panpf.sketch.asSketchImage
import com.github.panpf.sketch.request.bitmapConfig
import com.github.panpf.sketch.request.internal.RequestContext
import com.github.panpf.sketch.request.repeatCount
import com.github.panpf.sketch.source.DataSource
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import okio.buffer
Expand Down Expand Up @@ -68,7 +68,8 @@ class GifMovieDecoder(
@WorkerThread
override suspend fun decode(): Result<DecodeResult> = kotlin.runCatching {
val request = requestContext.request
val movie: Movie? = dataSource.openSource().buffer().inputStream().use { Movie.decodeStream(it) }
val movie: Movie? =
dataSource.openSource().buffer().inputStream().use { Movie.decodeStream(it) }

val width = movie?.width() ?: 0
val height = movie?.height() ?: 0
Expand Down Expand Up @@ -123,14 +124,12 @@ class GifMovieDecoder(
fetchResult: FetchResult
): Decoder? {
val dataSource = fetchResult.dataSource
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT
if (
Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT
&& !requestContext.request.disallowAnimatedImage
&& fetchResult.headerBytes.isGif()
) {
val imageFormat = ImageFormat.parseMimeType(fetchResult.mimeType)
val isGif = imageFormat == ImageFormat.GIF || fetchResult.headerBytes.isGif()
if (isGif) {
return GifMovieDecoder(requestContext, dataSource)
}
return GifMovieDecoder(requestContext, dataSource)
}
return null
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,11 @@ package com.github.panpf.sketch.decode
import android.os.Build
import androidx.annotation.RequiresApi
import com.github.panpf.sketch.ComponentRegistry
import com.github.panpf.sketch.source.DataSource
import com.github.panpf.sketch.decode.internal.ImageDecoderAnimatedDecoder
import com.github.panpf.sketch.decode.internal.ImageFormat
import com.github.panpf.sketch.decode.internal.isAnimatedHeif
import com.github.panpf.sketch.fetch.FetchResult
import com.github.panpf.sketch.request.internal.RequestContext
import com.github.panpf.sketch.source.DataSource

/**
* Adds animation heif support by AnimatedImageDrawable
Expand Down Expand Up @@ -62,13 +61,9 @@ class HeifAnimatedDecoder(
if (
Build.VERSION.SDK_INT >= Build.VERSION_CODES.R
&& !requestContext.request.disallowAnimatedImage
&& fetchResult.headerBytes.isAnimatedHeif()
) {
val imageFormat = ImageFormat.parseMimeType(fetchResult.mimeType)
if ((imageFormat == null || imageFormat == ImageFormat.HEIC || imageFormat == ImageFormat.HEIF)
&& fetchResult.headerBytes.isAnimatedHeif()
) {
return HeifAnimatedDecoder(requestContext, dataSource)
}
return HeifAnimatedDecoder(requestContext, dataSource)
}
return null
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,11 @@ package com.github.panpf.sketch.decode
import android.os.Build
import androidx.annotation.RequiresApi
import com.github.panpf.sketch.ComponentRegistry
import com.github.panpf.sketch.source.DataSource
import com.github.panpf.sketch.decode.internal.ImageDecoderAnimatedDecoder
import com.github.panpf.sketch.decode.internal.ImageFormat
import com.github.panpf.sketch.decode.internal.isAnimatedWebP
import com.github.panpf.sketch.fetch.FetchResult
import com.github.panpf.sketch.request.internal.RequestContext
import com.github.panpf.sketch.source.DataSource

/**
* Adds animation webp support by AnimatedImageDrawable
Expand Down Expand Up @@ -62,11 +61,9 @@ class WebpAnimatedDecoder(
if (
Build.VERSION.SDK_INT >= Build.VERSION_CODES.P
&& !requestContext.request.disallowAnimatedImage
&& fetchResult.headerBytes.isAnimatedWebP()
) {
val imageFormat = ImageFormat.parseMimeType(fetchResult.mimeType)
if ((imageFormat == null || imageFormat == ImageFormat.WEBP) && fetchResult.headerBytes.isAnimatedWebP()) {
return WebpAnimatedDecoder(requestContext, dataSource)
}
return WebpAnimatedDecoder(requestContext, dataSource)
}
return null
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
package com.github.panpf.sketch.decode

import com.github.panpf.sketch.ComponentRegistry
import com.github.panpf.sketch.source.DataSource
import com.github.panpf.sketch.decode.internal.SkiaAnimatedDecoder
import com.github.panpf.sketch.decode.internal.ImageFormat
import com.github.panpf.sketch.decode.internal.isGif
import com.github.panpf.sketch.fetch.FetchResult
import com.github.panpf.sketch.request.internal.RequestContext
import com.github.panpf.sketch.source.DataSource

/**
* Adds gif support by Skia
Expand All @@ -25,12 +24,11 @@ class GifSkiaAnimatedDecoder(
override val key: String = "GifSkiaAnimatedDecoder"

override fun create(requestContext: RequestContext, fetchResult: FetchResult): Decoder? {
if (!requestContext.request.disallowAnimatedImage) {
val imageFormat = ImageFormat.parseMimeType(fetchResult.mimeType)
val isGif = imageFormat == ImageFormat.GIF || fetchResult.headerBytes.isGif()
if (isGif) {
return GifSkiaAnimatedDecoder(requestContext, fetchResult.dataSource)
}
if (
!requestContext.request.disallowAnimatedImage
&& fetchResult.headerBytes.isGif()
) {
return GifSkiaAnimatedDecoder(requestContext, fetchResult.dataSource)
}
return null
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
package com.github.panpf.sketch.decode

import com.github.panpf.sketch.ComponentRegistry
import com.github.panpf.sketch.source.DataSource
import com.github.panpf.sketch.decode.internal.SkiaAnimatedDecoder
import com.github.panpf.sketch.decode.internal.ImageFormat
import com.github.panpf.sketch.decode.internal.isAnimatedWebP
import com.github.panpf.sketch.fetch.FetchResult
import com.github.panpf.sketch.request.internal.RequestContext
import com.github.panpf.sketch.source.DataSource

/**
* Adds animation webp support by Skia
Expand All @@ -25,13 +24,11 @@ class WebpSkiaAnimatedDecoder(
override val key: String = "WebpSkiaAnimatedDecoder"

override fun create(requestContext: RequestContext, fetchResult: FetchResult): Decoder? {
if (!requestContext.request.disallowAnimatedImage) {
val imageFormat = ImageFormat.parseMimeType(fetchResult.mimeType)
val isAnimatedWebp =
(imageFormat == null || imageFormat == ImageFormat.WEBP) && fetchResult.headerBytes.isAnimatedWebP()
if (isAnimatedWebp) {
return WebpSkiaAnimatedDecoder(requestContext, fetchResult.dataSource)
}
if (
!requestContext.request.disallowAnimatedImage
&& fetchResult.headerBytes.isAnimatedWebP()
) {
return WebpSkiaAnimatedDecoder(requestContext, fetchResult.dataSource)
}
return null
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ interface FetchResult {

val dataSource: DataSource

/**
* This mimeType is extracted from the uri and may not be accurate
*/
val mimeType: String?

val dataFrom: DataFrom
Expand Down
Loading

0 comments on commit d07d5cb

Please sign in to comment.