Tratamiento y conversión de imágenes con ImageMagick (II) Aplicación a densidades de pantalla de Android.

Siguiendo con el tratamiento de imágenes desde la terminal con ImageMagick, vamos a aplicar lo visto en el post anterior al desarrollo de aplicaciones para Android. En concreto, a la necesidad de trabajar con imágenes en diferentes tamaños para las diferentes densidades de pantalla que puede tener un dispositivo Android.

La necesidad: Los diferentes tamaños y densidades de pantalla de un dispositivo Android.

Si no eres desarrollador de aplicaciones Android seguro que nos te has parado a pensar en este aspecto. Pero si lo eres… te habrás dado cuenta de que Android corre en miles de dispositivos diferentes, y que la diversidad de tamaños y densidades de pantalla es absolutamente abrumadora. Si desarrollas una aplicación para Android, está puede acabar corriendo en smartphones, tablets, Android TV, etcétera. Y en cada una de estas categorías te puedes encontrar con pantallas completamente diferentes.

Esto que en realidad es un punto positivo porque tu aplicación puede funcionar en multitud de dispositivos diferentes, también puede ser un dolor de cabeza en el desarrollo de la parte gráfica de tu app. Porque si lo piensas, el resultado puede ser muy diferente en función de donde se esté ejecutando. A nivel de disposición de elementos sobre la pantalla, la solución más recomendable y usada es la de definir los elementos usando medidas y dimensiones relativas, pero ese no es el ámbito de este post. Aquí nos vamos a centrar en las imágenes, que aunque puedes definir sus contenedores de manera relativa, el resultado puede ser deficiente. Por ejemplo, aunque conserves la relación de aspecto, si tienes una imagen de 25x25 píxeles, se mostrará de manera muy diferente en dispositivos con pantallas muy diferentes, y no solo en resolución, sino sobre todo en densidad.

Densidad de pantalla o DPI

Vamos a ver que es esto de la Densidad de pantalla o los DPI. Anteriormente, la medida base para ver como se veía algo un una pantalla era la resolución, es decir, 640x480, 1980x1080… Pero en la época de los smartphones es fácil ver como afecta la densidad de pantalla al escalado de las imágenes. Solo tenemos que pensar en dos dispositivos con el mismo tamaño de pantalla pero con diferentes resoluciones máximas. Es bastante común ver pantallas iguales, por ejemplo 6 pulgadas, pero que una tenga una resolución HD (1280x720) y otro FHD (1920x1080), o similares. Si se define la densidad de pantalla como el número de pixeles por pantalla está bastante claro que las dos pantallas que hemos puesto como ejemplo tendrán diferentes densidades de pantalla. Y a que nos lleva esto, pues a que la misma imagen mostrada en pantallas del mismo tamaño se verá mucho más grande (y puede que pixelada) en la pantalla HD que en la FHD. Si no te queda claro con mi explicación te recomiendo que le eches un ojo a este enlace de la documentación oficial.

La solución oficial de Android: Definir diferentes versiones de la misma imagen para según que DPI.

¿Y que proponen desde Android para lidiar con este problema? En el caso de las imágenes, una de las soluciones es crear 6 conjuntos de gráficos diferentes que serían los siguientes:

  • ldpi: Para densidades de pantalla de aproximadamente 120 dpi. Tamaño de imagen 0.75x.
  • mdpi: Para densidades de pantalla de aproximadamente 160 dpi. Tamaño de imagen de referencia 1x.
  • hdpi: Para densidades de pantalla de aproximadamente 240 dpi. Tamaño de imagen 1.5x.
  • xhdpi: Para densidades de pantalla de aproximadamente 320 dpi. Tamaño de imagen 2x.
  • xxhdpi: Para densidades de pantalla de aproximadamente 480 dpi. Tamaño de imagen 3x.
  • xxxhdpi: Para densidades de pantalla de aproximadamente 640 dpi. Tamaño de imagen 4x.

Por tanto, la proporción de escalamiento de estas 6 densidades principales sería 3:4:6:8:12:16. Pero puedes ver más claramente estas proporciones en la siguiente imagen extraida de la documentación de Android:

Android diferentes dpi

Por ejemplo, si tenemos una imagen de tamaño de referencia (mdpi) de 48x48 píxeles, los diferentes tamaños según dpi deberían ser:

  • 36x36 (0.75x) para ldpi
  • 48x48 (1.0x) para mdpi
  • 72x72 (1.5x) para hdpi
  • 96x96 (2.0x) para xhdpi
  • 144x144 (3.0x) para xxhdpi
  • 192x192 (4.0x) para xxxhdpi

Todas estas imágenes se colocarán en diferentes subdirectorios dentro del directorio res/ de la aplicación. De modo que creando un directorio con drawable- más el nombre de cada densidad de pantalla y dejando dentro la imagen con la resolución correcta, la aplicación cogerá automáticamente la que corresponda en función de la pantalla en que esté funcionando. De este modo el árbol de recursos quedaría tal que así:

|--res
|  |--drawable-ldpi
|  |  |--imagenGuay.png
|  |--drawable-mdpi
|  |  |--imagenGuay.png
|  |--drawable-hdpi
|  |  |--imagenGuay.png
|  |--drawable-xhdpi
|  |  |--imagenGuay.png
|  |--drawable-xxhdpi
|  |  |--imagenGuay.png
|  |--drawable-xxxhdpi
|  |  |--imagenGuay.png

Además, merece la pena destacar que hay un par de densidades especiales que son las siguientes:

  • nodpi: Para gráficos que queremos que se muestren igual en todas las densidades de pantalla.
  • tvdpi: Para pantallas entre mdpi y hdpi de aproximadamente 213 dpi. Tamaño de imagen 1.33x.

Automatizando la creación de las diferentes imágenes para cada DPI con ImageMagick y un script en bash.

Una vez explicada la teoría, ya podemos pasar al objetivo de esta entrada del blog, que no es otro que el de poner a tu disposición un script bash que a partir de un directorio de imágenes base, las transforme y genere 6 imágenes diferentes, que ubicará en la ruta correcta dentro del directorio res/. Incluyendo además una conversión de formato si fuese necesaria. Es decir, vamos aplicar todo lo que os conté de ImageMagick en el capítulo I para automatizar el proceso de creación de imágenes para diferentes densidades de pantalla en el desarrollo de una aplicación de Android.

El script: generar_drawables_dpi.sh

El script que os comparto para automatizar la creación de las diferentes imágenes asociadas a las distintas densidades de pantalla lo he llamado 0018_generar_drawables_dpi.sh y lo tenéis compartido directamente en el repositorio del blog en Gitlab.

Este pequeño script hace exactamente lo que hemos hablado anteriormente:

  • Genera 6 imágenes por cada imagen encontrada en el directorio que llamaremos de origen. Es decir, una imagen para cada una de las densidades de pantalla.
  • Además, convertirá la imagen “original” del formato “original” al que le indiquemos. Por ejemplo, de XCF a PNG.
  • Por último, si así lo deseamos, copiará todas las imágenes generadas al proyecto de nuestra App Android en desarrollo

En cuanto al directorio y las imágenes originales hay que tener en cuenta los siguientes aspectos:

  • Todas las imágenes del directorio deberán ser del mismo formato de “origen”.
  • El tamaño de las imágenes deberá ser el deseado para la densidad xxxhdpi. Es decir, en vez de tomar como referencia mdpi tomaremos la imagen más grande para ir reduciendo al ir generando las sucesivas imágenes.

Usando generar_drawables_dpi.sh

El uso y la llamada del script sería como cualquier script en bash. Es decir ./generar_drawables_dpi.sh o 0018_generar_drawables_dpi.sh si has usado el mismo nombre del fichero en el repositorio. Pero la utilidad tiene varios parámetros obligatorios y otros opcionales que detallo a continuación:

  • -i: Se usa para definir el directorio donde se encuentran las imágenes “originales”. Parámetro obligatorio.
  • -o: Se usará para definir el directorio de salida para las nuevas imágenes. Dentro de este directorio se creará una carpeta para cada dpi. Parámetro obligatorio.
  • -f: Se utilizará para indicar al script el formato de partida de las imágenes dentro del directorio definido con -i. Parámetro obligatorio.
  • -s: Se utiliza para indicar al script el formato de salida de las nuevas imágenes. Parámetro obligatorio.
  • -c: Si se define, lo último que hará la herramienta es copiar todas las imágenes generadas al directorio definido con este parámetro. Debe ser una ruta al directorio res del proyecto de tu aplicación Android en desarrollo. Parámetro opcional.
  • -d: Modo Debug. Añade una salida más detallada. Parámetro opcional.
  • -h: Ayuda. Muestra información de como ejecutar la utilidad.

Ejemplos de uso

A continuación os muestro varios ejemplos de uso de la herramienta junto con la salida de la ejecución. Para estos ejemplos vamos a usar, como imagen dentro del directorio de entrada, una imagen con el logo del blog, con tamaño inicial 192x192, con formato inicial XCF y con formato de salida PNG. Está sería la imágen original:

Android diferentes dpi

Y estas las diferentes ejecuciones del script:

  • Generar imágenes para diferentes densidades en formato PNG desde formato XCF.
./0018_generar_drawables_dpi.sh -i /home/usuario/noroute2host.com/files/0018_imagemagick-android/originales -o /home/usuario/noroute2host.com/files/0018_imagemagick-android/imgs -f xcf -s png

Comienzo de la ejecución.
  Comprobando la instalación de ImageMagick...
  Generando recursos...
Fin de la ejecución.
  • Generar imágenes para diferentes densidades en formato PNG desde formato XCF y copiar a directorio de proyecto.
