2010-03-23 17 views
77

Ho problemi con BitmapFactory.decodeStream(inputStream). Quando lo si utilizza senza opzioni, restituirà un'immagine. Ma quando lo uso con le opzioni come in .decodeStream(inputStream, null, options) non restituisce mai le bitmap.BitmapFactory.decodeStream restituisce null quando le opzioni sono impostate

Quello che sto cercando di fare è di ridimensionare un bitmap prima di caricarlo effettivamente per risparmiare memoria. Ho letto alcune buone guide, ma nessuna utilizza .decodeStream.

funziona bene

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

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

non funziona

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

Qual è l'output dall'istruzione System.out.println ("Samplesize:" ...)? Sta indicando che options.inSampleSize è un valore accettabile? –

+0

Sì, restituisce un valore accettabile ogni volta. –

+0

Rimossa l'istruzione a causa del suo debug. –

risposta

98

Il problema era che una volta utilizzato un InputStream da un HttpUrlConnection per recuperare i metadati dell'immagine, non è possibile riavvolgere e utilizzare di nuovo lo stesso InputStream.

Pertanto è necessario creare un nuovo InputStream per il campionamento effettivo dell'immagine.

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

Significa che l'immagine deve essere caricata due volte? Una volta per ottenere le dimensioni e una volta per ottenere i dati dei pixel? – user123321

+1

@Robert probabilmente dovresti spiegare questo particolare comportamento in modo che gli altri utenti abbiano una chiara idea di quello –

+1

Mi stavo chiedendo perché non avrebbe funzionato con lo stesso inputstream me stesso, grazie per la breve spiegazione – kabuto178

3

Credo che il problema è con la logica "calcolare scala-factor", perché resto del codice sembra corretto a me (assumendo, naturalmente, che InputStream non è nullo).

Sarebbe meglio se si potesse calcolare tutta la logica di calcolo delle dimensioni da questa routine in un metodo (chiamarlo calculateScaleFactor() o qualsiasi altra cosa) e testare prima questo metodo in modo indipendente.

Qualcosa di simile:

// 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); 

e prova getScaleFactor (...) in modo indipendente.

Aiuterà anche a circondare l'intero codice con il blocco try..catch {}, se non è già stato fatto.

+0

Grazie mille per la risposta! Ho provato a impostare un valore int finale come "options.inSampleSize = 2". Ma si verificano gli stessi problemi. Logcat legge 'SkImageDecoder :: Factory restituito null', per ogni immagine che ho provato a decodificare. L'esecuzione del codice all'interno di un blocco try/catch non mi aiuta poiché non genera niente, giusto? Tuttavia BitmapFactory.decodeStream restituisce null se non è possibile creare un img, che non può essere utilizzato quando provo a utilizzare sampleSize. –

+0

Questo è strano. Puoi provare a ridimensionare alcuni Bitmap in bundle nella tua risorsa? Come aprire un file di risorse e provare a decodificarlo. Se è possibile farlo, c'è qualche problema con lo stream remoto che sta causando il fallimento della decodifica. – Samuh

+0

BitmapFactory.decodeResource (this.getResources(), R.drawable.icon, options) == null) funziona correttamente con il ricampionamento. Il primo BitmapFactory.decodeStream con options.inJustDecodeBounds = true funziona e restituisce le opzioni bene. Ma il seguente BitmapFactory.decodeStream con options.inJustDecodeBounds = false fallisce ogni volta. –

24

Provare a capo 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

ha funzionato sempre per te? per qualche ragione, ottengo null su alcuni casi molto specifici usando questo metodo. Ho scritto un post su questo qui: http://stackoverflow.com/questions/17774442/how-to-get-bitmap-information-and-then-decode-bitmap-from-internet-inputstream –

+1

ha funzionato così io lo ha messo in circolazione ma il documento is.available() viene fornito con avvertimento che dovrebbe essere usato solo per verificare se il flusso è vuoto o meno e non per calcolare le dimensioni in quanto non è affidabile. –

+0

down-votato, ma la connessione inputstream in questione è una connessione HTTP e reset() non funzionerà .... –

0

È possibile convertire InputStream in un array di byte e utilizzare decodeByteArray(). Ad esempio,

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; 
} 
Problemi correlati