"Bitmap trop volumineux pour être chargé dans une texture"

154

Je charge un bitmap dans un ImageView et vois cette erreur. Je suppose que cette limite concerne une limite de taille pour les textures matérielles OpenGL (2048x2048). L'image que je dois charger est une image à zoom par pincement d'environ 4 000 pixels de haut.

J'ai essayé de désactiver l'accélération matérielle dans le manifeste, mais pas de joie.

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

Est-il possible de charger une image de plus de 2048 pixels dans un ImageView?

Ollie C
la source
1
Pour tous ceux qui regardent ici, n'oubliez pas de mettre votre image dans une vue de défilement si vous voulez qu'elle puisse défiler. Cela éliminera l'erreur. J'ai perdu du temps avant de réaliser que c'était mon problème.
Jason Ridge
Pour ceux qui cherchent à afficher de grandes images tout en conservant la qualité d'image, reportez-vous à la bibliothèque dans la réponse @stoefln ci-dessous. Je l'ai utilisé et ça vaut le coup d'essayer. Certainement mieux que l' inSampleSizeapproche.
Mahendra Liya
1
La réponse actuelle de @Joe Blow n'a pas fonctionné dans votre cas? Si non, veuillez préciser le problème auquel vous avez été confronté dans le contexte de cette question?
Pravin Divraniya
1
hey @ VC.One - c'est une chose étrange de s'énerver à propos de mon homme. J'aurais dû cliquer sur "Récompenser la réponse existante". Personne ne se soucie d'appuyer fastidieusement sur le bouton "meilleur" de cette fenêtre contextuelle SO, parce que c'est idiot :) Tout est résolu maintenant que j'ai cliqué sur la prime. À votre santé!!
Fattie
1
J'essaie de toujours payer des primes (je n'aime pas accumuler des points). Je suppose que si vous cliquez sur mon profil, puis sur les primes, @ VC.One, vous verrez beaucoup de très bons QA de SO au fil des ans !!!!!!!!!!!!!!!
Fattie

Réponses:

48

Tout le rendu est basé sur OpenGL, donc non, vous ne pouvez pas dépasser cette limite ( GL_MAX_TEXTURE_SIZEdépend de l'appareil, mais le minimum est de 2048x2048, donc toute image inférieure à 2048x2048 conviendra).

Avec des images aussi grandes, si vous souhaitez effectuer un zoom arrière, et dans un mobile, vous devez configurer un système similaire à ce que vous voyez dans google maps par exemple. Avec l'image divisée en plusieurs morceaux et plusieurs définitions.

Ou vous pouvez réduire l'image avant de l'afficher (voir la réponse de user1352407 à cette question).

Et aussi, faites attention dans le dossier dans lequel vous mettez l'image, Android peut automatiquement agrandir les images. Jetez un œil à la réponse de Pilot_51 ci-dessous à cette question.

jptsetung
la source
23
Si tel est le cas, comment l'application Galerie permet-elle l'affichage et la manipulation d'images prises avec l'appareil photo? 2048x2048 n'est qu'une image de 4MP, et de nombreux téléphones Android prennent des photos beaucoup plus grandes que cela et l'application Galerie ne semble pas avoir de problèmes.
Ollie C
3
Parce que GL_MAX_TEXTURE_SIZE dépend du périphérique.
jptsetung
24
Cela n'a vraiment aucun sens. J'ai rencontré le même problème maintenant - avec une image de 1286x835 pixels. ET: uniquement sur un Galaxy Nexus, je reçois ce message d'erreur et aucune image! Il semble juste ridicule qu'un smartphone haut de gamme ne puisse pas afficher une si petite image! Mon HTC Hero est capable d'afficher cela! Que puis-je faire?
Zordid
1
Voir la réponse de Romain ici: stackoverflow.com/questions/7428996/…
Ben Lee
3
@OllieC J'aimerais également savoir comment les applications de la galerie le font. Donc, si quelqu'un sait ou a un exemple pour montrer de grandes images, ce serait génial.
Innova
408

Ce n'est pas une réponse directe à la question (chargement d'images> 2048), mais une solution possible pour toute personne rencontrant l'erreur.

