Cet article parle du côté ingénierie des taquins image : comment une photo arbitraire devient un N×N jouable. C’est la partie que les joueurs ne voient pas et à laquelle les développeurs consacrent plus de temps qu’ils ne pensent.
Le pipeline
Trois étapes :
1. Recadrage carré
La plupart des photos ne sont pas carrées. HEIC iPhone = 4:3 ; les verticales = 3:4. Plateau = 1:1. Étape un : choisir la région carrée à jouer.
Deux approches :
Centre. Le plus grand carré centré qui rentre. Rapide, prévisible, parfois mal centré.
Interactif. Carré déplaçable. Choix utilisateur. Plus lent, mais l’utilisateur obtient ce qu’il veut.
Slide Puzzle utilise l’interactif, centré par défaut. Le recadrage n’est pas destructif — l’original reste intact dans la photothèque.
2. Réduction à une résolution de travail
Après recadrage, l’image fait souvent 3000×3000 ou plus. Trop pour jouer. Un 6×6 à 320pt sur écran 3× iPhone = chaque tuile à ~53pt = 160 px physiques. 1024×1024 donne ~170 px par tuile — plus net que ce que l’écran affiche.
Réduire à 1024 (parfois 2048) est la norme. Plus grand gaspille mémoire et temps de chargement sans bénéfice visible.
3. Rendu de tuiles à la demande
C’est la partie que la plupart ratent au début. L’image n’est pas vraiment découpée en 16 fichiers. Ce serait lent, gaspilleur, inutile.
L’app peint chaque tuile en dessinant la même image source dans un rectangle de la taille d’une tuile, avec un décalage de la source correspondant à la position de la tuile. En CSS c’est l’astuce background-position ; sur iOS, un clip rectangle de CGImage ; en SwiftUI, Rectangle().clipped() sur une Image.
En pseudo-code, dessiner la tuile en position (row, col) sur un N×N avec image de côté S :
draw image at (-col * S/N, -row * S/N)
within a clip rectangle of (S/N × S/N)
C’est tout. Seize tuiles pour un 4×4 = 16 appels au même renderer avec 16 décalages. L’app n’a jamais besoin de découper le fichier.
C’est aussi pour ça que découper un 6×6 est instantané : 36 appels au même renderer, pas 36 opérations fichier.
Et l’animation ?
Glisser une tuile = une transformation de translation sur la tuile rendue. Le contenu interne ne change pas — seule la position du cadre clippé bouge à l’écran. L’animation est triviale pour le GPU, tourne à 120 Hz sur écrans ProMotion.
Trois détails qui comptent :
- Le clip et le contenu sont frères, pas parent/enfant. Imbriqués, le contenu bouge avec le clip et le glissement casse.
- Transform, pas layout. Animer x/y via CSS transform ou SwiftUI offset est GPU ; animer left/top non.
- Pré-rastériser au premier affichage. Le premier rendu peut être lent (décodage source). Faites un rendu de toutes les tuiles au démarrage pour les chauffer.
Stockage
Où vit vraiment l’image importée ?
Dans une app respectueuse de la vie privée, dans le sandbox de l’app, chiffrée par iOS au repos. La photothèque garde l’original ; l’app garde une copie de travail 1024×1024 dans son dossier Documents. Suppression de l’app = disparition.
Dans une app cloud, la copie vit sur un serveur quelque part. Les implications sont très différentes — vie privée, hors-ligne, devenir si le serveur disparaît. Slide Puzzle est du type sandbox.
Budget mémoire
Un taquin 4×4 image typique :
- Image source : ~3 Mo JPEG.
- Décodée en mémoire : 1024 × 1024 × 4 octets = 4 Mo.
- Une copie par partie active.
Ça passe. En 6×6, source et tampon de décodage sont de la même taille. Le plateau n’a pas besoin de plus.
Là où le budget se tend : les bibliothèques de visuels — 300 × 4 Mo = 1,2 Go si on décode tout. Les apps évitent par décodage à la demande (seulement quand un visuel est montré) et libération en arrière-plan (seule l’image de la partie active reste).
Cas limites
Trois choses qui cassent en pratique :
Photos avec balise EXIF d’orientation. Une photo prise en portrait mais stockée en paysage avec rotation EXIF s’affichera bien dans la photothèque et mal dans le puzzle si l’app oublie la rotation. La première version de toute app de taquin photo a ce bug.
Très grandes images source. Certains HEIC font 6000×8000 px. Les charger à pleine résolution plante sur petits iPhone. Solution : décodage en streaming avec taille cible réduite — ImageIO Apple le supporte. Décoder à 2048×2048 directement depuis le disque ; jamais à pleine taille.
Rendu sous-pixel. Les tailles de tuiles qui ne tombent pas pile sur la grille de pixels produisent une colonne fractionnaire sur un côté. Avec un clipping pur, c’est un fil de vide. Solution : caler les tailles sur des entiers (jitter visible) ou laisser les tuiles se chevaucher de 0,5 px (pas de vide, pas de jitter).
Aucun n’est un problème profond, mais tous sont uniformément oubliés à la première implémentation.
Résumé
Pipeline : recadrer → réduire → clip-et-translation pour rendre les tuiles → translation transforms pour animer. Dans une app respectueuse, l’image ne quitte jamais l’appareil. Tout tient en ~200 lignes, plus 50 pour la rotation EXIF qu’on oublie à chaque fois.
Les taquins image sont plus simples qu’ils n’en ont l’air. L’image reste une image ; les tuiles ne sont que des vues cadrées dessus.