$30 off During Our Annual Pro Sale. View Details »

AppKitでお絵描きをしてみよう / macOS native symposium #06

AppKitでお絵描きをしてみよう / macOS native symposium #06

AppKitにはNSBezierPathやNSGraphicsContextを用いた図形描画の方法が用意されています。今回は図形描画の基本を押さえながら、注意点や雑学をご紹介いたします。汎用性が高い図形描画の世界に足を踏み入れてみましょう。

このスライドはmacOS native Symposium #06で使用されたものです。

Kyome (Takuto Nakamura)

August 21, 2022
Tweet

More Decks by Kyome (Takuto Nakamura)

Other Decks in Programming

Transcript

  1. "QQ,JUͰ͓ֆඳ͖Λͯ͠ΈΑ͏
    Kyome
    macOS native symposium #06

    2019/12/21

    View Slide

  2. ࣗݾ঺հ
    झຯͰNBD04޲͚ΞϓϦΛ։ൃ͍ͯ͠Δֶੜσϕϩούʔ
    དྷ೥౓͔ΒJ04ͷ৬ۀΤϯδχΞʹͳΔ༧ఆ
    Takuto Nakamura
    @Kyomesuke

    View Slide

  3. ࣗݾ঺հ
    RunCat TweetShot ScreenPointer Measures
    ϝχϡʔόʔͰೣΛ૸Βͤ͸͡ΊͨਓͰ͢🐈

    View Slide

  4. 4DSFFO1PJOUFS
    Ϛ΢εΧʔιϧΛϙΠϯλʔ

    ͱ༷ͯ͠ʑʹڧௐදݱͰ͖Δ

    ϓϨθϯςʔγϣϯࢧԉπʔϧ

    View Slide

  5. ࠓճͷτϐοΫ
    AppKitͰͷਤܗඳըख๏
    • macOSͷ࠲ඪܥͷ͓͞Β͍

    • NSBezierPathͰ؆୯ͳਤܗඳը
    • NSGraphicsContextͰҰาઌͷදݱ
    • ίʔυͰਤܗΛඳը͢ΔϝϦοτ

    View Slide

  6. NBD04ͷ࠲ඪܥ
    ref. Apple Inc. "Coordinate system" (2018)
    https://developer.apple.com/library/archive/documentation/General/Conceptual/Devpedia-CocoaApp/CoordinateSystem.html
    iOS macOS

    View Slide

  7. NBD04ͷ࠲ඪܥ
    macOS
    ਖ਼ํ޲ɿ൓࣌ܭճΓ

    View Slide

  8. NBD04Ͱͷਤܗඳը
    • Core Graphics
    - CGPath (CGMutablePath)
    - CGContext
    • AppKit
    - NSBezierPath
    - NSGraphicsContext

    View Slide

  9. NBD04Ͱͷਤܗඳը
    • Core Graphics
    - CGPath (CGMutablePath)
    - CGContext
    • AppKit
    - NSBezierPath
    - NSGraphicsContext
    Կ͕ҧ͏ͷʁʁ😖

    View Slide

  10. NBD04Ͱͷਤܗඳը
    • Core Graphics
    - CGPath (CGMutablePath)
    - CGContext
    • AppKit
    - NSBezierPath
    - NSGraphicsContext
    ͦΕͧΕͷϥούʔʁ
    ͲͪΒ͔ͷํ͕ѻָ͍͕ͱ͍͏͜ͱ΋ͳ͍🤔

    View Slide

  11. NBD04Ͱͷਤܗඳը
    • CGPath (CGMutablePath)
    w ͜Ε୯ମͰ͸7JFXʹ௚઀ඳըͰ͖ͳ͍

    $"4IBQF-BZFSʹQBUIΛ౉͔͢ɼ

    $($POUFYUͷྗΛआΓΔඞཁ͕͋Δ
    w ͭͷ$(1BUI͚ͩͰ͸ଟ৭දݱ͸Ͱ͖ͳ͍

    View Slide

  12. NBD04Ͱͷਤܗඳը
    • CGContext
    w άϥϑΟοΫεͷίϯςΩετΛࢦఆͯ͠ɼ

    ਤܗඳը΍ը૾ͷ݁߹͕Ͱ͖Δ
    w ࠷ڧ💪
    w /4(SBQIJDT$POUFYUʹঌשͯ͠΋Β͏ඞཁ͕͋Δ

    View Slide

  13. NBD04Ͱͷਤܗඳը
    • NSBezierPath
    w /47JFXͷESBXϝιου಺ʹͯ࢖͏͜ͱͰɼ

    ௚઀ਤܗͷඳը໋ྩΛग़ͤΔɹˠखܰʹ࢖͑Δ

    View Slide

  14. NBD04Ͱͷਤܗඳը
    • NSGraphicsContext
    w άϥϑΟοΫεͷίϯςΩετΛ੍ޚͰ͖Δ

    ʢݱࡏͷ$($POUFYUΛঌשͰ͖Δʣ
    w /4#F[JFS1BUIͱڠྗͯ͠खܰʹҰา౿ΈࠐΜͩ

    දݱ͕Մೳ😊

    View Slide

  15. NBD04Ͱͷओཁͳਤܗඳըख๏
    • Core Graphics
    - CGPath (CGMutablePath)
    - CGContext
    • AppKit
    - NSBezierPath
    - NSGraphicsContext
    ࠓճͷओ໾

    View Slide

  16. /4#F[JFS1BUI
    ઢ෼ͷύεͱϕδΣۂઢͷύεΛ·ͱΊͯѻ͑Δ
    ਤܗඳը༻ΦϒδΣΫτ

    View Slide

  17. /4#F[JFS1BUI
    ઢ෼ͷύεͱϕδΣۂઢͷύεΛ·ͱΊͯѻ͑Δ
    ਤܗඳը༻ΦϒδΣΫτ
    ͪΐͬͱ·ͬͯ
    ‼︎

    ϕδΣۂઢͬͯͳʹʁʁ

    View Slide

  18. ϕδΣۂઢ
    /ݸͷ੍ޚ఺͔ΒಘΒΕΔ/࣍ۂઢ
    ੍ޚ఺ͷσʔλ͚ͩͰ׈Β͔ͳۂઢΛඳ͚ΔͷͰศརʂ
    /4#F[JFS1BUI͸੍ޚ఺ݸͷ࣍ۂઢΛѻ͏
    P0
    P1
    P2
    P3

    View Slide

  19. /4#F[JFS1BUI
    class CustomView: NSView {


    override func draw(_ dirtyRect: NSRect) {


    super.draw(dirtyRect)


    let path = NSBezierPath()


    path.move(to: NSPoint(x: 10, y: 10))


    path.line(to: NSPoint(x: 150, y: 120))


    path.curve(to: NSPoint(x: 240, y: 10),


    controlPoint1: NSPoint(x: 225, y: 180),


    controlPoint2: NSPoint(x: 240, y: 165))


    path.close()


    path.stroke()


    }


    }
    ͜Μͳײ͡ˠ
    ͱΓ͋͑ͣඳ͍ͯΈΑ͏ʢجຊʣ

    View Slide

  20. /4#F[JFS1BUI
    class CustomView: NSView {


    override func draw(_ dirtyRect: NSRect) {


    super.draw(dirtyRect)




    }


    }
    εςοϓ͝ͱʹ֬ೝͯ͠ΈΑ͏

    View Slide

  21. /4#F[JFS1BUI
    let path = NSBezierPath()


    path.move(to: NSPoint(x: 10, y: 10))


    εςοϓ͝ͱʹ֬ೝͯ͠ΈΑ͏
    ඳ͖ग़͠ͷ࠲ඪΛҠಈ →
    ඳ͖ग़͠ͷ࠲ඪ
    ⤴︎

    View Slide

  22. /4#F[JFS1BUI
    path.line(to: NSPoint(x: 150, y: 120))
    εςοϓ͝ͱʹ֬ೝͯ͠ΈΑ͏
    ύεʹઢ෼Λ௥Ճ →
    ઢ෼ͷऴ఺ͷ࠲ඪ
    ⤴︎

    View Slide

  23. /4#F[JFS1BUI
    path.curve(to: NSPoint(x: 240, y: 10),


    controlPoint1: NSPoint(x: 225, y: 180),


    controlPoint2: NSPoint(x: 240, y: 165))
    εςοϓ͝ͱʹ֬ೝͯ͠ΈΑ͏
    ύεʹϕδΣۂઢΛ௥Ճ →
    ϕδΣۂઢͷऴ఺ͷ࠲ඪ
    ੍ޚ఺ͷ࠲ඪ

    View Slide

  24. /4#F[JFS1BUI
    path.close()


    path.stroke() // Ξ΢τϥΠϯ


    path.fill() // ృΓͭͿ͠
    ดͨ͡ύε͕׬੒ʂ →
    εςοϓ͝ͱʹ֬ೝͯ͠ΈΑ͏

    View Slide

  25. /4#F[JFS1BUI
    • ઢ෼͸࢝఺ͱऴ఺ͷ2఺ɼϕδΣۂઢ͸4ͭͷ੍ޚ఺

    • ύεʹ௥Ճ͞Εͨ࠷ޙͷ࠲ඪ͕࢝఺ʹͳΔ͜ͱʹ஫ҙ
    ஫ҙ఺⚠
    P0
    P1
    P2
    P3
    P0
    P1
    ύεͷ࠷ޙͷ࠲ඪ ύεͷ࠷ޙͷ࠲ඪ

    View Slide

  26. /4#F[JFS1BUI
    // ௕ํܗ


    path.appendRect(rect:)


    // ପԁ


    path.appendOval(in:)


    // ؙ֯ͷ௕ํܗ


    path.appendRoundedRect(rect:, xRadius:, yRadius:)


    // ހ


    path.appendArc(from:, to:, radius:)


    path.appendArc(withCenter:, radius:, startAngle:, endAngle:)
    ଞʹ΋༷ʑͳύε௥Ճϝιου͕͋Δ

    View Slide

  27. /4#F[JFS1BUI
    path.relativeMove(to: NSPoint.zero)


    path.relativeLine(to: NSPoint.zero)


    path.relativeCurve(to: NSPoint.zero,


    controlPoint1: NSPoint.zero,


    controlPoint2: NSPoint.zero)
    ૬ର࠲ඪΛ࢖ͬͯύεΛ௥ՃՄೳ
    ※ ݱࡏͷύεͷ࠷ऴ఺͔Βͷ૬ର࠲ඪ

    View Slide

  28. /4#F[JFS1BUI
    let face = NSBezierPath(ovalIn: NSRect(x: 60, y: 40,


    width: 80, height: 80))


    let earL = NSBezierPath(ovalIn: NSRect(x: 50, y: 105,


    width: 40, height: 40))


    let earR = NSBezierPath(ovalIn: NSRect(x: 110, y: 105,


    width: 40, height: 40))


    // ύεʹύεΛೖΕΒΕΔ


    face.append(earL)


    face.append(earR)




    face.fill()
    ෳ߹ύεͷ࡞੡

    View Slide

  29. /4#F[JFS1BUI
    // ઢ෯ͷઃఆ


    path.lineWidth = 3


    // ύεͷ୺఺ͷॲཧͷઃఆ


    path.lineCapStyle = .round


    // ύεͷηάϝϯτؒͷॲཧͷઃఆ


    path.lineJoinStyle = .bevel


    // ϚΠλʔݶքͷઃఆ


    path.miterLimit = 5


    // ഁઢͷઃఆ


    path.setLineDash([8.0, 12.0], count: 2, phase: 0)
    ετϩʔΫͷΧελϚΠζ

    View Slide

  30. /4#F[JFS1BUI
    extension NSBezierPath {


    func printPathElement() {


    var points = [CGPoint](repeating: CGPoint.zero, count: 3)


    for i in (0 ..< self.elementCount) {


    switch self.element(at: i, associatedPoints: &points) {


    case .moveTo:


    print("move:", points[0])


    case .lineTo:


    print("line to:", points[0])


    case .curveTo:


    print("curve to:", points[2])


    print("controlPoint1:", points[0])


    print("controlPoint2:", points[1])


    case.closePath:


    print("close")


    @unknown default:


    fatalError()


    }


    }


    }


    }
    ύεͷཁૉΛऔΓग़͢

    View Slide

  31. /4#F[JFS1BUI
    ύεͷཁૉΛऔΓग़͢
    DMPTF
    ͢Δͱɼ಺෦తʹ͸ύεΛดͨ͡ޙʹ

    ऴ఺ʹNPWF
    ͢Δ
    →ดͨ͡ύεͷཁૉΛऔΓग़࣌͢ɼ࠷ޙͷmoveʹ

    ஫ҙ͠ͳ͍ͱɼෳ߹ύεͷ෼ׂ࣌ʹۭͷύε΋

    ༨ܭʹੜ੒ͯ͠͠·͏ɽ

    View Slide

  32. /4#F[JFS1BUI
    جຊతʹดͨ͡ύε͸൓࣌ܭճΓΛ৺͕͚Δ
    ಺ଆ ֎ଆ
    ↙︎
    ύε

    View Slide

  33. /4#F[JFS1BUI
    جຊతʹดͨ͡ύε͸൓࣌ܭճΓΛ৺͕͚Δ
    ಺ଆ
    ֎ଆ
    ֎ଆ
    ಺ଆ

    View Slide

  34. /4#F[JFS1BUI
    جຊతʹดͨ͡ύε͸൓࣌ܭճΓΛ৺͕͚Δ
    let path = NSBezierPath()


    path.appendRect(NSRect(x: 10, y: 10, width: 190, height: 150))


    path.appendOval(in: NSRect(x: 40, y: 30, width: 70, height: 60))


    NSColor.red.setFill()


    path.fill()
    ͜͏͍ͨ͠ ͜͏ͳͬͪΌ͏

    View Slide

  35. /4#F[JFS1BUI
    جຊతʹดͨ͡ύε͸൓࣌ܭճΓΛ৺͕͚Δ
    ಺ଆ ֎ଆ
    ಺ଆ

    View Slide

  36. /4#F[JFS1BUI
    جຊతʹดͨ͡ύε͸൓࣌ܭճΓΛ৺͕͚Δ
    ಺ଆ ֎ଆ
    ֎ଆ

    View Slide

  37. /4#F[JFS1BUI
    جຊతʹดͨ͡ύε͸൓࣌ܭճΓΛ৺͕͚Δ
    let path = NSBezierPath()


    path.appendRect(NSRect(x: 10, y: 10, width: 190, height: 150))


    let ellipse = NSBezierPath()


    ellipse.appendOval(in: NSRect(x: 50, y: 50, width: 80, height: 70))


    // ύεͷਐߦํ޲Λ൓సͤͯ͞௥Ճ͢Δ


    path.append(ellipse.reversed)


    NSColor.red.setFill()


    path.fill()
    ͘Γ͵͖OK

    View Slide

  38. /4#F[JFS1BUI
    ύεͷਐߦํ޲Λߟ͑ͳͯ͘΋ྑ͍ํ๏΋͋Δ
    let path = NSBezierPath()


    // ͓·͡ͳ͍


    path.windingRule = .evenOdd


    path.appendRect(NSRect(x: 10, y: 10, width: 190, height: 150))


    path.appendOval(in: NSRect(x: 50, y: 50, width: 80, height: 70))


    NSColor.red.setFill()


    path.fill()
    ͍͍ײ͡ʹแؚؔ܎Λߟ͑ͯ͘Γൈ͍ͯ͘ΕΔ

    View Slide

  39. /4#F[JFS1BUI
    ΞϑΟϯม׵
    let path = NSBezierPath()


    path.appendRoundedRect(NSRect(x: 0, y: 0, width: 100, height: 100),


    xRadius: 15, yRadius: 15)


    path.transform(using: AffineTransform(translationByX: -50, byY: -50))


    path.transform(using: AffineTransform(scaleByX: 1.2, byY: 1.4))


    path.transform(using: AffineTransform(rotationByRadians: CGFloat.pi / 3))


    path.transform(using: AffineTransform(translationByX: 120, byY: 120))


    path.fill()

    View Slide

  40. /4#F[JFS1BUI
    ֯౓Λѻ͏࣌ͷ஫ҙ఺
    ހ౓๏ ౓਺๏
    π / 3 60°

    View Slide

  41. /4#F[JFS1BUI
    class CustomView: NSView {


    override func draw(_ dirtyRect: NSRect) {


    super.draw(dirtyRect)




    let path = NSBezierPath()


    NSColor.black.set()


    path.windingRule = NSBezierPath.WindingRule.evenOdd


    path.appendRect(self.frame)




    let apple = NSBezierPath()


    // Apple


    apple.move(to: NSPoint(x: 110.89, y: 99.2))


    apple.curve(to: NSPoint(x: 105.97, y: 108.09), controlPoint1: NSPoint(x: 109.5, y: 102.41), controlPoint2: NSPoint(x: 107.87, y: 105.37))


    apple.curve(to: NSPoint(x: 99.64, y: 115.79), controlPoint1: NSPoint(x: 103.39, y: 111.8), controlPoint2: NSPoint(x: 101.27, y: 114.37))


    apple.curve(to: NSPoint(x: 91.5, y: 119.4), controlPoint1: NSPoint(x: 97.11, y: 118.13), controlPoint2: NSPoint(x: 94.4, y: 119.33))


    apple.curve(to: NSPoint(x: 83.99, y: 117.59), controlPoint1: NSPoint(x: 89.42, y: 119.4), controlPoint2: NSPoint(x: 86.91, y: 118.8))


    apple.curve(to: NSPoint(x: 75.9, y: 115.79), controlPoint1: NSPoint(x: 81.06, y: 116.39), controlPoint2: NSPoint(x: 78.36, y: 115.79))


    apple.curve(to: NSPoint(x: 67.58, y: 117.59), controlPoint1: NSPoint(x: 73.31, y: 115.79), controlPoint2: NSPoint(x: 70.54, y: 116.39))


    apple.curve(to: NSPoint(x: 60.39, y: 119.49), controlPoint1: NSPoint(x: 64.61, y: 118.8), controlPoint2: NSPoint(x: 62.21, y: 119.43))


    apple.curve(to: NSPoint(x: 52.07, y: 115.79), controlPoint1: NSPoint(x: 57.6, y: 119.61), controlPoint2: NSPoint(x: 54.83, y: 118.38))


    apple.curve(to: NSPoint(x: 45.44, y: 107.82), controlPoint1: NSPoint(x: 50.3, y: 114.24), controlPoint2: NSPoint(x: 48.09, y: 111.58))


    apple.curve(to: NSPoint(x: 38.44, y: 93.82), controlPoint1: NSPoint(x: 42.6, y: 103.8), controlPoint2: NSPoint(x: 40.27, y: 99.14))


    apple.curve(to: NSPoint(x: 35.5, y: 77.15), controlPoint1: NSPoint(x: 36.48, y: 88.09), controlPoint2: NSPoint(x: 35.5, y: 82.53))


    apple.curve(to: NSPoint(x: 39.48, y: 61.21), controlPoint1: NSPoint(x: 35.5, y: 70.98), controlPoint2: NSPoint(x: 36.82, y: 65.67))


    apple.curve(to: NSPoint(x: 47.8, y: 52.74), controlPoint1: NSPoint(x: 41.56, y: 57.63), controlPoint2: NSPoint(x: 44.33, y: 54.81))


    apple.curve(to: NSPoint(x: 59.06, y: 49.54), controlPoint1: NSPoint(x: 51.27, y: 50.67), controlPoint2: NSPoint(x: 55.02, y: 49.61))


    apple.curve(to: NSPoint(x: 67.76, y: 51.58), controlPoint1: NSPoint(x: 61.27, y: 49.54), controlPoint2: NSPoint(x: 64.16, y: 50.23))


    apple.curve(to: NSPoint(x: 74.67, y: 53.62), controlPoint1: NSPoint(x: 71.35, y: 52.94), controlPoint2: NSPoint(x: 73.66, y: 53.62))


    apple.curve(to: NSPoint(x: 82.33, y: 51.22), controlPoint1: NSPoint(x: 75.42, y: 53.62), controlPoint2: NSPoint(x: 77.98, y: 52.82))


    apple.curve(to: NSPoint(x: 92.73, y: 49.36), controlPoint1: NSPoint(x: 86.43, y: 49.73), controlPoint2: NSPoint(x: 89.9, y: 49.12))


    apple.curve(to: NSPoint(x: 110.05, y: 58.53), controlPoint1: NSPoint(x: 100.43, y: 49.98), controlPoint2: NSPoint(x: 106.2, y: 53.03))


    apple.curve(to: NSPoint(x: 99.83, y: 76.13), controlPoint1: NSPoint(x: 103.17, y: 62.72), controlPoint2: NSPoint(x: 99.77, y: 68.59))


    apple.curve(to: NSPoint(x: 106.17, y: 90.76), controlPoint1: NSPoint(x: 99.89, y: 82), controlPoint2: NSPoint(x: 102.01, y: 86.88))


    apple.curve(to: NSPoint(x: 112.5, y: 94.94), controlPoint1: NSPoint(x: 108.05, y: 92.56), controlPoint2: NSPoint(x: 110.16, y: 93.95))


    apple.curve(to: NSPoint(x: 110.89, y: 99.2), controlPoint1: NSPoint(x: 111.99, y: 96.42), controlPoint2: NSPoint(x: 111.46, y: 97.84))




    // Leaf


    apple.move(to: NSPoint(x: 93.25, y: 29.36))


    apple.curve(to: NSPoint(x: 88.25, y: 42.23), controlPoint1: NSPoint(x: 93.25, y: 33.96), controlPoint2: NSPoint(x: 91.58, y: 38.26))


    apple.curve(to: NSPoint(x: 74.1, y: 49.26), controlPoint1: NSPoint(x: 84.23, y: 46.96), controlPoint2: NSPoint(x: 79.37, y: 49.69))


    apple.curve(to: NSPoint(x: 74, y: 47.52), controlPoint1: NSPoint(x: 74.03, y: 48.71), controlPoint2: NSPoint(x: 74, y: 48.13))


    apple.curve(to: NSPoint(x: 79.3, y: 34.51), controlPoint1: NSPoint(x: 74, y: 43.1), controlPoint2: NSPoint(x: 75.91, y: 38.38))


    apple.curve(to: NSPoint(x: 85.76, y: 29.63), controlPoint1: NSPoint(x: 80.99, y: 32.55), controlPoint2: NSPoint(x: 83.15, y: 30.93))


    apple.curve(to: NSPoint(x: 93.15, y: 27.52), controlPoint1: NSPoint(x: 88.37, y: 28.35), controlPoint2: NSPoint(x: 90.83, y: 27.65))


    apple.curve(to: NSPoint(x: 93.25, y: 29.36), controlPoint1: NSPoint(x: 93.22, y: 28.14), controlPoint2: NSPoint(x: 93.25, y: 28.75))


    apple.line(to: NSPoint(x: 93.25, y: 29.36))




    apple.close()


    apple.transform(using: AffineTransform(scaleByX: 1, byY: -1))


    apple.transform(using: AffineTransform(translationByX: -75, byY: 77))


    apple.transform(using: AffineTransform(scale: 2.0))


    apple.transform(using: AffineTransform(translationByX: frame.midX, byY: frame.midY))


    path.append(apple)


    path.fill()


    }


    }

    View Slide

  42. /4#F[JFS1BUI
    class CustomView: NSView {


    override func draw(_ dirtyRect: NSRect) {


    super.draw(dirtyRect)




    let path = NSBezierPath()


    NSColor.black.set()


    path.windingRule = NSBezierPath.WindingRule.evenOdd


    path.appendRect(self.frame)




    let apple = NSBezierPath()


    // Apple


    apple.move(to: NSPoint(x: 110.89, y: 99.2))


    apple.curve(to: NSPoint(x: 105.97, y: 108.09), controlPoint1: NSPoint(x: 109.5, y: 102.41), controlPoint2: NSPoint(x: 107.87, y: 105.37))


    apple.curve(to: NSPoint(x: 99.64, y: 115.79), controlPoint1: NSPoint(x: 103.39, y: 111.8), controlPoint2: NSPoint(x: 101.27, y: 114.37))


    apple.curve(to: NSPoint(x: 91.5, y: 119.4), controlPoint1: NSPoint(x: 97.11, y: 118.13), controlPoint2: NSPoint(x: 94.4, y: 119.33))


    apple.curve(to: NSPoint(x: 83.99, y: 117.59), controlPoint1: NSPoint(x: 89.42, y: 119.4), controlPoint2: NSPoint(x: 86.91, y: 118.8))


    apple.curve(to: NSPoint(x: 75.9, y: 115.79), controlPoint1: NSPoint(x: 81.06, y: 116.39), controlPoint2: NSPoint(x: 78.36, y: 115.79))


    apple.curve(to: NSPoint(x: 67.58, y: 117.59), controlPoint1: NSPoint(x: 73.31, y: 115.79), controlPoint2: NSPoint(x: 70.54, y: 116.39))


    apple.curve(to: NSPoint(x: 60.39, y: 119.49), controlPoint1: NSPoint(x: 64.61, y: 118.8), controlPoint2: NSPoint(x: 62.21, y: 119.43))


    apple.curve(to: NSPoint(x: 52.07, y: 115.79), controlPoint1: NSPoint(x: 57.6, y: 119.61), controlPoint2: NSPoint(x: 54.83, y: 118.38))


    apple.curve(to: NSPoint(x: 45.44, y: 107.82), controlPoint1: NSPoint(x: 50.3, y: 114.24), controlPoint2: NSPoint(x: 48.09, y: 111.58))


    apple.curve(to: NSPoint(x: 38.44, y: 93.82), controlPoint1: NSPoint(x: 42.6, y: 103.8), controlPoint2: NSPoint(x: 40.27, y: 99.14))


    apple.curve(to: NSPoint(x: 35.5, y: 77.15), controlPoint1: NSPoint(x: 36.48, y: 88.09), controlPoint2: NSPoint(x: 35.5, y: 82.53))


    apple.curve(to: NSPoint(x: 39.48, y: 61.21), controlPoint1: NSPoint(x: 35.5, y: 70.98), controlPoint2: NSPoint(x: 36.82, y: 65.67))


    apple.curve(to: NSPoint(x: 47.8, y: 52.74), controlPoint1: NSPoint(x: 41.56, y: 57.63), controlPoint2: NSPoint(x: 44.33, y: 54.81))


    apple.curve(to: NSPoint(x: 59.06, y: 49.54), controlPoint1: NSPoint(x: 51.27, y: 50.67), controlPoint2: NSPoint(x: 55.02, y: 49.61))


    apple.curve(to: NSPoint(x: 67.76, y: 51.58), controlPoint1: NSPoint(x: 61.27, y: 49.54), controlPoint2: NSPoint(x: 64.16, y: 50.23))


    apple.curve(to: NSPoint(x: 74.67, y: 53.62), controlPoint1: NSPoint(x: 71.35, y: 52.94), controlPoint2: NSPoint(x: 73.66, y: 53.62))


    apple.curve(to: NSPoint(x: 82.33, y: 51.22), controlPoint1: NSPoint(x: 75.42, y: 53.62), controlPoint2: NSPoint(x: 77.98, y: 52.82))


    apple.curve(to: NSPoint(x: 92.73, y: 49.36), controlPoint1: NSPoint(x: 86.43, y: 49.73), controlPoint2: NSPoint(x: 89.9, y: 49.12))


    apple.curve(to: NSPoint(x: 110.05, y: 58.53), controlPoint1: NSPoint(x: 100.43, y: 49.98), controlPoint2: NSPoint(x: 106.2, y: 53.03))


    apple.curve(to: NSPoint(x: 99.83, y: 76.13), controlPoint1: NSPoint(x: 103.17, y: 62.72), controlPoint2: NSPoint(x: 99.77, y: 68.59))


    apple.curve(to: NSPoint(x: 106.17, y: 90.76), controlPoint1: NSPoint(x: 99.89, y: 82), controlPoint2: NSPoint(x: 102.01, y: 86.88))


    apple.curve(to: NSPoint(x: 112.5, y: 94.94), controlPoint1: NSPoint(x: 108.05, y: 92.56), controlPoint2: NSPoint(x: 110.16, y: 93.95))


    apple.curve(to: NSPoint(x: 110.89, y: 99.2), controlPoint1: NSPoint(x: 111.99, y: 96.42), controlPoint2: NSPoint(x: 111.46, y: 97.84))




    // Leaf


    apple.move(to: NSPoint(x: 93.25, y: 29.36))


    apple.curve(to: NSPoint(x: 88.25, y: 42.23), controlPoint1: NSPoint(x: 93.25, y: 33.96), controlPoint2: NSPoint(x: 91.58, y: 38.26))


    apple.curve(to: NSPoint(x: 74.1, y: 49.26), controlPoint1: NSPoint(x: 84.23, y: 46.96), controlPoint2: NSPoint(x: 79.37, y: 49.69))


    apple.curve(to: NSPoint(x: 74, y: 47.52), controlPoint1: NSPoint(x: 74.03, y: 48.71), controlPoint2: NSPoint(x: 74, y: 48.13))


    apple.curve(to: NSPoint(x: 79.3, y: 34.51), controlPoint1: NSPoint(x: 74, y: 43.1), controlPoint2: NSPoint(x: 75.91, y: 38.38))


    apple.curve(to: NSPoint(x: 85.76, y: 29.63), controlPoint1: NSPoint(x: 80.99, y: 32.55), controlPoint2: NSPoint(x: 83.15, y: 30.93))


    apple.curve(to: NSPoint(x: 93.15, y: 27.52), controlPoint1: NSPoint(x: 88.37, y: 28.35), controlPoint2: NSPoint(x: 90.83, y: 27.65))


    apple.curve(to: NSPoint(x: 93.25, y: 29.36), controlPoint1: NSPoint(x: 93.22, y: 28.14), controlPoint2: NSPoint(x: 93.25, y: 28.75))


    apple.line(to: NSPoint(x: 93.25, y: 29.36))




    apple.close()


    apple.transform(using: AffineTransform(scaleByX: 1, byY: -1))


    apple.transform(using: AffineTransform(translationByX: -75, byY: 77))


    apple.transform(using: AffineTransform(scale: 2.0))


    apple.transform(using: AffineTransform(translationByX: frame.midX, byY: frame.midY))


    path.append(apple)


    path.fill()


    }


    }
    ؤுΕ͹͜Μͳ͜ͱ΋Ͱ͖Δʂ

    View Slide

  43. /4#F[JFS1BUI·ͱΊ
    w ઢ෼ͷύεͱϕδΣۂઢͷύεΛ·ͱΊͯ࢖ͬͯ

    ؆୯ͳखॱͰਤܗΛඳըͰ͖Δ
    w ෳ߹ύε΍ετϩʔΫͷΧελϚΠζ͕Ͱ͖Δ
    w ύεͷਐߦํ޲ʹ͸ཁ஫ҙ⚠

    View Slide

  44. /4#F[JFS1BUI·ͱΊ
    w ઢ෼ͷύεͱϕδΣۂઢͷύεΛ·ͱΊͯ࢖ͬͯ

    ؆୯ͳखॱͰਤܗΛඳըͰ͖Δ
    w ෳ߹ύε΍ετϩʔΫͷΧελϚΠζ͕Ͱ͖Δ
    w ύεͷਐߦํ޲ʹ͸ཁ஫ҙ⚠
    ͔͠͠...

    View Slide

  45. 4DSFFO1PJOUFSͰͷ՝୊

    View Slide

  46. 4DSFFO1PJOUFSͰͷ՝୊

    View Slide

  47. /4(SBQIJDT$POUFYU
    άϥϑΟοΫεͷίϯςΩετΛ੍ޚͰ͖Δ
    w ඳըઌ͸8JOEPXͱ*NBHFͷͲͬͪ
    w ృΓͷํ๏͸ͲΜͳ෩ʹ͢Δʁ
    w ӨΛ͚ͭΔʁ
    w จࣈΛϨϯμϦϯά͢Δͱ͖ͷϑΥϯτ͸ʁ
    w ΞϯνΤΠϦΞε͸Ͳ͏͢Δʁ
    w FUD
    ref. Apple Inc. "Graphics Contexts" (2012)
    https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/CocoaDrawingGuide/GraphicsContexts/GraphicsContexts.html

    View Slide

  48. /4(SBQIJDT$POUFYU
    ύεͷॏͶృΓͷํ๏Λ৭ʑม͑ΒΕΔ

    View Slide

  49. /4(SBQIJDT$POUFYU
    ύεͷॏͶృΓͷํ๏Λ৭ʑม͑ΒΕΔ
    source out
    clear exclusion
    source in
    multiply
    hue color burn
    luminosity
    NSCompositingOperation

    View Slide

  50. /4(SBQIJDT$POUFYU
    ύεͷॏͶృΓͷํ๏Λ৭ʑม͑ΒΕΔ
    class CustomView: NSView {


    override func draw(_ dirtyRect: NSRect) {


    super.draw(dirtyRect)


    guard let context = NSGraphicsContext.current else { return }


    context.saveGraphicsState()


    // Լ૚ͷύεΛඳ͘


    let pathA = NSBezierPath()


    // ɾɾɾ


    // ృΓॏͶͷઃఆΛ͢Δ


    context.compositingOperation = NSCompositingOperation.clear


    // ্૚ͷύεΛඳ͘


    let pathB = NSBezierPath()


    // ɾɾɾ


    context.restoreGraphicsState()


    }


    }

    View Slide

  51. /4(SBQIJDT$POUFYU
    ScreenPointerͰ͸clearΛ࢖ͬͯղܾ

    View Slide

  52. ͓·͚ը૾ͰృΓͭͿ͠
    ܧ͗໨ͷͳ͍ύλʔϯΛ༻͍Δ͜ͱͰςΫενϟΛషΕΔ
    let path = NSBezierPath()


    path.appendOval(in: NSRect(x: 10, y: 10, width: 200, height: 200))


    let pattern = NSImage(imageLiteralResourceName: "pattern")


    NSColor(patternImage: pattern).setFill()


    path.fill()

    View Slide

  53. ͓·͚/4(SBEJFOU
    μϝͳྫ
    /4$PMPSDMFBS͸࢖ͬͪΌμϝʂ
    let start = NSColor(red: 0.549, green: 0.757, blue: 0.847, alpha: 1.0)


    let endA = NSColor.clear


    let endB = start.withAlphaComponent(0.0)


    let gradientA = NSGradient(starting: start, ending: endA)


    gradientA?.draw(in: NSRect(x: 5, y: 5, width: 150, height: 150), angle: 90)


    let gradientB = NSGradient(starting: start, ending: endB)


    gradientB?.draw(in: NSRect(x: 160, y: 5, width: 150, height: 150), angle: 90)
    ྑ͍ྫ

    View Slide

  54. ίʔυͰਤܗΛඳը͢ΔϝϦοτ
    • ಈతʹਤܗΛมߋͰ͖Δ
    • ղ૾౓Λؾʹ͠ͳͯ͘ྑ͍

    View Slide

  55. ίʔυͰਤܗΛඳը͢ΔϝϦοτ
    ͜Μͳײ͡ͷΧ΢ϯλʔ͕࡞ΕΔ

    View Slide

  56. ίʔυͰਤܗΛඳը͢ΔϝϦοτ
    αΠζ΍܏͖ͷมߋ͕

    Մೳͳ෼౓ث΋࡞ΕΔ

    View Slide

  57. ίʔυͰਤܗΛඳը͢ΔϝϦοτ
    ͜ΜͳϙοϓΦʔόʔ

    ΋ࣗ࡞Ͱ͖Δ

    View Slide

  58. Thank you!

    View Slide

  59. ͓·͚/41PJOU /44J[F /43FDU
    ͦΕͧΕ$(1PJOUɼ$(4J[Fɼ$(3FDUͷΤΠϦΞε

    View Slide