avcodec/png: use libavutil/csp.h for cHRM chunks

The cHRM chunk is descriptive. That is, it describes the primaries that should
be used to interpret the pixel data in the PNG file. This is notably different
from Mastering Display Metadata, which describes which subset of the presented
gamut is relevant. MDM describes a gamut and says colors outside the gamut are
not required to be preserved, but it does not actually describe the gamut that
the pixel data from the frame resides in. Thus, to decode a cHRM chunk present
in a PNG file to Mastering Display Metadata is incorrect.

This commit changes this behavior so the cHRM chunk, if present, is decoded to
color metadata. For example, if the cHRM chunk describes BT.709 primaries, the
resulting AVFrame will be tagged with AVCOL_PRI_BT709, as a description of its
pixel data. To do this, it utilizes libavutil/csp.h, which exposes a funcction
av_csp_primaries_id_from_desc, to detect which enum value accurately describes
the white point and primaries represented by the cHRM chunk.

This commit also changes pngenc.c to utilize the libavuitl/csp.h API, since it
previously duplicated code contained in that API. Instead, taking advantage of
the API that exists makes more sense. pngenc.c does properly utilize the color
tags rather than incorrectly using MDM, so that required no change.

Signed-off-by: Leo Izen <leo.izen@gmail.com>
This commit is contained in:
Leo Izen 2023-01-17 11:09:29 -05:00
parent 843a446713
commit 2548c32cc1
No known key found for this signature in database
GPG Key ID: 5A71C331FD2FA19A
4 changed files with 44 additions and 70 deletions

View File

