Coverage Report

Created: 2025-12-03 07:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/graphicsmagick/magick/cdl.c
Line
Count
Source
1
/*
2
 * Copyright (C) 2009 GraphicsMagick Group
3
 *
4
 * American Society of Cinematographers Color Decision List (ASC-CDL)
5
 * implementation.
6
 *
7
 * Original implementation by Clément Follet.  Additional work by Bob
8
 * Friesenhahn.
9
 */
10

11
/*
12
  Include declarations.
13
*/
14
#include "magick/studio.h"
15
#include "magick/cdl.h"
16
#include "magick/pixel_iterator.h"
17
#include "magick/utility.h"
18

19
/*
20
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
21
%                                                                             %
22
%                                                                             %
23
%     C d l I m a g e                                                         %
24
%                                                                             %
25
%                                                                             %
26
%                                                                             %
27
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
28
%
29
%  The CdlImage() method applies ("bakes in") the ASC-CDL which is a format
30
%  for the exchange of basic primary color grading information between
31
%  equipment and software from different manufacturers. The format defines
32
%  the math for three functions: slope, offset and power. Each function uses
33
%  a number for the red, green, and blue color channels for a total of nine
34
%  numbers comprising a single color decision. A tenth number for chrominance
35
%  (saturation) has been proposed but is not yet standardized.
36
%
37
%  The cdl argument string is comma delimited and is in the form (but
38
%  without invervening spaces or line breaks):
39
%
40
%    redslope, redoffset, redpower :
41
%    greenslope, greenoffset, greenpower :
42
%    blueslope, blueoffset, bluepower :
43
%    saturation
44
%
45
%  with the unity (no change) specification being:
46
%
47
%    "1.0,0.0,1.0:1.0,0.0,1.0:1.0,0.0,1.0:0.0"
48
%
49
%  See http://en.wikipedia.org/wiki/ASC_CDL for more information.
50
%
51
%  The format of the CdlImage method is:
52
%
53
%      MagickPassFail CdlImage(Image *image,const char *cdl)
54
%
55
%  A description of each parameter follows:
56
%
57
%    o image: The image.
58
%
59
%    o cdl: Define the coefficients for slope offset and power in the
60
%      red green and blue channels, plus saturation.
61
%
62
*/
63
64
typedef struct _CdlImageParameters_t
65
{
66
  double
67
    redslope,
68
    redoffset,
69
    redpower,
70
    greenslope,
71
    greenoffset,
72
    greenpower,
73
    blueslope,
74
    blueoffset,
75
    bluepower,
76
    saturation;
77
78
  const PixelPacket
79
    *lut;
80
81
} CdlImageParameters_t;
82
83
static Quantum
84
CdlQuantum(const Quantum quantum, const double slope, const double offset,
85
           const double power, const double saturation)
86
0
{
87
0
  double
88
0
    v,
89
0
    t;
90
91
0
  t=(((double) quantum)/MaxRGBDouble)*slope+offset;
92
0
  if (t < 0.0)
93
0
    t = 0.0;
94
0
  else if (t > 1.0)
95
0
    t = 1.0;
96
0
  v = (pow(t,power)+saturation)*MaxRGBDouble;
97
0
  return RoundDoubleToQuantum(v);
98
0
}
99
100
static MagickPassFail
101
CdlImagePixels(void *mutable_data,         /* User provided mutable data */
102
               const void *immutable_data, /* User provided immutable data */
103
               Image * restrict image,               /* Modify image */
104
               PixelPacket * restrict pixels,        /* Pixel row */
105
               IndexPacket * restrict indexes,       /* Pixel row indexes */
106
               const long npixels,         /* Number of pixels in row */
107
               ExceptionInfo *exception)   /* Exception report */
