Lanzamiento de una nueva versión de ImageMagick
En Transloadit, nuestro deseo es ofrecerte la mejor experiencia de encoding posible, y uno de los factores principales para lograrlo es asegurarnos de usar las herramientas de encoding más recientes.
No es ningún secreto que para ello dependemos en gran medida de la tecnología de código abierto, por lo que hacemos un esfuerzo consciente por devolver algo a los gigantes sobre cuyos hombros nos apoyamos, por ejemplo proporcionando hardware gratuito al proyecto ImageMagick.
Aunque tenemos una relación muy estrecha con el proyecto, modernizar nuestro stack ha resultado ser muy problemático para nosotros.
Las partes «divertidas» de mantener software de encoding
A menudo, nuestros clientes dependen del comportamiento de la versión antigua (incluso de sus
errores, pero hablaremos de eso más adelante), así que «actualizar sin más», algo que intentamos
ingenuamente hace unos años, rompe su flujo de trabajo. Para garantizar que podemos ofrecer una
experiencia consistente incluso entre
actualizaciones del sistema operativo (English), tenemos que compilar
ImageMagick y sus 31 dependencias. Sí, 31, porque al añadir bibliotecas como pixman,
pango y ufraw, obtenemos compatibilidad con 131 formatos más de los que
ofrecería un apt-get install imagemagick estándar en Ubuntu. Y al incluir bibliotecas hasta llegar a
zlib, también nos protegemos de cualquier ruptura de la compatibilidad con versiones
anteriores que pudiera producirse a raíz de las actualizaciones del sistema operativo, lo que
permite que nuestras capacidades de encoding sean la constante fiable sobre la que nuestros clientes
pueden construir sus negocios.
Además de una amplia compatibilidad con formatos y de esta continuidad garantizada, la ventaja de compilar es que te permite crear un binario estático. Se trata de un único archivo ejecutable que lleva integradas todas sus dependencias. Así resulta fácil ejecutar versiones muy distintas del mismo software una junto a otra sin ningún conflicto, lo que ofrece a nuestros clientes una ruta de actualización opcional. Así es como hemos venido ofreciendo actualizaciones de nuestro software de encoding de video (English).
Sin embargo, el problema que tenemos ahora al actualizar nuestro stack de imágenes es que el proceso de compilar ImageMagick y sus 31 dependencias tardaría dos horas, y eso en condiciones perfectas sobre un servidor potente. Si la versión de una biblioteca concreta entraba en conflicto con otra, quizá solo lo descubrías después de 90 minutos, tras lo cual tenías que pensar en una posible solución y volver a empezar. Eso difícilmente puede considerarse viable o mantenible, y desde luego no está a la altura del nivel de agilidad al que aspiramos en nuestro desarrollo.
Pensábamos que la llegada de los contenedores de Docker resolvería
parte de esta lucha, ya que al menos podríamos ejecutar distintas versiones en paralelo con mayor
facilidad y acelerar nuestros tiempos de compilación mediante el uso de capas. Además, en lugar de
tener binarios enormes que recompilar, podríamos recompilar únicamente una biblioteca compartida
cuando hiciera falta. Nuestra alegría inicial se moderó cuando descubrimos que no había forma de
iniciar contenedores sin privilegios de sistema peligrosos. Esto significaba que teníamos que
mantener los contenedores en ejecución y dejar que nuestro usuario de API sin privilegios les
enviara comandos a través de un proxy, o bien aceptar darle a ese usuario el privilegio de iniciar
contenedores. Ninguna de las dos opciones era buena, por decir lo menos. El proxy sería bastante
frágil y, al repartir semejantes privilegios, en teoría un atacante podría montar cualquier
directorio de la máquina anfitriona como un --volume dentro del contenedor y luego leer, escribir
o eliminar lo que quisiera. Más adelante también supimos del trabajo que se estaba realizando en el
proyecto runC para lanzar
contenedores sin acceso root (y probablemente
adoptaremos ese enfoque en el futuro), pero todavía no tenía el nivel de madurez que necesitábamos.
También consideramos ejecutar un clúster separado para la nueva versión, pero eso tendría costos importantes en términos de hardware adicional, mantenimiento y sobrecarga operativa.
Profundizamos en todas estas direcciones, pero cada vez acabábamos teniendo que dar marcha atrás porque presentaban inconvenientes serios. ¿Cómo debería ser el futuro del mantenimiento de nuestro stack? ¿Qué solución es a la vez lo bastante fiable y madura para darnos continuidad garantizada sin perder nuestra agilidad?
Nix
Durante mucho tiempo también habíamos estado considerando Nix como posible solución. Hemos construido nuestro software con un sencillo framework de compilación llamado depmake, que Felix escribió en 2012 y que ya implementaba algunas de las ideas y cualidades básicas de Nix. Nos ha funcionado muy bien todos estos años, pero Nix lleva esos conceptos, y otros adicionales, mucho más lejos.
Nix es un lenguaje de programación funcional que te permite definir cómo se construye el software de forma declarativa. Al usar una caché global, solo tienes que compilar aquello que es exclusivo de tu entorno, pero en muchos casos puedes descargar el software resultante directamente desde esa caché. Esto puede mejorar enormemente los tiempos de compilación por el simple hecho de que no hay nada que compilar *agita la mano* 👋
Tras bastante aprendizaje y experimentación, descubrimos cómo podían coexistir Nix y depmake, de modo que podemos empezar a aprovechar lo nuevo sin tirar lo viejo.
Futuro
Usar Nix abre de par en par las puertas a mucho software nuevo y nos va a facilitar enormemente desplegar un nuevo stack, personalizarlo, garantizar que siga funcionando siempre de la misma manera y permitir que varias versiones del mismo software convivan una junto a otra.
El grueso del trabajo de ofrecer un nuevo stack a nuestros clientes ha pasado de compilarlo a probarlo, lo que supone una gran mejora.
Todavía estamos considerando envolver nuestro software compilado con Nix en contenedores y ejecutarlos sin root, pero esto sería sobre todo para aportar seguridad adicional además del aislamiento que ya proporcionamos.
¿Declararemos obsoletas alguna vez nuestras versiones antiguas? Sí. Pero lo haremos con periodos de gracia bastante largos, durante los cuales iremos orientando con suavidad a la gente hacia lo nuevo.
Sin más preámbulos.. ¡la versión dos!
Estamos muy orgullosos de presentarte nuestro nuevo stack de ImageMagick, apodado v2.0.3 (no es
SemVer: cualquier cambio puede romper la compatibilidad con versiones anteriores). Lleva ya dos
semanas en beta privada y ahora entra en beta pública. Está basado en la última versión de
ImageMagick que ofrece Nix, pero con añadidos personalizados como libraw para que nuestros
clientes puedan seguir disfrutando de esos 131 formatos adicionales.
Además de RAW y de todos los demás formatos que ya admitimos, a los clientes les alegrará saber que el nuevo stack incluye compatibilidad con los formatos WebP y DjVu.
Hemos actualizado nuestra página de formatos compatibles para que puedas descubrir más fácilmente las diferencias entre las versiones de nuestro stack. La captura de pantalla de abajo muestra los controles de comparación en el momento de este lanzamiento.

