«Растровое изображение слишком велико для загрузки в текстуру»

154

Я загружаю растровое изображение в ImageView и вижу эту ошибку. Я понимаю, что это ограничение относится к ограничению размера для аппаратных текстур OpenGL (2048x2048). Изображение, которое мне нужно загрузить, представляет собой изображение с пинч-масштабированием высотой около 4000 пикселей.

Я пытался отключить аппаратное ускорение в манифесте, но без радости.

    <application
        android:hardwareAccelerated="false"
        ....
        >

Можно ли загрузить изображение размером более 2048 пикселей в ImageView?

Олли С
источник
1
Для тех, кто ищет здесь, не забудьте поместить свое изображение в режим прокрутки, если вы хотите, чтобы оно было прокручиваемым. Это избавит от ошибки. Я потратил немного времени, прежде чем понял, что это была моя проблема.
Джейсон Ридж
Если вы хотите показать большие изображения, сохраняя при этом качество изображения, обратитесь к библиотеке в @stoefln ответ ниже. Я использовал это и стоит попробовать. Определенно лучше, чем inSampleSizeподход.
Махендра Лия
1
@Joe Blow текущий ответ не работает в вашем случае? Если нет, то, пожалуйста, опишите подробно, с чем вы столкнулись в контексте этого вопроса?
Правин Диврания
1
эй @ VC.One - это странная вещь, чтобы беспокоиться о моем человеке. Я должен был нажать «Награда за существующий ответ». Никто не мешает утомительно нажимать «лучшую» кнопку в этом всплывающем окне, потому что это глупо :) Теперь все решено, когда я нажал на награду. Ура !!
Толстяк
1
Я стараюсь всегда выплачивать вознаграждения (мне не нравится накапливать баллы). Я предполагаю, что если вы нажмете на мой профиль, то получите вознаграждение, @ VC. Один раз вы увидите много действительно хороших QA от SO за эти годы !!!!!!!!!!!!!!!
Толстяк

Ответы:

48

Все рендеринг основан на OpenGL, поэтому нельзя GL_MAX_TEXTURE_SIZEпревышать этот предел ( зависит от устройства, но минимально 2048x2048, поэтому подойдет любое изображение ниже 2048x2048).

С такими большими изображениями, если вы хотите уменьшить масштаб и в мобильном телефоне, вы должны настроить систему, подобную той, что вы видите на картах Google, например. Изображение разделено на несколько частей и несколько определений.

Или вы можете уменьшить изображение перед его отображением (см . Ответ пользователя 1353540 на этот вопрос).

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

jptsetung
источник
23
Если это так, то как приложение «Галерея» позволяет отображать и обрабатывать изображения, снятые камерой? 2048x2048 - это всего лишь 4-мегапиксельное изображение, и многие телефоны на Android снимают фотографии гораздо большего размера, и приложение Галерея, похоже, не имеет проблем.
Олли С
3
Потому что GL_MAX_TEXTURE_SIZE зависит от устройства.
jptsetung
24
Это действительно не имеет никакого смысла. Я столкнулся с той же проблемой сейчас - с изображением 1286x835 пикселей. И: только на Galaxy Nexus я получаю это сообщение об ошибке и нет изображения! Просто кажется смешным, что современный смартфон не может отображать такое маленькое изображение! Мой HTC Hero способен отображать это! Что я могу сделать?
Зордид
1
См. Ответ Ромена здесь: stackoverflow.com/questions/7428996/…
Бен Ли,
3
@OllieC Я также хотел бы знать, как приложения галереи делают это. Так что, если кто-то знает или имеет пример для показа больших изображений, это было бы здорово.
Innova
408

Это не прямой ответ на вопрос (загрузка изображений> 2048), а возможное решение для тех, кто испытывает ошибку.

В моем случае изображение было меньше, чем 2048 в обоих измерениях (1280x727, если быть точным), и проблема была специально испытана на Galaxy Nexus. Изображение было в drawableпапке и ни одна из соответствующих папок. В Android предполагается, что для рисования без спецификатора плотности заданы значения mdpi, а для других плотностей они масштабируются вверх или вниз, в этом случае для xhdpi увеличены в 2 раза. Перемещение изображения виновника drawable-nodpiдля предотвращения масштабирования решило проблему.

