Upgrade to Pro — share decks privately, control downloads, hide ads and more …

CoreGraphicsでドット絵を描こう/iOSDC22

noppefoxwolf
September 11, 2022

 CoreGraphicsでドット絵を描こう/iOSDC22

noppefoxwolf

September 11, 2022
Tweet

More Decks by noppefoxwolf

Other Decks in Programming

Transcript

  1. CoreGraphicsͰυοτֆΛඳ͜͏
    Track C ϨΪϡϥʔτʔΫʢ20෼ʣ
    1

    View Slide

  2. noppe
    • Twi%er: noppefoxwolf
    • גࣜձࣾσΟʔɾΤψɾΤʔ
    • ݸਓΞϓϦ։ൃऀ
    • iOSDC18~21 ొஃ
    • ͖ͭͶ͕޷͖
    2

    View Slide

  3. 3

    View Slide

  4. 4

    View Slide

  5. Editormode
    5

    View Slide

  6. 6

    View Slide

  7. Agenda
    1. υοτֆΤσΟλͱ͸
    2. γϯϓϧͳυοτֆΤσΟλΛ࡞Δ
    3. CoreGraphicsͷ֦ு
    4. ύϑΥʔϚϯεͷվળ
    7

    View Slide

  8. υοτֆΤσΟλͱ͸
    8

    View Slide

  9. 9

    View Slide

  10. 28x28 2bit
    10

    View Slide

  11. 48x48 8bit
    11

    View Slide

  12. 300x300 10bit
    12

    View Slide

  13. υοτֆΛදࣔ͢Δ
    imageView.contentMode = .center
    imageView.image = UIImage(named: "watch")
    13

    View Slide

  14. υοτֆΛදࣔ͢Δ
    imageView.contentMode = .scaleAspectFit
    imageView.image = UIImage(named: "watch")
    14

    View Slide

  15. υοτֆΛදࣔ͢Δ
    // Use nearest magnificationFilter
    imageView.contentMode = .scaleAspectFit
    imageView.layer.magnificationFilter = .nearest
    imageView.image = UIImage(named: "watch")
    // SwiftUI
    Image(...).interpolation(.none)
    -ɹը૾ࣗମͷϦαΠζ͸ॏ͘ϝϞϦΛ࿘
    අ͢ΔͷͰNG
    15

    View Slide

  16. γϯϓϧͳυοτֆΤσΟλΛ࡞Δ
    16

    View Slide

  17. 17

    View Slide

  18. 18

    View Slide

  19. υοτֆΤσΟλͷϫʔΫϑ
    ϩʔ
    let imageView = UIImageView()
    let context = CGContext()
    func setup() {
    imageView.onTap = { point in // 1
    context.fill(point) // 2
    let image = context.makeImage() // 3
    imageView.image = image // 4
    }
    }
    19

    View Slide

  20. άϨʔεέʔϧͷCGContextͷॳظԽ
    // άϨʔεέʔϧઐ༻ͷCGContextΛੜ੒
    let context = CGContext(
    data: nil,
    width: 16,
    height: 16,
    bitsPerComponent: 8,
    bytesPerRow: 1 * 16,
    space: CGColorSpaceCreateDeviceGray(),
    bitmapInfo: CGImageAlphaInfo.none.rawValue
    )
    20

    View Slide

  21. bitsPerComponent
    • 1,2,4,8 bit͔Β1৭ͷ֊ௐΛࢦఆ͢Δ
    • 4bit = 16֊ௐ
    • 8bit = 256֊ௐ
    • RGBͳͲɺෳ਺ͷίϯϙʔωϯτͰ৭Λද͢͜ͱ΋͋Γ·͢
    21

    View Slide

  22. bytesPerRow
    • ը૾ͷԣҰྻ͕ԿόΠτʹͳΔ͔
    • 8bit(1byte)ͳΒ ίϯϙʔωϯτ਺ x ԣͷϐΫηϧ਺
    ৭ۭؒ ԣ෯ͷόΠτ਺
    άϨʔ 1 * width = 16
    ϑϧΧϥʔRGB 4 * width = 256
    22

    View Slide

  23. space
    • Χϥʔεϖʔε
    ৭ۭؒ Ϋϥε
    άϨʔ CGColorSpaceCreateDevic
    eGray()
    ϑϧΧϥʔRGB CGColorSpaceCreateDevic
    eRGB()
    23

    View Slide

  24. bitmapInfo
    • ΞϧϑΝνϟϯωϧͷ৔ॴ | όΠτΦʔμʔ | ϑΥʔϚοτɹͷ
    ϏοτϚεΫ

    • CGImageAlphaInfo.noneSkipLast.rawValue |
    CGImageByteOrderInfo.order32LiCle.rawValue |
    CGImagePixelFormatInfo.packed.rawValue
    • CGImageAlphaInfo.none.rawValue
    24

    View Slide

  25. 25

    View Slide

  26. contextͷը૾Λඳը͢Δ
    let cgImage: CGImage = context.makeImage()!
    let uiImage: UIImage = UIImage(
    cgImage: cgImage,
    scale: 1,
    orientation: .downMirrored // UIImageͷ࠲ඪܥʹม׵
    )
    // UIKit
    imageView.image = uiImage
    // SwiftUI
    Image(uiImage: uiImage)
    26

    View Slide

  27. ৭ͷృΓͭͿ͠
    let color = CGColor(gray: 0, alpha: 1)
    context.setFillColor(color)
    let path = CGPath(
    rect: CGRect(origin: point, size: CGSize(width: 1, height: 1))
    )
    context.addPath(path) // Pathͷ௥Ճ
    context.fillPath()
    27

    View Slide

  28. CoreGraphicsͷ֦ு
    28

    View Slide

  29. 29

    View Slide

  30. 30

    View Slide

  31. 31

    View Slide

  32. CGContextʹࣗ࡞ؔ਺Λੜ΍͢
    extension CGContext {
    func fillEllipseLine(in rect: CGRect) {
    // ృΓͭͿ͢ύεΛ࡞Δ
    let path = makeEllipsePath(in: rect)
    // ύεΛ௥Ճ
    addPath(path)
    // ௥Ճͨ͠ύεΛృΓͭͿ͢
    fillPath()
    }
    }
    32

    View Slide

  33. Bresenham's line algorithm2
    • 1962೥ɺIBMͷδϟοΫɾϒϨθϯϋ
    Ϝ͕։ൃ
    • ௚ઢ͔Β࠷୹ڑ཭ʹ͋ΔըૉΛฒ΂Δ
    • ԁΛඳը͢ΔൃలܥͷΞϧΰϦζϜ͕
    ͋Δ
    • noppefoxwolf/swi,-line-algorithms
    2 h$ps:/
    /ja.wikipedia.org/wiki/ϒϨθϯϋϜͷΞϧΰϦζϜ
    33

    View Slide

  34. CoreGraphicsͷ֦ு2
    34

    View Slide

  35. όέππʔϧ
    • ด͡ΒΕͨൣғ಺ΛృΓͭͿ͢ػೳ
    • Flood Fillͱ͍͏ΞϧΰϦζϜ
    • બ୒ͨ͠৭Λಉ͡৭͔Ͳ͏͔Λ୳͠ͳ
    ͕Β৭Λஔ͖׵͍͑ͯ͘
    → CGContext͔Β৭Λऔಘ͢Δඞཁ͕
    ͋Δ
    35

    View Slide

  36. CGContext͔Β৭ΛऔΓग़͢
    • ಛఆͷ࠲ඪͷ৭Λऔಘ͢ΔAPI͸ͳ͍
    • cgContext.data͔Βը૾͕֨ೲ͞Ε͍ͯΔϙΠϯλΛ஌ΕΔ
    • ϝϞϦϨΠΞ΢τΛ֬ೝ͠ɺ೚ҙͷ࠲ඪͷ৭ΛऔΓग़͢APIΛ
    ࡞Δ
    36

    View Slide

  37. ϝϞϦϨΠΞ΢τΛ֬ೝ͢Δ
    2x2ͷCGContextΛ
    ৭ ɹ৭ʢHexʣɹ Ґஔ
    17.0 / 255.0 0x11 ɹࠨԼ
    34.0 / 255.0 0x22 ɹӈԼ
    51.0 / 255.0 0x33 ɹࠨ্
    68.0 / 255.0 0x44 ɹӈ্
    ͷάϨʔ஋̐৭Ͱண৭
    37

    View Slide

  38. ϝϞϦϨΠΞ΢τΛ֬ೝ͢Δ
    lldbͰcgContext.dataͷΞυϨεΛ֬ೝ͢Δ
    (lldb) frame var -L ctxData
    0x000000016d5379e8: (UnsafeMutableRawPointer) ctxData = 0x600000c51670
    → 0x600000c51670͕CGContextͷdata͕֨ೲ͞Ε͍ͯΔΞυϨ
    ε
    38

    View Slide

  39. ϝϞϦϨΠΞ΢τΛ֬ೝ͢Δ
    • Debug > Debug Workflow > View
    Memory
    • ϝϞϦΞυϨεΛೖྗ͢Δ
    39

    View Slide

  40. άϨʔεέʔϧͷϝϞϦϨΠΞ΢τ
    • ࠨԼ͔Βӈ্ʹ͔͚ͯॱ൪ʹ഑ஔ͞Ε͍ͯΔ
    • 1υοτ1byteͰ4υοτ෼ฒΜͰ͍Δ
    40

    View Slide

  41. CGContextͷdata͔Β৭ΛऔΓग़͢
    struct GrayColor {
    let gray: UInt8
    }
    // ϙΠϯλ͔Βઌ಄8bitΛGrayColorͱͯ͠औΓग़͢
    let color = data.load(as: GrayColor.self)
    // 100ϐΫηϧ໨ͷ৭ΛऔΓग़͢
    let color = data.load(
    fromByteOffset: MemoryLayout.size * 100,
    as: GrayColor.self
    )
    41

    View Slide

  42. ృΓ௵͠
    • ΩϟϯόεαΠζ΍ృΓͭͿ͠ൣғͷෳࡶ͞ʹΑ͕͔ͬͯ࣌ؒ
    ͔Δ
    • ࠷దͳΞϧΰϦζϜͷબఆ
    • ϝϞϦΛ௚઀ɾॻ͖ࠐΈͯ͠ߴ଎Խ
    → Swi'ͳΒܕ҆શʹͰ͖Δ
    • noppefoxwolf/PixelArtKit
    42

    View Slide

  43. ύϑΥʔϚϯεͷվળ
    43

    View Slide

  44. ύϑΥʔϚϯεͷվળ
    • ϝϞϦͷফඅΛ཈͑Δ޻෉Λͯ͠Έ·͢
    • ৗʹτϨʔυΦϑ͕͋Δ
    • Կ͕τϨʔυΦϑʹͳΔͷ͔Λߟ͑ͯΈ·͠ΐ͏
    44

    View Slide

  45. ΠϯσοΫεΧϥʔ
    1. ը૾Λ256৭ͷάϨʔεέʔϧͰฤू͢
    Δ
    2. 256৭ͷάϨʔͷ৭ͦΕͧΕʹϑϧΧ
    ϥʔͷ৭ΛରԠͤͨ͞ΠϯσοΫεΛ
    ༻ҙ͢Δ
    3. ΠϯσοΫεΛݩʹඳը
    • ΩϟϯόεͷϝϞϦ֬อ͕1/4ఔ౓Ͱࡁ
    Ή
    • ϨτϩήʔϜͷ2PΧϥʔͷ࢓૊Έ
    45

    View Slide

  46. ΠϯσοΫεΧϥʔͷ࣮૷
    • CGContextʹ͸ػೳͱͯ͠ଘࡏ͠ͳ͍
    ͷͰࣗ࡞
    • ࣗલͷCIFilter (CoreImage)Λ࢖ͬͯඳ
    ըͷ௚લͰண৭͢Δ
    • CIFilterͷγΣʔμ͸MetalͰ࣮૷
    46

    View Slide

  47. ΠϯσοΫεΧϥʔϑΟϧλͷ࣮૷
    float4 lookupTable(sampler src, sampler lut) {
    float2 pos = src.coord();
    float4 pixelColor = src.sample(pos);
    int index = pixelColor.r * 255;
    int x = index % int(lut.size().x);
    int y = index / int(lut.size().y);
    float2 lutPos = float2(x, y);
    float4 lutColor = lut.sample(lutPos);
    return lutColor;
    }
    • noppefoxwolf/PixelArtKit
    47

    View Slide

  48. ΠϯσοΫεΧϥʔͷτϨʔυΦϑ
    • ৭਺੍͕ݶ͞ΕΔ
    • ը૾ͷϖʔετ͕೉͍͠
    • άϥσʔγϣϯʹऑ͘ͳΔ
    • ൣғબ୒ͷᮢ஋Λѻ͍ʹ͍͘
    48

    View Slide

  49. Recap
    1. υοτֆΤσΟλ͸CoreGraphicsͱUIKit(Swi5UI)Ͱ࣮૷Ͱ͖Δ
    2. CGContext͸CGPathΛ࢖͑͹ࣗ༝ͳඳըؔ਺Λ࣮૷Ͱ͖Δ
    3. CGContext͸ϝϞϦΛ௚઀ࢀরͰ͖Δ
    4. ϒϨθϯϋϜ΍ΠϯσοΫεΧϥʔͳͲͷ஌ݟ͸ࠓ΋׆͔ͤΔ
    49

    View Slide