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

CoreGraphicsでドット絵を描こう

noppefoxwolf
September 11, 2022

 CoreGraphicsでドット絵を描こう

noppefoxwolf

September 11, 2022
Tweet

More Decks by noppefoxwolf

Other Decks in Programming

Transcript

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

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

    ొஃ • ͖ͭͶ͕޷͖ 2
  3. 3

  4. 4

  5. Editormode 5

  6. 6

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

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

  9. 9

  10. 28x28 2bit 10

  11. 48x48 8bit 11

  12. 300x300 10bit 12

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

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

  15. υοτֆΛදࣔ͢Δ // Use nearest magnificationFilter imageView.contentMode = .scaleAspectFit imageView.layer.magnificationFilter =

    .nearest imageView.image = UIImage(named: "watch") // SwiftUI Image(...).interpolation(.none) -ɹը૾ࣗମͷϦαΠζ͸ॏ͘ϝϞϦΛ࿘ අ͢ΔͷͰNG 15
  16. γϯϓϧͳυοτֆΤσΟλΛ࡞Δ 16

  17. 17

  18. 18

  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
  20. άϨʔεέʔϧͷCGContextͷॳظԽ // άϨʔεέʔϧઐ༻ͷCGContextΛੜ੒ let context = CGContext( data: nil, width:

    16, height: 16, bitsPerComponent: 8, bytesPerRow: 1 * 16, space: CGColorSpaceCreateDeviceGray(), bitmapInfo: CGImageAlphaInfo.none.rawValue ) 20
  21. bitsPerComponent • 1,2,4,8 bit͔Β1৭ͷ֊ௐΛࢦఆ͢Δ • 4bit = 16֊ௐ • 8bit

    = 256֊ௐ • RGBͳͲɺෳ਺ͷίϯϙʔωϯτͰ৭Λද͢͜ͱ΋͋Γ·͢ 21
  22. bytesPerRow • ը૾ͷԣҰྻ͕ԿόΠτʹͳΔ͔ • 8bit(1byte)ͳΒ ίϯϙʔωϯτ਺ x ԣͷϐΫηϧ਺ ৭ۭؒ ԣ෯ͷόΠτ਺

    άϨʔ 1 * width = 16 ϑϧΧϥʔRGB 4 * width = 256 22
  23. space • Χϥʔεϖʔε ৭ۭؒ Ϋϥε άϨʔ CGColorSpaceCreateDevic eGray() ϑϧΧϥʔRGB CGColorSpaceCreateDevic

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

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

  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
  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
  28. CoreGraphicsͷ֦ு 28

  29. 29

  30. 30

  31. 31

  32. CGContextʹࣗ࡞ؔ਺Λੜ΍͢ extension CGContext { func fillEllipseLine(in rect: CGRect) { //

    ృΓͭͿ͢ύεΛ࡞Δ let path = makeEllipsePath(in: rect) // ύεΛ௥Ճ addPath(path) // ௥Ճͨ͠ύεΛృΓͭͿ͢ fillPath() } } 32
  33. Bresenham's line algorithm2 • 1962೥ɺIBMͷδϟοΫɾϒϨθϯϋ Ϝ͕։ൃ • ௚ઢ͔Β࠷୹ڑ཭ʹ͋ΔըૉΛฒ΂Δ • ԁΛඳը͢ΔൃలܥͷΞϧΰϦζϜ͕

    ͋Δ • noppefoxwolf/swi,-line-algorithms 2 h$ps:/ /ja.wikipedia.org/wiki/ϒϨθϯϋϜͷΞϧΰϦζϜ 33
  34. CoreGraphicsͷ֦ு2 34

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

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

  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
  38. ϝϞϦϨΠΞ΢τΛ֬ೝ͢Δ lldbͰcgContext.dataͷΞυϨεΛ֬ೝ͢Δ (lldb) frame var -L ctxData 0x000000016d5379e8: (UnsafeMutableRawPointer) ctxData

    = 0x600000c51670 → 0x600000c51670͕CGContextͷdata͕֨ೲ͞Ε͍ͯΔΞυϨ ε 38
  39. ϝϞϦϨΠΞ΢τΛ֬ೝ͢Δ • Debug > Debug Workflow > View Memory •

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

  41. CGContextͷdata͔Β৭ΛऔΓग़͢ struct GrayColor { let gray: UInt8 } // ϙΠϯλ͔Βઌ಄8bitΛGrayColorͱͯ͠औΓग़͢

    let color = data.load(as: GrayColor.self) // 100ϐΫηϧ໨ͷ৭ΛऔΓग़͢ let color = data.load( fromByteOffset: MemoryLayout<GrayColor>.size * 100, as: GrayColor.self ) 41
  42. ృΓ௵͠ • ΩϟϯόεαΠζ΍ృΓͭͿ͠ൣғͷෳࡶ͞ʹΑ͕͔ͬͯ࣌ؒ ͔Δ • ࠷దͳΞϧΰϦζϜͷબఆ • ϝϞϦΛ௚઀ɾॻ͖ࠐΈͯ͠ߴ଎Խ → Swi'ͳΒܕ҆શʹͰ͖Δ

    • noppefoxwolf/PixelArtKit 42
  43. ύϑΥʔϚϯεͷվળ 43

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

  45. ΠϯσοΫεΧϥʔ 1. ը૾Λ256৭ͷάϨʔεέʔϧͰฤू͢ Δ 2. 256৭ͷάϨʔͷ৭ͦΕͧΕʹϑϧΧ ϥʔͷ৭ΛରԠͤͨ͞ΠϯσοΫεΛ ༻ҙ͢Δ 3. ΠϯσοΫεΛݩʹඳը

    • ΩϟϯόεͷϝϞϦ֬อ͕1/4ఔ౓Ͱࡁ Ή • ϨτϩήʔϜͷ2PΧϥʔͷ࢓૊Έ 45
  46. ΠϯσοΫεΧϥʔͷ࣮૷ • CGContextʹ͸ػೳͱͯ͠ଘࡏ͠ͳ͍ ͷͰࣗ࡞ • ࣗલͷCIFilter (CoreImage)Λ࢖ͬͯඳ ըͷ௚લͰண৭͢Δ • CIFilterͷγΣʔμ͸MetalͰ࣮૷

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

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