From 0a806a6620ef58d071f6192e546d16ae1b415201 Mon Sep 17 00:00:00 2001 From: Angrevol Date: Tue, 5 Aug 2025 01:51:55 +0200 Subject: [PATCH] Fix GPU crash when setting cover from downloaded chapters. Fixes #368 Prevent segmentation fault by applying size constraints during bitmap decode instead of after. Large downloaded chapter images were causing GPU memory overflow before size validation. - Add pre-decode size check using inJustDecodeBounds - Implement calculateInSampleSize to downsample large images during decode --- .../kanade/tachiyomi/data/cache/CoverCache.kt | 45 ++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/cache/CoverCache.kt b/app/src/main/java/eu/kanade/tachiyomi/data/cache/CoverCache.kt index 6157819c27..0fd7c0bebf 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/cache/CoverCache.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/cache/CoverCache.kt @@ -175,7 +175,28 @@ class CoverCache(val context: Context) { @Throws(IOException::class) fun setCustomCoverToCache(manga: Manga, inputStream: InputStream) { val maxTextureSize = 4096f - var bitmap = BitmapFactory.decodeStream(inputStream) + + val imageBytes = inputStream.readBytes() + inputStream.close() + + val bounds = BitmapFactory.Options().apply { + inJustDecodeBounds = true + } + BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size, bounds) + + val originalWidth = bounds.outWidth + val originalHeight = bounds.outHeight + + val sampleSize = calculateInSampleSize(originalWidth, originalHeight, maxTextureSize.toInt()) + + val decodeOptions = BitmapFactory.Options().apply { + inSampleSize = sampleSize + inPreferredConfig = if (sampleSize > 1) Bitmap.Config.RGB_565 else Bitmap.Config.ARGB_8888 + } + + var bitmap = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size, decodeOptions) + ?: throw IOException("Failed to decode image") + if (maxOf(bitmap.width, bitmap.height) > maxTextureSize) { val widthRatio = bitmap.width / maxTextureSize val heightRatio = bitmap.height / maxTextureSize @@ -209,6 +230,28 @@ class CoverCache(val context: Context) { } } + /** + * Calculate the largest inSampleSize value that is a power of 2 and keeps both + * height and width larger than the requested height and width. + * + * @param width the original image width. + * @param height the original image height. + * @param maxSize the maximum allowed dimension. + * @return the sample size to use for BitmapFactory.Options. + */ + private fun calculateInSampleSize(width: Int, height: Int, maxSize: Int): Int { + var inSampleSize = 1 + if (height > maxSize || width > maxSize) { + val halfHeight = height / 2 + val halfWidth = width / 2 + + while ((halfHeight / inSampleSize) >= maxSize && (halfWidth / inSampleSize) >= maxSize) { + inSampleSize *= 2 + } + } + return inSampleSize + } + /** * Delete custom cover of the manga from the cache *