From 0f68be9663d1ab276af9c2559698cc11c510ca91 Mon Sep 17 00:00:00 2001 From: Koitharu Date: Thu, 19 Dec 2024 17:10:01 +0200 Subject: [PATCH] Use advanced bitmap decoder for MangaLoaderContext --- .../kotatsu/core/image/BitmapDecoderCompat.kt | 23 ++++++++++++++++--- .../core/parser/MangaLoaderContextImpl.kt | 10 +++----- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/image/BitmapDecoderCompat.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/image/BitmapDecoderCompat.kt index 80e9a4bdb..34584ec57 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/image/BitmapDecoderCompat.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/image/BitmapDecoderCompat.kt @@ -5,6 +5,7 @@ import android.graphics.BitmapFactory import android.graphics.ImageDecoder import android.os.Build import android.webkit.MimeTypeMap +import androidx.annotation.RequiresApi import com.davemorrissey.labs.subscaleview.decoder.ImageDecodeException import okhttp3.MediaType import okhttp3.MediaType.Companion.toMediaTypeOrNull @@ -32,19 +33,21 @@ object BitmapDecoderCompat { } @Blocking - fun decode(stream: InputStream, type: MediaType?): Bitmap { + fun decode(stream: InputStream, type: MediaType?, isMutable: Boolean = false): Bitmap { val format = type?.subtype if (format == FORMAT_AVIF) { return decodeAvif(stream.toByteBuffer()) } if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) { - return checkBitmapNotNull(BitmapFactory.decodeStream(stream), format) + val opts = BitmapFactory.Options() + opts.inMutable = isMutable + return checkBitmapNotNull(BitmapFactory.decodeStream(stream, null, opts), format) } val byteBuffer = stream.toByteBuffer() return if (AvifDecoder.isAvifImage(byteBuffer)) { decodeAvif(byteBuffer) } else { - ImageDecoder.decodeBitmap(ImageDecoder.createSource(byteBuffer)) + ImageDecoder.decodeBitmap(ImageDecoder.createSource(byteBuffer), DecoderConfigListener(isMutable)) } } @@ -74,4 +77,18 @@ object BitmapDecoderCompat { } return bitmap } + + @RequiresApi(Build.VERSION_CODES.P) + private class DecoderConfigListener( + private val isMutable: Boolean, + ) : ImageDecoder.OnHeaderDecodedListener { + + override fun onHeaderDecoded( + decoder: ImageDecoder, + info: ImageDecoder.ImageInfo, + source: ImageDecoder.Source + ) { + decoder.isMutableRequired = isMutable + } + } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/MangaLoaderContextImpl.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/MangaLoaderContextImpl.kt index 824aea5d2..c68c79fec 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/MangaLoaderContextImpl.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/MangaLoaderContextImpl.kt @@ -2,12 +2,10 @@ package org.koitharu.kotatsu.core.parser import android.annotation.SuppressLint import android.content.Context -import android.graphics.BitmapFactory import android.util.Base64 import android.webkit.WebView import androidx.annotation.MainThread import androidx.core.os.LocaleListCompat -import com.davemorrissey.labs.subscaleview.decoder.ImageDecodeException import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking @@ -17,6 +15,7 @@ import okhttp3.OkHttpClient import okhttp3.Response import okhttp3.ResponseBody.Companion.asResponseBody import okio.Buffer +import org.koitharu.kotatsu.core.image.BitmapDecoderCompat import org.koitharu.kotatsu.core.network.MangaHttpClient import org.koitharu.kotatsu.core.network.cookies.MutableCookieJar import org.koitharu.kotatsu.core.prefs.SourceSettings @@ -31,7 +30,6 @@ import org.koitharu.kotatsu.parsers.config.MangaSourceConfig import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.network.UserAgents import org.koitharu.kotatsu.parsers.util.map -import org.koitharu.kotatsu.parsers.util.mimeType import java.lang.ref.WeakReference import java.util.Locale import javax.inject.Inject @@ -80,15 +78,13 @@ class MangaLoaderContextImpl @Inject constructor( override fun redrawImageResponse(response: Response, redraw: (image: Bitmap) -> Bitmap): Response { return response.map { body -> - val opts = BitmapFactory.Options() - opts.inMutable = true - BitmapFactory.decodeStream(body.byteStream(), null, opts)?.use { bitmap -> + BitmapDecoderCompat.decode(body.byteStream(), body.contentType(), isMutable = true).use { bitmap -> (redraw(BitmapWrapper.create(bitmap)) as BitmapWrapper).use { result -> Buffer().also { result.compressTo(it.outputStream()) }.asResponseBody("image/jpeg".toMediaType()) } - } ?: throw ImageDecodeException(response.request.url.toString(), response.mimeType) + } } }