Skip to content

Commit c2cf885

Browse files
authored
Merge pull request #3 from T8RIN/main
added animation
2 parents 9bf9ab9 + c76dde1 commit c2cf885

File tree

6 files changed

+592
-6
lines changed

6 files changed

+592
-6
lines changed

app/src/main/assets/output.avif

10.6 MB
Binary file not shown.

app/src/main/java/com/awxkee/avif/coil/MainActivity.kt

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ import android.graphics.Bitmap
44
import android.os.Build
55
import android.os.Bundle
66
import androidx.appcompat.app.AppCompatActivity
7-
import coil.ImageLoader
7+
import androidx.core.net.toUri
8+
import coil.imageLoader
89
import coil.load
10+
import coil.util.DebugLogger
911
import com.awxkee.avif.coil.databinding.ActivityMainBinding
10-
import com.github.awxkee.avifcoil.decoder.HeifDecoder
12+
import com.github.awxkee.avifcoil.decoder.animation.AnimatedAvifDecoder
1113

1214
class MainActivity : AppCompatActivity() {
1315

@@ -19,13 +21,17 @@ class MainActivity : AppCompatActivity() {
1921
setContentView(binding.root)
2022

2123
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
22-
binding.imageView.load("https://wh.aimuse.online/preset/federico-beccari.avif",
23-
imageLoader = ImageLoader.Builder(this)
24+
binding.imageView.load(
25+
data = "file:///android_asset/output.avif".toUri(),
26+
imageLoader = imageLoader
27+
.newBuilder()
28+
.logger(DebugLogger())
2429
.components {
25-
add(HeifDecoder.Factory())
30+
add(AnimatedAvifDecoder.Factory(preheatFrames = 2))
2631
}
2732
.bitmapConfig(Bitmap.Config.RGBA_1010102)
28-
.build())
33+
.build()
34+
)
2935
}
3036
}
3137
}
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
package com.github.awxkee.avifcoil.decoder.animation
2+
3+
4+
import android.annotation.SuppressLint
5+
import android.graphics.Bitmap
6+
import android.graphics.drawable.BitmapDrawable
7+
import android.graphics.drawable.Drawable
8+
import android.os.Build
9+
import coil.ImageLoader
10+
import coil.decode.DecodeResult
11+
import coil.decode.Decoder
12+
import coil.fetch.SourceResult
13+
import coil.request.Options
14+
import coil.size.Scale
15+
import coil.size.Size
16+
import coil.size.pxOrElse
17+
import com.radzivon.bartoshyk.avif.coder.AvifAnimatedDecoder
18+
import com.radzivon.bartoshyk.avif.coder.PreferredColorConfig
19+
import com.radzivon.bartoshyk.avif.coder.ScaleMode
20+
import kotlinx.coroutines.runInterruptible
21+
import okio.ByteString.Companion.encodeUtf8
22+
23+
public class AnimatedAvifDecoder(
24+
private val source: SourceResult,
25+
private val options: Options,
26+
private val preheatFrames: Int,
27+
private val exceptionLogger: ((Exception) -> Unit)? = null,
28+
) : Decoder {
29+
30+
override suspend fun decode(): DecodeResult? = runInterruptible {
31+
try {
32+
// ColorSpace is preferred to be ignored due to lib is trying to handle all color profile by itself
33+
val sourceData = source.source.source().readByteArray()
34+
35+
var mPreferredColorConfig: PreferredColorConfig = when (options.config) {
36+
Bitmap.Config.ALPHA_8 -> PreferredColorConfig.RGBA_8888
37+
Bitmap.Config.RGB_565 -> if (options.allowRgb565) PreferredColorConfig.RGB_565 else PreferredColorConfig.DEFAULT
38+
Bitmap.Config.ARGB_8888 -> PreferredColorConfig.RGBA_8888
39+
else -> PreferredColorConfig.DEFAULT
40+
}
41+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && options.config == Bitmap.Config.RGBA_F16) {
42+
mPreferredColorConfig = PreferredColorConfig.RGBA_F16
43+
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && options.config == Bitmap.Config.HARDWARE) {
44+
mPreferredColorConfig = PreferredColorConfig.HARDWARE
45+
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && options.config == Bitmap.Config.RGBA_1010102) {
46+
mPreferredColorConfig = PreferredColorConfig.RGBA_1010102
47+
}
48+
49+
if (options.size == Size.ORIGINAL) {
50+
val originalImage = AvifAnimatedDecoder(sourceData)
51+
return@runInterruptible DecodeResult(
52+
drawable = originalImage.drawable(
53+
colorConfig = mPreferredColorConfig,
54+
scaleMode = ScaleMode.FIT,
55+
),
56+
isSampled = false
57+
)
58+
}
59+
60+
val dstWidth = options.size.width.pxOrElse { 0 }
61+
val dstHeight = options.size.height.pxOrElse { 0 }
62+
val scaleMode = when (options.scale) {
63+
Scale.FILL -> ScaleMode.FILL
64+
Scale.FIT -> ScaleMode.FIT
65+
}
66+
67+
val originalImage = AvifAnimatedDecoder(sourceData)
68+
69+
DecodeResult(
70+
drawable = originalImage.drawable(
71+
dstWidth = dstWidth,
72+
dstHeight = dstHeight,
73+
colorConfig = mPreferredColorConfig,
74+
scaleMode = scaleMode
75+
),
76+
isSampled = true
77+
)
78+
} catch (e: Exception) {
79+
exceptionLogger?.invoke(e)
80+
return@runInterruptible null
81+
}
82+
}
83+
84+
private fun AvifAnimatedDecoder.drawable(
85+
dstWidth: Int = 0,
86+
dstHeight: Int = 0,
87+
colorConfig: PreferredColorConfig,
88+
scaleMode: ScaleMode
89+
): Drawable = if (getFramesCount() > 1) {
90+
AnimatedDrawable(
91+
frameStore = AvifAnimatedStore(
92+
avifAnimatedDecoder = this,
93+
targetWidth = dstWidth,
94+
targetHeight = dstHeight,
95+
scaleMode = scaleMode,
96+
preferredColorConfig = colorConfig
97+
),
98+
preheatFrames = preheatFrames,
99+
firstFrameAsPlaceholder = true
100+
)
101+
} else {
102+
BitmapDrawable(
103+
options.context.resources,
104+
if (dstWidth == 0 || dstHeight == 0) {
105+
getFrame(
106+
frame = 0,
107+
preferredColorConfig = colorConfig
108+
)
109+
} else {
110+
getScaledFrame(
111+
frame = 0,
112+
scaledWidth = dstWidth,
113+
scaledHeight = dstHeight,
114+
scaleMode = scaleMode,
115+
preferredColorConfig = colorConfig
116+
)
117+
}
118+
)
119+
}
120+
121+
public class Factory(
122+
private val preheatFrames: Int = 6,
123+
private val exceptionLogger: ((Exception) -> Unit)? = null,
124+
) : Decoder.Factory {
125+
126+
override fun create(
127+
result: SourceResult,
128+
options: Options,
129+
imageLoader: ImageLoader,
130+
): Decoder? {
131+
return if (
132+
AVAILABLE_BRANDS.any {
133+
result.source.source().rangeEquals(4, it)
134+
}
135+
) AnimatedAvifDecoder(
136+
source = result,
137+
options = options,
138+
preheatFrames = preheatFrames,
139+
exceptionLogger = exceptionLogger,
140+
)
141+
else null
142+
}
143+
144+
companion object {
145+
private val AVIF = "ftypavif".encodeUtf8()
146+
private val AVIS = "ftypavis".encodeUtf8()
147+
148+
private val AVAILABLE_BRANDS = listOf(AVIF, AVIS)
149+
}
150+
}
151+
152+
@SuppressLint("ObsoleteSdkInt")
153+
companion object {
154+
init {
155+
if (Build.VERSION.SDK_INT >= 24) {
156+
System.loadLibrary("coder")
157+
}
158+
}
159+
}
160+
161+
}

0 commit comments

Comments
 (0)