"Bitmap muito grande para ser carregado em uma textura"

154

Estou carregando um bitmap em um ImageView e vendo esse erro. Entendo que esse limite se refere a um limite de tamanho para texturas de hardware OpenGL (2048x2048). A imagem que preciso carregar é uma imagem com zoom de pitada de cerca de 4.000 pixels de altura.

Tentei desativar a aceleração de hardware no manifesto, mas sem alegria.

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

É possível carregar uma imagem maior que 2048 pixels em um ImageView?

Ollie C
fonte
1
Para quem procura aqui, não se esqueça de colocar sua imagem em uma exibição de rolagem, se quiser que ela seja rolável. Isso vai se livrar do erro. Perdi um tempo antes de perceber que esse era o meu problema.
Jason Ridge
Para quem deseja exibir imagens grandes ainda mantendo a qualidade da imagem, consulte a biblioteca na resposta @stoefln abaixo. Eu usei e vale a pena tentar. Definitivamente melhor do que inSampleSizeabordagem.
Mahendra Liya
1
@ Joe Blow resposta atual não funcionou no seu caso? Se não, por favor, descreva o problema que você enfrentou no contexto desta questão?
Pravin Divraniya
1
ei @ VC.One - é uma coisa estranha se preocupar com o meu homem. Eu deveria ter clicado em "Recompensar resposta existente". Ninguém se incomoda em pressionar tediosamente o botão "melhor" nesse pop-up do SO, porque é bobagem :) Está tudo resolvido agora quando eu cliquei na recompensa. Felicidades!!
Fattie 02/12/19
1
Tento sempre pagar recompensas (não gosto de acumular pontos). Eu acho que, se você clicar no meu perfil, então recompensas, @ VC.One você verá muitos ótimos controles de qualidade da SO ao longo dos anos !!!!!!!!!!!!!!!
Fattie 02/12/19

Respostas:

48

Toda a renderização é baseada no OpenGL; portanto, você não pode ultrapassar esse limite ( GL_MAX_TEXTURE_SIZEdepende do dispositivo, mas o mínimo é 2048x2048; portanto, qualquer imagem menor que 2048x2048 será adequada).

Com imagens tão grandes, se você quiser diminuir o zoom e em um celular, configure um sistema semelhante ao que vê no google maps, por exemplo. Com a imagem dividida em várias partes e várias definições.

Ou você pode reduzir a imagem antes de exibi-la (consulte a resposta do usuário1352407 nesta pergunta).

Além disso, tenha cuidado com a pasta em que você coloca a imagem, o Android pode ampliar automaticamente as imagens. Dê uma olhada na resposta de Pilot_51 abaixo sobre esta questão.

jptsetung
fonte
23
Nesse caso, como o aplicativo Gallery permite a exibição e manipulação de imagens tiradas com a câmera? 2048x2048 é apenas uma imagem de 4MP, e muitos telefones Android tiram fotos muito maiores que isso, e o aplicativo Gallery parece não ter problemas.
Ollie C
3
Porque GL_MAX_TEXTURE_SIZE depende do dispositivo.
precisa saber é o seguinte
24
Isso realmente não faz sentido. Encontrei o mesmo problema agora - com uma imagem de 1286x835 pixels. E: somente no Galaxy Nexus, recebo esta mensagem de erro e nenhuma imagem! Parece ridículo que um smartphone de ponta não consiga exibir uma imagem tão pequena! O meu HTC Hero é capaz de mostrar isso! O que eu posso fazer?
Zordid 26/04
1
Veja a resposta de Romain aqui: stackoverflow.com/questions/7428996/…
Ben Lee
3
@OllieC Eu também gostaria de saber como os aplicativos da galeria fazem isso. Portanto, se alguém souber, ou tiver um exemplo para mostrar imagens grandes, isso seria ótimo.
Innova 23/05
408

Esta não é uma resposta direta à pergunta (carregamento de imagens> 2048), mas uma solução possível para qualquer pessoa que esteja enfrentando o erro.

