🦋
SwiftUI: CIFilterで絵文字をスタンプ風に表現してみた
サンプル
用意するもの
- スタンプのベースになる画像
stamp.frame - ラベル部分をくり抜くための画像
stamp.text.frame
コード
CIFilter
をたくさん使って欲しい画像を組み立てます。
import SwiftUI
import CoreImage.CIFilterBuiltins
struct PlaygroundView: View {
var body: some View {
convertToStamp(emoji: "🛠️", label: "開発")
.renderingMode(.template) // 好きな色で塗りつぶすのに必要
.resizable()
.frame(width: 160, height: 160)
.foregroundColor(Color.red)
}
func convertToStamp(emoji: String, label: String) -> Image {
let dummy = Image(systemName: "questionmark.circle.fill")
let stampSize = CGSize(width: 320, height: 320)
// 絵文字を画像として出力(勝手に白黒になった)
let textFilter = CIFilter.textImageGenerator()
textFilter.text = emoji
textFilter.fontSize = 150
textFilter.scaleFactor = 1.0
guard let output = textFilter.outputImage else { return dummy }
// 絵文字をスタンプの中心になるようにずらず
var textSize = output.extent.size
var textAt = CGPoint(x: 0.5 * (stampSize.width - textSize.width),
y: 0.5 * (stampSize.height - textSize.height))
let output2 = output.transformed(by: CGAffineTransform(translationX: textAt.x, y: textAt.y))
// モノクロ絵文字の濃淡が淡いので濃くする
let exposureFilter = CIFilter.exposureAdjust()
exposureFilter.inputImage = output2
exposureFilter.ev = -0.5
guard let output3 = exposureFilter.outputImage else { return dummy }
// スタンプのベースになる画像と組み合わせる
let sourceOverFilter = CIFilter.sourceOverCompositing()
sourceOverFilter.inputImage = output3
sourceOverFilter.backgroundImage = CIImage(image: UIImage(named: "stamp.frame")!)
guard let output4 = sourceOverFilter.outputImage else { return dummy }
// ラベル部分をくり抜く
let sourceOutFilter = CIFilter.sourceOutCompositing()
sourceOutFilter.inputImage = output4
sourceOutFilter.backgroundImage = CIImage(image: UIImage(named: "stamp.text.frame")!)
guard let output5 = sourceOutFilter.outputImage else { return dummy }
// ラベルの文字列を画像として出力
textFilter.text = label
textFilter.fontSize = 30
textFilter.scaleFactor = 1.0
guard let output6 = textFilter.outputImage else { return dummy }
// ラベルの位置を調整
textSize = output6.extent.size
let ratio: CGFloat = min(1.0, 200 / textSize.width)
textAt = CGPoint(x: 0.5 * (stampSize.width - ratio * textSize.width), y: 52)
let output7 = output6
.transformed(by: CGAffineTransform(scaleX: ratio, y: 1))
.transformed(by: CGAffineTransform(translationX: textAt.x, y: textAt.y))
// スタンプとラベルを組み合わせる
sourceOverFilter.inputImage = output7
sourceOverFilter.backgroundImage = output5
guard let output8 = sourceOverFilter.outputImage else { return dummy }
// 白黒反転する
let invertFilter = CIFilter.colorInvert()
invertFilter.inputImage = output8
guard let output9 = invertFilter.outputImage else { return dummy }
// 黒抜きをする(白抜きだったら白黒反転しなくてもいいのに)
let alphaFilter = CIFilter.maskToAlpha()
alphaFilter.inputImage = output9
guard let output10 = alphaFilter.outputImage else { return dummy }
// CIImageをImageに変換する
guard let cgImage = CIContext().createCGImage(output10, from: output10.extent) else { return dummy }
return Image(cgImage, scale: 1.0, label: Text(emoji))
}
}
スタンプ風絵文字の出来上がり!
Discussion