Skip to content

Commit efff80c

Browse files
committed
swscale/graph: add color mapping pass
This leverages the previously introduced color management subsystem in order to adapt between transfer functions and color spaces, as well as for HDR tone mapping. Take special care to handle grayscale formats without a colorspace gracefully.
1 parent a57fe51 commit efff80c

File tree

1 file changed

+93
-2
lines changed

1 file changed

+93
-2
lines changed

libswscale/graph.c

Lines changed: 93 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
#include "libswscale/swscale.h"
3131
#include "libswscale/utils.h"
3232

33+
#include "cms.h"
34+
#include "lut3d.h"
3335
#include "swscale_internal.h"
3436
#include "graph.h"
3537

@@ -463,18 +465,107 @@ static int add_legacy_sws_pass(SwsGraph *graph, SwsFormat src, SwsFormat dst,
463465
return 0;
464466
}
465467

468+
/**************************
469+
* Gamut and tone mapping *
470+
**************************/
471+
472+
static void free_lut3d(void *priv)
473+
{
474+
SwsLut3D *lut = priv;
475+
sws_lut3d_free(&lut);
476+
}
477+
478+
static void run_lut3d(const SwsImg *out_base, const SwsImg *in_base,
479+
int y, int h, const SwsPass *pass)
480+
{
481+
SwsLut3D *lut = pass->priv;
482+
const SwsImg in = shift_img(in_base, y);
483+
const SwsImg out = shift_img(out_base, y);
484+
485+
sws_lut3d_apply(lut, in.data[0], in.linesize[0], out.data[0],
486+
out.linesize[0], pass->width, h);
487+
}
488+
489+
static int adapt_colors(SwsGraph *graph, SwsFormat src, SwsFormat dst,
490+
SwsPass *input, SwsPass **output)
491+
{
492+
enum AVPixelFormat fmt_in, fmt_out;
493+
SwsColorMap map = {0};
494+
SwsLut3D *lut;
495+
SwsPass *pass;
496+
int ret;
497+
498+
/**
499+
* Grayspace does not really have primaries, so just force the use of
500+
* the equivalent other primary set to avoid a conversion. Technically,
501+
* this does affect the weights used for the Grayscale conversion, but
502+
* in practise, that should give the expected results more often than not.
503+
*/
504+
if (isGray(dst.format)) {
505+
dst.color = src.color;
506+
} else if (isGray(src.format)) {
507+
src.color = dst.color;
508+
}
509+
510+
/* Fully infer color spaces before color mapping logic */
511+
graph->incomplete |= ff_infer_colors(&src.color, &dst.color);
512+
513+
map.intent = graph->ctx->intent;
514+
map.src = src.color;
515+
map.dst = dst.color;
516+
517+
if (sws_color_map_noop(&map))
518+
return 0;
519+
520+
lut = sws_lut3d_alloc();
521+
if (!lut)
522+
return AVERROR(ENOMEM);
523+
524+
fmt_in = sws_lut3d_pick_pixfmt(src, 0);
525+
fmt_out = sws_lut3d_pick_pixfmt(dst, 1);
526+
if (fmt_in != src.format) {
527+
SwsFormat tmp = src;
528+
tmp.format = fmt_in;
529+
ret = add_legacy_sws_pass(graph, src, tmp, input, &input);
530+
if (ret < 0)
531+
return ret;
532+
}
533+
534+
ret = sws_lut3d_generate(lut, fmt_in, fmt_out, &map);
535+
if (ret < 0) {
536+
sws_lut3d_free(&lut);
537+
return ret;
538+
}
539+
540+
pass = pass_add(graph, lut, fmt_out, src.width, src.height,
541+
input, 1, run_lut3d);
542+
if (!pass) {
543+
sws_lut3d_free(&lut);
544+
return AVERROR(ENOMEM);
545+
}
546+
pass->free = free_lut3d;
547+
548+
*output = pass;
549+
return 0;
550+
}
466551

467552
/***************************************
468553
* Main filter graph construction code *
469554
***************************************/
470555

471556
static int init_passes(SwsGraph *graph)
472557
{
473-
const SwsFormat src = graph->src;
474-
const SwsFormat dst = graph->dst;
558+
SwsFormat src = graph->src;
559+
SwsFormat dst = graph->dst;
475560
SwsPass *pass = NULL; /* read from main input image */
476561
int ret;
477562

563+
ret = adapt_colors(graph, src, dst, pass, &pass);
564+
if (ret < 0)
565+
return ret;
566+
src.format = pass ? pass->format : src.format;
567+
src.color = dst.color;
568+
478569
if (!ff_fmt_equal(&src, &dst)) {
479570
ret = add_legacy_sws_pass(graph, src, dst, pass, &pass);
480571
if (ret < 0)

0 commit comments

Comments
 (0)