Estou construindo um aplicativo social com uso intensivo de imagens, onde as imagens são enviadas do servidor para o dispositivo. Quando o dispositivo tem resoluções de tela menores, preciso redimensionar os bitmaps, no dispositivo, para corresponder aos tamanhos de exibição pretendidos.
O problema é que usar createScaledBitmap faz com que eu tenha muitos erros de falta de memória após redimensionar uma horda de imagens em miniatura.
Qual é a maneira mais eficiente de redimensionar bitmaps no Android?
android
performance
bitmap
out-of-memory
Colt McAnlis
fonte
fonte
Respostas:
Existem três maneiras dominantes de redimensionar um bitmap no Android, que têm diferentes propriedades de memória:
API createScaledBitmap
Esta API pegará um bitmap existente e criará um NOVO bitmap com as dimensões exatas que você selecionou.
No lado positivo, você pode obter exatamente o tamanho de imagem que está procurando (independentemente de sua aparência). Mas a desvantagem é que essa API requer um bitmap existente para funcionar . Significa que a imagem teria que ser carregada, decodificada e um bitmap criado, antes de ser capaz de criar uma nova versão menor. Isso é ideal em termos de obter suas dimensões exatas, mas horrível em termos de sobrecarga de memória adicional. Como tal, isso é uma espécie de quebra de negócio para a maioria dos desenvolvedores de aplicativos que tendem a se preocupar com a memória
Sinalizador inSampleSize
BitmapFactory.Options
tem uma propriedadeinSampleSize
que irá redimensionar sua imagem enquanto a decodifica, para evitar a necessidade de decodificar para um bitmap temporário. Este valor inteiro usado aqui carregará uma imagem em tamanho 1 / x reduzido. Por exemplo, definirinSampleSize
como 2 retorna uma imagem com metade do tamanho, e definir como 4 retorna uma imagem com 1/4 do tamanho. Basicamente, os tamanhos das imagens serão sempre um pouco menores do que o tamanho da fonte.Do ponto de vista da memória, usar
inSampleSize
é uma operação muito rápida. Efetivamente, ele só decodificará cada X pixel de sua imagem em seu bitmap resultante. Existem dois problemas principaisinSampleSize
:Não fornece resoluções exatas . Ele apenas diminui o tamanho do seu bitmap em uma potência de 2.
Não produz o redimensionamento de melhor qualidade . A maioria dos filtros de redimensionamento produz imagens com boa aparência ao ler blocos de pixels e, em seguida, ponderá-los para produzir o pixel redimensionado em questão.
inSampleSize
evita tudo isso lendo apenas alguns pixels. O resultado é bastante eficiente e com pouca memória, mas a qualidade é prejudicada.Se você está lidando apenas com o encolhimento de sua imagem em algum tamanho pow2, e a filtragem não é um problema, então você não encontrará um método mais eficiente em memória (ou desempenho) do que
inSampleSize
.Sinalizadores inScaled, inDensity, inTargetDensity
Se você precisa redimensionar uma imagem para uma dimensão que não é igual a uma potência de dois, então você vai precisar do
inScaled
,inDensity
einTargetDensity
bandeiras deBitmapOptions
. Quando oinScaled
sinalizador for definido, o sistema derivará o valor de escala a ser aplicado ao seu bitmap, dividindo oinTargetDensity
pelosinDensity
valores.Usar este método redimensionará sua imagem e também aplicará um 'filtro de redimensionamento' a ela, ou seja, o resultado final parecerá melhor porque alguma matemática adicional foi levada em consideração durante a etapa de redimensionamento. Mas esteja avisado: essa etapa de filtro extra, leva mais tempo de processamento e pode aumentar rapidamente para imagens grandes, resultando em redimensionamentos lentos e alocações de memória extra para o próprio filtro.
Geralmente não é uma boa ideia aplicar essa técnica a uma imagem significativamente maior do que o tamanho desejado, devido à sobrecarga de filtragem extra.
Combinação mágica
De uma perspectiva de memória e desempenho, você pode combinar essas opções para obter os melhores resultados. (definindo o
inSampleSize
,inScaled
,inDensity
einTargetDensity
bandeiras)inSampleSize
será aplicado primeiro à imagem, colocando-a na próxima potência de dois MAIOR do que o tamanho de destino. Em seguida,inDensity
&inTargetDensity
são usados para dimensionar o resultado para as dimensões exatas que você deseja, aplicando uma operação de filtro para limpar a imagem.Combinar esses dois é uma operação muito mais rápida, uma vez que a
inSampleSize
etapa reduzirá o número de pixels que a etapa baseada em densidade resultante precisará para aplicar seu filtro de redimensionamento.Se você precisar ajustar uma imagem a dimensões específicas, e alguma filtragem mais agradável, essa técnica é a melhor ponte para obter o tamanho certo, mas feita em uma operação rápida e com pouca memória.
Obtendo as dimensões da imagem
Obtendo o tamanho da imagem sem decodificar a imagem inteira Para redimensionar seu bitmap, você precisará saber as dimensões de entrada. Você pode usar o
inJustDecodeBounds
sinalizador para ajudá-lo a obter as dimensões da imagem, sem a necessidade de realmente decodificar os dados de pixel.Você pode usar este sinalizador para decodificar o tamanho primeiro e, em seguida, calcular os valores adequados para dimensionar a sua resolução alvo.
fonte
destination width
ou seja, ou dstWidth para abreviarPor melhor (e precisa) que essa resposta seja, também é muito complicada. Em vez de reinventar a roda, considere bibliotecas como Glide , Picasso , UIL , Ion ou qualquer outra que implementa essa lógica complexa e sujeita a erros para você.
O próprio Colt ainda recomenda dar uma olhada em Glide e Picasso no vídeo de padrões de desempenho de bitmaps pré-dimensionamento .
Ao usar bibliotecas, você pode obter toda a eficiência mencionada na resposta da Colt, mas com APIs muito mais simples que funcionam de forma consistente em todas as versões do Android.
fonte