BitmapFactory.decodeStream повертає нуль, коли встановлено параметри


90

У мене проблеми з BitmapFactory.decodeStream(inputStream). Якщо використовувати його без опцій, він поверне зображення. Але коли я використовую його з параметрами, як у .decodeStream(inputStream, null, options)ньому, ніколи не повертає растрові зображення.

Я намагаюся зробити дескрипцію растрового зображення, перш ніж фактично завантажити його, щоб заощадити пам’ять. Я прочитав кілька хороших посібників, але жоден із них не використовує .decodeStream.

РОБОТИ ПРОСТО ТОКІ

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
Що виводиться з вашого оператора System.out.println ("Samplesize:" ...)? Вказівка ​​на те, що options.inSampleSize є прийнятним значенням?
Steve Haley

Так, він повертає прийнятне значення кожного разу.
Роберт Фосс,

Видалено оператор через його налагодження.
Роберт Фосс,

1
Дякуємо, що опублікували своє рішення, але потрібно зробити ще одне. Це запитання все ще відображається у списках "невирішених питань", оскільки ви не позначили відповідь як "прийняте". Це можна зробити, натиснувши значок галочки біля відповіді. Ви можете прийняти відповідь Самуха, якщо вважаєте, що це допомогло вам знайти рішення, або ви можете опублікувати власну відповідь і прийняти її. (Зазвичай ви б додавали своє рішення у своїй відповіді, але оскільки ви вже включили це, відредагувавши своє запитання, ви можете просто передати їх на запитання.)
Стів Хейлі,

Дякуємо за допомогу новому користувачеві інтегруватися до спільноти :)
Роберт Фосс

Відповіді:


114

Проблема полягала в тому, що як тільки ви використовуєте 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();

17
Чи означає це, що зображення потрібно завантажувати двічі? Один раз, щоб отримати розмір, і один раз, щоб отримати піксельні дані?
user123321

1
@ Роберт, ти, мабуть, повинен пояснити саме цю поведінку, щоб інші користувачі отримали чітке уявлення про це
Мухаммад Бабар,

1
Мені було цікаво, чому я сам не працював би з тим самим вхідним потоком, дякую за коротке пояснення
kabuto178,

1
вам не потрібно його відтворювати, просто скидання це вирішить мету. Дивіться мою відповідь
Шашанк Томар,

5
Я повинен сказати, Bitmap клас Android відстій. Це так бентежить і засмучує у використанні.
Neon Warge

30

Спробуйте обернути InputStream за допомогою BufferedInputStream.

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

2
це завжди працювало для вас? з якихось причин я отримую нуль у деяких дуже конкретних випадках за допомогою цього методу. я написав про це тут: stackoverflow.com/questions/17774442/…
розробник Android

1
він працював, тому я підтримав його, але is.available () doc поставляється з попередженням, що його слід використовувати лише для перевірки, чи потік порожній чи ні, а не для обчислення розміру, оскільки це ненадійно.
Абхішек Чаухан

1
не голосували, але підключення вхідного потоку є з'єднанням HTTP, і reset () не буде працювати ....
Джонні Ву,

3

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

Було б краще, якщо б ви могли розкласти всю логіку обчислення розміру з цієї підпрограми на метод (назвіть це CalcuScaleFactor () або іншим способом) і спершу протестувати цей метод самостійно.

Щось на зразок:

// 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 {}, якщо це ще не зроблено.


Велике спасибі за відповідь! Я спробував встановити остаточне значення int, наприклад 'options.inSampleSize = 2'. Але це призводить до тих самих питань. Logcat читає 'SkImageDecoder :: Factory return null' для кожного зображення, яке я намагався декодувати. Запуск коду всередині блоку try / catch не допоможе мені, оскільки він нічого не кидає, так? Однак BitmapFactory.decodeStream повертає null, якщо не може створити img, чого не може, коли я намагаюся використовувати sampleSize.
Роберт Фосс,

Це дивно. Чи можете ви спробувати змінити розмір деяких растрових зображень, включених у ваш ресурс? Як відкрийте файл ресурсу та спробуйте його розшифрувати. Якщо ви можете це зробити, можливо, є проблема з віддаленим потоком, яка спричиняє збій декодування.
Самух

BitmapFactory.decodeResource (this.getResources (), R.drawable.icon, options) == null) чудово працює з повторною вибіркою. Перший BitmapFactory.decodeStream з options.inJustDecodeBounds = true працює і відмінно повертає параметри. Але наступний BitmapFactory.decodeStream з options.inJustDecodeBounds = false щоразу не вдається.
Роберт Фосс,

Я боюся, що це поза мною ... Мені було б цікаво дізнатись, що тут може піти не так, тому що я використовую подібний код, і він для мене чудово працює.
Самух

4
В порядку. Я це вирішив. Проблема полягає в http-з'єднанні. Коли ви прочитали один раз із вхідного потоку, наданого HttpUrlConnection, ви не зможете читати з нього знову, і вам доведеться повторно підключитися, щоб виконати другий decodeStream ().
Роберт Фосс,

2

Ви можете перетворити 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;
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.