0

I have an issue with drawing lines on top of each other in OS X. The following code draws a red rectangle and then the same rectangle in green color. I would expect a green rectangle as a result because it is drawn last. I get a mixture of red and green - olive green. How to modify the code to get green rectangle in this situation?

import Cocoa

class TestView : NSView {

    override func drawRect (dirtyRect : NSRect) {

        super.drawRect (dirtyRect)

        let rect = NSRect (x : 100 , y : 200 , width : 100 , height : 100)

        var p = NSBezierPath ()

        var c = NSColor (red : 1 , green : 0 , blue : 0 , alpha : 1)

        c.set ()

        p.appendBezierPathWithRect (rect)

        p.stroke ()

        p = NSBezierPath ()

        c = NSColor (red : 0 , green : 1 , blue : 0 , alpha : 1)

        c.set ()

        p.appendBezierPathWithRect (rect)

        p.stroke ()
    }
}

Thanks/Mikael

2 Answers2

1

Why stroke twice if you only want one color? Anyway you might need to explicitly set the blend mode of the graphics context. NSGraphicsContext has a compositingOperation property

Many drawing commands in CoreGraphics and the NS AppKit wrappers can also take a compositing operation argument.

Also ensure your view is also at an alpha of 1.0 a non opaque view with an alpha less that 1.0 may composite

Lastly there are Foundation functions to get integral rects. You may want to look at those and experiment with stroke widths. If your drawing edges fall on sub pixel boundaries or sub point boundaries antialiasing may occur mixing colors due to physical limits.

uchuugaka
  • 12,679
  • 6
  • 37
  • 55
  • >> Why stroke twice if you only want one color? – Mikael Hakman Sep 05 '15 at 18:08
  • Why stroke twice if you only want one color? This is just a test code. In reality I'm implementing a charting framework. The user can specify both border color and colors of the axes and a point where the axes cross. First I draw border then i draw axes. If an axis has a different color than the border then it will be drawn in the requested color but only if it does not coincide with the border. If it coincides with the border, which it often will, then the result will be a mixture of border and axis colors. This is not what the user asked for. – Mikael Hakman Sep 05 '15 at 18:21
0

"NSGraphicsContext has a compositingOperation property".

I have tried compositeOperation() before I asked the question here. I get the same results when using CompositeCopy specification. I don't think compositeOperation() affects the results of stroke().

"Many drawing commands in CoreGraphics and the NS AppKit wrappers can also take a compositing operation argument"

I'm not sure that I understand what drawing commands you mean. I use NSBezierPath.stroke() as recommended by Apple.

"Also ensure your view is also at an alpha of 1.0 a non opaque view with an alpha less that 1.0 may composite"

A newly created view has an alpha of 1.0 by default. I do not change it.

"Lastly there are Foundation functions to get integral rects"

I'm using integral rects in the test code above.

Summarizing, the question still remains, how to draw 2 lines on top of each other and get the color of the line drawn last?

Later today I discovered the solution. Uchuugaka was on the right track. It is the anti-aliasing that makes the colors to blend. Even pure horizontal and vertical lines. The solution is to turn the anti-aliasing off, for both lines:

let g = NSGraphicsContext.currentContext ()!

g.saveGraphicsState ()

g.shouldAntialias = false

// Do the drawing

g.restoreGraphicsState ()

Thanks/Mikael

  • Just giving you the tools. CoreGraphics and its NS and UI wrappers aren't hard, just not super documented. Reading the old but still very relevant Quartz book by Apple engineers is worth it. – uchuugaka Sep 11 '15 at 06:07