Dans mon cas, l'image était plus petite que 2048 dans les deux dimensions (1280x727 pour être exact) et le problème a été spécifiquement rencontré sur un Galaxy Nexus. L'image était dans le drawabledossier et aucun des dossiers qualifiés. Android suppose que les dessinables sans qualificatif de densité sont mdpi et les met à l'échelle vers le haut ou vers le bas pour d'autres densités, dans ce cas mis à l'échelle 2x pour xhdpi. Le déplacement de l'image coupable vers drawable-nodpipour éviter la mise à l'échelle a résolu le problème.

Pilot_51
la source
3
J'avais des problèmes de mémoire en essayant de charger des drawables. J'ai passé 2 heures à essayer de comprendre pourquoi cela se passait. Merci pour la réponse indirecte.
TrueCoke
16
C'est la bonne réponse et doit être marquée comme telle.
wblaschko
3
Environ trois mois et demi maintenant, je programme presque à plein temps sur Android et je viens de m'en rendre compte. Cela semble stupide de créer drawableessentiellement un drawable-mdpidossier caché . Cela expliquait également pourquoi mes marqueurs de carte personnalisés semblaient horribles (ils étaient en cours de mise à l'échelle, puis de réduction).
theblang
10
ouais c'est quoi ce bordel! Je pense que 99% des programmeurs Android pensent que "dessinable" signifie "ne pas mettre à l'échelle".
Matt Logan
3
C'est la réponse la plus utile que j'aie jamais vue, n'importe où. Tout ce que je peux dire, c'est que j'envoie une prime! MERCI.
Fattie
105

J'ai réduit l'image de cette manière:

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
la source
2
merci, le createScaledBitmap m'a été utile pour pouvoir afficher une image bitmap trop grande
Garçon
Résolu mon problème .. merci. En fait, je prenais l'image de la caméra et l'affichais dans ImageView dans Samsung Galaxy S4 avec 4.4.2
Mitesh Shah
désolé, mais je ne peux pas détecter ce qui est "w"
Bobbelinio
1
Pardon, qu'est-ce que cela ctxsignifie?
Ameer Sabith
1
@AmeerSabith ressemble à ctx est un objet de contexte (votre activité en cours d'exécution par exemple)
Matt
34

Au lieu de passer des heures et des heures à essayer d'écrire / déboguer manuellement tout ce code de sous-échantillonnage, pourquoi ne pas l'utiliser Picasso? Il a été conçu pour traiter bitmapsde tous types et / ou tailles.

J'ai utilisé cette seule ligne de code pour supprimer mon problème "bitmap trop grand ..." :

Picasso.load(resourceId).fit().centerCrop().into(imageView);
Phileo99
la source
L'utilisation de centerCrop () sans appeler resize () entraînera une IllegalStateException. La bibliothèque oblige à appeler le redimensionnement lorsque le recadrage central est utilisé.
Zsolt Boldizsár
Cela est parfaitement logique, car le recadrage implique que vous redimensionnez l'image.
Phileo99
2
Solution rapide, facile et simple. Je ne sais pas si c'est le meilleur, mais j'ai eu ce que je voulais. Merci beaucoup
Nabin
toujours la même erreur lors de l'utilisation de la cible avec Picasso, pour les images de plus grande taille :(
Narendra Singh
Essayez d'utiliser la version 2.5.3-SNAPSHOT, semble que cela fonctionne maintenant correctement avec de grandes images
Anton Malmygin
20

L'ajout des 2 attributs suivants dans ( AndroidManifest.xml) a fonctionné pour moi:

android:largeHeap="true"
android:hardwareAccelerated="false"
Sachin Lala
la source
Cela a résolu mon problème sur l'appareil Samsung. Merci
Hitesh Bisht
16

Changer le fichier image en dossier à drawable-nodpipartir du drawabledossier a fonctionné pour moi.

Asha Antony
la source
Cela empêchera Android d'essayer de redimensionner automatiquement l'image en fonction des dimensions de l'appareil et de la densité de l'écran; c'est pourquoi cette solution fonctionne. Android essaiera de mettre à l'échelle automatiquement tout ce qui se trouve dans le drawabledossier.
Chris Cirefice
10

J'ai utilisé Picasso et j'ai eu le même problème. l'image était trop grande au moins en taille, largeur ou hauteur. enfin j'ai trouvé la solution ici. vous pouvez redimensionner la grande image en fonction de la taille d'affichage et également conserver le rapport hauteur / largeur:

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

et utilisez cette méthode pour charger l'image par 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);

également pour de meilleures performances, vous pouvez télécharger l'image en fonction de la largeur et de la hauteur de l'écran d'affichage, pas de l'image entière:

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

puis:

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

MODIFIER: getDisplaySize ()

display.getWidth()/getHeight()est obsolète. Au lieu de l' Displayutiliser DisplayMetrics.

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

BitmapRegionDecoder fait l'affaire.

Vous pouvez remplacer onDraw(Canvas canvas), démarrer un nouveau fil et décoder la zone visible par l'utilisateur.

Larcho
la source
5

Comme l'a souligné Larcho, à partir du niveau d'API 10, vous pouvez utiliser BitmapRegionDecoderpour charger des régions spécifiques à partir d'une image et avec cela, vous pouvez accomplir pour afficher une grande image en haute résolution en allouant en mémoire uniquement les régions nécessaires. J'ai récemment développé une bibliothèque qui permet de visualiser de grandes images avec la gestion des gestes tactiles. Le code source et des exemples sont disponibles ici .

diegocarloslima
la source
3

Afficher le niveau

Vous pouvez désactiver l'accélération matérielle pour une vue individuelle au moment de l'exécution avec le code suivant:

myView.setLayerType (View.LAYER_TYPE_SOFTWARE, null);

Marveson
la source
3

J'ai rencontré le même problème, voici ma solution. définir la largeur de l'image comme la largeur de l'écran Android, puis redimensionner la hauteur

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

C'est mieux pour n'importe quel écran Android de taille. Dites-moi si cela marche pour vous.

Abhijit Gujar
la source
2

Réduire l'image:

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

L'image sera réduite à la taille de reqHeight et reqWidth. Comme je le comprends, inSampleSize ne prend qu'une puissance de 2 valeurs.

vtlinh
la source
comment connaître reqHeight et reqWidth? Je veux dire, devons-nous le fixer à une valeur statique? ou nous pouvons changer ces appareils wrt?
Prashanth Debbadwar
2

Utilisez la bibliothèque Glide au lieu de charger directement dans imageview

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

Glide.with(this).load(Uri.parse(filelocation))).into(img_selectPassportPic);
Sai Gopi N
la source
2
Utiliser Glide au lieu de Picasso a aidé. On dirait que Glide gère ces problèmes par défaut
Johnny Five
1