Pilot_51
источник
3
У меня были проблемы с памятью при попытке загрузить drawables. Потратил 2 часа, пытаясь понять, почему это происходит. Спасибо за косвенный ответ.
TrueCoke
16
Это правильный ответ и должен быть помечен как таковой.
wblaschko
3
Около трех с половиной месяцев я почти полностью программировал на Android и только сейчас понял это. Кажется глупым сделать по drawableсуществу скрытую drawable-mdpiпапку. Это также объясняет, почему мои пользовательские маркеры карты выглядели ужасно (их масштабировали, а затем уменьшали).
Theblang
10
да какого черта! Я думаю, что 99% программистов Android считают «рисуемые» означает «не масштабировать это».
Мэтт Логан
3
Это самый полезный ответ, который я когда-либо видел. Все, что я могу сказать, это то, что я посылаю награду! СПАСИБО.
Толстяк
105

Я уменьшил изображение таким образом:

ImageView iv  = (ImageView)waypointListView.findViewById(R.id.waypoint_picker_photo);
Bitmap d = new BitmapDrawable(ctx.getResources() , w.photo.getAbsolutePath()).getBitmap();
int nh = (int) ( d.getHeight() * (512.0 / d.getWidth()) );
Bitmap scaled = Bitmap.createScaledBitmap(d, 512, nh, true);
iv.setImageBitmap(scaled);
user1352407
источник
2
спасибо, createScaledBitmap помог мне показать слишком большое растровое изображение
Мальчик,
Решил мою проблему .. спасибо. На самом деле я
снимал
извините, но я не могу определить, что такое "w"
Bobbelinio
1
Извините, что это ctxзначит?
Амир Сабит
1
@AmeerSabith выглядит так, как будто ctx - это объект Context (например, ваша текущая активность)
Matt
34

Вместо того, чтобы часами часами пытаться написать / отладить весь этот код сокращения вручную, почему бы не использовать Picasso? Это было сделано для работы со bitmapsвсеми типами и / или размерами.

Я использовал эту единственную строку кода, чтобы удалить мою проблему «слишком большое растровое изображение…» :

Picasso.load(resourceId).fit().centerCrop().into(imageView);
Phileo99
источник
Использование centerCrop () без вызова resize () приведет к исключению IllegalStateException. Библиотека заставляет вызывать изменение размера, когда используется центральный кроп.
Жолт Болдиссар
Это имеет смысл, потому что обрезка подразумевает, что вы изменяете размер изображения.
Phileo99
2
Быстрое, простое и простое решение. Не уверен, что это лучшее, но я получил то, что хотел. Большое спасибо
Набин
по-прежнему выдает ту же ошибку при использовании цели с Пикассо для изображений большего размера :(
Нарендра Сингх
Попробуйте использовать версию 2.5.3-SNAPSHOT, похоже, теперь она корректно работает с большими изображениями
Антон Малмыгин
20

У AndroidManifest.xmlменя сработало добавление следующих 2 атрибутов в ( ):

android:largeHeap="true"
android:hardwareAccelerated="false"
Сачин Лала
источник
Это исправило мою проблему на устройстве Samsung. Спасибо
Хитеш Бишт
16

Смена файла изображения в drawable-nodpiпапку из drawableпапки работала для меня.

Аша Антоний
источник
Это предотвратит попытку Android автоматически масштабировать изображение в зависимости от размеров устройства и плотности экрана; Вот почему это решение работает. Android будет пытаться автоматически масштабировать что-либо в drawableпапке.
Крис Сирфице
10

Я использовал Пикассо и у меня была такая же проблема. изображение было слишком большим, по крайней мере, по размеру, ширине или высоте. наконец я нашел решение здесь. Вы можете уменьшить масштаб изображения в соответствии с размером дисплея, а также сохранить соотношение сторон:

    public Point getDisplaySize(Display display) {
    Point size = new Point();

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
        display.getSize(size);
    } else {
        int width = display.getWidth();
        int height = display.getHeight();
        size = new Point(width, height);
    }

    return size;
}

и используйте этот метод для загрузки изображения Пикассо:

    final Point displySize = getDisplaySize(getWindowManager().getDefaultDisplay());
        final int size = (int) Math.ceil(Math.sqrt(displySize.x * displySize.y));
        Picasso.with(this)
                .load(urlSource)
                .resize(size, size)
                .centerInside()
                .into(imageViewd);

