„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.