]> 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+
 
 * 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
 
 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)
 
 ![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
   
   public var foregroundColor: Color
   public var backgroundColor: Color
   
+  private let image: CGImage
   private var patternSize: CGFloat {
     pixelSize * 8.0;
   }
   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
   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
   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()
   }
 }
     }.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
   
   case rhombus
   case balls
   
-  func pixels() -> [Int] {
+  func pixels() -> [UInt8] {
     switch self {
     case .grid:
       return [
     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
+  }
+}