Также для повышения производительности вы можете загрузить изображение в соответствии с шириной и высотой экрана дисплея, а не целое изображение:

    public String reviseImageUrl(final Integer displayWidth,     final Integer displayHeight,
        final String originalImageUrl) {
    final String revisedImageUrl;

    if (displayWidth == null && displayHeight == null) {
        revisedImageUrl = originalImageUrl;
    } else {
        final Uri.Builder uriBuilder = Uri.parse(originalImageUrl).buildUpon();

        if (displayWidth != null && displayWidth > 0) {
            uriBuilder.appendQueryParameter(QUERY_KEY_DISPLAY_WIDTH, String.valueOf(displayWidth));
        }

        if (displayHeight != null && displayHeight > 0) {
            uriBuilder.appendQueryParameter(QUERY_KEY_DISPLAY_HEIGHT, String.valueOf(displayHeight));
        }

        revisedImageUrl = uriBuilder.toString();
    }

    return revisedImageUrl;
}

    final String newImageUlr = reviseImageUrl(displySize.x, displySize.y, urlSource);

а потом:

    Picasso.with(this)
                .load(newImageUlr)
                .resize(size, size)
                .centerInside()
                .into(imageViewd);

РЕДАКТИРОВАТЬ: getDisplaySize ()

display.getWidth()/getHeight()устарела. Вместо Displayиспользования DisplayMetrics.

public Point getDisplaySize(DisplayMetrics displayMetrics) {
        int width = displayMetrics.widthPixels;
        int height = displayMetrics.heightPixels;
        return new Point(width, height);
}
шерри
источник
6

BitmapRegionDecoder делает трюк.

Вы можете переопределить onDraw(Canvas canvas), запустить новый поток и декодировать область, видимую пользователю.

Larcho
источник
5

Как отметил Ларчо, начиная с уровня API 10, вы можете использовать BitmapRegionDecoderдля загрузки определенных областей из изображения, и с этим вы можете добиться показа большого изображения в высоком разрешении, выделяя в памяти только необходимые области. Недавно я разработал библиотеку, которая обеспечивает визуализацию больших изображений с помощью сенсорных жестов. Исходный код и примеры доступны здесь .

diegocarloslima
источник
3

Просмотр уровня

Вы можете отключить аппаратное ускорение для отдельного представления во время выполнения с помощью следующего кода:

myView.setLayerType (View.LAYER_TYPE_SOFTWARE, null);

marveson
источник
3

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

Bitmap myBitmap = BitmapFactory.decodeFile(image.getAbsolutePath());
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
int width = size.x;
int height = size.y;
Log.e("Screen width ", " "+width);
Log.e("Screen height ", " "+height);
Log.e("img width ", " "+myBitmap.getWidth());
Log.e("img height ", " "+myBitmap.getHeight());
float scaleHt =(float) width/myBitmap.getWidth();
Log.e("Scaled percent ", " "+scaleHt);
Bitmap scaled = Bitmap.createScaledBitmap(myBitmap, width, (int)(myBitmap.getWidth()*scaleHt), true);
myImage.setImageBitmap(scaled);

Это лучше для любого размера экрана Android. дайте мне знать, если это работает для вас.

Абхиджит Гуджар
источник
2

Уменьшить изображение:

BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;

// Set height and width in options, does not return an image and no resource taken
BitmapFactory.decodeStream(imagefile, null, options);

int pow = 0;
while (options.outHeight >> pow > reqHeight || options.outWidth >> pow > reqWidth)
    pow += 1;
options.inSampleSize = 1 << pow; 
options.inJustDecodeBounds = false;
image = BitmapFactory.decodeStream(imagefile, null, options);

Изображение будет уменьшено до размеров reqHeight и reqWidth. Как я понимаю, inSampleSize принимает только 2 значения.

vtlinh
источник
как узнать reqHeight и reqWidth? Я имею в виду, нам нужно исправить это статическое значение? или мы можем изменить эти устройства?
Прашант Деббадвар
2

Используйте библиотеку Glide вместо прямой загрузки в imageview

Glide: https://github.com/bumptech/glide

Glide.with(this).load(Uri.parse(filelocation))).into(img_selectPassportPic);
Саи Гопи Н
источник
2
Помогло использование Glide вместо Picasso. Похоже, Glide справляется с такими проблемами по умолчанию
Johnny Five
1

