[spip-dev] ***UNCHECKED*** Dérapage image_reduire

Le ticket https://core.spip.net/issues/4430 présente le problème :
image_reduire{200,200} produit parfois des images de 201px de haut ce qui semble bien peu de différence
mais fait déraper grave certaines mises en page : https://core.spip.net/attachments/download/1140/screenshot.jpg

Le problème se situe au niveau de image_ratio qui,
par exemple recevant (427x427) pour un ratio de 1:1 renvoie (200 x 201) !

C'est à cause d'un ceil, qui fait déraper de 1.0000000000000000 le résultat de certains calculs
en raison des précédentes conversions binaires <-> décimal.

La solution proposée est simple : remplacer le le ceil par un round :
- le round évite ces ripages aux conséquences si disgracieuses
- l'erreur maximale d'arrondi avec round est de 0.5 alors qu'elle est de 1 avec ceil

Cette correction peut elle être intégrée ?

JLuc

J'ai déposé sur https://contrib.spip.net/tester_image_ratio un programme de test
de _image_ratio avec ceil et avec round.

On passe un argument debut par l’url et on teste toutes les valeurs entre debut et debut+100.
Chacune de ces valeurs $z va être sucessivement la dimension cible de _image_ratio
pour des valeurs d’entrées allant de $z à $z+100.
On teste à la fois la fonction qui utilise ceil ET sa version lorsqu'elle utilise round.

Comme c’est un carré, le résultat doit être un carré aussi.
On compte les erreurs pour ceil et pour round : il y en a plein pour round.
Je n'en ai jamais trouvé pour round.
Ça ne teste que les images carrées mais ça suffit à mon avis.

Exemple : 29 d'erreurs pour la dimension cible 1000x1000 testée pour les dimensions d'entrées de 1000x1000 à 1100x1100

JL

