"Bitmap zu groß, um in eine Textur hochgeladen zu werden"


154

Ich lade eine Bitmap in eine ImageView und sehe diesen Fehler. Ich nehme an, diese Beschränkung bezieht sich auf eine Größenbeschränkung für OpenGL-Hardwaretexturen (2048 x 2048). Das Bild, das ich laden muss, ist ein Pinch-Zoom-Bild mit einer Höhe von etwa 4.000 Pixel.

Ich habe versucht, die Hardwarebeschleunigung im Manifest auszuschalten, aber keine Freude.

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

Ist es möglich, ein Bild mit mehr als 2048 Pixel in eine ImageView zu laden?


1
Wenn Sie hier suchen, vergessen Sie nicht, Ihr Bild in eine Bildlaufansicht zu setzen, wenn Sie möchten, dass es scrollbar ist. Das wird den Fehler beseitigen. Ich habe einige Zeit verschwendet, bevor mir klar wurde, dass dies mein Problem war.
Jason Ridge

Informationen zum Anzeigen großer Bilder unter Beibehaltung der Bildqualität finden Sie in der Bibliothek unter @stoefln. Ich habe es benutzt und es lohnt sich, es auszuprobieren. Auf jeden Fall besser als inSampleSizeAnsatz.
Mahendra Liya

1
@ Joe Blow aktuelle Antwort hat in deinem Fall nicht funktioniert? Wenn nein, erläutern Sie bitte das Problem, mit dem Sie im Zusammenhang mit dieser Frage konfrontiert waren.
Pravin Divraniya

1
hey @ VC.One - es ist eine seltsame Sache, sich über meinen Mann aufzuregen. Ich hätte auf "Vorhandene Antwort belohnen" klicken sollen. Niemand stört es, mühsam den "besten" Knopf in diesem SO-Popup zu drücken, weil es albern ist :) Jetzt ist alles gelöst, als ich auf das Kopfgeld geklickt habe. Prost!!
Fattie

1
Ich versuche immer, Kopfgelder auszuzahlen (ich sammle nicht gerne Punkte). Ich denke, wenn du auf mein Profil klickst, dann Kopfgelder, @ VC.One wirst du im Laufe der Jahre viele wirklich großartige QS von SO sehen !!!!!!!!!!!!!!!
Fattie

Antworten:


48

Das gesamte Rendering basiert auf OpenGL. Nein, Sie können dieses Limit nicht überschreiten ( GL_MAX_TEXTURE_SIZEabhängig vom Gerät, aber das Minimum ist 2048 x 2048, sodass alle Bilder unter 2048 x 2048 passen).

Wenn Sie bei so großen Bildern verkleinern und ein Mobiltelefon verwenden möchten, sollten Sie ein System einrichten, das dem ähnelt, das Sie beispielsweise in Google Maps sehen. Mit dem Bild in mehrere Teile und mehrere Definitionen aufgeteilt.

Sie können das Bild auch verkleinern, bevor Sie es anzeigen (siehe Antwort von user1352407) von auf diese Frage).

Achten Sie auch darauf, in welchen Ordner Sie das Bild legen. Android kann Bilder automatisch skalieren. Schauen Sie sich die Antwort von Pilot_51 auf diese Frage an.


23
Wenn dies der Fall ist, wie ermöglicht die Galerie-App die Anzeige und Bearbeitung von Bildern, die mit der Kamera aufgenommen wurden? 2048x2048 ist nur ein 4-Megapixel-Bild, und viele Android-Telefone machen Fotos, die viel größer sind, und die Galerie-App scheint keine Probleme zu haben.
Ollie C

3
Weil GL_MAX_TEXTURE_SIZE vom Gerät abhängt.
jptsetung

24
Das macht wirklich keinen Sinn. Ich habe jetzt das gleiche Problem festgestellt - mit einem Bild von 1286 x 835 Pixel. UND: Nur auf einem Galaxy Nexus bekomme ich diese Fehlermeldung und kein Bild! Es scheint einfach lächerlich, dass ein hochmodernes Smartphone kein so kleines Bild anzeigen kann! Mein HTC Hero kann das anzeigen! Was kann ich tun?
Zordid

1
Siehe Romains Antwort hier: stackoverflow.com/questions/7428996/…
Ben Lee

3
@OllieC Ich würde auch gerne wissen, wie die Galerie-Apps das machen. Wenn also jemand weiß oder ein Beispiel für die Darstellung großer Bilder hat, wäre das großartig.
Innova

408

Dies ist keine direkte Antwort auf die Frage (Laden von Bildern> 2048), sondern eine mögliche Lösung für alle, bei denen der Fehler auftritt.