108
0
{
109
0
  const CdlImageParameters_t
110
0
    param = *(const CdlImageParameters_t *) immutable_data;
111
112
0
  register long
113
0
    i;
114
115
0
  ARG_NOT_USED(mutable_data);
116
0
  ARG_NOT_USED(image);
117
0
  ARG_NOT_USED(indexes);
118
0
  ARG_NOT_USED(exception);
119
120
0
  if (param.lut != (PixelPacket *) NULL)
121
0
    {
122
0
      for(i = 0; i < npixels; i++)
123
0
        {
124
0
          pixels[i].red=param.lut[pixels[i].red].red;
125
0
          pixels[i].green=param.lut[pixels[i].green].green;
126
0
          pixels[i].blue=param.lut[pixels[i].blue].blue;
127
0
        }
128
0
    }
129
0
  else
130
0
    {
131
0
      for(i = 0; i < npixels; i++)
132
0
        {
133
0
          pixels[i].red=CdlQuantum(pixels[i].red,param.redslope,param.redoffset,
134
0
                                   param.redpower,param.saturation);
135
0
          pixels[i].green=CdlQuantum(pixels[i].green,param.greenslope,param.greenoffset,
136
0
                                     param.greenpower,param.saturation);
137
0
          pixels[i].blue=CdlQuantum(pixels[i].blue,param.blueslope,param.blueoffset,
138
0
                                    param.bluepower,param.saturation);
139
0
        }
140
0
    }
141
142
0
  return MagickPass;
143
0
}
144
145
146
MagickExport MagickPassFail CdlImage(Image *image,const char *cdl)
147
0
{
148
0
  char
149
0
    progress_message[MaxTextExtent];
150
151
0
  CdlImageParameters_t
152
0
    param;
153
154
0
  PixelPacket
155
0
    *lut = (PixelPacket *) NULL;
156
157
0
  register long
158
0
    i;
159
160
0
  MagickPassFail
161
0
    status=MagickPass;
162
163
0
  assert(image != (Image *) NULL);
164
0
  assert(image->signature == MagickSignature);
165
0
  if (cdl == (char *) NULL)
166
0
    return(MagickFail);
167
168
0
  param.redslope=1.0;
169
0
  param.redoffset=0.0;
170
0
  param.redpower=1.0;
171
0
  param.greenslope=1.0;
172
0
  param.greenoffset=0.0;
173
0
  param.greenpower=1.0;
174
0
  param.blueslope=1.0;
175
0
  param.blueoffset=0.0;
176
0
  param.bluepower=1.0;
177
0
  param.saturation=0.0;
178
0
  param.lut=(PixelPacket *) NULL;
179
180
0
  (void) sscanf(cdl,
181
0
                "%lf%*[,/]%lf%*[,/]%lf%*[:/]%lf%*[,/]%lf%*[,/]%lf%*"
182
0
                "[:/]%lf%*[,/]%lf%*[,/]%lf%*[:/]%lf",
183
0
                &param.redslope,&param.redoffset,&param.redpower,
184
0
                &param.greenslope,&param.greenoffset,&param.greenpower
185
0
                ,&param.blueslope,&param.blueoffset,&param.bluepower,
186
0
                &param.saturation);
187
188
0
  param.redslope=AbsoluteValue(param.redslope);
189
0
  param.redpower=AbsoluteValue(param.redpower);
190
0
  param.greenslope=AbsoluteValue(param.greenslope);
191
0
  param.greenpower=AbsoluteValue(param.greenpower);
192
0
  param.blueslope=AbsoluteValue(param.blueslope);
193
0
  param.bluepower=AbsoluteValue(param.bluepower);
194
195
0
  FormatString(progress_message,
196
0
               "[%%s] cdl %g/%g/%g/%g/%g/%g/%g/%g/%g/%g image...",
197
0
               param.redslope,param.redoffset,param.redpower,
198
0
               param.greenslope,param.greenoffset,param.greenpower,
199
0
               param.blueslope,param.blueoffset,param.bluepower,
200
0
               param.saturation);
201
202
0
  if (!IsRGBCompatibleColorspace(image->colorspace))
203
0
    TransformColorspace(image,RGBColorspace);
204
205
  /*
206
    Build a LUT if it is beneficial to do so.
207
  */
208
0
  if ((MaxMap == MaxRGB) && (image->columns*image->rows > MaxMap*3))
209
0
    {
210
0
      lut=MagickAllocateMemory(PixelPacket *,(MaxMap+1)*sizeof(PixelPacket));
211
0
      if (lut != (PixelPacket *) NULL)
212
0
        {
213
#if (MaxMap > 256) && defined(HAVE_OPENMP)
214
#  if defined(USE_STATIC_SCHEDULING_ONLY)
215
#    pragma omp parallel for schedule(static,4)
216
#  else
217
#    pragma omp parallel for schedule(guided)
218
#  endif
219
#endif
220
0
          for (i=0; i <= (long) MaxMap; i++)
221
0
            {
222
0
              lut[i].red=CdlQuantum((Quantum) i,param.redslope,param.redoffset,
223
0
                                       param.redpower,param.saturation);
224
0
              lut[i].green=CdlQuantum((Quantum) i,param.greenslope,param.greenoffset,
225
0
                                         param.greenpower,param.saturation);
226
0
              lut[i].blue=CdlQuantum((Quantum) i,param.blueslope,param.blueoffset,
227
0
                                        param.bluepower,param.saturation);
228
0
            }
229
0
          param.lut=lut;
230
0
        }
231
0
    }
232
233
0
  if (image->storage_class == PseudoClass)
234
0
    {
235
0
      (void) CdlImagePixels(NULL,&param,image,image->colormap,
236
0
                            (IndexPacket *) NULL,image->colors,
237
0
                            &image->exception);
238
0
      status=SyncImage(image);
239
0
    }
240
0
  else
241
0
    {
242
0
      status=PixelIterateMonoModify(CdlImagePixels,NULL,progress_message,
243
0
                                    NULL,&param,0,0,image->columns,image->rows,
244
0
                                    image,&image->exception);
245
0
    }
246
247
0
  MagickFreeMemory(lut);
248
249
0
  return(status);
250
0
}