Le ticket image_reduire gère mal les arrondis (#4430) · Tickets · spip / spip · GitLab présente le problème :
image_reduire{200,200} produit parfois des images de 201px de haut ce qui semble bien peu de différence
mais fait déraper grave certaines mises en page : https://core.spip.net/attachments/download/1140/screenshot.jpg

Le problème se situe au niveau de image_ratio qui,
par exemple recevant (427x427) pour un ratio de 1:1 renvoie (200 x 201) !

C'est à cause d'un ceil, qui fait déraper de 1.0000000000000000 le résultat de certains calculs
en raison des précédentes conversions binaires <-> décimal.

La solution proposée est simple : remplacer le le ceil par un round :
- le round évite ces ripages aux conséquences si disgracieuses
- l'erreur maximale d'arrondi avec round est de 0.5 alors qu'elle est de 1 avec ceil

Cette correction peut elle être intégrée ?

J'ai déposé sur tester_image_ratio un programme de test
de _image_ratio avec ceil et avec round.

On passe un argument debut par l’url et on teste toutes les valeurs entre debut et debut+100.
Chacune de ces valeurs $z va être sucessivement la dimension cible de _image_ratio
pour des valeurs d’entrées allant de $z à $z+100.
On teste à la fois la fonction qui utilise ceil ET sa version lorsqu'elle utilise round.

Comme c’est un carré, le résultat doit être un carré aussi.
On compte les erreurs pour ceil et pour round : il y en a plein pour round.
Je n'en ai jamais trouvé pour round.

Ceux qui ont lu auront corrigé : c'est pour "ceil" (= le code actuel) qu'il y a plein d'erreur ;
et je n'en ai jamais trouvé pour round (= le fix).

C’est vraiment une histoire d’arrondis car je viens de tester avec un patch de la forme
ceil($destWidth-0.01)
ceil($destHeight-0.01)

et ça corrige aussi toutes les erreurs.
Maintenant j’hésites à mettre un round.
Il faudrait retourner dans l’historique de ce morceau de code, mais il faudrait creuser profond j’ai peur :frowning:

Je pense que l’idée c’est de pas créer une image trop petite quand on va appliquer les rescale derrière : il vaut mieux avoir 1 px de trop que 1px de moins
Maintenant si je fais une biblio rapide sur internet des pratiques, je trouve de tout : du ceil, du rien du tout (c’est osé), et du round.

Bref, soit on fix a minima avec le ceil corrigé pour les erreurs d’arrondi, soit on est fou et on prend le risque d’un round.

Cela dit probablement que l’enjeu est mineur entre les 2 car on manipule des images de plus en plus grandes, et un écart d’1px représente de moins en moins d’erreur relativement à la taille de l’image…

Si quelqu’un a un avis, qu’il parle maintenant ou se taise à jamais ! :stuck_out_tongue:

C’est vraiment une histoire d’arrondis car je viens de tester avec un patch de la forme
ceil($destWidth-0.01)
ceil($destHeight-0.01)
et ça corrige aussi toutes les erreurs.

Comme tu ne précises pas,je suppose que tu as testé avec scripts ou en tout cas dans le cas "carré" ?

J'avais écarté cette manière de corriger avec -0.01 car certes ça règle le pb dans les
valeurs testées actuellement, mais je m'étais dit que ça décalait l'erreur en fait :
au lieu que l'arrondi dérape sur les valeurs entières
c'est pour les valeurs "entières+0.01" que l'erreur d'arrondi sera fatal.
Faudrait vérifier.

Maintenant j’hésites à mettre un round.
Je pense que l’idée c’est de pas créer une image trop petite quand on va appliquer les rescale derrière : il vaut mieux avoir 1 px de trop que 1px de moins
Maintenant si je fais une biblio rapide sur internet des pratiques, je trouve de tout : du ceil, du rien du tout (c’est osé), et du round.
Bref, soit on fix a minima avec le ceil corrigé pour les erreurs d’arrondi, soit on est fou et on prend le risque d’un round.

Cela dit probablement que l’enjeu est mineur entre les 2 car on manipule des images de plus en plus grandes, et un écart d’1px représente de moins en moins d’erreur relativement à la taille de l’image…

1px ça peut sembler chipotage
mais dans les suites d'images de dimension identiques comme un portfolio,
si une image a 1px de plus en hauteur, elle ne tient pas dans la ligne
et ça décale tout le reste.
Dans l'exemple donné https://core.spip.net/attachments/download/1140/screenshot.jpg
c'est du bootstrap de base.

D'ailleurs je suis étonné que ces irrégularités soit pas plus souvent signalées.

JL

Y'aurait pas un lien avec https://core.spip.net/issues/4382 ?

Oui ok, mais si on va par là, quelle que soit la méthode utilisée, les erreurs d’arrondis sur les décimales sont susceptibles de se produire, même avec un round : il suffit que ta division tombe autour de 0.5 pour que ça bascule en + ou - 1 px, donc aucune méthode ne te garantira que toutes tes images sont strictement de la même hauteur (ou alors tu devrais repasser un image_recadre par dessus à la fin au cas où)

Cela dit :
1/ je suis quand même tenté de passer à round parce que en effet c’est ce quand même ce qui produire la moins d’ecart entre la dimension float calculée et la dimension int finale

2/ ton problème est purement un soucis de CSS, qui n’a rien à voir avec les images : il faut ajouter un clear sur la 1ère image de chaque ligne, soit via le html, soit via la css si tu veux que ta mise en forme soit robuste, sinon ton problème est susceptible de se reproduire dans tous les cas...

Cela dit :
1/ je suis quand même tenté de passer à round parce que en effet c’est ce quand même ce qui produire la moins d’ecart entre la dimension float calculée et la dimension int finale

2 fois moins exactement dans le pire des cas.

2/ ton problème est purement un soucis de CSS, qui n’a rien à voir avec les images : il faut ajouter un clear sur la 1ère image de chaque ligne, soit via le html, soit via la css si tu veux que ta mise en forme soit robuste, sinon ton problème est susceptible de se reproduire dans tous les cas...

Ce que tu décris me semble très rustique, car pas responsive.
Mais peut être est-ce que je ne comprend pas.

JL

Ce que tu décris me semble très rustique, car pas responsive.
Mais peut être est-ce que je ne comprend pas.

Ça n’a rien à voir avec le responsive ou non.

Tu as 2 techniques pour ce genre d’affichage, mais dans tous les cas tu ne peux pas décemment te reposer sur le fait que tous tes div ont pile la même hauteur, car ce n’est pas robuste

* la plus ancienne, à base de float
et si tu veux qu’elle soit un peu robuste tu es obligé de mettre un clear sur le premier élément de chaque ligne
En responsive, cela peut se faire de plusieurs façons : avec des classes qui font varier le comportement selon la largeur de l’écran, en insérant un élément clear qui idem sera visible ou pas selon la largeur, ou purement en css avec des :nth(2n+1) :nth(3n+1) :nth(4n+1) … dans des media queries, selon la largeur de ta page

* une technique plus récente repose sur flexbox
dans ce cas tu n’as pas à gérer de clear ou quoi que ce soit, et même si une box est plus haute de 1px cela dérapera pas

Cédric