Swiftでバケツツールの塗りつぶし
ペイントアプリのバケツツールのように、閉じた領域を指定した色で塗りつぶす操作を、Accelerate frameworkのflood fillsを用いて簡単に実現できます
実装
CGImageをvImageに変換し、vImageFloodFill_ARGB8888を使って、塗りつぶしを始める点を含む閉じた領域を指定した色で塗りつぶします
func floodFill(input: CGImage, atPoint point: CGPoint, withColor color: UIColor) -> CGImage {
// 1. 入力画像のフォーマットとバッファの初期化
var format = vImage_CGImageFormat(cgImage: input)!
var inputBuffer = vImage_Buffer()
vImageBuffer_InitWithCGImage(&inputBuffer, &format, nil, input, vImage_Flags(kvImageNoFlags))
defer { inputBuffer.free() }
// 2. 塗りつぶし色の変換と実行
var r: CGFloat = 0, g: CGFloat = 0, b: CGFloat = 0, a: CGFloat = 0
color.getRed(&r, green: &g, blue: &b, alpha: &a)
var fillColor: [UInt8] = [r, g, b, a].map { UInt8($0 * 255) }
vImageFloodFill_ARGB8888(&inputBuffer, nil, vImagePixelCount(point.x), vImagePixelCount(point.y), &fillColor, 8, 0)
// 3. 結果のCGImageの取得
let resPtr = vImageCreateCGImageFromBuffer(&inputBuffer, &format, nil, nil, vImage_Flags(kvImageNoFlags), nil)!
return resPtr.takeRetainedValue()
}
1. 入力画像のフォーマットとバッファの初期化
最初に、CGImageからvImage_Bufferを作成します
deferキーワードを使って、このバッファが関数を抜ける際に解放されるように設定しておきます
CGImageFormatのColor spaceについて
関数名はARGB8888ですが、入力画像がモノクロの場合はモノクロ、アルファ無しのRGB場合はアルファ無しのRGBのように、入力画像のcolor spaceの色でしか塗りつぶすことができませんでした
入力画像のcolor space以外で塗りつぶしたい場合は、フォーマットを変える必要がありそうでした
var format = vImage_CGImageFormat(cgImage: input)!
var inputBuffer = vImage_Buffer()
vImageBuffer_InitWithCGImage(&inputBuffer, &format, nil, input, vImage_Flags(kvImageNoFlags))
defer { inputBuffer.free() }
2. 塗りつぶし色の変換と塗りつぶしの実行
UIColorのrgbaをUInt8の配列に変換し、塗りつぶしを実行します
var r: CGFloat = 0, g: CGFloat = 0, b: CGFloat = 0, a: CGFloat = 0
color.getRed(&r, green: &g, blue: &b, alpha: &a)
var fillColor: [UInt8] = [r, g, b, a].map { UInt8($0 * 255) }
vImageFloodFill_ARGB8888(&inputBuffer, nil, vImagePixelCount(point.x), vImagePixelCount(point.y), &fillColor, 8, 0)
3. 結果のCGImageの取得
最後に、塗りつぶし処理の結果を取得します
vImageCreateCGImageFromBuffer 関数を使用して、vImageバッファから結果のCGImageを生成します
この関数は、処理が完了したら解放される必要のないCGImageを返します
そのため、takeRetainedValue関数を使用して、結果のCGImageを保持し、関数の戻り値として返します
let resPtr = vImageCreateCGImageFromBuffer(&inputBuffer, &format, nil, nil, vImage_Flags(kvImageNoFlags), nil)!
return resPtr.takeRetainedValue()
まとめ
ペイントアプリのバケツツールのように、閉じた領域を指定した色で塗りつぶす操作を、Accelerate frameworkのflood fillsを用いて簡単に実現できました
この関数を使えば、色の塗りつぶし処理を簡単に実現でき、ペイントツールや画像処理で活用できそうです