/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 | ¶m.redslope,¶m.redoffset,¶m.redpower, |
184 | 0 | ¶m.greenslope,¶m.greenoffset,¶m.greenpower |
185 | 0 | ,¶m.blueslope,¶m.blueoffset,¶m.bluepower, |
186 | 0 | ¶m.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,¶m,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,¶m,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 | } |