"Mapa de bits demasiado grande para cargarlo en una textura"

154

Estoy cargando un mapa de bits en un ImageView y veo este error. Entiendo que este límite se refiere a un límite de tamaño para las texturas de hardware OpenGL (2048x2048). La imagen que necesito cargar es una imagen de zoom de aproximadamente 4,000 píxeles de alto.

He intentado desactivar la aceleración de hardware en el manifiesto, pero no me alegro.

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

¿Es posible cargar una imagen de más de 2048 píxeles en un ImageView?

Ollie C
fuente
1
Para cualquiera que busque aquí, no olvide poner su imagen en una vista de desplazamiento si desea que se pueda desplazar. Eso eliminará el error. Perdí algo de tiempo antes de darme cuenta de que ese era mi problema.
Jason Ridge
Para cualquiera que quiera mostrar imágenes grandes que aún mantengan la calidad de la imagen, consulte la biblioteca en la respuesta @stoefln a continuación. Lo usé y vale la pena intentarlo. Definitivamente mejor que el inSampleSizeenfoque.
Mahendra Liya
1
@Joe Blow ¿la respuesta actual no funcionó en tu caso? En caso negativo, ¿puede explicar el problema que enfrentó en el contexto de esta pregunta?
Pravin Divraniya
1
hola @ VC.One - es algo extraño que te preocupes por mi hombre. Debería haber hecho clic en "Recompensar la respuesta existente". Nadie se molesta tediosamente al presionar el botón "mejor" en esa ventana emergente SO, porque es una tontería :) Todo se resolvió ahora cuando hice clic en la recompensa. ¡¡Salud!!
Fattie
1
Intento siempre pagar recompensas (no me gusta acumular puntos). Supongo que si haces clic en mi perfil y luego recompensas, @ VC ¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡
Fattie

Respuestas:

48

Todo el renderizado se basa en OpenGL, por lo que no puede superar este límite ( GL_MAX_TEXTURE_SIZEdepende del dispositivo, pero el mínimo es 2048x2048, por lo que cualquier imagen inferior a 2048x2048 se ajustará).

Con imágenes tan grandes, si desea alejarse y en un dispositivo móvil, debe configurar un sistema similar a lo que ve en Google Maps, por ejemplo. Con la imagen dividida en varias piezas y varias definiciones.

O puede reducir la imagen antes de mostrarla (consulte la respuesta del usuario 1352407 sobre esta pregunta).

Y también, tenga cuidado con la carpeta en la que coloca la imagen, Android puede escalar automáticamente las imágenes. Eche un vistazo a la respuesta de Pilot_51 a continuación sobre esta pregunta.

jptsetung
fuente
23
Si este es el caso, ¿cómo permite la aplicación Galería la visualización y manipulación de imágenes tomadas con la cámara? 2048x2048 es solo una imagen de 4MP, y muchos teléfonos Android toman fotos mucho más grandes que esto y la aplicación Gallery parece no tener problemas.
Ollie C
3
Porque GL_MAX_TEXTURE_SIZE depende del dispositivo.
jptsetung
24
Esto realmente no tiene ningún sentido. Encontré el mismo problema ahora, con una imagen de 1286x835 píxeles. Y: solo en un Galaxy Nexus recibo este mensaje de error y no hay imagen. ¡Simplemente parece ridículo que un teléfono inteligente de última generación no pueda mostrar una imagen tan pequeña! ¡Mi HTC Hero es capaz de mostrar eso! ¿Que puedo hacer?
Zordid
1
Vea la respuesta de Romain aquí: stackoverflow.com/questions/7428996/…
Ben Lee
3
@OllieC También me gustaría saber cómo lo hacen las aplicaciones de la galería. Entonces, si alguien sabe, o tiene un ejemplo para mostrar imágenes grandes, sería genial.
Innova
408

Esta no es una respuesta directa a la pregunta (cargar imágenes> 2048), sino una posible solución para cualquiera que experimente el error.

