Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
OrganizationAdvent CalendarQiitadon (β)
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

MTLTexture から CGImage を生成

More than 3 years have passed since last update.

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

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


(注)上記は Photoshop で加工

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

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


Screen Shot 2016-12-29 at 0.30.09.png

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

Converting BGRA to ARGB

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

コードは gist からも入手できます。

import Foundation
import CoreGraphics
import MetalKit
import GLKit
import Accelerate

extension MTLTexture {

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

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



Xcode Version 8.2.1 (8C1002)
Apple Swift version 3.0.2 (swiftlang-800.0.63 clang-800.0.42.1)
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away


No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
Help us understand the problem. What is going on with this article?