不应用Fresco自定义图像解码器调整大小选项

时间:2017-06-12 17:21:53

标签: android fresco facebook-conceal

我有一个自定义解码器,它使用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()

为什么忽略调整大小选项,是否还有其他选项,我缺少解码器?

2 个答案:

答案 0 :(得分:1)

我相信在解码之前完成调整大小(即图像被转码为较小的JPEG) - 现在只支持JPEG。

看看ResizeAndRotateProducer

答案 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的简化。由于我的应用程序不需要旋转,因此忽略旋转。