画像データ(R8G8B8A8構造体カラー)を画面に表示する

この記事は下記の記事の改善案です。連続した R8,G8,B8,A8,R8,G8,B8,A8,R8,G8,B8,A8.... のデータを (R8G8B8A8),(R8G8B8A8),(R8G8B8A8)... のように構造体として扱い、画像処理のアドレス計算を簡単にしてバグを防ぐ試みです。

画像データ(符号なし32bitsカラー)を画面に表示する

符号なし32bitsカラー画像データを画面に表示する方法を紹介します。

いきなりコード解説にはいります。

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()
    }
}

構造体を使うことでカラー画像処理のアドレス起因のバグは最小限におさえられるはずです。みなさんがんばりましょう!!!