diff options
Diffstat (limited to 'video')
45 files changed, 944 insertions, 478 deletions
diff --git a/video/filter/refqueue.c b/video/filter/refqueue.c index f5124087c6..031feb8c96 100644 --- a/video/filter/refqueue.c +++ b/video/filter/refqueue.c @@ -119,10 +119,10 @@ bool mp_refqueue_is_top_field(struct mp_refqueue *q) { if (!mp_refqueue_has_output(q)) return false; - + bool tff = q->field_parity == MP_FIELD_PARITY_TFF; bool bff = q->field_parity == MP_FIELD_PARITY_BFF; - bool ret = (!!(q->queue[q->pos]->fields & MP_IMGFIELD_TOP_FIRST) ^ q->second_field + bool ret = (!!(q->queue[q->pos]->fields & MP_IMGFIELD_TOP_FIRST) ^ q->second_field && !tff && !bff); // Default parity ret = ret || (tff && !q->second_field); // Check if top field is forced ret = ret || (bff && q->second_field); // Check if bottom field is forced diff --git a/video/filter/vf_gpu.c b/video/filter/vf_gpu.c index e19faaef42..d2ba990a00 100644 --- a/video/filter/vf_gpu.c +++ b/video/filter/vf_gpu.c @@ -24,95 +24,49 @@ #include "options/options.h" #include "video/out/aspect.h" #include "video/out/gpu/video.h" -#include "video/out/opengl/egl_helpers.h" -#include "video/out/opengl/ra_gl.h" - -struct offscreen_ctx { - struct mp_log *log; - struct ra *ra; - void *priv; - - void (*set_context)(struct offscreen_ctx *ctx, bool enable); -}; - -struct gl_offscreen_ctx { - GL gl; - EGLDisplay egl_display; - EGLContext egl_context; +#include "video/filter/vf_gpu.h" + +extern const struct offscreen_context offscreen_vk; +extern const struct offscreen_context offscreen_egl; + +static const struct offscreen_context *contexts[] = { +#if HAVE_EGL + &offscreen_egl, +#endif +#if HAVE_VULKAN + &offscreen_vk, +#endif }; -static void gl_ctx_destroy(void *p) +static inline OPT_STRING_VALIDATE_FUNC(offscreen_ctx_validate_api) { - struct offscreen_ctx *ctx = p; - struct gl_offscreen_ctx *gl = ctx->priv; - - ra_free(&ctx->ra); - - if (gl->egl_context) - eglDestroyContext(gl->egl_display, gl->egl_context); + struct bstr param = bstr0(*value); + for (int i = 0; i < MP_ARRAY_SIZE(contexts); i++) { + if (bstr_equals0(param, contexts[i]->api)) + return 1; + } + return M_OPT_INVALID; } -static void gl_ctx_set_context(struct offscreen_ctx *ctx, bool enable) +static int offscreen_ctx_api_help(struct mp_log *log, const struct m_option *opt, + struct bstr name) { - struct gl_offscreen_ctx *gl = ctx->priv; - EGLContext c = enable ? gl->egl_context : EGL_NO_CONTEXT; - - if (!eglMakeCurrent(gl->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, c)) - MP_ERR(ctx, "Could not make EGL context current.\n"); + mp_info(log, "GPU APIs (offscreen contexts):\n"); + for (int i = 0; i < MP_ARRAY_SIZE(contexts); i++) + mp_info(log, " %s\n", contexts[i]->api); + return M_OPT_EXIT; } -static struct offscreen_ctx *gl_offscreen_ctx_create(struct mpv_global *global, - struct mp_log *log) +static struct offscreen_ctx *offscreen_ctx_create(struct mpv_global *global, + struct mp_log *log, + const char *api) { - struct offscreen_ctx *ctx = talloc_zero(NULL, struct offscreen_ctx); - struct gl_offscreen_ctx *gl = talloc_zero(ctx, struct gl_offscreen_ctx); - talloc_set_destructor(ctx, gl_ctx_destroy); - *ctx = (struct offscreen_ctx){ - .log = log, - .priv = gl, - .set_context = gl_ctx_set_context, - }; - - // This appears to work with Mesa. EGL 1.5 doesn't specify what a "default - // display" is at all. - gl->egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY); - if (!eglInitialize(gl->egl_display, NULL, NULL)) { - MP_ERR(ctx, "Could not initialize EGL.\n"); - goto error; + for (int i = 0; i < MP_ARRAY_SIZE(contexts); i++) { + if (api && strcmp(contexts[i]->api, api) != 0) + continue; + mp_info(log, "Creating offscreen GPU context '%s'\n", contexts[i]->api); + return contexts[i]->offscreen_ctx_create(global, log); } - - // Unfortunately, mpegl_create_context() is entangled with ra_ctx. - // Fortunately, it does not need much, and we can provide a stub. - struct ra_ctx ractx = { - .log = ctx->log, - .global = global, - }; - EGLConfig config; - if (!mpegl_create_context(&ractx, gl->egl_display, &gl->egl_context, &config)) - { - MP_ERR(ctx, "Could not create EGL context.\n"); - goto error; - } - - if (!eglMakeCurrent(gl->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, - gl->egl_context)) - { - MP_ERR(ctx, "Could not make EGL context current.\n"); - goto error; - } - - mpegl_load_functions(&gl->gl, ctx->log); - ctx->ra = ra_create_gl(&gl->gl, ctx->log); - - if (!ctx->ra) - goto error; - - gl_ctx_set_context(ctx, false); - - return ctx; - -error: - talloc_free(ctx); return NULL; } @@ -124,6 +78,7 @@ static void offscreen_ctx_set_current(struct offscreen_ctx *ctx, bool enable) struct gpu_opts { int w, h; + char *api; }; struct priv { @@ -327,7 +282,7 @@ static struct mp_filter *gpu_create(struct mp_filter *parent, void *options) priv->vo_opts_cache = m_config_cache_alloc(f, f->global, &vo_sub_opts); priv->vo_opts = priv->vo_opts_cache->opts; - priv->ctx = gl_offscreen_ctx_create(f->global, f->log); + priv->ctx = offscreen_ctx_create(f->global, f->log, priv->opts->api); if (!priv->ctx) { MP_FATAL(f, "Could not create offscreen ra context.\n"); goto error; @@ -368,6 +323,8 @@ const struct mp_user_filter_entry vf_gpu = { .options = (const struct m_option[]){ {"w", OPT_INT(w)}, {"h", OPT_INT(h)}, + {"api", OPT_STRING_VALIDATE(api, offscreen_ctx_validate_api), + .help = offscreen_ctx_api_help}, {0} }, }, diff --git a/video/filter/vf_gpu.h b/video/filter/vf_gpu.h new file mode 100644 index 0000000000..2cc9a16eea --- /dev/null +++ b/video/filter/vf_gpu.h @@ -0,0 +1,35 @@ +/* + * This file is part of mpv. + * + * mpv is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * mpv 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with mpv. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "common/common.h" +#include "common/global.h" + +struct offscreen_ctx { + struct mp_log *log; + struct ra *ra; + void *priv; + + void (*set_context)(struct offscreen_ctx *ctx, bool enable); +}; + +struct offscreen_context { + const char *api; + struct offscreen_ctx *(*offscreen_ctx_create)(struct mpv_global *, + struct mp_log *); +}; diff --git a/video/filter/vf_gpu_egl.c b/video/filter/vf_gpu_egl.c new file mode 100644 index 0000000000..cd90a6e06a --- /dev/null +++ b/video/filter/vf_gpu_egl.c @@ -0,0 +1,107 @@ +/* + * This file is part of mpv. + * + * mpv is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * mpv 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with mpv. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "common/common.h" +#include "video/filter/vf_gpu.h" +#include "video/out/opengl/egl_helpers.h" +#include "video/out/opengl/ra_gl.h" + +struct gl_offscreen_ctx { + GL gl; + EGLDisplay egl_display; + EGLContext egl_context; +}; + +static void gl_ctx_destroy(void *p) +{ + struct offscreen_ctx *ctx = p; + struct gl_offscreen_ctx *gl = ctx->priv; + + ra_free(&ctx->ra); + + if (gl->egl_context) + eglDestroyContext(gl->egl_display, gl->egl_context); +} + +static void gl_ctx_set_context(struct offscreen_ctx *ctx, bool enable) +{ + struct gl_offscreen_ctx *gl = ctx->priv; + EGLContext c = enable ? gl->egl_context : EGL_NO_CONTEXT; + + if (!eglMakeCurrent(gl->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, c)) + MP_ERR(ctx, "Could not make EGL context current.\n"); +} + +static struct offscreen_ctx *gl_offscreen_ctx_create(struct mpv_global *global, + struct mp_log *log) +{ + struct offscreen_ctx *ctx = talloc(NULL, struct offscreen_ctx); + struct gl_offscreen_ctx *gl = talloc_zero(ctx, struct gl_offscreen_ctx); + talloc_set_destructor(ctx, gl_ctx_destroy); + *ctx = (struct offscreen_ctx){ + .log = log, + .priv = gl, + .set_context = gl_ctx_set_context, + }; + + // This appears to work with Mesa. EGL 1.5 doesn't specify what a "default + // display" is at all. + gl->egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (!eglInitialize(gl->egl_display, NULL, NULL)) { + MP_ERR(ctx, "Could not initialize EGL.\n"); + goto error; + } + + // Unfortunately, mpegl_create_context() is entangled with ra_ctx. + // Fortunately, it does not need much, and we can provide a stub. + struct ra_ctx ractx = { + .log = ctx->log, + .global = global, + }; + EGLConfig config; + if (!mpegl_create_context(&ractx, gl->egl_display, &gl->egl_context, &config)) + { + MP_ERR(ctx, "Could not create EGL context.\n"); + goto error; + } + + if (!eglMakeCurrent(gl->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, + gl->egl_context)) + { + MP_ERR(ctx, "Could not make EGL context current.\n"); + goto error; + } + + mpegl_load_functions(&gl->gl, ctx->log); + ctx->ra = ra_create_gl(&gl->gl, ctx->log); + + if (!ctx->ra) + goto error; + + gl_ctx_set_context(ctx, false); + + return ctx; + +error: + talloc_free(ctx); + return NULL; +} + +const struct offscreen_context offscreen_egl = { + .api = "egl", + .offscreen_ctx_create = gl_offscreen_ctx_create +}; diff --git a/video/filter/vf_gpu_vulkan.c b/video/filter/vf_gpu_vulkan.c new file mode 100644 index 0000000000..d477fd153a --- /dev/null +++ b/video/filter/vf_gpu_vulkan.c @@ -0,0 +1,115 @@ +/* + * This file is part of mpv. + * + * mpv is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * mpv 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with mpv. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "options/m_config.h" +#include "video/filter/vf_gpu.h" +#include "video/out/placebo/ra_pl.h" +#include "video/out/placebo/utils.h" +#include "video/out/vulkan/context.h" +#include "video/out/vulkan/utils.h" + +struct vk_offscreen_ctx { + struct ra_ctx *ractx; + struct mpvk_ctx *vk; +}; + +extern const struct m_sub_options vulkan_conf; + +static void vk_ctx_destroy(void *p) +{ + struct offscreen_ctx *ctx = p; + struct vk_offscreen_ctx *vkctx = ctx->priv; + struct ra_ctx *ractx = vkctx->ractx; + struct mpvk_ctx *vk = vkctx->vk; + + if (ractx->ra) { + pl_gpu_finish(vk->gpu); + ractx->ra->fns->destroy(ctx->ra); + ractx->ra = NULL; + ctx->ra = NULL; + } + + vk->gpu = NULL; + pl_vulkan_destroy(&vk->vulkan); + mpvk_uninit(vk); + talloc_free(vk); + talloc_free(ractx); +} + +static struct offscreen_ctx *vk_offscreen_ctx_create(struct mpv_global *global, + struct mp_log *log) +{ + struct offscreen_ctx *ctx = talloc(NULL, struct offscreen_ctx); + talloc_set_destructor(ctx, vk_ctx_destroy); + *ctx = (struct offscreen_ctx){ + .log = log, + }; + + struct ra_ctx *ractx = talloc_zero(ctx, struct ra_ctx); + struct mpvk_ctx *vk = talloc_zero(ctx, struct mpvk_ctx); + ractx->log = ctx->log; + ractx->global = global; + + vk->pllog = mppl_log_create(ctx, log); + if (!vk->pllog) + goto error; + + struct pl_vk_inst_params pl_vk_params = {0}; + struct ra_ctx_opts *ctx_opts = mp_get_config_group(NULL, global, &ra_ctx_conf); + pl_vk_params.debug = ctx_opts->debug; + talloc_free(ctx_opts); + mppl_log_set_probing(vk->pllog, true); + vk->vkinst = pl_vk_inst_create(vk->pllog, &pl_vk_params); + mppl_log_set_probing(vk->pllog, false); + if (!vk->vkinst) + goto error; + + struct vulkan_opts *vk_opts = mp_get_config_group(NULL, global, &vulkan_conf); + vk->vulkan = mppl_create_vulkan(vk_opts, vk->vkinst, vk->pllog, VK_NULL_HANDLE); + talloc_free(vk_opts); + if (!vk->vulkan) + goto error; + + vk->gpu = vk->vulkan->gpu; + ractx->ra = ra_create_pl(vk->gpu, ractx->log); + if (!ractx->ra) + goto error; + + struct vk_offscreen_ctx *vkctx = talloc(ctx, struct vk_offscreen_ctx); + *vkctx = (struct vk_offscreen_ctx){ + .ractx = ractx, + .vk = vk + }; + + ctx->ra = ractx->ra; + ctx->priv = vkctx; + + return ctx; + +error: + pl_vulkan_destroy(&vk->vulkan); + mpvk_uninit(vk); + talloc_free(vk); + talloc_free(ractx); + talloc_free(ctx); + return NULL; +} + +const struct offscreen_context offscreen_vk = { + .api = "vulkan", + .offscreen_ctx_create = vk_offscreen_ctx_create +}; diff --git a/video/filter/vf_vapoursynth.c b/video/filter/vf_vapoursynth.c index 0b798c8daf..597ef5cbf2 100644 --- a/video/filter/vf_vapoursynth.c +++ b/video/filter/vf_vapoursynth.c @@ -193,8 +193,9 @@ static void copy_mp_to_vs_frame_props_map(struct priv *p, VSMap *map, p->vsapi->propSetInt(map, "_ColorSpace", pl_system_to_av(params->repr.sys), 0); if (params->chroma_location) { + // 0=left, 1=center, 2=topleft, 3=top, 4=bottomleft, 5=bottom. p->vsapi->propSetInt(map, "_ChromaLocation", - params->chroma_location == PL_CHROMA_CENTER, 0); + params->chroma_location - 1, 0); } char pict_type = 0; switch (img->pict_type) { @@ -208,6 +209,10 @@ static void copy_mp_to_vs_frame_props_map(struct priv *p, VSMap *map, if (img->fields & MP_IMGFIELD_INTERLACED) field = img->fields & MP_IMGFIELD_TOP_FIRST ? 2 : 1; p->vsapi->propSetInt(map, "_FieldBased", field, 0); + + // Don't increase the reference count. It is not intended to be read externally, + // and we know it will be alive when we retrieve it. + p->vsapi->propSetData(map, "_MP_IMAGE", (const char *)img, sizeof(*img), 0); } static int set_vs_frame_props(struct priv *p, VSFrameRef *frame, @@ -230,11 +235,13 @@ static VSFrameRef *alloc_vs_frame(struct priv *p, struct mp_image_params *fmt) } static struct mp_image map_vs_frame(struct priv *p, const VSFrameRef *ref, - bool w) + bool w, struct mp_image *ref_image) { const VSFormat *fmt = p->vsapi->getFrameFormat(ref); struct mp_image img = {0}; + if (ref_image) + img = *ref_image; mp_image_setfmt(&img, mp_from_vs(fmt->id)); mp_image_set_size(&img, p->vsapi->getFrameWidth(ref, 0), p->vsapi->getFrameHeight(ref, 0)); @@ -269,13 +276,19 @@ static void VS_CC vs_frame_done(void *userData, const VSFrameRef *f, int n, struct mp_image *res = NULL; if (f) { - struct mp_image img = map_vs_frame(p, f, false); - struct mp_image dummy = {.params = p->fmt_in}; - if (p->fmt_in.w != img.w || p->fmt_in.h != img.h) - dummy.params.crop = (struct mp_rect){0, 0, img.w, img.h}; - mp_image_copy_attributes(&img, &dummy); - img.pkt_duration = -1; const VSMap *map = p->vsapi->getFramePropsRO(f); + if (!map) + MP_ERR(p, "Failed to get frame properties!"); + struct mp_image *mpi = NULL; + if (map) { + mpi = (void *)p->vsapi->propGetData(map, "_MP_IMAGE", 0, NULL); + if (!mpi) + MP_ERR(p, "Failed to get mp_image attributes!"); + } + struct mp_image img = map_vs_frame(p, f, false, mpi); + img.pkt_duration = -1; + if (mpi && (mpi->params.w != img.w || mpi->params.h != img.h)) + img.params.crop = (struct mp_rect){0, 0, img.w, img.h}; if (map) { int err1, err2; int num = p->vsapi->propGetInt(map, "_DurationNum", 0, &err1); @@ -490,7 +503,7 @@ static const VSFrameRef *VS_CC infiltGetFrame(int frameno, int activationReason, p->vsapi->setFilterError("Could not allocate VS frame", frameCtx); break; } - struct mp_image vsframe = map_vs_frame(p, ret, true); + struct mp_image vsframe = map_vs_frame(p, ret, true, NULL); mp_image_clear(&vsframe, 0, 0, p->fmt_in.w, p->fmt_in.h); struct mp_image dummy = {0}; mp_image_set_params(&dummy, &p->fmt_in); @@ -538,7 +551,7 @@ static const VSFrameRef *VS_CC infiltGetFrame(int frameno, int activationReason, } mp_mutex_unlock(&p->lock); - struct mp_image vsframe = map_vs_frame(p, ret, true); + struct mp_image vsframe = map_vs_frame(p, ret, true, NULL); mp_image_copy(&vsframe, img); int res = 1e6; int dur = img->pkt_duration * res + 0.5; diff --git a/video/image_writer.c b/video/image_writer.c index cc795735ef..3c2d57a80f 100644 --- a/video/image_writer.c +++ b/video/image_writer.c @@ -19,7 +19,6 @@ #include <stdlib.h> #include <string.h> #include <inttypes.h> -#include <unistd.h> #include <libavcodec/avcodec.h> #include <libavformat/avformat.h> @@ -221,7 +220,7 @@ static bool write_lavc(struct image_writer_ctx *ctx, mp_image_t *image, FILE *fp } if (avcodec_open2(avctx, codec, NULL) < 0) { - print_open_fail: + print_open_fail: MP_ERR(ctx, "Could not open libavcodec encoder for saving images\n"); goto error_exit; } @@ -261,11 +260,11 @@ error_exit: static void write_jpeg_error_exit(j_common_ptr cinfo) { - // NOTE: do not write error message, too much effort to connect the libjpeg - // log callbacks with mplayer's log function mp_msp() + // NOTE: do not write error message, too much effort to connect the libjpeg + // log callbacks with mplayer's log function mp_msp() - // Return control to the setjmp point - longjmp(*(jmp_buf*)cinfo->client_data, 1); + // Return control to the setjmp point + longjmp(*(jmp_buf*)cinfo->client_data, 1); } static bool write_jpeg(struct image_writer_ctx *ctx, mp_image_t *image, FILE *fp) @@ -309,7 +308,7 @@ static bool write_jpeg(struct image_writer_ctx *ctx, mp_image_t *image, FILE *fp JSAMPROW row_pointer[1]; row_pointer[0] = image->planes[0] + (ptrdiff_t)cinfo.next_scanline * image->stride[0]; - jpeg_write_scanlines(&cinfo, row_pointer,1); + jpeg_write_scanlines(&cinfo, row_pointer, 1); } jpeg_finish_compress(&cinfo); diff --git a/video/mp_image.c b/video/mp_image.c index 609fb17bf0..35b302d328 100644 --- a/video/mp_image.c +++ b/video/mp_image.c @@ -1116,10 +1116,8 @@ struct mp_image *mp_image_from_av_frame(struct AVFrame *src) sd = av_frame_get_side_data(src, AV_FRAME_DATA_DOVI_RPU_BUFFER); if (sd) { -#ifdef PL_HAVE_LIBDOVI pl_hdr_metadata_from_dovi_rpu(&dst->params.color.hdr, sd->buf->data, sd->buf->size); -#endif } #endif diff --git a/video/out/cocoa_cb_common.swift b/video/out/cocoa_cb_common.swift index 9f32ed651f..b2910dba85 100644 --- a/video/out/cocoa_cb_common.swift +++ b/video/out/cocoa_cb_common.swift @@ -87,8 +87,7 @@ class CocoaCB: Common, EventSubscriber { } func updateWindowSize(_ vo: UnsafeMutablePointer<vo>) { - guard let targetScreen = getTargetScreen(forFullscreen: false) ?? NSScreen.main else - { + guard let targetScreen = getTargetScreen(forFullscreen: false) ?? NSScreen.main else { log.warning("Couldn't update Window size, no Screen available") return } @@ -102,11 +101,10 @@ class CocoaCB: Common, EventSubscriber { } override func displayLinkCallback(_ displayLink: CVDisplayLink, - _ inNow: UnsafePointer<CVTimeStamp>, - _ inOutputTime: UnsafePointer<CVTimeStamp>, - _ flagsIn: CVOptionFlags, - _ flagsOut: UnsafeMutablePointer<CVOptionFlags>) -> CVReturn - { + _ inNow: UnsafePointer<CVTimeStamp>, + _ inOutputTime: UnsafePointer<CVTimeStamp>, + _ flagsIn: CVOptionFlags, + _ flagsOut: UnsafeMutablePointer<CVOptionFlags>) -> CVReturn { libmpv.reportRenderFlip() return kCVReturnSuccess } @@ -122,7 +120,55 @@ class CocoaCB: Common, EventSubscriber { } libmpv.setRenderICCProfile(colorSpace) - layer?.colorspace = colorSpace.cgColorSpace + layer?.colorspace = getColorSpace() + } + + func getColorSpace() -> CGColorSpace? { + guard let colorSpace = window?.screen?.colorSpace?.cgColorSpace else { + log.warning("Couldn't retrieve ICC Profile, no color space available") + return nil + } + + let outputCsp = Int(option.mac.cocoa_cb_output_csp) + + switch outputCsp { + case MAC_CSP_AUTO: return colorSpace + case MAC_CSP_DISPLAY_P3: return CGColorSpace(name: CGColorSpace.displayP3) + case MAC_CSP_DISPLAY_P3_HLG: return CGColorSpace(name: CGColorSpace.displayP3_HLG) + case MAC_CSP_DISPLAY_P3_PQ: return CGColorSpace(name: CGColorSpace.displayP3_PQ) + case MAC_CSP_DCI_P3: return CGColorSpace(name: CGColorSpace.dcip3) + case MAC_CSP_BT_2020: return CGColorSpace(name: CGColorSpace.itur_2020) + case MAC_CSP_BT_709: return CGColorSpace(name: CGColorSpace.itur_709) + case MAC_CSP_SRGB: return CGColorSpace(name: CGColorSpace.sRGB) + case MAC_CSP_SRGB_LINEAR: return CGColorSpace(name: CGColorSpace.linearSRGB) + case MAC_CSP_RGB_LINEAR: return CGColorSpace(name: CGColorSpace.genericRGBLinear) + case MAC_CSP_ADOBE: return CGColorSpace(name: CGColorSpace.adobeRGB1998) + default: break + } + +#if HAVE_MACOS_11_FEATURES + if #available(macOS 11.0, *) { + switch outputCsp { + case MAC_CSP_BT_2100_HLG: return CGColorSpace(name: CGColorSpace.itur_2100_HLG) + case MAC_CSP_BT_2100_PQ: return CGColorSpace(name: CGColorSpace.itur_2100_PQ) + default: break + } + } +#endif + +#if HAVE_MACOS_12_FEATURES + if #available(macOS 12.0, *) { + switch outputCsp { + case MAC_CSP_DISPLAY_P3_LINEAR: return CGColorSpace(name: CGColorSpace.linearDisplayP3) + case MAC_CSP_BT_2020_LINEAR: return CGColorSpace(name: CGColorSpace.linearITUR_2020) + default: break + } + } +#endif + + |