画像データ(符号なし32bitsカラー)を画面に表示する
この記事は下記の記事の発展です。よってコード差分の説明にとどめますので、先にこちらを読んでいただいたほうがいいと思います。
カラーの画像データは、赤成分が符号なし8bits、緑成分が符号なし8bits、青成分が符号なし8bits、透明度成分が符号なし8bits で管理されます。昔から画像処理業界では「RGBA32bits」データとか「R8G8B8A8」データと略称されます。
下記のコードの解説をします。
59行目、カラーデータを格納するための画像メモリ領域を確保します。RGBAそれぞれ8bitsですから4倍のメモリ量が必要です。
62行目、任意のデータを画像メモリ領域にしこみます。
67行目、CGContext を作成します。bytesPerRow: space: bitmapInfo: の引数がグレー画像のときと違うことに注目してください。
105〜134行目、連続した R8G8B8A8 の UInt8 として画像データをしこんでいます。
import SwiftUI
struct ContentView: View {
// 画像データ.
let DATA_W = 256
let DATA_H = 256
@State var Data000: UnsafeMutablePointer<UInt8>?
// 画像データを画面に表示するためのグッズ.
@State var TheCGContext: CGContext? = nil
@State var TheUIImage: UIImage? = nil
var body: some View {
ZStack{
// 最背面は背景を塗りつぶす.
Rectangle()
.foregroundColor( .gray )
// 中層面で画像を表示する.
if let TheUIImageUnwrapped = TheUIImage {
let ui_img_w = CGFloat( DATA_W )
let ui_img_h = CGFloat( DATA_H )
Image( uiImage: TheUIImageUnwrapped )
.frame( width: ui_img_w, height: ui_img_h )
}
else
{
// TheUIImage が適切に生成されていなければ何も表示しない.
Spacer()
}
// 最前面の画面上部にデバッグ情報を表示する.
VStack{
let scr_w = UIScreen.main.bounds.size.width
let scr_h = UIScreen.main.bounds.size.height
Text( String( format: "ScreenWH is %.1f * %.1f", scr_w, scr_h ))
.foregroundColor( .white )
Text( String( format: "DataWH is %d * %d", DATA_W, DATA_H ))
.foregroundColor( .white )
}
.frame( maxWidth: .infinity, maxHeight: .infinity, alignment: .top )
.padding( .top )
} // ZStack end.
.onAppear{
// メモリを確保する.
let numpix = DATA_W * DATA_H
let alloc_bytes = numpix * 4 // R8G8B8A8.
Data000 = UnsafeMutablePointer<UInt8>.allocate(capacity: alloc_bytes)
// 確保したメモリに、ななめグラデーションデータを仕込む.
SetGradDataBits32( Data000!, DATA_W, DATA_H )
let one_scan_bytes = DATA_W * 4
// ビットマップコンテキストを作成する.
TheCGContext = CGContext(
data: Data000,
width: DATA_W,
height: DATA_H,
bitsPerComponent: 8,
bytesPerRow: one_scan_bytes, // 8bits のときとここが違う.
space: CGColorSpaceCreateDeviceRGB(), // 8bits のときとここが違う.
bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue // 8bits のときとここが違う.
)
if TheCGContext != nil
{
let the_cgimage = TheCGContext!.makeImage()
if let the_cgimage = the_cgimage
{
TheUIImage = UIImage( cgImage: the_cgimage )
}
else
{
// デバッグコンソールに出力する.
print( "the_cgimage is nil.")
}
}
else
{
// デバッグコンソールに出力する.
print( "the_cgcontext is nil." )
}
}
.onDisappear {
// メモリを破棄する.
Data000?.deallocate()
}
} // some View end.
// 画像にデータを仕込むメソッド.
func SetGradDataBits32(
_ data: UnsafeMutablePointer<UInt8>,
_ width: Int,
_ height: Int
)->Void {
let w = width
let h = height
var value: UInt8 = 0
let one_scan_bytes = w * 4
for j in 0 ..< h{
var adrs = ( one_scan_bytes * j )
for i in 0 ..< w{
let tmp = i + j
value = UInt8( tmp % 256 )
data[ adrs ] = 0x00; adrs += 1; // R
data[ adrs ] = 0x00; adrs += 1; // G
data[ adrs ] = value; adrs += 1; // B
data[ adrs ] = 0xff; adrs += 1; // A
}
}
return
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
コードとしては、これで動くのですがデータを処理するメソッドの SetGradDataBits32() の中が、連続した RGBA としてのデータ解釈なのでなんとも煩雑ですね。
たとえば画像データを RGBA をひとまとめとした画素の構造体配列として扱えば、画像処理は下記のコードのように簡単にできるはずです。なんとかならないものでしょうか?
struct STRUCT_RGBA {
var R: UInt8
var G: UInt8
var B: UInt8
var A: UInt8
}
// 画像にデータを仕込むメソッド.
func SetGradDataRGBA(
_ data: UnsafeMutablePointer<STRUCT_RGBA>,
_ width: Int,
_ height: Int
)->Void {
let w = width
let h = height
var value: UInt8 = 0
for j in 0 ..< h{
var adrs = w * j
for i in 0 ..< w{
let tmp = i + j
value = UInt8( tmp % 256 )
data[ adrs ].R = 0x00; // R
data[ adrs ].G = 0x00; // G
data[ adrs ].B = value; // B
data[ adrs ].A = 0xff; // A
adrs += 1
}
}
return
}
なんとかなります!構造体配列として扱い、わかりやすいコードを使うことが可能です。下記の記事でその方法をご紹介します。