Работа с ограничением размера текстуры Android

У меня есть требование отображать несколько больших изображений в приложении для Android. Прямо сейчас я использую ImageView с исходным растровым изображением. Я понимаю, что openGL имеет определенное независимое от устройства ограничение в отношении того, насколько большими могут быть размеры изображения для его обработки.

Существует ли ЛЮБОЙ способ отображения этих изображений (с фиксированной шириной, без обрезки) независимо от этого ограничения, кроме разделения изображения на несколько элементов ImageView?

Спасибо.

ОБНОВЛЕНИЕ 01 апреля 2013 г. По-прежнему безуспешно. Все предложения сводились к снижению качества изображения. Один предположил, что это ограничение можно обойти, используя ЦП для обработки вместо использования ГП (хотя обработка может занять больше времени). Я не понимаю, неужели нет возможности отображать длинные изображения с фиксированной шириной без снижения качества изображения? Бьюсь об заклад, есть, я был бы рад, если бы кто-нибудь хотя бы указал мне правильное направление.

Всем спасибо.


person woot    schedule 27.03.2013    source источник
comment
Я не уверен в этом, но вы пытались вместо этого использовать WebView?   -  person Edward van Raak    schedule 27.03.2013
comment
Не совсем, у меня есть несколько экземпляров очень больших изображений, случайно расположенных в списке. Я должен учитывать производительность, и это звучит как довольно плохая идея.   -  person woot    schedule 27.03.2013
comment
Взгляните на этот замечательный проект: github.com/ened/Android-Tiling-ScrollView   -  person Sherif elKhatib    schedule 05.04.2013


Ответы (4)


Вы можете использовать BitmapRegionDecoder для разделения больших растровых изображений (требуется уровень API 10). Я написал метод, который будет использовать этот класс и возвращать один Drawable, который можно поместить внутрь ImageView:

private static final int MAX_SIZE = 1024;

private Drawable createLargeDrawable(int resId) throws IOException {

    InputStream is = getResources().openRawResource(resId);
    BitmapRegionDecoder brd = BitmapRegionDecoder.newInstance(is, true);

    try {
        if (brd.getWidth() <= MAX_SIZE && brd.getHeight() <= MAX_SIZE) {
            return new BitmapDrawable(getResources(), is);
        }

        int rowCount = (int) Math.ceil((float) brd.getHeight() / (float) MAX_SIZE);
        int colCount = (int) Math.ceil((float) brd.getWidth() / (float) MAX_SIZE);

        BitmapDrawable[] drawables = new BitmapDrawable[rowCount * colCount];

        for (int i = 0; i < rowCount; i++) {

            int top = MAX_SIZE * i;
            int bottom = i == rowCount - 1 ? brd.getHeight() : top + MAX_SIZE;

            for (int j = 0; j < colCount; j++) {

                int left = MAX_SIZE * j;
                int right = j == colCount - 1 ? brd.getWidth() : left + MAX_SIZE;

                Bitmap b = brd.decodeRegion(new Rect(left, top, right, bottom), null);
                BitmapDrawable bd = new BitmapDrawable(getResources(), b);
                bd.setGravity(Gravity.TOP | Gravity.LEFT);
                drawables[i * colCount + j] = bd;
            }
        }

        LayerDrawable ld = new LayerDrawable(drawables);
        for (int i = 0; i < rowCount; i++) {
            for (int j = 0; j < colCount; j++) {
                ld.setLayerInset(i * colCount + j, MAX_SIZE * j, MAX_SIZE * i, 0, 0);
            }
        }

        return ld;
    }
    finally {
        brd.recycle();
    }
}

Метод проверит, меньше ли доступный для рисования ресурс, чем MAX_SIZE (1024) по обеим осям. Если это так, он просто возвращает рисуемый объект. Если это не так, он разобьет изображение на части, декодирует фрагменты изображения и поместит их в LayerDrawable.

Я выбрал 1024, потому что считаю, что большинство доступных телефонов будут поддерживать изображения как минимум такого размера. Если вы хотите найти фактический предел размера текстуры для телефона, вам нужно проделать кое-какие причудливые вещи через OpenGL, и я не хотел в это углубляться.

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