J'ai essayé toutes les solutions ci-dessus, l'une après l'autre, pendant de nombreuses heures, et aucune ne semblait fonctionner! Enfin, j'ai décidé de chercher un exemple officiel concernant la capture d'images avec l'appareil photo d'Android et leur affichage. L'exemple officiel ( ici ), m'a finalement donné la seule méthode qui a fonctionné. Ci-dessous, je présente la solution que j'ai trouvée dans cet exemple d'application:

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
la source
0

REMARQUE POUR CEUX QUI VEULENT METTRE DES IMAGES DE PETITE TAILLE:

La solution de Pilot_51 (déplacer vos images dans un drawable-nodpidossier) fonctionne, mais présente un autre problème: elle rend les images TROP PETITES à l'écran à moins que les images ne soient redimensionnées à une très grande résolution (comme 2000 x 3800) pour s'adapter à l'écran - alors cela rend votre application plus lourde .

SOLUTION : mettez vos fichiers image dedans drawable-hdpi- Cela a fonctionné comme un charme pour moi.

facile
la source
1
Vous ne faites que masquer votre problème de densité d'image. Sur du matériel à faible densité, vos images auront l'air plus grandes.
rupps
Je ne pense pas non plus que la solution de Pilot_51 ait des problèmes, vous devez utiliser des tailles d'image appropriées selon vos besoins
Nilabja
0

L'utilisation du sous-dossier dessinable correct a résolu le problème pour moi. Ma solution était de mettre mon image pleine résolution (1920x1200) dans le dossier drawable-xhdpi , au lieu du dossier drawable .

J'ai également mis une image réduite (1280x800) dans le dossier drawable-hdpi .

Ces deux résolutions correspondent aux tablettes Nexus 7 2013 et 2012 que je programme. J'ai également testé la solution sur d'autres tablettes.

steveputz
la source
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);

    }
}

Ce code est du travail

Alobaidi
la source