MTLTexture から CGImage を生成

MTLTexture の内容を確認する為に、MTLTexture から CGImage を作りコードとづっと格闘していました。良さげなコードが出来上がったので公開いたします。

そもそも、MTLTexture から バイト列取り出して、CGImage を生成すると、色が何かおかしい CGImage が出来上がりました。Photoshop でチャネルを交換して調査していたら B（青）と R（赤）のチャネルが入れ替わっている事がわかりました。

![rgba-bgra.png](https://qiita-image-store.s3.amazonaws.com/0/65634/097ad176-7c1c-d829-a7aa-b949a21760e8.png)

<font color="silver">（注）上記は Photoshop で加工</font>

コード書いて 1ピクセル毎変換するのも効率が悪いので、いい方法を探していました。CGDataProvider を使って CGImage を作るといいなんて話も Stack Overflow かどこがで見つけましたダメでした。

そもそも、MTKView の段階から、RGBA8Unorm になっていればいいかと思いきや、`mtkView.colorPixelFormat = .rgba8Unorm` や 他の renderPiplelineDescriptor とか全てに「.rgba8Unorm」を設定しても画面は黒以外表示されなくなります。 

さらに調べていると「MTLTexture」に「makeTextureView()」なんてメソッドがあって、見かけ上別のピクセルフォーマットで扱えるなんて書いてあり、試してみると、サポートしてないなんて感じのエラーがログに表示されて（ログのメモ忘れた）、使えそうなのに使えなくてがっかりしていました。

<img width="512" alt="Screen Shot 2016-12-29 at 0.30.09.png" src="https://qiita-image-store.s3.amazonaws.com/0/65634/6e157b6f-52fb-137a-8ce1-c7b684f7d451.png">

で、また検索しながら、そういえば昔誰かが、vImage で変換できると言っていたような気がする事を思い出して、vImage をキーワードに追加して検索。でここに到着。

#### Converting BGRA to ARGB
http://stackoverflow.com/questions/10654909/converting-bgra-to-argb

ちゃんと、macOS でも iOS でも動作を確認しました。そしてついでに上下逆にするのも、vImage で API を見つけたので、それも実装してやっと出来上がりました。

コードは gist からも入手できます。
https://gist.github.com/codelynx/4e56758fb89e94d0d1a58b40ddaade45

```swift

import Foundation
import CoreGraphics
import MetalKit
import GLKit
import Accelerate

extension MTLTexture {

	#if os(iOS)
	typealias XImage = UIImage
	#elseif os(macOS)
	typealias XImage = NSImage
	#endif

	var cgImage: CGImage? {

		assert(self.pixelFormat == .bgra8Unorm)
	
		// read texture as byte array
		let rowBytes = self.width * 4
		let length = rowBytes * self.height
		let bgraBytes = [UInt8](repeating: 0, count: length)
		let region = MTLRegionMake2D(0, 0, self.width, self.height)
		self.getBytes(UnsafeMutableRawPointer(mutating: bgraBytes), bytesPerRow: rowBytes, from: region, mipmapLevel: 0)

		// use Accelerate framework to convert from BGRA to RGBA
		var bgraBuffer = vImage_Buffer(data: UnsafeMutableRawPointer(mutating: bgraBytes),
					height: vImagePixelCount(self.height), width: vImagePixelCount(self.width), rowBytes: rowBytes)
		let rgbaBytes = [UInt8](repeating: 0, count: length)
		var rgbaBuffer = vImage_Buffer(data: UnsafeMutableRawPointer(mutating: rgbaBytes),
					height: vImagePixelCount(self.height), width: vImagePixelCount(self.width), rowBytes: rowBytes)
		let map: [UInt8] = [2, 1, 0, 3]
		vImagePermuteChannels_ARGB8888(&bgraBuffer, &rgbaBuffer, map, 0)

		// flipping image vertically
		let flippedBytes = bgraBytes // share the buffer
		var flippedBuffer = vImage_Buffer(data: UnsafeMutableRawPointer(mutating: flippedBytes),
					height: vImagePixelCount(self.height), width: vImagePixelCount(self.width), rowBytes: rowBytes)
		vImageVerticalReflect_ARGB8888(&rgbaBuffer, &flippedBuffer, 0)

		// create CGImage with RGBA Flipped Bytes
		let colorScape = CGColorSpaceCreateDeviceRGB()
		let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue)
		guard let data = CFDataCreate(nil, flippedBytes, length) else { return nil }
		guard let dataProvider = CGDataProvider(data: data) else { return nil }
		let cgImage = CGImage(width: self.width, height: self.height, bitsPerComponent: 8, bitsPerPixel: 32, bytesPerRow: rowBytes,
					space: colorScape, bitmapInfo: bitmapInfo, provider: dataProvider,
					decode: nil, shouldInterpolate: true, intent: .defaultIntent)
		return cgImage
	}

	var image: XImage? {
		guard let cgImage = self.cgImage else { return nil }
		#if os(iOS)
		return UIImage(cgImage: cgImage)
		#elseif os(macOS)
		return NSImage(cgImage: cgImage, size: CGSize(width: cgImage.width, height: cgImage.height))
		#endif
	}

}
```

［環境に関する表記］

```
Xcode Version 8.2.1 (8C1002)
Apple Swift version 3.0.2 (swiftlang-800.0.63 clang-800.0.42.1)
```