2011-08-13 17 views
10

Tôi có một ứng dụng Android rất chuyên sâu về hình ảnh. Tôi hiện đang sử dụng Bitmap.createScaledBitmap() để chia tỷ lệ hình ảnh thành kích thước mong muốn. Tuy nhiên, phương pháp này yêu cầu tôi đã có bitmap ban đầu trong bộ nhớ, có thể khá lớn.Làm cách nào để chia tỷ lệ bitmap phát trực tiếp tại chỗ mà không đọc toàn bộ hình ảnh trước?

Tôi làm cách nào để chia tỷ lệ bitmap mà tôi đang tải xuống mà không cần ghi toàn bộ nội dung vào bộ nhớ cục bộ hoặc hệ thống tệp?

Trả lời

23

Phương pháp này sẽ đọc thông tin tiêu đề từ hình ảnh để xác định kích thước của nó, sau đó đọc hình ảnh và chia tỷ lệ theo kích thước mong muốn mà không cần cấp phát bộ nhớ cho hình ảnh có kích thước gốc đầy đủ.

Nó cũng sử dụng BitmapFactory.Options.inPurgeable, có vẻ là một tài liệu thưa thớt nhưng tùy chọn mong muốn để ngăn chặn ngoại lệ OoM khi sử dụng nhiều bitmap. CẬP NHẬT: không còn sử dụng inPurgeable, xem this note từ Romain

Nó hoạt động bằng cách sử dụng BufferedInputStream để đọc thông tin tiêu đề cho hình ảnh trước khi đọc toàn bộ hình ảnh qua InputStream.

/** 
* Read the image from the stream and create a bitmap scaled to the desired 
* size. Resulting bitmap will be at least as large as the 
* desired minimum specified dimensions and will keep the image proportions 
* correct during scaling. 
*/ 
protected Bitmap createScaledBitmapFromStream(InputStream s, int minimumDesiredBitmapWith, int minimumDesiredBitmapHeight) { 

    final BufferedInputStream is = new BufferedInputStream(s, 32*1024); 
    try { 
     final Options decodeBitmapOptions = new Options(); 
     // For further memory savings, you may want to consider using this option 
     // decodeBitmapOptions.inPreferredConfig = Config.RGB_565; // Uses 2-bytes instead of default 4 per pixel 

     if(minimumDesiredBitmapWidth >0 && minimumDesiredBitmapHeight >0) { 
      final Options decodeBoundsOptions = new Options(); 
      decodeBoundsOptions.inJustDecodeBounds = true; 
      is.mark(32*1024); // 32k is probably overkill, but 8k is insufficient for some jpgs 
      BitmapFactory.decodeStream(is,null,decodeBoundsOptions); 
      is.reset(); 

      final int originalWidth = decodeBoundsOptions.outWidth; 
      final int originalHeight = decodeBoundsOptions.outHeight; 

      // inSampleSize prefers multiples of 2, but we prefer to prioritize memory savings 
      decodeBitmapOptions.inSampleSize= Math.max(1,Math.min(originalWidth/minimumDesiredBitmapWidth, originalHeight/minimumDesiredBitmapHeight)); 

     } 

     return BitmapFactory.decodeStream(is,null,decodeBitmapOptions); 

    } catch(IOException e) { 
     throw new RuntimeException(e); // this shouldn't happen 
    } finally { 
     try { 
      is.close(); 
     } catch(IOException ignored) {} 
    } 

} 
+1

Bạn cũng có thể cần phải thực hiện một cuộc gọi GC rõ ràng trước khi gọi BitmapFactory.decodeStream, như mỗi câu trả lời này: http://stackoverflow.com/questions/6718855/using-caching-to-improve-scrolling-performance -in-an-android-listview-with-images/7245318 # 7245318 – emmby

+0

nó có thể có ý nghĩa để sử dụng mới/đóng thay vì đánh dấu/thiết lập lại trên một FileInputStream. Nhưng ý tưởng là tốt. – kellogs

+1

Tại sao tôi nhận được 'java.io.IOException: Mark đã bị vô hiệu' trên 'is.reset(); ' – sancho21

1

Đây là phiên bản của tôi, dựa trên giải pháp @emmby (nhờ người đàn ông!) tôi đã bao gồm một giai đoạn thứ hai, nơi bạn lấy bitmap giảm và mở rộng nó một lần nữa để phù hợp với kích thước chính xác mong muốn của bạn. Phiên bản của tôi có đường dẫn tệp chứ không phải luồng.

protected Bitmap createScaledBitmap(String filePath, int desiredBitmapWith, int desiredBitmapHeight) throws IOException, FileNotFoundException { 
    BufferedInputStream imageFileStream = new BufferedInputStream(new FileInputStream(filePath)); 
    try { 
     // Phase 1: Get a reduced size image. In this part we will do a rough scale down 
     int sampleSize = 1; 
     if (desiredBitmapWith > 0 && desiredBitmapHeight > 0) { 
      final BitmapFactory.Options decodeBoundsOptions = new BitmapFactory.Options(); 
      decodeBoundsOptions.inJustDecodeBounds = true; 
      imageFileStream.mark(64 * 1024); 
      BitmapFactory.decodeStream(imageFileStream, null, decodeBoundsOptions); 
      imageFileStream.reset(); 
      final int originalWidth = decodeBoundsOptions.outWidth; 
      final int originalHeight = decodeBoundsOptions.outHeight; 
      // inSampleSize prefers multiples of 2, but we prefer to prioritize memory savings 
      sampleSize = Math.max(1, Math.max(originalWidth/desiredBitmapWith, originalHeight/desiredBitmapHeight)); 
     } 
     BitmapFactory.Options decodeBitmapOptions = new BitmapFactory.Options(); 
     decodeBitmapOptions.inSampleSize = sampleSize; 
     decodeBitmapOptions.inPreferredConfig = Bitmap.Config.RGB_565; // Uses 2-bytes instead of default 4 per pixel 

     // Get the roughly scaled-down image 
     Bitmap bmp = BitmapFactory.decodeStream(imageFileStream, null, decodeBitmapOptions); 

     // Phase 2: Get an exact-size image - no dimension will exceed the desired value 
     float ratio = Math.min((float)desiredBitmapWith/ (float)bmp.getWidth(), (float)desiredBitmapHeight/ (float)bmp.getHeight()); 
     int w =(int) ((float)bmp.getWidth() * ratio); 
     int h =(int) ((float)bmp.getHeight() * ratio); 
     return Bitmap.createScaledBitmap(bmp, w,h, true); 

    } catch (IOException e) { 
     throw e; 
    } finally { 
     try { 
      imageFileStream.close(); 
     } catch (IOException ignored) { 
     } 
    } 
} 
Các vấn đề liên quan