« CrossFitting | Main | Dubai »

NSRectFill() and +[NSColor clearColor]

I'm reading a code review from a colleague when I see this:

[[NSColor clearColor] set];
NSRectFill(rect);

Ouch. I see that's broken, but only because I made the same mistake yesterday. What's wrong?

+[NSColor clearColor] is actually black with an alpha 0. That would be fine but for a subtle fact about NSRectFill(): it ignores alpha. Really! The code, as written above, will fill the specified rectangle with black.

What's going on here? NSRectFill() uses the compositing mode NSCompositeCopy, which ignores alpha. If you want alpha, you must say:

[[NSColor clearColor] set];
NSRectFillUsingOperation(rect, NSCompositeSourceOver);

A quick one-liner, but too easy to get wrong.

TrackBack

TrackBack URL for this entry:
http://www.drissman.com/cgi-sys/cgiwrap/drissman/mt/mt-tb.cgi/888

Comments

Being the "colleague" in question, thought I'd do a small rebuttal. I agree with Avi's change, but just wanted to clear up the actual reason why.

What is happening here is that in Avi's case his window is marked as opaque (which they are by default).

NSRectFill doesn't ignore alpha, it actually copies the alpha across. However since the window is marked as opaque, the window server needs to draw something in that space that you have filled with clear and it falls back on black. If you mark the window as non-opaque you will see through it as expected.

NSRectFillUsingOperation with NSCompositeSourceOver is probably the better choice in both the opaque and non-opaque cases as it will do what you would expect either way.

It was working properly as NSRectFill in my case due to my window being marked as non-opaque.

Ah. Interesting point.

Did I mention that I wasn't talking about windows, but views?

In my case, I had an NSView that I wanted to use as an overlay. It was marked as non-opaque, but doing a rectfill kept it black. Only by using the SourceOver attribute did it work.

Why would views and windows be different?

We're both talking views as you can't "draw" in a window.

In your case with your overlay view and NSRectFill you are replacing the bits that you are overlaying with clear. The window server composites views by drawing them from the bottom up.
Since the window is opaque, it needs to draw something, and draws black.

When you use NSRectFillWithOperation(blah, NSCompositeSourceOver) you are compositing the bits in your overlay view on top of the bits in the views underneath it. It doesn't matter if your views are opaque in this case, as it's the backing window for the views which is important.

Hopefully this makes sense.

When you use NSRectFillWithOperation(blah, NSCompositeSourceOver) you are compositing the bits in your overlay view on top of the bits in the views underneath it. It doesn't matter if your views are opaque in this case, as it's the backing window for the views which is important.

Post a comment