En mi caso, la imagen era más pequeña que 2048 en ambas dimensiones (1280x727 para ser exactos) y el problema se experimentó específicamente en un Galaxy Nexus. La imagen estaba en la drawablecarpeta y ninguna de las carpetas calificadas. Android asume que los elementos dibujables sin un calificador de densidad son mdpi y los escala hacia arriba o hacia abajo para otras densidades, en este caso aumentados 2 veces para xhdpi. Mover la imagen del culpable a drawable-nodpipara evitar la escala solucionó el problema.

Piloto_51
fuente
3
Estaba teniendo problemas de memoria al intentar cargar elementos extraíbles. Pasé 2 horas tratando de entender por qué sucedía esto. Gracias por la respuesta indirecta.
TrueCoke
16
Esta es la respuesta correcta y debe marcarse como tal.
wblaschko
3
Hace unos tres meses y medio que he estado programando casi a tiempo completo en Android y ahora me di cuenta de esto. Parece tonto hacer drawableesencialmente una drawable-mdpicarpeta oculta . Esto también explicaba por qué mis marcadores de mapas personalizados se veían horribles (estaban siendo escalados, luego reducidos).
theblang
10
si que demonios! Creo que el 99% de los programadores de Android piensan que "dibujable" significa "no escalar esto".
Matt Logan
3
Esta es la respuesta más útil que he visto en ningún lado. ¡Todo lo que puedo decir es que estoy enviando una recompensa! GRACIAS.
Fattie
105

He reducido la imagen de esta manera:

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);
usuario1352407
fuente
2
gracias, createScaledBitmap fue útil para poder mostrar un mapa de bits demasiado grande
Boy
Resuelto mi problema ... gracias. En realidad, estaba tomando la imagen de la cámara y mostrándola en el ImageView en Samsung Galaxy S4 con 4.4.2
Mitesh Shah
lo siento, pero no puedo detectar lo que es "w"
Bobbelinio
1
Lo siento, ¿qué ctxsignifica esto ?
Ameer Sabith
1
@AmeerSabith parece que ctx es un objeto de contexto (su actividad en ejecución, por ejemplo)
Matt
34

En lugar de pasar horas y horas tratando de escribir / depurar todo este código de reducción de muestras manualmente, ¿por qué no usarlo Picasso? Fue hecho para tratar conbitmaps todo tipo y / o tamaño.

He usado esta única línea de código para eliminar mi problema "mapa de bits demasiado grande ..." :