No meu caso, a imagem era menor que 2048 em ambas as dimensões (1280x727, para ser exato) e o problema ocorreu especificamente no Galaxy Nexus. A imagem estava na drawablepasta e nenhuma das pastas qualificadas. O Android assume que os drawables sem um qualificador de densidade sejam mdpi e os dimensione para cima ou para baixo para outras densidades; nesse caso, aumentado em 2x para xhdpi. Mover a imagem do culpado drawable-nodpipara evitar o dimensionamento resolveu o problema.

Pilot_51
fonte
3
Eu estava com problemas de memória tentando carregar drawables. Passamos 2 horas tentando entender por que isso estava acontecendo. Obrigado pela resposta indireta.
TrueCoke
16
Esta é a resposta correta e deve ser marcada como tal.
Wblaschko
3
Há cerca de três meses e meio, tenho programado quase o tempo todo no Android e agora percebi isso. Parece estúpido criar drawableessencialmente uma drawable-mdpipasta oculta . Isso também explicava por que meus marcadores de mapa personalizados pareciam horríveis (eles estavam sendo aumentados e reduzidos).
theblang
10
sim que diabos! Eu acho que 99% dos programadores do Android pensam que "drawable" significa "não dimensionar isso".
Matt Logan
3
Esta é a resposta mais útil que eu já vi, em qualquer lugar. Tudo o que posso dizer é que estou enviando uma recompensa! OBRIGADO.
Fattie
105

Eu reduzi a imagem desta maneira:

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
fonte
2
graças, o createScaledBitmap foi útil para mim ser capaz de mostrar uma muito grande bitmap
Boy
Resolvido meu problema .. obrigado. Na verdade eu estava tomando a imagem da câmera e indicar no ImageView no Samsung Galaxy S4 com 4.4.2
Mitesh Shah
Desculpe, mas eu não posso detectar o que é "w"
Bobbelinio
1
Desculpe, o que isso ctxsignifica?
Ameer Sabith
1
@AmeerSabith olhares como CTX é um objeto de contexto (o seu correndo Atividade por exemplo)
Matt
34

Em vez de gastar horas e horas tentando escrever / depurar todo esse código de redução de amostragem manualmente, por que não usar Picasso? Foi feito para lidar com bitmapstodos os tipos e / ou tamanhos.

Eu usei essa única linha de código para remover o meu problema "bitmap muito grande ...." :

Picasso.load(resourceId).fit().centerCrop().into(imageView);
Phileo99
fonte
Usar centerCrop () sem chamar redimensionar () resultará em uma IllegalStateException. A biblioteca obriga a redimensionar quando o recorte central é usado.
Zsolt Boldizsár
Isso faz todo o sentido, porque cortar implica que você está redimensionando a imagem.
precisa saber é o seguinte
2
Solução rápida, fácil e simples. Não tenho certeza se é o melhor, mas consegui o que queria. Muito obrigado
Nabin
ainda obter o mesmo erro quando usado alvo com Picasso, para as imagens de tamanho maior :(
Narendra Singh
Tente usar a versão 2.5.3-SNAPSHOT, parece que está agora funcionam corretamente com imagens grandes
Anton Malmygin
20

A adição dos 2 atributos a seguir em ( AndroidManifest.xml) funcionou para mim:

android:largeHeap="true"
android:hardwareAccelerated="false"
Sachin Lala
fonte
Isso corrigiu meu problema no dispositivo Samsung. Obrigado
Hitesh Bisht
16

Alterar o arquivo de imagem para drawable-nodpipasta da drawablepasta funcionou para mim.

Asha Antony
fonte
Isso impedirá que o Android tente dimensionar automaticamente a imagem com base nas dimensões do dispositivo e na densidade da tela; é por isso que esta solução funciona. O Android tentará dimensionar automaticamente qualquer coisa na drawablepasta.
Chris Cirefice
10

Eu usei Picasso e tive o mesmo problema. a imagem era muito grande, pelo menos no tamanho, largura ou altura. finalmente encontrei a solução aqui. você pode reduzir a imagem grande de acordo com o tamanho da tela e também manter a proporção:

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

e use este método para carregar a imagem do 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);

