MTLPixelFormat+Extensions.swift 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  1. /******************************************************************************
  2. Copyright (C) 2024 by Patrick Heyer <[email protected]>
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published by
  5. the Free Software Foundation, either version 2 of the License, or
  6. (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. ******************************************************************************/
  14. import CoreGraphics
  15. import CoreVideo
  16. import Foundation
  17. import Metal
  18. extension MTLPixelFormat {
  19. /// Property to check whether the pixel format is an 8-bit format
  20. var is8Bit: Bool {
  21. switch self {
  22. case .a8Unorm, .r8Unorm, .r8Snorm, .r8Uint, .r8Sint:
  23. return true
  24. case .r8Unorm_srgb:
  25. return true
  26. default:
  27. return false
  28. }
  29. }
  30. /// Property to check whether the pixel format is a 16-bit format
  31. var is16Bit: Bool {
  32. switch self {
  33. case .r16Unorm, .r16Snorm, .r16Uint, .r16Sint:
  34. return true
  35. case .rg8Unorm, .rg8Snorm, .rg8Uint, .rg8Sint:
  36. return true
  37. case .rg16Float:
  38. return true
  39. case .rg8Unorm_srgb:
  40. return true
  41. default:
  42. return false
  43. }
  44. }
  45. /// Property to check whether the pixel format is a packed 16-bit format
  46. var isPacked16Bit: Bool {
  47. switch self {
  48. case .b5g6r5Unorm, .a1bgr5Unorm, .abgr4Unorm, .bgr5A1Unorm:
  49. return true
  50. default:
  51. return false
  52. }
  53. }
  54. /// Property to check whether the pixel format is a 32-bit format
  55. var is32Bit: Bool {
  56. switch self {
  57. case .r32Uint, .r32Sint:
  58. return true
  59. case .r32Float:
  60. return true
  61. case .rg16Unorm, .rg16Snorm, .rg16Uint, .rg16Sint:
  62. return true
  63. case .rg16Float:
  64. return true
  65. case .rgba8Unorm, .rgba8Snorm, .rgba8Uint, .rgba8Sint, .bgra8Unorm:
  66. return true
  67. case .rgba8Unorm_srgb, .bgra8Unorm_srgb:
  68. return true
  69. default:
  70. return false
  71. }
  72. }
  73. /// Property to check whether the pixel format is a packed 32-bit format
  74. var isPacked32Bit: Bool {
  75. switch self {
  76. case .rgb10a2Unorm, .rgb10a2Uint, .bgr10a2Unorm:
  77. return true
  78. case .rg11b10Float:
  79. return true
  80. case .rgb9e5Float:
  81. return true
  82. case .bgr10_xr, .bgr10_xr_srgb:
  83. return true
  84. default:
  85. return false
  86. }
  87. }
  88. /// Property to check whether the pixel format is a 64-bit format
  89. var is64Bit: Bool {
  90. switch self {
  91. case .rg32Uint, .rg32Sint:
  92. return true
  93. case .rg32Float:
  94. return true
  95. case .rgba16Unorm, .rgba16Snorm, .rgba16Uint, .rgba16Sint:
  96. return true
  97. case .rgba16Float:
  98. return true
  99. case .bgra10_xr, .bgra10_xr_srgb:
  100. return true
  101. default:
  102. return false
  103. }
  104. }
  105. /// Property to check whether the pixel format is a 128-bit format
  106. var is128Bit: Bool {
  107. switch self {
  108. case .rgba32Uint, .rgba32Sint:
  109. return true
  110. case .rgba32Float:
  111. return true
  112. default:
  113. return false
  114. }
  115. }
  116. /// Property to check whether the pixel format will trigger automatic sRGB gamma encoding and decoding
  117. var isSRGB: Bool {
  118. switch self {
  119. case .r8Unorm_srgb, .rg8Unorm_srgb, .bgra8Unorm_srgb, .rgba8Unorm_srgb:
  120. return true
  121. case .bgr10_xr_srgb, .bgra10_xr_srgb:
  122. return true
  123. case .astc_4x4_srgb, .astc_5x4_srgb, .astc_5x5_srgb, .astc_6x5_srgb, .astc_6x6_srgb, .astc_8x5_srgb,
  124. .astc_8x6_srgb, .astc_8x8_srgb, .astc_10x5_srgb, .astc_10x6_srgb, .astc_10x8_srgb, .astc_10x10_srgb,
  125. .astc_12x10_srgb, .astc_12x12_srgb:
  126. return true
  127. case .bc1_rgba_srgb, .bc2_rgba_srgb, .bc3_rgba_srgb, .bc7_rgbaUnorm_srgb:
  128. return true
  129. case .eac_rgba8_srgb, .etc2_rgb8, .etc2_rgb8a1_srgb:
  130. return true
  131. default:
  132. return false
  133. }
  134. }
  135. /// Property to check whether the pixel format is an extended dynamic range (EDR) format
  136. var isEDR: Bool {
  137. switch self {
  138. case .bgr10_xr, .bgra10_xr, .bgr10_xr_srgb, .bgra10_xr_srgb:
  139. return true
  140. default:
  141. return false
  142. }
  143. }
  144. /// Property to check whether the pixel format uses a form of texture compression
  145. var isCompressed: Bool {
  146. switch self {
  147. // S3TC
  148. case .bc1_rgba, .bc1_rgba_srgb, .bc2_rgba, .bc2_rgba_srgb, .bc3_rgba, .bc3_rgba_srgb:
  149. return true
  150. // RGTC
  151. case .bc4_rUnorm, .bc4_rSnorm, .bc5_rgUnorm, .bc5_rgSnorm:
  152. return true
  153. // BPTC
  154. case .bc6H_rgbFloat, .bc6H_rgbuFloat, .bc7_rgbaUnorm, .bc7_rgbaUnorm_srgb:
  155. return true
  156. // EAC
  157. case .eac_r11Unorm, .eac_r11Snorm, .eac_rg11Unorm, .eac_rg11Snorm, .eac_rgba8, .eac_rgba8_srgb:
  158. return true
  159. // ETC
  160. case .etc2_rgb8, .etc2_rgb8_srgb, .etc2_rgb8a1, .etc2_rgb8a1_srgb:
  161. return true
  162. // ASTC
  163. case .astc_4x4_srgb, .astc_5x4_srgb, .astc_5x5_srgb, .astc_6x5_srgb, .astc_6x6_srgb, .astc_8x5_srgb,
  164. .astc_8x6_srgb, .astc_8x8_srgb, .astc_10x5_srgb, .astc_10x6_srgb, .astc_10x8_srgb, .astc_10x10_srgb,
  165. .astc_12x10_srgb, .astc_12x12_srgb, .astc_4x4_ldr, .astc_5x4_ldr, .astc_5x5_ldr, .astc_6x5_ldr,
  166. .astc_6x6_ldr, .astc_8x5_ldr, .astc_8x6_ldr, .astc_8x8_ldr, .astc_10x5_ldr, .astc_10x6_ldr, .astc_10x8_ldr,
  167. .astc_10x10_ldr, .astc_12x10_ldr, .astc_12x12_ldr:
  168. return true
  169. // ASTC HDR
  170. case .astc_4x4_hdr, .astc_5x4_hdr, .astc_5x5_hdr, .astc_6x5_hdr, .astc_6x6_hdr, .astc_8x5_hdr, .astc_8x6_hdr,
  171. .astc_8x8_hdr, .astc_10x5_hdr, .astc_10x6_hdr, .astc_10x8_hdr, .astc_10x10_hdr, .astc_12x10_hdr,
  172. .astc_12x12_hdr:
  173. return true
  174. default:
  175. return false
  176. }
  177. }
  178. /// Property to check whether the pixel format is a depth buffer format
  179. var isDepth: Bool {
  180. switch self {
  181. case .depth16Unorm, .depth32Float:
  182. return true
  183. default:
  184. return false
  185. }
  186. }
  187. /// Property to check whether the pixel format is depth stencil format
  188. var isStencil: Bool {
  189. switch self {
  190. case .stencil8, .x24_stencil8, .x32_stencil8, .depth24Unorm_stencil8, .depth32Float_stencil8:
  191. return true
  192. default:
  193. return false
  194. }
  195. }
  196. /// Returns number of color components used by the pixel format
  197. var componentCount: Int? {
  198. switch self {
  199. case .a8Unorm, .r8Unorm, .r8Snorm, .r8Uint, .r8Sint, .r8Unorm_srgb:
  200. return 1
  201. case .r16Unorm, .r16Snorm, .r16Uint, .r16Sint, .r16Float:
  202. return 1
  203. case .r32Uint, .r32Sint, .r32Float:
  204. return 1
  205. case .rg8Unorm, .rg8Snorm, .rg8Uint, .rg8Sint, .rg8Unorm_srgb:
  206. return 2
  207. case .rg16Unorm, .rg16Snorm, .rg16Uint, .rg16Sint:
  208. return 2
  209. case .rg32Uint, .rg32Sint, .rg32Float:
  210. return 2
  211. case .b5g6r5Unorm, .rg11b10Float, .rgb9e5Float, .gbgr422, .bgrg422:
  212. return 3
  213. case .a1bgr5Unorm, .abgr4Unorm, .bgr5A1Unorm:
  214. return 4
  215. case .rgba8Unorm, .rgba8Snorm, .rgba8Uint, .rgba8Sint, .rgba8Unorm_srgb, .bgra8Unorm, .bgra8Unorm_srgb:
  216. return 4
  217. case .rgb10a2Unorm, .rgb10a2Uint, .bgr10a2Unorm, .bgr10_xr, .bgr10_xr_srgb:
  218. return 4
  219. case .rgba16Unorm, .rgba16Snorm, .rgba16Uint, .rgba16Sint, .rgba16Float:
  220. return 4
  221. case .rgba32Uint, .rgba32Sint, .rgba32Float:
  222. return 4
  223. case .bc4_rUnorm, .bc4_rSnorm, .eac_r11Unorm, .eac_r11Snorm:
  224. return 1
  225. case .bc5_rgUnorm, .bc5_rgSnorm:
  226. return 2
  227. case .bc6H_rgbFloat, .bc6H_rgbuFloat, .eac_rg11Unorm, .eac_rg11Snorm, .etc2_rgb8, .etc2_rgb8_srgb:
  228. return 3
  229. case .bc1_rgba, .bc1_rgba_srgb, .bc2_rgba, .bc2_rgba_srgb, .bc3_rgba, .bc3_rgba_srgb, .etc2_rgb8a1,
  230. .etc2_rgb8a1_srgb, .eac_rgba8, .eac_rgba8_srgb, .bc7_rgbaUnorm, .bc7_rgbaUnorm_srgb:
  231. return 4
  232. default:
  233. return nil
  234. }
  235. }
  236. /// Conversion of pixel format to `libobs` color format
  237. var gsColorFormat: gs_color_format {
  238. switch self {
  239. case .a8Unorm:
  240. return GS_A8
  241. case .r8Unorm:
  242. return GS_R8
  243. case .rgba8Unorm:
  244. return GS_RGBA
  245. case .bgra8Unorm:
  246. return GS_BGRA
  247. case .rgb10a2Unorm:
  248. return GS_R10G10B10A2
  249. case .rgba16Unorm:
  250. return GS_RGBA16
  251. case .r16Unorm:
  252. return GS_R16
  253. case .rgba16Float:
  254. return GS_RGBA16F
  255. case .rgba32Float:
  256. return GS_RGBA32F
  257. case .rg16Float:
  258. return GS_RG16F
  259. case .rg32Float:
  260. return GS_RG32F
  261. case .r16Float:
  262. return GS_R16F
  263. case .r32Float:
  264. return GS_R32F
  265. case .bc1_rgba:
  266. return GS_DXT1
  267. case .bc2_rgba:
  268. return GS_DXT3
  269. case .bc3_rgba:
  270. return GS_DXT5
  271. default:
  272. return GS_UNKNOWN
  273. }
  274. }
  275. /// Returns the bits per pixel based on the pixel format
  276. var bitsPerPixel: Int? {
  277. if self.is8Bit {
  278. return 8
  279. } else if self.is16Bit || self.isPacked16Bit {
  280. return 16
  281. } else if self.is32Bit || self.isPacked32Bit {
  282. return 32
  283. } else if self.is64Bit {
  284. return 64
  285. } else if self.is128Bit {
  286. return 128
  287. } else {
  288. return nil
  289. }
  290. }
  291. /// Returns the bytes per pixel based on the pixel format
  292. var bytesPerPixel: Int? {
  293. if self.is8Bit {
  294. return 1
  295. } else if self.is16Bit || self.isPacked16Bit {
  296. return 2
  297. } else if self.is32Bit {
  298. return 4
  299. } else if self.isPacked32Bit {
  300. switch self {
  301. case .rgb10a2Unorm, .rgb10a2Uint, .bgr10a2Unorm, .rg11b10Float, .rgb9e5Float:
  302. return 4
  303. case .bgr10_xr, .bgr10_xr_srgb:
  304. return 8
  305. default:
  306. return nil
  307. }
  308. } else if self.is64Bit {
  309. return 8
  310. } else {
  311. return nil
  312. }
  313. }
  314. /// Returns the bytes used per color component of the pixel format
  315. var bitsPerComponent: Int? {
  316. if !self.isCompressed {
  317. if let bitsPerPixel = self.bitsPerPixel, let componentCount = self.componentCount {
  318. return bitsPerPixel / componentCount
  319. }
  320. }
  321. return nil
  322. }
  323. }
  324. extension MTLPixelFormat {
  325. /// Converts the pixel format into a compatible CoreGraphics color space
  326. var colorSpace: CGColorSpace? {
  327. switch self {
  328. case .a8Unorm, .r8Unorm, .r8Snorm, .r8Uint, .r8Sint, .r16Unorm, .r16Snorm, .r16Uint, .r16Sint,
  329. .r16Float, .r32Uint, .r32Sint, .r32Float:
  330. return CGColorSpace(name: CGColorSpace.linearGray)
  331. case .rg8Unorm, .rg8Snorm, .rg8Uint, .rg8Sint, .rgba8Unorm, .rgba8Snorm, .rgba8Uint, .rgba8Sint, .bgra8Unorm,
  332. .rgba16Unorm, .rgba16Snorm, .rgba16Uint, .rgba16Sint:
  333. return CGColorSpace(name: CGColorSpace.linearSRGB)
  334. case .rg8Unorm_srgb, .rgba8Unorm_srgb, .bgra8Unorm_srgb:
  335. return CGColorSpace(name: CGColorSpace.sRGB)
  336. case .rg16Float, .rg32Float, .rgba16Float, .rgba32Float, .bgr10_xr, .bgr10a2Unorm:
  337. return CGColorSpace(name: CGColorSpace.extendedLinearSRGB)
  338. case .bgr10_xr_srgb:
  339. return CGColorSpace(name: CGColorSpace.extendedSRGB)
  340. default:
  341. return nil
  342. }
  343. }
  344. }
  345. extension MTLPixelFormat {
  346. /// Initializes a ``MTLPixelFormat`` with a compatible CoreVideo video pixel format
  347. init?(osType: OSType) {
  348. guard let pixelFormat = osType.mtlFormat else {
  349. return nil
  350. }
  351. self = pixelFormat
  352. }
  353. /// Conversion of the pixel format into a compatible CoreVideo video pixel format
  354. var videoPixelFormat: OSType? {
  355. switch self {
  356. case .r8Unorm, .r8Unorm_srgb:
  357. return kCVPixelFormatType_OneComponent8
  358. case .r16Float:
  359. return kCVPixelFormatType_OneComponent16Half
  360. case .r32Float:
  361. return kCVPixelFormatType_OneComponent32Float
  362. case .rg8Unorm, .rg8Unorm_srgb:
  363. return kCVPixelFormatType_TwoComponent8
  364. case .rg16Float:
  365. return kCVPixelFormatType_TwoComponent16Half
  366. case .rg32Float:
  367. return kCVPixelFormatType_TwoComponent32Float
  368. case .bgra8Unorm, .bgra8Unorm_srgb:
  369. return kCVPixelFormatType_32BGRA
  370. case .rgba8Unorm, .rgba8Unorm_srgb:
  371. return kCVPixelFormatType_32RGBA
  372. case .rgba16Float:
  373. return kCVPixelFormatType_64RGBAHalf
  374. case .rgba32Float:
  375. return kCVPixelFormatType_128RGBAFloat
  376. default:
  377. return nil
  378. }
  379. }
  380. }