Picasso.load(resourceId).fit().centerCrop().into(imageView);
Phileo99
fuente
El uso de centerCrop () sin llamar a resize () dará como resultado una IllegalStateException. La biblioteca obliga a llamar a cambiar el tamaño cuando se utiliza el recorte central
Zsolt Boldizsár
Eso tiene mucho sentido, porque recortar implica que está cambiando el tamaño de la imagen.
Phileo99
2
Solución rápida, fácil y simple. No estoy seguro de si es lo mejor, pero obtuve lo que quería. Muchas gracias
Nabin
sigue obteniendo el mismo error cuando se usa el objetivo con Picasso, para las imágenes de mayor tamaño :(
Narendra Singh
Intente utilizar la versión 2.5.3-SNAPSHOT, parece que ahora funciona correctamente con imágenes grandes
Anton Malmygin
20

La adición de los siguientes 2 atributos en ( AndroidManifest.xml) funcionó para mí:

android:largeHeap="true"
android:hardwareAccelerated="false"
Sachin Lala
fuente
Esto solucionó mi problema en el dispositivo Samsung. Gracias
Hitesh Bisht
16

Cambiar el archivo de imagen a drawable-nodpicarpeta de drawablecarpeta funcionó para mí.

Asha Antony
fuente
Esto evitará que Android intente escalar automáticamente la imagen según las dimensiones del dispositivo y la densidad de la pantalla; Por eso esta solución funciona. Android intentará escalar automáticamente cualquier cosa en la drawablecarpeta.
Chris Cirefice
10

Usé Picasso y tuve el mismo problema. la imagen era demasiado grande al menos en tamaño, ancho o alto. Finalmente encontré la solución aquí. puede reducir la imagen grande de acuerdo con el tamaño de la pantalla y también mantener la relación de aspecto:

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

y use este método para cargar imágenes de Picasso:

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

También para un mejor rendimiento, puede descargar la imagen de acuerdo con el ancho y la altura de la pantalla, no toda la imagen:

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

y entonces:

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

EDITAR: getDisplaySize ()

display.getWidth()/getHeight()es obsoleto. En lugar de Displayusar DisplayMetrics.

public Point getDisplaySize(DisplayMetrics displayMetrics) {
        int width = displayMetrics.widthPixels;
        int height = displayMetrics.heightPixels;
        return new Point(width, height);
}
Jerez
fuente
6

BitmapRegionDecoder Hace el truco.

Puede anular onDraw(Canvas canvas), iniciar un nuevo hilo y decodificar el área visible para el usuario.

Larcho
fuente
5

Como señaló Larcho, a partir del nivel API 10, puede usar BitmapRegionDecoderpara cargar regiones específicas de una imagen y con eso, puede lograr mostrar una imagen grande en alta resolución asignando en la memoria solo las regiones necesarias. Recientemente desarrollé una biblioteca que proporciona la visualización de imágenes grandes con manejo de gestos táctiles. El código fuente y las muestras están disponibles aquí .

diegocarloslima
fuente
3

Ver nivel

Puede deshabilitar la aceleración de hardware para una vista individual en tiempo de ejecución con el siguiente código:

myView.setLayerType (View.LAYER_TYPE_SOFTWARE, nulo);

Marvelson
fuente
3

Me encontré con el mismo problema, aquí está mi solución. establece el ancho de la imagen igual que el ancho de la pantalla de Android y luego escala la altura

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

Esto es mejor para cualquier tamaño de pantalla de Android. Avísame si te funciona.

Abhijit Gujar
fuente
2

Reducir imagen:

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

La imagen se reducirá al tamaño de reqHeight y reqWidth. Según tengo entendido, inSampleSize solo tiene una potencia de 2 valores.

vtlinh
fuente
¿Cómo saber reqHeight y reqWidth? Quiero decir, ¿tenemos que arreglarlo al valor estático? o podemos cambiar esos dispositivos wrt?
Prashanth Debbadwar
2

Use la biblioteca Glide en lugar de cargar directamente en la vista de imagen

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

Glide.with(this).load(Uri.parse(filelocation))).into(img_selectPassportPic);
Sai Gopi N
fuente
2
Usar Glide en lugar de Picasso ayudó. Parece que Glide maneja tales problemas por defecto
Johnny Five
1

Probé todas las soluciones anteriores, una tras otra, durante muchas horas, ¡y ninguna parecía funcionar! Finalmente, decidí buscar un ejemplo oficial sobre capturar imágenes con la cámara de Android y mostrarlas. El ejemplo oficial ( aquí ), finalmente me dio el único método que funcionó. A continuación presento la solución que encontré en esa aplicación de ejemplo:

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
fuente
0

NOTA PARA AQUELLOS QUE QUIEREN PONER IMÁGENES DE TAMAÑO PEQUEÑO:

La solución de Pilot_51 (mover sus imágenes a la drawable-nodpicarpeta) funciona, pero tiene otro problema: hace que las imágenes sean MUY PEQUEÑAS en la pantalla a menos que las imágenes cambien de tamaño a una resolución muy grande (como 2000 x 3800) para ajustarse a la pantalla, entonces hace que su aplicación sea más pesada .

SOLUCIÓN : coloque sus archivos de imagen drawable-hdpi: funcionó como un encanto para mí.

mamada
fuente
1
Solo está enmascarando su problema de densidad de imagen. En hardware con baja densidad, sus imágenes se verán más grandes.
rompe el
Asimismo, no creo que la solución de Pilot_51 tiene algún problema, debe utilizar tamaños de imagen adecuados según sus necesidades
Nilabja
0

Usar la subcarpeta dibujable correcta lo resolvió para mí. Mi solución fue poner mi imagen de resolución completa (1920x1200) en el dibujable-xhdpi carpeta, en lugar de la dibujable carpeta.

También puse una imagen reducida (1280x800) en la carpeta drawable-hdpi .

Estas dos resoluciones coinciden con las tabletas Nexus 7 de 2013 y 2012 que estoy programando. También probé la solución en algunas otras tabletas.

steveputz
fuente
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);

    }
}

Este código es trabajo

Alobaidi
fuente