]> git.r.bdr.sh - rbdr/patterns/commitdiff
Use CoreGraphics to draw instead of SwifTUI main 2.0.0
authorRuben Beltran del Rio <redacted>
Fri, 28 Apr 2023 20:23:36 +0000 (22:23 +0200)
committerRuben Beltran del Rio <redacted>
Fri, 28 Apr 2023 20:23:36 +0000 (22:23 +0200)
README.md
Sources/Patterns/CGImage+resize.swift [new file with mode: 0644]
Sources/Patterns/PatternView.swift
Sources/Patterns/Tile.swift [deleted file]
Sources/Patterns/TileDesign.swift
Sources/Patterns/TileImage.swift [new file with mode: 0644]

index 1a862f6d8d94f8b3ac678e9cb909346658c18f88..900c4b22340adc2f3900b05ba2e2953345dda983 100644 (file)
--- a/README.md
+++ b/README.md
@@ -97,12 +97,16 @@ PatternView(design: $design)
 * watchOS 8+
 * catalyst 15+
 
-## The Tile view
+## The TileImage struct
 
 If you'd like to do other things with the individual tiles, we also provide the
-Tile view, which is just a single tile.
+TileImage struct, which generates a CGImage.
 
-The tiles support the same properties as `PatternView` with the exception that
-`design` is a `TileDesign` and not a `Binding<TileDesign>`
+The tiles support similar properties as `PatternView` with the exception that
+
+* `design: TileDesign`: **required**, which design to use to tile the frame.
+* `pixelSize: CGFloat`: **defaults to 2.0**, the size of a pixel in the tile.
+* `foregroundColor: CGColor`: **defaults to black**, the foreground color.
+* `backgroundColor: CGColor`: **defaults to white**, the background color.
 
 ![Screenshots of the tiles showing the different overrides](./doc/images/tile_example.png)
diff --git a/Sources/Patterns/CGImage+resize.swift b/Sources/Patterns/CGImage+resize.swift
new file mode 100644 (file)
index 0000000..72ef00e
--- /dev/null
@@ -0,0 +1,19 @@
+import CoreGraphics
+
+extension CGImage {
+    func resize(size:CGSize) -> CGImage? {
+        let width: Int = Int(size.width)
+        let height: Int = Int(size.height)
+
+        let bytesPerPixel = self.bitsPerPixel / self.bitsPerComponent
+        let destBytesPerRow = width * bytesPerPixel
+
+        guard let colorSpace = self.colorSpace else { return nil }
+        guard let context = CGContext(data: nil, width: width, height: height, bitsPerComponent: self.bitsPerComponent, bytesPerRow: destBytesPerRow, space: colorSpace, bitmapInfo: self.alphaInfo.rawValue) else { return nil }
+
+        context.interpolationQuality = .none
+        context.draw(self, in: CGRect(x: 0, y: 0, width: width, height: height))
+
+        return context.makeImage()
+    }
+}
index be78bde419843940ce503c92c9a969ad6cb12dbf..d4ed37b3df8c241f2d1b4eec85e75b105c525d08 100644 (file)
@@ -7,28 +7,31 @@ public struct PatternView: View {
   public var foregroundColor: Color
   public var backgroundColor: Color
   
+  private let image: CGImage
   private var patternSize: CGFloat {
     pixelSize * 8.0;
   }
-  
+
   public init(design: Binding<TileDesign>, pixelSize: CGFloat = 2.0, foregroundColor: Color = .black, backgroundColor: Color = .white) {
     self._design = design
     self.pixelSize = pixelSize
     self.foregroundColor = foregroundColor
     self.backgroundColor = backgroundColor
+    
+    #if os(iOS) || os(watchOS) || os(tvOS)
+      let foregroundCGColor = UIColor(foregroundColor).cgColor
+      let backgroundCGColor = UIColor(backgroundColor).cgColor
+    #else
+      let foregroundCGColor = NSColor(foregroundColor).cgColor
+    let backgroundCGColor = NSColor(backgroundColor).cgColor
+    #endif
+    
+    self.image = TileImage.image(design.wrappedValue, pixelSize: pixelSize, foregroundColor: foregroundCGColor, backgroundColor: backgroundCGColor)
   }
-  
+
   public var body: some View {
     GeometryReader { gr in
-      VStack(spacing: 0) {
-        ForEach(0 ..< 1 + Int(ceil(gr.size.height / patternSize)), id: \.self) { i in
-          HStack(spacing: 0) {
-            ForEach(0 ..< Int(ceil(gr.size.width / patternSize)), id: \.self) { j in
-              Tile(design: design, pixelSize: pixelSize, foregroundColor: foregroundColor, backgroundColor: backgroundColor)
-            }
-          }
-        }
-      }
+      Image(image, scale: 1, label: Text("Test")).resizable(resizingMode: .tile)
     }.drawingGroup()
   }
 }