também para um melhor desempenho, você pode fazer o download da imagem de acordo com a largura e a altura da tela, e não toda a imagem:

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

e depois:

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

EDIT: getDisplaySize ()

display.getWidth()/getHeight()está obsoleto. Em vez de Displayusar DisplayMetrics.

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

BitmapRegionDecoder faz o truque.

Você pode substituir onDraw(Canvas canvas), iniciar um novo Thread e decodificar a área visível para o usuário.

Larcho
fonte
5

Conforme apontado por Larcho, a partir do nível 10 da API, você pode BitmapRegionDecodercarregar regiões específicas de uma imagem e, com isso, pode mostrar uma imagem grande em alta resolução alocando na memória apenas as regiões necessárias. Recentemente, desenvolvi uma biblioteca que fornece a visualização de imagens grandes com manipulação de gestos por toque. O código fonte e as amostras estão disponíveis aqui .

diegocarloslima
fonte
3

Ver nível

Você pode desativar a aceleração de hardware para uma visualização individual em tempo de execução com o seguinte código:

myView.setLayerType (View.LAYER_TYPE_SOFTWARE, nulo);

marveson
fonte
3

Corri pelo mesmo problema, aqui está a minha solução. defina a largura da imagem igual à largura da tela do Android e depois dimensione a 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);

Isto é melhor para qualquer tamanho de tela Android. deixe-me saber se funciona para você.

Abhijit Gujar
fonte
2

Reduzir imagem:

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

A imagem será reduzida no tamanho de reqHeight e reqWidth. Pelo que entendi, inSampleSize apenas absorve uma potência de 2 valores.

vtlinh
fonte
como saber reqHeight e reqWidth? Quero dizer, precisamos corrigi-lo com valor estático? ou podemos mudar esses dispositivos wrt?
Prashanth Debbadwar
2

Use a biblioteca Glide em vez de carregar diretamente na visualização de imagem

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

Glide.with(this).load(Uri.parse(filelocation))).into(img_selectPassportPic);
Sai Gopi N
fonte
2
Usar o Glide em vez de Picasso ajudou. Parece que o Glide lida com esses problemas por padrão
Johnny Five
1

Tentei todas as soluções acima, uma após a outra, por muitas horas, e nenhuma parecia funcionar! Finalmente, decidi procurar um exemplo oficial sobre a captura de imagens com a câmera do Android e exibi-las. O exemplo oficial ( aqui ) finalmente me deu o único método que funcionou. Abaixo, apresento a solução que encontrei nesse aplicativo de exemplo:

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

NOTA PARA QUEM QUER COLOCAR IMAGENS DE TAMANHO PEQUENO:

A solução do Pilot_51 (mover suas imagens para a drawable-nodpipasta) funciona, mas tem outro problema: torna as imagens MUITO PEQUENAS na tela, a menos que as imagens sejam redimensionadas para uma resolução muito grande (como 2000 x 3800) para caber na tela - e torna seu aplicativo mais pesado .

SOLUÇÃO : coloque seus arquivos de imagem drawable-hdpi- funcionou como um encanto para mim.

makeasy
fonte
1
Você está apenas ocultando seu problema de densidade de imagem. Em hardware com baixa densidade, suas imagens parecerão maiores.
Rupps
Eu também não acho que a solução da Pilot_51 tem quaisquer problemas, você deve usar tamanhos de imagem apropriados de acordo com suas necessidades
Nilabja
0

Usar a subpasta desenhável correta resolveu isso para mim. Minha solução foi colocar minha imagem em resolução máxima (1920x1200) na pasta drawable-xhdpi , em vez da pasta drawable .

Também coloquei uma imagem reduzida (1280x800) na pasta drawable-hdpi .

Essas duas resoluções correspondem aos tablets Nexus 7 de 2013 e 2012 que estou programando. Também testei a solução em alguns outros tablets.

steveputz
fonte
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 é trabalho

Alobaidi
fonte