2009-12-04 13 views
4

J'essaie de créer un NSImage ou NSImageCell avec des coins arrondis à l'intérieur d'un NSTableView. Je ne peux rien faire pour travailler. Voici le meilleur que j'ai jusqu'à présent dans ma coutume NSCell:Comment dessiner un NSImage arrondi

- (void)drawInteriorWithFrame:(NSRect)frame inView:(NSView *)controlView { 
    if (thumbnailLink) { 
    NSURL *url = [NSURL URLWithString:thumbnailLink]; 
    if (url) { 
     NSRect imageFrame = [self _imageFrameForInteriorFrame:frame]; 
     NSImage *image = [[NSImage alloc] initWithContentsOfURL:url]; 
     [image setScalesWhenResized:YES]; 
     [image setSize:NSMakeSize(IMAGE_HEIGHT, IMAGE_WIDTH)]; 

     [NSGraphicsContext saveGraphicsState]; 
     imageFrame = NSInsetRect(imageFrame, 1, 1); 
     NSBezierPath *clipPath = [NSBezierPath bezierPathWithRoundedRect:imageFrame cornerRadius:5.0]; 
     [clipPath setWindingRule:NSEvenOddWindingRule]; 
     [clipPath addClip]; 
     [NSGraphicsContext restoreGraphicsState]; 
     [image drawInRect:imageFrame fromRect:NSMakeRect(0, 0, 0, 0) operation:NSCompositeSourceIn fraction:1.0]; 
     [image release]; 
    } 
} 
... 

Toutes les idées sur la façon de cela?

Répondre

22

Vous devez conserver le jeu de clips lors du dessin de votre image et restaurer le contexte après. Je ne pense pas non plus que vous vouliez utiliser le compositing "in" mais plutôt "over" si vous voulez juste dessiner votre image normalement sans prendre en compte l'opacité de la destination. Essayez quelque chose comme:

[NSGraphicsContext saveGraphicsState]; 

NSBezierPath *path = [NSBezierPath bezierPathWithRoundedRect:imageFrame 
                xRadius:5 
                yRadius:5]; 
[path addClip]; 

[image drawInRect:imageFrame 
     fromRect:NSZeroRect 
     operation:NSCompositeSourceOver 
     fraction:1.0]; 

[NSGraphicsContext restoreGraphicsState]; 
+0

Cela fonctionne !! Merci beaucoup!! – nonamelive

3

Juste pour donner un peu plus d'informations sur ce que vous avez tort: ​​

Le but de sauver et restaurer la gstate est de pouvoir annuler vos modifications à la gstate. Dans votre cas, la restauration du gsave après écrêtage supprime le clip. C'est pourquoi la solution (comme expliqué par Rhult) est de dessiner ce que vous voulez clippé avant vous restaurez le gstate.

9
+ (NSImage*)roundCorners:(NSImage *)image 
{ 

NSImage *existingImage = image; 
NSSize existingSize = [existingImage size]; 
NSSize newSize = NSMakeSize(existingSize.width, existingSize.height); 
NSImage *composedImage = [[[NSImage alloc] initWithSize:newSize] autorelease]; 

[composedImage lockFocus]; 
[[NSGraphicsContext currentContext] setImageInterpolation:NSImageInterpolationHigh]; 

NSRect imageFrame = NSRectFromCGRect(CGRectMake(0, 0, 1024, 1024)); 
NSBezierPath *clipPath = [NSBezierPath bezierPathWithRoundedRect:imageFrame xRadius:200 yRadius:200]; 
[clipPath setWindingRule:NSEvenOddWindingRule]; 
[clipPath addClip]; 

[image drawAtPoint:NSZeroPoint fromRect:NSMakeRect(0, 0, newSize.width, newSize.height) operation:NSCompositeSourceOver fraction:1]; 

[composedImage unlockFocus]; 

return composedImage; 
} 
+0

Bon travail ... Vous avez la largeur et la hauteur en arrière pour newSize, cependant. –

+0

Bonne réponse. Ce message écrête l'image alors que la réponse marquée masque simplement l'image. Cela importait dans mon cas parce que je devais dessiner une bordure blanche autour d'une image circulaire. À l'aide de la méthode de réponse marquée, les pixels de l'image ont saigné dans la bordure (ce qui a l'air mauvais). Ce post rend la frontière parfaite! – rocky

4

Version Swift:

func roundCorners(image: NSImage, width: CGFloat = 192, height: CGFloat = 192) -> NSImage { 
    let xRad = width/2 
    let yRad = height/2 
    let existing = image 
    let esize = existing.size 
    let newSize = NSMakeSize(esize.width, esize.height) 
    let composedImage = NSImage(size: newSize) 

    composedImage.lockFocus() 
    let ctx = NSGraphicsContext.currentContext() 
    ctx?.imageInterpolation = NSImageInterpolation.High 

    let imageFrame = NSRect(x: 0, y: 0, width: width, height: height) 
    let clipPath = NSBezierPath(roundedRect: imageFrame, xRadius: xRad, yRadius: yRad) 
    clipPath.windingRule = NSWindingRule.EvenOddWindingRule 
    clipPath.addClip() 

    let rect = NSRect(x: 0, y: 0, width: newSize.width, height: newSize.height) 
    image.drawAtPoint(NSZeroPoint, fromRect: rect, operation: NSCompositingOperation.CompositeSourceOver, fraction: 1) 
    composedImage.unlockFocus() 

    return composedImage 
} 

// then 
roundCorners(image) 
roundCorners(image, width: 512, height: 512) 
+0

Merci, Paul; simple et doux :) (Juste fixé certaines valeurs codées en dur, en attente d'examen par les pairs.) –

+0

Beau travail, merci. Pour ceux qui arrivent des interwebs, assurez-vous de modifier les paramètres 'width',' height', 'xRadius' et' yRadius' pour correspondre aux dimensions de l'objet que vous arrondissez. –

+0

Les paramètres de NSMakeSize() sont inversés, et il est préférable d'utiliser l'initialiseur NSize de toute façon: let newSize = NSSize (width: esize.width, height: esize.height) –