5

If I'm not really fussy about the exact tone etc of the dimmed image, is there a quick and dirty way to draw an NSImage slightly dimmed, like this? I've searched online and can't really find what I'm looking for (but I'm not very good when it comes to understanding graphics and the correct technical terms).

Non-dimmed Dimmed

I'm actually dimming icons for the same use-case as Xcode dimming icons like above (i.e. document has unsaved changes).

d11wtq
  • 34,788
  • 19
  • 120
  • 195

2 Answers2

17

This works fine for me:

NSImage *iconImage = [NSImage imageNamed:@"Icon"];
NSSize iconSize = [iconImage size];
NSRect iconRect = NSMakeRect(0.0, 0.0, iconSize.width, iconSize.height);
[iconImage lockFocus];
[[NSColor colorWithCalibratedWhite:0.0 alpha:0.33] set];
NSRectFillUsingOperation(iconRect, NSCompositeSourceAtop);
[iconImage unlockFocus];
[iconImage drawInRect:iconRect
              fromRect:iconRect
             operation:NSCompositeSourceOver
              fraction:0.75];

Basically I'm adding a black layer with 33% opacity on top of the actual icon (masking it with NSCompositeSourceAtop). And then I'm just drawing the dimmed icon with an opacity of 75%.

[Edit: got rid of temporary black image by using NSRectFillUsingOperation(...), as advised by Nikolai Ruhe]

Regexident
  • 29,441
  • 10
  • 93
  • 100
  • Whoever downvoted you was a bit harsh. This does work. Can whoever downvoted please do the decent thing and comment on why, please? :) – d11wtq Nov 25 '10 at 13:00
  • 1
    Using `NSRectFillUsingOperation` to draw on top of `iconImage` you can get rid of the `blackImage`. – Nikolai Ruhe Nov 25 '10 at 13:02
  • I was just thinking that myself. Just draw a dark rect that semi-opaque over the top of the rect the image has been drawn in. Should basically do the same thing. About to refactor. – d11wtq Nov 25 '10 at 13:04
  • Nikolai, are you able to show an example? I think I'm doing it wrong as I just get a big black square obscuring the image (which has transparent areas). Maybe I've misunderstood. – d11wtq Nov 25 '10 at 13:11
  • @d11wtq, @Nikolai: fixed it. Didn't know about `NSRectFillUsingOperation()` until now, thanks Nikolai! Learning Cocoa in its entirety sure is a life-time project. :) – Regexident Nov 25 '10 at 14:01
  • I was also coming back to post this same thing :) Thanks for the help! – d11wtq Nov 25 '10 at 14:12
  • I'm not sure this is safe to do multiple times. It would be safer to begin and end a transparency layer using Core Graphics, and draw the image (without ever changing it) and do the source-atop fill in between. – Peter Hosey Nov 26 '10 at 03:40
  • Yeah, running my code (unchanged) several times (in, say a loop) would obviously result in unwanted results. (Increasingly darkened icons, that is, e.g.) My code answered the question though, so I didn't bother. ;) You're more than right though. – Regexident Nov 27 '10 at 18:05
2

Swift 5 function to create a dimmed version of any input image:

func dimmedImage(_ image: NSImage, alpha: CGFloat) -> NSImage {
    let newImage = NSImage(size: image.size)
    newImage.lockFocus()

    let imageRect = NSRect(origin: .zero, size: image.size)
    image.draw(in: imageRect, from: imageRect, operation: .sourceOver, fraction: alpha)

    newImage.unlockFocus()
    return newImage
}
Robin Stewart
  • 3,147
  • 20
  • 29