„Mapa bitowa jest zbyt duża, aby można ją było przesłać do tekstury”


154

Ładuję mapę bitową do ImageView i widzę ten błąd. Rozumiem, że ten limit odnosi się do limitu rozmiaru tekstur sprzętowych OpenGL (2048x2048). Obraz, który muszę załadować, to powiększony przez szczypanie obraz o wysokości około 4000 pikseli.

Próbowałem wyłączyć akcelerację sprzętową w manifeście, ale bez radości.

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

Czy można załadować obraz większy niż 2048 pikseli do ImageView?


1
Każdy, kto tutaj szuka, nie zapomnij umieścić swojego obrazu w widoku przewijania, jeśli chcesz, aby można go było przewijać. Pozwoli to usunąć błąd. Zmarnowałem trochę czasu, zanim zdałem sobie sprawę, że to mój problem.
Jason Ridge

Dla każdego, kto chce wyświetlać duże obrazy, zachowując jakość obrazu, zapoznaj się z biblioteką w odpowiedzi @stoefln poniżej. Skorzystałem z niego i warto spróbować. Zdecydowanie lepsze niż inSampleSizepodejście.
Mahendra Liya

1
Obecna odpowiedź @Joe Blow nie działa w Twoim przypadku? Jeśli nie, to proszę szczegółowo opisać problem, z jakim się spotkałeś w kontekście tego pytania?
Pravin Divraniya

1
hej @ VC.One - dziwnie się denerwuję z powodu mojego mężczyzny. Powinienem był kliknąć „Nagradzaj istniejącą odpowiedź”. Nikt nie kłopocze się nużącym wciskaniem przycisku „najlepsze” w tym wyskakującym okienku, bo to głupie :) Wszystko zostało rozwiązane teraz, gdy kliknąłem nagrodę. Twoje zdrowie!!
Fattie

1
Staram się zawsze wypłacać nagrody (nie lubię zbierać punktów). Myślę, że jeśli klikniesz na mój profil, a następnie bounties, @ VC.One zobaczysz wiele naprawdę świetnych QA od SO na przestrzeni lat !!!!!!!!!!!!!!!
Fattie

Odpowiedzi:


48

Całe renderowanie jest oparte na OpenGL, więc nie, nie możesz przekroczyć tego limitu (w GL_MAX_TEXTURE_SIZEzależności od urządzenia, ale minimum to 2048x2048, więc każdy obraz niższy niż 2048x2048 będzie pasował).

Przy tak dużych obrazach, jeśli chcesz pomniejszyć i na telefonie komórkowym, powinieneś skonfigurować system podobny do tego, który widzisz na przykład na mapach Google. Z obrazem podzielonym na kilka części i kilkoma definicjami.

Lub możesz zmniejszyć obraz przed wyświetleniem go (zobacz odpowiedź użytkownika1352407 na to pytanie).

Uważaj także, w którym folderze umieścisz obraz, Android może automatycznie skalować obrazy. Spójrz na odpowiedź pilota_51 poniżej na to pytanie.


23
W takim przypadku w jaki sposób aplikacja Galeria umożliwia wyświetlanie i manipulowanie zdjęciami wykonanymi aparatem? 2048x2048 to tylko obraz 4MP, a wiele telefonów z Androidem robi zdjęcia znacznie większe niż to, a aplikacja Galeria wydaje się nie mieć problemów.
Ollie C

3
Ponieważ GL_MAX_TEXTURE_SIZE zależy od urządzenia.
jptsetung,

24
To naprawdę nie ma sensu. Teraz napotkałem ten sam problem - z obrazem o wymiarach 1286x835 pikseli. I: tylko na Galaxy Nexus pojawia się ten komunikat o błędzie i nie ma obrazu! To po prostu wydaje się śmieszne, że supernowoczesny smartfon nie może wyświetlić tak małego obrazu! Mój HTC Hero jest w stanie to wyświetlić! Co mogę zrobić?
Zordid

1
Zobacz odpowiedź Romaina tutaj: stackoverflow.com/questions/7428996/…
Ben Lee

3
@OllieC Chciałbym również wiedzieć, jak robią to aplikacje galerii. Więc jeśli ktoś wie lub ma przykład pokazujący duże obrazy, byłoby świetnie.
Innova

408

To nie jest bezpośrednia odpowiedź na pytanie (ładowanie obrazów> 2048), ale możliwe rozwiązanie dla każdego, kto doświadcza błędu.

