Resize image : dimensions trop entières !

Bonjour au bar.
Je suis de retour sur la planète, prêt à swifter.
Ma question est un peu débile, j'en suis convaincu, mais y'a quand même un truc qui m'échappe.
Je fais un resize image avec ça (issu de pros sur le web). Je l'ai peut-être modifié, je ne m'en souviens pas :-)

extension UIImage {
func resizedImage(newSize: CGSize) -> UIImage {
guard self.size != newSize else { return self }

    let imageVide = UIImage()

    print("resizedImage newSize : width = \(newSize.width) - height = \(newSize.height)")

    UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0);

    self.draw(in: CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height))

    if let newImage = UIGraphicsGetImageFromCurrentImageContext() {
        UIGraphicsEndImageContext()
        print("resizedImage newImage : width = \(newImage.size.width) - height = \(newImage.size.height)")
        return newImage
    }

    return imageVide
}

}
Et sur les print, on voit que je perd la précision des dimensions
resizedImage newSize : width = 43.6586482553513 - height = 57.7671681750195
resizedImage newImage : width = 44.0 - height = 58.0

Si quelqu'un a une judicieuse idée, je suis preneur...

Réponses

  • La perte de ta "précision" est normale. Je ne sais pas d'où tu tire tes images avec une taille aussi étrange que 43.65655656757. Tu confond peut-être la taille physique réelle de l'image, exprimé en vrais pixels avec sa représentation graphique sur l'écran, en point.

    La taille d'une UImage est forcément un nombre entier, puisqu'elle est composé de pixels PHYSIQUE. On peut avoir une image de 120x100 pixels, pas une image de 120,543x99,654 pixels ! C'est physiquement impossible.

    Ta routine de resize est une manipulation de pixels. On crée en mémoire un espace graphique (le contexte graphique) composé d'un nombre entier de pixels et on dessine dessus.** Les dimensions du context sont forcément un nombre entier de pixels !**

  • Merci Draken, je connais tout ça, t'inquiètes.
    Mais dans swift, j'ai vérifié chez Apple, les width, height, size, frame, bounds, etc, tout ça c'est en CGFloat, avec donc plusieurs chiffres après la virgule, et ça y reste ainsi (en faisant des print) tant que l'on ne fait pas un resize.

    Pourquoi peut-on mettre une largeur de CGFloat(1152,675) alors qu'un pixel ne se coupe pas en deux !!!
    C'est juste ça qui m'excite.

    J'avais prévenu, ma question est débile

    De toute façon je sais que je vais rester avec mes valeurs entières (CGFloat(1153.0).

    Rigolade à part, voici quand même l'origine de ce besoin.

    Je fait un resize sur un "png" ainsi que sur un bezierPath issu du "tracé" récupéré dans le psd d'où est exporté le "png", puis copier coller dans un fichier (de la taille du "png") "ai" (illustrator) en vectoriel, puis importé dans Paintcode d'où je récupère le code pour le bidouiller dans une moulinette swift - Ouf, voilà.

    Bref j'ai quand même un besoin d'ultra précision sur les scales et dimensions de l'image et le path après des resize.
    Je vais devoir faire des compromis, je pense...

  • busterTheobusterTheo Membre
    octobre 2018 modifié #4

    Ah mince, j'ai oublié le principal (donc le futur compromis).
    Je fais un resize de l'image avec un scale issu de la valeur d'un zoom de scrolView, qui lui est du type
    zoomcadrage = 4,36586482553513.

    Et pour couronner le tout, à la fin des manips, l'idée est que les dimensions affichées sur l'écran sont affichées en mm, avec bien sur une certaine précision , limitée à 111.55 mm voire 111.5 mm.

    Donc pour résumé, j'ai une dimension associée à une mesure de 10 px correspondant selon l'utilisateur à 6 mm, tout ça avec un zoom sur l'écran de 4,36586482553513.

    J'ai donc 6 mm qui correspondent maintenant à 43.6586482553513 pixels.
    Donc 44 (l'objectif du resize), bien sur.

    Ça me laisse perplexe, tout ça.

  • Je vais donc convertir mon zoom de CGFloat(4,36586482553513) en CGFloat(4.0), tout simplement.

    C'est dommage, mais ça m'empêchera pas de dormir !

    Désolé pour le dérangement.

    Encore merci.

  • DrakenDraken Membre
    novembre 2018 modifié #6

    @busterTheo a dit :

    J'ai donc 6 mm qui correspondent maintenant à 43.6586482553513 pixels.

    NON, NON et NON ! Il ne s'agit pas de PIXELS, mais de POINTS ! C'est une unité de mesure virtuelle ne correspondant à aucune réalité physique fixe. Le développeur n'a pas à se soucier de la structure graphique sous-jacente, qui n'est pas la même sur toutes les versions d'iPad.

    iOS se charge ensuite d'adapter les dimensions en POINTS à la réalité physique de l'affichage. Et contrairement à ce que tu penses:

    @busterTheo a dit :
    Pourquoi peut-on mettre une largeur de CGFloat(1152,675) alors qu'un pixel ne se coupe pas en deux !!!

    On peut parfaitement afficher des fractions de pixel. La 2D c'est fini depuis longtemps. Les chipsets vidéo actuels ne travaillent qu'en 3D. Ce que tu crois être de la 2D est de la 3D projetée sur une surface plate (l'écran).

    Quand deux objets veulent "occuper" le même pixel, le moteur 3D a des algorithmes pour calculer sa teinte finale en tenant compte de la couleur des deux objets et de leurs tailles. Par exemple un rouge vif voulant occuper 1/3 d'un pixel, en concurrence avec un blanc occupant les 2/3 restants donnera un rouge atténué. A moins de faire une copie d'écran et de zoomer à fond avec un logiciel graphique, l'utilisateur ne verra jamais le "trucage".

    De nos jours, il ne faut plus penser :

    "je fabrique une image à partir de pixels, qui sera affichée EXACTEMENT de la même manière sur l'écran", mais :

    "Je fabrique une texture bitmap qui sera utilisée par le moteur graphique pour générer une image 3D plate, en fonction des dimensions de l'objet container (UIImageView par exemple) placé sur l'écran".

    En partant de cette logique, tu n'as peut-être même pas besoin de resizer ton image par code. Une imageView peut le faire plus efficacement que toi, à partir de l'image source. Cela vas même diminuer la charge processeur et la consommation d'énergie, la puce vidéo étant bien plus efficace que le CPU pour les opérations graphiques.

    Je vais donc convertir mon zoom de CGFloat(4,36586482553513) en CGFloat(4.0), tout simplement.

    NON ! En faisant ça tu tronques une partie de l'information dont le moteur 3D a besoin pour positionner correctement les objets.

  • @Draken a raison sur le fond mais un peu moins sur la forme.
    L'affichage sous macOS —iOS suit le même concept— est basé sur le PDF. Ce qui fait que depuis la nuit des temps les développeurs réfléchissent en points non pas parce c'est de la 3D projetée mais parce qu'on adopte le principe de l'impression.
    On donne des coordonnées et le medium se démerde comme il peut pour rendre le tout correctement. Que ce soit une imprimante compatible PostScript ou un écran. C'est pour ça que le code graphique n'a pas eu besoin d'être réécrit lors du passage au retina (bon presque pas, ok, le code CoreGraphics standard du moins).

    Donc c'est vrai que l'affichage est géré au travers d'une API 3D (Metal maintenant, OpenGL avant) mais c'est pour nous un détail d'implémentation et ça n'était pas vrai à l'origine pour des raisons évidentes.
    Ce qui est une réalité c'est que un point n'est pas forcément égal à un pixel et tu peux tranquillement les trancher et le subdiviser à l'envie. SAUF pour les tailles et positions des frames des views qui doivent être exprimées en valeurs entières (pour les bounds c'est bon).

  • DrakenDraken Membre
    novembre 2018 modifié #8

    @Pyroh a dit :
    SAUF pour les tailles et positions des frames des views qui doivent être exprimées en valeurs entières (pour les bounds c'est bon).

    Euh ..

    import UIKit
    
        class ViewController: UIViewController {
    
            let image = UIImageView()
    
            override func viewDidLoad() {
                super.viewDidLoad()
    
                let frame = CGRect(x: 100.66777,
                                   y: 306.8765,
                                   width: 145.9876,
                                   height: 90.7877)
    
                image.frame = frame
                self.view.addSubview(image)
    
                image.backgroundColor = UIColor.cyan
    
                print ("Frame : ", image.frame)
            }
    
        }
    

    Affichage dans la console :

    Frame : (100.66776999999999, 306.8765, 145.9876, 90.7877)

    EDIT : Il y a une sorte de trait autour du rectangle bleu, c'est dus à la compression JPEG exagéré que j'ai utilisé. Voici la même image en .png, sans réduction de taille, ni compression jpeg pour éliminer l'artefact de compression :

  • Alors si je ne dis pas de bêtise c'est vrai sous macOS où la taille est arrondie à l'entier le plus proche.
    Je ne sais pas ce qu'il en est pour iOS mais on peut tester parce que rien ne t'assure que les valeurs n'ont pas été arrondies 😉.

  • @Pyroh a dit :
    Alors si je ne dis pas de bêtise c'est vrai sous macOS où la taille est arrondie à l'entier le plus proche.
    Je ne sais pas ce qu'il en est pour iOS mais on peut tester parce que rien ne t'assure que les valeurs n'ont pas été arrondies 😉.

    On doit pouvoir faire ça, en affichant une grille d'éléments graphiques de différentes couleurs. J'y réfléchirais ce WE.

  • DrakenDraken Membre
    novembre 2018 modifié #11

    J'ai effectué quelques tests, avec des vues de couleurs de différentes tailles, anti-aliasing déconnecté. On peut mettre n'importe quelle valeur fractionnaire pour la position et la taille des vues. iOS ajuste automatiquement les valeurs à l'affichage pour les faire coller avec les pixels réels de l'écran les plus proches.

    En mode retina @2x la précision de positionnement est de 0,5 points. En retina @3x elle est de 0,333333 points.

    Conclusion, @busterTheo, évite d'arrondir par toi-même la taille des graphismes. iOS le fait de lui-même, de manière optimale.

  • @Draken a dit :
    J'ai effectué quelques tests, avec des vues de couleurs de différentes tailles, anti-aliasing déconnecté. On peut mettre n'importe quelle valeur fractionnaire pour la position et la taille des vues. iOS ajuste automatiquement les valeurs à l'affichage pour les faire coller avec les pixels réels de l'écran les plus proches.

    En mode retina @2x la précision de positionnement est de 0,5 points. En retina @3x elle est de 0,333333 points.

    Conclusion, @busterTheo, évite d'arrondir par toi-même la taille des graphismes. iOS le fait de lui-même, de manière optimale.

    Donc l'info que j'avais datait de l'aire pré-retina. Bon à savoir.

  • DrakenDraken Membre
    novembre 2018 modifié #13

    @Pyroh a dit :

    Donc l'info que j'avais datait de l'aire pré-retina.

    L'aire pré-retira .. jeu de mot volontaire ou erreur de frappe ? En tout cas c'est amusant.

  • Bah non c'est écrit retina chez moi ou alors je ne comprends pas.

  • DrakenDraken Membre
    novembre 2018 modifié #15

    L'ère pré-retina !!

    L'aire est une unité de surface, pas une référence temporelle ..

    Exemples : L'ère de la vapeur. L'aire d'un rectangle..

    Bah non c'est écrit retina chez moi ou alors je ne comprends pas.

    Le correcteur orthographique de safari a changé retina en retira sur mon post.. Grrrr .. Très énervant ce truc !

  • Ah ouais j'avoue je l'ai pas vue celle là. On n'en parlera pas à ma prof de français de femme hein ?

  • DrakenDraken Membre
    novembre 2018 modifié #17

    @Pyroh a dit :
    Ah ouais j'avoue je l'ai pas vue celle là. On n'en parlera pas à ma prof de français de femme hein ?

    *rigole discrètement

  • Bien l'bonjour à vous, je reviens sur la converse des lustres après.
    Pour moi, c'est du lourd, mais j'ai adoré votre discussion, et je vous en remercie grandement.
    À la votre...

Connectez-vous ou Inscrivez-vous pour répondre.