BitmapFactory.decodeStream возвращает значение null при настройке параметров

У меня проблемы с BitmapFactory.decodeStream(inputStream). Когда вы используете его без параметров, он вернет изображение. Но когда я использую его с параметрами, как в .decodeStream(inputStream, null, options), он никогда не возвращает битмапы.

То, что я пытаюсь сделать, это уменьшить размер растрового изображения, прежде чем я загружу его для сохранения памяти. Я прочитал несколько полезных руководств, но никто не использовал .decodeStream.

РАБОТЫ JUST FINE

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

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

НЕ РАБОТАЕТ

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

Проблема заключалась в том, что после того, как вы использовали InputStream из HttpUrlConnection для извлечения метаданных изображения, вы не можете перемотать и снова использовать тот же InputStream.

Поэтому вам нужно создать новый InputStream для фактической выборки изображения.

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

Ответ 2

Попробуйте обернуть InputStream с помощью BufferedInputStream.

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

Ответ 3

Я думаю, что проблема связана с логикой "калькулятор-масштаб-фактор", потому что остальная часть кода выглядит правильно для меня (если, конечно, входной поток не является нулевым).

Было бы лучше, если бы вы могли отчислить всю логику вычисления размера из этой подпрограммы в метод (назовите его calculateScaleFactor() или что-то еще) и сначала сначала проверите этот метод.

Что-то вроде:

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

и проверить getScaleFactor (...) самостоятельно.

Это также поможет объединить весь код с помощью try..catch {} block, если он еще не сделан.

Ответ 4

Вы можете преобразовать InputStream в массив байтов и использовать decodeByteArray(). Например,

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