2017-06-12 1 views
0

Ich habe einen benutzerdefinierten Decoder, der Conceal verwendet, um verschlüsselte Bilder aus dem lokalen Speicher zu laden.Fresco benutzerdefinierte Bild Decoder Größenanpassung Optionen werden nicht angewendet

Alles funktioniert (Bilder werden angezeigt), aber die Leistung ist schrecklich, wenn lokale Kamerabilder geladen werden, da kein Downsampling oder Bitmap-Größenanpassung angewendet wird, wenn der eigentliche JPEG-Decoder funktioniert.

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) 
      } 
     } 
    } 
} 

Die Art und Weise die Anfrage ziemlich einfach gemacht wird

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() 

Warum Resize-Optionen ignoriert, gibt es eine weitere Option, die ich für den Decoder bin fehlt?

Antwort

1

Ich glaube, dass die Größenänderung vor der Decodierung erfolgt (d. H. Das Bild wird in ein kleineres JPEG umcodiert) - und nur JPEG wird jetzt unterstützt.

Werfen Sie einen Blick auf ResizeAndRotateProducer.

+0

Interessant, das scheint das Stück zu sein, das ich vermisse. Irgendeine Idee darüber, wie man diesen Produzenten für meinen Decoder integriert? Es sieht nicht so aus, als gäbe es einen einfachen Weg, um einen ProducerContext zu erhalten, um die Anfrage von meinem Decoder zu extrahieren. – minivac

+0

Ich denke, der einfachste Weg dies zu tun ist, einen benutzerdefinierten Decoder zu liefern, der entschlüsselt und dekodiert. Wir haben noch nicht viel Dokumentation dafür, da sich dies noch ein wenig ändert, aber Sie können sich die Beispiel-App für die Unterstützung von SVG/Keyframes ansehen. Sie fügen ein neues Bildformat, einen Header-Checker und dann einen Entschlüsselungsdecoder hinzu, der nach Dekodierung intern an den Standarddecoder weiterleitet. –

0

Nach dem Beispiel relevant Implementierung von Alexander Oprisnik gegeben ich mit meiner benutzerdefinierten Decoder habe Ändern der Größe arbeiten, das sind die relevanten Teile der Lösung:

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 
} 

Der Trick ist CryptoDecodeOptions zu verwenden, die einfach einen Verweis hält an die Anfrage, die die Größenänderungsparameter enthält. Der Rest des Codes ist eine Vereinfachung der Methode doTransform von ResizeAndRotateProducer. Die Rotation wird ignoriert, da meine Anwendung dies nicht benötigt.

Verwandte Themen