Fix image loading

This commit is contained in:
Koitharu
2025-04-30 17:27:41 +03:00
parent d45bab3879
commit 257f583f78
6 changed files with 47 additions and 16 deletions

View File

@@ -13,7 +13,7 @@ import com.davemorrissey.labs.subscaleview.decoder.ImageDecodeException
import kotlinx.coroutines.runInterruptible import kotlinx.coroutines.runInterruptible
import org.aomedia.avif.android.AvifDecoder import org.aomedia.avif.android.AvifDecoder
import org.aomedia.avif.android.AvifDecoder.Info import org.aomedia.avif.android.AvifDecoder.Info
import org.koitharu.kotatsu.core.util.ext.toByteBuffer import org.koitharu.kotatsu.core.util.ext.readByteBuffer
class AvifImageDecoder( class AvifImageDecoder(
private val source: ImageSource, private val source: ImageSource,
@@ -21,9 +21,7 @@ class AvifImageDecoder(
) : Decoder { ) : Decoder {
override suspend fun decode(): DecodeResult = runInterruptible { override suspend fun decode(): DecodeResult = runInterruptible {
val bytes = source.source().use { val bytes = source.source().readByteBuffer()
it.inputStream().toByteBuffer()
}
val info = Info() val info = Info()
if (!AvifDecoder.getInfo(bytes, bytes.remaining(), info)) { if (!AvifDecoder.getInfo(bytes, bytes.remaining(), info)) {
throw ImageDecodeException( throw ImageDecodeException(

View File

@@ -9,12 +9,15 @@ import androidx.annotation.RequiresApi
import androidx.core.graphics.createBitmap import androidx.core.graphics.createBitmap
import com.davemorrissey.labs.subscaleview.decoder.ImageDecodeException import com.davemorrissey.labs.subscaleview.decoder.ImageDecodeException
import okio.IOException import okio.IOException
import okio.buffer
import okio.source
import org.aomedia.avif.android.AvifDecoder import org.aomedia.avif.android.AvifDecoder
import org.aomedia.avif.android.AvifDecoder.Info import org.aomedia.avif.android.AvifDecoder.Info
import org.jetbrains.annotations.Blocking import org.jetbrains.annotations.Blocking
import org.koitharu.kotatsu.core.util.MimeTypes import org.koitharu.kotatsu.core.util.MimeTypes
import org.koitharu.kotatsu.core.util.ext.MimeType import org.koitharu.kotatsu.core.util.ext.MimeType
import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug
import org.koitharu.kotatsu.core.util.ext.readByteBuffer
import org.koitharu.kotatsu.core.util.ext.toByteBuffer import org.koitharu.kotatsu.core.util.ext.toByteBuffer
import org.koitharu.kotatsu.core.util.ext.toMimeTypeOrNull import org.koitharu.kotatsu.core.util.ext.toMimeTypeOrNull
import org.koitharu.kotatsu.parsers.util.runCatchingCancellable import org.koitharu.kotatsu.parsers.util.runCatchingCancellable
@@ -28,7 +31,7 @@ object BitmapDecoderCompat {
@Blocking @Blocking
fun decode(file: File): Bitmap = when (val format = probeMimeType(file)?.subtype) { fun decode(file: File): Bitmap = when (val format = probeMimeType(file)?.subtype) {
FORMAT_AVIF -> file.inputStream().use { decodeAvif(it.toByteBuffer()) } FORMAT_AVIF -> file.source().buffer().use { decodeAvif(it.readByteBuffer()) }
else -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { else -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
ImageDecoder.decodeBitmap(ImageDecoder.createSource(file)) ImageDecoder.decodeBitmap(ImageDecoder.createSource(file))
} else { } else {

View File

@@ -25,7 +25,7 @@ class CbzFetcher(
val entryName = requireNotNull(uri.fragment) val entryName = requireNotNull(uri.fragment)
val fs = options.fileSystem.openZip(filePath) val fs = options.fileSystem.openZip(filePath)
SourceFetchResult( SourceFetchResult(
source = ImageSource(entryName.toPath(), fs, closeable = fs), source = ImageSource(entryName.toPath(), fs),
mimeType = MimeTypes.getMimeTypeFromExtension(entryName)?.toString(), mimeType = MimeTypes.getMimeTypeFromExtension(entryName)?.toString(),
dataSource = DataSource.DISK, dataSource = DataSource.DISK,
) )

View File

@@ -23,6 +23,7 @@ import coil3.size.Scale
import coil3.size.Size import coil3.size.Size
import coil3.size.isOriginal import coil3.size.isOriginal
import coil3.size.pxOrElse import coil3.size.pxOrElse
import org.koitharu.kotatsu.core.util.ext.copyWithNewSource
import kotlin.math.roundToInt import kotlin.math.roundToInt
class RegionBitmapDecoder( class RegionBitmapDecoder(
@@ -34,16 +35,21 @@ class RegionBitmapDecoder(
override suspend fun decode(): DecodeResult? { override suspend fun decode(): DecodeResult? {
val regionDecoder = BitmapDecoderCompat.createRegionDecoder(fetchResult.source.source().inputStream()) val regionDecoder = BitmapDecoderCompat.createRegionDecoder(fetchResult.source.source().inputStream())
if (regionDecoder == null) { if (regionDecoder == null) {
val fallbackDecoder = imageLoader.components.newDecoder( val revivedFetchResult = fetchResult.copyWithNewSource()
result = fetchResult, return try {
options = options, val fallbackDecoder = imageLoader.components.newDecoder(
imageLoader = imageLoader, result = revivedFetchResult,
startIndex = 0, options = options,
)?.first imageLoader = imageLoader,
return if (fallbackDecoder == null || fallbackDecoder is RegionBitmapDecoder) { startIndex = 0,
null )?.first
} else { if (fallbackDecoder == null || fallbackDecoder is RegionBitmapDecoder) {
fallbackDecoder.decode() null
} else {
fallbackDecoder.decode()
}
} finally {
revivedFetchResult.source.close()
} }
} }
val bitmapOptions = BitmapFactory.Options() val bitmapOptions = BitmapFactory.Options()

View File

@@ -1,10 +1,13 @@
package org.koitharu.kotatsu.core.util.ext package org.koitharu.kotatsu.core.util.ext
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import androidx.annotation.CheckResult
import coil3.Extras import coil3.Extras
import coil3.ImageLoader import coil3.ImageLoader
import coil3.asDrawable import coil3.asDrawable
import coil3.decode.ImageSource
import coil3.fetch.FetchResult import coil3.fetch.FetchResult
import coil3.fetch.SourceFetchResult
import coil3.request.ErrorResult import coil3.request.ErrorResult
import coil3.request.ImageRequest import coil3.request.ImageRequest
import coil3.request.ImageResult import coil3.request.ImageResult
@@ -12,6 +15,7 @@ import coil3.request.Options
import coil3.request.SuccessResult import coil3.request.SuccessResult
import coil3.request.bitmapConfig import coil3.request.bitmapConfig
import coil3.toBitmap import coil3.toBitmap
import okio.buffer
import org.koitharu.kotatsu.bookmarks.domain.Bookmark import org.koitharu.kotatsu.bookmarks.domain.Bookmark
import org.koitharu.kotatsu.core.image.RegionBitmapDecoder import org.koitharu.kotatsu.core.image.RegionBitmapDecoder
import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.Manga
@@ -67,3 +71,14 @@ suspend fun ImageLoader.fetch(data: Any, options: Options): FetchResult? {
val mangaKey = Extras.Key<Manga?>(null) val mangaKey = Extras.Key<Manga?>(null)
val bookmarkKey = Extras.Key<Bookmark?>(null) val bookmarkKey = Extras.Key<Bookmark?>(null)
val mangaSourceKey = Extras.Key<MangaSource?>(null) val mangaSourceKey = Extras.Key<MangaSource?>(null)
@CheckResult
fun SourceFetchResult.copyWithNewSource(): SourceFetchResult = SourceFetchResult(
source = ImageSource(
source = source.fileSystem.source(source.file()).buffer(),
fileSystem = source.fileSystem,
metadata = source.metadata,
),
mimeType = mimeType,
dataSource = dataSource,
)

View File

@@ -7,6 +7,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import okhttp3.ResponseBody import okhttp3.ResponseBody
import okio.BufferedSink import okio.BufferedSink
import okio.BufferedSource
import okio.FileSystem import okio.FileSystem
import okio.IOException import okio.IOException
import okio.Path import okio.Path
@@ -30,6 +31,14 @@ suspend fun BufferedSink.writeAllCancellable(source: Source) = withContext(Dispa
writeAll(source.cancellable()) writeAll(source.cancellable())
} }
fun BufferedSource.readByteBuffer(): ByteBuffer {
val bytes = readByteArray()
return ByteBuffer.allocateDirect(bytes.size)
.put(bytes)
.rewind() as ByteBuffer
}
@Deprecated("")
fun InputStream.toByteBuffer(): ByteBuffer { fun InputStream.toByteBuffer(): ByteBuffer {
val outStream = ByteArrayOutputStream(available()) val outStream = ByteArrayOutputStream(available())
copyTo(outStream) copyTo(outStream)