web-dev-qa-db-fra.com

Faire pivoter un contexte d'image (CGContextRotateCTM) le rendant vide. Pourquoi?

#define radians(degrees) (degrees * M_PI/180)

UIImage *rotate(UIImage *image) {
  CGSize size = image.size;;

  UIGraphicsBeginImageContext(size);
  CGContextRef context = UIGraphicsGetCurrentContext();

  // If this is commented out, image is returned as it is.
  CGContextRotateCTM (context, radians(90));

  [image drawInRect:CGRectMake(0, 0, size.width, size.height)];
  UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
  UIGraphicsEndImageContext();

  return newImage;
}

Quelque chose d'autre pourrait-il mal? Par idées.

30
Gurpartap Singh

Le problème est que votre contexte tourne autour de (0,0) qui est le coin supérieur gauche. Si vous pivotez de 90 degrés autour du coin supérieur gauche, tout votre dessin se produira hors des limites du contexte. Vous avez besoin d'une transformation en 2 étapes pour déplacer l'origine du contexte vers son milieu, puis tourner. Vous devez également dessiner votre image centrée autour de l'origine déplacée/tournée, comme ceci:

CGContextTranslateCTM( context, 0.5f * size.width, 0.5f * size.height ) ;
CGContextRotateCTM( context, radians( 90 ) ) ;

[ image drawInRect:(CGRect){ { -size.width * 0.5f, -size.height * 0.5f }, size } ] ;

HTH

52
nielsbot

J'essaie le code suivi, cela fonctionne, obtenez de http://www.catamount.com/blog/1015/uiimage-extensions-for-cutting-scaling-and-rotating-uiimages/

+ (UIImage *)rotateImage:(UIImage*)src byRadian:(CGFloat)radian
{
    // calculate the size of the rotated view's containing box for our drawing space
    //UIView *rotatedViewBox = [[UIView alloc] initWithFrame:CGRectMake(0,0, src.size.width, src.size.height)];
    //CGAffineTransform t = CGAffineTransformMakeRotation(radian);
    //rotatedViewBox.transform = t;
    //CGSize rotatedSize = rotatedViewBox.frame.size;
    CGRect rect = CGRectApplyAffineTransform(CGRectMake(0,0, src.size.width, src.size.height), CGAffineTransformMakeRotation(radian));
    CGSize rotatedSize = rect.size;

    // Create the bitmap context
    UIGraphicsBeginImageContext(rotatedSize);
    CGContextRef bitmap = UIGraphicsGetCurrentContext();

    // Move the Origin to the middle of the image so we will rotate and scale around the center.
    CGContextTranslateCTM(bitmap, rotatedSize.width/2, rotatedSize.height/2);

    //   // Rotate the image context
    CGContextRotateCTM(bitmap, radian);

    // Now, draw the rotated/scaled image into the context
    CGContextScaleCTM(bitmap, 1.0, -1.0);
    CGContextDrawImage(bitmap, CGRectMake(-src.size.width / 2, -src.size.height / 2, src.size.width, src.size.height), [src CGImage]);

    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return newImage;
}
11
lbsweek

Si vous ne voulez pas changer toutes les origines et tailles, déplacez le centre, faites pivoter et changez à nouveau le centre en coin comme ceci:

CGContextTranslateCTM( context, 0.5f * size.width, 0.5f * size.height ) ; CGContextRotateCTM( context, radians( 90 ) ) ; CGContextTranslateCTM( context, -0.5f * size.width, -0.5f * size.height ) ;

Puis dessinez normalement comme ceci:

[image drawInRect:CGRectMake(0, 0, size.width, size.height)];

9
Farhad Malekpour

En développant la réponse de Nielsbot, j'ai créé l'extrait de code suivant. Notez que cela devrait de préférence être une fonction plutôt qu'un extrait de code, pour éviter la "programmation copier/coller". Je n'y suis pas encore arrivé, alors n'hésitez pas à l'adapter.

Extrait de code:

// <Draw in frame with orientation>
UIImage* imageToDrawRotated = <#image#>;
float rotationAngle = <#angle#>;
CGRect drawFrame = CGRectMake(<#CGFloat x#>, <#CGFloat y#>, <#CGFloat width#>, <#CGFloat height#>);
CGContextTranslateCTM(UIGraphicsGetCurrentContext(), floor(drawFrame.Origin.x + drawFrame.size.width/2), floor(drawFrame.Origin.y + drawFrame.size.height/2));
CGContextRotateCTM(UIGraphicsGetCurrentContext(), rotationAngle);
[imageToDrawRotated drawInRect:CGRectMake(-floor(drawFrame.size.width/2), -floor(drawFrame.size.height/2), drawFrame.size.width, drawFrame.size.height)];
CGContextRotateCTM(UIGraphicsGetCurrentContext(), -rotationAngle);
CGContextTranslateCTM(UIGraphicsGetCurrentContext(), -floor(drawFrame.Origin.x + drawFrame.size.width/2), -floor(drawFrame.Origin.y + drawFrame.size.height/2));
// </Draw in frame with orientation>

De plus, je me réjouis d'une meilleure façon de réinitialiser le CGContext à son état précédent.

1
Timo

J'ai aimé la solution de @lbsweek mais j'étais un peu inquiète d'allouer UIView juste pour la transformation, au lieu de cela j'ai essayé ce qui suit

- (UIImage *)rotateImage:(UIImage *)sourceImage byDegrees:(float)degrees
{
    CGFloat radian = degrees * (M_PI/ 180);
    CGRect contextRect = CGRectMake(0, 0, sourceImage.size.width, sourceImage.size.height);
    float newSide = MAX([sourceImage size].width, [sourceImage size].height);
    CGSize newSize =  CGSizeMake(newSide, newSide);
    UIGraphicsBeginImageContext(newSize);
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    CGPoint contextCenter = CGPointMake(CGRectGetMidX(contextRect), CGRectGetMidY(contextRect));
    CGContextTranslateCTM(ctx, contextCenter.x, contextCenter.y);
    CGContextRotateCTM(ctx, radian);
    CGContextTranslateCTM(ctx, -contextCenter.x, -contextCenter.y);
    [sourceImage drawInRect:contextRect];
    UIImage* rotatedImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return rotatedImage;
}
1
anoop4real