Я перепробовал все вышеописанные решения, одно за другим, довольно много часов, и ни одно из них не помогло! Наконец, я решил поискать официальный пример захвата изображений с помощью камеры Android и их отображения. Официальный пример ( здесь ), наконец, дал мне единственный метод, который работал. Ниже я представляю решение, которое я нашел в этом примере приложения:

public void setThumbnailImageAndSave(final ImageView imgView, File imgFile) {

            /* There isn't enough memory to open up more than a couple camera photos */
    /* So pre-scale the target bitmap into which the file is decoded */

    /* Get the size of the ImageView */
    int targetW = imgView.getWidth();
    int targetH = imgView.getHeight();

    /* Get the size of the image */
    BitmapFactory.Options bmOptions = new BitmapFactory.Options();
    bmOptions.inJustDecodeBounds = true;
    BitmapFactory.decodeFile(imgFile.getAbsolutePath(), bmOptions);
    int photoW = bmOptions.outWidth;
    int photoH = bmOptions.outHeight;

    /* Figure out which way needs to be reduced less */
    int scaleFactor = 1;
    if ((targetW > 0) || (targetH > 0)) {
        scaleFactor = Math.min(photoW/targetW, photoH/targetH);
    }

    /* Set bitmap options to scale the image decode target */
    bmOptions.inJustDecodeBounds = false;
    bmOptions.inSampleSize = scaleFactor;
    bmOptions.inPurgeable = true;

    /* Decode the JPEG file into a Bitmap */
    Bitmap bitmap = BitmapFactory.decodeFile(imgFile.getAbsolutePath(), bmOptions);

    /* Associate the Bitmap to the ImageView */
    imgView.setImageBitmap(bitmap);
    imgView.setVisibility(View.VISIBLE);
}
nemesisfixx
источник
0

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

Решение Pilot_51 (перемещение ваших изображений в drawable-nodpiпапку) работает, но имеет еще одну проблему: оно делает изображения СЛИШКОМ МАЛЕНЬКИМИ на экране, если размеры изображений не увеличиваются до очень большого (например, 2000 x 3800), чтобы соответствовать экрану - тогда это делает ваше приложение более тяжелым ,

РЕШЕНИЕ : поместите свои файлы изображений в drawable-hdpi- это работало как очарование для меня.

makeasy
источник
1
Вы просто маскируете проблему плотности изображения. На оборудовании с низкой плотностью ваши изображения будут выглядеть больше.
rupps
я также не думаю, что у решения Pilot_51 есть какие-либо проблемы, вы должны использовать изображения подходящего размера в соответствии с вашими потребностями
Nilabja
0

Использование правильной прорисовываемой подпапки решило это за меня. Мое решение было положить мое полное разрешение изображения (1920х1200) в вытяжке-xhdpi папку, вместо вытяжки папки.

Я также поместил уменьшенное изображение (1280x800) в папку drawable-hdpi .

Эти два разрешения соответствуют планшетам Nexus 7 2013 и 2012 года, которые я программирую. Я также проверил решение на некоторых других планшетах.

steveputz
источник
0
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {

    super.onActivityResult(requestCode, resultCode, data);
    ///*
    if (requestCode == PICK_FROM_FILE && resultCode == RESULT_OK && null != data){



        uri = data.getData();

        String[] prjection ={MediaStore.Images.Media.DATA};

        Cursor cursor = getContentResolver().query(uri,prjection,null,null,null);

        cursor.moveToFirst();

        int columnIndex = cursor.getColumnIndex(prjection[0]);

        ImagePath = cursor.getString(columnIndex);

        cursor.close();

        FixBitmap = BitmapFactory.decodeFile(ImagePath);

        ShowSelectedImage = (ImageView)findViewById(R.id.imageView);

      //  FixBitmap = new BitmapDrawable(ImagePath);
        int nh = (int) ( FixBitmap.getHeight() * (512.0 / FixBitmap.getWidth()) );
        FixBitmap = Bitmap.createScaledBitmap(FixBitmap, 512, nh, true);

       // ShowSelectedImage.setImageBitmap(BitmapFactory.decodeFile(ImagePath));

        ShowSelectedImage.setImageBitmap(FixBitmap);

    }
}

Этот код работает

Alobaidi
источник