2010-03-23 20 views
77

Tôi đang gặp sự cố với BitmapFactory.decodeStream(inputStream). Khi sử dụng nó mà không có tùy chọn, nó sẽ trả về một hình ảnh. Nhưng khi tôi sử dụng nó với các tùy chọn như trong .decodeStream(inputStream, null, options) nó không bao giờ trả về bitmap.BitmapFactory.decodeStream trả về null khi các tùy chọn được đặt

Những gì tôi đang cố gắng làm là downsample một Bitmap trước khi tôi thực sự tải nó để tiết kiệm bộ nhớ. Tôi đã đọc một số hướng dẫn tốt, nhưng không có hướng dẫn nào sử dụng .decodeStream.

làm việc tốt

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

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

KHÔNG LÀM VIỆC

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

Đầu ra từ câu lệnh System.out.println ("Samplesize:" ...) của bạn là gì? Chỉ ra rằng options.inSampleSize là một giá trị chấp nhận được? –

+0

Có, nó trả về giá trị chấp nhận được mỗi lần. –

+0

Đã xóa tuyên bố do lỗi đang được gỡ lỗi. –

Trả lời

98

Vấn đề là khi bạn đã sử dụng InputStream từ HttpUrlConnection để tìm nạp siêu dữ liệu hình ảnh, bạn không thể tua lại và sử dụng lại cùng một InputStream.

Vì vậy, bạn phải tạo một InputStream mới để lấy mẫu thực tế của hình ảnh.

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

Điều này có nghĩa là hình ảnh phải tải xuống hai lần? Một lần để có được kích thước và một lần để có được dữ liệu pixel? – user123321

+1

@ Robert có thể bạn nên giải thích hành vi cụ thể này để những người dùng khác có ý tưởng rõ ràng về điều đó –

+1

Tôi đã tự hỏi tại sao nó không hoạt động với cùng một luồng đầu vào, nhờ lời giải thích ngắn gọn – kabuto178

3

Tôi nghĩ rằng vấn đề là với "tính toán quy mô yếu tố" logic vì phần còn lại của các mã có vẻ đúng với tôi (giả sử tất nhiên rằng InputStream không phải là null). Sẽ tốt hơn nếu bạn có thể tính toán tất cả logic tính toán kích thước từ thói quen này thành một phương thức (gọi nó là calculateScaleFactor() hoặc bất kỳ thứ gì) và kiểm tra phương thức đó một cách độc lập trước tiên.

Cái gì như:

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

và thử nghiệm getScaleFactor (...) một cách độc lập.

Nó cũng sẽ giúp bao quanh toàn bộ mã với khối try..catch {}, nếu chưa hoàn thành.

+0

Cảm ơn rất nhiều cho câu trả lời! Tôi đã thử đặt giá trị int cuối cùng như 'options.inSampleSize = 2'. Nhưng kết quả là trong cùng một vấn đề. Logcat đọc 'SkImageDecoder :: Factory trả về null', cho mọi hình ảnh mà tôi đã cố giải mã. Chạy mã bên trong khối thử/nắm bắt sẽ không giúp tôi vì nó không ném bất cứ thứ gì, phải không? Tuy nhiên BitmapFactory.decodeStream không trả lại null nếu nó không thể tạo ra một img, mà nó không thể khi tôi cố gắng sử dụng một sampleSize. –

+0

Điều này thật kỳ lạ. Bạn có thể thử thay đổi kích thước một số bitmap đi kèm trong tài nguyên của bạn không? Giống như mở một tệp tài nguyên và cố gắng giải mã nó. Nếu bạn có thể làm điều đó có thể có một số vấn đề với dòng từ xa mà gây ra giải mã thất bại. – Samuh

+0

BitmapFactory.decodeResource (this.getResources(), R.drawable.icon, tùy chọn) == null) hoạt động tốt với việc lấy mẫu lại. BitmapFactory.decodeStream đầu tiên với tùy chọn.inJustDecodeBounds = true hoạt động và trả về các tùy chọn tốt. Nhưng BitmapFactory.decodeStream sau với tùy chọn.inJustDecodeBounds = false không thành công mỗi lần. –

24

Thử bọc InputStream bằng BufferedInputStream.

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

nó có luôn hoạt động cho bạn không? vì một số lý do, tôi nhận được null trên một số trường hợp rất cụ thể bằng cách sử dụng phương pháp này. Tôi đã viết một bài về nó ở đây: http://stackoverflow.com/questions/17774442/how-to-get-bitmap-information-and-then-decode-bitmap-from-internet-inputstream –

+1

nó làm việc vì vậy tôi upvoted nó nhưng is.available() doc đi kèm với cảnh báo rằng nó chỉ nên được sử dụng để kiểm tra xem luồng có trống hay không và không phải để tính toán kích thước vì điều này là không đáng tin cậy. –

+0

bị bỏ phiếu xuống, nhưng kết nối đầu vào được đề cập là kết nối HTTP và đặt lại() sẽ không hoạt động .... –

0

Bạn có thể chuyển đổi InputStream thành mảng byte và sử dụng decodeByteArray(). Ví dụ:

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; 
} 
Các vấn đề liên quan