diff --git a/Sources/Patterns/Tile.swift b/Sources/Patterns/Tile.swift
deleted file mode 100644 (file)
index 9c39640..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-import SwiftUI
-
-public struct Tile: View {
-  
-  public let design: TileDesign
-  public var pixelSize: CGFloat = 2.0;
-  public var foregroundColor: Color = .black
-  public var backgroundColor: Color = .white
-  
-  private var pixels: [Int] {
-    design.pixels()
-  }
-  
-  public init(design: TileDesign, pixelSize: CGFloat = 2.0, foregroundColor: Color = .black, backgroundColor: Color = .white) {
-    self.design = design
-    self.pixelSize = pixelSize
-    self.foregroundColor = foregroundColor
-    self.backgroundColor = backgroundColor
-  }
-  
-    public var body: some View {
-      VStack(spacing: 0) {
-        ForEach(0 ..< 8) { i in
-          HStack(spacing: 0) {
-            ForEach(0 ..< 8) { j in
-              Rectangle()
-                .frame(width: pixelSize, height: pixelSize)
-                .foregroundColor(pixels[(i % 8) * 8 + j % 8] == 0
-                                  ? foregroundColor
-                                  : backgroundColor
-                )
-            }
-          }
-        }
-      }
-    }
-}
-
-struct Tile_Previews: PreviewProvider {
-    static var previews: some View {
-      VStack {
-        Text("Default")
-        Tile(design: .grid)
-        Text("Color override")
-        Tile(design: .balls, foregroundColor: .pink, backgroundColor: .cyan)
-        Text("Pixel size override")
-        Tile(design: .shingles, pixelSize: 8.0)
-      }
-    }
-}
index 55411029486fb2b9f263a141cb57a1217739ff9f..84a8b00ca7a68355fcea191f4cd5a3cfa754525f 100644 (file)
@@ -13,7 +13,7 @@ public enum TileDesign: CaseIterable {
   case rhombus
   case balls
   
-  func pixels() -> [Int] {
+  func pixels() -> [UInt8] {
     switch self {
     case .grid:
       return [
diff --git a/Sources/Patterns/TileImage.swift b/Sources/Patterns/TileImage.swift
new file mode 100644 (file)
index 0000000..4ef84da
--- /dev/null
@@ -0,0 +1,51 @@
+import CoreGraphics
+
+struct TileImage {
+  static func image(_ design: TileDesign, pixelSize: CGFloat, foregroundColor: CGColor? = nil, backgroundColor: CGColor? = nil) -> CGImage {
+    
+    let fg = foregroundColor ?? .init(red: 0, green: 0, blue: 0, alpha: 255)
+    let bg = backgroundColor ?? .init(red: 255, green: 255, blue: 255, alpha: 255)
+    
+    // Convert the array to image data
+    
+    let pixels: [UInt8] = design.pixels().map({ x in
+      var color = bg
+      if x == 0 {
+        color = fg
+      }
+      let components = color.components ?? [0,0,0,1]
+      return components.map({ c in
+        UInt8(round(c * 255))
+      })
+    }).reduce([], +)
+    
+    // Generate the image
+    
+    let pointer = UnsafeMutablePointer<UInt8>.allocate(capacity: pixels.count)
+    pointer.initialize(from: pixels, count: pixels.count)
+    let width = 8.0
+    let height = 8.0
+    let bitsPerComponent = 8
+    let componentCount = 4
+    let bitsPerPixel = bitsPerComponent * componentCount
+    let colorSpace = CGColorSpaceCreateDeviceRGB()
+    
+    let bitmapInfo = CGBitmapInfo([.byteOrderDefault, CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue)])
+    let provider = CGDataProvider(dataInfo: nil, data: pointer, size: Int(width * height * 4)) { _, _, _ in
+      return
+    }!
+    
+    let image = CGImage(
+      width: Int(width),
+      height: Int(height),
+      bitsPerComponent: bitsPerComponent,
+      bitsPerPixel: bitsPerPixel,
+      bytesPerRow: Int(width) * componentCount,
+      space: colorSpace,
+      bitmapInfo: bitmapInfo,
+      provider: provider,
+      decode: nil, shouldInterpolate: false, intent: CGColorRenderingIntent.defaultIntent)!
+    
+    return image.resize(size: CGSize(width: width * pixelSize, height: height * pixelSize)) ?? image
+  }
+}