Centro de corte de bitmap do Android

150

Eu tenho bitmaps que são quadrados ou retângulos. Pego o lado mais curto e faço algo assim:

int value = 0;
if (bitmap.getHeight() <= bitmap.getWidth()) {
    value = bitmap.getHeight();
} else {
    value = bitmap.getWidth();
}

Bitmap finalBitmap = null;
finalBitmap = Bitmap.createBitmap(bitmap, 0, 0, value, value);

Então eu o dimensiono para um bitmap 144 x 144 usando o seguinte:

Bitmap lastBitmap = null;
lastBitmap = Bitmap.createScaledBitmap(finalBitmap, 144, 144, true);

O problema é que ele corta o canto superior esquerdo do bitmap original. Alguém tem o código para cortar o centro do bitmap?

Maurice
fonte

Respostas:

354

insira a descrição da imagem aqui

Isso pode ser alcançado com: Bitmap.createBitmap (origem, x, y, largura, altura)

if (srcBmp.getWidth() >= srcBmp.getHeight()){

  dstBmp = Bitmap.createBitmap(
     srcBmp, 
     srcBmp.getWidth()/2 - srcBmp.getHeight()/2,
     0,
     srcBmp.getHeight(), 
     srcBmp.getHeight()
     );

}else{

  dstBmp = Bitmap.createBitmap(
     srcBmp,
     0, 
     srcBmp.getHeight()/2 - srcBmp.getWidth()/2,
     srcBmp.getWidth(),
     srcBmp.getWidth() 
     );
}
Lumis
fonte
1
Editou a resposta para que o bitmap de destino real seja um quadrado.
Joram van den Boezem 29/03
1
@ Lumis: Por que você reverte a revisão 3? Parecia ser um correto válido. Sua versão atual cria o ponto de partida correto, mas inclui o restante do lado muito longo. Por exemplo, dada uma 100x1000imagem, você recebe de volta uma 100x550imagem.
Guvante 29/03
Obrigado, você está certo, o formato é Bitmap.createBitmap (fonte, x, y, largura, altura)
Lumis
11
Veja minha resposta sobre o uso do método ThumbnailUtils.extractThumbnail () interno. Por que reinventar a roda ??? stackoverflow.com/a/17733530/1103584
DiscDev 3/13/13
1
essa solução é mais curta do que criar uma tela e depois desenhar um desenho para ela. Ele também faz menos processamento do que a solução ThumbnailUtils (que calcula o tamanho da amostra para descobrir como dimensionar).
yincrash
319

Embora a maioria das respostas acima forneça uma maneira de fazer isso, já existe uma maneira interna de fazer isso e é uma linha de código ( ThumbnailUtils.extractThumbnail())

int dimension = getSquareCropDimensionForBitmap(bitmap);
bitmap = ThumbnailUtils.extractThumbnail(bitmap, dimension, dimension);

...

//I added this method because people keep asking how 
//to calculate the dimensions of the bitmap...see comments below
public int getSquareCropDimensionForBitmap(Bitmap bitmap)
{
    //use the smallest dimension of the image to crop to
    return Math.min(bitmap.getWidth(), bitmap.getHeight());
}

Se você deseja que o objeto bitmap seja reciclado, pode passar opções que o tornam:

bitmap = ThumbnailUtils.extractThumbnail(bitmap, dimension, dimension, ThumbnailUtils.OPTIONS_RECYCLE_INPUT);

De: documentação ThumbnailUtils

public estático Bitmap extractThumbnail (fonte Bitmap, largura int, altura int)

Adicionado no nível 8 da API Cria um bitmap centralizado do tamanho desejado.

Parâmetros fonte largura de origem de bitmap original largura de destino altura de destino altura de destino

Às vezes, eu estava perdendo erros de memória ao usar a resposta aceita e o ThumbnailUtils resolveu esses problemas para mim. Além disso, isso é muito mais limpo e reutilizável.

DiscDev
fonte
6
+1 Acho que você precisaria melhorar esse código e, em vez de usar 400px, passar o menor tamanho de bmp, a fim de fornecer uma alternativa para a postagem original acima. Mas obrigado por trazer isso à nossa atenção, parece uma função muito útil. Pena que eu não vi isso antes ...
Lumis 10/10
Certo, eu só codificado 400 para dar um exemplo concreto ... os detalhes são até o implementor :)
DiscDev
4
@ DiscDev - isso teria que ser literalmente uma das respostas mais úteis em todo o site. Seriamente. É estranho nas perguntas do Android - muitas vezes você pode pesquisar por duas horas antes de encontrar a resposta óbvia simples. Não sei como agradecer o suficiente. Recompensa a caminho!
Fattie 25/05
2
Você pode usar uma função um pouco diferente para reciclar também o bitmap antigo: = ThumbnailUtils.extractThumbnail (bitmap, largura, altura, ThumbnailUtils.OPTIONS_RECYCLE_INPUT)
desenvolvedor Android
1
Esta é a resposta mais poderosa que já vi até agora para a pergunta do Android. Eu já vi 20 soluções diferentes como dimensionar / cortar / reduzir / redimensionar e assim por diante no Bitmap no Android. Todas as respostas são diferentes. E este apenas pega 1 linha e funciona exatamente como o esperado. Obrigado.
Alex Sorokoletov 27/07/2014
12

Você já pensou em fazer isso a partir do layout.xml? Você pode definir para o seu ImageViewo ScaleType para android:scaleType="centerCrop"e definir as dimensões da imagem no ImageViewinterior do layout.xml.

