mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-10-31 06:17:57 +01:00 
			
		
		
		
	Support more image formats for covers (#5524)
* Add TachiyomiImageDecoder for Coil Is currently used to decode AVIF and JPEG XL images. * LocalSource: Check against file name for cover This allows file with all supported formats to be set as cover * TachiyomiImageDecoder: Handle HEIF on Android 7 and older
This commit is contained in:
		| @@ -23,6 +23,7 @@ import coil.decode.GifDecoder | ||||
| import coil.decode.ImageDecoderDecoder | ||||
| import eu.kanade.tachiyomi.data.coil.ByteBufferFetcher | ||||
| import eu.kanade.tachiyomi.data.coil.MangaCoverFetcher | ||||
| import eu.kanade.tachiyomi.data.coil.TachiyomiImageDecoder | ||||
| import eu.kanade.tachiyomi.data.notification.Notifications | ||||
| import eu.kanade.tachiyomi.data.preference.PreferencesHelper | ||||
| import eu.kanade.tachiyomi.network.NetworkHelper | ||||
| @@ -105,6 +106,7 @@ open class App : Application(), LifecycleObserver, ImageLoaderFactory { | ||||
|     override fun newImageLoader(): ImageLoader { | ||||
|         return ImageLoader.Builder(this).apply { | ||||
|             componentRegistry { | ||||
|                 add(TachiyomiImageDecoder(this@App.resources)) | ||||
|                 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { | ||||
|                     add(ImageDecoderDecoder(this@App)) | ||||
|                 } else { | ||||
|   | ||||
| @@ -0,0 +1,53 @@ | ||||
| package eu.kanade.tachiyomi.data.coil | ||||
|  | ||||
| import android.content.res.Resources | ||||
| import android.os.Build | ||||
| import androidx.core.graphics.drawable.toDrawable | ||||
| import coil.bitmap.BitmapPool | ||||
| import coil.decode.DecodeResult | ||||
| import coil.decode.Decoder | ||||
| import coil.decode.Options | ||||
| import coil.size.Size | ||||
| import eu.kanade.tachiyomi.util.system.ImageUtil | ||||
| import okio.BufferedSource | ||||
| import tachiyomi.decoder.ImageDecoder | ||||
|  | ||||
| /** | ||||
|  * A [Decoder] that uses built-in [ImageDecoder] to decode images that is not supported by the system. | ||||
|  */ | ||||
| class TachiyomiImageDecoder(private val resources: Resources) : Decoder { | ||||
|  | ||||
|     override fun handles(source: BufferedSource, mimeType: String?): Boolean { | ||||
|         val type = source.peek().inputStream().use { | ||||
|             ImageUtil.findImageType(it) | ||||
|         } | ||||
|         return when (type) { | ||||
|             ImageUtil.ImageType.AVIF, ImageUtil.ImageType.JXL -> true | ||||
|             ImageUtil.ImageType.HEIF -> Build.VERSION.SDK_INT < Build.VERSION_CODES.O | ||||
|             else -> false | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     override suspend fun decode( | ||||
|         pool: BitmapPool, | ||||
|         source: BufferedSource, | ||||
|         size: Size, | ||||
|         options: Options | ||||
|     ): DecodeResult { | ||||
|         val decoder = source.use { | ||||
|             ImageDecoder.newInstance(it.inputStream()) | ||||
|         } | ||||
|  | ||||
|         check(decoder != null && decoder.width > 0 && decoder.height > 0) { "Failed to initialize decoder." } | ||||
|  | ||||
|         val bitmap = decoder.decode(rgb565 = options.allowRgb565) | ||||
|         decoder.recycle() | ||||
|  | ||||
|         check(bitmap != null) { "Failed to decode image." } | ||||
|  | ||||
|         return DecodeResult( | ||||
|             drawable = bitmap.toDrawable(resources), | ||||
|             isSampled = false | ||||
|         ) | ||||
|     } | ||||
| } | ||||
| @@ -29,7 +29,6 @@ class LocalSource(private val context: Context) : CatalogueSource { | ||||
|         const val ID = 0L | ||||
|         const val HELP_URL = "https://tachiyomi.org/help/guides/local-manga/" | ||||
|  | ||||
|         private const val COVER_NAME = "cover.jpg" | ||||
|         private val SUPPORTED_ARCHIVE_TYPES = setOf("zip", "rar", "cbr", "cbz", "epub") | ||||
|  | ||||
|         private val LATEST_THRESHOLD = TimeUnit.MILLISECONDS.convert(7, TimeUnit.DAYS) | ||||
| @@ -40,18 +39,29 @@ class LocalSource(private val context: Context) : CatalogueSource { | ||||
|                 input.close() | ||||
|                 return null | ||||
|             } | ||||
|             val cover = File("${dir.absolutePath}/${manga.url}", COVER_NAME) | ||||
|             val cover = getCoverFile(File("${dir.absolutePath}/${manga.url}")) | ||||
|  | ||||
|             // It might not exist if using the external SD card | ||||
|             cover.parentFile?.mkdirs() | ||||
|             input.use { | ||||
|                 cover.outputStream().use { | ||||
|                     input.copyTo(it) | ||||
|             if (cover != null && cover.exists()) { | ||||
|                 // It might not exist if using the external SD card | ||||
|                 cover.parentFile?.mkdirs() | ||||
|                 input.use { | ||||
|                     cover.outputStream().use { | ||||
|                         input.copyTo(it) | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             return cover | ||||
|         } | ||||
|  | ||||
|         /** | ||||
|          * Returns valid cover file inside [parent] directory. | ||||
|          */ | ||||
|         private fun getCoverFile(parent: File): File? { | ||||
|             return parent.listFiles()?.find { it.nameWithoutExtension == "cover" }?.takeIf { | ||||
|                 it.isFile && ImageUtil.isImage(it.name) { it.inputStream() } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         private fun getBaseDirectories(context: Context): List<File> { | ||||
|             val c = context.getString(R.string.app_name) + File.separator + "local" | ||||
|             return DiskUtil.getExternalStorages(context).map { File(it.absolutePath, c) } | ||||
| @@ -105,8 +115,8 @@ class LocalSource(private val context: Context) : CatalogueSource { | ||||
|  | ||||
|                 // Try to find the cover | ||||
|                 for (dir in baseDirs) { | ||||
|                     val cover = File("${dir.absolutePath}/$url", COVER_NAME) | ||||
|                     if (cover.exists()) { | ||||
|                     val cover = getCoverFile(File("${dir.absolutePath}/$url")) | ||||
|                     if (cover != null && cover.exists()) { | ||||
|                         thumbnail_url = cover.absolutePath | ||||
|                         break | ||||
|                     } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user