2011-01-08 15 views
8

Ho bisogno di ridimensionare un'immagine prima di crearla e voglio farlo solo se supera i 1024KB (per esempio).Come conoscere una dimensione Bitmap da InputStream prima di creare la bitmap?

Effettuando le operazioni seguenti è possibile ridimensionare l'immagine ma è necessario ridimensionare solo quelle più grandi rispetto alla dimensione specificata.

Bitmap bmImg = null; 
InputStream is = url.openStream(); 
BitmapFactory.Options opts = new BitmapFactory.Options(); 
opts.inSampleSize = 10; 
bmImg = BitmapFactory.decodeStream(is,null,opts); 

Come posso ottenere la dimensione di Bitmap? (Sono felice di sapere la quantità di byte, non le dimensioni dopo la decompressione).

Edit:

sto cercando questo:

BitmapFactory.Options opts = new BitmapFactory.Options(); 
opts.inJustDecodeBounds = true; 
Bitmap bmImg=BitmapFactory.decodeStream(is,null,opts); 
Log.e("optwidth",opts.outWidth+""); 
Bitmap bmImg1 = BitmapFactory.decodeStream(is); 

La prima volta che uso l'InputStream (è) per decodificare con i "inJustDecodeBounds" funziona bene e posso ottenere la Dimensioni bitmap. Il problema è che la seconda volta che lo uso per decodificare effettivamente l'immagine, non viene mostrata alcuna immagine.

Cosa sto sbagliando?

risposta

0

Non è possibile ottenere la dimensione da un flusso per la sua stessa definizione. Se lo desideri, puoi caricare il flusso in un array di byte, ottenere il conteggio dei byte da quello. È inoltre possibile utilizzare BitmapFactory per decodificare direttamente un array di byte, in modo che possa funzionare abbastanza bene.

InputStream in = ... 
ByteArrayOutputStream bos = new ByteArrayOutputStream(); 
int i; 
while ((i = in.read()) != -1) { 
    bos.write(i); 
} 
byte[] byteArray = bos.toByteArray(); 
if (byteArray.length > SOME_VALUE) { 
    // do things here 
} 
BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length); 
+0

Questo in realtà funziona, ma è molto più lento. – sergi

-2

Non è possibile ottenere la dimensione di una bitmap, non esiste un modo integrato. Puoi comunque ottenere la dimensione del contenuto (un file), che è quello che probabilmente dovrai fare. Qualcosa del genere dovrebbe funzionare.

int file_size = url.getContentLength(); 
+0

url è di quale tipo? Non trovo questo metodo in java.net.URL – Vikas

+0