@ -26,10 +26,12 @@
#include "libavutil/avassert.h"
#include "libavutil/bprint.h"
#include "libavutil/crc.h"
#include "libavutil/csp.h"
#include "libavutil/imgutils.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/pixfmt.h"
#include "libavutil/rational.h"
#include "libavutil/stereo3d.h"
#include "libavutil/mastering_display_metadata.h"
#include "avcodec.h"
#include "bytestream.h"
@ -1472,6 +1474,7 @@ static void clear_frame_metadata(PNGDecContext *s)
static int output_frame(PNGDecContext *s, AVFrame *f)
{
AVCodecContext *avctx = s->avctx;
int ret;
if (s->iccp_data) {
@ -1483,8 +1486,30 @@ static int output_frame(PNGDecContext *s, AVFrame *f)
memcpy(sd->data, s->iccp_data, s->iccp_data_len);
av_dict_set(&sd->metadata, "name", s->iccp_name, 0);
} else if (s->have_chrm) {
AVColorPrimariesDesc desc;
enum AVColorPrimaries prim;
desc.wp.x = av_make_q(s->white_point[0], 100000);
desc.wp.y = av_make_q(s->white_point[1], 100000);
desc.prim.r.x = av_make_q(s->display_primaries[0][0], 100000);
desc.prim.r.y = av_make_q(s->display_primaries[0][1], 100000);
desc.prim.g.x = av_make_q(s->display_primaries[1][0], 100000);
desc.prim.g.y = av_make_q(s->display_primaries[1][1], 100000);
desc.prim.b.x = av_make_q(s->display_primaries[2][0], 100000);
desc.prim.b.y = av_make_q(s->display_primaries[2][1], 100000);
prim = av_csp_primaries_id_from_desc(&desc);
if (prim != AVCOL_PRI_UNSPECIFIED)
avctx->color_primaries = f->color_primaries = prim;
else
av_log(avctx, AV_LOG_WARNING, "unknown cHRM primaries\n");
}
/* this chunk overrides gAMA */
if (s->iccp_data)
av_dict_set(&s->frame_metadata, "gamma", NULL, 0);
avctx->colorspace = f->colorspace = AVCOL_SPC_RGB;
if (s->stereo_mode >= 0) {
AVStereo3D *stereo3d = av_stereo3d_create_side_data(f);
if (!stereo3d) {
@ -1496,25 +1521,6 @@ static int output_frame(PNGDecContext *s, AVFrame *f)
stereo3d->flags = s->stereo_mode ? 0 : AV_STEREO3D_FLAG_INVERT;
}
if (s->have_chrm) {
AVMasteringDisplayMetadata *mdm = av_mastering_display_metadata_create_side_data(f);
if (!mdm) {
ret = AVERROR(ENOMEM);
goto fail;
}
mdm->white_point[0] = av_make_q(s->white_point[0], 100000);
mdm->white_point[1] = av_make_q(s->white_point[1], 100000);
/* RGB Primaries */
for (int i = 0; i < 3; i++) {
mdm->display_primaries[i][0] = av_make_q(s->display_primaries[i][0], 100000);
mdm->display_primaries[i][1] = av_make_q(s->display_primaries[i][1], 100000);
}
mdm->has_primaries = 1;
}
FFSWAP(AVDictionary*, f->metadata, s->frame_metadata);
return 0;

View File

@ -29,10 +29,12 @@
#include "zlib_wrapper.h"
#include "libavutil/avassert.h"
#include "libavutil/color_utils.h"
#include "libavutil/crc.h"
#include "libavutil/csp.h"
#include "libavutil/libm.h"
#include "libavutil/opt.h"
#include "libavutil/color_utils.h"
#include "libavutil/rational.h"
#include "libavutil/stereo3d.h"
#include <zlib.h>
@ -294,45 +296,22 @@ static int png_write_row(AVCodecContext *avctx, const uint8_t *data, int size)
}
#define AV_WB32_PNG(buf, n) AV_WB32(buf, lrint((n) * 100000))
#define AV_WB32_PNG_D(buf, d) AV_WB32_PNG(buf, av_q2d(d))
static int png_get_chrm(enum AVColorPrimaries prim, uint8_t *buf)
{
double rx, ry, gx, gy, bx, by, wx = 0.3127, wy = 0.3290;
switch (prim) {
case AVCOL_PRI_BT709:
rx = 0.640; ry = 0.330;
gx = 0.300; gy = 0.600;
bx = 0.150; by = 0.060;
break;
case AVCOL_PRI_BT470M:
rx = 0.670; ry = 0.330;
gx = 0.210; gy = 0.710;
bx = 0.140; by = 0.080;
wx = 0.310; wy = 0.316;
break;
case AVCOL_PRI_BT470BG:
rx = 0.640; ry = 0.330;
gx = 0.290; gy = 0.600;
bx = 0.150; by = 0.060;
break;
case AVCOL_PRI_SMPTE170M:
case AVCOL_PRI_SMPTE240M:
rx = 0.630; ry = 0.340;
gx = 0.310; gy = 0.595;
bx = 0.155; by = 0.070;
break;
case AVCOL_PRI_BT2020:
rx = 0.708; ry = 0.292;
gx = 0.170; gy = 0.797;
bx = 0.131; by = 0.046;
break;
default:
return 0;
}
const AVColorPrimariesDesc *desc = av_csp_primaries_desc_from_id(prim);
if (!desc)
return 0;
AV_WB32_PNG_D(buf, desc->wp.x);
AV_WB32_PNG_D(buf + 4, desc->wp.y);
AV_WB32_PNG_D(buf + 8, desc->prim.r.x);
AV_WB32_PNG_D(buf + 12, desc->prim.r.y);
AV_WB32_PNG_D(buf + 16, desc->prim.g.x);
AV_WB32_PNG_D(buf + 20, desc->prim.g.y);
AV_WB32_PNG_D(buf + 24, desc->prim.b.x);
AV_WB32_PNG_D(buf + 28, desc->prim.b.y);
AV_WB32_PNG(buf , wx); AV_WB32_PNG(buf + 4 , wy);
AV_WB32_PNG(buf + 8 , rx); AV_WB32_PNG(buf + 12, ry);
AV_WB32_PNG(buf + 16, gx); AV_WB32_PNG(buf + 20, gy);
AV_WB32_PNG(buf + 24, bx); AV_WB32_PNG(buf + 28, by);
return 1;
}

View File

@ -33,7 +33,7 @@ interlaced_frame=0
top_field_first=0
repeat_pict=0
color_range=pc
color_space=unknown
color_space=gbr
color_primaries=unknown
color_transfer=unknown
chroma_location=unspecified

View File

@ -25,7 +25,7 @@ interlaced_frame=1
top_field_first=0
repeat_pict=0
color_range=pc
color_space=unknown
color_space=gbr
color_primaries=unknown
color_transfer=unknown
chroma_location=unspecified
@ -34,15 +34,4 @@ side_data_type=ICC profile
name=Photoshop ICC profile
size=3144
[/SIDE_DATA]
[SIDE_DATA]
side_data_type=Mastering display metadata
red_x=63999/100000
red_y=33001/100000
green_x=30000/100000
green_y=60000/100000
blue_x=15000/100000
blue_y=5999/100000
white_point_x=31269/100000
white_point_y=32899/100000
[/SIDE_DATA]
[/FRAME]