Lancement d’une nouvelle version d’ImageMagick
Chez Transloadit, nous souhaitons vous offrir la meilleure expérience d’encodage possible, et l’un des principaux facteurs pour y parvenir est de nous assurer d’utiliser les outils d’encodage les plus récents.
Ce n’est un secret pour personne : nous nous appuyons fortement sur des technologies open source pour cela, et nous faisons donc un effort conscient pour rendre la pareille aux géants sur les épaules desquels nous nous tenons, par exemple en fournissant gratuitement du matériel au projet ImageMagick.
Même si nous entretenons une relation étroite avec le projet, moderniser notre pile logicielle s’est révélé très problématique pour nous.
Les « joies » de la maintenance d’un logiciel d’encodage
Souvent, nos clients dépendent du comportement de l’ancienne version (y compris de ses bugs, mais
nous y reviendrons), si bien que « se contenter de mettre à niveau », comme nous avons naïvement
tenté de le faire il y a quelques années, casse leur flux de travail.
Pour garantir une expérience cohérente même entre les
mises à niveau du système d’exploitation, nous devons compiler
ImageMagick et ses 31 dépendances. Oui, 31, car en ajoutant des bibliothèques telles que pixman,
pango et ufraw, nous prenons en charge 131 formats de plus que ce que
fournirait un apt-get install imagemagick standard sous Ubuntu. Et en incluant les bibliothèques
jusqu’à zlib, nous nous protégeons aussi de toute rupture de rétrocompatibilité pouvant résulter
des mises à niveau du système d’exploitation, ce qui permet à nos capacités d’encodage d’être la
constante fiable sur laquelle nos clients peuvent bâtir leur activité.
Outre la vaste prise en charge de formats et cette continuité garantie, l’avantage de la compilation est qu’elle permet de créer un binaire statique. Il s’agit d’un fichier exécutable unique dans lequel toutes les dépendances sont intégrées. Il devient ainsi facile d’exécuter des versions très différentes d’un même logiciel côte à côte sans aucun conflit, ce qui offre à nos clients un chemin de mise à niveau sur option (opt-in). C’est ainsi que nous avons fourni les mises à niveau de notre logiciel d’encodage vidéo.
Cependant, le problème que nous rencontrons aujourd’hui pour mettre à niveau notre pile d’images, c’est que la compilation d’ImageMagick et de ses 31 dépendances prendrait deux heures, et ce dans des conditions idéales sur un serveur costaud. Si la version d’une bibliothèque entrait en conflit avec une autre, vous ne pourriez vous en rendre compte qu’au bout de 90 minutes, après quoi il faudrait imaginer un correctif possible, puis tout recommencer. On peut difficilement qualifier cela de viable ou de maintenable, et cela n’atteint certainement pas le niveau d’agilité auquel nous aspirons dans notre développement.
Nous pensions que l’arrivée des conteneurs Docker résoudrait une partie
de ces difficultés, puisque nous pourrions au moins exécuter plus facilement différentes versions
côte à côte et accélérer nos temps de compilation grâce aux couches. De plus, au lieu de recompiler
d’énormes binaires uniques, nous pourrions simplement recompiler une seule bibliothèque partagée si
nécessaire. Notre enthousiasme initial est retombé quand nous avons appris qu’il était impossible de
démarrer des conteneurs sans privilèges système dangereux. Cela signifiait que nous devions soit
garder des conteneurs en fonctionnement et laisser notre utilisateur d’API non privilégié leur
relayer les commandes, soit accepter de donner à cet utilisateur le privilège de démarrer des
conteneurs. Aucune des deux options n’était idéale, c’est le moins qu’on puisse dire. Le relais
serait assez fragile, et si nous accordions de tels privilèges, un attaquant pourrait en théorie
monter n’importe quel répertoire de la machine hôte comme --volume
dans le conteneur, puis lire, écrire ou supprimer tout ce qu’il voudrait. Nous avons ensuite appris
l’existence des travaux menés dans le projet runC pour lancer des
conteneurs sans accès root (et nous adopterons probablement
cette approche à l’avenir), mais ils n’avaient pas encore atteint le niveau de maturité dont nous
avions besoin.
Nous avons aussi envisagé de faire fonctionner un cluster séparé pour la nouvelle version, mais cela aurait entraîné des coûts importants en matériel supplémentaire, en maintenance et en charge opérationnelle.
Nous avons exploré en profondeur toutes ces pistes, mais avons fini par y renoncer à chaque fois en raison d’inconvénients sérieux. À quoi devrait ressembler l’avenir de la maintenance de notre pile ? Quelle solution est à la fois assez fiable et assez mature pour nous garantir la continuité tout en préservant notre agilité ?
Nix
Depuis longtemps, nous envisagions aussi Nix comme solution possible. Nous compilons nos logiciels avec un framework de compilation simple appelé depmake, que Felix a écrit en 2012 et qui mettait déjà en œuvre certaines des idées et qualités fondamentales de Nix. Il nous a très bien servi toutes ces années, mais Nix pousse ces concepts, et d’autres encore, beaucoup plus loin.
Nix est un langage de programmation fonctionnel qui permet de définir de manière déclarative comment compiler un logiciel. Grâce à un cache global, vous n’avez à compiler que ce qui est propre à votre environnement, et dans de nombreux cas, vous pouvez simplement récupérer le logiciel résultant directement depuis ce cache. Cela peut considérablement réduire les temps de compilation, pour la simple raison qu’il n’y a rien à compiler *geste de la main* 👋
Après pas mal d’apprentissage et d’expérimentation, nous avons trouvé comment faire coexister Nix et depmake, afin de pouvoir commencer à profiter du nouveau sans jeter l’ancien.
Avenir
Utiliser Nix ouvre véritablement les vannes à de nouveaux logiciels potentiels et va nous permettre de déployer très facilement une nouvelle pile, de la personnaliser, de garantir qu’elle continue de fonctionner de la même manière pour toujours, et de faire coexister plusieurs versions d’un même logiciel.
L’essentiel du travail nécessaire pour fournir une nouvelle pile à nos clients est désormais passé de la compilation aux tests, ce qui constitue une grande amélioration.
Nous envisageons toujours d’encapsuler nos logiciels compilés avec Nix dans des conteneurs et de les exécuter sans root, mais ce serait avant tout pour apporter une sécurité supplémentaire en plus de l’isolation que nous fournissons déjà.
Allons-nous un jour déprécier nos anciennes versions ? Oui. Mais nous le ferons avec des périodes de grâce assez longues, pendant lesquelles nous inciterons doucement les utilisateurs à passer à la nouvelle.
Sans plus tarder… voici la version deux !
Nous sommes très fiers de vous présenter notre nouvelle pile ImageMagick, surnommée v2.0.3 (pas de
SemVer, toute modification peut potentiellement casser la rétrocompatibilité). Elle est en bêta
privée depuis déjà deux semaines et passe maintenant en bêta publique. Elle repose sur la dernière
version d’ImageMagick fournie par Nix, avec des ajouts personnalisés tels que libraw pour que nos
clients puissent continuer à profiter de ces 131 formats supplémentaires.
Outre RAW et tous les autres formats que nous prenons déjà en charge, nos clients seront heureux d’apprendre que la nouvelle pile prend en charge les formats WebP et DjVu.
Nous avons mis à jour notre page des formats pris en charge pour que vous puissiez découvrir plus facilement les différences entre les versions de nos piles. La capture d’écran ci-dessous montre les commandes de comparaison au moment de cette publication.

Pour plus de commodité, voici les listes complètes :
Lecture désormais prise en charge : 3FR, 3G2, 3GP, CANVAS, CLIP (auparavant en écriture seule), FILE, FTP, HALD, HTTP, IIQ, JNX, MAC, MEF, NRW, PES, RAW, RMF, RW2, SCREENSHOT, WMF, WMZ.
Écriture désormais prise en charge : BRF, DDS (auparavant en lecture seule), INLINE (auparavant en lecture seule), ISOBRL, ISOBRL6, JSON, SPARSE, UBRL, UBRL6
Lecture et écriture désormais prises en charge : AAI, BGRA, BGRO, CAL, CALS, DXT1, DXT5, G4, GROUP4, HDR, JPE, JPS, MASK, MKV, PNG00, PNG48, PNG64, PSB, RGF, SIX, SIXEL, TIFF64, VIPS, WEBP
Outre davantage de formats, notre nouvelle version d’ImageMagick active aussi par défaut le HDRI (High Dynamic Range Imaging) : vous pouvez donc vous attendre à des résultats de traitement d’images plus précis.
Au-delà de ces améliorations visibles pour nos clients, nous sommes très satisfaits que la nouvelle version nous apporte aussi des avantages opérationnels, comme la correction d’un certain nombre de vulnérabilités de sécurité, de fuites de mémoire, etc.
Changements incompatibles notables
Notre version précédente d’ImageMagick comportait un bug dans la gestion des espaces colorimétriques, qui produisait des images plus sombres et un signalement incorrect de sRGB par rapport à RGB. Ce problème est maintenant corrigé, mais si vous aviez trouvé un moyen de contourner ce bug, vous en dépendez de fait et devrez donc annuler cette compensation si vous choisissez de mettre à niveau.
Nous ne prenons plus en charge la lecture des fichiers PGX et JPX, et pour les formats BRG, GBR, GRB et RBG, il est désormais recommandé de créer un format RGB puis d’intervertir les canaux ensuite.
Pour référence, vous trouverez le journal des modifications complet à la fin de cet article.
Mise à niveau
Cette section décrit la version publiée en 2017. La pile
v2.0.3a été dépréciée en 2019. Pour une nouvelle intégration, utilisez la pile recommandée dans la documentation actuelle de /image/resize.
Transloadit recommande de commencer à utiliser la nouvelle pile ImageMagick pour les tests et les
charges de travail non critiques. Pour cela, ajoutez imagemagick_stack: "v2.0.3" dans votre
Step utilisant le Robot /image/resize.
Toutes nos excuses pour avoir mis autant de temps à livrer cette mise à niveau. Vous pouvez vous attendre à des mises à jour bien plus rapides grâce à notre nouvelle façon de gérer nos piles ! 😄
Nous serions ravis de connaître vos retours, alors n’hésitez pas à nous faire part de votre avis via la bulle de discussion bien connue sur notre site !
Journal des modifications
Pour référence, voici le journal des modifications complet :
- Ajout de vérifications supplémentaires au lecteur DCM pour éviter les erreurs provoquées par les données (signalement de bug par Hanno Böck)
- Ajout d’un
ClampToQuantumfinal dans la boucle de la table de couleurs sigmoïdale - Prise en charge des langues nécessitant une mise en page de texte complexe (référence)
- Ajout d’un clone tanh/atanh de l’ancienne fonction sigmoïdale (plus rapide et plus précis)
- Ajout de la définition
psd:additional-infopour conserver les informations supplémentaires d’un fichier PSD - Ajout de la définition
psd:preserve-opacity-maskpour conserver le masque d’opacité d’un fichier PSD - Ajout de la compression RLE des calques à l’encodeur PSD
- Ajout de la compression RLE des calques à l’encodeur PSD
- Prise en charge de la compression GROUP4 dans le codeur FAX
- Prise en charge de RGB555, RGB565, ARGB4444 et ARGB1555 dans l’encodeur BMP (référence)
- Utilisation de set et des séquences d’échappement autorisée lorsqu’aucune image n’est en mémoire
(sauf si vous tentez d’accéder aux métadonnées propres à chaque image). N’inclut pour l’instant ni
%[fx:...]ni%[pixel:...] - Application des correctifs Debian (référence)
- Recul de l’epsilon de précision finie (référence)
- Incrémentation de la version de l’objet partagé (shared object, SO) de Magick++. Auparavant, un
remplacement global avait changé
matteColorenalphaColor - Les métadonnées EXIF géographiques peuvent à nouveau être lues (référence)
- Vérification du dépassement de tampon dans
magick/draw.c/DrawStrokePolygon() - La traversée de chemin par les codeurs n’est pas autorisée (signalement de bug fourni par Masaaki Chida)
- coders/png.c : ajout de la prise en charge d’un nouveau chunk PNG proposé (exIf en lecture-écriture, eXIf en lecture seule), actuellement en discussion sur la liste de diffusion png-mng-misc at lists.sourceforge.net
- coders/png.c : ajout de la prise en charge d’un nouveau chunk PNG proposé (zxIf, en lecture
seule), actuellement en discussion sur la liste de diffusion png-mng-misc at
lists.sourceforge.net. Activez exIf et zxIf avec
CPPFLAGS="-DexIf_SUPPORTED -DxzIf_SUPPORTED". Si exIf est activé, seul le chunk exIF non compressé sera écrit, et le chunk zTXt encodé en hexadécimal contenant le profil Exif brut ne sera pas écrit. - Correction d’une instabilité numérique (référence)
- Correction de l’opérateur de composition
Over(référence) - Définition du masque
CompositeChannelsàRed,Green,Blue,AlphaetBlack - Lectures indirectes refusées par la politique ; supprimez la politique pour les autoriser, par
exemple
convert caption:@mytext.txt - Distort ne convertit plus une image
grayscaleensRGB(référence) - Ne pas fermer le chemin pour les jointures de ligne arrondies (référence)
- Documentation du changement de comportement de la politique de sécurité (merci à yoya)
- Ne pas interpréter les arguments de l’option
-fx(référence) - Ne pas renvoyer une boîte englobante nulle pour
QueryMultilineFontMetrics()(référence) - Ne pas définir d’arrière-plan pour les images transparentes en mosaïque (référence)
- Ne pas définir le trait de mise à jour sur le canal alpha (e-mail privé concernant l’option
-levels-colors) - Ne pas synchroniser le cache de pixels dans
AcquireAuthenticCacheView()(signalement de bug par Hanno Böck) - Suppression d’un avertissement du compilateur
- Activation du canal alpha si la couleur d’arrière-plan n’est pas opaque (référence)
- Évaluation de la morphologie du cache de pixels paresseux pour éviter un dépassement de tampon (signalement de bug par Ibrahim M. El-Sayed)
- Correction d’une erreur de compilation dans
opencl.c(référence) - Correction d’un défaut de dessin pour les épaisseurs de trait supérieures à 2 (référence)
- Correction de possibles vulnérabilités de sécurité (référence)
- Correction d’une erreur de décalage d’une unité dans
GetNextToken() - Correction d’une fuite de mémoire dans le format MPC
- Correction de stroke-opacity en MVG (référence)
- Correction d’une régression du cache de pixels sur disque (référence)
- Correction d’un possible dépassement de tampon lors de l’écriture de fichiers TIFF compressés (signalement de vulnérabilité par Cisco Talos, CVE-2016-8707)
- Correction de la redéclaration de i (au début et à l’intérieur d’une condition)
- Correction d’une petite fuite de mémoire (correctif fourni par Андрей Черный)
- Correction d’un problème de décalage du trait pour
-annotate(référence) - Correction d’une fuite de descripteurs de fichier (fd) dans le codeur WebP (référence)
- Correction de la mise à l’échelle incorrecte de certaines images FITS (référence)
- Correction d’un calcul de remplissage incorrect dans l’encodeur PSD
- Correction d’une analyse incorrecte avec le tramage ordonné (référence)
- Correction d’un décodage RLE incorrect lors de la lecture d’une image DCM contenant plusieurs segments
- Correction d’un décodage RLE incorrect lors de la lecture d’une image SGI (référence)
- Correction d’un problème où la fenêtre d’affichage était utilisée au lieu de la fenêtre de données lors de la lecture de fichiers EXR (référence)
- Correction d’une fuite de mémoire lors de la création d’exceptions imbriquées dans Magick++ (référence)
- Correction du placement de l’annotation de texte pour
east/westgravity - Correction de la lecture des images DXT1 avec un canal alpha.
- Si aucun saut de ligne approprié n’est trouvé, le forcer pour caption: (référence)
- Implémentation d’un chunk PNG privé caNv (canvas) pour mémoriser les dimensions et décalages d’origine lorsqu’une image est recadrée. Auparavant, nous utilisions les chunks oFFs et vpAg à cette fin, mais cela pouvait entrer en conflit avec d’autres applications qui utilisent aussi le chunk oFFs
- Augmentation de l’allocation mémoire pour les pixels TIFF (référence)
- Initialisation du membre alpha de draw_info à OpaqueAlpha
- Initialisation du canal d’index pour obtenir les résultats attendus du codeur stegano
- Itération des canaux sur l’image source plutôt que sur la destination (signalement de bug par Hanno Böck)
- La composition avec masque produit des résultats corrects pour l’utilitaire
convert(référence) - Les images monochromes n’ont plus de couleurs inversées (référence)
- Prise en compte du canal alpha lors de la combinaison de 4 images ou plus (référence)
- Erreur de décalage de 1 lors du calcul de l’écart type (référence)
- Allocation mémoire décalée d’une unité (référence)
- Correctif permettant à l’option
-kuwaharade préserver les bords en couleurs indexées - Autorisation des images EPT ne contenant qu’une image TIFF ou EPS, et non les deux (référence)
- Prévention d’un dépassement de tampon (signalement de bug par Max Thrane)
- Prévention d’un dépassement de tampon et d’autres problèmes dans les codeurs SIXEL, PDB, MAP, TIFF et CALS (signalement de bug par Donghai Zhu)
- Prévention d’un dépassement de tampon dans les codeurs BMP et SGI (signalement de bug par pwchen&rayzhong de tencent)
- Prévention d’un dépassement de tampon lors de la diffusion en flux d’une image (référence)
- Prévention d’une erreur dans l’interpréteur MSL
- Prévention de l’utilisation de mémoire après libération (référence)
- Prévention d’un possible dépassement de tampon lors de la lecture d’images TIFF (signalement de bug par Shi Pu de la MS509 Team)
- Prévention d’une possible vulnérabilité d’injection de commandes shell via le paramètre authenticate des codeurs PDF, PCL et XPS (signalement d’Erez Turjeman)
- Prévention de données de pixels aléatoires pour une image JPEG corrompue (signalement de bug par Hirokazu Moriguchi, Sony)
- Prévention de la suppression intempestive des fichiers de cache MPC (référence)
- Traitement indépendant des canaux pour
-channel-equalize(référence) - Ajustement automatique correct de caption (référence)
- Centrage correct de l’étiquette de texte (référence)
- Initialisation correcte des blocs PES (référence)
- Mise entre guillemets des mots de passe transmis à un programme délégué.
- Au lieu de répliquer
optionsdansartifacts, création d’un lien de l’image versimage_infoet recherche d’une option globale si aucun artefact n’est défini. - Reconnaissance des balises fermantes des politiques XML (référence)
- Suppression du délégué https
- Suppression des appels OpenMP dans les boucles de mise à jour de la table de couleurs
- Suppression de la prise en charge du codeur interne ephemeral
- Renommage de la fonction
read_vpag_chunk_callback()enpng_user_chunk_callback()danscoders/png.c - Rendu dans le masque de découpe plutôt que dans l’image pour la primitive graphique MVG clip-path
- Remplacement du titre du délégué show par le nom de fichier de l’image plutôt que par l’étiquette
- Remplacement de
CoderSeekableStreamFlagparCoderDecoderSeekableStreamFlagetCoderEncoderSeekableStreamFlag - Respect de la définition
connected-components:area-threshold(référence) - Respect de l’option
gravity(référence) - Rétablissement de l’option
-mattecolor - Renvoi du décalage correct pour un index négatif avec l’option
-fx(référence) - Renvoi de l’écart type non biaisé pour les statistiques d’image (référence)
- Annulation du correctif qui ne définissait pas le trait de mise à jour sur le canal alpha
- Vérification RLE d’un décalage de pixel inférieur à 0 (signalement de dépassement de tas par Craig Young)
- Assainissement de tous les caractères de formatage intégrés aux délégués
- Assainissement des commentaires contenant des accolades pour le format d’image MIFF (référence)
- Assainissement du nom de fichier d’entrée pour les délégués http / https (correctif amélioré)
- Les améliorations de sécurité du codeur TEXT l’avaient cassé (référence)
- Définition du membre alpha de la structure draw à
OpaqueAlpha(référence) - Définition de l’espace colorimétrique à
sRGBsi-appendcomporte des espaces colorimétriques non homogènes (référence) - sigmoidal-contrast : calcul direct, sans LUT
- sigmoidal-contrast : suppression d’un
ClampToQuantuminitial inutile - Prise en charge de l’option
-region(référence) - Prise en charge de l’option
--enable-indirect-readsdu script configure pour activer les lectures indirectes (@) dans les noms de fichiers - Prise en charge de
--enable-pipes optiondans le script configure pour activer les pipes (|) dans les noms de fichiers - Prise en charge des politiques de sécurité pixel-cache et shred
- Prise en charge des masques de lecture pour l’option
-modulate - Prise en charge de l’option
-read-maskde compare - Prise en charge des options
phash:colorspacesetphash:normalize - L’option
-clonene provoque plus de fuite de mémoire - L’option
-extentdonne désormais les mêmes résultats qu’IMv6 (référence) - L’option
-streamincrémente désormais correctement le pointeur de pixel (référence) - Le codeur histogram renvoie désormais l’étendue correcte
- Pour respecter la norme SVG, utilisation de
stroke-opacitypour les traits transparents - Désactivation du canal alpha pour l’image de différence de compare (référence)
- Réparation de la compilation sans prise en charge de JPEG (référence)
- Données non initialisées dans le format d’image MAT (référence)
- Les tests unitaires passent à nouveau après un petit correctif des images SUN
- Utilisation de
CopyMagickString()plutôt que deCopyMagickMemory()pour les chaînes - Le test unitaire de validation pour MNG fonctionne à nouveau
