Add deterministic test image generator
This commit is contained in:
@@ -0,0 +1,129 @@
|
|||||||
|
import CoreGraphics
|
||||||
|
import Foundation
|
||||||
|
import ImageIO
|
||||||
|
import UniformTypeIdentifiers
|
||||||
|
|
||||||
|
/// テスト用の画像を決まった内容で作る。乱数は自前の線形合同法なので毎回同じ絵になる。
|
||||||
|
enum TestImages {
|
||||||
|
static let width = 640
|
||||||
|
static let height = 480
|
||||||
|
|
||||||
|
struct Generator {
|
||||||
|
private var state: UInt64
|
||||||
|
|
||||||
|
init(seed: UInt64) { state = seed &* 2_862_933_555_777_941_757 &+ 3_037_000_493 }
|
||||||
|
|
||||||
|
mutating func next() -> UInt64 {
|
||||||
|
state = state &* 6_364_136_223_846_793_005 &+ 1_442_695_040_888_963_407
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
|
||||||
|
mutating func unit() -> Double {
|
||||||
|
Double(next() >> 11) / Double(1 << 53)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 色のついたブロックと円を敷き詰めた、構造のある絵を作る。
|
||||||
|
static func pattern(seed: UInt64) -> CGImage {
|
||||||
|
var generator = Generator(seed: seed)
|
||||||
|
|
||||||
|
let context = CGContext(
|
||||||
|
data: nil,
|
||||||
|
width: width,
|
||||||
|
height: height,
|
||||||
|
bitsPerComponent: 8,
|
||||||
|
bytesPerRow: 0,
|
||||||
|
space: CGColorSpaceCreateDeviceRGB(),
|
||||||
|
bitmapInfo: CGImageAlphaInfo.premultipliedFirst.rawValue
|
||||||
|
)!
|
||||||
|
|
||||||
|
let columns = 8
|
||||||
|
let rows = 6
|
||||||
|
let cellWidth = CGFloat(width) / CGFloat(columns)
|
||||||
|
let cellHeight = CGFloat(height) / CGFloat(rows)
|
||||||
|
|
||||||
|
for row in 0..<rows {
|
||||||
|
for column in 0..<columns {
|
||||||
|
context.setFillColor(
|
||||||
|
red: generator.unit(),
|
||||||
|
green: generator.unit(),
|
||||||
|
blue: generator.unit(),
|
||||||
|
alpha: 1
|
||||||
|
)
|
||||||
|
context.fill(
|
||||||
|
CGRect(
|
||||||
|
x: CGFloat(column) * cellWidth,
|
||||||
|
y: CGFloat(row) * cellHeight,
|
||||||
|
width: cellWidth,
|
||||||
|
height: cellHeight
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _ in 0..<12 {
|
||||||
|
context.setFillColor(
|
||||||
|
red: generator.unit(),
|
||||||
|
green: generator.unit(),
|
||||||
|
blue: generator.unit(),
|
||||||
|
alpha: 0.85
|
||||||
|
)
|
||||||
|
let radius = 20 + generator.unit() * 60
|
||||||
|
context.fillEllipse(
|
||||||
|
in: CGRect(
|
||||||
|
x: generator.unit() * Double(width),
|
||||||
|
y: generator.unit() * Double(height),
|
||||||
|
width: radius,
|
||||||
|
height: radius
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return context.makeImage()!
|
||||||
|
}
|
||||||
|
|
||||||
|
static func scaled(_ image: CGImage, factor: Double) -> CGImage {
|
||||||
|
let newWidth = max(1, Int(Double(image.width) * factor))
|
||||||
|
let newHeight = max(1, Int(Double(image.height) * factor))
|
||||||
|
|
||||||
|
let context = CGContext(
|
||||||
|
data: nil,
|
||||||
|
width: newWidth,
|
||||||
|
height: newHeight,
|
||||||
|
bitsPerComponent: 8,
|
||||||
|
bytesPerRow: 0,
|
||||||
|
space: CGColorSpaceCreateDeviceRGB(),
|
||||||
|
bitmapInfo: CGImageAlphaInfo.premultipliedFirst.rawValue
|
||||||
|
)!
|
||||||
|
context.interpolationQuality = .high
|
||||||
|
context.draw(image, in: CGRect(x: 0, y: 0, width: newWidth, height: newHeight))
|
||||||
|
return context.makeImage()!
|
||||||
|
}
|
||||||
|
|
||||||
|
static func writeJPEG(_ image: CGImage, to url: URL, quality: Double) throws {
|
||||||
|
guard let destination = CGImageDestinationCreateWithURL(
|
||||||
|
url as CFURL, UTType.jpeg.identifier as CFString, 1, nil
|
||||||
|
) else {
|
||||||
|
throw TestImageError.cannotWrite(url)
|
||||||
|
}
|
||||||
|
CGImageDestinationAddImage(
|
||||||
|
destination,
|
||||||
|
image,
|
||||||
|
[kCGImageDestinationLossyCompressionQuality: quality] as CFDictionary
|
||||||
|
)
|
||||||
|
guard CGImageDestinationFinalize(destination) else {
|
||||||
|
throw TestImageError.cannotWrite(url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static func makeTemporaryDirectory() throws -> URL {
|
||||||
|
let url = URL(fileURLWithPath: NSTemporaryDirectory())
|
||||||
|
.appendingPathComponent("PhotochkTests-\(UUID().uuidString)", isDirectory: true)
|
||||||
|
try FileManager.default.createDirectory(at: url, withIntermediateDirectories: true)
|
||||||
|
return url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum TestImageError: Error {
|
||||||
|
case cannotWrite(URL)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user