画像データ(R8G8B8A8構造体カラー)を画面に表示する
この記事は下記の記事の改善案です。連続した R8,G8,B8,A8,R8,G8,B8,A8,R8,G8,B8,A8.... のデータを (R8G8B8A8),(R8G8B8A8),(R8G8B8A8)... のように構造体として扱い、画像処理のアドレス計算を簡単にしてバグを防ぐ試みです。
いきなりコード解説にはいります。
3〜8行目、画像データの1画素にあたる構造体を定義しています。
68行目、画素数の4倍のデータをメモリ確保します。
71行目、bindMemory をつかって、連続したメモリ領域を4バイトごとの構造体として解釈するようにします。
80行目、CGContext の生成において data: は連続したメモリ領域の先頭アドレスを示せばいいようです。コメントに書いてあるとおり DataRGBA でも Buffer でもどちらでもいいです。
import SwiftUI
struct STRUCT_RGBA {
var R: UInt8
var G: UInt8
var B: UInt8
var A: UInt8
}
struct ContentView: View {
// 画像データ.
let DATA_W = 256
let DATA_H = 256
@State var Buffer: UnsafeMutableRawPointer?
@State var DataRGBA: UnsafeMutablePointer<STRUCT_RGBA>?
// 画像データを画面に表示するためのグッズ.
@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 size = MemoryLayout<STRUCT_RGBA>.stride * numpix
let align = MemoryLayout<STRUCT_RGBA>.alignment
Buffer = UnsafeMutableRawPointer.allocate( byteCount: size, alignment: align )
// 連続メモリをRGBAの構造体の並びとして解釈する.
DataRGBA = Buffer?.bindMemory( to: STRUCT_RGBA.self, capacity: numpix )
// 確保したメモリに、ななめグラデーションデータを仕込む.
SetGradDataRGBA( DataRGBA!, DATA_W, DATA_H )
let one_scan_bytes = DATA_W * 4
// ビットマップコンテキストを作成する.
TheCGContext = CGContext(
data: DataRGBA, // ここは data: Buffer, としても同じ動作になる.
width: DATA_W,
height: DATA_H,
bitsPerComponent: 8,
bytesPerRow: one_scan_bytes,
space: CGColorSpaceCreateDeviceRGB(),
bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue
)
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 {
// メモリを破棄する.
Buffer?.deallocate()
}
} // some View end.
// 画像にデータを仕込むメソッド.
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
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
構造体を使うことでカラー画像処理のアドレス起因のバグは最小限におさえられるはずです。みなさんがんばりましょう!!!