2010-03-23 7 views
77

Tengo problemas con BitmapFactory.decodeStream(inputStream). Al usarlo sin opciones, devolverá una imagen. Pero cuando lo uso con opciones como en .decodeStream(inputStream, null, options), nunca devuelve Bitmaps.BitmapFactory.decodeStream devuelve nulo cuando se establecen las opciones

Lo que intento hacer es reducir la resolución de un mapa de bits antes de que realmente lo cargue para ahorrar memoria. He leído algunas buenas guías, pero ninguna usando .decodeStream.

funciona bien

URL url = new URL(sUrl); 
HttpURLConnection connection = (HttpURLConnection) url.openConnection(); 

InputStream is = connection.getInputStream(); 
Bitmap img = BitmapFactory.decodeStream(is, null, options); 

no funciona

InputStream is = connection.getInputStream(); 
Bitmap img = BitmapFactory.decodeStream(is, null, options); 

InputStream is = connection.getInputStream(); 

Options options = new BitmapFactory.Options(); 
options.inJustDecodeBounds = true; 

BitmapFactory.decodeStream(is, null, options); 

Boolean scaleByHeight = Math.abs(options.outHeight - TARGET_HEIGHT) >= Math.abs(options.outWidth - TARGET_WIDTH); 

if (options.outHeight * options.outWidth * 2 >= 200*100*2){ 
    // Load, scaling to smallest power of 2 that'll get it <= desired dimensions 
    double sampleSize = scaleByHeight 
    ? options.outHeight/TARGET_HEIGHT 
    : options.outWidth/TARGET_WIDTH; 
    options.inSampleSize = 
     (int)Math.pow(2d, Math.floor(
     Math.log(sampleSize)/Math.log(2d))); 
} 

// Do the actual decoding 
options.inJustDecodeBounds = false; 
Bitmap img = BitmapFactory.decodeStream(is, null, options); 
+1

¿Cuál es el resultado de su instrucción System.out.println ("Samplesize:" ...)? ¿Indica que options.inSampleSize es un valor aceptable? –

+0

Sí, devuelve un valor aceptable cada vez. –

+0

Se ha eliminado la declaración debido a que se está depurando. –

Respuesta

98

El problema es que una vez que ha utilizado un InputStream de HttpUrlConnection para buscar metadatos de imagen, no puede rebobinar y utilizar el mismo InputStream nuevamente.

Por lo tanto, debe crear un nuevo InputStream para el muestreo real de la imagen.

Options options = new BitmapFactory.Options(); 
    options.inJustDecodeBounds = true; 

    BitmapFactory.decodeStream(is, null, options); 

    Boolean scaleByHeight = Math.abs(options.outHeight - TARGET_HEIGHT) >= Math.abs(options.outWidth - TARGET_WIDTH); 

    if(options.outHeight * options.outWidth * 2 >= 200*200*2){ 
     // Load, scaling to smallest power of 2 that'll get it <= desired dimensions 
     double sampleSize = scaleByHeight 
       ? options.outHeight/TARGET_HEIGHT 
       : options.outWidth/TARGET_WIDTH; 
     options.inSampleSize = 
       (int)Math.pow(2d, Math.floor(
       Math.log(sampleSize)/Math.log(2d))); 
    } 

     // Do the actual decoding 
     options.inJustDecodeBounds = false; 

     is.close(); 
     is = getHTTPConnectionInputStream(sUrl); 
     Bitmap img = BitmapFactory.decodeStream(is, null, options); 
     is.close(); 
+14

¿Esto significa que la imagen debe descargarse dos veces? Una vez para obtener el tamaño y una vez para obtener los datos de píxeles? – user123321

+1

@Robert probablemente deberías explicar este comportamiento particular para que los otros usuarios tengan una idea clara de eso –

+1

Me preguntaba por qué no funcionaría con la misma corriente de entrada, gracias por la breve explicación – kabuto178

3

Creo que el problema es con la lógica "calcular-factor de escala", porque el resto del código es correcto para mí (asumiendo por supuesto que InputStream No es nulo).

Sería mejor si puede factorizar toda la lógica de cálculo de tamaño de esta rutina en un método (llámalo calculateScaleFactor() o lo que sea) y pruebe ese método de forma independiente primero.

Algo así como:

// Get the stream 
InputStream is = mUrl.openStream(); 

