我有一个自定义解码器,它使用Conceal从本地存储加载加密图像。
所有工作(显示图像)但加载本地相机图像时性能很差,因为当实际JPEG解码器工作时,根本不应用缩减采样或位图大小调整。
class CryptoImageDecoder(val crypto: Crypto, val poolFactory: PoolFactory) : ImageDecoder {
val defaultDecoder = DefaultImageDecoder(null,
Fresco.getImagePipelineFactory().platformDecoder,
Bitmap.Config.ARGB_8888)
override fun decode(encodedImage: EncodedImage,
length: Int,
qualityInfo: QualityInfo?,
options: ImageDecodeOptions?): CloseableImage {
encodedImage.use {
val inputStream = encodedImage.inputStream
inputStream.skip(CRYPTO_HEADER_BYTES.size.toLong()) //Skip header
val cipherInputStream = crypto.getCipherInputStream(inputStream, CRYPTO_ENTITY)
cipherInputStream.use {
val bytes = poolFactory.pooledByteBufferFactory.newByteBuffer(cipherInputStream)
val bytesClosable = CloseableReference.of(bytes)
val clearEncodedImage = EncodedImage(bytesClosable)
//This is always 1, no matter what resizeOptions I use in the request
//clearEncodedImage.sampleSize = how to calculate this?
clearEncodedImage.encodedCacheKey = encodedImage.encodedCacheKey
return defaultDecoder.decode(clearEncodedImage, bytes.size(), qualityInfo, options)
}
}
}
}
请求的完成方式非常简单
val request = ImageRequestBuilder.newBuilderWithSource(attachment.sourceImageUri)
.setSource(attachment.sourceImageUri)
.setResizeOptions(ResizeOptions.forSquareSize(300))
.build()
val controller = Fresco.newDraweeControllerBuilder()
.setOldController(holder.draweeView.controller)
.setImageRequest(request)
.build()
为什么忽略调整大小选项,是否还有其他选项,我缺少解码器?
答案 0 :(得分:1)
我相信在解码之前完成调整大小(即图像被转码为较小的JPEG) - 现在只支持JPEG。
答案 1 :(得分:0)
按照Alexander Oprisnik给出的示例相关实现,我调整了使用自定义解码器的大小,这些是解决方案的相关部分:
class CryptoImageDecoder(val crypto: Crypto, val poolFactory: PoolFactory) : ImageDecoder {
val defaultDecoder by lazy {
DefaultImageDecoder(null,
Fresco.getImagePipelineFactory().platformDecoder,
Bitmap.Config.ARGB_8888)
}
override fun decode(encodedImage: EncodedImage,
length: Int,
qualityInfo: QualityInfo?,
options: ImageDecodeOptions?): CloseableImage {
var cipherInputStream: InputStream? = null
var clearEncodedImage: EncodedImage? = null
var transcodedImage: EncodedImage? = null
var transcodedRef: CloseableReference<PooledByteBuffer>? = null
try {
val inputStream = encodedImage.inputStream
inputStream.skip(CRYPTO_HEADER_BYTES.size.toLong()) //Skip header
cipherInputStream = crypto.getCipherInputStream(inputStream, CRYPTO_ENTITY)
val bytes = poolFactory.pooledByteBufferFactory.newByteBuffer(cipherInputStream)
val bytesClosable = CloseableReference.of(bytes)
clearEncodedImage = EncodedImage(bytesClosable)
val dimensions = BitmapUtil.decodeDimensions(clearEncodedImage.inputStream)
clearEncodedImage.width = dimensions?.first ?: -1
clearEncodedImage.height = dimensions?.second ?: -1
clearEncodedImage.rotationAngle = 0
val decodeOptions = options as? CryptoDecodeOptions ?: error("ImageOptions should be CryptoDecodeOptions")
val imageRequest = decodeOptions.imageRequest
val downsampleRatio = DownsampleUtil.determineSampleSize(imageRequest, clearEncodedImage)
val downsampleNumerator = calculateDownsampleNumerator(downsampleRatio)
if (downsampleNumerator == JpegTranscoder.SCALE_DENOMINATOR) {
//No need to apply any transformation
return defaultDecoder.decode(clearEncodedImage, bytes.size(), qualityInfo, options)
}
val outputStream = poolFactory.pooledByteBufferFactory.newOutputStream()
JpegTranscoder.transcodeJpeg(
PooledByteBufferInputStream(bytes),
outputStream,
0, //Rotation is ignored
downsampleNumerator,
DEFAULT_JPEG_QUALITY)
val bb = outputStream.toByteBuffer()
transcodedRef = CloseableReference.of(bb)
transcodedImage = EncodedImage(transcodedRef)
transcodedImage.encodedCacheKey = encodedImage.encodedCacheKey
return defaultDecoder.decode(transcodedImage, bb.size(), qualityInfo, options)
} catch (ex: Exception) {
Grove.e { "Something went wrong decoding the image" }
throw ex
} finally {
cipherInputStream?.close()
clearEncodedImage?.close()
transcodedImage?.close()
transcodedRef?.close()
}
}
private fun calculateDownsampleNumerator(downsampleRatio: Int): Int {
return Math.max(1, JpegTranscoder.SCALE_DENOMINATOR / downsampleRatio)
}
}
/**
* Dummy wrapper that hold a reference to the request that used this options, required
* to perform jpeg resizing
*/
class CryptoDecodeOptions(builder: ImageDecodeOptionsBuilder) : ImageDecodeOptions(builder) {
internal lateinit var imageRequest: ImageRequest
}
/**
* Decoded images need the actual request to determine resize operations since
* transcoding is not possible with encrypted images.
*/
fun ImageRequestBuilder.buildForCrypto(): ImageRequest {
val cryptoDecodeOptions = CryptoDecodeOptions(ImageDecodeOptionsBuilder())
this.imageDecodeOptions = cryptoDecodeOptions
val request = this.build()
cryptoDecodeOptions.imageRequest = request
return request
}
诀窍是使用CryptoDecodeOptions
只保存对包含resize参数的请求的引用。代码的其余部分是ResizeAndRotateProducer中方法doTransform
的简化。由于我的应用程序不需要旋转,因此忽略旋转。