In meinem Fall war das Bild in beiden Dimensionen kleiner als 2048 (um genau zu sein 1280 x 727), und das Problem trat speziell bei einem Galaxy Nexus auf. Das Bild befand sich im drawableOrdner und in keinem der qualifizierten Ordner. Android geht davon aus, dass Drawables ohne Dichtequalifizierer mdpi sind, und skaliert sie für andere Dichten nach oben oder unten, in diesem Fall 2x für xhdpi. drawable-nodpiDas Problem wurde behoben, indem das Bild des Täters verschoben wurde, um eine Skalierung zu verhindern.


3
Ich hatte Speicherprobleme beim Laden von Drawables. Verbrachte 2 Stunden damit zu verstehen, warum dies geschah. Danke für die indirekte Antwort.
TrueCoke

16
Dies ist die richtige Antwort und sollte als solche gekennzeichnet werden.
wblaschko

3
Ungefähr dreieinhalb Monate habe ich fast Vollzeit auf Android programmiert und das habe ich gerade erst gemerkt. Scheint dumm, im drawableWesentlichen einen versteckten drawable-mdpiOrdner zu machen . Dies erklärte auch, warum meine benutzerdefinierten Kartenmarkierungen schrecklich aussahen (sie wurden hochskaliert und dann verkleinert).
Theblang

10
Ja was zur Hölle! Ich denke, 99% der Android-Programmierer denken, "zeichnbar" bedeutet "skaliere das nicht".
Matt Logan

3
Dies ist die hilfreichste Antwort, die ich je gesehen habe. Ich kann nur sagen, dass ich ein Kopfgeld sende! VIELEN DANK.
Fattie

105

Ich habe das Bild folgendermaßen verkleinert:

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

2
danke, die createScaledBitmap war hilfreich für mich, um eine zu große Bitmap anzeigen zu können
Boy

Mein Problem wurde gelöst. Danke. Eigentlich habe ich das Bild von der Kamera genommen und in der ImageView in Samsung Galaxy S4 mit 4.4.2
Mitesh Shah

Entschuldigung, aber ich kann nicht erkennen, was "w" ist
Bobbelinio

1
Entschuldigung, was ctxbedeutet das?
Ameer Sabith

1
@AmeerSabith sieht aus wie ctx ein Kontextobjekt ist (Ihre laufende Aktivität zum Beispiel)
Matt

34

Anstatt stundenlang zu versuchen, diesen gesamten Downsampling-Code manuell zu schreiben / zu debuggen, sollten Sie ihn verwenden Picasso. Es wurde für den Umgang mit gemachtbitmaps allen Arten und / oder Größen gemacht.

Ich habe diese einzelne Codezeile verwendet, um mein Problem "Bitmap zu groß ..." zu entfernen :

Picasso.load(resourceId).fit().centerCrop().into(imageView);

Die Verwendung von centerCrop () ohne Aufruf von resize () führt zu einer IllegalStateException. Die Bibliothek erzwingt das Aufrufen der Größenänderung, wenn der mittlere Zuschnitt verwendet wird.
Zsolt Boldizsár

Das ist absolut sinnvoll, da das Zuschneiden bedeutet, dass Sie die Bildgröße ändern.
Phileo99

2
Schnelle, einfache und einfache Lösung. Ich bin mir nicht sicher, ob es das Beste ist, aber ich habe bekommen, was ich wollte. Vielen Dank
Nabin

Immer noch den gleichen Fehler, wenn Ziel mit Picasso verwendet wird, für die größeren Bilder :(
Narendra Singh

Versuchen Sie, Version 2.5.3-SNAPSHOT zu verwenden, anscheinend funktioniert es jetzt korrekt mit großen Bildern
Anton Malmygin

20

Das Hinzufügen der folgenden 2 Attribute in ( AndroidManifest.xml) hat bei mir funktioniert:

android:largeHeap="true"
android:hardwareAccelerated="false"

Dies hat mein Problem auf dem Samsung-Gerät behoben. Vielen Dank
Hitesh Bisht

16

Das Ändern der Bilddatei in einen drawable-nodpiOrdner von drawableOrdner zu Ordner hat bei mir funktioniert.


Dadurch wird verhindert, dass Android versucht, das Bild basierend auf den Abmessungen und der Bildschirmdichte des Geräts automatisch zu skalieren. Deshalb funktioniert diese Lösung. Android wird versuchen, alles im drawableOrdner automatisch zu skalieren .
Chris Cirefice

10

Ich habe Picasso benutzt und hatte das gleiche Problem. Das Bild war zumindest in Bezug auf Größe, Breite oder Höhe zu groß. Endlich habe ich hier die Lösung gefunden. Sie können das große Bild entsprechend der Anzeigegröße verkleinern und das Seitenverhältnis beibehalten:

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

und verwenden Sie diese Methode zum Laden des Bildes von 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);