// get the Image bounds 
BitmapFactory.Options options=new BitmapFactory.Options(); 
options.inJustDecodeBounds = true; 

bitmap = BitmapFactory.decodeStream(is,null,options); 

//get actual width x height of the image and calculate the scale factor 
options.inSampleSize = getScaleFactor(options.outWidth,options.outHeight, 
       view.getWidth(),view.getHeight()); 

options.inJustDecodeBounds = false; 
bitmap=BitmapFactory.decodeStream(mUrl.openStream(),null,options); 

y la prueba getScaleFactor (...) de forma independiente.

También ayudará a rodear todo el código con el bloque try..catch {}, si no lo ha hecho ya.

+0

¡Muchas gracias por la respuesta! Intenté establecer un valor int final como 'options.inSampleSize = 2'. Pero resulta en los mismos problemas. Logcat lee 'SkImageDecoder :: Factory returned null', por cada imagen que intenté decodificar. Ejecutar el código dentro de un bloque try/catch no me ayudaría ya que no arroja nada, ¿verdad? Sin embargo, BitmapFactory.decodeStream devuelve null si no puede crear un img, que no funciona cuando intento usar sampleSize. –

+0

Esto es extraño. ¿Puedes intentar cambiar el tamaño de un mapa de bits incluido en tu recurso? Como abrir un archivo de recursos e intentar descifrarlo. Si puede hacerlo, tal vez haya algún problema con la transmisión remota que está causando que la decodificación falle. – Samuh

+0

BitmapFactory.decodeResource (this.getResources(), R.drawable.icon, options) == null) funciona bien con el re-muestreo. El primer BitmapFactory.decodeStream con options.inJustDecodeBounds = true funciona y devuelve opciones simplemente bien. Pero el siguiente BitmapFactory.decodeStream con options.inJustDecodeBounds = false falla todo el tiempo. –

24

Try wrap InputStream con BufferedInputStream.

InputStream is = new BufferedInputStream(conn.getInputStream()); 
is.mark(is.available()); 
// Do the bound decoding 
// inJustDecodeBounds =true 
is.reset(); 
// Do the actual decoding 
+2

¿Siempre funcionó para ti? por alguna razón, obtengo nulo en algunos casos muy específicos usando este método. He escrito una publicación al respecto aquí: http://stackoverflow.com/questions/17774442/how-to-get-bitmap-information-and-then-decode-bitmap-from-internet-inputstream –

+1

funcionó, así que upvoted it pero is.available() doc viene con una advertencia de que solo debe usarse para verificar si la transmisión está vacía o no y no para calcular el tamaño, ya que no es confiable. –

+0

down-votada, pero la conexión de inputstream en cuestión es una conexión HTTP y el restablecimiento() no funcionará ... –

0

Puede convertir el InputStream en una matriz de bytes y usar decodeByteArray(). Por ejemplo,

public static Bitmap decodeSampledBitmapFromStream(InputStream inputStream, int reqWidth, int reqHeight) { 
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 
    try { 
     int n; 
     byte[] buffer = new byte[1024]; 
     while ((n = inputStream.read(buffer)) > 0) { 
      outputStream.write(buffer, 0, n); 
     } 
     return decodeSampledBitmapFromByteArray(outputStream.toByteArray(), reqWidth, reqHeight); 
    } catch (IOException e) { 
     e.printStackTrace(); 
    } finally { 
     try { 
      outputStream.close(); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 
    } 
    return null; 
} 

public static Bitmap decodeSampledBitmapFromByteArray(byte[] data, int reqWidth, int reqHeight) { 
    BitmapFactory.Options options = new BitmapFactory.Options(); 
    options.inJustDecodeBounds = true; 
    BitmapFactory.decodeByteArray(data, 0, data.length, options); 
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); 
    options.inJustDecodeBounds = false; 
    return BitmapFactory.decodeByteArray(data, 0, data.length, options); 
} 

private static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int 
     reqHeight) { 
    int width = options.outWidth; 
    int height = options.outHeight; 
    int inSampleSize = 1; 
    if (width > reqWidth || height > reqHeight) { 
     int halfWidth = width/2; 
     int halfHeight = height/2; 
     while (halfWidth/inSampleSize >= reqWidth && halfHeight/inSampleSize >= reqHeight) { 
      inSampleSize *= 2; 
     } 
    } 
    return inSampleSize; 
} 
Cuestiones relacionadas