Estoy creando una aplicación social con uso intensivo de imágenes en la que las imágenes se envían desde el servidor al dispositivo. Cuando el dispositivo tiene resoluciones de pantalla más pequeñas, necesito cambiar el tamaño de los mapas de bits, en el dispositivo, para que coincidan con los tamaños de pantalla previstos.
El problema es que el uso de createScaledBitmap hace que me encuentre con muchos errores de memoria insuficiente después de cambiar el tamaño de una horda de imágenes en miniatura.
¿Cuál es la forma más eficiente de memoria para cambiar el tamaño de mapas de bits en Android?
android
performance
bitmap
out-of-memory
Colt McAnlis
fuente
fuente
Respuestas:
Hay tres formas dominantes de cambiar el tamaño de un mapa de bits en Android que tienen diferentes propiedades de memoria:
API createScaledBitmap
Esta API tomará un mapa de bits existente y creará un NUEVO mapa de bits con las dimensiones exactas que ha seleccionado.
En el lado positivo, puede obtener exactamente el tamaño de imagen que está buscando (independientemente de cómo se vea). Pero la desventaja es que esta API requiere un mapa de bits existente para funcionar . Lo que significa que la imagen tendría que cargarse, decodificarse y crearse un mapa de bits antes de poder crear una nueva versión más pequeña. Esto es ideal en términos de obtener sus dimensiones exactas, pero horrible en términos de sobrecarga de memoria adicional. Como tal, esto es una especie de factor decisivo para la mayoría de los desarrolladores de aplicaciones que tienden a ser conscientes de la memoria.
inSampleSize bandera
BitmapFactory.Options
tiene una propiedadinSampleSize
que cambiará el tamaño de su imagen mientras la decodifica, para evitar la necesidad de decodificar a un mapa de bits temporal. Este valor entero utilizado aquí cargará una imagen con un tamaño reducido 1 / x. Por ejemplo, establecerinSampleSize
en 2 devuelve una imagen que tiene la mitad del tamaño, y establecerlo en 4 devuelve una imagen que es 1/4 del tamaño. Básicamente, los tamaños de imagen siempre serán una potencia de dos más pequeños que el tamaño de la fuente.Desde la perspectiva de la memoria, usar
inSampleSize
es una operación realmente rápida. Efectivamente, solo decodificará cada X píxel de su imagen en su mapa de bits resultante. SininSampleSize
embargo, hay dos problemas principales :No te da resoluciones exactas . Solo reduce el tamaño de su mapa de bits en una potencia de 2.
No produce un cambio de tamaño de la mejor calidad . La mayoría de los filtros de cambio de tamaño producen imágenes atractivas al leer bloques de píxeles y luego ponderarlos para producir el píxel redimensionado en cuestión.
inSampleSize
evita todo esto con solo leer cada pocos píxeles. El resultado es bastante eficiente y con poca memoria, pero la calidad se ve afectada.Si solo está lidiando con reducir su imagen en un tamaño de pow2 y el filtrado no es un problema, entonces no puede encontrar un método más eficiente en memoria (o rendimiento) que
inSampleSize
.Indicadores inScaled, inDensity, inTargetDensity
Si tiene que escalar una imagen a una dimensión que no es igual a una potencia de dos, entonces usted tendrá el
inScaled
,inDensity
yinTargetDensity
banderas deBitmapOptions
. CuandoinScaled
se ha establecido la bandera, el sistema derivará el valor de escala para aplicar a su mapa de bits dividiendo elinTargetDensity
por losinDensity
valores.El uso de este método redimensionará su imagen y también le aplicará un 'filtro de cambio de tamaño', es decir, el resultado final se verá mejor porque se han tenido en cuenta algunas matemáticas adicionales durante el paso de cambio de tamaño. Pero tenga cuidado: ese paso de filtro adicional, requiere tiempo de procesamiento adicional y puede sumarse rápidamente para imágenes grandes, lo que resulta en cambios de tamaño lentos y asignaciones de memoria adicionales para el filtro en sí.
Por lo general, no es una buena idea aplicar esta técnica a una imagen que sea significativamente más grande que el tamaño deseado, debido a la sobrecarga de filtrado adicional.
Combinación mágica
Desde la perspectiva de la memoria y el rendimiento, puede combinar estas opciones para obtener los mejores resultados. (ajuste de la
inSampleSize
,inScaled
,inDensity
yinTargetDensity
banderas)inSampleSize
primero se aplicará a la imagen, llevándola a la siguiente potencia de dos MÁS GRANDE que su tamaño objetivo. Luego,inDensity
yinTargetDensity
se utilizan para escalar el resultado a las dimensiones exactas que desee, aplicando una operación de filtro para limpiar la imagen.Combinar estos dos es una operación mucho más rápida, ya que el
inSampleSize
paso reducirá la cantidad de píxeles que el paso basado en Densidad resultante necesitará para aplicar su filtro de cambio de tamaño.Si necesita ajustar una imagen a dimensiones específicas y un filtrado más agradable, esta técnica es el mejor puente para obtener el tamaño correcto, pero se realiza en una operación rápida y con poca memoria.
Obtener las dimensiones de la imagen
Obtener el tamaño de la imagen sin decodificar la imagen completa Para cambiar el tamaño de su mapa de bits, necesitará conocer las dimensiones entrantes. Puede usar la
inJustDecodeBounds
bandera para obtener las dimensiones de la imagen, sin necesidad de decodificar los datos de píxeles.Puede usar esta bandera para decodificar el tamaño primero y luego calcular los valores adecuados para escalar a su resolución objetivo.
fuente
destination width
o dstWidth para abreviarPor muy agradable (y precisa) que sea esta respuesta, también es muy complicada. En lugar de reinventar la rueda, considere bibliotecas como Glide , Picasso , UIL , Ion o cualquier otra que implemente esta lógica compleja y propensa a errores.
El mismo Colt incluso recomienda echar un vistazo a Glide y Picasso en el video de patrones de rendimiento de mapas de bits de preescalado .
Al usar bibliotecas, puede obtener toda la eficiencia que se menciona en la respuesta de Colt, pero con API mucho más simples que funcionan de manera consistente en todas las versiones de Android.
fuente