Skip to content

Commit 731cf68

Browse files
authored
fix: GPU crash when setting cover from downloaded chapters. (#493)
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
1 parent e1511a1 commit 731cf68

File tree

1 file changed

+44
-1
lines changed

1 file changed

+44
-1
lines changed

app/src/main/java/eu/kanade/tachiyomi/data/cache/CoverCache.kt

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,28 @@ class CoverCache(val context: Context) {
175175
@Throws(IOException::class)
176176
fun setCustomCoverToCache(manga: Manga, inputStream: InputStream) {
177177
val maxTextureSize = 4096f
178-
var bitmap = BitmapFactory.decodeStream(inputStream)
178+
179+
val imageBytes = inputStream.readBytes()
180+
inputStream.close()
181+
182+
val bounds = BitmapFactory.Options().apply {
183+
inJustDecodeBounds = true
184+
}
185+
BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size, bounds)
186+
187+
val originalWidth = bounds.outWidth
188+
val originalHeight = bounds.outHeight
189+
190+
val sampleSize = calculateInSampleSize(originalWidth, originalHeight, maxTextureSize.toInt())
191+
192+
val decodeOptions = BitmapFactory.Options().apply {
193+
inSampleSize = sampleSize
194+
inPreferredConfig = if (sampleSize > 1) Bitmap.Config.RGB_565 else Bitmap.Config.ARGB_8888
195+
}
196+
197+
var bitmap = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size, decodeOptions)
198+
?: throw IOException("Failed to decode image")
199+
179200
if (maxOf(bitmap.width, bitmap.height) > maxTextureSize) {
180201
val widthRatio = bitmap.width / maxTextureSize
181202
val heightRatio = bitmap.height / maxTextureSize
@@ -209,6 +230,28 @@ class CoverCache(val context: Context) {
209230
}
210231
}
211232

233+
/**
234+
* Calculate the largest inSampleSize value that is a power of 2 and keeps both
235+
* height and width larger than the requested height and width.
236+
*
237+
* @param width the original image width.
238+
* @param height the original image height.
239+
* @param maxSize the maximum allowed dimension.
240+
* @return the sample size to use for BitmapFactory.Options.
241+
*/
242+
private fun calculateInSampleSize(width: Int, height: Int, maxSize: Int): Int {
243+
var inSampleSize = 1
244+
if (height > maxSize || width > maxSize) {
245+
val halfHeight = height / 2
246+
val halfWidth = width / 2
247+
248+
while ((halfHeight / inSampleSize) >= maxSize && (halfWidth / inSampleSize) >= maxSize) {
249+
inSampleSize *= 2
250+
}
251+
}
252+
return inSampleSize
253+
}
254+
212255
/**
213256
* Delete custom cover of the manga from the cache
214257
*

0 commit comments

Comments
 (0)