{"id":33,"date":"2019-12-01T02:25:00","date_gmt":"2019-12-01T02:25:00","guid":{"rendered":"http:\/\/gator3303.temp.domains\/~mathaes\/wp\/index.php\/2019\/12\/01\/dev-notebook-brush-like-drawing-in-swift-without-cgpattern\/"},"modified":"2019-12-01T02:25:00","modified_gmt":"2019-12-01T02:25:00","slug":"dev-notebook-brush-like-drawing-in-swift-without-cgpattern","status":"publish","type":"post","link":"https:\/\/mathaesthetics.com\/wp\/dev-notebook-brush-like-drawing-in-swift-without-cgpattern\/","title":{"rendered":"Dev notebook: brush-like drawing in Swift, without CGPattern"},"content":{"rendered":"<p>I&#8217;m spending a lot of time in Cocoa drawing and Core Graphics lately, working in Swift.<\/p>\n<p>The API around the Core Graphics <a href=\"https:\/\/developer.apple.com\/documentation\/coregraphics\/cgpattern\">CGPattern<\/a> object in Swift is a little challenging &#8211; it requires C callbacks and unsafe pointers for basic pattern-creation and drawing functionality. It also doesn&#8217;t work exactly as I&#8217;d like it to; I want to have more of a &#8216;brush&#8217; metaphor where I can vary the &#8216;paint&#8217; flow of the pattern being used to draw a line or a curve. But I definitely want a pattern-like construct that I can apply different stroke and fill colors to in order to draw.<\/p>\n<p>My solution is to use NSImage, which is really convenient because it&#8217;s a high-level object in Swift that can easily be created in memory or loaded from a .png resource in the app bundle.<\/p>\n<p><b>The approach:<\/b> given an NSImage that represents your brush pattern, create a copy NSImage that is rendered in the desired stroke color, then use that copy to draw your desired lines and curves, or tile it along with clipping to fill an area. I&#8217;ll include an example of drawing a line with varying density, and of filling an area with clipping and offset adjustment.<\/p>\n<p><a name='more'><\/a>To give the key ingredient first, let&#8217;s add an extension to NSImage, to enable it make a copy of itself as a mask with a different foreground color applied. Any color can work, and the copied, colorized brush image then doesn&#8217;t rely on context stroke or fill color; we just draw it with normal image drawing operations.<\/p>\n<pre style=\"background-color: #303030; padding: 3px;\"><span style=\"color: #ff80c0;\">import<\/span> AppKit<br \/><br \/><span style=\"color: #ff80c0;\">extension<\/span> <span style=\"color: cyan;\">NSImage<\/span> {<br \/>    <span style=\"color: #ff80c0;\">func<\/span> <span style=\"color: #7080c0;\">copyAsMaskWithFillColor<\/span>(color: <span style=\"color: cyan;\">NSColor<\/span>) -&gt; <span style=\"color: cyan;\">NSImage<\/span> {<br \/>        <span style=\"color: #ff80c0;\">let<\/span> newImage = <span style=\"color: cyan;\">NSImage.init<\/span>(size: <span style=\"color: #ff80c0;\">self<\/span>.<span style=\"color: #7080c0;\">size<\/span>)<br \/>        newImage.<span style=\"color: #7080c0;\">lockFocus<\/span>()<br \/>        <br \/>        <span style=\"color: #ff80c0;\">let<\/span> drawRect = <span style=\"color: cyan;\">CGRect<\/span>(origin: <span style=\"color: cyan;\">CGPoint<\/span>.zero, size: <span style=\"color: #ff80c0;\">self<\/span>.size)<br \/>        <span style=\"color: #ff80c0;\">let<\/span> context = <span style=\"color: cyan;\">NSGraphicsContext<\/span>.<span style=\"color: #7080c0;\">current<\/span><br \/>        context?.<span style=\"color: #7080c0;\">cgContext<\/span>.<span style=\"color: #7080c0;\">clip<\/span>(to: drawRect, <br \/>                             mask: <span style=\"color: #ff80c0;\">self<\/span>.<span style=\"color: #7080c0;\">cgImage<\/span>(forProposedRect: <span style=\"color: #ff80c0;\">nil<\/span>, <br \/>                                                context: <span style=\"color: #ff80c0;\">nil<\/span>, <br \/>                                                hints: <span style=\"color: #ff80c0;\">nil<\/span>)!)<br \/>        color.<span style=\"color: #7080c0;\">setFill<\/span>()<br \/>        context?.<span style=\"color: #7080c0;\">cgContext<\/span>.<span style=\"color: #7080c0;\">fill<\/span>(drawRect)<br \/>        context?.<span style=\"color: #7080c0;\">cgContext<\/span>.<span style=\"color: #7080c0;\">resetClip<\/span>()<br \/>        newImage.<span style=\"color: #7080c0;\">unlockFocus<\/span>()<br \/>        <span style=\"color: #ff80c0;\">return<\/span> newImage<br \/>    }<br \/>}<br \/><\/pre>\n<p>Next, let&#8217;s create a 24&#215;24 pixel NSImage with a pattern that we will use as our brush. I&#8217;m creating a fuzzy gray circle that has higher alpha near the edge and is solid in the center. I&#8217;m doing this in an NSView <code style=\"background-color: #303030;\">draw(_ dirtyRect: NSRect)<\/code> override, but this part could be done earlier, in the initialization of a drawing component.<\/p>\n<pre style=\"background-color: #303030; padding: 3px;\"><span style=\"color: #ff80c0;\">let<\/span> brushImage = <span style=\"color: cyan;\">NSImage<\/span>(size: <span style=\"color: cyan;\">CGSize<\/span>(width: <span style=\"color: yellow;\">24.0<\/span>, height: <span style=\"color: yellow;\">24.0<\/span>))<br \/>brushImage.<span style=\"color: #7080c0;\">lockFocus<\/span>()<br \/><span style=\"color: #ff80c0;\">var<\/span> circleBounds = <span style=\"color: cyan;\">NSRect<\/span>(origin: <span style=\"color: cyan;\">CGPoint<\/span>.<span style=\"color: #7080c0;\">zero<\/span>, <br \/>         size: brushImage.<span style=\"color: #7080c0;\">size<\/span>).<span style=\"color: #7080c0;\">insetBy<\/span>(dx: <span style=\"color: yellow;\">2.0<\/span>, dy: <span style=\"color: yellow;\">2.0<\/span>)<br \/><span style=\"color: #ff80c0;\">var<\/span> alpha = <span style=\"color: cyan;\">CGFloat<\/span>(<span style=\"color: yellow;\">0.1<\/span>)<br \/><span style=\"color: #ff80c0;\">repeat<\/span> {<br \/>    circleBounds = circleBounds.<span style=\"color: #7080c0;\">insetBy<\/span>(dx: <span style=\"color: yellow;\">1.0<\/span>, dy: <span style=\"color: yellow;\">1.0<\/span>)<br \/>    <span style=\"color: cyan;\">NSColor<\/span>(red: <span style=\"color: yellow;\">0.5<\/span>, green: <span style=\"color: yellow;\">0.5<\/span>, blue: <span style=\"color: yellow;\">0.5<\/span>, alpha: alpha).<span style=\"color: #7080c0;\">setFill<\/span>()<br \/>    <span style=\"color: cyan;\">NSGraphicsContext<\/span>.<span style=\"color: #7080c0;\">current<\/span>?.<span style=\"color: #7080c0;\">cgContext<\/span>.<span style=\"color: #7080c0;\">fillEllipse<\/span>(in: circleBounds)<br \/>    alpha += <span style=\"color: yellow;\">0.1<\/span><br \/>} <span style=\"color: #ff80c0;\">while<\/span> (alpha &lt;= <span style=\"color: yellow;\">1.0<\/span>)<br \/>brushImage.<span style=\"color: #7080c0;\">unlockFocus<\/span>()<br \/><\/pre>\n<p>Making color copies of this brush could also be done beforehand, but I&#8217;m going to do it in the drawRect() override and just draw each image once to the screen to see the results:<\/p>\n<pre style=\"background-color: #303030; padding: 3px;\"><span style=\"color: lightgrey; font-style: italic;\">\/\/ draw our brush image in its original form<\/span><br \/><span style=\"color: #ff80c0;\">var<\/span> drawRect = <span style=\"color: cyan;\">NSRect<\/span>(origin: <span style=\"color: cyan;\">CGPoint<\/span>(x:<span style=\"color: yellow;\">36.0<\/span>, y: <span style=\"color: yellow;\">36.0<\/span>), <br \/>                      size: brushImage.<span style=\"color: #7080c0;\">size<\/span>)<br \/>brushImage.<span style=\"color: #7080c0;\">draw<\/span>(in: drawRect)<br \/>        <br \/><span style=\"color: lightgrey; font-style: italic;\">\/\/ draw with orange color<\/span><br \/><span style=\"color: #ff80c0;\">let<\/span> orangeBrush = brushImage.<span style=\"color: #7080c0;\">copyAsMaskWithFillColor<\/span>(color: <span style=\"color: cyan;\">NSColor<\/span>.<span style=\"color: #7080c0;\">orange<\/span>)<br \/>drawRect.<span style=\"color: #7080c0;\">origin<\/span>.<span style=\"color: #7080c0;\">x<\/span> += <span style=\"color: yellow;\">36.0<\/span><br \/>orangeBrush.<span style=\"color: #7080c0;\">draw<\/span>(in: drawRect)<br \/><br \/><span style=\"color: lightgrey; font-style: italic;\">\/\/ draw with with blue color<\/span><br \/><span style=\"color: #ff80c0;\">let<\/span> blueBrush = brushImage.<span style=\"color: #7080c0;\">copyAsMaskWithFillColor<\/span>(color: <span style=\"color: cyan;\">NSColor<\/span>.<span style=\"color: #7080c0;\">blue<\/span>)<br \/>drawRect.<span style=\"color: #7080c0;\">origin<\/span>.<span style=\"color: #7080c0;\">x<\/span> += <span style=\"color: yellow;\">36.0<\/span><br \/>blueBrush.<span style=\"color: #7080c0;\">draw<\/span>(in: drawRect)<br \/><\/pre>\n<p>Voila!<\/p>\n<div style=\"clear: both; text-align: center;\"><a href=\"https:\/\/i0.wp.com\/4.bp.blogspot.com\/-ZfwAs1i-Cjg\/XeMTLbldAoI\/AAAAAAAACY8\/3bCqKc_rCU8AxfmqqYfwU_ANR2lfwaykQCLcBGAsYHQ\/s1600\/Screen%2BShot%2B2019-11-30%2Bat%2B5.10.43%2BPM.png?ssl=1\" style=\"margin-left: 1em; margin-right: 1em;\"><img data-recalc-dims=\"1\" decoding=\"async\" border=\"0\" data-original-height=\"111\" data-original-width=\"186\" src=\"https:\/\/i0.wp.com\/4.bp.blogspot.com\/-ZfwAs1i-Cjg\/XeMTLbldAoI\/AAAAAAAACY8\/3bCqKc_rCU8AxfmqqYfwU_ANR2lfwaykQCLcBGAsYHQ\/s1600\/Screen%2BShot%2B2019-11-30%2Bat%2B5.10.43%2BPM.png?w=1200&#038;ssl=1\" \/><\/a><\/div>\n<p>To draw a line with such a brush, I defined a function that includes a <b><code>density<\/code><\/b> parameter, which controls how often the image is drawn along the line. A density of 1.0 indicates one draw per pixel-length; 2.0 would draw twice per pixel-length, while 0.25 would only draw once every four pixel-lengths. Here&#8217;s the routine: <\/p>\n<pre style=\"background-color: #303030; padding: 3px;\"><br \/><span style=\"color: #ff80c0;\">func<\/span> <span style=\"color: #7080c0;\">imageBrushLine<\/span>(startPt: <span style=\"color: cyan;\">CGPoint<\/span>,<br \/>                    endPt: <span style=\"color: cyan;\">CGPoint<\/span>,<br \/>                    with image: <span style=\"color: cyan;\">NSImage<\/span>,<br \/>                    density: <span style=\"color: cyan;\">CGFloat<\/span> = <span style=\"color: yellow;\">1.0<\/span>) {<br \/>    <span style=\"color: #ff80c0;\">if<\/span> density &lt;= <span style=\"color: yellow;\">0.0<\/span> {<br \/>        <span style=\"color: lightgrey; font-style: italic;\">\/\/ throw exception?<\/span><br \/>        <span style=\"color: #ff80c0;\">return<\/span><br \/>    }<br \/>    <span style=\"color: #ff80c0;\">var<\/span> imageRect = <span style=\"color: cyan;\">CGRect<\/span>.<span style=\"color: #7080c0;\">zero<\/span><br \/>    imageRect.<span style=\"color: #7080c0;\">size<\/span> = image.<span style=\"color: #7080c0;\">size<\/span><br \/>  <br \/>    <span style=\"color: #ff80c0;\">let<\/span> distanceX = endPt.<span style=\"color: #7080c0;\">x<\/span> - startPt.<span style=\"color: #7080c0;\">x<\/span><br \/>    <span style=\"color: #ff80c0;\">let<\/span> distanceY = endPt.<span style=\"color: #7080c0;\">y<\/span> - startPt.<span style=\"color: #7080c0;\">y<\/span><br \/>    <span style=\"color: #ff80c0;\">let<\/span> distanceR = <span style=\"color: #7080c0;\">sqrt<\/span>(distanceX * distanceX + distanceY * distanceY)<br \/><br \/>    <span style=\"color: #ff80c0;\">let<\/span> deltaR = (<span style=\"color: yellow;\">1.0<\/span> \/ density)<br \/>    <span style=\"color: #ff80c0;\">let<\/span> steps = <span style=\"color: #7080c0;\">ceil<\/span>(distanceR \/ deltaR)<br \/>    <span style=\"color: #ff80c0;\">var<\/span> renders : <span style=\"color: cyan;\">CGFloat<\/span> = <span style=\"color: yellow;\">0.0<\/span><br \/>            <br \/>    <span style=\"color: #ff80c0;\">let<\/span> deltaX = distanceX \/ steps<br \/>    <span style=\"color: #ff80c0;\">let<\/span> deltaY = distanceY \/ steps<br \/>        <br \/>    <span style=\"color: #ff80c0;\">var<\/span> currentCenter = startPt<br \/>    <span style=\"color: #ff80c0;\">repeat<\/span> {<br \/>        imageRect.<span style=\"color: #7080c0;\">origin<\/span>.<span style=\"color: #7080c0;\">x<\/span> = currentCenter.<span style=\"color: #7080c0;\">x<\/span> - imageRect.<span style=\"color: #7080c0;\">width<\/span> \/ <span style=\"color: yellow;\">2.0<\/span><br \/>        imageRect.<span style=\"color: #7080c0;\">origin<\/span>.<span style=\"color: #7080c0;\">y<\/span> = currentCenter.<span style=\"color: #7080c0;\">y<\/span> - imageRect.<span style=\"color: #7080c0;\">height<\/span> \/ <span style=\"color: yellow;\">2.0<\/span><br \/><br \/>        image.<span style=\"color: #7080c0;\">draw<\/span>(in: imageRect)<br \/>        currentCenter.<span style=\"color: #7080c0;\">x<\/span> += deltaX<br \/>        currentCenter.<span style=\"color: #7080c0;\">y<\/span> += deltaY<br \/>        renders += <span style=\"color: yellow;\">1.0<\/span><br \/>    } <span style=\"color: #ff80c0;\">while<\/span> (renders &lt;= steps)<br \/>}<br \/><\/pre>\n<p>The lines here were drawn with the function above, and the brush images created previously. There are a variety of densities here, and also some lines drawn with start and end point moving negative in x or y direction or both.<\/p>\n<div style=\"clear: both; text-align: center;\"><a href=\"https:\/\/i0.wp.com\/2.bp.blogspot.com\/-2qS54QBT5KY\/XeMeYUAgRxI\/AAAAAAAACZY\/9fXcH1B83ZUm3xdhkLCsyxPJdbqEQjROACLcBGAsYHQ\/s1600\/Screen%2BShot%2B2019-11-30%2Bat%2B5.56.29%2BPM.png?ssl=1\" style=\"margin-left: 1em; margin-right: 1em;\"><img data-recalc-dims=\"1\" decoding=\"async\" border=\"0\" data-original-height=\"239\" data-original-width=\"302\" src=\"https:\/\/i0.wp.com\/2.bp.blogspot.com\/-2qS54QBT5KY\/XeMeYUAgRxI\/AAAAAAAACZY\/9fXcH1B83ZUm3xdhkLCsyxPJdbqEQjROACLcBGAsYHQ\/s1600\/Screen%2BShot%2B2019-11-30%2Bat%2B5.56.29%2BPM.png?w=1200&#038;ssl=1\" \/><\/a><\/div>\n<p>These brush images may be more suited for strokes than pattern filling, where you&#8217;d typically want a repeating pattern. Nonetheless, the same approach can be used on a fill pattern, to obtain a different color and draw the fill pattern with the desired colorized instance. Here is a sample method that fills a rect with a tiled image; other shapes would just require a different cgContext method and\/or clip region: <\/p>\n<pre style=\"background-color: #303030; padding: 3px;\"><span style=\"color: #ff80c0;\">func<\/span> <span style=\"color: #7080c0;\">fillRectWithImagePattern<\/span>(_ rect: <span style=\"color: cyan;\">CGRect<\/span>, image: <span style=\"color: cyan;\">NSImage<\/span>) {<br \/>    <span style=\"color: #ff80c0;\">let<\/span> gc = <span style=\"color: cyan;\">NSGraphicsContext<\/span>.<span style=\"color: #7080c0;\">current<\/span>!<br \/>    gc.<span style=\"color: #7080c0;\">saveGraphicsState<\/span>()<br \/>    gc.<span style=\"color: #7080c0;\">cgContext<\/span>.<span style=\"color: #7080c0;\">setPatternPhase<\/span>(<span style=\"color: cyan;\">CGSize<\/span>(width: rect.<span style=\"color: #7080c0;\">origin<\/span>.<span style=\"color: #7080c0;\">x<\/span>,<br \/>                                        height: rect.<span style=\"color: #7080c0;\">origin<\/span>.<span style=\"color: #7080c0;\">y<\/span>))<br \/>    <span style=\"color: #ff80c0;\">let<\/span> imgPattern = <span style=\"color: cyan;\">NSColor<\/span>(patternImage: image)<br \/>    imgPattern.<span style=\"color: #7080c0;\">set<\/span>()<br \/>    gc.<span style=\"color: #7080c0;\">cgContext<\/span>.<span style=\"color: #7080c0;\">fill<\/span>(rect)<br \/>    gc.<span style=\"color: #7080c0;\">restoreGraphicsState<\/span>()<br \/>}<br \/><\/pre>\n<p>And the results with our orange brush, also using NSBezierPath to draw a frame around the rectangle: <\/p>\n<div style=\"clear: both; text-align: center;\"><a href=\"https:\/\/i0.wp.com\/3.bp.blogspot.com\/-CTj9lR1lkvU\/XeMki3jPOMI\/AAAAAAAACaA\/_PvKiCCh4W4bVkID9XflRh0QOzkygmICwCLcBGAsYHQ\/s1600\/Screen%2BShot%2B2019-11-30%2Bat%2B6.24.25%2BPM.png?ssl=1\" style=\"margin-left: 1em; margin-right: 1em;\"><img data-recalc-dims=\"1\" decoding=\"async\" border=\"0\" src=\"https:\/\/i0.wp.com\/3.bp.blogspot.com\/-CTj9lR1lkvU\/XeMki3jPOMI\/AAAAAAAACaA\/_PvKiCCh4W4bVkID9XflRh0QOzkygmICwCLcBGAsYHQ\/s1600\/Screen%2BShot%2B2019-11-30%2Bat%2B6.24.25%2BPM.png?w=1200&#038;ssl=1\" data-original-width=\"313\" data-original-height=\"146\" \/><\/a><\/div>\n","protected":false},"excerpt":{"rendered":"<p>I&#8217;m spending a lot of time in Cocoa drawing and Core Graphics lately, working in Swift. The API around the Core Graphics CGPattern object in Swift is a little challenging &#8211; it requires C callbacks and unsafe pointers for basic pattern-creation and drawing functionality. It also doesn&#8217;t work exactly as I&#8217;d like it to; I &#8230; <a title=\"Dev notebook: brush-like drawing in Swift, without CGPattern\" class=\"read-more\" href=\"https:\/\/mathaesthetics.com\/wp\/dev-notebook-brush-like-drawing-in-swift-without-cgpattern\/\">Read more<span class=\"screen-reader-text\">Dev notebook: brush-like drawing in Swift, without CGPattern<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"nf_dc_page":"","_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[57,63,43,39,24,72,46,71,45,30],"tags":[],"class_list":["post-33","post","type-post","status-publish","format-standard","hentry","category-cocoa","category-core-graphics","category-development","category-drawing","category-macosprogrammer","category-nsimage","category-programming","category-quartz2d","category-swift","category-swiftprogramming"],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/mathaesthetics.com\/wp\/wp-json\/wp\/v2\/posts\/33","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/mathaesthetics.com\/wp\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mathaesthetics.com\/wp\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mathaesthetics.com\/wp\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/mathaesthetics.com\/wp\/wp-json\/wp\/v2\/comments?post=33"}],"version-history":[{"count":0,"href":"https:\/\/mathaesthetics.com\/wp\/wp-json\/wp\/v2\/posts\/33\/revisions"}],"wp:attachment":[{"href":"https:\/\/mathaesthetics.com\/wp\/wp-json\/wp\/v2\/media?parent=33"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mathaesthetics.com\/wp\/wp-json\/wp\/v2\/categories?post=33"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mathaesthetics.com\/wp\/wp-json\/wp\/v2\/tags?post=33"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}