Ovidiu Latcu
fonte
Tentei essa idéia com o seguinte erro do OpenGLRenderer: "Bitmap muito grande para ser carregado em uma textura (2432x4320, max = 4096x4096)" Então, acho que a altura do 4320 não pode ser processada.
22414 GregMr
1
Claro que esta é uma resposta correta e responde a pergunta perfeita! Otimizando a qualidade / tamanho da imagem para imagens grandes ... Bem, é uma pergunta diferente!
Ichalos
@ichalos Talvez você tenha encontrado o que estava procurando, mas isso não responde à pergunta original. A pergunta original era sobre renderização manual na tela .. na verdade, não tenho certeza de como a primeira tentativa de cortar uma foto seria renderizada manualmente na tela, mas, ei, que bom que você encontrou uma solução aqui :)
milosmns
@milosmns Talvez você esteja certo. Talvez seja assim que o usuário tentou abordar o problema de recortar manualmente no centro. Tudo depende das necessidades exatas do usuário original.
ichalos 26/03/19
9

Você pode usar o seguinte código que pode resolver seu problema.

Matrix matrix = new Matrix();
matrix.postScale(0.5f, 0.5f);
Bitmap croppedBitmap = Bitmap.createBitmap(bitmapOriginal, 100, 100,100, 100, matrix, true);

O método acima postScalling da imagem antes do corte, para que você possa obter o melhor resultado com a imagem cortada sem obter erro OOM.

Para mais detalhes, você pode consultar este blog

Hitesh Patel
fonte
1
E / AndroidRuntime (30010): Causado por: java.lang.IllegalArgumentException: x + width deve ser <= bitmap.width () e isso por causa de 4 vezes 100px
Stan
9

Aqui está um trecho mais completo que recorta o centro de um [bitmap] de dimensões arbitrárias e dimensiona o resultado para o [IMAGE_SIZE] desejado . Assim, você sempre obterá um quadrado em escala [croppedBitmap] do centro da imagem com um tamanho fixo. ideal para miniaturas e tal.

É uma combinação mais completa das outras soluções.

final int IMAGE_SIZE = 255;
boolean landscape = bitmap.getWidth() > bitmap.getHeight();

float scale_factor;
if (landscape) scale_factor = (float)IMAGE_SIZE / bitmap.getHeight();
else scale_factor = (float)IMAGE_SIZE / bitmap.getWidth();
Matrix matrix = new Matrix();
matrix.postScale(scale_factor, scale_factor);

Bitmap croppedBitmap;
if (landscape){
    int start = (tempBitmap.getWidth() - tempBitmap.getHeight()) / 2;
    croppedBitmap = Bitmap.createBitmap(tempBitmap, start, 0, tempBitmap.getHeight(), tempBitmap.getHeight(), matrix, true);
} else {
    int start = (tempBitmap.getHeight() - tempBitmap.getWidth()) / 2;
    croppedBitmap = Bitmap.createBitmap(tempBitmap, 0, start, tempBitmap.getWidth(), tempBitmap.getWidth(), matrix, true);
}
mschmoock
fonte
8

Provavelmente a solução mais fácil até agora:

public static Bitmap cropCenter(Bitmap bmp) {
    int dimension = Math.min(bmp.getWidth(), bmp.getHeight());
    return ThumbnailUtils.extractThumbnail(bmp, dimension, dimension);
}

importações:

import android.media.ThumbnailUtils;
import java.lang.Math;
import android.graphics.Bitmap;
Kirill Kulakov
fonte
3

Para corrigir a solução @willsteel:

if (landscape){
                int start = (tempBitmap.getWidth() - tempBitmap.getHeight()) / 2;
                croppedBitmap = Bitmap.createBitmap(tempBitmap, start, 0, tempBitmap.getHeight(), tempBitmap.getHeight(), matrix, true);
            } else {
                int start = (tempBitmap.getHeight() - tempBitmap.getWidth()) / 2;
                croppedBitmap = Bitmap.createBitmap(tempBitmap, 0, start, tempBitmap.getWidth(), tempBitmap.getWidth(), matrix, true);
            }
Iman
fonte
essa é uma correção para a solução WillSteel. Nesse caso, tempBitmap é apenas uma cópia do Bitmap original (inalterado), ou ele próprio.
Yman
1
public static Bitmap resizeAndCropCenter(Bitmap bitmap, int size, boolean recycle) {
    int w = bitmap.getWidth();
    int h = bitmap.getHeight();
    if (w == size && h == size) return bitmap;
    // scale the image so that the shorter side equals to the target;
    // the longer side will be center-cropped.
    float scale = (float) size / Math.min(w,  h);
    Bitmap target = Bitmap.createBitmap(size, size, getConfig(bitmap));
    int width = Math.round(scale * bitmap.getWidth());
    int height = Math.round(scale * bitmap.getHeight());
    Canvas canvas = new Canvas(target);
    canvas.translate((size - width) / 2f, (size - height) / 2f);
    canvas.scale(scale, scale);
    Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.DITHER_FLAG);
    canvas.drawBitmap(bitmap, 0, 0, paint);
    if (recycle) bitmap.recycle();
    return target;
}

private static Bitmap.Config getConfig(Bitmap bitmap) {
    Bitmap.Config config = bitmap.getConfig();
    if (config == null) {
        config = Bitmap.Config.ARGB_8888;
    }
    return config;
}
kakopappa
fonte
1
public Bitmap getResizedBitmap(Bitmap bm) {
    int width = bm.getWidth();
    int height = bm.getHeight();

    int narrowSize = Math.min(width, height);
    int differ = (int)Math.abs((bm.getHeight() - bm.getWidth())/2.0f);
    width  = (width  == narrowSize) ? 0 : differ;
    height = (width == 0) ? differ : 0;

    Bitmap resizedBitmap = Bitmap.createBitmap(bm, width, height, narrowSize, narrowSize);
    bm.recycle();
    return resizedBitmap;
}
Vahe Gharibyan
fonte