mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-20 00:29:11 +01:00
Improve memory usage of new APNG renderer by making it streaming.
This commit is contained in:
committed by
Cody Henthorne
parent
48374e6950
commit
25b01a30be
@@ -33,7 +33,7 @@ import org.thoughtcrime.securesms.giph.model.ChunkedImageUrl;
|
||||
import org.thoughtcrime.securesms.glide.cache.ApngDrawableTranscoder;
|
||||
import org.thoughtcrime.securesms.glide.cache.ApngFrameDrawableTranscoder;
|
||||
import org.thoughtcrime.securesms.glide.cache.ApngInputStreamFactoryResourceDecoder;
|
||||
import org.thoughtcrime.securesms.glide.cache.ApngInputStreamResourceDecoder;
|
||||
import org.thoughtcrime.securesms.glide.cache.EncryptedApngCacheDecoder;
|
||||
import org.thoughtcrime.securesms.glide.cache.ByteBufferApngDecoder;
|
||||
import org.thoughtcrime.securesms.glide.cache.EncryptedApngCacheEncoder;
|
||||
import org.thoughtcrime.securesms.glide.cache.EncryptedApngResourceEncoder;
|
||||
@@ -93,10 +93,9 @@ public class SignalGlideComponents implements RegisterGlideComponents {
|
||||
|
||||
|
||||
if (SignalStore.labs().getNewApngRenderer()) {
|
||||
registry.prepend(InputStream.class, ApngDecoder.class, new ApngInputStreamResourceDecoder());
|
||||
registry.prepend(InputStreamFactory.class, ApngDecoder.class, new ApngInputStreamFactoryResourceDecoder());
|
||||
registry.prepend(ApngDecoder.class, new EncryptedApngResourceEncoder(secret));
|
||||
registry.prepend(File.class, ApngDecoder.class, new EncryptedCacheDecoder<>(secret, new ApngInputStreamResourceDecoder()));
|
||||
registry.prepend(File.class, ApngDecoder.class, new EncryptedApngCacheDecoder(secret));
|
||||
registry.register(ApngDecoder.class, Drawable.class, new ApngDrawableTranscoder());
|
||||
} else {
|
||||
ByteBufferApngDecoder byteBufferApngDecoder = new ByteBufferApngDecoder();
|
||||
|
||||
@@ -9,10 +9,8 @@ import com.bumptech.glide.load.Options
|
||||
import com.bumptech.glide.load.ResourceDecoder
|
||||
import com.bumptech.glide.load.engine.Resource
|
||||
import org.signal.apng.ApngDecoder
|
||||
import org.signal.core.util.readFully
|
||||
import org.signal.glide.apng.ApngOptions
|
||||
import org.signal.glide.common.io.InputStreamFactory
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.IOException
|
||||
|
||||
class ApngInputStreamFactoryResourceDecoder : ResourceDecoder<InputStreamFactory, ApngDecoder> {
|
||||
@@ -27,8 +25,7 @@ class ApngInputStreamFactoryResourceDecoder : ResourceDecoder<InputStreamFactory
|
||||
|
||||
@Throws(IOException::class)
|
||||
override fun decode(source: InputStreamFactory, width: Int, height: Int, options: Options): Resource<ApngDecoder>? {
|
||||
val data: ByteArray = source.create().readFully()
|
||||
val decoder = ApngDecoder(ByteArrayInputStream(data))
|
||||
return ApngResource(decoder, data.size)
|
||||
val decoder = ApngDecoder.create { source.create() }
|
||||
return ApngResource(decoder)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Copyright 2024 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.thoughtcrime.securesms.glide.cache
|
||||
|
||||
import com.bumptech.glide.load.Options
|
||||
import com.bumptech.glide.load.ResourceDecoder
|
||||
import com.bumptech.glide.load.engine.Resource
|
||||
import org.signal.apng.ApngDecoder
|
||||
import org.signal.core.util.readFully
|
||||
import org.signal.core.util.stream.LimitedInputStream
|
||||
import org.signal.glide.apng.ApngOptions
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
|
||||
class ApngInputStreamResourceDecoder : ResourceDecoder<InputStream, ApngDecoder> {
|
||||
companion object {
|
||||
/** Set to match [com.bumptech.glide.load.data.InputStreamRewinder]'s read limit */
|
||||
private const val READ_LIMIT: Long = 5 * 1024 * 1024
|
||||
}
|
||||
|
||||
override fun handles(source: InputStream, options: Options): Boolean {
|
||||
return if (options.get(ApngOptions.ANIMATE)!!) {
|
||||
ApngDecoder.isApng(LimitedInputStream(source, READ_LIMIT))
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
override fun decode(source: InputStream, width: Int, height: Int, options: Options): Resource<ApngDecoder>? {
|
||||
val data: ByteArray = source.readFully()
|
||||
val decoder = ApngDecoder(ByteArrayInputStream(data))
|
||||
return ApngResource(decoder, data.size)
|
||||
}
|
||||
}
|
||||
@@ -8,14 +8,14 @@ package org.thoughtcrime.securesms.glide.cache
|
||||
import com.bumptech.glide.load.engine.Resource
|
||||
import org.signal.apng.ApngDecoder
|
||||
|
||||
class ApngResource(private val decoder: ApngDecoder, private val size: Int) : Resource<ApngDecoder> {
|
||||
class ApngResource(private val decoder: ApngDecoder) : Resource<ApngDecoder> {
|
||||
override fun getResourceClass(): Class<ApngDecoder> = ApngDecoder::class.java
|
||||
|
||||
override fun get(): ApngDecoder = decoder
|
||||
|
||||
override fun getSize(): Int = size
|
||||
override fun getSize(): Int = 0
|
||||
|
||||
override fun recycle() {
|
||||
decoder.inputStream.close()
|
||||
decoder.close()
|
||||
}
|
||||
}
|
||||
|
||||
45
app/src/main/java/org/thoughtcrime/securesms/glide/cache/EncryptedApngCacheDecoder.kt
vendored
Normal file
45
app/src/main/java/org/thoughtcrime/securesms/glide/cache/EncryptedApngCacheDecoder.kt
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2024 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.thoughtcrime.securesms.glide.cache
|
||||
|
||||
import com.bumptech.glide.load.Options
|
||||
import com.bumptech.glide.load.ResourceDecoder
|
||||
import com.bumptech.glide.load.engine.Resource
|
||||
import org.signal.apng.ApngDecoder
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.signal.core.util.stream.LimitedInputStream
|
||||
import org.signal.glide.apng.ApngOptions
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
|
||||
internal class EncryptedApngCacheDecoder(private val secret: ByteArray) : EncryptedCoder(), ResourceDecoder<File, ApngDecoder> {
|
||||
|
||||
companion object {
|
||||
private val TAG = Log.tag(EncryptedApngCacheDecoder::class.java)
|
||||
private const val READ_LIMIT: Long = 5 * 1024 * 1024
|
||||
}
|
||||
|
||||
override fun handles(source: File, options: Options): Boolean {
|
||||
if (options.get(ApngOptions.ANIMATE) != true) {
|
||||
return false
|
||||
}
|
||||
|
||||
return try {
|
||||
createEncryptedInputStream(secret, source).use { inputStream ->
|
||||
ApngDecoder.isApng(LimitedInputStream(inputStream, READ_LIMIT))
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
Log.w(TAG, e)
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
override fun decode(source: File, width: Int, height: Int, options: Options): Resource<ApngDecoder>? {
|
||||
val decoder = ApngDecoder.create { createEncryptedInputStream(secret, source) }
|
||||
return ApngResource(decoder)
|
||||
}
|
||||
}
|
||||
@@ -17,11 +17,11 @@ internal class EncryptedApngResourceEncoder(private val secret: ByteArray) : Enc
|
||||
|
||||
override fun encode(data: Resource<ApngDecoder>, file: File, options: Options): Boolean {
|
||||
try {
|
||||
val input = data.get().inputStream
|
||||
val input = data.get().streamFactory()
|
||||
val output = createEncryptedOutputStream(secret, file)
|
||||
|
||||
input.reset()
|
||||
input.copyTo(output)
|
||||
input.close()
|
||||
|
||||
return true
|
||||
} catch (e: IOException) {
|
||||
|
||||
Reference in New Issue
Block a user