/***************************************************************************** * placebo.c: Definition of various libplacebo helpers ***************************************************************************** * Copyright (C) 2018 Niklas Haas * * This program 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. * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include "utils.h" static void Log(void *priv, enum pl_log_level level, const char *msg) { struct vlc_object_t *obj = priv; switch (level) { case PL_LOG_FATAL: // fall through case PL_LOG_ERR: msg_Err(obj, "%s", msg); break; case PL_LOG_WARN: msg_Warn(obj, "%s", msg); break; case PL_LOG_INFO: msg_Info(obj, "%s", msg); break; case PL_LOG_DEBUG: msg_Dbg(obj, "%s", msg); break; default: break; } } pl_log vlc_placebo_CreateLog(vlc_object_t *obj) { return pl_log_create(PL_API_VER, &(struct pl_log_params) { .log_level = PL_LOG_DEBUG, .log_cb = Log, .log_priv = obj, }); } struct plane_desc { int components; size_t pixel_size; int comp_bits[4]; int comp_map[4]; int w_denom; int h_denom; }; struct fmt_desc { enum pl_fmt_type type; struct plane_desc planes[4]; int num_planes; int color_bits; // relevant bits, or 0 if the same as the texture depth }; #define SIZE(n, bits, pad) (((n) * (bits) + (pad) + 7) / 8) #define COMPS(...) {__VA_ARGS__} #define PLANE(n, bits, map, wd, hd, pad) \ { .components = n, \ .pixel_size = SIZE(n, bits, pad), \ .comp_bits = {bits, bits, bits, bits}, \ .comp_map = map, \ .w_denom = wd, \ .h_denom = hd, \ } #define _PLANAR(n, bits, wd, hd) \ .type = PL_FMT_UNORM, \ .num_planes = n, \ .planes = { \ PLANE(1, bits, {0}, 1, 1, 0), \ PLANE(1, bits, {1}, wd, hd, 0), \ PLANE(1, bits, {2}, wd, hd, 0), \ PLANE(1, bits, {3}, 1, 1, 0), \ } #define _SEMIPLANAR(n, bits, wd, hd) \ .type = PL_FMT_UNORM, \ .num_planes = n, \ .planes = { \ PLANE(1, bits, {0}, 1, 1, 0), \ PLANE(2, bits, COMPS(1, 2), wd, hd, 0), \ PLANE(1, bits, {3}, 1, 1, 0), \ } #define PACKED(n, bits, pad) \ .type = PL_FMT_UNORM, \ .num_planes = 1, \ .planes = { \ PLANE(n, bits, COMPS(0, 1, 2, 3), 1, 1, pad), \ } #define SINGLE(t, bits) \ .type = PL_FMT_##t, \ .num_planes = 1, \ .planes = { \ PLANE(1, bits, {0}, 1, 1, 0), \ } #define PLANAR(...) _PLANAR(__VA_ARGS__) #define SEMIPLANAR(...) _SEMIPLANAR(__VA_ARGS__) #define _410 4, 2 #define _411 4, 1 #define _420 2, 2 #define _422 2, 1 #define _440 1, 2 #define _444 1, 1 // NOTE: This list contains some special formats that don't follow the normal // rules, but which are included regardless. The corrections for these // exceptions happen below, in the function vlc_placebo_PlaneFormat! static const struct { vlc_fourcc_t fcc; struct fmt_desc desc; } formats[] = { { VLC_CODEC_YV9, {PLANAR(3, 8, _410)} }, { VLC_CODEC_I410, {PLANAR(3, 8, _410)} }, { VLC_CODEC_I411, {PLANAR(3, 8, _411)} }, { VLC_CODEC_I440, {PLANAR(3, 8, _440)} }, { VLC_CODEC_GREY, {PLANAR(1, 8, _444)} }, { VLC_CODEC_I420, {PLANAR(3, 8, _420)} }, #ifdef WORDS_BIGENDIAN { VLC_CODEC_I420_9B, {PLANAR(3, 16, _420), .color_bits = 9} }, { VLC_CODEC_I420_10B, {PLANAR(3, 16, _420), .color_bits = 10} }, { VLC_CODEC_I420_12B, {PLANAR(3, 16, _420), .color_bits = 12} }, { VLC_CODEC_I420_16B, {PLANAR(3, 16, _420), .color_bits = 16} }, #else { VLC_CODEC_I420_9L, {PLANAR(3, 16, _420), .color_bits = 9} }, { VLC_CODEC_I420_10L, {PLANAR(3, 16, _420), .color_bits = 10} }, { VLC_CODEC_I420_12L, {PLANAR(3, 16, _420), .color_bits = 12} }, { VLC_CODEC_I420_16L, {PLANAR(3, 16, _420), .color_bits = 16} }, #endif { VLC_CODEC_I422, {PLANAR(3, 8, _422)} }, #ifdef WORDS_BIGENDIAN { VLC_CODEC_I422_9B, {PLANAR(3, 16, _422), .color_bits = 9} }, { VLC_CODEC_I422_10B, {PLANAR(3, 16, _422), .color_bits = 10} }, { VLC_CODEC_I422_12B, {PLANAR(3, 16, _422), .color_bits = 12} }, #else { VLC_CODEC_I422_9L, {PLANAR(3, 16, _422), .color_bits = 9} }, { VLC_CODEC_I422_10L, {PLANAR(3, 16, _422), .color_bits = 10} }, { VLC_CODEC_I422_12L, {PLANAR(3, 16, _422), .color_bits = 12} }, #endif { VLC_CODEC_I444, {PLANAR(3, 8, _444)} }, #ifdef WORDS_BIGENDIAN { VLC_CODEC_I444_9B, {PLANAR(3, 16, _444), .color_bits = 9} }, { VLC_CODEC_I444_10B, {PLANAR(3, 16, _444), .color_bits = 10} }, { VLC_CODEC_I444_12B, {PLANAR(3, 16, _444), .color_bits = 12} }, { VLC_CODEC_I444_16B, {PLANAR(3, 16, _444), .color_bits = 16} }, #else { VLC_CODEC_I444_9L, {PLANAR(3, 16, _444), .color_bits = 9} }, { VLC_CODEC_I444_10L, {PLANAR(3, 16, _444), .color_bits = 10} }, { VLC_CODEC_I444_12L, {PLANAR(3, 16, _444), .color_bits = 12} }, { VLC_CODEC_I444_16L, {PLANAR(3, 16, _444), .color_bits = 16} }, #endif { VLC_CODEC_YUVA, {PLANAR(4, 8, _444)} }, { VLC_CODEC_YUV422A, {PLANAR(4, 8, _422)} }, #ifdef WORDS_BIGENDIAN { VLC_CODEC_YUVA_444_10B, {PLANAR(4, 16, _444), .color_bits = 10} }, #else { VLC_CODEC_YUVA_444_10L, {PLANAR(4, 16, _444), .color_bits = 10} }, #endif { VLC_CODEC_NV12, {SEMIPLANAR(2, 8, _420)} }, { VLC_CODEC_NV21, {SEMIPLANAR(2, 8, _420)} }, { VLC_CODEC_P010, {SEMIPLANAR(2, 16, _420)} }, { VLC_CODEC_P016, {SEMIPLANAR(2, 16, _420)} }, { VLC_CODEC_NV16, {SEMIPLANAR(2, 8, _422)} }, { VLC_CODEC_NV61, {SEMIPLANAR(2, 8, _422)} }, { VLC_CODEC_NV24, {SEMIPLANAR(2, 8, _444)} }, { VLC_CODEC_NV42, {SEMIPLANAR(2, 8, _444)} }, { VLC_CODEC_RGB233, {PACKED(3, 2, 2)} }, { VLC_CODEC_BGR233, {PACKED(3, 2, 2)} }, { VLC_CODEC_RGB332, {PACKED(3, 2, 2)} }, { VLC_CODEC_RGB565LE, {PACKED(3, 5, 1)} }, { VLC_CODEC_RGB555LE, {PACKED(3, 5, 1)} }, { VLC_CODEC_RGB24, {PACKED(3, 8, 0)} }, { VLC_CODEC_BGR24, {PACKED(3, 8, 0)} }, { VLC_CODEC_RGBA, {PACKED(4, 8, 0)} }, { VLC_CODEC_BGRX, {PACKED(3, 8, 8)} }, { VLC_CODEC_BGRA, {PACKED(4, 8, 0)} }, { VLC_CODEC_GBR_PLANAR, {PLANAR(3, 8, _444)} }, #ifdef WORDS_BIGENDIAN { VLC_CODEC_GBR_PLANAR_9B, {PLANAR(3, 16, _444), .color_bits = 9} }, { VLC_CODEC_GBR_PLANAR_10B, {PLANAR(3, 16, _444), .color_bits = 10} }, { VLC_CODEC_GBR_PLANAR_16B, {PLANAR(3, 16, _444), .color_bits = 16} }, #else { VLC_CODEC_GBR_PLANAR_9L, {PLANAR(3, 16, _444), .color_bits = 9} }, { VLC_CODEC_GBR_PLANAR_10L, {PLANAR(3, 16, _444), .color_bits = 10} }, { VLC_CODEC_GBR_PLANAR_16L, {PLANAR(3, 16, _444), .color_bits = 16} }, #endif { VLC_CODEC_U8, {SINGLE(UNORM, 8)} }, { VLC_CODEC_S8, {SINGLE(SNORM, 8)} }, { VLC_CODEC_U16N, {SINGLE(UNORM, 16)} }, { VLC_CODEC_S16N, {SINGLE(SNORM, 16)} }, { VLC_CODEC_U24N, {SINGLE(UNORM, 24)} }, { VLC_CODEC_S24N, {SINGLE(SNORM, 24)} }, { VLC_CODEC_U32N, {SINGLE(UNORM, 32)} }, { VLC_CODEC_S32N, {SINGLE(SNORM, 32)} }, { VLC_CODEC_FL32, {SINGLE(FLOAT, 32)} }, { VLC_CODEC_FL64, {SINGLE(FLOAT, 64)} }, { 0 }, }; static const struct fmt_desc *FindDesc(vlc_fourcc_t fcc) { for (int i = 0; formats[i].fcc; i++) { if (formats[i].fcc == fcc) { return &formats[i].desc; } } return NULL; } // This fills everything except width/height, which are left as 1 static void FillDesc(vlc_fourcc_t fcc, const struct fmt_desc *desc, struct pl_plane_data data[4]) { assert(desc->num_planes <= 4); for (int i = 0; i < desc->num_planes; i++) { const struct plane_desc *p = &desc->planes[i]; data[i] = (struct pl_plane_data) { .type = desc->type, .width = 1, .height = 1, .pixel_stride = p->pixel_size, }; for (int c = 0; c < p->components; c++) { data[i].component_size[c] = p->comp_bits[c]; data[i].component_map[c] = p->comp_map[c]; } } // Exceptions to the rule switch (fcc) { case VLC_CODEC_YV9: case VLC_CODEC_YV12: // Planar Y:V:U data[1].component_map[0] = 2; data[2].component_map[0] = 1; break; case VLC_CODEC_BGRX: case VLC_CODEC_BGRA: // Packed BGR data[0].component_map[0] = 2; data[0].component_map[1] = 1; data[0].component_map[2] = 0; break; case VLC_CODEC_GBR_PLANAR: case VLC_CODEC_GBR_PLANAR_9L: case VLC_CODEC_GBR_PLANAR_10L: case VLC_CODEC_GBR_PLANAR_16L: case VLC_CODEC_GBR_PLANAR_9B: case VLC_CODEC_GBR_PLANAR_10B: case VLC_CODEC_GBR_PLANAR_16B: // Planar GBR data[0].component_map[0] = 1; data[1].component_map[0] = 2; data[2].component_map[0] = 0; break; case VLC_CODEC_RGB565LE: // 5:6:5 instead of 5:5:5 data[0].component_size[1] += 1; break; case VLC_CODEC_RGB233: // 2:3:3 instead of 2:2:2 data[0].component_size[1] += 1; data[0].component_size[2] += 1; break; case VLC_CODEC_BGR233: // 2:3:3 instead of 2:2:2 data[0].component_size[1] += 1; data[0].component_size[2] += 1; // Packed BGR data[0].component_map[0] = 2; data[0].component_map[1] = 1; data[0].component_map[2] = 0; break; case VLC_CODEC_RGB332: // 3:3:2 instead of 2:2:2 data[0].component_size[0] += 1; data[0].component_size[1] += 1; break; default: break; } } int vlc_placebo_PlaneFormat(const video_format_t *fmt, struct pl_plane_data data[4]) { const struct fmt_desc *desc = FindDesc(fmt->i_chroma); if (!desc) return 0; FillDesc(fmt->i_chroma, desc, data); for (int i = 0; i < desc->num_planes; i++) { const struct plane_desc *p = &desc->planes[i]; data[i].width = (fmt->i_visible_width + p->w_denom - 1) / p->w_denom; data[i].height = (fmt->i_visible_height + p->h_denom - 1) / p->h_denom; } return desc->num_planes; } int vlc_placebo_PlaneData(const picture_t *pic, struct pl_plane_data data[4], pl_buf buf) { int planes = vlc_placebo_PlaneFormat(&pic->format, data); if (!planes) return 0; assert(planes == pic->i_planes); for (int i = 0; i < planes; i++) { assert(data[i].height == pic->p[i].i_visible_lines); data[i].row_stride = pic->p[i].i_pitch; if (buf) { assert(buf->data); assert(pic->p[i].p_pixels <= buf->data + buf->params.size); data[i].buf = buf; data[i].buf_offset = (uintptr_t) pic->p[i].p_pixels - (ptrdiff_t) buf->data; } else { data[i].pixels = pic->p[i].p_pixels; } } return planes; } bool vlc_placebo_FormatSupported(pl_gpu gpu, vlc_fourcc_t fcc) { const struct fmt_desc *desc = FindDesc(fcc); if (!desc) return false; struct pl_plane_data data[4]; FillDesc(fcc, desc, data); for (int i = 0; i < desc->num_planes; i++) { if (!pl_plane_find_fmt(gpu, NULL, &data[i])) return false; } return true; } struct pl_color_space vlc_placebo_ColorSpace(const video_format_t *fmt) { static const enum pl_color_primaries primaries[COLOR_PRIMARIES_MAX+1] = { [COLOR_PRIMARIES_UNDEF] = PL_COLOR_PRIM_UNKNOWN, [COLOR_PRIMARIES_BT601_525] = PL_COLOR_PRIM_BT_601_525, [COLOR_PRIMARIES_BT601_625] = PL_COLOR_PRIM_BT_601_625, [COLOR_PRIMARIES_BT709] = PL_COLOR_PRIM_BT_709, [COLOR_PRIMARIES_BT2020] = PL_COLOR_PRIM_BT_2020, [COLOR_PRIMARIES_DCI_P3] = PL_COLOR_PRIM_DCI_P3, [COLOR_PRIMARIES_BT470_M] = PL_COLOR_PRIM_BT_470M, }; static const enum pl_color_transfer transfers[TRANSFER_FUNC_MAX+1] = { [TRANSFER_FUNC_UNDEF] = PL_COLOR_TRC_UNKNOWN, [TRANSFER_FUNC_LINEAR] = PL_COLOR_TRC_LINEAR, [TRANSFER_FUNC_SRGB] = PL_COLOR_TRC_SRGB, [TRANSFER_FUNC_SMPTE_ST2084] = PL_COLOR_TRC_PQ, [TRANSFER_FUNC_HLG] = PL_COLOR_TRC_HLG, // these are all designed to be displayed on BT.1886 displays, so this // is the correct way to handle them in libplacebo [TRANSFER_FUNC_BT470_BG] = PL_COLOR_TRC_BT_1886, [TRANSFER_FUNC_BT470_M] = PL_COLOR_TRC_BT_1886, [TRANSFER_FUNC_BT709] = PL_COLOR_TRC_BT_1886, [TRANSFER_FUNC_SMPTE_240] = PL_COLOR_TRC_BT_1886, }; return (struct pl_color_space) { .primaries = primaries[fmt->primaries], .transfer = transfers[fmt->transfer], .hdr = { .prim = { .green.x = fmt->mastering.primaries[0], .green.y = fmt->mastering.primaries[1], .blue.x = fmt->mastering.primaries[2], .blue.y = fmt->mastering.primaries[3], .red.x = fmt->mastering.primaries[4], .red.y = fmt->mastering.primaries[5], .white.x = fmt->mastering.white_point[0], .white.y = fmt->mastering.white_point[1], }, .min_luma = fmt->mastering.min_luminance, .max_luma = fmt->mastering.max_luminance, .max_cll = fmt->lighting.MaxCLL, .max_fall = fmt->lighting.MaxFALL, }, }; } struct pl_color_repr vlc_placebo_ColorRepr(const video_format_t *fmt) { static const enum pl_color_system yuv_systems[COLOR_SPACE_MAX+1] = { [COLOR_SPACE_UNDEF] = PL_COLOR_SYSTEM_BT_709, // _UNKNOWN is RGB [COLOR_SPACE_BT601] = PL_COLOR_SYSTEM_BT_601, [COLOR_SPACE_BT709] = PL_COLOR_SYSTEM_BT_709, [COLOR_SPACE_BT2020] = PL_COLOR_SYSTEM_BT_2020_NC, }; // fmt->space describes the YCbCr type only, it does not distinguish // between YUV, XYZ, RGB and the likes! enum pl_color_system sys; if (likely(vlc_fourcc_IsYUV(fmt->i_chroma))) { sys = yuv_systems[fmt->space]; } else if (unlikely(fmt->i_chroma == VLC_CODEC_XYZ12)) { sys = PL_COLOR_SYSTEM_XYZ; } else { sys = PL_COLOR_SYSTEM_RGB; } const struct fmt_desc *desc = FindDesc(fmt->i_chroma); int sample_depth = desc->planes[0].comp_bits[0]; // just use first component return (struct pl_color_repr) { .sys = sys, .alpha = PL_ALPHA_PREMULTIPLIED, .levels = unlikely(fmt->color_range == COLOR_RANGE_FULL) ? PL_COLOR_LEVELS_PC : PL_COLOR_LEVELS_TV, .bits = { .sample_depth = sample_depth, .color_depth = desc->color_bits ? desc->color_bits : sample_depth, .bit_shift = 0, }, }; } void vlc_placebo_HdrMetadata(const vlc_video_hdr_dynamic_metadata_t *src, struct pl_hdr_metadata *dst) { #if PL_API_VER >= 242 for (size_t i = 0; i < ARRAY_SIZE(dst->scene_max); i++) dst->scene_max[i] = src->maxscl[i]; dst->scene_avg = src->average_maxrgb; if (src->tone_mapping_flag) { static_assert(sizeof(dst->ootf.anchors) == sizeof(src->bezier_curve_anchors), "array mismatch"); memcpy(dst->ootf.anchors, src->bezier_curve_anchors, sizeof(dst->ootf.anchors)); dst->ootf.num_anchors = src->num_bezier_anchors; dst->ootf.target_luma = src->targeted_luminance; dst->ootf.knee_x = src->knee_point_x; dst->ootf.knee_y = src->knee_point_y; } #else (void) src; (void) dst; #endif } void vlc_placebo_DoviMetadata(const vlc_video_dovi_metadata_t *src, struct pl_dovi_metadata *dst) { static_assert(sizeof(dst->nonlinear_offset) == sizeof(src->nonlinear_offset), "array mismatch"); static_assert(sizeof(dst->nonlinear) == sizeof(src->nonlinear_matrix), "matrix mismatch"); static_assert(sizeof(dst->linear) == sizeof(src->linear_matrix), "matrix mismatch"); memcpy(dst->nonlinear_offset, src->nonlinear_offset, sizeof(dst->nonlinear_offset)); memcpy(dst->nonlinear.m[0], src->nonlinear_matrix, sizeof(dst->nonlinear.m)); memcpy(dst->linear.m[0], src->linear_matrix, sizeof(dst->linear.m)); for (size_t c = 0; c < ARRAY_SIZE(dst->comp); c++) { const struct vlc_dovi_reshape_t *csrc = &src->curves[c]; struct pl_reshape_data *cdst = &dst->comp[c]; cdst->num_pivots = csrc->num_pivots; assert(csrc->num_pivots <= ARRAY_SIZE(csrc->pivots)); for (int i = 0; i < csrc->num_pivots; i++) { const float scale = 1.0f / ((1 << src->bl_bit_depth) - 1); cdst->pivots[i] = scale * csrc->pivots[i]; } for (int i = 0; i < csrc->num_pivots - 1; i++) { const float scale = 1.0f / (1 << src->coef_log2_denom); cdst->method[i] = csrc->mapping_idc[i]; switch (csrc->mapping_idc[i]) { case VLC_DOVI_RESHAPE_POLYNOMIAL: for (size_t k = 0; k < ARRAY_SIZE(cdst->poly_coeffs[i]); k++) { cdst->poly_coeffs[i][k] = (k <= csrc->poly_order[i]) ? scale * csrc->poly_coef[i][k] : 0.0f; } break; case VLC_DOVI_RESHAPE_MMR: cdst->mmr_order[i] = csrc->mmr_order[i]; cdst->mmr_constant[i] = scale * csrc->mmr_constant[i]; for (int j = 0; j < csrc->mmr_order[i]; j++) { for (size_t k = 0; k < ARRAY_SIZE(cdst->mmr_coeffs[i][j]); k++) cdst->mmr_coeffs[i][j][k] = scale * csrc->mmr_coef[i][j][k]; } break; } } } } void vlc_placebo_frame_DoviMetadata(struct pl_frame *frame, const picture_t *pic, struct pl_dovi_metadata *dst) { struct vlc_ancillary *ancillary = picture_GetAncillary(pic, VLC_ANCILLARY_ID_DOVI); if (!ancillary) return; const vlc_video_dovi_metadata_t *src = vlc_ancillary_GetData(ancillary); vlc_placebo_DoviMetadata(src, dst); // The output of the Dolby Vision reshaping process is always BT.2020/PQ, // no matter the color space of the base layer, so override these fields frame->color.primaries = PL_COLOR_PRIM_BT_2020; frame->color.transfer = PL_COLOR_TRC_PQ; frame->repr.sys = PL_COLOR_SYSTEM_DOLBYVISION; frame->repr.dovi = dst; // These fields are specified to always have 12-bit precision const float scale = 1.0f / ((1 << 12) - 1); frame->color.hdr.min_luma = pl_hdr_rescale(PL_HDR_PQ, PL_HDR_NITS, scale * src->source_min_pq); frame->color.hdr.max_luma = pl_hdr_rescale(PL_HDR_PQ, PL_HDR_NITS, scale * src->source_max_pq); } enum pl_chroma_location vlc_placebo_ChromaLoc(const video_format_t *fmt) { static const enum pl_chroma_location locs[CHROMA_LOCATION_MAX+1] = { [CHROMA_LOCATION_UNDEF] = PL_CHROMA_UNKNOWN, [CHROMA_LOCATION_LEFT] = PL_CHROMA_LEFT, [CHROMA_LOCATION_CENTER] = PL_CHROMA_CENTER, [CHROMA_LOCATION_TOP_LEFT] = PL_CHROMA_TOP_LEFT, [CHROMA_LOCATION_TOP_CENTER] = PL_CHROMA_TOP_CENTER, [CHROMA_LOCATION_BOTTOM_LEFT] = PL_CHROMA_BOTTOM_LEFT, [CHROMA_LOCATION_BOTTOM_CENTER] = PL_CHROMA_BOTTOM_CENTER, }; return locs[fmt->chroma_location]; } int vlc_placebo_PlaneComponents(const video_format_t *fmt, struct pl_plane planes[4]) { const struct fmt_desc *desc = FindDesc(fmt->i_chroma); if (!desc) return 0; for (int i = 0; i < desc->num_planes; i++) { const struct plane_desc *p = &desc->planes[i]; planes[i].components = p->components; for (int c = 0; c < p->components; ++c) planes[i].component_mapping[c] = p->comp_map[c]; } return desc->num_planes; } void vlc_placebo_ColorMapParams(vlc_object_t *obj, const char *prefix, struct pl_color_map_params *params) { #define PREFIX(str) (snprintf(opt, sizeof(opt), "%s-%s", prefix, str), opt) char opt[64]; *params = pl_color_map_default_params; switch (var_InheritInteger(obj, PREFIX("gamut-mapping"))) { case GAMUT_AUTO: break; #if PL_API_VER >= 269 case GAMUT_CLIP: params->gamut_mapping = &pl_gamut_map_clip; break; case GAMUT_PERCEPTUAL: params->gamut_mapping = &pl_gamut_map_perceptual; break; case GAMUT_RELATIVE: params->gamut_mapping = &pl_gamut_map_relative; break; case GAMUT_SATURATION: params->gamut_mapping = &pl_gamut_map_saturation; break; case GAMUT_ABSOLUTE: params->gamut_mapping = &pl_gamut_map_absolute; break; case GAMUT_DESATURATE: params->gamut_mapping = &pl_gamut_map_desaturate; break; case GAMUT_DARKEN: params->gamut_mapping = &pl_gamut_map_darken; break; case GAMUT_WARN: params->gamut_mapping = &pl_gamut_map_highlight; break; case GAMUT_LINEAR: params->gamut_mapping = &pl_gamut_map_linear; break; #else case GAMUT_CLIP: params->gamut_mode = PL_GAMUT_CLIP; break; case GAMUT_PERCEPTUAL: break; // unsupported case GAMUT_RELATIVE: params->intent = PL_INTENT_RELATIVE_COLORIMETRIC; break; case GAMUT_SATURATION: params->intent = PL_INTENT_SATURATION; break; case GAMUT_ABSOLUTE: params->intent = PL_INTENT_ABSOLUTE_COLORIMETRIC; break; case GAMUT_DESATURATE: params->gamut_mode = PL_GAMUT_DESATURATE; break; case GAMUT_DARKEN: params->gamut_mode = PL_GAMUT_DARKEN; break; case GAMUT_WARN: params->gamut_mode = PL_GAMUT_WARN; break; case GAMUT_LINEAR: break; // unsupported #endif } switch (var_InheritInteger(obj, PREFIX("tone-mapping-function"))) { case TONEMAP_AUTO: break; case TONEMAP_CLIP: params->tone_mapping_function = &pl_tone_map_clip; break; case TONEMAP_BT2390: params->tone_mapping_function = &pl_tone_map_bt2390; break; case TONEMAP_REINHARD: params->tone_mapping_function = &pl_tone_map_reinhard; break; case TONEMAP_MOBIUS: params->tone_mapping_function = &pl_tone_map_mobius; break; case TONEMAP_HABLE: params->tone_mapping_function = &pl_tone_map_hable; break; case TONEMAP_GAMMA: params->tone_mapping_function = &pl_tone_map_gamma; break; case TONEMAP_LINEAR: params->tone_mapping_function = &pl_tone_map_linear; break; case TONEMAP_BT2446A: params->tone_mapping_function = &pl_tone_map_bt2446a; break; case TONEMAP_SPLINE: params->tone_mapping_function = &pl_tone_map_spline; break; } params->tone_mapping_param = var_InheritFloat(obj, PREFIX("tone-mapping-param")); params->inverse_tone_mapping = var_InheritBool(obj, PREFIX("inverse-tone-mapping")); }