Eric Asked: 2024-11-11 15:57:49 +0800 CST2024-11-11 15:57:49 +0800 CST 2024-11-11 15:57:49 +0800 CST 如何在 SwiftUI 中将点阵滤镜应用于图像 772 我想实现在证券交易所、机场等处的股票行情显示器上看到的点阵样式效果。如何在 swiftui 中将此效果应用于我的图像 struct ContentView: View { var body: some View { Image(.bitcoin) } } 这是我想要的图像效果: swiftui 2 个回答 Voted Benzy Neez 2024-11-11T22:06:13+08:002024-11-11T22:06:13+08:00 我们需要一种方法来检查图像在特定位置的 alpha 值。然后可以在图像不完全透明的位置(alpha > 0)以网格形式绘制点。 可以使用AnImageRenderer来创建图像的位图,然后可以通过这种方式检查单个像素。下面的示例基于SwiftUI - 从图像获取像素颜色的答案,该答案展示了如何获取特定像素位置的颜色值。 这里使用的图像是使用 SF 符号“bitcoinsign”构建的,但您应该能够插入问题中描述的图像: struct ContentView: View { let dotSize: CGFloat = 8 let gapSize: CGFloat = 3 let imageSize: CGFloat = 220 // a multiple of (dotSize + gapsize) private var imageView: some View { Image(systemName: "bitcoinsign") .resizable() .scaledToFit() .frame(width: imageSize, height: imageSize) .fontWeight(.black) } var body: some View { if let cgi = ImageRenderer(content: imageView).cgImage, let dp = cgi.dataProvider, let pixelData = dp.data { Canvas { ctx, size in var path = Path() let stepSize = dotSize + gapSize var y: CGFloat = stepSize / 2 while y < size.height { var x: CGFloat = stepSize / 2 while x < size.width { let pixelInfo: Int = (cgi.bytesPerRow * Int(y)) + Int(x) * 4 if cgi.bitmapInfo.contains(.byteOrderDefault) { let data: UnsafePointer<UInt8> = CFDataGetBytePtr(pixelData) let a = CGFloat(data[pixelInfo + 3]) / CGFloat(255) if a > 0 { path.addEllipse( in: CGRect( x: x - (dotSize / 2), y: y - (dotSize / 2), width: dotSize, height: dotSize ) ) } } x += stepSize } y += stepSize } ctx.fill(path, with: .foreground) } .frame(width: imageSize, height: imageSize) .foregroundStyle(.yellow) } } } 或者,如果可以将图像转换为Path,则可以使用路径函数contains来确定点是否包含在路径内。 如何从图像中获取路径()?的答案提供了一种将 SF 符号转换为路径的方法: struct ContentView: View { let dotSize: CGFloat = 8 let gapSize: CGFloat = 3 var body: some View { // https://stackoverflow.com/a/78360887/20386264 if let cgPath = try? CGPath.contourPath(forSystemName: "bitcoinsign", pointSize: 256, weight: .black) { Canvas { ctx, size in var path = Path() var y: CGFloat = 0 while y < size.height { var x: CGFloat = 0 while x < size.width { if cgPath.contains(CGPoint(x: x, y: y)) { path.addEllipse(in: CGRect(x: x, y: y, width: dotSize, height: dotSize)) } x += dotSize + gapSize } y += dotSize + gapSize } ctx.fill(path, with: .foreground) } .foregroundStyle(.yellow) } } } 此示例在检查哪些点方面采用了一种略显懒惰的方法。它不是检查点中间的点,而是检查点周围框架左上角的点。但结果与之前类似: Best Answer Sweeper 2024-11-11T17:01:08+08:002024-11-11T17:01:08+08:00 像这样的金属着色器可以工作, // put this in a file called Shaders.metal #include <metal_stdlib> using namespace metal; [[ stitchable ]] half4 dotMatrix(float2 position, half4 color) { if (int(position.x) % 6 < 3 && int(position.y) % 6 < 3) { return color; } else { return half4(0, 0, 0, 0); } } 这个想法是将透明颜色放在 或x % 6 >= 3的任何位置。您可以通过更改和y % 6 >= 3来调整点的大小和它们之间的距离。63 SwiftUI 中的用法: Image("some image") .colorEffect(Shader( function: .init(library: .default, name: "dotMatrix"), arguments: [] )) 简单胶囊形状的输出: 为了获得更圆的形状,可以使用如下条件: if ( pow((round(position.x / 10) - position.x / 10), 2) + pow((round(position.y / 10) - position.y / 10), 2) < 0.1 ) 10和0.1是您可以调整的参数。
我们需要一种方法来检查图像在特定位置的 alpha 值。然后可以在图像不完全透明的位置(alpha > 0)以网格形式绘制点。
可以使用An
ImageRenderer
来创建图像的位图,然后可以通过这种方式检查单个像素。下面的示例基于SwiftUI - 从图像获取像素颜色的答案,该答案展示了如何获取特定像素位置的颜色值。这里使用的图像是使用 SF 符号“bitcoinsign”构建的,但您应该能够插入问题中描述的图像:
或者,如果可以将图像转换为
Path
,则可以使用路径函数contains
来确定点是否包含在路径内。如何从图像中获取路径()?的答案提供了一种将 SF 符号转换为路径的方法:
此示例在检查哪些点方面采用了一种略显懒惰的方法。它不是检查点中间的点,而是检查点周围框架左上角的点。但结果与之前类似:
像这样的金属着色器可以工作,
这个想法是将透明颜色放在 或
x % 6 >= 3
的任何位置。您可以通过更改和y % 6 >= 3
来调整点的大小和它们之间的距离。6
3
SwiftUI 中的用法:
简单胶囊形状的输出:
为了获得更圆的形状,可以使用如下条件:
10
和0.1
是您可以调整的参数。