W moim przypadku obraz był mniejszy niż 2048 w obu wymiarach (dokładnie 1280x727), a problem wystąpił szczególnie w Galaxy Nexus. Obraz znajdował się w drawablefolderze, a żaden z kwalifikowanych folderów. Android zakłada, że ​​rysunki bez kwalifikatora gęstości są mdpi i skaluje je w górę lub w dół dla innych gęstości, w tym przypadku przeskalowane w górę 2x dla xhdpi. Przeniesienie obrazu winowajcy na, drawable-nodpiaby zapobiec skalowaniu, rozwiązało problem.


3
Miałem problemy z pamięcią podczas próby załadowania rysunków. Spędziłem 2 godziny, próbując zrozumieć, dlaczego tak się dzieje. Dzięki za pośrednią odpowiedź.
TrueCoke

16
To jest poprawna odpowiedź i należy ją jako taką oznaczyć.
wblaschko

3
Od około trzech i pół miesiąca programuję prawie na pełny etat na Androidzie i właśnie to sobie uświadomiłem. Wydaje się głupie, aby drawablezasadniczo utworzyć ukryty drawable-mdpifolder. To wyjaśniało również, dlaczego moje niestandardowe znaczniki mapy wyglądały okropnie (były przeskalowywane, a następnie zmniejszane).
theblang

10
tak, co do cholery! Myślę, że 99% programistów Androida uważa, że ​​„rysowalny” oznacza „nie skaluj tego”.
Matt Logan

3
To najbardziej pomocna odpowiedź, jaką kiedykolwiek widziałem. Mogę tylko powiedzieć, że wysyłam nagrodę! DZIĘKI.
Fattie

105

Obraz zmniejszyłem w ten sposób:

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
dzięki, createScaledBitmap była dla mnie pomocna, aby móc wyświetlić zbyt dużą bitmapę
Boy

Rozwiązałem mój problem… dzięki. Właściwie
robiłem

przepraszam, ale nie mogę wykryć, co to jest "w"
Bobbelinio

1
Przepraszam, co to ctxoznacza?
Ameer Sabith

1
@AmeerSabith wygląda na to, że ctx to obiekt kontekstu (na przykład Twoja aktywność biegowa)
Matt

34

Zamiast spędzać wiele godzin na próbach ręcznego pisania / debugowania całego tego kodu próbkującego, dlaczego by nie użyć Picasso? Został stworzony do radzenia sobie ze bitmapswszystkimi typami i / lub rozmiarami.

Użyłem tej pojedynczej linii kodu, aby usunąć problem „zbyt duża mapa bitowa ...” :

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

Użycie centerCrop () bez wywołania resize () spowoduje powstanie wyjątku IllegalStateException. Biblioteka wymusza wywołanie zmiany rozmiaru, gdy używane jest przycięcie środkowe.
Zsolt Boldizsár

Ma to sens, ponieważ kadrowanie oznacza, że ​​zmieniasz rozmiar obrazu.
Phileo99

2
Szybkie, łatwe i proste rozwiązanie. Nie jestem pewien, czy jest najlepszy, ale dostałem to, czego chciałem. Dziękuję bardzo
Nabin

