FFmpeg
fflcms2.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2022 Niklas Haas
3  * This file is part of FFmpeg.
4  *
5  * FFmpeg is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * FFmpeg is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with FFmpeg; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18  */
19 
20 #include "libavutil/color_utils.h"
21 #include "libavutil/csp.h"
22 
23 #include "fflcms2.h"
24 
25 static void log_cb(cmsContext ctx, cmsUInt32Number error, const char *str)
26 {
27  FFIccContext *s = cmsGetContextUserData(ctx);
28  av_log(s->avctx, AV_LOG_ERROR, "lcms2: [%"PRIu32"] %s\n", error, str);
29 }
30 
32 {
33  memset(s, 0, sizeof(*s));
34  s->avctx = avctx;
35  s->ctx = cmsCreateContext(NULL, s);
36  if (!s->ctx)
37  return AVERROR(ENOMEM);
38 
39  cmsSetLogErrorHandlerTHR(s->ctx, log_cb);
40  return 0;
41 }
42 
44 {
45  for (int i = 0; i < FF_ARRAY_ELEMS(s->curves); i++)
46  cmsFreeToneCurve(s->curves[i]);
47  cmsDeleteContext(s->ctx);
48  memset(s, 0, sizeof(*s));
49 }
50 
52  cmsToneCurve **out_curve)
53 {
54  if (trc >= AVCOL_TRC_NB)
55  return AVERROR_INVALIDDATA;
56 
57  if (s->curves[trc])
58  goto done;
59 
60  switch (trc) {
61  case AVCOL_TRC_LINEAR:
62  s->curves[trc] = cmsBuildGamma(s->ctx, 1.0);
63  break;
64  case AVCOL_TRC_GAMMA22:
65  s->curves[trc] = cmsBuildGamma(s->ctx, 2.2);
66  break;
67  case AVCOL_TRC_GAMMA28:
68  s->curves[trc] = cmsBuildGamma(s->ctx, 2.8);
69  break;
70  case AVCOL_TRC_BT709:
74  s->curves[trc] = cmsBuildParametricToneCurve(s->ctx, 4, (double[5]) {
75  /* γ = */ 1/0.45,
76  /* a = */ 1/1.099296826809442,
77  /* b = */ 1 - 1/1.099296826809442,
78  /* c = */ 1/4.5,
79  /* d = */ 4.5 * 0.018053968510807,
80  });
81  break;
83  s->curves[trc] = cmsBuildParametricToneCurve(s->ctx, 4, (double[5]) {
84  /* γ = */ 1/0.45,
85  /* a = */ 1/1.1115,
86  /* b = */ 1 - 1/1.1115,
87  /* c = */ 1/4.0,
88  /* d = */ 4.0 * 0.0228,
89  });
90  break;
91  case AVCOL_TRC_LOG:
92  s->curves[trc] = cmsBuildParametricToneCurve(s->ctx, 8, (double[5]) {
93  /* a = */ 1.0,
94  /* b = */ 10.0,
95  /* c = */ 2.0,
96  /* d = */ -1.0,
97  /* e = */ 0.0
98  });
99  break;
100  case AVCOL_TRC_LOG_SQRT:
101  s->curves[trc] = cmsBuildParametricToneCurve(s->ctx, 8, (double[5]) {
102  /* a = */ 1.0,
103  /* b = */ 10.0,
104  /* c = */ 2.5,
105  /* d = */ -1.0,
106  /* e = */ 0.0
107  });
108  break;
110  s->curves[trc] = cmsBuildParametricToneCurve(s->ctx, 4, (double[5]) {
111  /* γ = */ 2.4,
112  /* a = */ 1/1.055,
113  /* b = */ 1 - 1/1.055,
114  /* c = */ 1/12.92,
115  /* d = */ 12.92 * 0.0031308,
116  });
117  break;
118  case AVCOL_TRC_SMPTE428:
119  s->curves[trc] = cmsBuildParametricToneCurve(s->ctx, 2, (double[3]) {
120  /* γ = */ 2.6,
121  /* a = */ pow(52.37/48.0, 1/2.6),
122  /* b = */ 0.0
123  });
124  break;
125 
126  /* Can't be represented using the existing parametric tone curves.
127  * FIXME: use cmsBuildTabulatedToneCurveFloat instead */
130  case AVCOL_TRC_SMPTE2084:
132  return AVERROR_PATCHWELCOME;
133 
134  default:
135  return AVERROR_INVALIDDATA;
136  }
137 
138  if (!s->curves[trc])
139  return AVERROR(ENOMEM);
140 
141 done:
142  *out_curve = s->curves[trc];
143  return 0;
144 }
145 
147  enum AVColorPrimaries color_prim,
148  enum AVColorTransferCharacteristic color_trc,
149  cmsHPROFILE *out_profile)
150 {
151  cmsToneCurve *tonecurve;
152  const AVColorPrimariesDesc *prim;
153  int ret;
154 
155  if (!(prim = av_csp_primaries_desc_from_id(color_prim)))
156  return AVERROR_INVALIDDATA;
157  if ((ret = get_curve(s, color_trc, &tonecurve)) < 0)
158  return ret;
159 
160  *out_profile = cmsCreateRGBProfileTHR(s->ctx,
161  &(cmsCIExyY) { av_q2d(prim->wp.x), av_q2d(prim->wp.y), 1.0 },
162  &(cmsCIExyYTRIPLE) {
163  .Red = { av_q2d(prim->prim.r.x), av_q2d(prim->prim.r.y), 1.0 },
164  .Green = { av_q2d(prim->prim.g.x), av_q2d(prim->prim.g.y), 1.0 },
165  .Blue = { av_q2d(prim->prim.b.x), av_q2d(prim->prim.b.y), 1.0 },
166  },
167  (cmsToneCurve *[3]) { tonecurve, tonecurve, tonecurve }
168  );
169 
170  return *out_profile == NULL ? AVERROR(ENOMEM) : 0;
171 }
172 
174 {
175  cmsUInt32Number size;
176  AVBufferRef *buf;
177 
178  if (!cmsSaveProfileToMem(profile, NULL, &size))
179  return AVERROR_EXTERNAL;
180 
181  buf = av_buffer_alloc(size);
182  if (!buf)
183  return AVERROR(ENOMEM);
184 
185  if (!cmsSaveProfileToMem(profile, buf->data, &size) || size != buf->size) {
186  av_buffer_unref(&buf);
187  return AVERROR_EXTERNAL;
188  }
189 
191  av_buffer_unref(&buf);
192  return AVERROR(ENOMEM);
193  }
194 
195  return 0;
196 }
197 
198 static av_always_inline void XYZ_xy(cmsCIEXYZ XYZ, AVCIExy *xy)
199 {
200  double k = 1.0 / (XYZ.X + XYZ.Y + XYZ.Z);
201  xy->x = av_d2q(k * XYZ.X, 100000);
202  xy->y = av_d2q(k * XYZ.Y, 100000);
203 }
204 
206  AVColorPrimariesDesc *out_primaries)
207 {
208  static const uint8_t testprimaries[4][3] = {
209  { 0xFF, 0, 0 }, /* red */
210  { 0, 0xFF, 0 }, /* green */
211  { 0, 0, 0xFF }, /* blue */
212  { 0xFF, 0xFF, 0xFF }, /* white */
213  };
214 
215  AVWhitepointCoefficients *wp = &out_primaries->wp;
216  AVPrimaryCoefficients *prim = &out_primaries->prim;
217  cmsFloat64Number prev_adapt;
218  cmsHPROFILE xyz;
219  cmsHTRANSFORM tf;
220  cmsCIEXYZ dst[4];
221 
222  xyz = cmsCreateXYZProfileTHR(s->ctx);
223  if (!xyz)
224  return AVERROR(ENOMEM);
225 
226  /* We need to use an unadapted observer to get the raw values */
227  prev_adapt = cmsSetAdaptationStateTHR(s->ctx, 0.0);
228  tf = cmsCreateTransformTHR(s->ctx, profile, TYPE_RGB_8, xyz, TYPE_XYZ_DBL,
229  INTENT_ABSOLUTE_COLORIMETRIC,
230  /* Note: These flags mostly don't do anything
231  * anyway, but specify them regardless */
232  cmsFLAGS_NOCACHE |
233  cmsFLAGS_NOOPTIMIZE |
234  cmsFLAGS_LOWRESPRECALC |
235  cmsFLAGS_GRIDPOINTS(2));
236  cmsSetAdaptationStateTHR(s->ctx, prev_adapt);
237  cmsCloseProfile(xyz);
238  if (!tf) {
239  av_log(s->avctx, AV_LOG_ERROR, "Invalid ICC profile (e.g. CMYK)\n");
240  return AVERROR_INVALIDDATA;
241  }
242 
243  cmsDoTransform(tf, testprimaries, dst, 4);
244  cmsDeleteTransform(tf);
245  XYZ_xy(dst[0], &prim->r);
246  XYZ_xy(dst[1], &prim->g);
247  XYZ_xy(dst[2], &prim->b);
248  XYZ_xy(dst[3], wp);
249  return 0;
250 }
251 
253  enum AVColorTransferCharacteristic *out_trc)
254 {
255  /* 8-bit linear grayscale ramp */
256  static const uint8_t testramp[16][3] = {
257  { 1, 1, 1}, /* avoid exact zero due to log100 etc. */
258  { 17, 17, 17},
259  { 34, 34, 34},
260  { 51, 51, 51},
261  { 68, 68, 68},
262  { 85, 85, 85},
263  { 02, 02, 02},
264  {119, 119, 119},
265  {136, 136, 136},
266  {153, 153, 153},
267  {170, 170, 170},
268  {187, 187, 187},
269  {204, 204, 204},
270  {221, 221, 221},
271  {238, 238, 238},
272  {255, 255, 255},
273  };
274 
275  double dst[FF_ARRAY_ELEMS(testramp)];
276 
277  for (enum AVColorTransferCharacteristic trc = 0; trc < AVCOL_TRC_NB; trc++) {
278  cmsToneCurve *tonecurve;
279  cmsHPROFILE ref;
280  cmsHTRANSFORM tf;
281  double delta = 0.0;
282  if (get_curve(s, trc, &tonecurve) < 0)
283  continue;
284 
285  ref = cmsCreateGrayProfileTHR(s->ctx, cmsD50_xyY(), tonecurve);
286  if (!ref)
287  return AVERROR(ENOMEM);
288 
289  tf = cmsCreateTransformTHR(s->ctx, profile, TYPE_RGB_8, ref, TYPE_GRAY_DBL,
290  INTENT_RELATIVE_COLORIMETRIC,
291  cmsFLAGS_NOCACHE | cmsFLAGS_NOOPTIMIZE);
292  cmsCloseProfile(ref);
293  if (!tf) {
294  av_log(s->avctx, AV_LOG_ERROR, "Invalid ICC profile (e.g. CMYK)\n");
295  return AVERROR_INVALIDDATA;
296  }
297 
298  cmsDoTransform(tf, testramp, dst, FF_ARRAY_ELEMS(dst));
299  cmsDeleteTransform(tf);
300 
301  for (int i = 0; i < FF_ARRAY_ELEMS(dst); i++)
302  delta += fabs(testramp[i][0] / 255.0 - dst[i]);
303  if (delta < 0.01) {
304  *out_trc = trc;
305  return 0;
306  }
307  }
308 
309  *out_trc = AVCOL_TRC_UNSPECIFIED;
310  return 0;
311 }
error
static void error(const char *err)
Definition: target_bsf_fuzzer.c:31
ff_icc_profile_read_primaries
int ff_icc_profile_read_primaries(FFIccContext *s, cmsHPROFILE profile, AVColorPrimariesDesc *out_primaries)
Read the color primaries and white point coefficients encoded by an ICC profile, and return the raw v...
Definition: fflcms2.c:205
AVERROR
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFrame structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample they are references to shared objects When the negotiation mechanism computes the intersection of the formats supported at each end of a all references to both lists are replaced with a reference to the intersection And when a single format is eventually chosen for a link amongst the remaining all references to the list are updated That means that if a filter requires that its input and output have the same format amongst a supported all it has to do is use a reference to the same list of formats query_formats can leave some formats unset and return AVERROR(EAGAIN) to cause the negotiation mechanism toagain later. That can be used by filters with complex requirements to use the format negotiated on one link to set the formats supported on another. Frame references ownership and permissions
AVColorTransferCharacteristic
AVColorTransferCharacteristic
Color Transfer Characteristic.
Definition: pixfmt.h:496
AVColorPrimariesDesc::wp
AVWhitepointCoefficients wp
Definition: csp.h:70
AVColorPrimariesDesc
Struct that contains both white point location and primaries location, providing the complete descrip...
Definition: csp.h:69
AVBufferRef::data
uint8_t * data
The data buffer.
Definition: buffer.h:90
AVCOL_TRC_LINEAR
@ AVCOL_TRC_LINEAR
"Linear transfer characteristics"
Definition: pixfmt.h:505
ff_icc_profile_detect_transfer
int ff_icc_profile_detect_transfer(FFIccContext *s, cmsHPROFILE profile, enum AVColorTransferCharacteristic *out_trc)
Attempt detecting the transfer characteristic that best approximates the transfer function encoded by...
Definition: fflcms2.c:252
AVFrame
This structure describes decoded (raw) audio or video data.
Definition: frame.h:325
AVCOL_TRC_NB
@ AVCOL_TRC_NB
Not part of ABI.
Definition: pixfmt.h:518
AVCOL_TRC_UNSPECIFIED
@ AVCOL_TRC_UNSPECIFIED
Definition: pixfmt.h:499
AVCOL_TRC_BT2020_12
@ AVCOL_TRC_BT2020_12
ITU-R BT2020 for 12-bit system.
Definition: pixfmt.h:512
AVColorPrimaries
AVColorPrimaries
Chromaticity coordinates of the source primaries.
Definition: pixfmt.h:471
AVCOL_TRC_IEC61966_2_1
@ AVCOL_TRC_IEC61966_2_1
IEC 61966-2-1 (sRGB or sYCC)
Definition: pixfmt.h:510
AVPrimaryCoefficients
Struct defining the red, green, and blue primary locations in terms of CIE 1931 chromaticity x and y.
Definition: csp.h:55
AVCOL_TRC_GAMMA28
@ AVCOL_TRC_GAMMA28
also ITU-R BT470BG
Definition: pixfmt.h:502
XYZ_xy
static av_always_inline void XYZ_xy(cmsCIEXYZ XYZ, AVCIExy *xy)
Definition: fflcms2.c:198
AVCOL_TRC_LOG_SQRT
@ AVCOL_TRC_LOG_SQRT
"Logarithmic transfer characteristic (100 * Sqrt(10) : 1 range)"
Definition: pixfmt.h:507
AVCOL_TRC_GAMMA22
@ AVCOL_TRC_GAMMA22
also ITU-R BT470M / ITU-R BT1700 625 PAL & SECAM
Definition: pixfmt.h:501
color_utils.h
AV_LOG_ERROR
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:180
FF_ARRAY_ELEMS
#define FF_ARRAY_ELEMS(a)
Definition: sinewin_tablegen.c:29
s
#define s(width, name)
Definition: cbs_vp9.c:256
av_csp_primaries_desc_from_id
const AVColorPrimariesDesc * av_csp_primaries_desc_from_id(enum AVColorPrimaries prm)
Retrieves a complete gamut description from an enum constant describing the color primaries.
Definition: csp.c:85
AVCOL_TRC_BT1361_ECG
@ AVCOL_TRC_BT1361_ECG
ITU-R BT1361 Extended Colour Gamut.
Definition: pixfmt.h:509
ff_icc_context_uninit
void ff_icc_context_uninit(FFIccContext *s)
Definition: fflcms2.c:43
ctx
AVFormatContext * ctx
Definition: movenc.c:48
fabs
static __device__ float fabs(float a)
Definition: cuda_runtime.h:182
NULL
#define NULL
Definition: coverity.c:32
AVERROR_PATCHWELCOME
#define AVERROR_PATCHWELCOME
Not yet implemented in FFmpeg, patches welcome.
Definition: error.h:64
av_buffer_unref
void av_buffer_unref(AVBufferRef **buf)
Free a given reference and automatically free the buffer if there are no more references to it.
Definition: buffer.c:139
AVCOL_TRC_IEC61966_2_4
@ AVCOL_TRC_IEC61966_2_4
IEC 61966-2-4.
Definition: pixfmt.h:508
log_cb
static void log_cb(cmsContext ctx, cmsUInt32Number error, const char *str)
Definition: fflcms2.c:25
AV_FRAME_DATA_ICC_PROFILE
@ AV_FRAME_DATA_ICC_PROFILE
The data contains an ICC profile as an opaque octet buffer following the format described by ISO 1507...
Definition: frame.h:144
av_frame_new_side_data_from_buf
AVFrameSideData * av_frame_new_side_data_from_buf(AVFrame *frame, enum AVFrameSideDataType type, AVBufferRef *buf)
Add a new side data to a frame from an existing AVBufferRef.
Definition: frame.c:650
AVCOL_TRC_BT2020_10
@ AVCOL_TRC_BT2020_10
ITU-R BT2020 for 10-bit system.
Definition: pixfmt.h:511
AVCIExy
Struct containing chromaticity x and y values for the standard CIE 1931 chromaticity definition.
Definition: csp.h:47
AVCIExy::x
AVRational x
Definition: csp.h:48
AVCOL_TRC_SMPTE2084
@ AVCOL_TRC_SMPTE2084
SMPTE ST 2084 for 10-, 12-, 14- and 16-bit systems.
Definition: pixfmt.h:513
AVPrimaryCoefficients::b
AVCIExy b
Definition: csp.h:56
AVPrimaryCoefficients::r
AVCIExy r
Definition: csp.h:56
AVCOL_TRC_SMPTE240M
@ AVCOL_TRC_SMPTE240M
Definition: pixfmt.h:504
AVCOL_TRC_LOG
@ AVCOL_TRC_LOG
"Logarithmic transfer characteristic (100:1 range)"
Definition: pixfmt.h:506
size
int size
Definition: twinvq_data.h:10344
AVPrimaryCoefficients::g
AVCIExy g
Definition: csp.h:56
fflcms2.h
csp.h
AVERROR_EXTERNAL
#define AVERROR_EXTERNAL
Generic error in an external library.
Definition: error.h:59
av_buffer_alloc
AVBufferRef * av_buffer_alloc(size_t size)
Allocate an AVBuffer of the given size using av_malloc().
Definition: buffer.c:77
AVBufferRef::size
size_t size
Size of data in bytes.
Definition: buffer.h:94
AVCOL_TRC_BT709
@ AVCOL_TRC_BT709
also ITU-R BT1361
Definition: pixfmt.h:498
FFIccContext
Definition: fflcms2.h:34
i
#define i(width, name, range_min, range_max)
Definition: cbs_h2645.c:269
delta
float delta
Definition: vorbis_enc_data.h:430
av_always_inline
#define av_always_inline
Definition: attributes.h:49
ff_icc_profile_attach
int ff_icc_profile_attach(FFIccContext *s, cmsHPROFILE profile, AVFrame *frame)
Attach an ICC profile to a frame.
Definition: fflcms2.c:173
av_d2q
AVRational av_d2q(double d, int max)
Convert a double precision floating point number to a rational.
Definition: rational.c:106
ff_icc_profile_generate
int ff_icc_profile_generate(FFIccContext *s, enum AVColorPrimaries color_prim, enum AVColorTransferCharacteristic color_trc, cmsHPROFILE *out_profile)
Generate an ICC profile for a given combination of color primaries and transfer function.
Definition: fflcms2.c:146
profile
int profile
Definition: mxfenc.c:2005
ret
ret
Definition: filter_design.txt:187
frame
these buffered frames must be flushed immediately if a new input produces new the filter must not call request_frame to get more It must just process the frame or queue it The task of requesting more frames is left to the filter s request_frame method or the application If a filter has several the filter must be ready for frames arriving randomly on any input any filter with several inputs will most likely require some kind of queuing mechanism It is perfectly acceptable to have a limited queue and to drop frames when the inputs are too unbalanced request_frame For filters that do not use the this method is called when a frame is wanted on an output For a it should directly call filter_frame on the corresponding output For a if there are queued frames already one of these frames should be pushed If the filter should request a frame on one of its repeatedly until at least one frame has been pushed Return or at least make progress towards producing a frame
Definition: filter_design.txt:264
AVCIExy::y
AVRational y
Definition: csp.h:48
ff_icc_context_init
int ff_icc_context_init(FFIccContext *s, void *avctx)
Initializes an FFIccContext.
Definition: fflcms2.c:31
AVCOL_TRC_ARIB_STD_B67
@ AVCOL_TRC_ARIB_STD_B67
ARIB STD-B67, known as "Hybrid log-gamma".
Definition: pixfmt.h:517
ref
static int ref[MAX_W *MAX_W]
Definition: jpeg2000dwt.c:112
AVCOL_TRC_SMPTE170M
@ AVCOL_TRC_SMPTE170M
also ITU-R BT601-6 525 or 625 / ITU-R BT1358 525 or 625 / ITU-R BT1700 NTSC
Definition: pixfmt.h:503
tf
#define tf
Definition: regdef.h:73
AVBufferRef
A reference to a data buffer.
Definition: buffer.h:82
AVColorPrimariesDesc::prim
AVPrimaryCoefficients prim
Definition: csp.h:71
convert_header.str
string str
Definition: convert_header.py:20
av_log
#define av_log(a,...)
Definition: tableprint_vlc.h:27
AVERROR_INVALIDDATA
#define AVERROR_INVALIDDATA
Invalid data found when processing input.
Definition: error.h:61
AVCOL_TRC_SMPTE428
@ AVCOL_TRC_SMPTE428
SMPTE ST 428-1.
Definition: pixfmt.h:515
get_curve
static int get_curve(FFIccContext *s, enum AVColorTransferCharacteristic trc, cmsToneCurve **out_curve)
Definition: fflcms2.c:51