Refine and enable AnimatedWebPDrawable by default (#2442)
This commit is contained in:
1 parent
924d3c4ea3
commit
a10f9f2ce8
2 files changed
+37
-31
No files matched your search
@@ -127,7 +127,7 @@ object Settings : DataStorePreferences(null) {
|
||||
val animateItems = boolPref("animate_items", true)
|
||||
val enableCronet = boolPref("enable_cronet", true)
|
||||
val enableQuic = boolPref("enable_quic", true)
|
||||
val webpDecoder = boolPref("webp_decoder", false)
|
||||
val webpDecoder = boolPref("webp_decoder", true)
|
||||
val desktopSite = boolPref("desktop_site", true)
|
||||
val displayName = stringOrNullPref("display_name", null)
|
||||
val avatar = stringOrNullPref("avatar", null)
|
||||
|
||||
@@ -7,14 +7,15 @@ import android.graphics.Paint
|
||||
import android.graphics.PixelFormat
|
||||
import android.graphics.drawable.Animatable
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.SystemClock
|
||||
import androidx.core.graphics.createBitmap
|
||||
import eu.kanade.tachiyomi.util.system.logcat
|
||||
import java.nio.ByteBuffer
|
||||
import kotlin.time.TimeSource
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.ensureActive
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
// Hold a reference to the buffer as it's used by the decoder
|
||||
@@ -29,7 +30,7 @@ class AnimatedWebPDrawable(private val source: ByteBuffer) : Drawable(), Animata
|
||||
|
||||
private var decodeJob: Job? = null
|
||||
private var loopsCompleted = 0
|
||||
private var frameDuration: Int
|
||||
private var timeToShowNextFrame = 0L
|
||||
private var currentFrame: Frame
|
||||
private var nextFrame: Frame
|
||||
|
||||
@@ -45,7 +46,6 @@ class AnimatedWebPDrawable(private val source: ByteBuffer) : Drawable(), Animata
|
||||
nativeDestroyDecoder(decoder)
|
||||
"Failed to decode first frame"
|
||||
}
|
||||
frameDuration = timestamp
|
||||
currentFrame = Frame(bitmap, timestamp)
|
||||
nextFrame = Frame(createBitmap(width, height), 0)
|
||||
}
|
||||
@@ -54,30 +54,42 @@ class AnimatedWebPDrawable(private val source: ByteBuffer) : Drawable(), Animata
|
||||
|
||||
override fun getIntrinsicHeight() = height
|
||||
|
||||
private fun decodeNextFrame(reset: Boolean) {
|
||||
nextFrame.timestamp = nativeDecodeNextFrame(decoder, reset, nextFrame.bitmap)
|
||||
if (nextFrame.timestamp == 0) {
|
||||
throw CancellationException("Failed to decode next frame")
|
||||
private val runnable = Runnable {
|
||||
timeToShowNextFrame = SystemClock.uptimeMillis() + nextFrame.timestamp - currentFrame.timestamp
|
||||
invalidateSelf()
|
||||
}
|
||||
|
||||
private fun decodeNextFrame(reset: Boolean) = decodeScope.launch {
|
||||
val timestamp = nativeDecodeNextFrame(decoder, reset, nextFrame.bitmap)
|
||||
ensureActive()
|
||||
check(timestamp != 0) {
|
||||
decodeJob = null
|
||||
"Failed to decode next frame"
|
||||
}
|
||||
if (timestamp <= currentFrame.timestamp) {
|
||||
if (reset) loopsCompleted = 0 else loopsCompleted++
|
||||
currentFrame.timestamp = 0
|
||||
}
|
||||
nextFrame.timestamp = timestamp
|
||||
nextFrame.bitmap.prepareToDraw()
|
||||
}.apply {
|
||||
invokeOnCompletion { cause ->
|
||||
when (cause) {
|
||||
null -> if (reset) {
|
||||
runnable.run()
|
||||
} else {
|
||||
scheduleSelf(runnable, timeToShowNextFrame)
|
||||
}
|
||||
!is CancellationException -> logcat(cause)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun draw(canvas: Canvas) {
|
||||
if (decodeJob?.isCompleted == true && nextFrame.timestamp != 0) {
|
||||
val timeMark = TimeSource.Monotonic.markNow()
|
||||
frameDuration = if (nextFrame.timestamp > currentFrame.timestamp) {
|
||||
nextFrame.timestamp - currentFrame.timestamp
|
||||
} else {
|
||||
loopsCompleted++
|
||||
nextFrame.timestamp
|
||||
}
|
||||
currentFrame = nextFrame.also { nextFrame = currentFrame }
|
||||
if (decodeJob?.isCompleted == true) {
|
||||
decodeJob = if (loopCount == 0 || loopsCompleted < loopCount) {
|
||||
decodeScope.launch {
|
||||
decodeNextFrame(false)
|
||||
delay(frameDuration - timeMark.elapsedNow().inWholeMilliseconds)
|
||||
invalidateSelf()
|
||||
}
|
||||
currentFrame = nextFrame.also { nextFrame = currentFrame }
|
||||
decodeNextFrame(false)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
@@ -110,20 +122,14 @@ class AnimatedWebPDrawable(private val source: ByteBuffer) : Drawable(), Animata
|
||||
|
||||
override fun start() {
|
||||
if (decodeJob == null) {
|
||||
val timeMark = TimeSource.Monotonic.markNow()
|
||||
loopsCompleted = 0
|
||||
decodeJob = decodeScope.launch {
|
||||
val isFirstFrame = currentFrame.timestamp == frameDuration
|
||||
decodeNextFrame(!isFirstFrame)
|
||||
delay(frameDuration - timeMark.elapsedNow().inWholeMilliseconds)
|
||||
invalidateSelf()
|
||||
}
|
||||
decodeJob = decodeNextFrame(true)
|
||||
}
|
||||
}
|
||||
|
||||
override fun stop() {
|
||||
decodeJob?.cancel()
|
||||
decodeJob = null
|
||||
unscheduleSelf(runnable)
|
||||
}
|
||||
|
||||
fun dispose() {
|
||||
|
||||
Reference in new issue
Block a user