rotation d'une NSImage dans une NSImageView

ChachaChacha Membre
23:33 modifié dans API AppKit #1
Salut,

J'aimerais afficher une image dans une NSImageView, mais en la faisant pivoter de 90°.
J'ai réussi de façon assez lourdingue : à  coups de NSAffineTransform, et en recopiant l'image dans une nouvelle NSImage par un drawRect, ça marche. Mais bon...
Je pense qu'afficher une image pivotée, MacOS X doit savoir le faire pour moi, mais je ne comprends pas comment.
Dans Cocoa : tous les setRotation, setBoundsRotation, setFrameRotation, rotationByAngle... font pivoter la frame, certes, mais l'image contenue dedans n'y est pas tournée, elle reste droite; dommage !
Dans CoreGraphics, il y a apparemment tout ce qu'il faut avec les CGContextRotateCTM (), mais comment mixer cela avec une NSImage et des NSImageView ? Je n'arrive pas à  détourner l'affichage de l'image pour le faire avec CoreGraphics.
Vous avez des pistes ?

+
Chacha

Réponses

  • ChachaChacha Membre
    mars 2005 modifié #2
    Oyez, oyez, j'ai fini par trouver, en assemblant pas mal de trucs:

    <br />-(void) drawRect:(NSRect)aRect //surcharge de drawRect<br />{<br />  //[super drawRect:aRect];//j&#39;annule l&#39;appel au drawrect normal<br /><br />  //obtention du contexte CG<br />  CGContextRef myContext = [[NSGraphicsContext currentContext] graphicsPort];<br /><br />  //rotation centrée (c&#39;est pour ça qu&#39;il y a les translate)<br />  //l&#39;angle doit être spécifié en radians<br />  CGContextTranslateCTM(myContext, rect.size.width/2, rect.size.height/2);<br />  CGContextRotateCTM(myContext, angleDeg*3.1415927/180);<br />  CGContextTranslateCTM(myContext, -rect.size.width/2, -rect.size.height/2);<br /><br />  //conversion de la NSImage en CGImageRef<br />  CGImageRef imageRef = WebConvertNSImageToCGImageRef([self image]); //WebKit/CarbonUtils.h<br /><br />  //rectangle en CG<br />  NSRect rect = [self frame];<br />  CGRect cgRect = {{rect.origin.x, ret.origin.y}, {rect.size.width, rect.size.height}};<br /><br />  //drawing en CG<br />  CGContextDrawImage(myContext, cgRect, imageRef);<br />}<br />
    


    Voilà 
    +
    Chacha

    [edit]
    Apparemment, il faut faire un
    CGImageRelease(imageRef) à  la fin
    [/edit]
  • fouffouf Membre
    23:33 modifié #3
    Tu utilises le WebKit pour ca ?? ??  ???
  • mpergandmpergand Membre
    mars 2005 modifié #4
    Ma vision de la chose   :-*
    <br />NSSize size=[self bounds].size;<br />	NSImage* newImage=[[NSImage alloc] initWithSize: size];<br />	float angle=currentFrame*-18.0;<br />	NSAffineTransform* rotateTransform=[NSAffineTransform transform];<br />	NSAffineTransform* translateTransform=[NSAffineTransform transform];<br />	[rotateTransform rotateByDegrees:angle];<br /><br /><br />	[translateTransform translateXBy: size.width/2 yBy: size.height/2];<br />	[rotateTransform appendTransform:translateTransform];<br />	<br />	[newImage lockFocus];<br />	[rotateTransform concat];<br />	size=[image size];<br />	[image drawAtPoint:NSMakePoint(-size.width/2,-size.height/2) fromRect:NSZeroRect<br />						operation: NSCompositeSourceOver fraction: 1.0];<br />	[newImage unlockFocus];<br />	<br />	[self setImage:newImage];<br />	[newImage release];<br />
    


    Je ne vous en montre pas plus pour le moment, car le code est un gros sac de n½uds (genre oxitan  :) )

    [Fichier joint supprimé par l'administrateur]
  • fouffouf Membre
    23:33 modifié #5
    Ya deux trois trucs que je ne comprends pas dans ton code mpergand:
    image est une variable de classe qui pointe l'image de la NSImageView. C'est bien ca ??

    Ensuite, tu rajoute translateTransform a rotateTransform. C'est bien pour recentrer l'image ??
  • mpergandmpergand Membre
    23:33 modifié #6
    Oui, c'est une copie de l'image originale contenu dans NSImageView:
    image=[self image]copy];
  • ChachaChacha Membre
    23:33 modifié #7
    dans 1110727330:

    Oui, c'est une copie de l'image originale contenu dans NSImageView:
    image=[self image]copy];

    Oui, mais la technique avec Core Graphics est immensément plus rapide, car on ne crée pas une nouvelle image : on fait des rotations en temps réel !
  • mpergandmpergand Membre
    23:33 modifié #8
    Voila, j'ai fait mumuse avec les rotations d'images, ce qui donne la hiérarchie de classes suivante:

    NSImageView --> AnimatedImage --> ImageSuiteView
                                      |
                                       
    > SpinningImageView
                                                                   |
                                                                   
    > CurvedTextView

    AnimatedImage est une classe abstraite qui gère le timer, la méthode appelée devant être implémentée dans les classes dérivées.

    ImageSuiteView affiche les images contenues dans un dossier successivement, à  la manière d'un film.
    SpinningImageView effectue une rotation d'une image
    CurvedTextView effectue une rotation d'un texte, en fait, celui-ci est d'abord converti en une image, c'est pour ça que cette classe dérive de SpinningImage. OK c'est pas la meilleur soluce si l'image fait tout l'écran :) Je me suis inspiré de l'exemple d'Apple CircleView.

    La classe ScrollingTextView a été rajoutée suite au besoin d'oxitan de faire défiler un texte, ici j'utilise la classe AnimatedImage pour le timer, donc c'est un NSImageView, c'est bien sûr inutile, un simple NSView convient.

    Les sources sont en java  :o
    Je ne pense pas que ça soit très difficile à  adapter en ObjC, à  part le truc de la classe abstraite, qui semble pas avoir d'équivalent direct...
    AnimatedImageJava.Sources.zip
  • août 2007 modifié #9
    Je me permet de remonter le sujet car je galère.

    Pour mon logiciel de redimensionnement d'image, je veux rajouter la rotation. J'utilise ton code, mpergand. Et je galère un peu beaucoup (n'étant pas doué pour ce qui est du graphique).

    Ce que j'ai c'est :
    - L'image originale (ou redimensionnée si l'utilisateur l'a fait)
    - une NSImageView

    À un moment j'ai réussi à  bien caler la rotation, cependant l'image devient tronquée, j'ai l'impression qu'en fait il se base sur la taille de l'image originale.

    Edit : Apparemment j'ai trouvé le problème. En fait après avoir correctement analysé le code je me suis rendu compte que tu prenais la taille de l'image view. Et comme je l'avais calé à  la taille de l'image originale ben forcément c'est tronqué. Donc je dois faire quoi pour éviter ça ? Je dois recalculer la taille par rapport à  la rotation ?

    [Fichier joint supprimé par l'administrateur]
  • MulotMulot Membre
    août 2007 modifié #10
    Peut être inverser la largeur et la hauteur pour la taille de la nouvelle image, j'avais eu un cas similaire en Java.
  • UniXUniX Membre
    23:33 modifié #11
    Ben moi je dirais oui.

    Si la taille de ton NSImageView est figée, lorsque tu fais tourner ton image, tu dois lui réduire ses dimensions pour qu'elle ne soit pas tronquée.

    En fait, calcule les dimensions du rectangle enveloppant ton image tournée, et c'est ce rectange qui doit correspondre avec les dimensions de la NSImageView. Tu calcul le rapport entre les 2, et tu fais une NSAffineTransform.
  • Philippe49Philippe49 Membre
    23:33 modifié #12
    Comme chante (chantait) Souchon

    Y a du cosinus dans l'air
    cosinus de travers,
    ta vue tu peux pas la faire,
    Sans un p'tit calcul derrière ...
  • fouffouf Membre
    23:33 modifié #13
    Ca tombe drolement bien, parce que j'ai eu le même problème récemment et comme dis Philippe, ya du cosinus dans l'air ...

    Voici comment faire :
    Supposons que le centre de l'image soient de coordonnées (0;0), l'angle de rotation theta, l'image de largeur w et de hauteur h.
    Le point A de coordonnées (w/2;h/2) est un sommet du rectangle quand l'angle de rotation est 0 et, quel que soit r, un sommet de l'image encore non tournée. Maintenant, un peu de maths.
    Soit B le l'image de A par la rotation d'angle theta et de centre (0;0) (la rotation qui tourne l'image comme eaglelouk veut le faire). On a donc B de coordonnés (A.x*cos(theta)-A.y*sin(theta); A.x*sin(theta)+B.y*cos(theta)).
    Si vous relisez ce que j'ai dit, vous verrez que B est un des sommets de l'image tournée.  Pour continuer, il faudrait faire la même chose avec un autre point. Maintenant que l'on a des coordonnées, on va pouvoir récupérer une partie du rectangle.

    Pour faire court, voici le code :

    <br />NSPoint a,b,c,d;<br />float theta; // ici l&#39;angle de rotation<br />NSRect rect; // ici le rectangle d&#39;origine<br />NSRect newRect; // ici le nouveau rectangle<br /><br />a = NSMakePoint(rect.size.width/2.0, rect.size.height/2.0);<br />c = NSMakePoint(rect.size.width/2.0, -rect.size.height/2.0);<br /><br />b.x = a.x*cos(theta)-a.y*sin(theta);<br />b.y = a.x*sin(theta)+a.y*cos(theta);<br />d.x = c.x*cos(theta)-c.y*sin(theta);<br />d.y = c.x*sin(theta)+c.y*cos(theta);<br /><br />newRect.size.width = MAX(fabs(b.x), fabs(d.x));<br />newRect.size.height = MAX(fabs(b.y), fabs(d.y));<br />newRect.origin.x = -newRect.size.width;<br />newRect.origin.x = -newRect.size.width;<br />newRect.size.width *= 2.0;<br />newRect.size.height *= 2.0;<br /><br />// maintenant on a un rectangle centrer sur (0,0) et qui a les dimensions voulues.<br />// il suffit juste de le mettre là  où l&#39;on veut pour l&#39;utiliser ... <br />
    


    Voili voilou

    [edit]
    Il est très beau ton inspecteur ::)
  • 23:33 modifié #14
    dans 1187727945:

    Ca tombe drolement bien, parce que j'ai eu le même problème récemment et comme dis Philippe, ya du cosinus dans l'air ...

    Voici comment faire :
    Supposons que le centre de l'image soient de coordonnées (0;0), l'angle de rotation theta, l'image de largeur w et de hauteur h.
    Le point A de coordonnées (w/2;h/2) est un sommet du rectangle quand l'angle de rotation est 0 et, quel que soit r, un sommet de l'image encore non tournée. Maintenant, un peu de maths.
    Soit B le l'image de A par la rotation d'angle theta et de centre (0;0) (la rotation qui tourne l'image comme eaglelouk veut le faire). On a donc B de coordonnés (A.x*cos(theta)-A.y*sin(theta); A.x*sin(theta)+B.y*cos(theta)).
    Si vous relisez ce que j'ai dit, vous verrez que B est un des sommets de l'image tournée.  Pour continuer, il faudrait faire la même chose avec un autre point. Maintenant que l'on a des coordonnées, on va pouvoir récupérer une partie du rectangle.

    Pour faire court, voici le code :

    <br />NSPoint a,b,c,d;<br />float theta; // ici l&#39;angle de rotation<br />NSRect rect; // ici le rectangle d&#39;origine<br />NSRect newRect; // ici le nouveau rectangle<br /><br />a = NSMakePoint(rect.size.width/2.0, rect.size.height/2.0);<br />c = NSMakePoint(rect.size.width/2.0, -rect.size.height/2.0);<br /><br />b.x = a.x*cos(theta)-a.y*sin(theta);<br />b.y = a.x*sin(theta)+a.y*cos(theta);<br />d.x = c.x*cos(theta)-c.y*sin(theta);<br />d.y = c.x*sin(theta)+c.y*cos(theta);<br /><br />newRect.size.width = MAX(fabs(b.x), fabs(d.x));<br />newRect.size.height = MAX(fabs(b.y), fabs(d.y));<br />newRect.origin.x = -newRect.size.width;<br />newRect.origin.x = -newRect.size.width;<br />newRect.size.width *= 2.0;<br />newRect.size.height *= 2.0;<br /><br />// maintenant on a un rectangle centrer sur (0,0) et qui a les dimensions voulues.<br />// il suffit juste de le mettre là  où l&#39;on veut pour l&#39;utiliser ... <br />
    


    Voili voilou

    [edit]
    Il est très beau ton inspecteur ::)


    Merci ma foufoune :D (Encore merci pour l'inspecteur au fait :p )
  • schlumschlum Membre
    août 2007 modifié #15
    Plutôt que d'utiliser les NSAffineTransform qui sont d'une lourdeur absolue, je vous conseille d'utiliser CoreGraphics directement...
    Surtout que c'est très simple (bien plus que NSAffineTransform)  : on sauve le contexte graphique, on applique des transformations, on dessine et on restaure le contexte.

    ça devrait donner un truc du genre :
    CGContextRef *context = [[NSGraphicContext currentContext] graphicsPort]; <br /><br />CGContextSaveGState(context);<br /><br />CGContextTranslateCTM(context,-w/2.,-h/2.);<br />CGContextRotateCTM(context,angle);<br />CGContextTranslateCTM(context,w/2.,h/2.);<br /><br />// ... Dessin de l&#39;image<br /><br />CGContextRestoreGState(context);
    


    Et les cosinus et sinus peuvent retourner d'où ils viennent :P (ceci-dit, s'il faut calculer un "scale", il faut sans doute y passer...)
Connectez-vous ou Inscrivez-vous pour répondre.