./0018_generar_drawables_dpi.sh -i /home/usuario/noroute2host.com/files/0018_imagemagick-android/originales -o /home/usuario/noroute2host.com/files/0018_imagemagick-android/imgs -f xcf -s png -c /home/usuario/StudioProjects/miApp/app/src/main/res/

Comienzo de la ejecución.
  Comprobando la instalación de ImageMagick...
  Generando recursos...
  Copiando al proyecto...
Fin de la ejecución.
  • Generar imágenes para diferentes densidades en formato PNG desde formato XCF y copiar a directorio de proyecto incluyendo la opción para debug o depuración.
./0018_generar_drawables_dpi.sh -i /home/usuario/noroute2host.com/files/0018_imagemagick-android/originales -o /home/usuario/noroute2host.com/files/0018_imagemagick-android/imgs -f xcf -s png -c /home/usuario/StudioProjects/miApp/app/src/main/res/

Comienzo de la ejecución.
  Comprobando la instalación de ImageMagick...
DEBUG: Binario ImageMagick: /usr/local/bin/magick
  Generando recursos...
DEBUG: /usr/local/bin/magick mogrify -format png -flatten -path /home/usuario/noroute2host.com/files/0018_imagemagick-android/imgs/xxxhdpi /home/usuario/noroute2host.com/files/0018_imagemagick-android/originales/*.xcf
DEBUG: /usr/local/bin/magick mogrify -resize 75% -format png -flatten -path /home/usuario/noroute2host.com/files/0018_imagemagick-android/imgs/xxhdpi /home/usuario/noroute2host.com/files/0018_imagemagick-android/originales/*.xcf
DEBUG: /usr/local/bin/magick mogrify -resize 50% -format png -flatten -path /home/usuario/noroute2host.com/files/0018_imagemagick-android/imgs/xhdpi /home/usuario/noroute2host.com/files/0018_imagemagick-android/originales/*.xcf
DEBUG: /usr/local/bin/magick mogrify -resize 37.5% -format png -flatten -path /home/usuario/noroute2host.com/files/0018_imagemagick-android/imgs/hdpi /home/usuario/noroute2host.com/files/0018_imagemagick-android/originales/*.xcf
DEBUG: /usr/local/bin/magick mogrify -resize 25% -format png -flatten -path /home/usuario/noroute2host.com/files/0018_imagemagick-android/imgs/mdpi /home/usuario/noroute2host.com/files/0018_imagemagick-android/originales/*.xcf
DEBUG: /usr/local/bin/magick mogrify -resize 18.75% -format png -flatten -path /home/usuario/noroute2host.com/files/0018_imagemagick-android/imgs/ldpi /home/usuario/noroute2host.com/files/0018_imagemagick-android/originales/*.xcf
  Copiando al proyecto...
DEBUG: cp -a /home/usuario/noroute2host.com/files/0018_imagemagick-android/imgs/xxxhdpi/*.png /home/usuario/StudioProjects/miApp/app/src/main/res/drawable-xxxhdpi
DEBUG: cp -a /home/usuario/noroute2host.com/files/0018_imagemagick-android/imgs/xxhdpi/*.png /home/usuario/StudioProjects/miApp/app/src/main/res/drawable-xxhdpi
DEBUG: cp -a /home/usuario/noroute2host.com/files/0018_imagemagick-android/imgs/xhdpi/*.png /home/usuario/StudioProjects/miApp/app/src/main/res/drawable-xhdpi
DEBUG: cp -a /home/usuario/noroute2host.com/files/0018_imagemagick-android/imgs/hdpi/*.png /home/usuario/StudioProjects/miApp/app/src/main/res/drawable-hdpi
DEBUG: cp -a /home/usuario/noroute2host.com/files/0018_imagemagick-android/imgs/mdpi/*.png /home/usuario/StudioProjects/miApp/app/src/main/res/drawable-mdpi
DEBUG: cp -a /home/usuario/noroute2host.com/files/0018_imagemagick-android/imgs/ldpi/*.png /home/usuario/StudioProjects/miApp/app/src/main/res/drawable-ldpi
Fin de la ejecución.

Por último, solo me queda mostrar las imágenes resultantes que se han obtenido a partir de la que hemos usado de base:

ldpi (36x36) mdpi (48x48) hdpi (72x72) xhdpi (96x96) xxhdpi (144x144) xxxhdpi (192x192)
favicon ldpi favicon mdpi favicon hdpi favicon xhdpi favicon xxhdpi favicon xxxhdpi

Código fuente del script

Como ya os he comentado anteriormente este script, así como todos los del blog, lo tenéis en el repositorio Noroute2host Files. Así que mejor que poner por aquí el código, consultarlo y descargarlo directamenre aquí. ¡Y por supuesto es mejorable, modificable, ampliable y todo lo que se os ocurra!

Enlaces de interés:

Artículo anterior Artículo siguiente

Artículos relacionados: