Caché dans le JPEG depuis 30 ans : une image qui se transforme en animation

Caché dans le JPEG depuis 30 ans : une image qui se transforme en animation

JPEGImageHistoire TechniqueDécouverte Amusante

Sources:Lobsters + web research

Une question « lancée au hasard » qui a exhumé un pouvoir caché vieux de 30 ans

Le 17 juillet, le chercheur en sécurité bien connu lcamtuf (Michał Zalewski) a posté sur Mastodon une idée en l’air :

“Le JPEG progressif envoie d’abord les basses fréquences au chargement, puis affine progressivement. Je parie qu’on peut l’utiliser à l’envers — fabriquer un « JPEG dégressif » qui a d’abord belle allure, puis se dégrade peu à peu.”

Cette publication n’a pas déclenché de grande discussion dans les cercles techniques — mais quelqu’un l’a vue, et s’est mis au travail.

Deux jours plus tard, le développeur Maurycy a publié ses résultats sur la communauté technique Lobsters : non seulement il pouvait fabriquer une « image dégressive », mais il pouvait faire jouer un fichier JPEG comme un GIF. Ce fil a atteint △95 points et est devenu le contenu le plus voté de la journée.

Exemple de JPEG dégressif : une image de chat qui, au fil du chargement, passe de « chat sur l'herbe » à « un autre chat sur le béton »

▲ Source : maurycyz.com. Un même fichier JPEG, affichant des chats différents selon l’étape de chargement — le navigateur croit toujours « s’affiner progressivement », alors que le contenu a en réalité été substitué.

La norme JPEG est née en 1992, il y a donc 34 ans. Des appareils photo numériques aux albums de téléphone portable, en passant par les images web, des milliards de JPEG sont consultés, transmis et stockés chaque jour dans le monde. Et c’est le mois dernier seulement que quelqu’un a découvert, dans cette norme, une capacité que personne n’avait jamais exploitée — faire de l’animation.


D’abord flou, puis net — une conception de génie

Pour comprendre cette découverte, il faut d’abord expliquer une conception des années 1990.

Ceux qui surfaient tôt se souviendront : quand le réseau était lent, ouvrir une grande image affichait le cadre ligne par ligne, de haut en bas. On appelle cela le « JPEG de base » (baseline) — les données sont ordonnées séquentiellement, et le navigateur affiche ce qu’il a reçu.

La norme JPEG a ensuite ajouté une option, le « JPEG progressif ». Son fonctionnement est radicalement différent : on envoie d’abord la « silhouette » de l’image, puis on complète progressivement les détails.

Une analogie de la vie courante :

Votre ami, dans une montagne au signal exécrable, veut vous envoyer une photo de groupe. À l’ancienne, vous ne verriez le cliché s’afficher que petit à petit, de haut en bas. Mais en « progressif », votre ami peut d’abord envoyer une vignette floue à 1/16 — vous reconnaissez aussitôt qu’il s’agit d’un groupe. Puis arrivent les données de détail : expressions des visages, texture des vêtements, feuillage en arrière-plan, qui se précisent peu à peu.

Sur le plan technique, cette conception découpe les données de l’image en plusieurs « lots » (appelés scan dans la norme). Chaque lot porte une étiquette indiquant la plage de précision dont il a la charge. Le premier lot ne contient que l’information la plus grossière, les lots suivants complètent les détails de plus haute fréquence.

Le point clé : la norme prescrit que chaque lot de données doit déclarer la plage qu’il couvre, mais elle n’a jamais exigé que le contenu décrit par les lots suivants appartienne à la même image que les précédents.


Comme un bordereau de livraison qui n’écrit pas « doit être le même destinataire »

L’idée d’origine de lcamtuf était le « JPEG dégressif » : le premier lot contient une belle image, et les lots suivants la recouvrent progressivement par quelque chose de laid. Par exemple, une photo de nourriture qui, à mi-chargement, se transforme en aliment moisi.

Mais Maurycy a découvert qu’on pouvait aller plus loin.

Puisque les lots suivants peuvent recouvrir le contenu déjà rendu, pourquoi ne pas tout simplement glisser plusieurs images différentes dans un même fichier ? La méthode est d’une simplicité stupéfiante :

On relie tête-bêche les « segments de données » de plusieurs images JPEG de mêmes dimensions, en retirant les en-têtes de marqueur superflus au milieu ; le navigateur les traitera alors comme les multiples « couches de précision » d’une même image, qu’il rendra tour à tour.

Une analogie :

Vous envoyez à une compagnie de livraison une série de paquets, chacun accompagné d’un bordereau indiquant « adresse de livraison : chez Li, article : photo ». Le livreur les distribue l’un après l’autre, mettant à jour la « photo » chez Li à chaque fois. Mais la seconde photo reçue par Li est totalement différente de la première — le système de livraison ne vérifie que la catégorie « photo », pas si c’est la même photo.

Le navigateur fait de même. Il vérifie que l’étiquette de chaque lot (plage de précision, canaux de couleur) respecte la norme JPEG, mais il ne vérifie jamais « cette image est-elle le même contenu que la précédente ? ». Il recouvre fidèlement l’ancienne image avec les nouvelles données, et ainsi —

ce que vous voyez à l’écran change.


De « l’image substituée » à « la vidéo jouée »

La première version présentait une limite : un JPEG progressif normal compte environ 10 lots (scan). La plupart des décodeurs de navigateur, après avoir reçu environ 10 lots, estiment que « l’image est censée être chargée » et refusent d’en recevoir davantage. Cela signifie qu’on ne peut glisser que huit ou neuf images — bien insuffisant pour une animation.

Maurycy a continué d’optimiser. Il a découvert qu’on pouvait fabriquer un « lot minimal » : chaque image n’utilise qu’une seule analyse DC (ne contenant que les informations de couleur les plus élémentaires, sans détail). Ainsi, une image n’a que 1/16 de la netteté de la version complète, mais cela suffit pour tenir lieu d’image d’animation.

Par cette méthode, Chrome peut rendre environ 90 images avant d’abandonner, et la patience de Firefox est encore plus grande. 90 images, c’est suffisant pour un court extrait vidéo.

« Vidéo » réalisée avec un fichier JPEG : un chat noir s'avançant vers la caméra depuis le lointain

▲ Source : maurycyz.com. Un fichier JPEG statique qui, exploitant la propriété de recouvrement des lots progressifs, joue image par image, au fil du chargement, une animation du chat noir qui marche. C’est un fichier pleinement conforme à la norme JPEG, ouvrable par n’importe quel navigateur.


À quoi ça sert ?

Soyons honnêtes — à pas grand-chose.

Ce hack a un défaut fatal : impossible de contrôler la vitesse de lecture. Car le « framerate » de l’animation dépend entièrement de la vitesse du réseau — téléchargement rapide, lecture rapide ; téléchargement lent, lecture lente. Il ne possède aucun mécanisme de contrôle temporel comme le font les formats GIF ou vidéo.

Et la plupart des logiciels de visionnage d’images s’arrêtent d’afficher une fois l’image « chargée », sans la jouer en boucle. Pour voir l’effet d’animation, il faut charger progressivement dans des conditions de réseau lent, ou recourir à un mode d’affichage spécial.

Mais peu importe.

Ce qui est vraiment intéressant, c’est la découverte en elle-même : une norme internationale écrite il y a 30 ans, lue, implémentée et utilisée par d’innombrables ingénieurs pendant 30 ans, et à laquelle personne n’a remarqué qu’elle pouvait faire de l’animation — jusqu’à ce que quelqu’un pose la question au hasard.


Une norme ignorée, et la victoire de la curiosité technique

Cette histoire, à mes yeux, a un charme particulier.

La norme JPEG — publiée par l’Organisation internationale de normalisation (ISO), des milliers de pages de documentation technique, implémentée par tous les logiciels d’image du globe — elle est là, immobile, depuis 34 ans. Chaque ligne de sa spécification est publique, et n’importe qui peut la lire.

Le mécanisme de « recouvrement par balayage » du JPEG progressif figure dans la norme dès le départ. lcamtuf n’a découvert aucune faille nouvelle ; il s’est simplement posé une question que la norme n’a jamais dit « impossible » : si les données du balayage suivant diffèrent de celles d’avant, que se passe-t-il ?

La norme ne répond pas à cette question — parce qu’il ne lui est jamais venu à l’esprit que quelqu’un ferait cela.

Et Maurycy n’a même pas écrit de programme complexe. Son code tient dans un court fichier C, dont l’essence est de « rassembler les parties centrales de plusieurs images, en retirant les marqueurs de tête et de queue ». La véritable difficulté résidait dans l’idée qu’on pouvait procéder ainsi.

Les cercles techniques consacrent trop de discussions aux « meilleures pratiques », à « l’optimisation des performances », à « la conception d’architecture ». Apparaître de temps à autre une histoire comme celle-là — quelqu’un exhume un vieux document de 30 ans, pointe une ligne et dit « rien n’interdit cela ici » — rappelle l’essence même de l’esprit hacker.


Pour conclure

Si vous souhaitez essayer par vous-même, Maurycy a rendu public sur son blog tout le code et les images d’exemple. Un fichier JPEG de quelques centaines de ko, ouvert dans un navigateur, « prend vie » progressivement au fil du chargement.

Certes, ce n’est pas une percée technologique révolutionnaire. Cela ne remplacera pas le GIF, ne bouleversera pas le paysage des formats d’image. Mais c’est le genre de découverte qui fait sourire — comme trouver sous le plancher d’une vieille maison une porte dérobée que personne n’a jamais ouverte ; derrière la porte, pas de trésor, mais l’instant où on l’ouvre est en soi un plaisir.

Liens de référence :

  • Discussion Lobsters : Regressive JPEGs
  • Question d’origine de lcamtuf sur Mastodon
  • Blog technique de Maurycy : Regressive JPEGs