person Jason Robinson    schedule 07.04.2013
comment
Следует отметить, что эта реализация разбивает его только на строки, а не на сетку. Если ширина больше, чем максимальный размер, он пройдет через алгоритм, но фрагменты по-прежнему будут содержать ширину, превышающую MAX_SIZE. Это тривиальное исправление, чтобы добавить его поддержку, и я бы никогда не подумал об использовании LayerDrawable для отображения сетки рисунков. Отличный ответ. - person David Liu; 04.06.2013
comment
Отредактировано в исправлении для разделения на квадратные фрагменты, а не только на строки. - person David Liu; 04.06.2013
comment
Спасибо за ваши дополнения @DavidLiu. Было несколько ошибок с вашими правками, о которых я позаботился. Пожалуйста, просмотрите мои правки и убедитесь, что я не нарушил цель вашего редактирования. Метод выдает IOException, поэтому catch не нужен. - person Jason Robinson; 04.06.2013
comment
@ Джейсон Робинсон Упс, да. Я скопировал и вставил измененную версию, которую использовал в своем приложении, которая не вызывала исключение IOException. Выглядит неплохо. - person David Liu; 05.06.2013
comment
Это действительно очень хорошее решение для ограничения размера текстуры GL. Спасибо. - person MobileCushion; 01.04.2014
comment
Можно ли использовать этот метод с растровым изображением, если да, то как? Я разместил код для определения максимального размера текстуры gl здесь: pastebin.com/gFTdHPbg - person tim687; 10.08.2015
comment
@tim687 этот новый экземпляр (developer.android.com/reference /android/graphics/, int, int, boolean)) принимает массив байтов. Похоже, это должно сработать. Преобразуйте растровое изображение в массив байтов с помощью этого ответа. - person Jason Robinson; 11.08.2015
comment
SO не позволит мне связать это, не испортив ссылку. Перейдите к javadocs BitmapRegionDecoder и найдите фабричный метод с массивом байтов. . - person Jason Robinson; 11.08.2015
comment
@JasonRobinson В конце концов я использовал метод разделения фрагментов, но мои растровые изображения не рисуются. Я разберусь с этим :-P - person tim687; 11.08.2015

Вы можете использовать BitmapFactoryOptions, чтобы уменьшить размер изображения. Вы можете использовать что-то вроде этого:

BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 3; //reduce size 3 times
person developerCoder    schedule 27.03.2013
comment
Спасибо, но нельзя ли как-нибудь добиться желаемого результата без снижения качества изображения? - person woot; 27.03.2013
comment
я не думаю, что есть какое-либо решение без снижения качества изображения. Потому что выдает ошибку переполнения. - person developerCoder; 28.03.2013

Вы видели, как работают ваши карты? Однажды я сделал рендерер для карт. Вы можете использовать тот же трюк для отображения вашего изображения.

Разделите изображение на квадратные плитки (например, 128x128 пикселей). Создайте собственный imageView, поддерживающий рендеринг из плиток. Ваш imageView знает, какую часть растрового изображения он должен отображать сейчас, и отображает только необходимые фрагменты, загружая их с вашей SD-карты. Используя такую ​​тайловую карту, вы можете отображать бесконечные изображения.

person Leonidos    schedule 02.04.2013

Было бы полезно, если бы вы предоставили нам размеры вашего растрового изображения.

Пожалуйста, поймите, что OpenGL работает против естественных математических ограничений.

Например, есть очень веская причина, по которой текстура в OpenGL должна быть равна 2 в степени x. Это действительно единственный способ, которым математика любого масштабирования может быть выполнена чисто без остатка.

Поэтому, если вы сообщите нам точные размеры наименьшего растрового изображения, которое доставляет вам проблемы, некоторые из нас могут сказать вам, с каким фактическим ограничением вы столкнулись.

person Stephan Branczyk    schedule 02.04.2013
comment
Размеры растрового изображения являются динамическими, ширина может достигать 480 пикселей, высота может быть от 1 до 10000 пикселей. Желаемая отображаемая ширина равна ширине экрана за вычетом некоторых полей. Изображение должно быть масштабировано до нужной ширины, сохраняя при этом соотношение сторон кадрирования слева вверху. - person woot; 02.04.2013
comment
Тогда это не текстура OpenGL. Не относитесь к этому так. У вас могут быть фоновые изображения в OpenGL, это нормально, пока вы не рассматриваете их как текстуры, они будут в порядке. - person Stephan Branczyk; 02.04.2013
comment
Что значит не относиться к ним как к текстурам? Я не использую OpenGL напрямую, все, что я делаю, это устанавливаю источник для ImageView, после чего изображение не отображается из-за ограничения размера текстуры устройства. Например: растровое изображение слишком велико для загрузки в текстуру (480 x 5400, макс. = 4096 x 4096) на Nexus 10. - person woot; 02.04.2013
comment
Ваши изображения могут автоматически масштабироваться еще до того, как OpenGL доберется до них. Создайте папку drawable-nodpi и переместите в нее изображения. Кстати, вы планируете использовать пинч-зум? или какая-то другая функция масштабирования? Чтобы масштабирование работало, ваши изображения должны быть разбиты на квадраты, как предлагает Леонид. Не могли бы вы показать нам свой код, пожалуйста. Я считаю, что показ вашего кода должен помочь. - person Stephan Branczyk; 02.04.2013