Fix image loading
This commit is contained in:
@@ -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(
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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,
|
||||||
|
)
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user