summaryrefslogtreecommitdiffstats
path: root/video/out/gpu/lcms.c
diff options
context:
space:
mode:
Diffstat (limited to 'video/out/gpu/lcms.c')
-rw-r--r--video/out/gpu/lcms.c251
1 files changed, 123 insertions, 128 deletions
diff --git a/video/out/gpu/lcms.c b/video/out/gpu/lcms.c
index 0f3a0bf646..c197acf48e 100644
--- a/video/out/gpu/lcms.c
+++ b/video/out/gpu/lcms.c
@@ -46,58 +46,14 @@ struct gl_lcms {
char *current_profile;
bool using_memory_profile;
bool changed;
- enum mp_csp_prim current_prim;
- enum mp_csp_trc current_trc;
+ enum pl_color_primaries current_prim;
+ enum pl_color_transfer current_trc;
struct mp_log *log;
struct mpv_global *global;
struct mp_icc_opts *opts;
};
-static bool parse_3dlut_size(const char *arg, int *p1, int *p2, int *p3)
-{
- if (sscanf(arg, "%dx%dx%d", p1, p2, p3) != 3)
- return false;
- for (int n = 0; n < 3; n++) {
- int s = ((int[]) { *p1, *p2, *p3 })[n];
- if (s < 2 || s > 512)
- return false;
- }
- return true;
-}
-
-static int validate_3dlut_size_opt(struct mp_log *log, const m_option_t *opt,
- struct bstr name, struct bstr param)
-{
- int p1, p2, p3;
- char s[20];
- snprintf(s, sizeof(s), "%.*s", BSTR_P(param));
- return parse_3dlut_size(s, &p1, &p2, &p3);
-}
-
-#define OPT_BASE_STRUCT struct mp_icc_opts
-const struct m_sub_options mp_icc_conf = {
- .opts = (const m_option_t[]) {
- {"use-embedded-icc-profile", OPT_FLAG(use_embedded)},
- {"icc-profile", OPT_STRING(profile), .flags = M_OPT_FILE},
- {"icc-profile-auto", OPT_FLAG(profile_auto)},
- {"icc-cache-dir", OPT_STRING(cache_dir), .flags = M_OPT_FILE},
- {"icc-intent", OPT_INT(intent)},
- {"icc-contrast", OPT_CHOICE(contrast, {"inf", -1}),
- M_RANGE(0, 1000000)},
- {"icc-3dlut-size", OPT_STRING_VALIDATE(size_str, validate_3dlut_size_opt)},
- {"3dlut-size", OPT_REPLACED("icc-3dlut-size")},
- {"icc-cache", OPT_REMOVED("see icc-cache-dir")},
- {0}
- },
- .size = sizeof(struct mp_icc_opts),
- .defaults = &(const struct mp_icc_opts) {
- .size_str = "64x64x64",
- .intent = INTENT_RELATIVE_COLORIMETRIC,
- .use_embedded = true,
- },
-};
-
static void lcms2_error_handler(cmsContext ctx, cmsUInt32Number code,
const char *msg)
{
@@ -206,8 +162,8 @@ static bool vid_profile_eq(struct AVBufferRef *a, struct AVBufferRef *b)
// Return whether the profile or config has changed since the last time it was
// retrieved. If it has changed, gl_lcms_get_lut3d() should be called.
-bool gl_lcms_has_changed(struct gl_lcms *p, enum mp_csp_prim prim,
- enum mp_csp_trc trc, struct AVBufferRef *vid_profile)
+bool gl_lcms_has_changed(struct gl_lcms *p, enum pl_color_primaries prim,
+ enum pl_color_transfer trc, struct AVBufferRef *vid_profile)
{
if (p->changed || p->current_prim != prim || p->current_trc != trc)
return true;
@@ -224,7 +180,7 @@ bool gl_lcms_has_profile(struct gl_lcms *p)
static cmsHPROFILE get_vid_profile(struct gl_lcms *p, cmsContext cms,
cmsHPROFILE disp_profile,
- enum mp_csp_prim prim, enum mp_csp_trc trc)
+ enum pl_color_primaries prim, enum pl_color_transfer trc)
{
if (p->opts->use_embedded && p->vid_profile) {
// Try using the embedded ICC profile
@@ -241,78 +197,81 @@ static cmsHPROFILE get_vid_profile(struct gl_lcms *p, cmsContext cms,
// The input profile for the transformation is dependent on the video
// primaries and transfer characteristics
- struct mp_csp_primaries csp = mp_get_csp_primaries(prim);
- cmsCIExyY wp_xyY = {csp.white.x, csp.white.y, 1.0};
+ const struct pl_raw_primaries *csp = pl_raw_primaries_get(prim);
+ cmsCIExyY wp_xyY = {csp->white.x, csp->white.y, 1.0};
cmsCIExyYTRIPLE prim_xyY = {
- .Red = {csp.red.x, csp.red.y, 1.0},
- .Green = {csp.green.x, csp.green.y, 1.0},
- .Blue = {csp.blue.x, csp.blue.y, 1.0},
+ .Red = {csp->red.x, csp->red.y, 1.0},
+ .Green = {csp->green.x, csp->green.y, 1.0},
+ .Blue = {csp->blue.x, csp->blue.y, 1.0},
};
cmsToneCurve *tonecurve[3] = {0};
switch (trc) {
- case MP_CSP_TRC_LINEAR: tonecurve[0] = cmsBuildGamma(cms, 1.0); break;
- case MP_CSP_TRC_GAMMA18: tonecurve[0] = cmsBuildGamma(cms, 1.8); break;
- case MP_CSP_TRC_GAMMA20: tonecurve[0] = cmsBuildGamma(cms, 2.0); break;
- case MP_CSP_TRC_GAMMA22: tonecurve[0] = cmsBuildGamma(cms, 2.2); break;
- case MP_CSP_TRC_GAMMA24: tonecurve[0] = cmsBuildGamma(cms, 2.4); break;
- case MP_CSP_TRC_GAMMA26: tonecurve[0] = cmsBuildGamma(cms, 2.6); break;
- case MP_CSP_TRC_GAMMA28: tonecurve[0] = cmsBuildGamma(cms, 2.8); break;
-
- case MP_CSP_TRC_SRGB:
+ case PL_COLOR_TRC_LINEAR: tonecurve[0] = cmsBuildGamma(cms, 1.0); break;
+ case PL_COLOR_TRC_GAMMA18: tonecurve[0] = cmsBuildGamma(cms, 1.8); break;
+ case PL_COLOR_TRC_GAMMA20: tonecurve[0] = cmsBuildGamma(cms, 2.0); break;
+ case PL_COLOR_TRC_GAMMA22: tonecurve[0] = cmsBuildGamma(cms, 2.2); break;
+ case PL_COLOR_TRC_GAMMA24: tonecurve[0] = cmsBuildGamma(cms, 2.4); break;
+ case PL_COLOR_TRC_GAMMA26: tonecurve[0] = cmsBuildGamma(cms, 2.6); break;
+ case PL_COLOR_TRC_GAMMA28: tonecurve[0] = cmsBuildGamma(cms, 2.8); break;
+
+ case PL_COLOR_TRC_ST428:
+ tonecurve[0] = cmsBuildParametricToneCurve(cms, 2,
+ (double[3]){2.6, pow(52.37/48.0, 1/2.6), 0.0});
+ break;
+
+ case PL_COLOR_TRC_SRGB:
// Values copied from Little-CMS
tonecurve[0] = cmsBuildParametricToneCurve(cms, 4,
(double[5]){2.40, 1/1.055, 0.055/1.055, 1/12.92, 0.04045});
break;
- case MP_CSP_TRC_PRO_PHOTO:
+ case PL_COLOR_TRC_PRO_PHOTO:
tonecurve[0] = cmsBuildParametricToneCurve(cms, 4,
(double[5]){1.8, 1.0, 0.0, 1/16.0, 0.03125});
break;
- case MP_CSP_TRC_BT_1886: {
- // To build an appropriate BT.1886 transformation we need access to
- // the display's black point, so we LittleCMS' detection function.
- // Relative colorimetric is used since we want to approximate the
- // BT.1886 to the target device's actual black point even in e.g.
- // perceptual mode
- const int intent = MP_INTENT_RELATIVE_COLORIMETRIC;
- cmsCIEXYZ bp_XYZ;
- if (!cmsDetectBlackPoint(&bp_XYZ, disp_profile, intent, 0))
- return false;
-
- // Map this XYZ value back into the (linear) source space
- cmsToneCurve *linear = cmsBuildGamma(cms, 1.0);
- cmsHPROFILE rev_profile = cmsCreateRGBProfileTHR(cms, &wp_xyY, &prim_xyY,
- (cmsToneCurve*[3]){linear, linear, linear});
- cmsHPROFILE xyz_profile = cmsCreateXYZProfile();
- cmsHTRANSFORM xyz2src = cmsCreateTransformTHR(cms,
- xyz_profile, TYPE_XYZ_DBL, rev_profile, TYPE_RGB_DBL,
- intent, 0);
- cmsFreeToneCurve(linear);
- cmsCloseProfile(rev_profile);
- cmsCloseProfile(xyz_profile);
- if (!xyz2src)
- return false;
-
+ case PL_COLOR_TRC_BT_1886: {
double src_black[3];
- cmsDoTransform(xyz2src, &bp_XYZ, src_black, 1);
- cmsDeleteTransform(xyz2src);
-
- // Contrast limiting
- if (p->opts->contrast > 0) {
+ if (p->opts->contrast < 0) {
+ // User requested infinite contrast, return 2.4 profile
+ tonecurve[0] = cmsBuildGamma(cms, 2.4);
+ break;
+ } else if (p->opts->contrast > 0) {
+ MP_VERBOSE(p, "Using specified contrast: %d\n", p->opts->contrast);
for (int i = 0; i < 3; i++)
- src_black[i] = MPMAX(src_black[i], 1.0 / p->opts->contrast);
- }
-
- // Built-in contrast failsafe
- double contrast = 3.0 / (src_black[0] + src_black[1] + src_black[2]);
- MP_VERBOSE(p, "Detected ICC profile contrast: %f\n", contrast);
- if (contrast > 100000 && !p->opts->contrast) {
- MP_WARN(p, "ICC profile detected contrast very high (>100000),"
- " falling back to contrast 1000 for sanity. Set the"
- " icc-contrast option to silence this warning.\n");
- src_black[0] = src_black[1] = src_black[2] = 1.0 / 1000;
+ src_black[i] = 1.0 / p->opts->contrast;
+ } else {
+ // To build an appropriate BT.1886 transformation we need access to
+ // the display's black point, so we use LittleCMS' detection
+ // function. Relative colorimetric is used since we want to
+ // approximate the BT.1886 to the target device's actual black
+ // point even in e.g. perceptual mode
+ const int intent = PL_INTENT_RELATIVE_COLORIMETRIC;
+ cmsCIEXYZ bp_XYZ;
+ if (!cmsDetectBlackPoint(&bp_XYZ, disp_profile, intent, 0))
+ return false;
+
+ // Map this XYZ value back into the (linear) source space
+ cmsHPROFILE rev_profile;
+ cmsToneCurve *linear = cmsBuildGamma(cms, 1.0);
+ rev_profile = cmsCreateRGBProfileTHR(cms, &wp_xyY, &prim_xyY,
+ (cmsToneCurve*[3]){linear, linear, linear});
+ cmsHPROFILE xyz_profile = cmsCreateXYZProfile();
+ cmsHTRANSFORM xyz2src = cmsCreateTransformTHR(cms,
+ xyz_profile, TYPE_XYZ_DBL, rev_profile, TYPE_RGB_DBL,
+ intent, cmsFLAGS_NOCACHE | cmsFLAGS_NOOPTIMIZE);
+ cmsFreeToneCurve(linear);
+ cmsCloseProfile(rev_profile);
+ cmsCloseProfile(xyz_profile);
+ if (!xyz2src)
+ return false;
+
+ cmsDoTransform(xyz2src, &bp_XYZ, src_black, 1);
+ cmsDeleteTransform(xyz2src);
+
+ double contrast = 3.0 / (src_black[0] + src_black[1] + src_black[2]);
+ MP_VERBOSE(p, "Detected ICC profile contrast: %f\n", contrast);
}
// Build the parametric BT.1886 transfer curve, one per channel
@@ -346,7 +305,7 @@ static cmsHPROFILE get_vid_profile(struct gl_lcms *p, cmsContext cms,
}
bool gl_lcms_get_lut3d(struct gl_lcms *p, struct lut3d **result_lut3d,
- enum mp_csp_prim prim, enum mp_csp_trc trc,
+ enum pl_color_primaries prim, enum pl_color_transfer trc,
struct AVBufferRef *vid_profile)
{
int s_r, s_g, s_b;
@@ -363,23 +322,28 @@ bool gl_lcms_get_lut3d(struct gl_lcms *p, struct lut3d **result_lut3d,
if (vid_profile) {
MP_VERBOSE(p, "Got an embedded ICC profile.\n");
p->vid_profile = av_buffer_ref(vid_profile);
- if (!p->vid_profile)
- abort();
+ MP_HANDLE_OOM(p->vid_profile);
}
- if (!parse_3dlut_size(p->opts->size_str, &s_r, &s_g, &s_b))
+ if (!gl_parse_3dlut_size(p->opts->size_str, &s_r, &s_g, &s_b))
return false;
if (!gl_lcms_has_profile(p))
return false;
+ // For simplicity, default to 65x65x65, which is large enough to cover
+ // typical profiles with good accuracy while not being too wasteful
+ s_r = s_r ? s_r : 65;
+ s_g = s_g ? s_g : 65;
+ s_b = s_b ? s_b : 65;
+
void *tmp = talloc_new(NULL);
uint16_t *output = talloc_array(tmp, uint16_t, s_r * s_g * s_b * 4);
struct lut3d *lut = NULL;
cmsContext cms = NULL;
char *cache_file = NULL;
- if (p->opts->cache_dir && p->opts->cache_dir[0]) {
+ if (p->opts->cache) {
// Gamma is included in the header to help uniquely identify it,
// because we may change the parameter in the future or make it
// customizable, same for the primaries.
@@ -390,8 +354,7 @@ bool gl_lcms_get_lut3d(struct gl_lcms *p, struct lut3d **result_lut3d,
uint8_t hash[32];
struct AVSHA *sha = av_sha_alloc();
- if (!sha)
- abort();
+ MP_HANDLE_OOM(sha);
av_sha_init(sha, 256);
av_sha_update(sha, cache_info, strlen(cache_info));
if (vid_profile)
@@ -400,13 +363,20 @@ bool gl_lcms_get_lut3d(struct gl_lcms *p, struct lut3d **result_lut3d,
av_sha_final(sha, hash);
av_free(sha);
- char *cache_dir = mp_get_user_path(tmp, p->global, p->opts->cache_dir);
- cache_file = talloc_strdup(tmp, "");
- for (int i = 0; i < sizeof(hash); i++)
- cache_file = talloc_asprintf_append(cache_file, "%02X", hash[i]);
- cache_file = mp_path_join(tmp, cache_dir, cache_file);
+ char *cache_dir = p->opts->cache_dir;
+ if (cache_dir && cache_dir[0]) {
+ cache_dir = mp_get_user_path(tmp, p->global, cache_dir);
+ } else {
+ cache_dir = mp_find_user_file(tmp, p->global, "cache", "");
+ }
- mp_mkdirp(cache_dir);
+ if (cache_dir && cache_dir[0]) {
+ cache_file = talloc_strdup(tmp, "");
+ for (int i = 0; i < sizeof(hash); i++)
+ cache_file = talloc_asprintf_append(cache_file, "%02X", hash[i]);
+ cache_file = mp_path_join(tmp, cache_dir, cache_file);
+ mp_mkdirp(cache_dir);
+ }
}
// check cache
@@ -441,7 +411,8 @@ bool gl_lcms_get_lut3d(struct gl_lcms *p, struct lut3d **result_lut3d,
cmsHTRANSFORM trafo = cmsCreateTransformTHR(cms, vid_hprofile, TYPE_RGB_16,
profile, TYPE_RGBA_16,
p->opts->intent,
- cmsFLAGS_HIGHRESPRECALC |
+ cmsFLAGS_NOCACHE |
+ cmsFLAGS_NOOPTIMIZE |
cmsFLAGS_BLACKPOINTCOMPENSATION);
cmsCloseProfile(profile);
cmsCloseProfile(vid_hprofile);
@@ -498,12 +469,6 @@ error_exit:
#else /* HAVE_LCMS2 */
-const struct m_sub_options mp_icc_conf = {
- .opts = (const m_option_t[]) { {0} },
- .size = sizeof(struct mp_icc_opts),
- .defaults = &(const struct mp_icc_opts) {0},
-};
-
struct gl_lcms *gl_lcms_init(void *talloc_ctx, struct mp_log *log,
struct mpv_global *global,
struct mp_icc_opts *opts)
@@ -514,8 +479,8 @@ struct gl_lcms *gl_lcms_init(void *talloc_ctx, struct mp_log *log,
void gl_lcms_update_options(struct gl_lcms *p) { }
bool gl_lcms_set_memory_profile(struct gl_lcms *p, bstr profile) {return false;}
-bool gl_lcms_has_changed(struct gl_lcms *p, enum mp_csp_prim prim,
- enum mp_csp_trc trc, struct AVBufferRef *vid_profile)
+bool gl_lcms_has_changed(struct gl_lcms *p, enum pl_color_primaries prim,
+ enum pl_color_transfer trc, struct AVBufferRef *vid_profile)
{
return false;
}
@@ -526,10 +491,40 @@ bool gl_lcms_has_profile(struct gl_lcms *p)
}
bool gl_lcms_get_lut3d(struct gl_lcms *p, struct lut3d **result_lut3d,
- enum mp_csp_prim prim, enum mp_csp_trc trc,
+ enum pl_color_primaries prim, enum pl_color_transfer trc,
struct AVBufferRef *vid_profile)
{
return false;
}
#endif
+
+static inline OPT_STRING_VALIDATE_FUNC(validate_3dlut_size_opt)
+{
+ int p1, p2, p3;
+ return gl_parse_3dlut_size(*value, &p1, &p2, &p3) ? 0 : M_OPT_INVALID;
+}
+
+#define OPT_BASE_STRUCT struct mp_icc_opts
+const struct m_sub_options mp_icc_conf = {
+ .opts = (const m_option_t[]) {
+ {"use-embedded-icc-profile", OPT_BOOL(use_embedded)},
+ {"icc-profile", OPT_STRING(profile), .flags = M_OPT_FILE},
+ {"icc-profile-auto", OPT_BOOL(profile_auto)},
+ {"icc-cache", OPT_BOOL(cache)},
+ {"icc-cache-dir", OPT_STRING(cache_dir), .flags = M_OPT_FILE},
+ {"icc-intent", OPT_INT(intent)},
+ {"icc-force-contrast", OPT_CHOICE(contrast, {"no", 0}, {"inf", -1}),
+ M_RANGE(0, 1000000)},
+ {"icc-3dlut-size", OPT_STRING_VALIDATE(size_str, validate_3dlut_size_opt)},
+ {"icc-use-luma", OPT_BOOL(icc_use_luma)},
+ {0}
+ },
+ .size = sizeof(struct mp_icc_opts),
+ .defaults = &(const struct mp_icc_opts) {
+ .size_str = "auto",
+ .intent = PL_INTENT_RELATIVE_COLORIMETRIC,
+ .use_embedded = true,
+ .cache = true,
+ },
+};