Para tu comodidad, aquí tienes las listas completas:
Nueva compatibilidad de lectura: 3FR, 3G2, 3GP, CANVAS, CLIP (antes solo escritura), FILE, FTP, HALD, HTTP, IIQ, JNX, MAC, MEF, NRW, PES, RAW, RMF, RW2, SCREENSHOT, WMF, WMZ.
Nueva compatibilidad de escritura: BRF, DDS (antes solo lectura), INLINE (antes solo lectura), ISOBRL, ISOBRL6, JSON, SPARSE, UBRL, UBRL6
Nueva compatibilidad de lectura/escritura: AAI, BGRA, BGRO, CAL, CALS, DXT1, DXT5, G4, GROUP4, HDR, JPE, JPS, MASK, MKV, PNG00, PNG48, PNG64, PSB, RGF, SIX, SIXEL, TIFF64, VIPS, WEBP
Además de más formatos, nuestra nueva versión de ImageMagick también habilita HDRI (High Dynamic Range Imaging) de forma predeterminada, así que puedes esperar resultados de procesamiento de imágenes más precisos.
Aparte de estas mejoras de cara al cliente, estamos muy contentos de que la nueva versión también tenga algunos beneficios operativos para nosotros, como el cierre de varias vulnerabilidades de seguridad (English), fugas de memoria, etc.
Cambios incompatibles destacados
Nuestra versión anterior de ImageMagick tenía un error en el manejo del espacio de color, lo que daba lugar a imágenes más oscuras y a que se informara incorrectamente de sRGB frente a RGB. Esto ya está corregido, pero si encontraste una manera de sortear ese error, en esencia dependes de él y por lo tanto tendrás que deshacer esa compensación cuando decidas actualizar.
Ya no admitimos la lectura de archivos PGX y JPX y, para los formatos: BRG, GBR, GRB y RBG, ahora se recomienda crear un formato RGB e intercambiar los canales después.
Como referencia, encontrarás el registro de cambios completo al final de esta publicación.
Actualización
Esta sección describe la versión de 2017. El stack
v2.0.3quedó obsoleto en 2019 (English). Para una integración nueva, usa el stack recomendado en la documentación de /image/resize actual.
Transloadit recomienda empezar a usar el nuevo stack de ImageMagick para pruebas y cargas de trabajo
no críticas. Puedes hacerlo añadiendo imagemagick_stack: "v2.0.3" en tu Step del Robot
/image/resize.
Pedimos disculpas por haber tardado tanto en entregar esta actualización. ¡Puedes esperar actualizaciones mucho más puntuales gracias a nuestra nueva forma de gestionar los stacks! 😄
¡Nos encantaría conocer tus conclusiones, así que cuéntanos qué te parece a través de la conocida burbuja de chat de nuestro sitio!
Registro de cambios
Como referencia, este es el registro de cambios completo:
- Se añadieron comprobaciones adicionales al lector DCM para evitar fallos provocados por los datos (informe de error de Hanno Böck)
- Se añadió el
ClampToQuantumfinal en el bucle del mapa de colores sigmoidal - Se añadió compatibilidad con idiomas que requieren un diseño de texto complejo (referencia)
- Se añadió un clon tanh/atanh del mapa sigmoidal heredado (más rápido y más preciso)
- Se añadió la definición
psd:additional-infopara conservar la información adicional de un archivo PSD - Se añadió la definición
psd:preserve-opacity-maskpara conservar la máscara de opacidad de un archivo PSD - Se añadió la compresión RLE de capas al codificador PSD
- Se añadió la compresión RLE de capas al codificador PSD
- Se añadió compatibilidad con la compresión GROUP4 al coder FAX
- Se añadió compatibilidad con RGB555, RGB565, ARGB4444 y ARGB1555 al codificador BMP (referencia)
- Se permitió el uso de set y de escapes cuando no hay imágenes en memoria (salvo que intentes
acceder a metadatos por imagen). Actualmente no incluye
%[fx:...]ni%[pixel:...] - Se aplicaron los parches de Debian (referencia)
- Se redujo el épsilon de precisión finita (referencia)
- Se incrementó la versión del objeto compartido (shared object, SO) de Magick++. Anteriormente, un
reemplazo global cambiaba
matteColorporalphaColor - Se pueden volver a leer los metadatos EXIF geográficos (referencia)
- Se comprueba el desbordamiento de búfer en
magick/draw.c/DrawStrokePolygon() - El recorrido de rutas del coder no está autorizado (informe de error proporcionado por Masaaki Chida)
- coders/png.c: se añadió compatibilidad con un nuevo chunk PNG propuesto (exIf de lectura y escritura, eXIf de solo lectura) que actualmente se está debatiendo en la lista de correo png-mng-misc at lists.sourceforge.net
- coders/png.c: se añadió compatibilidad con un nuevo chunk PNG propuesto (zxIf, de solo lectura)
que actualmente se está debatiendo en la lista de correo png-mng-misc at lists.sourceforge.net.
Habilita exIf y zxIf con
CPPFLAGS="-DexIf_SUPPORTED -DxzIf_SUPPORTED"Si exIf está habilitado, solo se escribirá el chunk exIF sin comprimir y no se escribirá el chunk zTXt codificado en hexadecimal que contiene el perfil Exif sin procesar. - Se corrigió la inestabilidad numérica (referencia)
- Corrección del operador composite
Over(referencia) - Se define la máscara
CompositeChannelsparaRed,Green,Blue,AlphayBlack - Se deniegan las lecturas indirectas por política; elimina la política para permitirlas, p. ej.,
convert caption:@mytext.txt - Distort ya no convierte una imagen
grayscaleensRGB(referencia) - No se cierra la ruta en las uniones de línea redondeadas (referencia)
- Se documentó el cambio de comportamiento en la política de seguridad (gracias a yoya)
- No se interpretan los argumentos de la opción
-fx(referencia) - No se devuelve un cuadro delimitador de cero para
QueryMultilineFontMetrics()(referencia) - No se establece el fondo en las imágenes en mosaico transparentes (referencia)
- No se establece el atributo de actualización en el canal alfa (email privado sobre la opción
-levels-colors) - No se sincroniza la caché de píxeles en
AcquireAuthenticCacheView()(informe de error de Hanno Böck) - Se eliminó una advertencia del compilador
- Se habilita el canal alfa si el color de fondo no es opaco (referencia)
- Se evalúa la morfología de la caché de píxeles diferida para evitar desbordamientos de búfer (informe de error de Ibrahim M. El-Sayed)
- Se corrigió el error de compilación en
opencl.c(referencia) - Se corrigió un fallo de dibujo con anchos de trazo mayores que 2 (referencia)
- Corrección de posibles vulnerabilidades de seguridad (referencia)
- Se corrigió el error de desfase por uno de
GetNextToken() - Se corrigió una fuga de memoria en el formato MPC
- Se corrigió el stroke-opacity de MVG (referencia)
- Se corrigió una regresión de la caché de píxeles en disco (referencia)
- Se corrigió un posible desbordamiento de búfer al escribir TIFFS comprimidos (informe de vulnerabilidad de Cisco Talos, CVE-2016-8707)
- Se corrigió la redeclaración de i (al principio y dentro de un condicional)
- Se corrigió una pequeña fuga de memoria (parche proporcionado por Андрей Черный)
- Se corrigió el problema de desplazamiento del trazo en
-annotate(referencia) - Se corrigió una fuga de descriptores de archivo en el coder WebP (referencia)
- Se corrigió el escalado incorrecto de ciertas imágenes FITS (referencia)
- Se corrigió el cálculo incorrecto del relleno en el codificador PSD
- Se corrigió el análisis incorrecto con el tramado ordenado (referencia)
- Se corrigió la decodificación RLE incorrecta al leer una imagen DCM que contiene varios segmentos
- Se corrigió la decodificación RLE incorrecta al leer una imagen SGI (referencia)
- Se corrigió un problema por el que se usaba la ventana de visualización en lugar de la ventana de datos al leer archivos EXR (referencia)
- Se corrigió una fuga de memoria al crear excepciones anidadas en Magick++ (referencia)
- Se corrigió la colocación correcta de la anotación de texto para
east/westgravity - Se corrigió la lectura de imágenes DXT1 con canal alfa.
- Si no se encuentra un salto de línea conveniente, se fuerza para caption: (referencia)
- Se implementó un chunk PNG privado caNv (canvas) para recordar las dimensiones y los desplazamientos originales cuando se recorta una imagen. Anteriormente usábamos los chunks oFFs y vpAg con este fin, pero esto tenía posibles conflictos con otras aplicaciones que también usan el chunk oFFs
- Se aumentó la asignación de memoria para los píxeles TIFF (referencia)
- Se inicializa el miembro alpha de draw_info en OpaqueAlpha
- Se inicializa el canal de índice para obtener los resultados esperados del coder stegano
- Se iteran los canales sobre la imagen de origen en lugar de sobre la de destino (informe de error de Hanno Böck)
- El composite de máscara produce resultados correctos para la utilidad
convert(referencia) - Las imágenes monocromas ya no tienen los colores invertidos (referencia)
- Se tiene en cuenta el canal alfa al combinar 4 o más imágenes (referencia)
- Error de desfase por 1 al calcular la desviación estándar (referencia)
- Asignación de memoria con desfase por uno (referencia)
- Parche para que la opción
-kuwaharapueda conservar los bordes con mapa de colores - Se permiten imágenes EPT con solo una imagen TIFF o EPS, no ambas (referencia)
- Se evita el desbordamiento de búfer (informe de error de Max Thrane)
- Se evitan desbordamientos de búfer y otros problemas en los coders SIXEL, PDB, MAP, TIFF y CALS (informe de error de Donghai Zhu)
- Se evita el desbordamiento de búfer en los coders BMP y SGI (informe de error de pwchen&rayzhong de tencent)
- Se evita el desbordamiento de búfer al transmitir una imagen (referencia)
- Se evita un fallo en el intérprete MSL
- Se evita el uso de memoria después de liberarla (referencia)
- Se evita un posible desbordamiento de búfer al leer imágenes TIFF (informe de error de Shi Pu del MS509 Team)
- Se evita una posible vulnerabilidad de inyección de comandos de shell a través del parámetro authenticate de los coders PDF, PCL y XPS (informe de Erez Turjeman)
- Se evitan datos de píxeles aleatorios en imágenes JPEG corruptas (informe de error de Hirokazu Moriguchi, Sony)
- Se evita la eliminación espuria de archivos de caché MPC (referencia)
- Se procesan los canales de forma independiente para
-channel-equalize(referencia) - Se ajusta automáticamente el caption de forma correcta (referencia)
- Se centra correctamente la etiqueta de texto (referencia)
- Se inicializan correctamente los bloques PES (referencia)
- Se entrecomillan las contraseñas al pasarlas a un programa delegado.
- En lugar de replicar
optionsenartifacts, se crea un enlace desde la imagen aimage_infoy se busca una opción global si no hay ningún artefacto definido. - Se reconocen las etiquetas de cierre de política XML (referencia)
- Se eliminó el delegado https
- Se eliminaron las llamadas a OpenMP de los bucles de actualización del mapa de colores
- Se eliminó la compatibilidad con el coder ephemeral interno
- Se renombró la función
read_vpag_chunk_callback()apng_user_chunk_callback()encoders/png.c - Se renderiza en la máscara de recorte en lugar de en la imagen para la primitiva gráfica MVG clip-path
- Se reemplaza el título del delegado show por el nombre de archivo de la imagen en lugar de por la etiqueta
- Se reemplazó
CoderSeekableStreamFlagporCoderDecoderSeekableStreamFlagyCoderEncoderSeekableStreamFlag - Se respeta la definición
connected-components:area-threshold(referencia) - Se respeta la opción
gravity(referencia) - Se restauró la opción
-mattecolor - Se devuelve el desplazamiento correcto para índices negativos en la opción
-fx(referencia) - Se devuelve la desviación estándar insesgada en las estadísticas de imagen (referencia)
- Se revirtió el parche que no establecía el atributo de actualización en el canal alfa
- Comprobación RLE para desplazamientos de píxel menores que 0 (informe de desbordamiento de heap de Craig Young)
- Se sanean todos los caracteres de formato incrustados en los delegados
- Se sanean los comentarios que incluyen llaves en el formato de imagen MIFF (referencia)
- Se sanea el nombre de archivo de entrada para los delegados http / https (parche mejorado)
- Las mejoras de seguridad del coder TEXT lo rompieron (referencia)
- Se establece el miembro alpha de la estructura draw en
OpaqueAlpha(referencia) - Se establece el espacio de color en
sRGBsi-appendtiene espacios de color no homogéneos (referencia) - sigmoidal-contrast: cálculo directo, sin LUT
- sigmoidal-contrast: se eliminó el
ClampToQuantuminicial innecesario - Compatibilidad con la opción
-region(referencia) - Compatibilidad con la opción
--enable-indirect-readsdel script configure para habilitar lecturas indirectas (@) en los nombres de archivo - Compatibilidad con
--enable-pipes optiondel script configure para habilitar tuberías (|) en los nombres de archivo - Compatibilidad con las políticas de seguridad pixel-cache y shred
- Compatibilidad con las máscaras de lectura para la opción
-modulate - Compatibilidad con la opción
-read-maskde compare - Compatibilidad con las opciones
phash:colorspacesyphash:normalize - La opción
-cloneya no pierde memoria - La opción
-extentahora coincide con los resultados de IMv6 (referencia) - La opción
-streamahora incrementa correctamente el puntero de píxel (referencia) - El coder histogram ahora devuelve la extensión correcta
- Para cumplir con el estándar SVG, se usa
stroke-opacitypara los trazos transparentes - Se desactiva el canal alfa en la imagen de diferencia de compare (referencia)
- Se reparó la compilación sin compatibilidad con JPEG (referencia)
- Datos sin inicializar en el formato de imagen MAT (referencia)
- Las pruebas unitarias vuelven a pasar tras un pequeño parche de imagen SUN
- Se usa
CopyMagickString()en lugar deCopyMagickMemory()para las cadenas - La prueba unitaria de validación de MNG vuelve a funcionar
