|
|
|
@ -75,6 +75,7 @@ typedef struct PNGDecContext { |
|
|
|
|
int have_chrm; |
|
|
|
|
uint32_t white_point[2]; |
|
|
|
|
uint32_t display_primaries[3][2]; |
|
|
|
|
int gamma; |
|
|
|
|
int have_srgb; |
|
|
|
|
int have_cicp; |
|
|
|
|
enum AVColorPrimaries cicp_primaries; |
|
|
|
@ -641,6 +642,83 @@ static int decode_phys_chunk(AVCodecContext *avctx, PNGDecContext *s, |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* This populates AVCodecContext fields so it must be called before |
|
|
|
|
* ff_thread_finish_setup() to avoid a race condition with respect to the |
|
|
|
|
* generic copying of avctx fields. |
|
|
|
|
*/ |
|
|
|
|
static int populate_avctx_color_fields(AVCodecContext *avctx, AVFrame *frame) |
|
|
|
|
{ |
|
|
|
|
PNGDecContext *s = avctx->priv_data; |
|
|
|
|
|
|
|
|
|
if (s->have_cicp) { |
|
|
|
|
if (s->cicp_primaries >= AVCOL_PRI_NB) |
|
|
|
|
av_log(avctx, AV_LOG_WARNING, "unrecognized cICP primaries\n"); |
|
|
|
|
else |
|
|
|
|
avctx->color_primaries = frame->color_primaries = s->cicp_primaries; |
|
|
|
|
if (s->cicp_trc >= AVCOL_TRC_NB) |
|
|
|
|
av_log(avctx, AV_LOG_WARNING, "unrecognized cICP transfer\n"); |
|
|
|
|
else |
|
|
|
|
avctx->color_trc = frame->color_trc = s->cicp_trc; |
|
|
|
|
if (s->cicp_range == 0) |
|
|
|
|
av_log(avctx, AV_LOG_WARNING, "unsupported tv-range cICP chunk\n"); |
|
|
|
|
} else if (s->iccp_data) { |
|
|
|
|
AVFrameSideData *sd = av_frame_new_side_data(frame, AV_FRAME_DATA_ICC_PROFILE, s->iccp_data_len); |
|
|
|
|
if (!sd) |
|
|
|
|
return AVERROR(ENOMEM); |
|
|
|
|
memcpy(sd->data, s->iccp_data, s->iccp_data_len); |
|
|
|
|
av_dict_set(&sd->metadata, "name", s->iccp_name, 0); |
|
|
|
|
} else if (s->have_srgb) { |
|
|
|
|
avctx->color_primaries = frame->color_primaries = AVCOL_PRI_BT709; |
|
|
|
|
avctx->color_trc = frame->color_trc = AVCOL_TRC_IEC61966_2_1; |
|
|
|
|
} 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 = frame->color_primaries = prim; |
|
|
|
|
else |
|
|
|
|
av_log(avctx, AV_LOG_WARNING, "unknown cHRM primaries\n"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* these chunks override gAMA */ |
|
|
|
|
if (s->iccp_data || s->have_srgb || s->have_cicp) { |
|
|
|
|
av_dict_set(&s->frame_metadata, "gamma", NULL, 0); |
|
|
|
|
} else if (s->gamma) { |
|
|
|
|
/*
|
|
|
|
|
* These values are 100000/2.2, 100000/2.8, 100000/2.6, and |
|
|
|
|
* 100000/1.0 respectively. 45455, 35714, and 38462, and 100000. |
|
|
|
|
* There's a 0.001 gamma tolerance here in case of floating |
|
|
|
|
* point issues when the PNG was written. |
|
|
|
|
* |
|
|
|
|
* None of the other enums have a pure gamma curve so it makes |
|
|
|
|
* sense to leave those to sRGB and cICP. |
|
|
|
|
*/ |
|
|
|
|
if (s->gamma > 45355 && s->gamma < 45555) |
|
|
|
|
avctx->color_trc = frame->color_trc = AVCOL_TRC_GAMMA22; |
|
|
|
|
else if (s->gamma > 35614 && s->gamma < 35814) |
|
|
|
|
avctx->color_trc = frame->color_trc = AVCOL_TRC_GAMMA28; |
|
|
|
|
else if (s->gamma > 38362 && s->gamma < 38562) |
|
|
|
|
avctx->color_trc = frame->color_trc = AVCOL_TRC_SMPTE428; |
|
|
|
|
else if (s->gamma > 99900 && s->gamma < 100100) |
|
|
|
|
avctx->color_trc = frame->color_trc = AVCOL_TRC_LINEAR; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* we only support pc-range RGB */ |
|
|
|
|
avctx->colorspace = frame->colorspace = AVCOL_SPC_RGB; |
|
|
|
|
avctx->color_range = frame->color_range = AVCOL_RANGE_JPEG; |
|
|
|
|
|
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int decode_idat_chunk(AVCodecContext *avctx, PNGDecContext *s, |
|
|
|
|
GetByteContext *gb, AVFrame *p) |
|
|
|
|
{ |
|
|
|
@ -756,6 +834,8 @@ static int decode_idat_chunk(AVCodecContext *avctx, PNGDecContext *s, |
|
|
|
|
p->key_frame = 1; |
|
|
|
|
p->interlaced_frame = !!s->interlace_type; |
|
|
|
|
|
|
|
|
|
if ((ret = populate_avctx_color_fields(avctx, p)) < 0) |
|
|
|
|
return ret; |
|
|
|
|
ff_thread_finish_setup(avctx); |
|
|
|
|
|
|
|
|
|
/* compute the compressed row size */ |
|
|
|
@ -1257,6 +1337,10 @@ static int decode_frame_common(AVCodecContext *avctx, PNGDecContext *s, |
|
|
|
|
case MKTAG('t', 'E', 'X', 't'): |
|
|
|
|
case MKTAG('I', 'D', 'A', 'T'): |
|
|
|
|
case MKTAG('t', 'R', 'N', 'S'): |
|
|
|
|
case MKTAG('s', 'R', 'G', 'B'): |
|
|
|
|
case MKTAG('c', 'I', 'C', 'P'): |
|
|
|
|
case MKTAG('c', 'H', 'R', 'M'): |
|
|
|
|
case MKTAG('g', 'A', 'M', 'A'): |
|
|
|
|
break; |
|
|
|
|
default: |
|
|
|
|
continue; |
|
|
|
@ -1359,10 +1443,10 @@ static int decode_frame_common(AVCodecContext *avctx, PNGDecContext *s, |
|
|
|
|
case MKTAG('g', 'A', 'M', 'A'): { |
|
|
|
|
AVBPrint bp; |
|
|
|
|
char *gamma_str; |
|
|
|
|
int num = bytestream2_get_be32(&gb_chunk); |
|
|
|
|
s->gamma = bytestream2_get_be32(&gb_chunk); |
|
|
|
|
|
|
|
|
|
av_bprint_init(&bp, 0, AV_BPRINT_SIZE_UNLIMITED); |
|
|
|
|
av_bprintf(&bp, "%i/%i", num, 100000); |
|
|
|
|
av_bprintf(&bp, "%i/%i", s->gamma, 100000); |
|
|
|
|
ret = av_bprint_finalize(&bp, &gamma_str); |
|
|
|
|
if (ret < 0) |
|
|
|
|
return ret; |
|
|
|
@ -1500,56 +1584,8 @@ static void clear_frame_metadata(PNGDecContext *s) |
|
|
|
|
|
|
|
|
|
static int output_frame(PNGDecContext *s, AVFrame *f) |
|
|
|
|
{ |
|
|
|
|
AVCodecContext *avctx = s->avctx; |
|
|
|
|
int ret; |
|
|
|
|
|
|
|
|
|
if (s->have_cicp) { |
|
|
|
|
if (s->cicp_primaries >= AVCOL_PRI_NB) |
|
|
|
|
av_log(avctx, AV_LOG_WARNING, "unrecognized cICP primaries\n"); |
|
|
|
|
else |
|
|
|
|
avctx->color_primaries = f->color_primaries = s->cicp_primaries; |
|
|
|
|
if (s->cicp_trc >= AVCOL_TRC_NB) |
|
|
|
|
av_log(avctx, AV_LOG_WARNING, "unrecognized cICP transfer\n"); |
|
|
|
|
else |
|
|
|
|
avctx->color_trc = f->color_trc = s->cicp_trc; |
|
|
|
|
avctx->color_range = f->color_range = |
|
|
|
|
s->cicp_range == 0 ? AVCOL_RANGE_MPEG : AVCOL_RANGE_JPEG; |
|
|
|
|
} else if (s->iccp_data) { |
|
|
|
|
AVFrameSideData *sd = av_frame_new_side_data(f, AV_FRAME_DATA_ICC_PROFILE, s->iccp_data_len); |
|
|
|
|
if (!sd) { |
|
|
|
|
ret = AVERROR(ENOMEM); |
|
|
|
|
goto fail; |
|
|
|
|
} |
|
|
|
|
memcpy(sd->data, s->iccp_data, s->iccp_data_len); |
|
|
|
|
|
|
|
|
|
av_dict_set(&sd->metadata, "name", s->iccp_name, 0); |
|
|
|
|
} else if (s->have_srgb) { |
|
|
|
|
avctx->color_primaries = f->color_primaries = AVCOL_PRI_BT709; |
|
|
|
|
avctx->color_trc = f->color_trc = AVCOL_TRC_IEC61966_2_1; |
|
|
|
|
} 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"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* these chunks override gAMA */ |
|
|
|
|
if (s->iccp_data || s->have_srgb || s->have_cicp) |
|
|
|
|
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) { |
|
|
|
|