Questo utente si riferisce in realtà a URLConnection [vedi qui] (http://developer.android.com/reference/java/net/URLConnection.html). Ma questo metodo in realtà non è raccomandato .. si basa sul server che fornisce un'intestazione Content-Length valida (e questa intestazione può essere fuorviante se i dati sono compressi per esempio). – kwazi

2

Se si conosce il formato del file che si potrebbe essere in grado di leggere l'intestazione del file. Abitualmente l'intestazione contiene le dimensioni e il formato dell'immagine. E fortunatamente per te, questo è sempre il primo byte del file.

Potrebbe non essere possibile per alcuni tipi.

PNG, JPEG, BMP, TGA

appena letto l'intestazione e qualche file potrebbe essere necessario indovinare la larghezza della prima. Ad esempio, il PNG non sembra includere widht/height ma dovresti essere in grado di leggere la larghezza e indovinare un'altezza con la dimensione/rapporto in pixel.

Edit: Il mio male ho pensato che si voleva scalare utilizzando la dimensione ... Sì ContentLenght dovrebbe aiutare ma a volte restituirà 0 se il server non imposta la giusta dimensione nell'intestazione HTTP. In tal caso è necessario conoscere la dimensione restituita. Ma comunque, puoi salvare un bytearray e una volta caricato tutto, controlla la lunghezza dell'array. se è più grande di 1024kb, ridimensiona l'immagine. E salvalo o fai quello che vuoi con esso.

0

La maggior parte degli stream viene consumata quando si legge da essi. Nel secondo blocco di codice, il flusso viene consumato per la prima volta come bitmap. Quando invochi decodeString una seconda volta, lo stream è "vuoto" (se solo nel senso che tutti i dati sono già stati letti).

La risposta migliore sarà probabilmente quella di un monaco, ma forse con solo un piccolo codice in più per garantire che non stai leggendo un file gigantesco in memoria.Raccomando solo un assegno mentre lo stai riversando in un buffer per le dimensioni (ad esempio, fermati a 10 MB); Non sono sicuro di quanto bene Android gestirà un'eccezione di memoria insufficiente.

+0

Il problema è che la soluzione di Monkjack è molto più lenta. Android si blocca appena quando provo ad allocare un'immagine troppo grande. – sergi

2

È possibile avvolgere is con un new BufferedInputStream(is); e utilizzare i metodi mark(int readlimit) e reset() per rileggere lo stream. Dovrai decidere cosa impostare readlimit per consentire a quest'ultimo di leggere l'intera intestazione, i collegamenti forniti da Loïc dovrebbero aiutare qui.

BufferedInputStream bis = new BufferedInputStream(is); 
bis.mark(1024 * 1024); 
BitmapFactory.Options opts = new BitmapFactory.Options(); 
opts.inJustDecodeBounds = true; 
Bitmap bmImg=BitmapFactory.decodeStream(bis,null,opts); 
Log.e("optwidth",opts.outWidth+""); 
bis.reset(); 
Bitmap bmImg1 = BitmapFactory.decodeStream(bis); 

ho lasciato fuori appropriata gestione per il metodo reset() errore, genera un IOException se avete letto più di readlimit byte dal flusso.

+0

Sto provando questo e sto ottenendo alcune immagini casuali: java.io.IOException: Mark è stato invalidato L'ho preso in immagini di 75KB, 120KB e non in immagini di 45KB o 700KB .. – sergi

+0

@ sergi il parametro readLimit deve essere uguale a bis.available(), in modo che il segno non venga invalidato durante la lettura del flusso per la prima decodifica. –

5

Sono un noob quindi non posso commentare direttamente la risposta di monkjack. Il motivo per cui la sua risposta è così lenta è che sta copiando un byte alla volta. L'uso di un buffer pari a 1K migliorerà in modo significativo le prestazioni.

InputStream in = getContentResolver().openInputStream(docUri); 

ByteArrayOutputStream bos = new ByteArrayOutputStream(); 
int i; 
byte[] buffer = new byte[1024]; 
while ((i = in.read(buffer)) != -1) { 
    bos.write(buffer); 
} 
byte[] docbuffer = bos.toByteArray(); 
+0

@sergi: si dovrebbe accettare questa risposta –

+1

Questo non funzionerà se lo scopo del downscaling è quello di prevenire OutOfMemoryException. – Ken

0

Se si downscaling per prevenire OutOfMemoryException come me, questa soluzione può funzionare meglio per voi (edificio su risposte fornite qui):

// <initialize stream> 

// figure out number of bytes in stream 
byte[] bytes = new byte[1024]; 
int i; 
int byteCount = 0; 
while ((i = stream.read(bytes)) != -1) { 
    byteCount += i; 
} 

// <reset stream> 

Bitmap bmImg; 

// downsample if necessary 
if (byteCount > MAX_SIZE) { 
    // scale down photo if too large 
    BitmapFactory.Options options = new BitmapFactory.Options(); 
    // scale to nearest power of 2 - faster 
    options.inSampleSize = (int)Math.pow(2, (int)(Math.log10(Math.sqrt((double)byteCount/MAX_SIZE))/Math.log10(2) + 1)); // 2^(log2(lengthRatio)+1) 
    options.inPreferredConfig = Bitmap.Config.ARGB_8888; 
    bmImg = BitmapFactory.decodeStream(stream, null, options); 
} 
else { 
    bmImg = BitmapFactory.decodeStream(stream); 
} 

ho evitato l'uso di mark(int) e reset() perché nella mia caso non c'è limite superiore alla dimensione del flusso. Quindi, come resettare il flusso può dipendere dalla tua implementazione. Il mio flusso è stato specificato da un Uri, quindi ho solo usato:

stream.close(); 
stream = this.getContentResolver().openInputStream(uri); // where this is an Activity 

per ripristinare il flusso.

2

Se vi trovate cercando di evitare che OutOfMemory o qualcos'altro ...

è possibile creare un buffer con i tuoi dati di immagine,

BitmapFactory.Options buffer = new BitmapFactory.Options(); 
buffer.inJustDecodeBounds = true; 
BitmapFactory.decodeByteArray(photo, 0, photo.length, buffer); 

allora si può ottenere le dimensioni utilizzando buffer.outHeight, buffer.outWidth ..

Se si desidera ridimensionare, si utilizza solo buffer.isSampleSize ma ho notato che si imposta un valore "10" ... che penso sia sbagliato ... sampleSize dovrebbe ottenere un 2^valore .. come 2,4,8,16 ecc.

Infine, dopo aver impostato sampleSize, è possibile creare la bitmap come sempre.

Bitmap bmp = BitmapFactory.decodeByteArray(photo, 0, photo.length, buffer); 

spero di aiutarti!

PS: SQS mio inglese = (!!!

0

Controllare logcat, se si dispone di un errore che dice "Bitmap too large to be uploaded into a texture" allora avete bisogno di fare quanto segue La mia risposta è simile a @Sergio A. ma è possibile utilizzare. BitmapFactory.decodeStream direttamente anziché decodeByteArray.

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

// Pass in a Rect if you want to know the padding. 
BitmapFactory.decodeStream(inputStream,null,options); 

Questo otterrà solo la dimensione dell'immagine (non l'intera immagine stessa). È possibile isolare la larghezza e l'altezza utilizzando il seguente codice.

final int height = options.outHeight; 
final int width = options.outWidth; 

È possibile utilizzare questi valori (insieme con la larghezza e l'altezza desiderate - molto probabilmente sulla base del IMAGEVIEW o lo schermo) per calcolare options.inSampleSize. Non ho intenzione di entrare nel modo di farlo, ma Google ha fornito un tutorial here.

Spero che questo aiuti :)

Problemi correlati