mirror of
https://github.com/obsproject/obs-studio.git
synced 2026-03-27 02:42:26 -04:00
407 lines
14 KiB
Swift
407 lines
14 KiB
Swift
/******************************************************************************
|
|
Copyright (C) 2024 by Patrick Heyer <PatTheMav@users.noreply.github.com>
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
******************************************************************************/
|
|
|
|
import CoreGraphics
|
|
import CoreVideo
|
|
import Foundation
|
|
import Metal
|
|
|
|
extension MTLPixelFormat {
|
|
/// Property to check whether the pixel format is an 8-bit format
|
|
var is8Bit: Bool {
|
|
switch self {
|
|
case .a8Unorm, .r8Unorm, .r8Snorm, .r8Uint, .r8Sint:
|
|
return true
|
|
case .r8Unorm_srgb:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
/// Property to check whether the pixel format is a 16-bit format
|
|
var is16Bit: Bool {
|
|
switch self {
|
|
case .r16Unorm, .r16Snorm, .r16Uint, .r16Sint:
|
|
return true
|
|
case .rg8Unorm, .rg8Snorm, .rg8Uint, .rg8Sint:
|
|
return true
|
|
case .rg16Float:
|
|
return true
|
|
case .rg8Unorm_srgb:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
/// Property to check whether the pixel format is a packed 16-bit format
|
|
var isPacked16Bit: Bool {
|
|
switch self {
|
|
case .b5g6r5Unorm, .a1bgr5Unorm, .abgr4Unorm, .bgr5A1Unorm:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
/// Property to check whether the pixel format is a 32-bit format
|
|
var is32Bit: Bool {
|
|
switch self {
|
|
case .r32Uint, .r32Sint:
|
|
return true
|
|
case .r32Float:
|
|
return true
|
|
case .rg16Unorm, .rg16Snorm, .rg16Uint, .rg16Sint:
|
|
return true
|
|
case .rg16Float:
|
|
return true
|
|
case .rgba8Unorm, .rgba8Snorm, .rgba8Uint, .rgba8Sint, .bgra8Unorm:
|
|
return true
|
|
case .rgba8Unorm_srgb, .bgra8Unorm_srgb:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
/// Property to check whether the pixel format is a packed 32-bit format
|
|
var isPacked32Bit: Bool {
|
|
switch self {
|
|
case .rgb10a2Unorm, .rgb10a2Uint, .bgr10a2Unorm:
|
|
return true
|
|
case .rg11b10Float:
|
|
return true
|
|
case .rgb9e5Float:
|
|
return true
|
|
case .bgr10_xr, .bgr10_xr_srgb:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
/// Property to check whether the pixel format is a 64-bit format
|
|
var is64Bit: Bool {
|
|
switch self {
|
|
case .rg32Uint, .rg32Sint:
|
|
return true
|
|
case .rg32Float:
|
|
return true
|
|
case .rgba16Unorm, .rgba16Snorm, .rgba16Uint, .rgba16Sint:
|
|
return true
|
|
case .rgba16Float:
|
|
return true
|
|
case .bgra10_xr, .bgra10_xr_srgb:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
/// Property to check whether the pixel format is a 128-bit format
|
|
var is128Bit: Bool {
|
|
switch self {
|
|
case .rgba32Uint, .rgba32Sint:
|
|
return true
|
|
case .rgba32Float:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
/// Property to check whether the pixel format will trigger automatic sRGB gamma encoding and decoding
|
|
var isSRGB: Bool {
|
|
switch self {
|
|
case .r8Unorm_srgb, .rg8Unorm_srgb, .bgra8Unorm_srgb, .rgba8Unorm_srgb:
|
|
return true
|
|
case .bgr10_xr_srgb, .bgra10_xr_srgb:
|
|
return true
|
|
case .astc_4x4_srgb, .astc_5x4_srgb, .astc_5x5_srgb, .astc_6x5_srgb, .astc_6x6_srgb, .astc_8x5_srgb,
|
|
.astc_8x6_srgb, .astc_8x8_srgb, .astc_10x5_srgb, .astc_10x6_srgb, .astc_10x8_srgb, .astc_10x10_srgb,
|
|
.astc_12x10_srgb, .astc_12x12_srgb:
|
|
return true
|
|
case .bc1_rgba_srgb, .bc2_rgba_srgb, .bc3_rgba_srgb, .bc7_rgbaUnorm_srgb:
|
|
return true
|
|
case .eac_rgba8_srgb, .etc2_rgb8, .etc2_rgb8a1_srgb:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
/// Property to check whether the pixel format is an extended dynamic range (EDR) format
|
|
var isEDR: Bool {
|
|
switch self {
|
|
case .bgr10_xr, .bgra10_xr, .bgr10_xr_srgb, .bgra10_xr_srgb:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
/// Property to check whether the pixel format uses a form of texture compression
|
|
var isCompressed: Bool {
|
|
switch self {
|
|
// S3TC
|
|
case .bc1_rgba, .bc1_rgba_srgb, .bc2_rgba, .bc2_rgba_srgb, .bc3_rgba, .bc3_rgba_srgb:
|
|
return true
|
|
// RGTC
|
|
case .bc4_rUnorm, .bc4_rSnorm, .bc5_rgUnorm, .bc5_rgSnorm:
|
|
return true
|
|
// BPTC
|
|
case .bc6H_rgbFloat, .bc6H_rgbuFloat, .bc7_rgbaUnorm, .bc7_rgbaUnorm_srgb:
|
|
return true
|
|
// EAC
|
|
case .eac_r11Unorm, .eac_r11Snorm, .eac_rg11Unorm, .eac_rg11Snorm, .eac_rgba8, .eac_rgba8_srgb:
|
|
return true
|
|
// ETC
|
|
case .etc2_rgb8, .etc2_rgb8_srgb, .etc2_rgb8a1, .etc2_rgb8a1_srgb:
|
|
return true
|
|
// ASTC
|
|
case .astc_4x4_srgb, .astc_5x4_srgb, .astc_5x5_srgb, .astc_6x5_srgb, .astc_6x6_srgb, .astc_8x5_srgb,
|
|
.astc_8x6_srgb, .astc_8x8_srgb, .astc_10x5_srgb, .astc_10x6_srgb, .astc_10x8_srgb, .astc_10x10_srgb,
|
|
.astc_12x10_srgb, .astc_12x12_srgb, .astc_4x4_ldr, .astc_5x4_ldr, .astc_5x5_ldr, .astc_6x5_ldr,
|
|
.astc_6x6_ldr, .astc_8x5_ldr, .astc_8x6_ldr, .astc_8x8_ldr, .astc_10x5_ldr, .astc_10x6_ldr, .astc_10x8_ldr,
|
|
.astc_10x10_ldr, .astc_12x10_ldr, .astc_12x12_ldr:
|
|
return true
|
|
// ASTC HDR
|
|
case .astc_4x4_hdr, .astc_5x4_hdr, .astc_5x5_hdr, .astc_6x5_hdr, .astc_6x6_hdr, .astc_8x5_hdr, .astc_8x6_hdr,
|
|
.astc_8x8_hdr, .astc_10x5_hdr, .astc_10x6_hdr, .astc_10x8_hdr, .astc_10x10_hdr, .astc_12x10_hdr,
|
|
.astc_12x12_hdr:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
/// Property to check whether the pixel format is a depth buffer format
|
|
var isDepth: Bool {
|
|
switch self {
|
|
case .depth16Unorm, .depth32Float:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
/// Property to check whether the pixel format is depth stencil format
|
|
var isStencil: Bool {
|
|
switch self {
|
|
case .stencil8, .x24_stencil8, .x32_stencil8, .depth24Unorm_stencil8, .depth32Float_stencil8:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
/// Returns number of color components used by the pixel format
|
|
var componentCount: Int? {
|
|
switch self {
|
|
case .a8Unorm, .r8Unorm, .r8Snorm, .r8Uint, .r8Sint, .r8Unorm_srgb:
|
|
return 1
|
|
case .r16Unorm, .r16Snorm, .r16Uint, .r16Sint, .r16Float:
|
|
return 1
|
|
case .r32Uint, .r32Sint, .r32Float:
|
|
return 1
|
|
case .rg8Unorm, .rg8Snorm, .rg8Uint, .rg8Sint, .rg8Unorm_srgb:
|
|
return 2
|
|
case .rg16Unorm, .rg16Snorm, .rg16Uint, .rg16Sint:
|
|
return 2
|
|
case .rg32Uint, .rg32Sint, .rg32Float:
|
|
return 2
|
|
case .b5g6r5Unorm, .rg11b10Float, .rgb9e5Float, .gbgr422, .bgrg422:
|
|
return 3
|
|
case .a1bgr5Unorm, .abgr4Unorm, .bgr5A1Unorm:
|
|
return 4
|
|
case .rgba8Unorm, .rgba8Snorm, .rgba8Uint, .rgba8Sint, .rgba8Unorm_srgb, .bgra8Unorm, .bgra8Unorm_srgb:
|
|
return 4
|
|
case .rgb10a2Unorm, .rgb10a2Uint, .bgr10a2Unorm, .bgr10_xr, .bgr10_xr_srgb:
|
|
return 4
|
|
case .rgba16Unorm, .rgba16Snorm, .rgba16Uint, .rgba16Sint, .rgba16Float:
|
|
return 4
|
|
case .rgba32Uint, .rgba32Sint, .rgba32Float:
|
|
return 4
|
|
case .bc4_rUnorm, .bc4_rSnorm, .eac_r11Unorm, .eac_r11Snorm:
|
|
return 1
|
|
case .bc5_rgUnorm, .bc5_rgSnorm:
|
|
return 2
|
|
case .bc6H_rgbFloat, .bc6H_rgbuFloat, .eac_rg11Unorm, .eac_rg11Snorm, .etc2_rgb8, .etc2_rgb8_srgb:
|
|
return 3
|
|
case .bc1_rgba, .bc1_rgba_srgb, .bc2_rgba, .bc2_rgba_srgb, .bc3_rgba, .bc3_rgba_srgb, .etc2_rgb8a1,
|
|
.etc2_rgb8a1_srgb, .eac_rgba8, .eac_rgba8_srgb, .bc7_rgbaUnorm, .bc7_rgbaUnorm_srgb:
|
|
return 4
|
|
default:
|
|
return nil
|
|
}
|
|
}
|
|
|
|
/// Conversion of pixel format to `libobs` color format
|
|
var gsColorFormat: gs_color_format {
|
|
switch self {
|
|
case .a8Unorm:
|
|
return GS_A8
|
|
case .r8Unorm:
|
|
return GS_R8
|
|
case .rgba8Unorm:
|
|
return GS_RGBA
|
|
case .bgra8Unorm:
|
|
return GS_BGRA
|
|
case .rgb10a2Unorm:
|
|
return GS_R10G10B10A2
|
|
case .rgba16Unorm:
|
|
return GS_RGBA16
|
|
case .r16Unorm:
|
|
return GS_R16
|
|
case .rgba16Float:
|
|
return GS_RGBA16F
|
|
case .rgba32Float:
|
|
return GS_RGBA32F
|
|
case .rg16Float:
|
|
return GS_RG16F
|
|
case .rg32Float:
|
|
return GS_RG32F
|
|
case .r16Float:
|
|
return GS_R16F
|
|
case .r32Float:
|
|
return GS_R32F
|
|
case .bc1_rgba:
|
|
return GS_DXT1
|
|
case .bc2_rgba:
|
|
return GS_DXT3
|
|
case .bc3_rgba:
|
|
return GS_DXT5
|
|
default:
|
|
return GS_UNKNOWN
|
|
}
|
|
}
|
|
|
|
/// Returns the bits per pixel based on the pixel format
|
|
var bitsPerPixel: Int? {
|
|
if self.is8Bit {
|
|
return 8
|
|
} else if self.is16Bit || self.isPacked16Bit {
|
|
return 16
|
|
} else if self.is32Bit || self.isPacked32Bit {
|
|
return 32
|
|
} else if self.is64Bit {
|
|
return 64
|
|
} else if self.is128Bit {
|
|
return 128
|
|
} else {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
/// Returns the bytes per pixel based on the pixel format
|
|
var bytesPerPixel: Int? {
|
|
if self.is8Bit {
|
|
return 1
|
|
} else if self.is16Bit || self.isPacked16Bit {
|
|
return 2
|
|
} else if self.is32Bit {
|
|
return 4
|
|
} else if self.isPacked32Bit {
|
|
switch self {
|
|
case .rgb10a2Unorm, .rgb10a2Uint, .bgr10a2Unorm, .rg11b10Float, .rgb9e5Float:
|
|
return 4
|
|
case .bgr10_xr, .bgr10_xr_srgb:
|
|
return 8
|
|
default:
|
|
return nil
|
|
}
|
|
} else if self.is64Bit {
|
|
return 8
|
|
} else {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
/// Returns the bytes used per color component of the pixel format
|
|
var bitsPerComponent: Int? {
|
|
if !self.isCompressed {
|
|
if let bitsPerPixel = self.bitsPerPixel, let componentCount = self.componentCount {
|
|
return bitsPerPixel / componentCount
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
}
|
|
|
|
extension MTLPixelFormat {
|
|
/// Converts the pixel format into a compatible CoreGraphics color space
|
|
var colorSpace: CGColorSpace? {
|
|
switch self {
|
|
case .a8Unorm, .r8Unorm, .r8Snorm, .r8Uint, .r8Sint, .r16Unorm, .r16Snorm, .r16Uint, .r16Sint,
|
|
.r16Float, .r32Uint, .r32Sint, .r32Float:
|
|
return CGColorSpace(name: CGColorSpace.linearGray)
|
|
case .rg8Unorm, .rg8Snorm, .rg8Uint, .rg8Sint, .rgba8Unorm, .rgba8Snorm, .rgba8Uint, .rgba8Sint, .bgra8Unorm,
|
|
.rgba16Unorm, .rgba16Snorm, .rgba16Uint, .rgba16Sint:
|
|
return CGColorSpace(name: CGColorSpace.linearSRGB)
|
|
case .rg8Unorm_srgb, .rgba8Unorm_srgb, .bgra8Unorm_srgb:
|
|
return CGColorSpace(name: CGColorSpace.sRGB)
|
|
case .rg16Float, .rg32Float, .rgba16Float, .rgba32Float, .bgr10_xr, .bgr10a2Unorm:
|
|
return CGColorSpace(name: CGColorSpace.extendedLinearSRGB)
|
|
case .bgr10_xr_srgb:
|
|
return CGColorSpace(name: CGColorSpace.extendedSRGB)
|
|
default:
|
|
return nil
|
|
}
|
|
}
|
|
}
|
|
|
|
extension MTLPixelFormat {
|
|
/// Initializes a ``MTLPixelFormat`` with a compatible CoreVideo video pixel format
|
|
init?(osType: OSType) {
|
|
guard let pixelFormat = osType.mtlFormat else {
|
|
return nil
|
|
}
|
|
|
|
self = pixelFormat
|
|
}
|
|
|
|
/// Conversion of the pixel format into a compatible CoreVideo video pixel format
|
|
var videoPixelFormat: OSType? {
|
|
switch self {
|
|
case .r8Unorm, .r8Unorm_srgb:
|
|
return kCVPixelFormatType_OneComponent8
|
|
case .r16Float:
|
|
return kCVPixelFormatType_OneComponent16Half
|
|
case .r32Float:
|
|
return kCVPixelFormatType_OneComponent32Float
|
|
case .rg8Unorm, .rg8Unorm_srgb:
|
|
return kCVPixelFormatType_TwoComponent8
|
|
case .rg16Float:
|
|
return kCVPixelFormatType_TwoComponent16Half
|
|
case .rg32Float:
|
|
return kCVPixelFormatType_TwoComponent32Float
|
|
case .bgra8Unorm, .bgra8Unorm_srgb:
|
|
return kCVPixelFormatType_32BGRA
|
|
case .rgba8Unorm, .rgba8Unorm_srgb:
|
|
return kCVPixelFormatType_32RGBA
|
|
case .rgba16Float:
|
|
return kCVPixelFormatType_64RGBAHalf
|
|
case .rgba32Float:
|
|
return kCVPixelFormatType_128RGBAFloat
|
|
default:
|
|
return nil
|
|
}
|
|
}
|
|
}
|