Für eine bessere Leistung können Sie das Bild auch nach Breite und Höhe des Bildschirms herunterladen, nicht nach dem gesamten Bild:

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

und dann:

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

BEARBEITEN: getDisplaySize ()

display.getWidth()/getHeight()ist veraltet. Anstelle von DisplayGebrauch DisplayMetrics.

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

6

BitmapRegionDecoder macht den Trick.

Sie können überschreiben onDraw(Canvas canvas), einen neuen Thread starten und den für den Benutzer sichtbaren Bereich dekodieren.


5

Wie von Larcho ausgeführt, können Sie ab API-Ebene 10 BitmapRegionDecoderbestimmte Regionen aus einem Bild laden und so ein großes Bild in hoher Auflösung anzeigen, indem Sie nur die benötigten Regionen im Speicher zuweisen. Ich habe kürzlich eine Bibliothek entwickelt, die die Visualisierung großer Bilder mit Touch-Gesten-Handhabung ermöglicht. Der Quellcode und Beispiele finden Sie hier .


3

Ansichtsebene

Sie können die Hardwarebeschleunigung für eine einzelne Ansicht zur Laufzeit mit dem folgenden Code deaktivieren:

myView.setLayerType (View.LAYER_TYPE_SOFTWARE, null);


3

Ich habe das gleiche Problem durchlaufen, hier ist meine Lösung. Stellen Sie die Breite des Bildes auf die Breite des Android-Bildschirms ein und skalieren Sie dann die Höhe

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

Dies ist besser für jede Größe Android-Bildschirm. Lassen Sie mich wissen, ob es für Sie funktioniert.


2

Bild verkleinern:

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

Das Bild wird auf die Größe von reqHeight und reqWidth verkleinert. Soweit ich weiß, nimmt inSampleSize nur eine Potenz von 2 Werten auf.


wie man reqHeight und reqWidth kennt? Ich meine, müssen wir es auf einen statischen Wert korrigieren? oder können wir diese wrt geräte ändern?
Prashanth Debbadwar

2

Verwenden Sie die Glide-Bibliothek, anstatt sie direkt in die Bildansicht zu laden

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

Glide.with(this).load(Uri.parse(filelocation))).into(img_selectPassportPic);

2
Die Verwendung von Glide anstelle von Picasso hat geholfen. Scheint, als würde Glide solche Probleme standardmäßig behandeln
Johnny Five

1

Ich habe alle oben genannten Lösungen viele Stunden lang nacheinander ausprobiert, und keine schien zu funktionieren! Schließlich habe ich mich nach einem offiziellen Beispiel umgesehen, um Bilder mit der Android-Kamera aufzunehmen und anzuzeigen. Das offizielle Beispiel ( hier ) gab mir endlich die einzige Methode, die funktionierte. Unten präsentiere ich die Lösung, die ich in dieser Beispiel-App gefunden habe:

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

0

HINWEIS FÜR DIE, DIE BILDER VON KLEINER GRÖSSE EINSTELLEN MÖCHTEN:

Die Lösung von Pilot_51 (Verschieben Ihrer Bilder in einen drawable-nodpiOrdner) funktioniert, hat jedoch ein weiteres Problem: Sie macht Bilder ZU KLEIN auf dem Bildschirm , wenn nicht die Bilder , die auf eine sehr große Größe verändert werden (wie 2000 x 3800) Auflösung fit Bildschirm - dann macht es Ihre Anwendung schwerer .

LÖSUNG : Legendrawable-hdpi Sie Ihre Bilddateien ein - Es hat wie ein Zauber für mich funktioniert.


1
Sie maskieren nur Ihr Problem mit der Bilddichte. Auf Hardware mit geringer Dichte sehen Ihre Bilder größer aus.
Rupps

Ich glaube auch nicht, dass die Lösung von Pilot_51 irgendwelche Probleme hat. Sie sollten geeignete Bildgrößen verwenden, die Ihren Anforderungen entsprechen
Nilabja,

0

Die Verwendung des richtigen zeichnbaren Unterordners hat es für mich gelöst. Meine Lösung bestand darin, mein Bild in voller Auflösung (1920 x 1200) in den Ordner drawable-xhdpi anstatt in den Ordner drawable zu legen .

Ich habe auch ein verkleinertes Bild (1280x800) in den Ordner drawable-hdpi gelegt .

Diese beiden Auflösungen stimmen mit den von mir programmierten Nexus 7-Tablets 2013 und 2012 überein. Ich habe die Lösung auch auf einigen anderen Tabletten getestet.


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

    }
}

Dieser Code ist Arbeit

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.