如何绘制一个圆角的NSImage?

7

我正在尝试在NSTableView中创建一个带有圆角的NSImageNSImageCell,但一直无法成功。下面是我的自定义NSCell中目前最好的代码:

- (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];
    }
}
...

有没有关于如何做到这一点的想法?
4个回答

23
你需要在绘制图像时保留夹子设置,并在之后恢复上下文。此外,我认为如果您只想正常绘制图像而不考虑目标不透明度,则不应使用“in”混合模式,而应该使用“over”。尝试类似以下的内容:
[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];

10
+ (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;
}

干得好...不过你的newSize宽度和高度搞反了。 - El Tomato
非常好的答案。这个帖子实际上剪辑了图片,而标记的答案只是掩盖了图片。对我的情况很重要,因为我必须在圆形图片周围画一个白色边框。使用标记的答案方法,图像中的像素会渗入边框(使其看起来不好)。这篇帖子让边框看起来完美! - rocky

7

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)

谢谢,Paul;简单而甜美 :) (只是修复了一些硬编码的值;等待同行审查。) - Aral Balkan
干得好,谢谢。对于从互联网上来的人,请确保修改“宽度”,“高度”,“x半径”和“y半径”以匹配您要圆角的对象的尺寸。 - Clifton Labrum
NSMakeSize() 的参数是颠倒的,你最好使用 NSize 初始化器:let newSize = NSSize(width: esize.width, height: esize.height)。 - Guillaume Laurent

3

关于您所犯的错误,我想提供更多信息:

保存和恢复gstate的目的是为了能够撤消对gstate的更改。在您的情况下,在剪辑后恢复gsave会撤销剪辑。这就是为什么解决方案(由Rhult解释)是在恢复gstate之前绘制您想要剪辑的内容。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接