nadal pojawia się ten sam błąd, gdy używany jest cel z Picassem, dla większych obrazów :(
Narendra Singh

Spróbuj użyć wersji 2.5.3-SNAPSHOT, wygląda na to, że teraz działa poprawnie z dużymi obrazami
Anton Malmygin

20

Pomogło mi dodanie następujących 2 atrybutów w ( AndroidManifest.xml):

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

To rozwiązało mój problem na urządzeniu Samsung. Dzięki
Hitesh Bisht

16

Zmiana pliku obrazu na drawable-nodpifolder z drawablefolderu zadziałała dla mnie.


Uniemożliwi to systemowi Android próbę automatycznego skalowania obrazu na podstawie wymiarów urządzenia i gęstości ekranu; dlatego to rozwiązanie działa. Android spróbuje automatycznie skalować wszystko w drawablefolderze.
Chris Cirefice

10

Użyłem Picassa i miałem ten sam problem. obraz był zbyt duży, przynajmniej pod względem rozmiaru, szerokości lub wysokości. w końcu znalazłem tutaj rozwiązanie. możesz skalować duży obraz w dół zgodnie z rozmiarem wyświetlacza, a także zachować proporcje:

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

i użyj tej metody wczytywania obrazu przez Picassa:

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

również dla lepszej wydajności możesz pobrać obraz według szerokości i wysokości ekranu wyświetlacza, a nie całego obrazu:

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

i wtedy:

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

EDYCJA: getDisplaySize ()

display.getWidth()/getHeight()jest przestarzałe. Zamiast Displayużywać DisplayMetrics.

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

6

BitmapRegionDecoder Zrób sztuczkę.

Możesz nadpisać onDraw(Canvas canvas), rozpocząć nowy wątek i zdekodować obszar widoczny dla użytkownika.


5

Jak wskazał Larcho, zaczynając od poziomu API 10, możesz użyć BitmapRegionDecoderdo załadowania określonych regionów z obrazu, a dzięki temu możesz wyświetlić duży obraz w wysokiej rozdzielczości, przydzielając w pamięci tylko potrzebne regiony. Niedawno opracowałem bibliotekę, która zapewnia wizualizację dużych obrazów z obsługą gestów dotykowych. Kod źródłowy i próbki są dostępne tutaj .


3

Poziom widoku

Możesz wyłączyć przyspieszanie sprzętowe dla pojedynczego widoku w czasie wykonywania za pomocą następującego kodu:

myView.setLayerType (View.LAYER_TYPE_SOFTWARE, null);


3

Przeszedłem przez ten sam problem, oto moje rozwiązanie. ustaw szerokość obrazu taką samą jak szerokość ekranu Androida, a następnie skaluje wysokość

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

Jest to lepsze dla ekranu Androida o dowolnym rozmiarze. daj mi znać, czy to działa dla Ciebie.


2

Zmniejsz obraz:

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

Obraz zostanie zmniejszony do rozmiaru reqHeight i reqWidth. Jak rozumiem, inSampleSize przyjmuje tylko 2 wartości.


jak poznać reqHeight i reqWidth? Mam na myśli, czy musimy to naprawić do wartości statycznej? czy możemy zmienić te urządzenia wrt?
Prashanth Debbadwar

2

Użyj biblioteki Glide zamiast bezpośredniego ładowania do widoku obrazu

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

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

2
Pomogło użycie Glide zamiast Picassa. Wygląda na to, że Glide domyślnie radzi sobie z takimi problemami
Johnny Five

1

Wypróbowałem wszystkie powyższe rozwiązania, jedno po drugim, przez wiele godzin i żadne z nich nie działało! W końcu postanowiłem rozejrzeć się za oficjalnym przykładem robienia zdjęć aparatem Androida i ich wyświetlania. Oficjalny przykład ( tutaj ) dał mi wreszcie jedyną metodę, która zadziałała. Poniżej przedstawiam rozwiązanie, które znalazłem w tej przykładowej aplikacji:

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

UWAGA DLA OSÓB, KTÓRZY CHCĄ WPROWADZAĆ ZDJĘCIA MAŁEGO ROZMIARU

Rozwiązanie Pilot_51 (przenoszenie obrazów do drawable-nodpifolderu) działa, ale ma inny problem: powoduje, że obrazy na ekranie są ZBYT MAŁE, chyba że obrazy są przeskalowane do bardzo dużej (np. 2000 x 3800) rozdzielczości, aby zmieścić się na ekranie - wtedy aplikacja jest cięższa .

ROZWIĄZANIE : włóż pliki graficzne drawable-hdpi- dla mnie zadziałało jak urok.


1
Po prostu maskujesz problem z gęstością obrazu. Na sprzęcie o niskiej gęstości obrazy będą wyglądać na większe.
pęknięcia

nie sądzę też, żeby rozwiązanie Pilot_51 miało jakiekolwiek problemy, powinieneś użyć odpowiednich rozmiarów obrazu zgodnie z
własnymi

0

Użycie odpowiedniego podfolderu do rysowania rozwiązało to za mnie. Moim rozwiązaniem było umieszczenie mojego obrazu w pełnej rozdzielczości (1920x1200) w folderze drawable-xhdpi zamiast folderu do rysowania .

Umieściłem również zmniejszony obraz (1280x800) do folderu drawable-hdpi .

Te dwie rozdzielczości pasują do tabletów Nexus 7 z 2013 i 2012 roku, które programuję. Przetestowałem to rozwiązanie również na kilku innych tabletach.


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

    }
}

Ten kod działa

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.